197 lines
6.5 KiB
Rust
197 lines
6.5 KiB
Rust
use crate::prelude::*;
|
|
use std::marker::PhantomData;
|
|
|
|
pub struct Span {
|
|
pub children: Vec<WidgetId>,
|
|
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<const LEN: usize, Wa: WidgetArrLike<LEN, Tag>, Tag> {
|
|
pub children: Wa,
|
|
pub dir: Dir,
|
|
pub gap: f32,
|
|
_pd: PhantomData<Tag>,
|
|
}
|
|
|
|
impl<const LEN: usize, Wa: WidgetArrLike<LEN, Tag>, Tag> FnOnce<(&mut Ui,)>
|
|
for SpanBuilder<LEN, Wa, Tag>
|
|
{
|
|
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<const LEN: usize, Wa: WidgetArrLike<LEN, Tag>, Tag> SpanBuilder<LEN, Wa, Tag> {
|
|
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<WidgetId>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.children
|
|
}
|
|
}
|
|
|
|
impl std::ops::DerefMut for Span {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.children
|
|
}
|
|
}
|