refresh: stack al día (vello 0.7 / wgpu 27 / parley 0.6) + motor 3D voxel
Re-sincroniza las fuentes desde el monorepo (estaba en vello 0.5/wgpu 24 y con la estructura vieja de eventloop) y suma el 3D: - bump del workspace a vello 0.7 / wgpu 27 / parley 0.6, + accesskit 0.24 / accesskit_winit 0.33 / vello_hybrid 0.0.9. - nuevos crates: llimphi-3d (voxels ray-march + mallas en un depth compartido, montable dentro de un View 2D vía set_viewport+scissor) y llimphi-voxel (world-gen, personajes, director de escenas) + shared/foreign-vox (puente .vox). - README: sección "Not just 2D — a 3D voxel engine" + GIF (docs/llimphi_voxel.gif). - excluido modules/allichay (arrastra deps fuera del alcance del front-door). - cargo check --workspace: verde. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "llimphi-widget-rating"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
publish.workspace = true
|
||||
description = "llimphi-widget-rating — N estrellas clicables (típicamente 5) que reflejan un valor y emiten on_change al elegir otro nivel. Para reseñas, encuestas, quality flags."
|
||||
|
||||
[dependencies]
|
||||
llimphi-ui = { workspace = true }
|
||||
llimphi-theme = { workspace = true }
|
||||
@@ -0,0 +1,109 @@
|
||||
//! `llimphi-widget-rating` — N estrellas clicables.
|
||||
//!
|
||||
//! Render: estrellas dibujadas a mano con `paint_with` (polígono de 10
|
||||
//! vértices). El caller pasa `value` (0..=max) y `max` (típicamente 5).
|
||||
//! Cada estrella es clickable: emite `on_change(idx + 1)`.
|
||||
//!
|
||||
//! Sober, no chillón: las estrellas inactivas son `border` (gris),
|
||||
//! las activas `accent`. Tamaño configurable, default 18 px.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
use llimphi_ui::llimphi_layout::taffy::{
|
||||
prelude::{length, FlexDirection, Size, Style},
|
||||
AlignItems,
|
||||
};
|
||||
use llimphi_ui::llimphi_raster::peniko::Color;
|
||||
use llimphi_ui::View;
|
||||
use llimphi_theme::Theme;
|
||||
|
||||
/// Paleta del rating.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct RatingPalette {
|
||||
pub on: Color,
|
||||
pub off: Color,
|
||||
}
|
||||
|
||||
impl RatingPalette {
|
||||
pub fn from_theme(t: &Theme) -> Self {
|
||||
Self {
|
||||
on: t.accent,
|
||||
off: t.border,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compone `max` estrellas en fila; las primeras `value` están "on",
|
||||
/// el resto "off". Cada estrella dispara `on_change(idx + 1)` al click.
|
||||
pub fn rating_view<Msg, F>(
|
||||
value: u32,
|
||||
max: u32,
|
||||
star_size: f32,
|
||||
palette: &RatingPalette,
|
||||
on_change: F,
|
||||
) -> View<Msg>
|
||||
where
|
||||
Msg: Clone + 'static,
|
||||
F: Fn(u32) -> Msg + Clone + Send + Sync + 'static,
|
||||
{
|
||||
let mut children: Vec<View<Msg>> = Vec::with_capacity(max as usize);
|
||||
for i in 0..max {
|
||||
let filled = i < value;
|
||||
let color = if filled { palette.on } else { palette.off };
|
||||
let star = View::new(Style {
|
||||
size: Size { width: length(star_size), height: length(star_size) },
|
||||
..Default::default()
|
||||
})
|
||||
.paint_with(move |scene, _ts, rect| {
|
||||
paint_star(scene, rect, color);
|
||||
})
|
||||
.on_click(on_change(i + 1))
|
||||
.cursor(llimphi_ui::Cursor::Pointer);
|
||||
children.push(star);
|
||||
}
|
||||
|
||||
View::new(Style {
|
||||
flex_direction: FlexDirection::Row,
|
||||
align_items: Some(AlignItems::Center),
|
||||
gap: Size {
|
||||
width: length(2.0_f32),
|
||||
height: length(0.0_f32),
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.children(children)
|
||||
}
|
||||
|
||||
/// Polígono de estrella de 5 puntas centrado en `rect`. Radios `R`
|
||||
/// (exterior) y `R·0.42` (interior).
|
||||
fn paint_star(
|
||||
scene: &mut llimphi_ui::llimphi_raster::vello::Scene,
|
||||
rect: llimphi_ui::PaintRect,
|
||||
color: Color,
|
||||
) {
|
||||
use llimphi_ui::llimphi_raster::kurbo::{Affine, BezPath, Point};
|
||||
use llimphi_ui::llimphi_raster::peniko::Fill;
|
||||
if rect.w <= 0.0 || rect.h <= 0.0 {
|
||||
return;
|
||||
}
|
||||
let cx = (rect.x + rect.w * 0.5) as f64;
|
||||
let cy = (rect.y + rect.h * 0.5) as f64;
|
||||
let r_out = (rect.w.min(rect.h) as f64) * 0.5;
|
||||
let r_in = r_out * 0.42;
|
||||
let mut p = BezPath::new();
|
||||
let n_points = 5;
|
||||
let start_angle = -std::f64::consts::FRAC_PI_2; // primera punta arriba
|
||||
for i in 0..(n_points * 2) {
|
||||
let r = if i % 2 == 0 { r_out } else { r_in };
|
||||
let theta = start_angle + (i as f64) * std::f64::consts::PI / (n_points as f64);
|
||||
let x = cx + r * theta.cos();
|
||||
let y = cy + r * theta.sin();
|
||||
if i == 0 {
|
||||
p.move_to(Point::new(x, y));
|
||||
} else {
|
||||
p.line_to(Point::new(x, y));
|
||||
}
|
||||
}
|
||||
p.close_path();
|
||||
scene.fill(Fill::NonZero, Affine::IDENTITY, color, None, &p);
|
||||
}
|
||||
Reference in New Issue
Block a user