From f1a47e9b7be1b823cf4b798e366a844e1f7e20ea Mon Sep 17 00:00:00 2001 From: iris-ai <4+iris-ai@noreply.localhost> Date: Mon, 14 Sep 2026 18:49:08 -0400 Subject: [PATCH] Say what three retained-layout details mean Reading this back, three things claim something they do not do. `OnResize::Translate` is returned by `TextView::on_resize` under a comment weighing anchored glyphs against reshaping ones, but nothing consumes it: `try_reuse` asks only whether the answer is `Scale`, so a widget saying `Translate` is redrawn. Say so on the variant, since the comment beside it reads as a description of behaviour. `depend_on_size(child, false)` and `depend_on_size(child, true)` are the difference between a hint, which is context-free, and a size the child produced by drawing, which carries every pixel axis the child read. That is the subtlest rule in the file and it was spelled as a bool; give the two cases their names. `draw_started` is the record of what has drawn during the pass under way, and it worked only because `redraw` removes an id before asking about it -- nothing emptied the set, so it accumulated the id of every widget ever drawn, including ones long gone. Empty it with the pass. Co-Authored-By: Claude Opus 5 --- core/src/ui/painter.rs | 31 ++++++++++++++++++------------- core/src/ui/render_state.rs | 5 +++++ core/src/widget/mod.rs | 3 +++ 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/core/src/ui/painter.rs b/core/src/ui/painter.rs index 24fdce8..e1a7248 100644 --- a/core/src/ui/painter.rs +++ b/core/src/ui/painter.rs @@ -161,7 +161,7 @@ impl<'a> Painter<'a> { Some(hint) => { #[cfg(feature = "layout-diagnostics")] diag::bump(Counter::HintHits); - self.depend_on_size(id, false); + self.depend_on_hint(id); Some(hint) } None => { @@ -203,17 +203,22 @@ impl<'a> Painter<'a> { Some(size) } - fn depend_on_size(&mut self, child: &StrongWidget, inherit_inputs: bool) { - let (box_inputs, output_inputs) = match inherit_inputs { - true => self - .state - .active - .get(&child.id()) - .map_or(([false; 2], [false; 2]), |active| { - (active.size_box_inputs, active.size_output_inputs) - }), - false => ([false; 2], [false; 2]), - }; + /// Depends on a length the child gave without being drawn. A hint is + /// context-free, so this depends on the child but on no pixel axis. + fn depend_on_hint(&mut self, child: &StrongWidget) { + self.depend_on_size_inputs(child, [false; 2], [false; 2]); + } + + /// Depends on a size the child produced by drawing, which carries + /// whatever the child read to produce it. + fn depend_on_drawn_size(&mut self, child: &StrongWidget) { + let (box_inputs, output_inputs) = self + .state + .active + .get(&child.id()) + .map_or(([false; 2], [false; 2]), |active| { + (active.size_box_inputs, active.size_output_inputs) + }); self.depend_on_size_inputs(child, box_inputs, output_inputs); } @@ -351,7 +356,7 @@ impl DrawResult<'_, '_, W> { diag::bump(Counter::SizeReads); diag::size_read(self.child.id(), self.painter.id, self.size); } - self.painter.depend_on_size(self.child, true); + self.painter.depend_on_drawn_size(self.child); self.size } diff --git a/core/src/ui/render_state.rs b/core/src/ui/render_state.rs index 5c52aff..06bc266 100644 --- a/core/src/ui/render_state.rs +++ b/core/src/ui/render_state.rs @@ -27,6 +27,9 @@ pub struct UiRenderState { /// content dirtiness, these may retain an answer whose observed pixel /// axes did not change. resize_marks: HashSet, + /// What has already been drawn during the pass under way, so a widget + /// reached by redrawing an ancestor is not drawn again on its own + /// account. Emptied when the pass ends. draw_started: HashSet, /// A widget's move slot, which outlives any one `ActiveData`: a redraw /// replaces that while its children go on pointing at the slot. @@ -138,6 +141,7 @@ impl UiRenderState { self.resized = [false; 2]; self.invalid_sizes.clear(); self.resize_marks.clear(); + self.draw_started.clear(); } fn redraw_all(&mut self, root: Option<&StrongWidget>, rsc: &mut dyn UiRsc) { @@ -574,6 +578,7 @@ impl UiRenderState { self.layers.clear(); self.invalid_sizes.clear(); self.resize_marks.clear(); + self.draw_started.clear(); rsc.widgets_mut().needs_redraw.clear(); rsc.free(); } diff --git a/core/src/widget/mod.rs b/core/src/widget/mod.rs index a702f47..af24f7b 100644 --- a/core/src/widget/mod.rs +++ b/core/src/widget/mod.rs @@ -21,6 +21,9 @@ pub use widgets::*; #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum OnResize { Scale, + /// Reserved: nothing reads this yet, so a widget saying it is redrawn. + /// Keeping an unchanged drawing in a bigger box needs the widget to say + /// *where* in that box it should sit, which is the alignment work. Translate, #[default] Redraw,