tawasuyu/llimphi
Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.
chore: refresco desde el monorepo — vello 0.9 / wgpu 29, 120 miembros
1//! Verifica el contrato de [`measure_intrinsic_node`]: una hoja de contenido
2//! REEMPLAZADO (una imagen) aporta **0 a min-content y su tamaño natural a
3//! max-content**.
4//!
5//! No es una sutileza: es lo único que un `Style` no puede expresar, y cada
6//! forma de escribirlo con `Style` solo rompe una punta distinta —medidas las
7//! dos sobre el corpus de puriy antes de llegar acá—:
8//! - tamaño DEFINIDO → aporta el natural a las dos, y un contenedor con
9//! `max-width` no puede encoger por debajo del ancho de su imagen.
10//! - tamaño en PORCENTAJE → aporta 0 a las dos, y adentro de un padre
11//! shrink-to-fit (un `float`) la imagen DESAPARECE.
12use llimphi_compositor::{measure_intrinsic_node, mount, IntrinsicSize, View};
13use llimphi_layout::taffy::prelude::*;
14use llimphi_layout::taffy::{AvailableSpace, Size as TSize};
15use llimphi_layout::LayoutTree;
16
17const NAT: IntrinsicSize = IntrinsicSize { width: 600.0, height: 300.0 };
18
19fn medir(w: AvailableSpace) -> TSize<f32> {
20 measure_intrinsic_node(
21 &NAT,
22 TSize { width: None, height: None },
23 TSize { width: w, height: AvailableSpace::MaxContent },
24 )
25}
26
27#[test]
28fn min_content_es_cero_y_max_content_es_el_natural() {
29 assert_eq!(medir(AvailableSpace::MaxContent).width, 600.0, "max-content = ancho natural");
30 assert_eq!(medir(AvailableSpace::MinContent).width, 0.0, "min-content = 0: puede encoger");
31}
32
33#[test]
34fn con_ancho_definido_nunca_pasa_del_natural_ni_del_disponible() {
35 assert_eq!(medir(AvailableSpace::Definite(200.0)).width, 200.0, "más chico: se adapta");
36 assert_eq!(medir(AvailableSpace::Definite(900.0)).width, 600.0, "más grande: no se estira");
37}
38
39#[test]
40fn el_alto_sigue_la_proporcion() {
41 let m = medir(AvailableSpace::Definite(300.0));
42 assert_eq!((m.width, m.height), (300.0, 150.0), "600x300 a la mitad = 300x150");
43 // Con el alto ya decidido por el layout, se respeta.
44 let fijo = measure_intrinsic_node(
45 &NAT,
46 TSize { width: None, height: Some(90.0) },
47 TSize { width: AvailableSpace::Definite(300.0), height: AvailableSpace::MaxContent },
48 );
49 assert_eq!(fijo.height, 90.0, "un alto conocido manda sobre la proporción");
50}
51
52/// Un natural degenerado (0 en un eje) no debe dividir por cero.
53#[test]
54fn un_natural_degenerado_no_rompe() {
55 let cero = IntrinsicSize { width: 0.0, height: 0.0 };
56 let m = measure_intrinsic_node(
57 &cero,
58 TSize { width: None, height: None },
59 TSize { width: AvailableSpace::MaxContent, height: AvailableSpace::MaxContent },
60 );
61 assert!(m.width.is_finite() && m.height.is_finite(), "sin NaN ni infinito: {m:?}");
62}
63
64/// Extremo a extremo: la medida se registra en el `mount` y el layout la usa.
65/// Una imagen de 600px dentro de un contenedor de 200 se adapta a 200 (no
66/// desborda) y conserva la proporción.
67#[test]
68fn el_mount_registra_la_medida_y_el_layout_la_usa() {
69 let mut lt = LayoutTree::new();
70 let vista: View<()> = View::new(Style {
71 size: TSize { width: length(200.0), height: auto() },
72 ..Default::default()
73 })
74 .children(vec![View::new(Style::default()).intrinsic(600.0, 300.0)]);
75 let m = mount(&mut lt, vista);
76 assert_eq!(m.intrinsic_measures.len(), 1, "la hoja quedó registrada para medir");
77
78 lt.compute_with_measure(m.root, (1000.0, 1000.0), |nid, known, avail| {
79 match m.intrinsic_measures.get(&nid) {
80 Some(nat) => measure_intrinsic_node(nat, known, avail),
81 None => TSize::ZERO,
82 }
83 })
84 .expect("layout");
85 let hoja = lt.inner().children(m.root).unwrap()[0];
86 let l = lt.inner().layout(hoja).unwrap();
87 assert_eq!(l.size.width, 200.0, "se adapta al contenedor en vez de desbordarlo");
88 assert_eq!(l.size.height, 100.0, "y conserva la proporción 2:1");
89}