Stop composer layout recursion on spaces
This commit is contained in:
1 parent
46e6edfd0b
commit
2bc0ff1866
1 file changed
+37
-13
+37
-13
@@ -1217,18 +1217,50 @@ impl UiRenderState {
|
||||
/// newly grown subtree can retain the provisional (even inverted) region
|
||||
/// it was measured in until an unrelated later update redraws it.
|
||||
fn redraw_and_settle(&mut self, id: WidgetId, rsc: &mut dyn UiRsc) {
|
||||
let Some((parent, changed)) = self.redraw_once(id, rsc) else {
|
||||
return;
|
||||
};
|
||||
if changed {
|
||||
if let Some(pid) = parent {
|
||||
self.redraw_and_settle(pid, rsc);
|
||||
}
|
||||
// The parent pass above has now placed this widget in its final
|
||||
// region. Draw it once more there; unchanged descendants still
|
||||
// take draw_inner's retained fast path. This is deliberately one
|
||||
// redraw rather than another settling pass: feeding its size
|
||||
// back into the same upward walk can alternate between the
|
||||
// provisional and final regions forever (a text edit first did
|
||||
// that when an Android IME committed a space), overflowing the
|
||||
// native thread's stack before Rust can report a panic.
|
||||
let settled_size = self.active.get(&id).map(|active| active.size);
|
||||
let _ = self.redraw_once(id, rsc);
|
||||
debug_assert_eq!(
|
||||
self.active.get(&id).map(|active| active.size),
|
||||
settled_size,
|
||||
"a widget changed size after its parent settled its final region"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Redraw `id` exactly once, returning its parent and whether the size it
|
||||
/// reports changed. [`Self::redraw_and_settle`] owns any propagation; in
|
||||
/// particular, its final downward redraw must not start another upward
|
||||
/// pass through the same branch.
|
||||
fn redraw_once(
|
||||
&mut self,
|
||||
id: WidgetId,
|
||||
rsc: &mut dyn UiRsc,
|
||||
) -> Option<(Option<WidgetId>, bool)> {
|
||||
rsc.widgets_mut().needs_redraw.remove(&id);
|
||||
// An ancestor is drawing this widget right now, and that draw is
|
||||
// about to write fresh primitives for it. Drawing it a second time
|
||||
// here would leave one of the two copies on screen with nothing
|
||||
// owning it -- see `draw_started`'s own doc.
|
||||
if self.draw_started.contains(&id) {
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
let Some(active) = self.remove(id, false, true, rsc) else {
|
||||
return;
|
||||
};
|
||||
let active = self.remove(id, false, true, rsc)?;
|
||||
let old_size = active.size;
|
||||
let parent = active.parent;
|
||||
// `old_move_slot` being `Some` below means the slot is reused in
|
||||
@@ -1259,15 +1291,7 @@ impl UiRenderState {
|
||||
// there is no query left that answers "what size would this be"
|
||||
// without actually drawing (LAYOUT.md section 5).
|
||||
let changed = self.active.get(&id).map(|a| a.size) != Some(old_size);
|
||||
if changed {
|
||||
if let Some(pid) = parent {
|
||||
self.redraw_and_settle(pid, rsc);
|
||||
}
|
||||
// The parent pass above has now placed this widget in its final
|
||||
// region. Draw it once more there; unchanged descendants still
|
||||
// take draw_inner's retained fast path.
|
||||
self.redraw_and_settle(id, rsc);
|
||||
}
|
||||
Some((parent, changed))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user