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//! **¿Un clic sobre el tablado llega al huésped en SUS coordenadas?**
2//!
3//! Es la afirmación sobre la que se apoya F4 entero, y es de las que aciertan
4//! por casualidad: con el tablado en el origen, «local» y «absoluto» son el
5//! mismo número, así que un tablado que mandara coordenadas absolutas pasaría
6//! cualquier prueba hecha en (0,0). Por eso acá el tablado va **corrido**.
7//!
8//! El kernel ya fijó la doctrina para una app enjaulada
9//! (`wawa-kernel/src/wasm/env/presentacion.rs`, `sys_puntero`): la app **jamás
10//! conoce la posición absoluta**, y sólo le llegan los eventos que caen adentro
11//! de su lienzo. En Linux las dos salen del compositor —`on_click_at` entrega
12//! coordenadas locales, y sólo se llama al nodo golpeado—, y este test lo
13//! comprueba haciendo **lo mismo que hace el bucle de eventos**: hit-test,
14//! rectángulo del nodo, resta.
15
16use llimphi_compositor::llimphi_layout::taffy::prelude::{length, Rect as TRect, Size, Style};
17use llimphi_compositor::llimphi_layout::LayoutTree;
18use llimphi_compositor::{hit_test_click, mount, View};
19use llimphi_widget_tablado::Tablado;
20
21const ANCHO: f32 = 200.0;
22const ALTO: f32 = 120.0;
23/// El tablado NO va en el origen: si fuera (0,0), el test no distinguiría local
24/// de absoluto y sería decorativo.
25const MARGEN: (f32, f32) = (30.0, 20.0);
26const CAJA: (f32, f32) = (60.0, 40.0);
27
28/// Reproduce lo que hace `llimphi-ui` al recibir un press: encuentra el nodo,
29/// toma su rectángulo y le resta el origen. Devuelve lo que el handler recibió.
30fn clic_en(t: &Tablado, abs_x: f32, abs_y: f32) -> Option<(f32, f32)> {
31 let hijo = t
32 .vista::<(f32, f32)>(Style {
33 size: Size { width: length(CAJA.0), height: length(CAJA.1) },
34 ..Default::default()
35 })
36 // Así es como el anfitrión engancha el puntero del huésped: el DSL ya
37 // compone, no hace falta un método nuevo en el tablado.
38 .on_click_at(|x, y, _w, _h| Some((x, y)));
39
40 let raiz = View::new(Style {
41 size: Size { width: length(ANCHO), height: length(ALTO) },
42 padding: TRect {
43 left: length(MARGEN.0),
44 right: length(0.0),
45 top: length(MARGEN.1),
46 bottom: length(0.0),
47 },
48 ..Default::default()
49 })
50 .children(vec![hijo]);
51
52 let mut arbol = LayoutTree::new();
53 let montado = mount(&mut arbol, raiz);
54 let computado = arbol.compute(montado.root, (ANCHO, ALTO)).expect("layout");
55
56 let i = hit_test_click(&montado, &computado, abs_x, abs_y)?;
57 let nodo = &montado.nodes[i];
58 let handler = nodo.on_click_at.as_ref()?;
59 let r = computado.get(nodo.id)?;
60 handler(abs_x - r.x, abs_y - r.y, r.w, r.h)
61}
62
63/// **La resta.** Un clic absoluto en (50,35) sobre un tablado que empieza en
64/// (30,20) le llega al huésped como (20,15).
65#[test]
66fn el_clic_llega_en_coordenadas_del_huesped() {
67 let t = Tablado::nuevo();
68 assert_eq!(
69 clic_en(&t, 50.0, 35.0),
70 Some((20.0, 15.0)),
71 "el huésped tiene que recibir SU (20,15), no el (50,35) de la ventana"
72 );
73}
74
75/// La esquina del tablado es el origen del huésped. Es el caso que delata a un
76/// tablado que mande coordenadas absolutas: acá tiene que dar (0,0).
77#[test]
78fn la_esquina_del_tablado_es_el_origen_del_huesped() {
79 let t = Tablado::nuevo();
80 assert_eq!(clic_en(&t, MARGEN.0, MARGEN.1), Some((0.0, 0.0)));
81}
82
83/// **Sólo llega lo que cae adentro.** Es la otra mitad de la doctrina del
84/// kernel: un clic sobre el chrome del anfitrión no es del huésped, y no lo
85/// decide una comprobación del huésped —que sería opcional— sino la geometría.
86#[test]
87fn un_clic_fuera_del_tablado_no_llega_al_huesped() {
88 let t = Tablado::nuevo();
89 // A la izquierda del tablado (el padding de la raíz).
90 assert_eq!(clic_en(&t, 5.0, 35.0), None, "el margen es del anfitrión");
91 // Arriba.
92 assert_eq!(clic_en(&t, 50.0, 5.0), None);
93 // A la derecha del ancho del tablado (30 + 60 = 90).
94 assert_eq!(clic_en(&t, 150.0, 35.0), None);
95 // Abajo (20 + 40 = 60).
96 assert_eq!(clic_en(&t, 50.0, 100.0), None);
97}
98
99/// Brazo de control de lo anterior: adentro, en las cuatro esquinas interiores,
100/// SÍ llega. Sin esto, un `clic_en` que devolviera siempre `None` dejaría verde
101/// el test de arriba.
102#[test]
103fn adentro_del_tablado_si_llega() {
104 let t = Tablado::nuevo();
105 assert!(clic_en(&t, 31.0, 21.0).is_some(), "esquina superior izquierda");
106 assert!(clic_en(&t, 89.0, 59.0).is_some(), "esquina inferior derecha");
107 assert!(clic_en(&t, 60.0, 40.0).is_some(), "centro");
108}