Files
iris/src/widget/position/stack.rs
T
iris-aiandClaude Opus 5 40b89c1f79 Say from_axes, which is what a constructor is called
`per_axis` on `PlaceDesc` and `Declared` builds a pair by asking for each
axis. `from_axis` beside it already names the three-argument form, so the
plural is the one that takes a function, and both follow Rust's convention
for a constructor rather than an invented word.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 21:53:58 -04:00

104 lines
3.2 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),
};
// 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
}
}