Hold a bound's length where it is decided, and say each thing once
A quality sweep over the deferred request system, which no earlier round has reviewed. `Bound::outside` said which end a length fell outside and left the caller to look that end up through `Bound::at`, which `expect`s an end the value it is given does not promise: only the pairing of the two calls kept `at(Shorter)` off a bound with no floor. It already had the length in hand, so it returns that, and `Outside` and `at` go with the state that could panic. `measured_request` pinned the rel base for any bound at all, so a measured share under a cap in pixels was invalidated by a change to a base its answer cannot depend on. That question is `Bound::has_fraction` now, which is also the one `Placing::ask` and `SizeRule::has_fraction` were each writing out over a bare array. The rest is one name where there were several spellings: `Span::gaps`, `Padding::along`, `Plan::drop_bounds` behind one `IRIS_UNBOUNDED` in both rigs that had grown their own, and `Stack::size_request` resolving its sizing child the way its draw already does. `Span`'s placement loop asked three times whether the row was allocated, twice to decide one child's length; one match answers all three, so the allocated and plain rules are read side by side. The buffers `draw_at` now reuses for their capacity are empty only because every path to it drains them in `remove`; a `debug_assert` says so, since a drawing over primitives left in one would record them twice. Comments: `with_requests` named discovery as the hazard where it is a child drawn mid-row, `Painter::allocate` documented the window it holds for rather than what it does, `minimum_request` had none, and the note saying a span carries its children's weight whole -- which is still what the unallocated path does, and still the surprising part -- had been replaced by one about the other path.
This commit is contained in:
1 parent
0e838e9dd1
commit
4cb6f6882a
12 files changed
+192
-160
No files matched your search
@@ -342,6 +342,21 @@ impl Plan {
|
||||
at(self);
|
||||
}
|
||||
|
||||
/// Drops every intrinsic bound from this tree, leaving the rest of it
|
||||
/// -- and the generator's draws -- exactly as they were. That isolates
|
||||
/// the ordinary path from the deferred one over the same shapes, which
|
||||
/// is what says whether a difference is the bounds or the trees.
|
||||
pub fn drop_bounds(&mut self) {
|
||||
self.walk_mut(&mut |node| {
|
||||
let Some(rules) = &mut node.size else { return };
|
||||
for axis in Axis::BOTH {
|
||||
if rules[axis].bound() != Bound::ANY {
|
||||
rules[axis] = SizeRule::Free;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// The same tree with `edits` applied, by the indices the generator would
|
||||
/// have used for them.
|
||||
///
|
||||
|
||||
@@ -7,11 +7,7 @@ pub struct Pad {
|
||||
|
||||
impl Widget for Pad {
|
||||
fn size_request(&self, requests: &mut SizeRequests, axis: Axis) -> Option<RequestedLen> {
|
||||
let padding = match axis {
|
||||
Axis::X => self.padding.left + self.padding.right,
|
||||
Axis::Y => self.padding.top + self.padding.bottom,
|
||||
};
|
||||
requests.inset(&self.inner, axis, padding)
|
||||
requests.inset(&self.inner, axis, self.padding.along(axis))
|
||||
}
|
||||
|
||||
fn draw(&mut self, painter: &mut Painter) -> Size {
|
||||
@@ -30,11 +26,11 @@ impl Widget for Pad {
|
||||
let inner = painter.widget_at(&self.inner, self.padding.region()).size();
|
||||
Size {
|
||||
x: LayoutLen {
|
||||
px: inner.x.px + self.padding.left + self.padding.right,
|
||||
px: inner.x.px + self.padding.along(Axis::X),
|
||||
..inner.x
|
||||
},
|
||||
y: LayoutLen {
|
||||
px: inner.y.px + self.padding.top + self.padding.bottom,
|
||||
px: inner.y.px + self.padding.along(Axis::Y),
|
||||
..inner.y
|
||||
},
|
||||
}
|
||||
@@ -65,6 +61,16 @@ impl Padding {
|
||||
bottom: amt,
|
||||
}
|
||||
}
|
||||
|
||||
/// Both sides of one axis together, which is what this padding takes
|
||||
/// of a length along it.
|
||||
pub fn along(&self, axis: Axis) -> Px {
|
||||
match axis {
|
||||
Axis::X => self.left + self.right,
|
||||
Axis::Y => self.top + self.bottom,
|
||||
}
|
||||
}
|
||||
|
||||
/// `region` less this padding on each side.
|
||||
pub fn region_of(&self, mut region: UiRegion) -> UiRegion {
|
||||
region.x.start.px += self.left;
|
||||
|
||||
+40
-28
@@ -14,11 +14,7 @@ impl Widget for Span {
|
||||
// cross-axis length then contributes nothing to the drawn answer.
|
||||
return None;
|
||||
}
|
||||
let mut total = RequestedLen::from(Len::from_parts(
|
||||
Rel::ZERO,
|
||||
self.gap
|
||||
.mul_int(self.children.len().saturating_sub(1) as i32),
|
||||
));
|
||||
let mut total = RequestedLen::from(Len::from_parts(Rel::ZERO, self.gaps()));
|
||||
for child in &self.children {
|
||||
let child = requests.widget(child, axis)?;
|
||||
total = requests.sum(total, child);
|
||||
@@ -45,9 +41,7 @@ impl Span {
|
||||
// the length alone.
|
||||
let row = painter.region_len(axis);
|
||||
self.collect(painter, row, lens, true);
|
||||
let gaps = self
|
||||
.gap
|
||||
.mul_int(self.children.len().saturating_sub(1) as i32);
|
||||
let gaps = self.gaps();
|
||||
let fixed = lens
|
||||
.iter()
|
||||
.try_fold(Len::from_parts(Rel::ZERO, gaps), |sum, len| {
|
||||
@@ -65,8 +59,8 @@ impl Span {
|
||||
if nonlinear {
|
||||
painter.allocate(lens, row - Len::from_parts(Rel::ZERO, gaps), axis, values);
|
||||
}
|
||||
let allocated = nonlinear.then_some(&values);
|
||||
let total = match &allocated {
|
||||
let allocated = nonlinear.then(|| &values[..]);
|
||||
let total = match allocated {
|
||||
Some(allocated) => LayoutLen {
|
||||
px: allocated.iter().fold(gaps, |sum, len| sum + *len),
|
||||
..LayoutLen::ZERO
|
||||
@@ -106,23 +100,27 @@ impl Span {
|
||||
true => fixed + room.scale(Rel::ratio(taken, total.leftover)),
|
||||
};
|
||||
for (index, (child, request)) in self.children.iter().zip(lens.iter()).enumerate() {
|
||||
let len = match &allocated {
|
||||
Some(allocated) => LayoutLen {
|
||||
px: allocated[index],
|
||||
..LayoutLen::ZERO
|
||||
},
|
||||
None => request.linear().unwrap(),
|
||||
// An allocated row already has a length for every child; without
|
||||
// one the request is the length and the room is divided here.
|
||||
// Either way a child asking for nothing but a part of what is
|
||||
// left over, when nothing is, is not drawn at all -- one that
|
||||
// also asked for pixels or a fraction keeps those and overflows.
|
||||
let (len, shares, nothing_left) = match allocated {
|
||||
Some(allocated) => {
|
||||
let len = LayoutLen {
|
||||
px: allocated[index],
|
||||
..LayoutLen::ZERO
|
||||
};
|
||||
let shares = request.has_leftover();
|
||||
(len, shares, shares && len.px == Px::ZERO)
|
||||
}
|
||||
None => {
|
||||
let len = request.linear().unwrap();
|
||||
let shares = len.leftover > Weight::ZERO && has_room;
|
||||
(len, shares, len.is_only_leftover() && !has_room)
|
||||
}
|
||||
};
|
||||
let shares = match &allocated {
|
||||
Some(_) => request.has_leftover(),
|
||||
None => len.leftover > Weight::ZERO && has_room,
|
||||
};
|
||||
// A child asking for nothing but a part of what is left over,
|
||||
// when nothing is, is not drawn at all. One that also asked for
|
||||
// pixels or a fraction keeps those and overflows.
|
||||
if (len.is_only_leftover() && !has_room)
|
||||
|| (allocated.is_some() && shares && len.px == Px::ZERO)
|
||||
{
|
||||
if nothing_left {
|
||||
painter.undraw(child);
|
||||
fixed.px += self.gap;
|
||||
continue;
|
||||
@@ -160,8 +158,15 @@ impl Span {
|
||||
fixed.px += self.gap;
|
||||
}
|
||||
|
||||
// Discovery carries nested requests to the allocating ancestor. The
|
||||
// draw still returns an ordinary Size for callers measuring content.
|
||||
// Where nothing was allocated the weight is carried whole rather
|
||||
// than collapsed to one share, so nesting spans divides the same
|
||||
// space rather than re-dividing a share of it: four `leftover(1)`
|
||||
// children under two spans under one span get a quarter each, which
|
||||
// one share per level does not give. Resolution happens at the
|
||||
// nearest ancestor with a length, and the root always has one --
|
||||
// or, where a comparison deferred the row, at the ancestor whose
|
||||
// allocation discovery carried these requests to, and `total` is
|
||||
// pixels by the time it gets here.
|
||||
let ortho = match shrinks {
|
||||
true => ortho,
|
||||
false => LayoutLen::rel(1.0),
|
||||
@@ -171,6 +176,13 @@ impl Span {
|
||||
}
|
||||
|
||||
impl Span {
|
||||
/// What the gaps between this span's children take, which is a length of
|
||||
/// the row before anything is divided.
|
||||
fn gaps(&self) -> Px {
|
||||
self.gap
|
||||
.mul_int(self.children.len().saturating_sub(1) as i32)
|
||||
}
|
||||
|
||||
fn collect(
|
||||
&self,
|
||||
painter: &mut Painter,
|
||||
|
||||
@@ -9,12 +9,15 @@ pub struct Stack {
|
||||
|
||||
impl Widget for Stack {
|
||||
fn size_request(&self, requests: &mut SizeRequests, axis: Axis) -> Option<RequestedLen> {
|
||||
match self.size {
|
||||
StackSize::Default => Some(LayoutLen::LEFTOVER.into()),
|
||||
StackSize::Child(i) => match self.children.get(i) {
|
||||
Some(child) => requests.widget(child, axis),
|
||||
None => Some(LayoutLen::LEFTOVER.into()),
|
||||
},
|
||||
let sizing = match self.size {
|
||||
StackSize::Default => None,
|
||||
StackSize::Child(i) => self.children.get(i),
|
||||
};
|
||||
// With nothing sizing it a stack is a share of the box it is given,
|
||||
// which is what its draw answers too.
|
||||
match sizing {
|
||||
Some(child) => requests.widget(child, axis),
|
||||
None => Some(LayoutLen::LEFTOVER.into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user