tawasuyu/llimphi
Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.
chore: refresco desde el monorepo — 128 miembros, los 76 widgets y los 11 módulos
1// =============================================================================
2// emitir_fixture :: la escena que el kernel va a reproducir
3// -----------------------------------------------------------------------------
4// El encoder (esta jaula) y el reproductor (`llimphi-wawa`) NO pueden vivir en
5// el mismo binario: `wire` y `cpu` son backends excluyentes de `llimphi-text`,
6// y encender los dos dispara un `compile_error!` puesto a propósito. Eso no es
7// un obstáculo del diseño, es el diseño — son dos procesos en dos lados de una
8// frontera, y forzarlos a convivir para testearlos sería probar otra cosa.
9//
10// Así que la costura del test es la misma que la de producción: **bytes**. Este
11// ejemplo compone una vista con el compositor de verdad, la serializa, y deja
12// el resultado donde el test del otro lado lo levanta con `include_bytes!`.
13//
14// cargo run --example emitir_fixture --no-default-features --features std
15//
16// Si alguna vez el fixture y el reproductor dejan de entenderse, el test de
17// `llimphi-wawa` lo dice — que es exactamente lo que uno quiere que pase si
18// alguien cambia el formato del cable de un solo lado.
19// =============================================================================
20
21use std::io::Write;
22
23use llimphi_jaula::taffy::prelude::*;
24use llimphi_jaula::{Color, Jaula, Style, View};
25use llimphi_text::wire::{Medida, Peticion};
26
27/// El destino, relativo a este crate.
28const DESTINO: &str = "../llimphi-wawa/pruebas/fixture_jaula.postcard";
29
30#[derive(Clone, Debug, PartialEq)]
31enum Msg {
32 Toque,
33}
34
35/// El medidor de la jaula.
36///
37/// **Acá está la única pieza de W4 que todavía es un muñón**, y conviene que se
38/// vea: en wawa esto tiene que ser un `sys_medir_texto` que cruza a parley del
39/// lado nativo. Mientras no exista, el fixture se layoutea con una fórmula. La
40/// consecuencia práctica: el texto del fixture se pinta bien —el kernel lo
41/// shapea de verdad— pero ubicado según una medida aproximada.
42fn medidor(p: &Peticion<'_>) -> Medida {
43 let chars = p.cadena.chars().count() as f32;
44 let ancho = chars * p.estilo.tamano_px * 0.52;
45 Medida {
46 width: ancho,
47 full_width: ancho,
48 height: p.estilo.tamano_px * p.estilo.alto_linea,
49 }
50}
51
52fn vista() -> View<Msg> {
53 let raiz = Style {
54 size: Size {
55 width: length(320.0),
56 height: length(72.0),
57 },
58 flex_direction: FlexDirection::Column,
59 justify_content: Some(JustifyContent::CENTER),
60 padding: Rect {
61 left: length(14.0),
62 right: length(14.0),
63 top: length(10.0),
64 bottom: length(10.0),
65 },
66 ..Default::default()
67 };
68
69 View::new(raiz)
70 .fill(Color::from_rgba8(22, 26, 38, 255))
71 .radius(8.0)
72 .children(vec![
73 View::new(Style::default()).text(
74 "escena emitida por la jaula",
75 15.0,
76 Color::from_rgba8(230, 232, 240, 255),
77 ),
78 View::new(Style::default())
79 .text(
80 "shapeada del otro lado",
81 12.0,
82 Color::from_rgba8(140, 200, 175, 255),
83 )
84 .on_click(Msg::Toque),
85 ])
86}
87
88fn main() {
89 let mut jaula = Jaula::new(320.0, 72.0, medidor);
90 let _montado = jaula.fotograma(vista());
91 let escena = jaula.escena();
92
93 let bytes = postcard::to_allocvec(escena).expect("serializar");
94
95 println!("órdenes : {}", escena.len());
96 println!("textos : {}", escena.textos());
97 println!("estilos : {}", escena.estilos().len());
98 println!("estimadas : {}", jaula.medidas_estimadas());
99 println!("bytes : {}", bytes.len());
100
101 let destino = std::path::Path::new(DESTINO);
102 if let Some(dir) = destino.parent() {
103 std::fs::create_dir_all(dir).expect("crear directorio");
104 }
105 let mut f = std::fs::File::create(destino).expect("crear fixture");
106 f.write_all(&bytes).expect("escribir fixture");
107 println!("escrito en : {}", destino.display());
108
109 emitir_peticion_de_prueba();
110}
111
112/// Emite además los bytes de una `PeticionMedida` lista para embeber en una app
113/// `.wasm` sin asignador.
114///
115/// `hello_wasm` es `no_std` y **sin alloc**: no puede construir un `String` para
116/// armar la petición en tiempo de ejecución. Pero tampoco lo necesita — lo único
117/// que el syscall pide son bytes, y los bytes se pueden calcular acá y embeber
118/// como constante. Así la app de prueba ejercita el cruce de verdad sin arrastrar
119/// un asignador que no le hace falta para nada más.
120fn emitir_peticion_de_prueba() {
121 use llimphi_jaula::wire::{EstiloTextoWire, PeticionMedida};
122
123 let peticion = PeticionMedida {
124 cadena: "wawa mide texto".into(),
125 estilo: EstiloTextoWire {
126 tamano_px: 16.0,
127 ..Default::default()
128 },
129 ancho_max: None,
130 };
131 let bytes = postcard::to_allocvec(&peticion).expect("serializar peticion");
132
133 println!("\n// --- pegar en 03_ukupacha/wawa/apps/hello_wasm/src/lib.rs ---");
134 println!(
135 "/// `PeticionMedida {{ cadena: {:?}, tamano_px: {} }}` en postcard.",
136 peticion.cadena, peticion.estilo.tamano_px
137 );
138 print!("const PETICION_MEDIDA: [u8; {}] = [", bytes.len());
139 for (i, b) in bytes.iter().enumerate() {
140 if i % 12 == 0 {
141 print!("\n ");
142 }
143 print!("0x{b:02x}, ");
144 }
145 println!("\n];");
146}