Avoid speculative layout when retained answers suffice

This commit is contained in:
iris-ai committed 2026-09-14 15:33:39 -04:00
1 parent cdec29351a
commit a640c6cce2
5 files changed
+116 -27

No files matched your search

+28 -13
View File
@@ -109,18 +109,19 @@ impl UiRenderState {
parent_move: MoveIdx,
slotted: bool,
mask: MaskIdx,
old_children: Option<Vec<WidgetId>>,
old_active: Option<ActiveData>,
rsc: &mut dyn UiRsc,
) -> Size {
let mut old_children = old_children.unwrap_or_default();
let mut old_active = old_active;
if self.active.contains_key(&id) {
if let Some(size) = self.try_reuse(id, region, parent_move, rsc) {
return size;
}
// if not, then maintain resize and track old children to remove unneeded
let active = self.remove(id, false, rsc).unwrap();
old_children = active.children;
old_active = self.remove(id, false, rsc);
}
let previous_px = old_active.as_ref().map(|active| active.px);
let old_children = old_active.map(|active| active.children).unwrap_or_default();
// draw widget
let (move_idx, local) = match slotted {
@@ -133,6 +134,7 @@ impl UiRenderState {
}
};
let px = self.px_of(move_idx, local);
let same_box = previous_px == Some(px);
rsc.widgets_mut().needs_redraw.remove(&id);
self.draw_started.insert(id);
@@ -147,6 +149,7 @@ impl UiRenderState {
children: Vec::new(),
size_deps: Vec::new(),
reads_output: false,
same_box,
move_idx,
rsc,
};
@@ -165,6 +168,7 @@ impl UiRenderState {
children,
size_deps,
reads_output,
same_box: _,
move_idx,
layer,
id,
@@ -449,14 +453,17 @@ impl UiRenderState {
/// redraws a widget that's currently active (drawn)
pub fn redraw(&mut self, id: WidgetId, rsc: &mut dyn UiRsc) {
self.draw_started.remove(&id);
// Whoever read this widget's size may be a different size now, so the
// highest reader is what draws. Everything between the two is marked
// as well: their own boxes have not changed, so the mark is the only
// thing stopping the draw reusing its way past this widget.
if let Some(top) = self.mark_readers(id, rsc) {
// A widget can only answer whether its size changed by drawing in the
// box its parent chose. If that box changed in pixels, its retained
// placement is stale and the highest size reader must choose the new
// box first. Otherwise the widget can draw locally, and its readers
// only matter if the returned size actually changed.
let box_changed = self
.active
.get(&id)
.is_some_and(|active| self.px_of(active.parent_move, active.region) != active.px);
if box_changed && let Some(top) = self.mark_readers(id, rsc) {
self.redraw(top, rsc);
// Cleared by that draw if it reached here; if it did not, this is
// no longer drawn and asking again would not end.
rsc.widgets_mut().needs_redraw.remove(&id);
return;
}
@@ -470,7 +477,8 @@ impl UiRenderState {
return;
};
self.draw_inner(
let was = active.size;
let size = self.draw_inner(
active.layer,
id,
active.region,
@@ -478,9 +486,16 @@ impl UiRenderState {
active.parent_move,
active.move_idx != active.parent_move,
active.mask,
Some(active.children),
Some(active),
rsc,
);
if size != was
&& let Some(top) = self.mark_readers(id, rsc)
{
self.redraw(top, rsc);
rsc.widgets_mut().needs_redraw.remove(&id);
}
}
/// The furthest ancestor that read this widget's size, directly or through