gioser

tawasuyu/llimphi

Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.

Rust 229TOML 101Markdown 82Shell 24052 Definiciones

⎇ main widgets/list/examples/reorderable_list_demo.rs Rust · 133 líneas · 4.2 KB Historia Quién tocó qué crudo

chore: refresco desde el monorepo — vello 0.9 / wgpu 29, 120 miembros d251eb32b · Sergio ·

1//! Showcase de [`llimphi_widget_list::reorderable_list_view`] (Bloque
2//! 14 de PARIDAD-FLUTTER, primera variante de Tier 5 backlog). Una
3//! lista de tareas con drag-handle al borde izquierdo (`⋮⋮`); arrastra
4//! una fila y soltala sobre otra para intercambiarlas. El destino se
5//! ilumina con `bg_drop_hover` mientras está bajo el cursor.
6//!
7//! Cliquear una fila la marca como "completada" (cambia a tachado en el
8//! label de su descripción de abajo) — sirve para ver que `on_click`
9//! coexiste con el drag sin pelearse.
10//!
11//! `cargo run -p llimphi-widget-list --example reorderable_list_demo --release`
12
13use std::sync::Arc;
14
15use llimphi_theme::Theme;
16use llimphi_ui::llimphi_layout::taffy::{
17    prelude::{length, percent, FlexDirection, Size, Style},
18    AlignItems, JustifyContent, Rect,
19};
20use llimphi_ui::{App, Handle, View};
21use llimphi_widget_list::{
22    reorderable_list_view, ListPalette, ReorderableListRow, ReorderableListSpec,
23};
24
25#[derive(Clone)]
26enum Msg {
27    Reorder { from: usize, to: usize },
28    Toggle(usize),
29}
30
31struct Model {
32    items: Vec<(String, bool)>,
33}
34
35struct Showcase;
36
37impl App for Showcase {
38    type Model = Model;
39    type Msg = Msg;
40
41    fn title() -> &'static str {
42        "llimphi · reorderable list (drag las filas para reordenar)"
43    }
44
45    fn initial_size() -> (u32, u32) {
46        (560, 520)
47    }
48
49    fn init(_: &Handle<Msg>) -> Model {
50        Model {
51            items: vec![
52                ("Cerrar Tier 1 del roadmap (backdrop blur)".into(), true),
53                ("Closeout Tier 2: RichText spans".into(), true),
54                ("ImageFit Contain/Cover/Fill/None".into(), true),
55                ("Reorderable list widget".into(), false),
56                ("Texto seleccionable fuera del editor".into(), false),
57                ("RepaintBoundary (Tier 8)".into(), false),
58                ("Cross-fade real entre identidades".into(), false),
59            ],
60        }
61    }
62
63    fn update(model: Model, msg: Msg, _: &Handle<Msg>) -> Model {
64        let mut m = model;
65        match msg {
66            Msg::Reorder { from, to } => {
67                if from != to && from < m.items.len() && to < m.items.len() {
68                    let item = m.items.remove(from);
69                    let dest = if to > from { to - 1 } else { to };
70                    m.items.insert(dest.min(m.items.len()), item);
71                }
72            }
73            Msg::Toggle(i) => {
74                if let Some(it) = m.items.get_mut(i) {
75                    it.1 = !it.1;
76                }
77            }
78        }
79        m
80    }
81
82    fn view(model: &Model) -> View<Msg> {
83        let theme = Theme::dark();
84        let palette = ListPalette::from_theme(&theme);
85
86        let rows: Vec<ReorderableListRow<Msg>> = model
87            .items
88            .iter()
89            .enumerate()
90            .map(|(i, (label, done))| {
91                let prefix = if *done { "✓ " } else { "○ " };
92                ReorderableListRow {
93                    label: format!("{prefix}{label}"),
94                    selected: *done,
95                    on_click: Some(Msg::Toggle(i)),
96                }
97            })
98            .collect();
99
100        let panel = reorderable_list_view(ReorderableListSpec {
101            rows,
102            caption: Some(format!("{} tareas — drag para reordenar, click para marcar", model.items.len())),
103            row_height: 36.0,
104            palette,
105            on_reorder: Arc::new(|from, to| Some(Msg::Reorder { from, to })),
106        });
107
108        // Marco exterior con padding para que el panel no toque los
109        // bordes de la ventana.
110        View::new(Style {
111            flex_direction: FlexDirection::Column,
112            size: Size {
113                width: percent(1.0_f32),
114                height: percent(1.0_f32),
115            },
116            align_items: Some(AlignItems::STRETCH),
117            justify_content: Some(JustifyContent::CENTER),
118            padding: Rect {
119                left: length(20.0_f32),
120                right: length(20.0_f32),
121                top: length(20.0_f32),
122                bottom: length(20.0_f32),
123            },
124            ..Default::default()
125        })
126        .fill(theme.bg_app)
127        .children(vec![panel])
128    }
129}
130
131fn main() {
132    llimphi_ui::run::<Showcase>();
133}