tawasuyu/llimphi
Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.
⎇ c5a232cf24788ab75a850ffe81eca5a7af50e2f8
widgets/tiled/examples/tiled_demo.rs
Ñawpa willay
Pi imata llamkarqan
ch'uya
1//! Showcase de `llimphi-widget-tiled` con drag-to-swap. Cinco paneles
2//! heterogéneos; arrastra la title bar de uno sobre otro para
3//! intercambiarlos. El destino se ilumina mientras está bajo el cursor.
4//!
5//! Corre con: `cargo run -p llimphi-widget-tiled --example tiled_demo --release`.
6
7use llimphi_theme::Theme;
8use llimphi_ui::llimphi_layout::taffy::{
9 prelude::{length, percent, FlexDirection, Size, Style},
10 Rect,
11};
12use llimphi_ui::llimphi_raster::peniko::Color;
13use llimphi_ui::llimphi_text::Alignment;
14use llimphi_ui::{App, Handle, View};
15use llimphi_widget_tiled::{tiled_view_reorderable, TileSpec, TiledPalette};
16
17#[derive(Clone, Copy, PartialEq, Eq)]
18enum TileId {
19 Logs,
20 Metrics,
21 Alerts,
22 Uptime,
23 Queue,
24}
25
26#[derive(Clone)]
27enum Msg {
28 Swap { from: usize, to: usize },
29}
30
31struct Model {
32 tiles: Vec<TileId>,
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 · tiled showcase (drag titles para intercambiar)"
43 }
44
45 fn initial_size() -> (u32, u32) {
46 (1100, 720)
47 }
48
49 fn init(_: &Handle<Msg>) -> Model {
50 Model {
51 tiles: vec![
52 TileId::Logs,
53 TileId::Metrics,
54 TileId::Alerts,
55 TileId::Uptime,
56 TileId::Queue,
57 ],
58 }
59 }
60
61 fn update(model: Model, msg: Msg, _: &Handle<Msg>) -> Model {
62 let mut m = model;
63 match msg {
64 Msg::Swap { from, to } => {
65 if from != to && from < m.tiles.len() && to < m.tiles.len() {
66 m.tiles.swap(from, to);
67 }
68 }
69 }
70 m
71 }
72
73 fn view(model: &Model) -> View<Msg> {
74 let theme = Theme::dark();
75 let palette = TiledPalette::from_theme(&theme);
76
77 let tiles: Vec<TileSpec<Msg>> = model
78 .tiles
79 .iter()
80 .map(|id| match id {
81 TileId::Logs => TileSpec {
82 label: "logs".into(),
83 content: log_body(&theme),
84 },
85 TileId::Metrics => TileSpec {
86 label: "métricas".into(),
87 content: metrics_body(&theme),
88 },
89 TileId::Alerts => TileSpec {
90 label: "alertas".into(),
91 content: alerts_body(&theme),
92 },
93 TileId::Uptime => TileSpec {
94 label: "uptime".into(),
95 content: uptime_body(&theme),
96 },
97 TileId::Queue => TileSpec {
98 label: "queue".into(),
99 content: queue_body(&theme),
100 },
101 })
102 .collect();
103
104 tiled_view_reorderable(
105 tiles,
106 |from, to| Some(Msg::Swap { from, to }),
107 &palette,
108 )
109 }
110}
111
112fn padded(text: &str, size: f32, color: Color, align: Alignment) -> View<Msg> {
113 View::new(Style {
114 size: Size {
115 width: percent(1.0_f32),
116 height: percent(1.0_f32),
117 },
118 flex_grow: 1.0,
119 padding: Rect {
120 left: length(14.0_f32),
121 right: length(14.0_f32),
122 top: length(10.0_f32),
123 bottom: length(10.0_f32),
124 },
125 ..Default::default()
126 })
127 .text_aligned(text.to_string(), size, color, align)
128}
129
130fn log_body(theme: &Theme) -> View<Msg> {
131 padded(
132 "[12:01:33] boot\n[12:01:34] config ok\n[12:01:35] esperando eventos…\n[12:02:01] cliente 1 conectó\n[12:02:02] cliente 2 conectó",
133 12.0,
134 theme.fg_text,
135 Alignment::Start,
136 )
137}
138
139fn metrics_body(theme: &Theme) -> View<Msg> {
140 let stat = |label: &str, value: &str, color: Color| -> View<Msg> {
141 let label_view = View::new(Style {
142 size: Size {
143 width: percent(1.0_f32),
144 height: length(14.0_f32),
145 },
146 ..Default::default()
147 })
148 .text_aligned(label.to_string(), 10.0, theme.fg_muted, Alignment::Start);
149 let value_view = View::new(Style {
150 size: Size {
151 width: percent(1.0_f32),
152 height: length(28.0_f32),
153 },
154 ..Default::default()
155 })
156 .text_aligned(value.to_string(), 22.0, color, Alignment::Start);
157 View::new(Style {
158 flex_direction: FlexDirection::Column,
159 size: Size {
160 width: percent(1.0_f32),
161 height: percent(1.0_f32),
162 },
163 flex_grow: 1.0,
164 ..Default::default()
165 })
166 .children(vec![label_view, value_view])
167 };
168
169 View::new(Style {
170 flex_direction: FlexDirection::Row,
171 size: Size {
172 width: percent(1.0_f32),
173 height: percent(1.0_f32),
174 },
175 gap: Size {
176 width: length(12.0_f32),
177 height: length(0.0_f32),
178 },
179 padding: Rect {
180 left: length(14.0_f32),
181 right: length(14.0_f32),
182 top: length(10.0_f32),
183 bottom: length(10.0_f32),
184 },
185 ..Default::default()
186 })
187 .children(vec![
188 stat("cpu", "37%", theme.accent),
189 stat("ram", "1.2 G", theme.fg_text),
190 stat("net", "12 kB/s", theme.fg_text),
191 ])
192}
193
194fn alerts_body(theme: &Theme) -> View<Msg> {
195 padded(
196 "● info: dos clientes online\n● warn: latencia 250 ms\n● ok: backup nocturno verde",
197 12.0,
198 theme.fg_text,
199 Alignment::Start,
200 )
201}
202
203fn uptime_body(theme: &Theme) -> View<Msg> {
204 padded("4d 12h 33m", 26.0, theme.accent, Alignment::Center)
205}
206
207fn queue_body(theme: &Theme) -> View<Msg> {
208 padded(
209 "pending: 7\nin-flight: 2\ndone (24h): 1842",
210 13.0,
211 theme.fg_text,
212 Alignment::Start,
213 )
214}
215
216fn main() {
217 llimphi_ui::run::<Showcase>();
218}