From e5a3e640d4cb9185ea35841163785752264de1b4 Mon Sep 17 00:00:00 2001 From: iris-ai <4+iris-ai@noreply.localhost> Date: Tue, 15 Sep 2026 01:10:45 -0400 Subject: [PATCH] Add the second shrunk case, and a trace rig for what box a text is drawn in Six widgets from 905, and it fails in 0.06s: everything inside a declared 189x176 box is the same size whatever the output is, so a resize may not reach any of it, and the text still comes out 3.92px narrower warm than cold. `tests/trace_unsettled.rs` says why, and it is not what it looked like. A span measures a content-sized child in the space remaining, is told 167.41, and then offers that back as the child's box -- so the text is re-broken at exactly its own longest line, which is a knife edge: warm lands on four lines and 163.49, cold stays on three and 167.41. Measuring an answer against itself is unstable precisely at the fixed point. `Painter::settle` -- move the child's slot, keep the drawing, never measure again -- is the shape of the fix and does not work yet. In a span it breaks five cases, because a container child may have laid its own children out as fractions of the box it drew in, so moving it into a shorter one shrinks them; reporting a length in pixels does not mean the drawing is positioned in pixels. In `Aligned` alone it breaks two. Recorded rather than kept: the condition wants to be something a widget declares, near `OnResize`, rather than something its caller infers. Co-Authored-By: Claude Opus 5 --- tests/trace_unsettled.rs | 58 ++++++++++++++++++++++++++++++++++++++++ tests/unsettled.rs | 56 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/tests/trace_unsettled.rs b/tests/trace_unsettled.rs index ea61ef6..c866caa 100644 --- a/tests/trace_unsettled.rs +++ b/tests/trace_unsettled.rs @@ -95,3 +95,61 @@ fn what_box_the_text_is_drawn_in() { } diag::clear_traced_widgets(); } + +fn plant_fixed(h: &mut Harness) -> Vec { + let words = "Wrapping shapes one source into as many lines as the box leaves"; + let text = wtext(words).size(16).wrap(true).add(&mut h.rsc); + let aligned = Aligned { + inner: text.add_strong(&mut h.rsc), + align: Align { + x: Some(AxisAlign::Neg), + y: None, + }, + } + .add(&mut h.rsc); + let inner = (aligned,).span(Dir::RIGHT).add(&mut h.rsc); + let sized = SetSize { + inner: inner.add_strong(&mut h.rsc), + x: Some(Len::px(189.0)), + y: Some(Len::px(176.0)), + } + .add(&mut h.rsc); + let filler = rect(Color::RED).add(&mut h.rsc); + let root = (filler, sized).span(Dir::RIGHT).add(&mut h.rsc); + h.state.root = Some(root.add_strong(&mut h.rsc)); + vec![ + text.id(), + aligned.id(), + inner.id(), + sized.id(), + filler.id(), + root.id(), + ] +} + +#[test] +#[ignore = "a diagnostic, not a check"] +fn what_box_the_fixed_text_is_drawn_in() { + diag::clear_traced_widgets(); + let _ = diag::take(); + let mut h = Harness::new((1920, 1200)); + let ids = plant_fixed(&mut h); + let text = ids[0]; + diag::trace_widget(text); + let _ = diag::take(); + + h.frame(); + dump("first frame at 1920", &diag::take(), text); + h.resize((640, 900)); + h.frame(); + dump("after resize to 640", &diag::take(), text); + + let mut cold = Harness::new((640, 900)); + let cids = plant_fixed(&mut cold); + diag::clear_traced_widgets(); + diag::trace_widget(cids[0]); + let _ = diag::take(); + cold.frame(); + dump("cold at 640", &diag::take(), cids[0]); + diag::clear_traced_widgets(); +} diff --git a/tests/unsettled.rs b/tests/unsettled.rs index f92ca07..d823854 100644 --- a/tests/unsettled.rs +++ b/tests/unsettled.rs @@ -95,3 +95,59 @@ fn repainting_everything_moves_nothing() { } assert!(wrong.is_empty(), "{}", wrong.join("\n")); } + +/// Six widgets, shrunk from 905. Everything inside the declared 189x176 box +/// is the same size whatever the output is, so a resize may not change any of +/// it -- but the text comes out 3.92px narrower warm than cold. +fn plant_fixed(h: &mut Harness) -> Vec { + let words = "Wrapping shapes one source into as many lines as the box leaves"; + let text = wtext(words).size(16).wrap(true).add(&mut h.rsc); + let aligned = Aligned { + inner: text.add_strong(&mut h.rsc), + align: Align { + x: Some(AxisAlign::Neg), + y: None, + }, + } + .add(&mut h.rsc); + let inner = (aligned,).span(Dir::RIGHT).add(&mut h.rsc); + let sized = SetSize { + inner: inner.add_strong(&mut h.rsc), + x: Some(Len::px(189.0)), + y: Some(Len::px(176.0)), + } + .add(&mut h.rsc); + let filler = rect(Color::RED).add(&mut h.rsc); + let root = (filler, sized).span(Dir::RIGHT).add(&mut h.rsc); + h.state.root = Some(root.add_strong(&mut h.rsc)); + vec![ + text.id(), + aligned.id(), + inner.id(), + sized.id(), + filler.id(), + root.id(), + ] +} + +#[test] +fn a_resize_does_not_reach_inside_a_box_of_declared_pixels() { + let mut warm = Harness::new((1920, 1200)); + let ids = plant_fixed(&mut warm); + warm.frame(); + warm.resize((640, 900)); + warm.frame(); + + let mut cold = Harness::new((640, 900)); + let cold_ids = plant_fixed(&mut cold); + cold.frame(); + + let mut wrong = Vec::new(); + for (i, (&w, &c)) in ids.iter().zip(&cold_ids).enumerate() { + let (got, want) = (warm.region(&w), cold.region(&c)); + if got != want { + wrong.push(format!("widget {i}: warm {got:?} cold {want:?}")); + } + } + assert!(wrong.is_empty(), "{}", wrong.join("\n")); +}