Record which widget is drawing a subtree that changed hands
A subtree can be reused whole under a different parent -- same box, same layer, same region node, clean -- and nothing in the drawing says it moved. Two things read who its parent is, and both were wrong after one of these. The old parent still listed it as a child, and a parent's next draw undraws whatever is missing from that list: two spans under one root, with the root swapping which of them it holds, drew the subtree under the new span and then erased it when the old one drew. The move is recorded on both sides where `draw_inner` already writes what the ask decided, rather than guarded at each reader. Its depth was also the one it had under the old parent, which is what the settling walk orders by, so a change made under it afterwards settled at the wrong point in the frame. `try_reuse` re-walks the subtree's depths, and only where the top of it moved, which is what makes that free in the ordinary case. Two tests: one shape where the subtree's box does not move and the span it left erases it, one where it changes depth and the change made under it has to reach the span it moved to. Each fails without one half.
This commit is contained in:
1 parent
a0693acc56
commit
e44dea34b4
3 files changed
+161
-19
No files matched your search
@@ -628,3 +628,112 @@ fn a_masked_widget_redrawn_on_its_own_sets_its_mask_again() {
|
||||
h.frame();
|
||||
assert_corners!(h, inner, (100, 0), (400, 200));
|
||||
}
|
||||
|
||||
/// The two spans a subtree changes hands between, and the branch that is not
|
||||
/// in the tree yet -- kept alive by the test until it is.
|
||||
struct Handover {
|
||||
leaf: WidgetId,
|
||||
first: WeakWidget<Span>,
|
||||
second: WeakWidget<Span>,
|
||||
root: WeakWidget<Span>,
|
||||
spare: StrongWidget,
|
||||
}
|
||||
|
||||
/// A subtree that changes hands while its box does not move, so nothing about
|
||||
/// reusing its drawing says it changed parents. `deeper` puts a span between
|
||||
/// the root and `second`, so it changes depth by changing hands as well.
|
||||
fn plant_handover(h: &mut Harness, moved: bool, deeper: bool, width: f32) -> Handover {
|
||||
let leaf = rect(Color::RED).add(&mut h.rsc);
|
||||
let sized = leaf.width(width).add(&mut h.rsc);
|
||||
let holder = (sized,).span(Dir::RIGHT).add(&mut h.rsc);
|
||||
let first = Span {
|
||||
children: match moved {
|
||||
true => Vec::new(),
|
||||
false => vec![holder.add_strong(&mut h.rsc)],
|
||||
},
|
||||
dir: Dir::RIGHT,
|
||||
gap: Px::ZERO,
|
||||
}
|
||||
.add(&mut h.rsc);
|
||||
let second = Span {
|
||||
children: match moved {
|
||||
true => vec![holder.add_strong(&mut h.rsc)],
|
||||
false => Vec::new(),
|
||||
},
|
||||
dir: Dir::RIGHT,
|
||||
gap: Px::ZERO,
|
||||
}
|
||||
.add(&mut h.rsc);
|
||||
let branch = match deeper {
|
||||
true => (second,).span(Dir::RIGHT).add_strong(&mut h.rsc),
|
||||
false => second.add_strong(&mut h.rsc),
|
||||
};
|
||||
let (in_tree, spare) = match moved {
|
||||
true => (branch, first.add_strong(&mut h.rsc)),
|
||||
false => (first.add_strong(&mut h.rsc), branch),
|
||||
};
|
||||
let root = Span {
|
||||
children: vec![in_tree],
|
||||
dir: Dir::RIGHT,
|
||||
gap: Px::ZERO,
|
||||
}
|
||||
.add(&mut h.rsc);
|
||||
h.state.root = Some(root.add_strong(&mut h.rsc));
|
||||
Handover {
|
||||
leaf: sized.id(),
|
||||
first,
|
||||
second,
|
||||
root,
|
||||
spare,
|
||||
}
|
||||
}
|
||||
|
||||
/// Moves the subtree and swaps the branch it sits in for the one it left.
|
||||
fn hand_over(h: &mut Harness, tree: Handover) -> WidgetId {
|
||||
let holder = h.rsc[tree.first].children.remove(0);
|
||||
h.rsc[tree.second].children.push(holder);
|
||||
h.rsc[tree.root].children.clear();
|
||||
h.rsc[tree.root].children.push(tree.spare);
|
||||
h.frame();
|
||||
tree.leaf
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_subtree_that_changed_parents_is_not_undrawn_by_the_one_it_left() {
|
||||
let mut warm = Harness::new((400, 200));
|
||||
let tree = plant_handover(&mut warm, false, false, 40.0);
|
||||
warm.frame();
|
||||
let leaf = hand_over(&mut warm, tree);
|
||||
|
||||
let mut cold = Harness::new((400, 200));
|
||||
let grown = plant_handover(&mut cold, true, false, 40.0);
|
||||
cold.frame();
|
||||
|
||||
assert_eq!(
|
||||
warm.region(&leaf),
|
||||
cold.region(&grown.leaf),
|
||||
"the span it left still listed it and undrew it"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_subtree_that_changed_parents_settles_at_the_depth_it_moved_to() {
|
||||
let mut warm = Harness::new((400, 200));
|
||||
let tree = plant_handover(&mut warm, false, true, 40.0);
|
||||
warm.frame();
|
||||
let leaf = hand_over(&mut warm, tree);
|
||||
// After it has changed hands, so what has to reach the new parent is a
|
||||
// change made under the subtree it now holds.
|
||||
warm.set_len(leaf, Axis::X, LayoutLen::px(90.0));
|
||||
warm.frame();
|
||||
|
||||
let mut cold = Harness::new((400, 200));
|
||||
let grown = plant_handover(&mut cold, true, true, 90.0);
|
||||
cold.frame();
|
||||
|
||||
assert_eq!(
|
||||
warm.region(&leaf),
|
||||
cold.region(&grown.leaf),
|
||||
"the span it moved to is the one the change has to reach"
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user