use crate::prelude::*; pub struct Span { pub children: Vec<(WidgetId, SpanLen)>, pub dir: Dir, } impl Widget for Span { fn draw(&mut self, painter: &mut Painter) { let total = self.setup(&mut painter.size_ctx()); let mut start = UiScalar::rel_min(); for (child, length) in &self.children { let mut child_region = UiRegion::full(); let mut axis = child_region.axis_mut(self.dir.axis); axis.top_left.set(start); match *length { SpanLen::Fixed(offset) => { start.abs += offset; } SpanLen::Ratio(ratio) => { let offset = UiScalar::new(total.rel, total.abs); let rel_end = UiScalar::from_anchor(ratio / total.ratio); start = rel_end.within(start, (UiScalar::rel_max() + start) - offset); } SpanLen::Relative(rel) => { start.rel += rel; } SpanLen::Sized(size) => { let size_axis = size.axis(self.dir.axis); start.abs += size_axis.abs; start.rel += size_axis.rel; } } axis.bot_right.set(start); if self.dir.sign == Sign::Neg { child_region.flip(self.dir.axis); } painter.widget_within(child, child_region); } } fn desired_size(&mut self, ctx: &mut SizeCtx) -> UiVec2 { let total = self.setup(ctx); let axis = self.dir.axis; let dir_len = if total.ratio != 0.0 { UiScalar::rel_max() } else { UiScalar::new(total.rel, total.abs) }; let mut max_ortho = UiScalar::ZERO; for (child, _) in &self.children { let size = ctx.size(child); max_ortho = max_ortho.max(size.axis(!self.dir.axis)); } UiVec2::from_axis(axis, dir_len, max_ortho) } } #[derive(Default)] pub struct SpanLenSums { pub abs: f32, pub ratio: f32, pub rel: f32, } impl Span { pub fn empty(dir: Dir) -> Self { Self { children: Vec::new(), dir, } } fn setup(&mut self, ctx: &mut SizeCtx) -> SpanLenSums { self.children .iter_mut() .fold(SpanLenSums::default(), |mut s, (id, l)| { match l { SpanLen::Fixed(v) => s.abs += *v, SpanLen::Ratio(v) => s.ratio += *v, SpanLen::Relative(v) => s.rel += *v, SpanLen::Sized(v) => { let size = ctx.size(id); let len = size.axis(self.dir.axis); *v = size; s.abs += len.abs; s.rel += len.rel; } } s }) } } #[derive(Clone, Copy)] pub enum SpanLen { /// exact (non dynamic) size Fixed(f32), /// relative to remaining free space and other ratios /// eg. 1 and 2 would take up 1/3 and 2/3 of the remaining space (after others) Ratio(f32), /// relative to the total space (of the entire span) /// eg. 0.5 means 1/2 of the total space Relative(f32), /// size determined by the child widget itself /// the value is not used externally, I just don't wanna make a duplicate enum /// there are util functions instead so Sized(UiVec2), } pub fn fixed(x: N) -> SpanLen { SpanLen::Fixed(x.to_f32()) } pub fn ratio(x: N) -> SpanLen { SpanLen::Ratio(x.to_f32()) } pub fn relative(x: N) -> SpanLen { SpanLen::Relative(x.to_f32()) } pub fn sized() -> SpanLen { SpanLen::Sized(UiVec2::default()) } impl From for SpanLen { fn from(value: N) -> Self { Self::Ratio(value.to_f32()) } }