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-chip"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
publish.workspace = true
|
||||
description = "llimphi-widget-chip — chip compacto (filter / choice / input) con estado opcional seleccionado y x removible. Para tags, filtros, multi-select compacto."
|
||||
|
||||
[dependencies]
|
||||
llimphi-ui = { workspace = true }
|
||||
llimphi-theme = { workspace = true }
|
||||
@@ -0,0 +1,189 @@
|
||||
//! `llimphi-widget-chip` — chip compacto con cuatro sabores: **filter**
|
||||
//! (toggle binario), **choice** (radio dentro de un grupo), **input**
|
||||
//! (etiqueta removible con × al final) y **assist** (chip-acción con
|
||||
//! ícono opcional).
|
||||
//!
|
||||
//! Forma: rectángulo redondeado bien pegado (radius pill), padding
|
||||
//! horizontal 10/4, alto 24 px. Tema-consciente: cuando `selected`,
|
||||
//! pinta `accent` apenas tinted (alpha bajo) — sobrio, no chillón.
|
||||
//!
|
||||
//! Como toda la elegancia de Llimphi, hereda de `llimphi-theme::motion`
|
||||
//! para la transición de fill al togglear (animación implícita via
|
||||
//! `View::animated(key, motion::MICRO)`).
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
use llimphi_ui::llimphi_layout::taffy::{
|
||||
prelude::{auto, length, FlexDirection, Size, Style},
|
||||
AlignItems, JustifyContent, Rect,
|
||||
};
|
||||
use llimphi_ui::llimphi_raster::peniko::Color;
|
||||
use llimphi_ui::llimphi_text::Alignment;
|
||||
use llimphi_ui::View;
|
||||
use llimphi_theme::{motion, radius, Theme};
|
||||
|
||||
/// Paleta del chip — tres slots: idle, selected, fg.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ChipPalette {
|
||||
pub bg_idle: Color,
|
||||
pub bg_selected: Color,
|
||||
pub fg_idle: Color,
|
||||
pub fg_selected: Color,
|
||||
pub border: Color,
|
||||
}
|
||||
|
||||
impl ChipPalette {
|
||||
pub fn from_theme(t: &Theme) -> Self {
|
||||
// El selected es el accent atenuado hacia el fondo —"tinte", no
|
||||
// bloque sólido—. Calculado en sRGB-ish: `accent · 0.32 + bg · 0.68`.
|
||||
let a = t.accent.components;
|
||||
let b = t.bg_panel.components;
|
||||
let mix = Color {
|
||||
components: [
|
||||
a[0] * 0.32 + b[0] * 0.68,
|
||||
a[1] * 0.32 + b[1] * 0.68,
|
||||
a[2] * 0.32 + b[2] * 0.68,
|
||||
1.0,
|
||||
],
|
||||
..t.accent
|
||||
};
|
||||
Self {
|
||||
bg_idle: t.bg_button,
|
||||
bg_selected: mix,
|
||||
fg_idle: t.fg_text,
|
||||
fg_selected: t.fg_text,
|
||||
border: t.border,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sabor del chip — decide cómo se interpreta el `selected` y si lleva
|
||||
/// botón × removible.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ChipKind {
|
||||
/// Toggle binario (selected ↔ no). Usado en `FilterChip` de Material.
|
||||
Filter,
|
||||
/// Radio dentro de un grupo (selected = exclusive). Visualmente igual
|
||||
/// a Filter; la diferencia está en el caller que mantiene 1 seleccionado.
|
||||
Choice,
|
||||
/// Etiqueta con × final que dispara `on_remove`. Para tags, multi-select
|
||||
/// presentado como contenedor de chips.
|
||||
Input,
|
||||
/// Acción rápida (no togglea); selected siempre falso.
|
||||
Assist,
|
||||
}
|
||||
|
||||
/// Chip base — devuelve el view con anim implícita de fill por estado.
|
||||
/// `key` debe ser estable entre frames del mismo chip (idx + grupo).
|
||||
pub fn chip_view<Msg: Clone + 'static>(
|
||||
label: impl Into<String>,
|
||||
kind: ChipKind,
|
||||
selected: bool,
|
||||
key: u64,
|
||||
palette: &ChipPalette,
|
||||
on_click: Msg,
|
||||
on_remove: Option<Msg>,
|
||||
) -> View<Msg> {
|
||||
let bg = if selected { palette.bg_selected } else { palette.bg_idle };
|
||||
let fg = if selected { palette.fg_selected } else { palette.fg_idle };
|
||||
|
||||
let label: String = label.into();
|
||||
let aria = label.clone();
|
||||
let mut children = vec![View::new(Style {
|
||||
size: Size { width: auto(), height: auto() },
|
||||
..Default::default()
|
||||
})
|
||||
.text_aligned(label, 12.0, fg, Alignment::Center)];
|
||||
|
||||
if let (ChipKind::Input, Some(rm)) = (kind, on_remove) {
|
||||
// ×: glifo pequeño a la derecha con on_click propio. No es
|
||||
// botón nested (View no soporta nested on_click), así que es
|
||||
// un nodo hermano con on_click separado dentro del mismo chip
|
||||
// — el árbitro de hit-test elige el más interno.
|
||||
children.push(
|
||||
View::new(Style {
|
||||
size: Size { width: length(14.0_f32), height: length(14.0_f32) },
|
||||
margin: Rect {
|
||||
left: length(6.0_f32),
|
||||
right: length(0.0_f32),
|
||||
top: length(0.0_f32),
|
||||
bottom: length(0.0_f32),
|
||||
},
|
||||
align_items: Some(AlignItems::Center),
|
||||
justify_content: Some(JustifyContent::Center),
|
||||
..Default::default()
|
||||
})
|
||||
.radius(7.0)
|
||||
.hover_fill(palette.border)
|
||||
.text_aligned("×".to_string(), 12.0, fg, Alignment::Center)
|
||||
.on_click(rm)
|
||||
.cursor(llimphi_ui::Cursor::Pointer),
|
||||
);
|
||||
}
|
||||
|
||||
View::new(Style {
|
||||
flex_direction: FlexDirection::Row,
|
||||
align_items: Some(AlignItems::Center),
|
||||
padding: Rect {
|
||||
left: length(10.0_f32),
|
||||
right: length(10.0_f32),
|
||||
top: length(0.0_f32),
|
||||
bottom: length(0.0_f32),
|
||||
},
|
||||
size: Size { width: auto(), height: length(24.0_f32) },
|
||||
..Default::default()
|
||||
})
|
||||
.fill(bg)
|
||||
.radius(radius::XL)
|
||||
.border(1.0, palette.border)
|
||||
.animated(key, motion::MICRO)
|
||||
.children(children)
|
||||
// Filter chips se reportan como botones de toggle (pressed = selected);
|
||||
// los input/assist chips son botones plain. AccessKit anuncia el estado
|
||||
// de toggle al lector, así "está seleccionado / no seleccionado" sale solo.
|
||||
.role(llimphi_ui::Role::Button)
|
||||
.aria_label(aria)
|
||||
.aria_pressed(selected)
|
||||
.on_click(on_click)
|
||||
.cursor(llimphi_ui::Cursor::Pointer)
|
||||
}
|
||||
|
||||
/// Atajo: chip filter (toggle).
|
||||
pub fn filter_chip<Msg: Clone + 'static>(
|
||||
label: impl Into<String>,
|
||||
selected: bool,
|
||||
key: u64,
|
||||
palette: &ChipPalette,
|
||||
on_toggle: Msg,
|
||||
) -> View<Msg> {
|
||||
chip_view(label, ChipKind::Filter, selected, key, palette, on_toggle, None)
|
||||
}
|
||||
|
||||
/// Atajo: chip input (removible).
|
||||
pub fn input_chip<Msg: Clone + 'static>(
|
||||
label: impl Into<String>,
|
||||
key: u64,
|
||||
palette: &ChipPalette,
|
||||
on_click: Msg,
|
||||
on_remove: Msg,
|
||||
) -> View<Msg> {
|
||||
chip_view(
|
||||
label,
|
||||
ChipKind::Input,
|
||||
false,
|
||||
key,
|
||||
palette,
|
||||
on_click,
|
||||
Some(on_remove),
|
||||
)
|
||||
}
|
||||
|
||||
/// Atajo: chip assist (acción).
|
||||
pub fn assist_chip<Msg: Clone + 'static>(
|
||||
label: impl Into<String>,
|
||||
key: u64,
|
||||
palette: &ChipPalette,
|
||||
on_click: Msg,
|
||||
) -> View<Msg> {
|
||||
chip_view(label, ChipKind::Assist, false, key, palette, on_click, None)
|
||||
}
|
||||
Reference in New Issue
Block a user