//! Verifica que un párrafo largo, dentro de un bloque angosto, reserva el //! alto de **varias líneas** (no se aplasta en una). Es el regresor del bug //! "textos aplastados" de puriy: sin medición con parley, taffy le daba a la //! hoja de texto una sola línea de alto y las líneas envueltas se solapaban. use llimphi_compositor::{measure_text_node, mount, View}; use llimphi_layout::taffy::prelude::*; use llimphi_layout::taffy::Size as TSize; use llimphi_layout::LayoutTree; #[derive(Clone)] enum Msg {} #[test] fn parrafo_largo_reserva_varias_lineas() { // Bloque de 200px de ancho con un párrafo que claramente excede una línea. let texto = "Lorem ipsum dolor sit amet consectetur adipiscing elit sed do \ eiusmod tempor incididunt ut labore et dolore magna aliqua ut \ enim ad minim veniam quis nostrud exercitation ullamco laboris."; let block: View = View::new(Style { size: TSize { width: length(200.0_f32), height: auto() }, flex_direction: FlexDirection::Row, flex_wrap: FlexWrap::Wrap, ..Default::default() }) .children(vec![View::new(Style { size: TSize { width: auto(), height: auto() }, flex_shrink: 1.0, ..Default::default() }) .text_aligned(texto, 16.0_f32, vello::peniko::Color::BLACK, llimphi_text::Alignment::Start)]); let mut layout = LayoutTree::new(); let mounted = mount(&mut layout, block); let mut ts = llimphi_text::Typesetter::new(); let tmap = &mounted.text_measures; assert_eq!(tmap.len(), 1, "debería haber exactamente una hoja de texto"); let computed = layout .compute_with_measure(mounted.root, (800.0, 600.0), |nid, known, avail| match tmap.get(&nid) { Some(tm) => measure_text_node(&mut ts, tm, known, avail), None => TSize::ZERO, }) .expect("layout"); // El nodo de texto es el segundo en orden DFS (root, luego la hoja). let leaf_id = mounted.nodes[1].id; let rect = computed.get(leaf_id).expect("rect de la hoja"); // A 16px y ~1.2 de interlínea, una línea ≈ 19px. Con ~150px de texto en // 200px de ancho deberían ser >= 4 líneas → bastante más de una. assert!( rect.h > 40.0, "el párrafo se aplastó: alto={} (esperaba varias líneas)", rect.h ); assert!(rect.w <= 200.0 + 1.0, "no debería exceder el ancho del bloque"); } #[test] fn no_wrap_mide_una_sola_linea_fase_7_1253() { use llimphi_layout::taffy::AvailableSpace; // Mismo texto largo, mismo ancho disponible (angosto): con `no_wrap` se // mide en una sola línea (ancho completo, ignora el available); sin él, // envuelve (más alto, ancho acotado al disponible). let texto = "una linea larga que normalmente envuelve en varios renglones \ cuando el ancho disponible es angosto de verdad"; let mk = |no_wrap: bool| llimphi_compositor::TextMeasure { content: texto.to_string(), size_px: 16.0, alignment: llimphi_text::Alignment::Start, italic: false, font_family: None, line_height: 1.2, weight: 400.0, max_lines: None, ellipsis: false, underline: false, strikethrough: false, spans: None, letter_spacing: 0.0, word_spacing: 0.0, no_wrap, overflow_wrap: false, }; let mut ts = llimphi_text::Typesetter::new(); let known = TSize { width: None, height: None }; let avail = TSize { width: AvailableSpace::Definite(160.0), height: AvailableSpace::MaxContent, }; let env = measure_text_node(&mut ts, &mk(false), known, avail); let nw = measure_text_node(&mut ts, &mk(true), known, avail); // Envuelto: ancho acotado al disponible y alto de varias líneas. assert!(env.width <= 160.0 + 1.0, "wrap acota el ancho: {}", env.width); assert!(env.height > 40.0, "wrap reserva varias líneas: {}", env.height); // no_wrap: una sola línea → mucho más ancho que el disponible y bajo. assert!(nw.width > 160.0, "no_wrap mide ancho completo: {}", nw.width); assert!( nw.height < env.height, "no_wrap es una línea (más bajo que el envuelto): nw={} env={}", nw.height, env.height ); } #[test] fn line_height_mayor_reserva_mas_alto() { let texto = "una línea de texto que envuelve en dos o tres renglones según \ el ancho disponible para el bloque contenedor angosto"; let medir = |lh: f32| -> f32 { let mut ts = llimphi_text::Typesetter::new(); let tm = llimphi_compositor::TextMeasure { content: texto.to_string(), size_px: 16.0, alignment: llimphi_text::Alignment::Start, italic: false, font_family: None, line_height: lh, weight: 400.0, max_lines: None, ellipsis: false, underline: false, strikethrough: false, spans: None, letter_spacing: 0.0, word_spacing: 0.0, no_wrap: false, overflow_wrap: false, }; let known = TSize { width: Some(180.0_f32), height: None }; let avail = TSize { width: AvailableSpace::Definite(180.0), height: AvailableSpace::MaxContent, }; measure_text_node(&mut ts, &tm, known, avail).height }; let compacto = medir(1.0); let comodo = medir(2.0); assert!( comodo > compacto * 1.5, "line-height: 2 debería reservar bastante más alto que 1.0 (got {compacto} vs {comodo})" ); } #[test] fn overflow_wrap_parte_la_palabra_larga_fase_7_1254() { use llimphi_layout::taffy::AvailableSpace; // Una sola palabra sin espacios, más ancha que la caja angosta. Sin // `overflow-wrap` parley la deja desbordar (mide más ancho que la caja); // con `overflow-wrap` la parte para que entre (ancho acotado, varias líneas // ⇒ más alto). Es el regresor directo de la Fase 7.1254. let palabrota = "supercalifragilisticoexpialidosoineluctableantidisestablishmentariano"; let mk = |overflow_wrap: bool| llimphi_compositor::TextMeasure { content: palabrota.to_string(), size_px: 16.0, alignment: llimphi_text::Alignment::Start, italic: false, font_family: None, line_height: 1.2, weight: 400.0, max_lines: None, ellipsis: false, underline: false, strikethrough: false, spans: None, letter_spacing: 0.0, word_spacing: 0.0, no_wrap: false, overflow_wrap, }; let mut ts = llimphi_text::Typesetter::new(); let known = TSize { width: None, height: None }; let avail = TSize { width: AvailableSpace::Definite(80.0), height: AvailableSpace::MaxContent, }; let normal = measure_text_node(&mut ts, &mk(false), known, avail); let roto = measure_text_node(&mut ts, &mk(true), known, avail); // Sin overflow-wrap: la palabra desborda → ancho mayor que la caja. assert!( normal.width > 80.0, "sin overflow-wrap la palabra desborda: {}", normal.width ); // Con overflow-wrap: se parte → ancho acotado a la caja y más alto. assert!( roto.width <= 80.0 + 1.0, "overflow-wrap acota el ancho a la caja: {}", roto.width ); assert!( roto.height > normal.height, "overflow-wrap parte en varias líneas (más alto): roto={} normal={}", roto.height, normal.height ); }