Carry a span's rest weight up instead of collapsing it to one share
A span reporting `Len::default()` whenever a child had a share threw away how many shares it was holding, so each level of nesting re-divided a share rather than dividing the same space. One span of a rect beside a span of three gave 1/2 and 1/6 each, where the same four rects directly in one span get a quarter. A span that sizes from its children does not resolve `rest`, it passes the weight up; resolution belongs at the nearest ancestor with a length, and since the output became a box there is always one. The placement loop already divides by `len.rest / total.rest`, so it consumes carried weights unchanged -- only what the span reported was wrong. The uneven nesting is the case that fails without this; the even one passes either way and is here as the statement of intent. Decided by the owner, 2026-09-14. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ef815dadfd
commit
b165164e59
2 files changed
+51
-4
No files matched your search
@@ -60,10 +60,14 @@ impl Widget for Span {
|
||||
start.px += self.gap;
|
||||
}
|
||||
|
||||
let along = match total.rest == 0.0 && total.rel == 0.0 {
|
||||
true => total,
|
||||
false => Len::default(),
|
||||
};
|
||||
// Carried whole rather than collapsed to one share: a span that sizes
|
||||
// from its children does not resolve `rest`, it passes the weight up,
|
||||
// so nesting spans divides the same space rather than re-dividing a
|
||||
// share of it. Four `rest(1)` children under two spans under one span
|
||||
// get a quarter each, which collapsing to `rest(1)` per level does
|
||||
// not give. Resolution happens at the nearest ancestor with a length,
|
||||
// and the root always has one.
|
||||
let along = total;
|
||||
Size::from_axis(axis, along, ortho)
|
||||
}
|
||||
|
||||
|
||||
@@ -186,3 +186,46 @@ fn only_a_container_that_places_its_children_lengthens_the_chain() {
|
||||
"the span above the leaf, and the root the window is held in"
|
||||
);
|
||||
}
|
||||
|
||||
/// A span that sizes from its children passes their `rest` weight up rather
|
||||
/// than collapsing it to one share, so nesting divides the same space instead
|
||||
/// of re-dividing a share of it.
|
||||
#[test]
|
||||
fn nested_spans_divide_the_space_once_however_deep_the_nesting_is() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
let (a, b, c, d) = (
|
||||
rect(Color::RED).add(&mut h.rsc),
|
||||
rect(Color::BLUE).add(&mut h.rsc),
|
||||
rect(Color::GREEN).add(&mut h.rsc),
|
||||
rect(Color::WHITE).add(&mut h.rsc),
|
||||
);
|
||||
let left = (a, b).span(Dir::RIGHT).add(&mut h.rsc);
|
||||
let right = (c, d).span(Dir::RIGHT).add(&mut h.rsc);
|
||||
h.set_root((left, right).span(Dir::RIGHT));
|
||||
|
||||
for (i, id) in [a, b, c, d].into_iter().enumerate() {
|
||||
let x = i as f32 * 100.0;
|
||||
assert_corners!(h, id, (x, 0), (x + 100.0, 200));
|
||||
}
|
||||
}
|
||||
|
||||
/// The same space, unevenly nested: weights carried up mean a share is a
|
||||
/// share of the whole, not of whatever branch a widget happens to sit in.
|
||||
#[test]
|
||||
fn an_uneven_nesting_still_gives_every_share_the_same_length() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
let (a, b, c, d) = (
|
||||
rect(Color::RED).add(&mut h.rsc),
|
||||
rect(Color::BLUE).add(&mut h.rsc),
|
||||
rect(Color::GREEN).add(&mut h.rsc),
|
||||
rect(Color::WHITE).add(&mut h.rsc),
|
||||
);
|
||||
let one = (a,).span(Dir::RIGHT).add(&mut h.rsc);
|
||||
let three = (b, c, d).span(Dir::RIGHT).add(&mut h.rsc);
|
||||
h.set_root((one, three).span(Dir::RIGHT));
|
||||
|
||||
for (i, id) in [a, b, c, d].into_iter().enumerate() {
|
||||
let x = i as f32 * 100.0;
|
||||
assert_corners!(h, id, (x, 0), (x + 100.0, 200));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user