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.
117 lines
3.7 KiB
Rust
117 lines
3.7 KiB
Rust
use std::marker::PhantomData;
|
|
|
|
use crate::prelude::*;
|
|
|
|
pub struct Stack {
|
|
pub children: Vec<StrongWidget>,
|
|
pub size: StackSize,
|
|
}
|
|
|
|
impl Widget for Stack {
|
|
fn size_request(&self, requests: &mut SizeRequests, axis: Axis) -> Option<RequestedLen> {
|
|
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()),
|
|
}
|
|
}
|
|
|
|
fn draw(&mut self, painter: &mut Painter) -> Size {
|
|
let sizing = match self.size {
|
|
StackSize::Default => None,
|
|
StackSize::Child(i) => Some(i),
|
|
};
|
|
// Whichever child sizes the stack is given the stack's whole box --
|
|
// the stack is the length that child asked for, so placing that
|
|
// answer inside the box it decided would apply it twice.
|
|
let size = match sizing.and_then(|i| self.children.get(i).map(|c| (i, c))) {
|
|
// On the layer that child ends up on, so the ask below is a reuse
|
|
// rather than a second drawing of it somewhere else: a retained
|
|
// drawing belongs to the layer it was made on.
|
|
Some((i, child)) => {
|
|
painter.child_layer_at(i);
|
|
painter.widget_at(child, PlaceDesc::WHOLE.fills()).size()
|
|
}
|
|
None => Size::LEFTOVER,
|
|
};
|
|
// Every other child gets the box the sizing child decided: the
|
|
// stack is that length, so that is the box they are asked in, and a
|
|
// fraction under them is a fraction of it. A share leaves the axis
|
|
// to whoever gave the stack its box. Where a child sits in a box
|
|
// bigger than itself is its own business.
|
|
let place = PlaceDesc::from_axes(|axis| {
|
|
let len = size[axis];
|
|
match len.leftover == Weight::ZERO {
|
|
true => len.without_leftover().as_desc().fills(),
|
|
false => PlaceDescAxis::WHOLE,
|
|
}
|
|
});
|
|
for (i, child) in self.children.iter().enumerate() {
|
|
if sizing == Some(i) {
|
|
continue;
|
|
}
|
|
painter.child_layer_at(i);
|
|
painter.widget_at(child, place);
|
|
}
|
|
size
|
|
}
|
|
|
|
/// Without a sizing child a stack is whatever box it is given, which it
|
|
/// can say without drawing anything.
|
|
fn size_hint(&self, _: Axis) -> Option<LayoutLen> {
|
|
match self.size {
|
|
StackSize::Default => Some(LayoutLen::LEFTOVER),
|
|
StackSize::Child(_) => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Default, Debug)]
|
|
pub enum StackSize {
|
|
#[default]
|
|
Default,
|
|
Child(usize),
|
|
}
|
|
|
|
pub struct StackBuilder<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Tag>, Tag> {
|
|
pub children: Wa,
|
|
pub size: StackSize,
|
|
_pd: PhantomData<(State, Tag)>,
|
|
}
|
|
|
|
impl<Rsc, const LEN: usize, Wa: WidgetArrLike<Rsc, LEN, Tag>, Tag> WidgetFnTrait<Rsc>
|
|
for StackBuilder<Rsc, LEN, Wa, Tag>
|
|
{
|
|
type Widget = Stack;
|
|
|
|
#[track_caller]
|
|
fn run(self, rsc: &mut Rsc) -> Self::Widget {
|
|
Stack {
|
|
children: self.children.add(rsc).arr.into_iter().collect(),
|
|
size: self.size,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Tag>, Tag>
|
|
StackBuilder<State, LEN, Wa, Tag>
|
|
{
|
|
pub fn new(children: Wa) -> Self {
|
|
Self {
|
|
children,
|
|
size: StackSize::default(),
|
|
_pd: PhantomData,
|
|
}
|
|
}
|
|
|
|
pub fn size(mut self, size: StackSize) -> Self {
|
|
self.size = size;
|
|
self
|
|
}
|
|
}
|