Avoid speculative layout when retained answers suffice
This commit is contained in:
1 parent
cdec29351a
commit
a640c6cce2
5 files changed
+116
-27
No files matched your search
@@ -22,6 +22,8 @@ pub struct Painter<'a> {
|
||||
/// The children whose size this widget read while drawing.
|
||||
pub(super) size_deps: Vec<WidgetId>,
|
||||
pub(super) reads_output: bool,
|
||||
/// Whether this widget is drawing in the same pixel-sized box as before.
|
||||
pub(super) same_box: bool,
|
||||
/// The slot this widget's primitives are positioned through: its own if
|
||||
/// its parent placed it, otherwise the nearest ancestor that has one.
|
||||
pub(super) move_idx: MoveIdx,
|
||||
@@ -144,6 +146,21 @@ impl<'a> Painter<'a> {
|
||||
Some(hint)
|
||||
}
|
||||
|
||||
/// A clean child's retained size, when this widget's own constraints are
|
||||
/// unchanged. This is the answer from its last real draw, not a guess.
|
||||
pub fn retained_size<W: ?Sized>(&mut self, id: &StrongWidget<W>) -> Option<Size> {
|
||||
if !self.same_box || self.rsc.widgets().needs_redraw.contains(&id.id()) {
|
||||
return None;
|
||||
}
|
||||
let active = self.state.active.get(&id.id())?;
|
||||
if active.parent != Some(self.id) {
|
||||
return None;
|
||||
}
|
||||
let size = active.size;
|
||||
self.depend_on_size(id);
|
||||
Some(size)
|
||||
}
|
||||
|
||||
fn depend_on_size<W: ?Sized>(&mut self, child: &StrongWidget<W>) {
|
||||
if !self.size_deps.contains(&child.id()) {
|
||||
self.size_deps.push(child.id());
|
||||
|
||||
+28
-13
@@ -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
|
||||
|
||||
Reference in new issue
Block a user