Keep a contract only where it still holds for this widget
`redraw` keeps the narrower of an old and a fresh contract so that widening and narrowing back do not churn the parent that reads it. The drawing's half asked whether the old range still covers this window and box before keeping it; the answer's half did not, so a widget whose answer contract widened in a frame that also resized the window kept a range the new window is outside. The parent's next ask then refuses that answer and draws the whole subtree again -- throwing away the drawing the widget had just made. Cost, not geometry: the size kept is the size just reported. `a_contract_this_window_is_outside_is_not_kept` draws the leaf twice before the change and once after.
This commit is contained in:
1 parent
781199a7c9
commit
d8d51221ee
2 files changed
+54
-11
No files matched your search
+41
-1
@@ -1480,14 +1480,17 @@ fn a_redrawn_subtree_is_not_undrawn_by_the_parent_it_left() {
|
||||
|
||||
/// A leaf that reports less than the box it is given and states which lengths
|
||||
/// of that box its drawing holds for, so a test can widen the contract
|
||||
/// without changing the answer.
|
||||
/// without changing the answer. It counts its draws, since what a kept
|
||||
/// contract costs is whether the parent has to make it draw again.
|
||||
struct Contracted {
|
||||
holds: std::ops::RangeInclusive<Px>,
|
||||
size: Size,
|
||||
draws: Rc<Cell<usize>>,
|
||||
}
|
||||
|
||||
impl Widget for Contracted {
|
||||
fn draw(&mut self, painter: &mut Painter) -> Size {
|
||||
self.draws.set(self.draws.get() + 1);
|
||||
painter.holds(Axis::X, self.holds.clone());
|
||||
self.size
|
||||
}
|
||||
@@ -1511,6 +1514,7 @@ fn widening_what_a_drawing_holds_for_does_not_relay_out_the_parent() {
|
||||
let child = Contracted {
|
||||
holds: Px::from_int(300)..=Px::from_int(500),
|
||||
size: Size::from((100, 200)),
|
||||
draws: Rc::new(Cell::new(0)),
|
||||
}
|
||||
.add(&mut h.rsc);
|
||||
let draws = Rc::new(Cell::new(0));
|
||||
@@ -1533,3 +1537,39 @@ fn widening_what_a_drawing_holds_for_does_not_relay_out_the_parent() {
|
||||
"a wider contract for the same answer is not a change to lay out"
|
||||
);
|
||||
}
|
||||
|
||||
/// The other half of the rule above: a kept contract is the narrower one, so
|
||||
/// it is only worth keeping where it still holds. A window the old range is
|
||||
/// outside is not one its parent can be handed back, and keeping it there
|
||||
/// throws away the drawing the widget just made.
|
||||
#[test]
|
||||
fn a_contract_this_window_is_outside_is_not_kept() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
let leaf_draws = Rc::new(Cell::new(0));
|
||||
let child = Contracted {
|
||||
holds: Px::from_int(300)..=Px::from_int(500),
|
||||
size: Size::from((100, 200)),
|
||||
draws: leaf_draws.clone(),
|
||||
}
|
||||
.add(&mut h.rsc);
|
||||
let root = CountedParent {
|
||||
inner: child.upgrade(&mut h.rsc),
|
||||
draws: Rc::new(Cell::new(0)),
|
||||
}
|
||||
.add(&mut h.rsc);
|
||||
h.set_root(root);
|
||||
|
||||
// Wide enough that the old contract leaves the new box out, and the leaf
|
||||
// is marked in the same frame -- so it settles itself first and its
|
||||
// parent draws afterwards, asking about what it settled.
|
||||
h.resize((600, 200));
|
||||
h.rsc[child].holds = Px::from_int(200)..=Px::from_int(700);
|
||||
let settled = leaf_draws.get();
|
||||
h.frame();
|
||||
|
||||
assert_eq!(
|
||||
leaf_draws.get(),
|
||||
settled + 1,
|
||||
"the leaf settled once and its parent kept what it settled"
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user