use crate::prelude::*; use std::marker::PhantomData; pub struct Span { pub children: Vec, pub dir: Dir, pub gap: f32, } impl Widget for Span { fn draw(&mut self, painter: &mut Painter) { let total = self.len_sum(&mut painter.size_ctx()); let mut start = UiScalar::rel_min(); for child in &self.children { let mut span = UiSpan::FULL; span.start = start; let len = painter.len_axis(child, self.dir.axis); if len.rest > 0.0 { let offset = UiScalar::new(total.rel, total.abs); let rel_end = UiScalar::rel(len.rest / total.rest); let end = (UiScalar::rel_max() + start) - offset; start = rel_end.within(&start.to(end)); } start.abs += len.abs; start.rel += len.rel; span.end = start; let mut child_region = UiRegion::from_axis(self.dir.axis, span, UiSpan::FULL); if self.dir.sign == Sign::Neg { child_region.flip(self.dir.axis); } painter.widget_within(child, child_region); start.abs += self.gap; } } fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { match self.dir.axis { Axis::X => self.desired_len(ctx), Axis::Y => self.desired_ortho(ctx), } } fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { match self.dir.axis { Axis::X => self.desired_ortho(ctx), Axis::Y => self.desired_len(ctx), } } } impl Span { pub fn empty(dir: Dir) -> Self { Self { children: Vec::new(), dir, gap: 0.0, } } pub fn gap(mut self, gap: impl UiNum) -> Self { self.gap = gap.to_f32(); self } fn len_sum(&mut self, ctx: &mut SizeCtx) -> Len { let gap = self.gap * self.children.len().saturating_sub(1) as f32; self.children.iter().fold(Len::abs(gap), |mut s, id| { // it's tempting to subtract the abs & rel from the ctx outer, // but that would create inconsistent sizing if you put // a rest first vs last & only speed up in one direction. // I think this is only solvable by restricting how you can // compute size, bc currently you need child to define parent's // sectioning and you need parent's sectioning to define child. // Fortunately, that doesn't matter in most cases let len = ctx.len_axis(id, self.dir.axis); s += len; s }) } fn desired_len(&mut self, ctx: &mut SizeCtx) -> Len { let len = self.len_sum(ctx); if len.rest == 0.0 && len.rel == 0.0 { len } else { Len::default() } } fn desired_ortho(&mut self, ctx: &mut SizeCtx) -> Len { // this is a weird hack to get text wrapping to work properly when in a downward span // the correct solution here is to add a function to widget that lets them // request that ctx.outer has an axis "resolved" before checking the other, // and panicking or warning if two request opposite axis (unsolvable in that case) let outer = ctx.outer.axis(self.dir.axis); if self.dir.axis == Axis::X { // so....... this literally copies draw so that the lengths are correctly set in the // context, which makes this slow and not cool let total = self.len_sum(ctx); let mut start = UiScalar::rel_min(); let mut ortho_len = Len::ZERO; for child in &self.children { let mut span = UiSpan::FULL; span.start = start; let len = ctx.len_axis(child, self.dir.axis); if len.rest > 0.0 { let offset = UiScalar::new(total.rel, total.abs); let rel_end = UiScalar::rel(len.rest / total.rest); let end = (UiScalar::rel_max() + start) - offset; start = rel_end.within(&start.to(end)); } start.abs += len.abs; start.rel += len.rel; span.end = start; let scalar = span.len(); *ctx.outer.axis_mut(self.dir.axis) = outer.select_len(scalar); let ortho = ctx.len_axis(child, !self.dir.axis); // TODO: rel shouldn't do this, but no easy way before actually calculating pixels if ortho.rel > 0.0 || ortho.rest > 0.0 { ortho_len.rest = 1.0; ortho_len.abs = 0.0; break; } ortho_len.abs = ortho_len.abs.max(ortho.abs); start.abs += self.gap; } ortho_len } else { let mut ortho_len = Len::ZERO; let ortho = !self.dir.axis; for child in &self.children { let len = ctx.len_axis(child, ortho); // TODO: rel shouldn't do this, but no easy way before actually calculating pixels if len.rel > 0.0 || len.rest > 0.0 { ortho_len.rest = 1.0; ortho_len.abs = 0.0; break; } ortho_len.abs = ortho_len.abs.max(len.abs); } ortho_len } } } pub struct SpanBuilder, Tag> { pub children: Wa, pub dir: Dir, pub gap: f32, _pd: PhantomData, } impl, Tag> FnOnce<(&mut Ui,)> for SpanBuilder { type Output = Span; extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output { Span { children: self.children.ui(args.0).arr.to_vec(), dir: self.dir, gap: self.gap, } } } impl, Tag> SpanBuilder { pub fn new(children: Wa, dir: Dir) -> Self { Self { children, dir, gap: 0.0, _pd: PhantomData, } } pub fn gap(mut self, gap: impl UiNum) -> Self { self.gap = gap.to_f32(); self } } impl std::ops::Deref for Span { type Target = Vec; fn deref(&self) -> &Self::Target { &self.children } } impl std::ops::DerefMut for Span { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.children } }