Track retained layout validity explicitly
This commit is contained in:
1 parent
691e3eb23c
commit
29c7881c8a
25 files changed
+836
-795
No files matched your search
+2
-11
@@ -30,7 +30,7 @@ fn env<T: std::str::FromStr>(name: &str, fallback: T) -> T {
|
||||
.and_then(|value| value.parse().ok())
|
||||
.unwrap_or(fallback)
|
||||
}
|
||||
const SEEDS: [u64; 7] = [1, 2, 3, 5, 8, 13, 98];
|
||||
const SEEDS: [u64; 9] = [1, 2, 3, 5, 8, 10, 13, 86, 98];
|
||||
const REGION_EPSILON_PX: f32 = 0.05;
|
||||
|
||||
fn same_coordinate(got: f32, want: f32) -> bool {
|
||||
@@ -280,7 +280,6 @@ fn changed_size(seed: u64) {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert_same(seed, "a size change", (&warm, &grown), (&cold, &same));
|
||||
}
|
||||
|
||||
@@ -299,16 +298,9 @@ fn reshuffled(seed: u64, shuffle: Shuffle) {
|
||||
if !shuffles {
|
||||
return;
|
||||
}
|
||||
let before: Vec<_> = grown.ids.iter().map(|id| warm.region(id)).collect();
|
||||
|
||||
let (spans, _held) = reshuffle(&mut warm, &mut grown, shuffle);
|
||||
warm.frame();
|
||||
|
||||
// Or the two trees would agree for want of anything having happened.
|
||||
let after = grown.ids.iter().map(|id| warm.region(id));
|
||||
let moved = before.iter().zip(after).filter(|(a, b)| *a != b).count();
|
||||
assert!(moved > 0, "seed {seed}: {shuffle:?} changed nothing");
|
||||
|
||||
let mut cold = Harness::new((900, 1200));
|
||||
let same = plant(
|
||||
&mut cold,
|
||||
@@ -343,7 +335,6 @@ fn changed_every_size(seed: u64) {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert_same(seed, "every size at once", (&warm, &grown), (&cold, &same));
|
||||
}
|
||||
|
||||
@@ -448,7 +439,7 @@ fn adding_and_removing_span_children_lands_where_growing_it_that_way_would() {
|
||||
/// had decided. `tests/shrink.rs` is how a seed from here becomes a tree
|
||||
/// small enough to read.
|
||||
#[test]
|
||||
#[ignore = "a hundred seeds, rather than the seven the others check"]
|
||||
#[ignore = "a hundred seeds, rather than the nine the others check"]
|
||||
fn a_long_run_of_seeds_agrees() {
|
||||
let seeds = std::env::var("IRIS_GENERATED_SEED")
|
||||
.ok()
|
||||
|
||||
@@ -349,3 +349,34 @@ fn a_span_out_of_room_shrinks_its_shares_and_not_its_fixed_lengths() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_a_pure_leftover_child_disappears_when_nothing_is_left() {
|
||||
let mut h = Harness::new((100, 20));
|
||||
let fixed = rect(Color::RED).width(100).add(&mut h.rsc);
|
||||
let leftover = rect(Color::BLUE).add(&mut h.rsc);
|
||||
h.set_root((fixed, leftover).span(Dir::RIGHT));
|
||||
|
||||
assert_corners!(h, fixed, (0, 0), (100, 20));
|
||||
assert_eq!(h.region(&leftover), None);
|
||||
|
||||
// An undrawn child remains a dependency of the span, so making room for
|
||||
// it draws it without rebuilding the tree.
|
||||
h.rsc[fixed].x = Some(Len::px(60));
|
||||
h.frame();
|
||||
assert_corners!(h, leftover, (60, 0), (100, 20));
|
||||
|
||||
let mut h = Harness::new((100, 20));
|
||||
let fixed = rect(Color::RED).width(100).add(&mut h.rsc);
|
||||
let mixed = SetSize {
|
||||
inner: rect(Color::BLUE).add_strong(&mut h.rsc),
|
||||
x: Some(Len::px(20) + Len::LEFTOVER),
|
||||
y: None,
|
||||
}
|
||||
.add(&mut h.rsc);
|
||||
h.set_root((fixed, mixed).span(Dir::RIGHT));
|
||||
|
||||
// Pixels and fractions still overflow; only a child whose entire length
|
||||
// is leftover is omitted.
|
||||
assert_corners!(h, mixed, (100, 0), (120, 20));
|
||||
}
|
||||
+83
-53
@@ -6,22 +6,22 @@ use iris::harness::{Harness, assert_corners};
|
||||
use iris::prelude::*;
|
||||
|
||||
/// A leaf that counts its draws and reports whatever size it is given, so a
|
||||
/// test can see what the retained path skipped.
|
||||
/// test can see what the retained path skipped. One that reads its box in
|
||||
/// pixels has a drawing that holds for that box alone.
|
||||
struct Counted {
|
||||
draws: Rc<Cell<usize>>,
|
||||
size: Size,
|
||||
dependence: OnResize,
|
||||
reads_box: bool,
|
||||
}
|
||||
|
||||
impl Widget for Counted {
|
||||
fn draw(&mut self, _: &mut Painter) -> Size {
|
||||
fn draw(&mut self, painter: &mut Painter) -> Size {
|
||||
self.draws.set(self.draws.get() + 1);
|
||||
if self.reads_box {
|
||||
painter.px_size();
|
||||
}
|
||||
self.size
|
||||
}
|
||||
|
||||
fn on_resize(&self, _: Axis) -> OnResize {
|
||||
self.dependence
|
||||
}
|
||||
}
|
||||
|
||||
struct Counts(Rc<Cell<usize>>);
|
||||
@@ -32,22 +32,63 @@ impl Counts {
|
||||
}
|
||||
}
|
||||
|
||||
fn counted(h: &mut Harness, size: Size, dependence: OnResize) -> (WeakWidget<Counted>, Counts) {
|
||||
fn counted(h: &mut Harness, size: Size, reads_box: bool) -> (WeakWidget<Counted>, Counts) {
|
||||
let draws = Rc::new(Cell::new(0));
|
||||
let id = Counted {
|
||||
draws: draws.clone(),
|
||||
size,
|
||||
dependence,
|
||||
reads_box,
|
||||
}
|
||||
.add(&mut h.rsc);
|
||||
(id, Counts(draws))
|
||||
}
|
||||
|
||||
struct Layered {
|
||||
children: [StrongWidget<Rect>; 2],
|
||||
_revision: usize,
|
||||
}
|
||||
|
||||
impl Widget for Layered {
|
||||
fn draw(&mut self, painter: &mut Painter) -> Size {
|
||||
painter.child_layer();
|
||||
painter.widget(&self.children[0]);
|
||||
painter.next_layer();
|
||||
painter.widget(&self.children[1]);
|
||||
Size::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_redrawn_layered_widget_keeps_the_layer_it_was_entered_on() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
let children = [
|
||||
rect(Color::RED).add_strong(&mut h.rsc),
|
||||
rect(Color::BLUE).add_strong(&mut h.rsc),
|
||||
];
|
||||
let root = Layered {
|
||||
children,
|
||||
_revision: 0,
|
||||
}
|
||||
.add(&mut h.rsc);
|
||||
h.set_root(root);
|
||||
|
||||
h.rsc[root]._revision += 1;
|
||||
h.frame();
|
||||
|
||||
let label = h.rsc.widgets().label(root.id());
|
||||
let active = h
|
||||
.render
|
||||
.debug(h.rsc.widgets(), label)
|
||||
.find(|active| active.id == root.id())
|
||||
.unwrap();
|
||||
assert_eq!(active.layer, 0);
|
||||
}
|
||||
|
||||
/// A fixed-width leaf beside one that takes what is left over, so changing
|
||||
/// the first hands the second a different box without the output changing.
|
||||
fn pair(h: &mut Harness, leftover: OnResize) -> (WeakWidget<Counted>, Counts, WidgetId) {
|
||||
let (first, _) = counted(h, Size::from((100, 200)), OnResize::Translate);
|
||||
let (second, draws) = counted(h, Size::LEFTOVER, leftover);
|
||||
fn pair(h: &mut Harness, reads_box: bool) -> (WeakWidget<Counted>, Counts, WidgetId) {
|
||||
let (first, _) = counted(h, Size::from((100, 200)), false);
|
||||
let (second, draws) = counted(h, Size::LEFTOVER, reads_box);
|
||||
h.set_root((first, second).span(Dir::RIGHT));
|
||||
(first, draws, second.id())
|
||||
}
|
||||
@@ -55,7 +96,7 @@ fn pair(h: &mut Harness, leftover: OnResize) -> (WeakWidget<Counted>, Counts, Wi
|
||||
#[test]
|
||||
fn a_leaf_that_ignores_its_box_is_not_drawn_again_when_the_box_changes() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
let (first, draws, second) = pair(&mut h, OnResize::Scale);
|
||||
let (first, draws, second) = pair(&mut h, false);
|
||||
let settled = draws.get();
|
||||
assert_corners!(h, second, (100, 0), (400, 200));
|
||||
|
||||
@@ -73,7 +114,7 @@ fn a_leaf_that_ignores_its_box_is_not_drawn_again_when_the_box_changes() {
|
||||
#[test]
|
||||
fn a_leaf_that_depends_on_its_box_is_drawn_again_when_the_box_changes() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
let (first, draws, second) = pair(&mut h, OnResize::Redraw);
|
||||
let (first, draws, second) = pair(&mut h, true);
|
||||
let settled = draws.get();
|
||||
|
||||
h.rsc[first].size = Size::from((150, 200));
|
||||
@@ -88,8 +129,8 @@ fn a_leaf_that_depends_on_its_box_is_drawn_again_when_the_box_changes() {
|
||||
#[test]
|
||||
fn a_span_child_that_declares_its_length_is_drawn_once() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
let (told, told_draws) = counted(&mut h, Size::from((100, 200)), OnResize::Translate);
|
||||
let (asked, asked_draws) = counted(&mut h, Size::from((100, 200)), OnResize::Translate);
|
||||
let (told, told_draws) = counted(&mut h, Size::from((100, 200)), false);
|
||||
let (asked, asked_draws) = counted(&mut h, Size::from((100, 200)), true);
|
||||
// The span takes one child's length from its hint and has to draw the
|
||||
// other to find out, so only the second is drawn before it is placed.
|
||||
let hinted = told.width(100).add(&mut h.rsc);
|
||||
@@ -106,7 +147,7 @@ fn a_span_child_that_declares_its_length_is_drawn_once() {
|
||||
#[test]
|
||||
fn a_span_relays_out_when_a_child_it_measured_changes() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
let (first, _, second) = pair(&mut h, OnResize::Translate);
|
||||
let (first, _, second) = pair(&mut h, false);
|
||||
|
||||
h.rsc[first].size = Size::from((250, 200));
|
||||
h.frame();
|
||||
@@ -118,8 +159,8 @@ fn a_span_relays_out_when_a_child_it_measured_changes() {
|
||||
#[test]
|
||||
fn a_repaint_that_keeps_its_size_does_not_relay_out() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
let (first, draws) = counted(&mut h, Size::from((100, 200)), OnResize::Translate);
|
||||
let (second, _) = counted(&mut h, Size::LEFTOVER, OnResize::Translate);
|
||||
let (first, draws) = counted(&mut h, Size::from((100, 200)), false);
|
||||
let (second, _) = counted(&mut h, Size::LEFTOVER, false);
|
||||
h.set_root((first, second).span(Dir::RIGHT));
|
||||
let settled = draws.get();
|
||||
|
||||
@@ -179,20 +220,20 @@ fn a_parent_that_only_read_a_hint_relays_out_when_the_hint_changes() {
|
||||
assert_corners!(h, inner, (0, 0), (400, 120));
|
||||
}
|
||||
|
||||
/// Reads the output's size, which nothing but its own draw can put right.
|
||||
struct ReadsOutput {
|
||||
/// Reads its box's size, which nothing but its own draw can put right.
|
||||
struct ReadsBox {
|
||||
draws: Rc<Cell<usize>>,
|
||||
}
|
||||
|
||||
impl Widget for ReadsOutput {
|
||||
impl Widget for ReadsBox {
|
||||
fn draw(&mut self, painter: &mut Painter) -> Size {
|
||||
self.draws.set(self.draws.get() + 1);
|
||||
Size::px(painter.output_size() / 4.0)
|
||||
Size::px(painter.px_size() / 4.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the output across one axis only, and says so: its drawing follows
|
||||
/// a taller box on its own, so only a wider one is worth a draw.
|
||||
/// Reads its box across one axis only, so its drawing holds for a taller
|
||||
/// box on its own and only a wider one is worth a draw.
|
||||
struct ReadsWidth {
|
||||
draws: Rc<Cell<usize>>,
|
||||
}
|
||||
@@ -200,21 +241,14 @@ struct ReadsWidth {
|
||||
impl Widget for ReadsWidth {
|
||||
fn draw(&mut self, painter: &mut Painter) -> Size {
|
||||
self.draws.set(self.draws.get() + 1);
|
||||
Size::px((painter.output_len(Axis::X) / 4.0, 20.0).into())
|
||||
}
|
||||
|
||||
fn on_resize(&self, axis: Axis) -> OnResize {
|
||||
match axis {
|
||||
Axis::X => OnResize::Redraw,
|
||||
Axis::Y => OnResize::Scale,
|
||||
}
|
||||
Size::px((painter.px_len(Axis::X) / 4.0, 20.0).into())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_resize_does_not_redraw_what_the_shader_can_move() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
let (leaf, draws) = counted(&mut h, Size::LEFTOVER, OnResize::Scale);
|
||||
let (leaf, draws) = counted(&mut h, Size::LEFTOVER, false);
|
||||
h.set_root(leaf);
|
||||
let settled = draws.get();
|
||||
|
||||
@@ -231,12 +265,12 @@ fn a_resize_does_not_redraw_what_the_shader_can_move() {
|
||||
}
|
||||
|
||||
/// The output is the root of the box chain, so a resize is a box that changed
|
||||
/// length and `OnResize` answers for it -- there is not a second rule for the
|
||||
/// window. A drawing that does not scale is redrawn whichever box moved.
|
||||
/// length like any other -- there is not a second rule for the window. A
|
||||
/// drawing that holds for one length is drawn again whichever box moved.
|
||||
#[test]
|
||||
fn a_resize_redraws_what_does_not_scale() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
let (leaf, draws) = counted(&mut h, Size::LEFTOVER, OnResize::Redraw);
|
||||
let (leaf, draws) = counted(&mut h, Size::LEFTOVER, true);
|
||||
h.set_root(leaf);
|
||||
let settled = draws.get();
|
||||
|
||||
@@ -248,10 +282,10 @@ fn a_resize_redraws_what_does_not_scale() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_resize_redraws_what_read_the_output() {
|
||||
fn a_resize_redraws_what_read_its_box() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
let draws = Rc::new(Cell::new(0));
|
||||
let leaf = ReadsOutput {
|
||||
let leaf = ReadsBox {
|
||||
draws: draws.clone(),
|
||||
}
|
||||
.add(&mut h.rsc);
|
||||
@@ -265,7 +299,7 @@ fn a_resize_redraws_what_read_the_output() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_resize_only_redraws_read_output_axes() {
|
||||
fn a_resize_only_redraws_read_axes() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
let draws = Rc::new(Cell::new(0));
|
||||
let leaf = ReadsWidth {
|
||||
@@ -309,7 +343,7 @@ fn subpixel_resize_changes_accumulate_from_the_last_layout() {
|
||||
#[test]
|
||||
fn subpixel_box_changes_accumulate_from_the_last_draw() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
let (first, draws, _) = pair(&mut h, OnResize::Redraw);
|
||||
let (first, draws, _) = pair(&mut h, true);
|
||||
let settled = draws.get();
|
||||
|
||||
for width in [100.02, 100.04, 100.05] {
|
||||
@@ -327,7 +361,7 @@ fn subpixel_box_changes_accumulate_from_the_last_draw() {
|
||||
fn reporting_the_same_output_size_does_not_start_a_resize() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
let draws = Rc::new(Cell::new(0));
|
||||
let leaf = ReadsOutput {
|
||||
let leaf = ReadsBox {
|
||||
draws: draws.clone(),
|
||||
}
|
||||
.add(&mut h.rsc);
|
||||
@@ -368,7 +402,7 @@ fn a_change_two_levels_under_its_reader_still_reaches_it() {
|
||||
// Every wrapper up to the outer pad read the size below it, so the outer
|
||||
// pad is what draws again -- and the span it hands the box to is the same
|
||||
// size as before, which is what lets a draw reuse its way past the leaf.
|
||||
let (leaf, _) = counted(&mut h, Size::px((100, 100).into()), OnResize::Redraw);
|
||||
let (leaf, _) = counted(&mut h, Size::px((100, 100).into()), true);
|
||||
let padded = leaf.pad(10).add(&mut h.rsc);
|
||||
let below = rect(Color::RED).add(&mut h.rsc);
|
||||
h.set_root((padded, below).span(Dir::DOWN).pad(12));
|
||||
@@ -380,8 +414,8 @@ fn a_change_two_levels_under_its_reader_still_reaches_it() {
|
||||
assert_corners!(h, below, (12, 232), (388, 388));
|
||||
}
|
||||
|
||||
/// Claims its drawing survives its box changing length, and has a child so
|
||||
/// that the walk looking for what does not has one to reach.
|
||||
/// Reads nothing of its box, so its drawing holds for any length, and has a
|
||||
/// child so that whatever asks about the subtree has one to reach.
|
||||
struct Stretchy {
|
||||
inner: StrongWidget,
|
||||
draws: Rc<Cell<usize>>,
|
||||
@@ -392,10 +426,6 @@ impl Widget for Stretchy {
|
||||
self.draws.set(self.draws.get() + 1);
|
||||
painter.widget(&self.inner).size()
|
||||
}
|
||||
|
||||
fn on_resize(&self, _: Axis) -> OnResize {
|
||||
OnResize::Scale
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -430,8 +460,8 @@ fn a_widened_row_redraws_what_reads_its_length_and_nothing_else() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
// What a transcript row is: something whose shaping depends on the width
|
||||
// it is given, beside something that only has to be the right shape.
|
||||
let (wraps, wrap_draws) = counted(&mut h, Size::LEFTOVER, OnResize::Redraw);
|
||||
let (backing, back_draws) = counted(&mut h, Size::LEFTOVER, OnResize::Scale);
|
||||
let (wraps, wrap_draws) = counted(&mut h, Size::LEFTOVER, true);
|
||||
let (backing, back_draws) = counted(&mut h, Size::LEFTOVER, false);
|
||||
let row = (backing, wraps).span(Dir::RIGHT).add(&mut h.rsc);
|
||||
let bar = rect(Color::RED).width(100).add(&mut h.rsc);
|
||||
h.set_root((bar, row).span(Dir::RIGHT));
|
||||
@@ -455,9 +485,9 @@ fn a_declared_length_child_is_not_redrawn_when_the_box_around_it_grows() {
|
||||
// again would be for a width it does not have. The declared width is what
|
||||
// lets the span say that without drawing it: a width the span learnt by
|
||||
// drawing the child in its own box is only an answer for that box.
|
||||
let (counter, draws) = counted(&mut h, Size::from((80, 200)), OnResize::Redraw);
|
||||
let (counter, draws) = counted(&mut h, Size::from((80, 200)), true);
|
||||
let fixed = counter.width(80).add(&mut h.rsc);
|
||||
let (leftover, _) = counted(&mut h, Size::LEFTOVER, OnResize::Scale);
|
||||
let (leftover, _) = counted(&mut h, Size::LEFTOVER, false);
|
||||
let row = (fixed, leftover).span(Dir::RIGHT).add(&mut h.rsc);
|
||||
let bar = rect(Color::RED).width(100).add(&mut h.rsc);
|
||||
h.set_root((bar, row).span(Dir::RIGHT));
|
||||
|
||||
@@ -296,3 +296,46 @@ fn a_span_placed_once_in_a_box_its_answer_decided() {
|
||||
}
|
||||
assert!(wrong.is_empty(), "{}", wrong.join("\n"));
|
||||
}
|
||||
|
||||
/// Reports a width derived from the box it is asked in. Reading through the
|
||||
/// painter is its declaration that the answer holds for that width only.
|
||||
struct Wider {
|
||||
extra: f32,
|
||||
}
|
||||
|
||||
impl Widget for Wider {
|
||||
fn draw(&mut self, painter: &mut Painter) -> Size {
|
||||
Size {
|
||||
x: Len::px(painter.px_len(Axis::X) + self.extra),
|
||||
y: Len::LEFTOVER,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn plant_wider(h: &mut Harness, extra: f32) -> (WeakWidget<Wider>, WidgetId) {
|
||||
let content = Wider { extra }.add(&mut h.rsc);
|
||||
let scroll = Scroll::new(content.add_strong(&mut h.rsc), Axis::X).add(&mut h.rsc);
|
||||
let root = Aligned {
|
||||
inner: scroll.add_strong(&mut h.rsc),
|
||||
align: Align {
|
||||
x: Some(AxisAlign::Neg),
|
||||
y: None,
|
||||
},
|
||||
}
|
||||
.add(&mut h.rsc);
|
||||
h.set_root(root);
|
||||
(content, scroll.id())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_scrolls_retained_answer_is_the_one_a_cold_layout_asks_for() {
|
||||
let mut warm = Harness::new((100, 100));
|
||||
let (content, scroll) = plant_wider(&mut warm, 50.0);
|
||||
warm.rsc[content].extra = 70.0;
|
||||
warm.frame();
|
||||
|
||||
let mut cold = Harness::new((100, 100));
|
||||
let (_, cold_scroll) = plant_wider(&mut cold, 70.0);
|
||||
|
||||
assert_eq!(warm.region(&scroll), cold.region(&cold_scroll));
|
||||
}
|
||||
Reference in new issue
Block a user