use std::marker::PhantomData; use crate::prelude::*; 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 child_region = UiRegion::full(); let mut axis = child_region.axis_mut(self.dir.axis); axis.top_left.set(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::from_anchor(len.rest / total.rest); start = rel_end.within(start, (UiScalar::rel_max() + start) - offset); } start.abs += len.abs; start.rel += len.rel; axis.bot_right.set(start); 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 an awful hack to get text wrapping to work properly when in a downward span 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 outer = ctx.outer.axis(self.dir.axis); let mut ortho_len = Len::ZERO; for child in &self.children { let mut child_region = UiRegion::full(); let mut axis = child_region.axis_mut(self.dir.axis); axis.top_left.set(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::from_anchor(len.rest / total.rest); start = rel_end.within(start, (UiScalar::rel_max() + start) - offset); } start.abs += len.abs; start.rel += len.rel; axis.bot_right.set(start); // if self.dir.sign == Sign::Neg { // child_region.flip(self.dir.axis); // } let scalar = child_region.size().axis(self.dir.axis); ctx.outer .axis_mut(self.dir.axis) .set(scalar.within_len(outer)); 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 } }