Give a slot to the children a container places, and nothing else
A widget's region is now held in the coordinates of the slot it draws in rather than the window's, and `Painter::place` is how a container asks for a slot: it draws a child it decides the box of and may decide again. Everything under that slot is a fraction of its box, so placing the child a second time is one entry to write whether it moved or changed length. A child drawn any other way has no slot and shares its nearest ancestor's. That is what keeps the chain short. `chain_cost` measured depth as the cost -- free to 8, +42.6% at 16 -- and a slot per widget put a transcript's glyphs past that for nothing, since almost every slot was zero. `Span`, `Aligned` and `Scroll` are the containers that re-place a child after drawing it, and `tests/layout.rs` pins that four widgets between a span and a leaf leave the leaf's chain one deep. `UiRegion::stretch`, `UiRegion::stretchable` and `UiScalar::stretch` are gone. Nothing is inverted any more: a box that changed length is written to its slot, and the descendants recompose against it in the shader. That also retires the case the guard existed for, where a fixed length has no fraction to recover -- `tests/layout.rs` now stretches a 40-tall row on its other axis, which `stretchable` refused outright. What still walks the CPU is deciding who must draw again, which no chain can answer: `mark_resized` descends from the widget whose box changed and marks anything whose own box changed length and whose drawing reads it. A part of a box with no relative extent on an axis is a fixed length, and composing into it leaves none either, so the walk stops where a length did not change -- an 80-wide child in a widened row is not redrawn though it says `Redraw`. `Span`, `Pad`, `Stack`, `Offset`, `Aligned`, `SetSize` and `LayerOffset` say `Scale`: each places in fractions and offsets of its own box and none reads the box's pixel length. `Scroll` and `MaxSize` do read pixels and stay `Redraw`. 45 tests pass, five of them new. Render verification comes after the CPU side, per the owner. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1f9dc48b80
commit
d98969158f
18 files changed
+332
-181
No files matched your search
+46
-5
@@ -251,9 +251,8 @@ fn a_change_two_levels_under_its_reader_still_reaches_it() {
|
||||
assert_corners!(h, below, (12, 232), (388, 388));
|
||||
}
|
||||
|
||||
/// Claims its drawing may be stretched, and has a child so that the stretch
|
||||
/// has to reach one. No shipped container claims `Scale` -- `Rect`, `Image`
|
||||
/// and `()` are all childless -- so nothing else walks a subtree to remap it.
|
||||
/// 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.
|
||||
struct Stretchy {
|
||||
inner: StrongWidget,
|
||||
draws: Rc<Cell<usize>>,
|
||||
@@ -271,7 +270,7 @@ impl Widget for Stretchy {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stretching_a_subtree_remaps_the_children_in_it() {
|
||||
fn stretching_a_subtree_carries_the_children_in_it() {
|
||||
let mut h = Harness::new((400, 400));
|
||||
let first = rect(Color::RED).height(40).add(&mut h.rsc);
|
||||
let inner = rect(Color::BLUE).add(&mut h.rsc);
|
||||
@@ -291,8 +290,50 @@ fn stretching_a_subtree_remaps_the_children_in_it() {
|
||||
assert_eq!(
|
||||
draws.get(),
|
||||
settled,
|
||||
"its drawing is stretched, not redrawn"
|
||||
"its drawing follows its box, rather than being made again"
|
||||
);
|
||||
assert_corners!(h, outer, (0, 80), (400, 400));
|
||||
assert_corners!(h, inner, (0, 80), (400, 400));
|
||||
}
|
||||
|
||||
#[test]
|
||||
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::REST, OnResize::Redraw);
|
||||
let (backing, back_draws) = counted(&mut h, Size::REST, OnResize::Scale);
|
||||
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));
|
||||
let (settled_wrap, settled_back) = (wrap_draws.get(), back_draws.get());
|
||||
|
||||
h.rsc[bar].x = Some(Len::abs(200));
|
||||
h.frame();
|
||||
|
||||
// The span reads every child's size, so redrawing one takes the span
|
||||
// with it -- and the span then measures and places the redrawn child.
|
||||
assert!(wrap_draws.get() > settled_wrap, "reads the width it got");
|
||||
assert_eq!(back_draws.get(), settled_back, "only has to be the shape");
|
||||
assert_corners!(h, backing, (200, 0), (300, 200));
|
||||
assert_corners!(h, wraps, (300, 0), (400, 200));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_fixed_length_child_is_not_redrawn_when_the_box_around_it_grows() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
// It would be drawn again for a width it does not have: its own box is
|
||||
// a fixed 80 wherever the row's edges end up.
|
||||
let (fixed, draws) = counted(&mut h, Size::from((80, 200)), OnResize::Redraw);
|
||||
let (rest, _) = counted(&mut h, Size::REST, OnResize::Scale);
|
||||
let row = (fixed, rest).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));
|
||||
let settled = draws.get();
|
||||
|
||||
h.rsc[bar].x = Some(Len::abs(200));
|
||||
h.frame();
|
||||
|
||||
assert_eq!(draws.get(), settled, "its own length did not change");
|
||||
assert_corners!(h, fixed, (200, 0), (280, 200));
|
||||
}
|
||||
Reference in new issue
Block a user