use std::marker::PhantomData; use crate::prelude::*; pub struct Stack { pub children: Vec, 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), }; // Whichever child sizes the stack decides the box every child gets. // The stack reports that size, so a child given a longer box would // draw outside what the stack says it occupies. 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(child).size() } None => Size::LEFTOVER, }; let region = painter.box_of(size); for (i, child) in self.children.iter().enumerate() { painter.child_layer_at(i); // The sizing child placed its own content in the box its answer // decided, and this box was derived from that answer, so applying // its alignment again here would place it twice. Every other // child is handed a box that owes nothing to its own answer, and // where it sits in one bigger than itself is its own business. match sizing == Some(i) { true => painter.widget_at(child, region, region.size(), [true; 2]), false => painter.widget_within(child, region), }; } size } } #[derive(Default, Debug)] pub enum StackSize { #[default] Default, Child(usize), } pub struct StackBuilder, Tag> { pub children: Wa, pub size: StackSize, _pd: PhantomData<(State, Tag)>, } impl, Tag> WidgetFnTrait for StackBuilder { 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, Tag> StackBuilder { 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 } }