Files
iris/src/core/span.rs
2025-08-20 12:18:44 -04:00

100 lines
2.8 KiB
Rust

use crate::{Dir, Painter, Sign, UiNum, UiRegion, UIScalar, Widget, WidgetId};
pub struct Span {
pub children: Vec<(WidgetId, SpanLen)>,
pub dir: Dir,
}
impl<Ctx: 'static> Widget<Ctx> for Span {
fn draw(&self, painter: &mut Painter<Ctx>) {
let total = self.sums();
let mut start = UIScalar::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.offset += offset;
*axis.bot_right.offset = start.offset;
*axis.bot_right.anchor = *axis.top_left.anchor;
}
SpanLen::Ratio(ratio) => {
let offset = UIScalar::new(total.relative, total.fixed);
let rel_end = UIScalar::from_anchor(ratio / total.ratio);
start = rel_end.within(start, (UIScalar::max() + start) - offset);
axis.bot_right.set(start);
}
SpanLen::Relative(rel) => {
start.anchor += rel;
axis.bot_right.set(start);
}
}
if self.dir.sign == Sign::Neg {
child_region.flip();
}
painter.draw_within(child, child_region);
}
}
}
#[derive(Default)]
pub struct SpanLenSums {
pub fixed: f32,
pub ratio: f32,
pub relative: f32,
}
impl Span {
pub fn empty(dir: Dir) -> Self {
Self {
children: Vec::new(),
dir,
}
}
pub fn sums(&self) -> SpanLenSums {
self.lengths().fold(SpanLenSums::default(), |mut s, l| {
match l {
SpanLen::Fixed(v) => s.fixed += v,
SpanLen::Ratio(v) => s.ratio += v,
SpanLen::Relative(v) => s.relative += v,
}
s
})
}
pub fn lengths(&self) -> impl ExactSizeIterator<Item = &SpanLen> {
self.children.iter().map(|(_, s)| 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),
}
pub fn fixed<N: UiNum>(x: N) -> SpanLen {
SpanLen::Fixed(x.to_f32())
}
pub fn ratio<N: UiNum>(x: N) -> SpanLen {
SpanLen::Ratio(x.to_f32())
}
pub fn rel<N: UiNum>(x: N) -> SpanLen {
SpanLen::Relative(x.to_f32())
}
impl<N: UiNum> From<N> for SpanLen {
fn from(value: N) -> Self {
Self::Ratio(value.to_f32())
}
}