The protocol split: `region` is the box a parent gives a widget -- what a fraction it declares or reports is a fraction of, and the coordinates every region it writes composes within -- and it is the same box on the ask that measures and the ask that places. `placement` is what of that region the drawing takes, chosen by the parent per axis or by the widget's own answer and alignment. That is what stops a fraction being resolved twice: the placing ask no longer hands the widget its own answer as its box, so nothing under it re-resolves against a box that came from its own report. `reports_of` and `decided` are gone, folded into the two regions; `box_of` is gone; `declared_box` becomes `ask_box`, which gives a rule the region's length and takes the position from the placement. 84 of 92 suite tests pass. Five text and region-node cases still diverge warm against cold, and three count a second widget draw where a span's measuring ask and its placing ask give different placements.
95 lines
2.8 KiB
Rust
95 lines
2.8 KiB
Rust
use std::marker::PhantomData;
|
|
|
|
use crate::prelude::*;
|
|
|
|
pub struct Stack {
|
|
pub children: Vec<StrongWidget>,
|
|
pub size: StackSize,
|
|
}
|
|
|
|
impl Widget for Stack {
|
|
fn draw(&mut self, painter: &mut Painter) -> Size {
|
|
let sizing = match self.size {
|
|
StackSize::Default => None,
|
|
StackSize::Child(i) => Some(i),
|
|
};
|
|
// This stack's own box, which is `FULL` until its answer is known.
|
|
let placement = painter.placement();
|
|
// Whichever child sizes the stack keeps the stack's whole region as
|
|
// its own -- the stack is the length that child asked for, so taking
|
|
// the fraction of the stack's box again would take it twice -- and is
|
|
// put where the stack itself is put.
|
|
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,
|
|
UiRegion::FULL,
|
|
[Some(placement.x), Some(placement.y)],
|
|
)
|
|
.size()
|
|
}
|
|
None => Size::LEFTOVER,
|
|
};
|
|
for (i, child) in self.children.iter().enumerate() {
|
|
if sizing == Some(i) {
|
|
continue;
|
|
}
|
|
painter.child_layer_at(i);
|
|
// Every other child has the stack's own box for its region, since
|
|
// the stack is what contains it, and where it sits in one bigger
|
|
// than itself is its own business.
|
|
painter.widget_within(child, placement);
|
|
}
|
|
size
|
|
}
|
|
}
|
|
|
|
#[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
|
|
}
|
|
}
|