From 0e107f0e89f668bbde600d426ebb6f8de29280e6 Mon Sep 17 00:00:00 2001 From: iris-ai <4+iris-ai@noreply.localhost> Date: Thu, 17 Sep 2026 17:24:53 -0400 Subject: [PATCH] Keep valid layout guarantees when a redraw widens their range --- core/src/ui/layout_holds.rs | 11 +++++++++++ core/src/ui/render_state.rs | 14 +++++++++++++- tests/cases/retained.rs | 22 ++++++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/core/src/ui/layout_holds.rs b/core/src/ui/layout_holds.rs index 89d61c6..abc2d59 100644 --- a/core/src/ui/layout_holds.rs +++ b/core/src/ui/layout_holds.rs @@ -34,6 +34,17 @@ impl LayoutHolds { } } + pub fn covers(self, other: Self) -> bool { + self.placement + .is_none_or(|placement| other.placement == Some(placement)) + && [0, 1].into_iter().all(|n| { + self.frame[n].lo <= other.frame[n].lo + && self.frame[n].hi >= other.frame[n].hi + && self.extent[n].lo <= other.extent[n].lo + && self.extent[n].hi >= other.extent[n].hi + }) + } + pub fn contains(self, px: PxVec2, placement: UiRegion) -> bool { self.placement.is_none_or(|old| old == placement) && [Axis::X, Axis::Y].into_iter().all(|axis| { diff --git a/core/src/ui/render_state.rs b/core/src/ui/render_state.rs index 7f55008..b24a8f3 100644 --- a/core/src/ui/render_state.rs +++ b/core/src/ui/render_state.rs @@ -1163,7 +1163,19 @@ impl UiRenderState { if info.placement != offered.placement { self.draw_inner(id, given, info, None, false, rsc); } - if Some((answer.0, answer.1)) != was_answer || self.active[&id].holds != was_holds { + let active = self.active.get_mut(&id).unwrap(); + // A wider contract does not invalidate the guarantee the parent kept. + // Retain that guarantee so widening and narrowing back do not churn it. + if let Some((size, holds)) = was_answer + && answer.0 == size + && answer.1.covers(holds) + { + active.answer = was_answer; + } + if active.holds.covers(was_holds) && was_holds.contains(given_px, active.placement) { + active.holds = was_holds; + } + if active.answer != was_answer || active.holds != was_holds { // The parent retains both the answer and the drawing's validity; // even an unchanged size can narrow the range safe for a resize. #[cfg(feature = "layout-diagnostics")] diff --git a/tests/cases/retained.rs b/tests/cases/retained.rs index cca9dd6..2052e5b 100644 --- a/tests/cases/retained.rs +++ b/tests/cases/retained.rs @@ -1105,3 +1105,25 @@ fn changed_drawing_dependencies_reach_ancestors_without_a_size_change() { assert_eq!(draws.get(), settled + 1); assert_corners!(h, leaf, (0, 0), (800, 200)); } + +#[test] +fn widening_and_restoring_a_contract_does_not_invalidate_its_reader() { + let mut h = Harness::new((400, 200)); + let (leaf, leaf_draws) = counted(&mut h, Size::LEFTOVER, true); + let draws = Rc::new(Cell::new(0)); + let child = leaf.add_strong(&mut h.rsc); + h.set_root(Unmeasured { + child, + draws: draws.clone(), + }); + let settled = draws.get(); + for reads_box in [false, true, false, true] { + h.rsc[leaf].reads_box = reads_box; + h.frame(); + assert_eq!(draws.get(), settled); + } + let settled = leaf_draws.get(); + h.resize((800, 200)); + h.frame(); + assert_eq!(leaf_draws.get(), settled + 1); +}