tawasuyu

tawasuyu/llimphi

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

Rust 660Markdown 302TOML 157RON 312382 Definiciones

⎇ main llimphi-wasm-demo-form/src/lib.rs Rust · 184 líneas · 5.3 KB Historia Quién tocó qué crudo

chore: refresco desde el monorepo — 27 crates nuevos (anim, lottie, svg, mesh, image, layer, test, wire-view, widgets rag-sidebar/router/lazy-list/rive-button) y sync de los 95 existentes 93253cc22 · Sergio ·

1//! Formulario WASM Tier 3: texto + checkbox + slider + dropdown + multiline +
2//! radio + saludo vivo.
3//!
4//! Ejercita todos los eventos con payload: `Text` (nombre y notas multilínea),
5//! `Toggle` (mayúsculas), `Value` (slider edad), `Select` (dropdown color y
6//! grupo de radio prioridad) y `Click` (limpiar). El estado vive en el guest;
7//! el host sólo pinta y notifica.
8
9use llimphi_wasm_app_sdk::{
10    col, export_wasm_app, row, text, Align, Dim, Justify, TextAlign, Ui, WasmApp, WireNode,
11};
12
13const COLORES: [&str; 3] = ["rojo", "verde", "azul"];
14const PRIORIDADES: [&str; 3] = ["baja", "media", "alta"];
15
16#[derive(Clone)]
17pub enum Msg {
18    SetNombre(String),
19    SetMayus(bool),
20    SetEdad(f32),
21    SetColor(u32),
22    SetNotas(String),
23    SetPrioridad(u32),
24    Limpiar,
25}
26
27pub struct Form {
28    nombre: String,
29    mayus: bool,
30    edad: f32,
31    color: u32,
32    notas: String,
33    prioridad: u32,
34}
35
36impl Default for Form {
37    fn default() -> Self {
38        Form {
39            nombre: String::new(),
40            mayus: false,
41            edad: 30.0,
42            color: 1,
43            notas: String::new(),
44            prioridad: 0,
45        }
46    }
47}
48
49impl Form {
50    fn saludo(&self) -> String {
51        if self.nombre.is_empty() {
52            return "Hola, …".into();
53        }
54        let s = format!("Hola, {}!", self.nombre);
55        if self.mayus {
56            s.to_uppercase()
57        } else {
58            s
59        }
60    }
61}
62
63impl WasmApp for Form {
64    type Msg = Msg;
65
66    fn init() -> Self {
67        Form::default()
68    }
69
70    fn update(&mut self, msg: Msg) {
71        match msg {
72            Msg::SetNombre(s) => self.nombre = s,
73            Msg::SetMayus(b) => self.mayus = b,
74            Msg::SetEdad(v) => self.edad = v,
75            Msg::SetColor(i) => self.color = i,
76            Msg::SetNotas(s) => self.notas = s,
77            Msg::SetPrioridad(i) => self.prioridad = i,
78            Msg::Limpiar => {
79                self.nombre.clear();
80                self.mayus = false;
81                self.edad = 30.0;
82                self.color = 1;
83                self.notas.clear();
84                self.prioridad = 0;
85            }
86        }
87    }
88
89    fn view(&self, ui: &mut Ui<Msg>) -> WireNode {
90        let titulo = text("Saludo", 32.0, [230, 240, 250, 255]);
91
92        // EventId 0: campo de nombre.
93        let campo = ui
94            .text_input(self.nombre.clone(), "tu nombre…", Msg::SetNombre)
95            .fill([28, 34, 44, 255])
96            .radius(8.0)
97            .padding(10.0, 12.0, 10.0, 12.0)
98            .width(Dim::Pct(1.0))
99            .height(Dim::Px(44.0));
100
101        // EventId 1: checkbox.
102        let check = row(vec![
103            ui.checkbox(self.mayus, Msg::SetMayus),
104            text("MAYÚSCULAS", 20.0, [200, 210, 225, 255]).align(Align::Center),
105        ])
106        .gap(10.0)
107        .align(Align::Center);
108
109        // EventId 2: slider de edad (0..100).
110        let edad_row = row(vec![
111            text(format!("Edad {}", self.edad as i32), 20.0, [200, 210, 225, 255])
112                .width(Dim::Px(90.0)),
113            ui.slider(self.edad, 0.0, 100.0, Msg::SetEdad)
114                .grow(1.0)
115                .height(Dim::Px(20.0)),
116        ])
117        .gap(12.0)
118        .align(Align::Center);
119
120        // EventId 3: dropdown de color.
121        let color = ui
122            .select(
123                COLORES.iter().map(|s| s.to_string()).collect(),
124                self.color,
125                Msg::SetColor,
126            )
127            .width(Dim::Pct(1.0));
128
129        let saludo = text(self.saludo(), 28.0, [120, 220, 170, 255]);
130        let detalle = text(
131            format!(
132                "Edad: {} · Color: {} · Prioridad: {} · Notas: {} líneas",
133                self.edad as i32,
134                COLORES[self.color as usize % COLORES.len()],
135                PRIORIDADES[self.prioridad as usize % PRIORIDADES.len()],
136                self.notas.lines().count(),
137            ),
138            18.0,
139            [150, 160, 180, 255],
140        )
141        .grow(1.0);
142
143        // EventId 4: notas multilínea (Enter inserta salto de línea).
144        let notas = ui
145            .multiline_input(self.notas.clone(), "notas (Enter = nueva línea)…", Msg::SetNotas)
146            .fill([28, 34, 44, 255])
147            .radius(8.0)
148            .padding(10.0, 12.0, 10.0, 12.0)
149            .width(Dim::Pct(1.0))
150            .height(Dim::Px(72.0));
151
152        // EventId 5: grupo de radio de prioridad (el nodo es el contenedor; el
153        // host pinta una fila por opción).
154        let prioridad = ui
155            .radio(
156                PRIORIDADES.iter().map(|s| s.to_string()).collect(),
157                self.prioridad,
158                Msg::SetPrioridad,
159            )
160            .width(Dim::Pct(1.0))
161            .gap(2.0);
162
163        // EventId 6: botón limpiar.
164        let limpiar = ui
165            .button("limpiar", 20.0, [30, 10, 10, 255], Msg::Limpiar)
166            .text_align(TextAlign::Center)
167            .fill([200, 120, 90, 255])
168            .radius(8.0)
169            .size(Dim::Px(120.0), Dim::Px(40.0))
170            .align(Align::Center)
171            .justify(Justify::Center);
172
173        col(vec![
174            titulo, campo, check, edad_row, color, saludo, detalle, notas, prioridad, limpiar,
175        ])
176        .gap(16.0)
177            .pad(28.0)
178            .fill([18, 22, 30, 255])
179            .width(Dim::Pct(1.0))
180            .height(Dim::Pct(1.0))
181    }
182}
183
184export_wasm_app!(Form);