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//! Un `margin-right` NEGATIVO saca la caja de borde del contenedor a
2//! propósito: la que tiene que entrar es la caja de MARGEN.
3//!
4//! Es el botón de idiomas de Wikipedia («97 languages»), que lleva
5//! `margin-right: -12px` para que su padding interno no meta sangría en el
6//! borde derecho del encabezado. Medido en el árbol real: fila de 880, botón de
7//! 147 en x=745 → el borde llega a 892 y parece un desborde de 12px, pero la
8//! caja de margen mide 135 y cierra EXACTO en 880. Taffy reparte bien; el que
9//! medía mal era el harness `salud_web`, que comparaba bordes.
10//!
11//! Este test fija las dos mitades del contrato, porque de la primera sola se
12//! saca la conclusión equivocada: (a) sin el margen negativo el item entra
13//! justo, y (b) con él el borde asoma exactamente lo que se pidió, ni más.
14
15use llimphi_layout::taffy::{self, prelude::*, AvailableSpace};
16
17/// Fila de 880 con una columna elástica y un item inflexible de 147 (una hoja
18/// medida, como el texto real) que lleva `margin-right: margen`. Devuelve
19/// `(x, ancho)` del item.
20fn fila_con_item(margen: f32) -> (f32, f32) {
21 let mut t: TaffyTree<f32> = TaffyTree::new();
22 let item = t
23 .new_leaf_with_context(
24 Style {
25 flex_grow: 0.0,
26 flex_shrink: 0.0,
27 margin: Rect {
28 left: zero(),
29 right: length(margen),
30 top: zero(),
31 bottom: zero(),
32 },
33 ..Default::default()
34 },
35 147.0,
36 )
37 .unwrap();
38 let columna = t
39 .new_leaf(Style {
40 flex_grow: 1.0,
41 flex_basis: length(0.0_f32),
42 min_size: Size { width: length(0.0_f32), height: auto() },
43 ..Default::default()
44 })
45 .unwrap();
46 let fila = t
47 .new_with_children(
48 Style {
49 display: Display::Flex,
50 flex_direction: FlexDirection::Row,
51 flex_wrap: FlexWrap::Wrap,
52 align_items: Some(AlignItems::START),
53 size: Size { width: length(880.0_f32), height: auto() },
54 ..Default::default()
55 },
56 &[columna, item],
57 )
58 .unwrap();
59 t.compute_layout_with_measure(
60 fila,
61 taffy::Size { width: AvailableSpace::Definite(880.0), height: AvailableSpace::MaxContent },
62 |known, avail, _n, ctx, _st| {
63 let Some(&mut mx) = ctx else { return taffy::Size::ZERO };
64 let w = known.width.unwrap_or(match avail.width {
65 AvailableSpace::Definite(w) => w.min(mx),
66 AvailableSpace::MaxContent => mx,
67 AvailableSpace::MinContent => mx,
68 });
69 taffy::Size { width: w, height: 18.0 }
70 },
71 )
72 .unwrap();
73 let l = t.layout(item).unwrap();
74 (l.location.x, l.size.width)
75}
76
77#[test]
78fn sin_margen_negativo_el_item_cierra_justo_en_el_contenedor() {
79 let (x, w) = fila_con_item(0.0);
80 assert_eq!(w, 147.0, "el item mide su max-content");
81 assert!((x + w - 880.0).abs() < 0.5, "cierra en el borde: {x}+{w}");
82}
83
84#[test]
85fn con_margen_negativo_el_borde_asoma_justo_lo_pedido() {
86 let (x, w) = fila_con_item(-12.0);
87 // La caja de MARGEN cierra exacto en 880…
88 assert!((x + w - 12.0 - 880.0).abs() < 0.5, "la caja de margen no cierra: {x}+{w}-12");
89 // …y por eso la de BORDE asoma 12, ni uno más. Un harness que mida bordes
90 // ve acá un «desborde» de 12px que ningún browser reporta.
91 assert!((x + w - 892.0).abs() < 0.5, "el borde asoma otra cosa: {x}+{w}");
92}