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
|
/// newly grown subtree can retain the provisional (even inverted) region
|
||||||
/// it was measured in until an unrelated later update redraws it.
|
/// it was measured in until an unrelated later update redraws it.
|
||||||
fn redraw_and_settle(&mut self, id: WidgetId, rsc: &mut dyn UiRsc) {
|
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);
|
rsc.widgets_mut().needs_redraw.remove(&id);
|
||||||
// An ancestor is drawing this widget right now, and that draw is
|
// An ancestor is drawing this widget right now, and that draw is
|
||||||
// about to write fresh primitives for it. Drawing it a second time
|
// about to write fresh primitives for it. Drawing it a second time
|
||||||
// here would leave one of the two copies on screen with nothing
|
// here would leave one of the two copies on screen with nothing
|
||||||
// owning it -- see `draw_started`'s own doc.
|
// owning it -- see `draw_started`'s own doc.
|
||||||
if self.draw_started.contains(&id) {
|
if self.draw_started.contains(&id) {
|
||||||
return;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(active) = self.remove(id, false, true, rsc) else {
|
let active = self.remove(id, false, true, rsc)?;
|
||||||
return;
|
|
||||||
};
|
|
||||||
let old_size = active.size;
|
let old_size = active.size;
|
||||||
let parent = active.parent;
|
let parent = active.parent;
|
||||||
// `old_move_slot` being `Some` below means the slot is reused in
|
// `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"
|
// there is no query left that answers "what size would this be"
|
||||||
// without actually drawing (LAYOUT.md section 5).
|
// without actually drawing (LAYOUT.md section 5).
|
||||||
let changed = self.active.get(&id).map(|a| a.size) != Some(old_size);
|
let changed = self.active.get(&id).map(|a| a.size) != Some(old_size);
|
||||||
if changed {
|
Some((parent, 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user