use crate::*; /// Standard numeric type. pub trait Coordinate: Send + Sync + Copy + Add + Sub + Mul + Div + Ord + PartialEq + Eq + Debug + Display + Default + From + Into + Into + Into { fn minus (self, other: Self) -> Self { if self >= other { self - other } else { 0.into() } } } impl Coordinate for T where T: Send + Sync + Copy + Add + Sub + Mul + Div + Ord + PartialEq + Eq + Debug + Display + Default + From + Into + Into + Into {} // TODO: return impl Point and impl Size instead of [N;x] // to disambiguate between usage of 2-"tuple"s pub trait Size { fn x (&self) -> N; fn y (&self) -> N; #[inline] fn w (&self) -> N { self.x() } #[inline] fn h (&self) -> N { self.y() } #[inline] fn wh (&self) -> [N;2] { [self.x(), self.y()] } #[inline] fn clip_w (&self, w: N) -> [N;2] { [self.w().min(w.into()), self.h()] } #[inline] fn clip_h (&self, h: N) -> [N;2] { [self.w(), self.h().min(h.into())] } #[inline] fn expect_min (&self, w: N, h: N) -> Usually<&Self> { if self.w() < w || self.h() < h { Err(format!("min {w}x{h}").into()) } else { Ok(self) } } } impl Size for (N, N) { fn x (&self) -> N { self.0 } fn y (&self) -> N { self.1 } } impl Size for [N;2] { fn x (&self) -> N { self[0] } fn y (&self) -> N { self[1] } } pub trait Area: Copy { fn x (&self) -> N; fn y (&self) -> N; fn w (&self) -> N; fn h (&self) -> N; fn x2 (&self) -> N { self.x() + self.w() } fn y2 (&self) -> N { self.y() + self.h() } #[inline] fn wh (&self) -> [N;2] { [self.w(), self.h()] } #[inline] fn xywh (&self) -> [N;4] { [self.x(), self.y(), self.w(), self.h()] } #[inline] fn lrtb (&self) -> [N;4] { [self.x(), self.x2(), self.y(), self.y2()] } #[inline] fn push_x (&self, x: N) -> [N;4] { [self.x() + x, self.y(), self.w(), self.h()] } #[inline] fn push_y (&self, y: N) -> [N;4] { [self.x(), self.y() + y, self.w(), self.h()] } #[inline] fn shrink_x (&self, x: N) -> [N;4] { [self.x(), self.y(), self.w() - x, self.h()] } #[inline] fn shrink_y (&self, y: N) -> [N;4] { [self.x(), self.y(), self.w(), self.h() - y] } #[inline] fn set_w (&self, w: N) -> [N;4] { [self.x(), self.y(), w, self.h()] } #[inline] fn set_h (&self, h: N) -> [N;4] { [self.x(), self.y(), self.w(), h] } #[inline] fn clip_h (&self, h: N) -> [N;4] { [self.x(), self.y(), self.w(), self.h().min(h.into())] } #[inline] fn clip_w (&self, w: N) -> [N;4] { [self.x(), self.y(), self.w().min(w.into()), self.h()] } #[inline] fn clip (&self, wh: impl Size) -> [N;4] { [self.x(), self.y(), wh.w(), wh.h()] } #[inline] fn expect_min (&self, w: N, h: N) -> Usually<&Self> { if self.w() < w || self.h() < h { Err(format!("min {w}x{h}").into()) } else { Ok(self) } } #[inline] fn split_fixed (&self, direction: Direction, a: N) -> ([N;4],[N;4]) { match direction { Direction::Up => ( [self.x(), (self.y()+self.h()).minus(a), self.w(), a], [self.x(), self.y(), self.w(), self.h().minus(a)], ), Direction::Down => ( [self.x(), self.y(), self.w(), a], [self.x(), self.y() + a, self.w(), self.h().minus(a)], ), Direction::Right => ( [self.x(), self.y(), a, self.h()], [self.x() + a, self.y(), self.w().minus(a), self.h()], ), _ => todo!(), } } } impl Area for (N, N, N, N) { #[inline] fn x (&self) -> N { self.0 } #[inline] fn y (&self) -> N { self.1 } #[inline] fn w (&self) -> N { self.2 } #[inline] fn h (&self) -> N { self.3 } } impl Area for [N;4] { #[inline] fn x (&self) -> N { self[0] } #[inline] fn y (&self) -> N { self[1] } #[inline] fn w (&self) -> N { self[2] } #[inline] fn h (&self) -> N { self[3] } } pub trait Layout: Widget + Sized { fn align_center (self) -> Align { Align::Center(self) } fn align_n (self) -> Align { Align::N(self) } fn align_s (self) -> Align { Align::S(self) } fn align_e (self) -> Align { Align::E(self) } fn align_w (self) -> Align { Align::W(self) } fn align_nw (self) -> Align { Align::NW(self) } fn align_sw (self) -> Align { Align::SW(self) } fn align_ne (self) -> Align { Align::NE(self) } fn align_se (self) -> Align { Align::SE(self) } fn align_x (self) -> Align { Align::X(self) } fn align_y (self) -> Align { Align::Y(self) } fn fixed_x (self, x: E::Unit) -> Fixed { Fixed::X(x, self) } fn fixed_y (self, y: E::Unit) -> Fixed { Fixed::Y(y, self) } fn fixed_xy (self, x: E::Unit, y: E::Unit) -> Fixed { Fixed::XY(x, y, self) } fn min_x (self, x: E::Unit) -> Min { Min::X(x, self) } fn min_y (self, y: E::Unit) -> Min { Min::Y(y, self) } fn min_xy (self, x: E::Unit, y: E::Unit) -> Min { Min::XY(x, y, self) } fn max_x (self, x: E::Unit) -> Max { Max::X(x, self) } fn max_y (self, y: E::Unit) -> Max { Max::Y(y, self) } fn max_xy (self, x: E::Unit, y: E::Unit) -> Max { Max::XY(x, y, self) } fn push_x (self, x: E::Unit) -> Push { Push::X(x, self) } fn push_y (self, y: E::Unit) -> Push { Push::Y(y, self) } fn push_xy (self, x: E::Unit, y: E::Unit) -> Push { Push::XY(x, y, self) } fn pull_x (self, x: E::Unit) -> Pull { Pull::X(x, self) } fn pull_y (self, y: E::Unit) -> Pull { Pull::Y(y, self) } fn pull_xy (self, x: E::Unit, y: E::Unit) -> Pull { Pull::XY(x, y, self) } fn grow_x (self, x: E::Unit) -> Grow { Grow::X(x, self) } fn grow_y (self, y: E::Unit) -> Grow { Grow::Y(y, self) } fn grow_xy (self, x: E::Unit, y: E::Unit) -> Grow { Grow::XY(x, y, self) } fn shrink_x (self, x: E::Unit) -> Shrink { Shrink::X(x, self) } fn shrink_y (self, y: E::Unit) -> Shrink { Shrink::Y(y, self) } fn shrink_xy (self, x: E::Unit, y: E::Unit) -> Shrink { Shrink::XY(x, y, self) } fn inset_x (self, x: E::Unit) -> Inset { Inset::X(x, self) } fn inset_y (self, y: E::Unit) -> Inset { Inset::Y(y, self) } fn inset_xy (self, x: E::Unit, y: E::Unit) -> Inset { Inset::XY(x, y, self) } fn outset_x (self, x: E::Unit) -> Outset { Outset::X(x, self) } fn outset_y (self, y: E::Unit) -> Outset { Outset::Y(y, self) } fn outset_xy (self, x: E::Unit, y: E::Unit) -> Outset { Outset::XY(x, y, self) } fn fill_x (self) -> Fill { Fill::X(self) } fn fill_y (self) -> Fill { Fill::Y(self) } fn fill_xy (self) -> Fill { Fill::XY(self) } fn debug (self) -> DebugOverlay { DebugOverlay(self) } fn split > ( self, direction: Direction, amount: E::Unit, other: W ) -> Split { Split::new(direction, amount, self, other) } fn split_flip > ( self, direction: Direction, amount: E::Unit, other: W ) -> Split { Split::new(direction, amount, other, self) } } impl> Layout for W {} pub struct DebugOverlay>(pub W); pub enum Fill> { X(W), Y(W), XY(W) } impl> Fill { fn inner (&self) -> &W { match self { Self::X(inner) => &inner, Self::Y(inner) => &inner, Self::XY(inner) => &inner, } } } impl> Widget for Fill { type Engine = E; fn layout (&self, to: E::Size) -> Perhaps { let area = self.inner().layout(to.into())?; if let Some(area) = area { Ok(Some(match self { Self::X(_) => [to.w().into(), area.h()], Self::Y(_) => [area.w(), to.h().into()], Self::XY(_) => [to.w().into(), to.h().into()], }.into())) } else { Ok(None) } } fn render (&self, to: &mut E::Output) -> Usually<()> { self.inner().render(to) } } pub struct Layers< E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Widget)->Usually<()>)->Usually<()> >(pub F, PhantomData); impl< E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Widget)->Usually<()>)->Usually<()> > Layers { #[inline] pub fn new (build: F) -> Self { Self(build, Default::default()) } } impl Widget for Layers where F: Send + Sync + Fn(&mut dyn FnMut(&dyn Widget)->Usually<()>)->Usually<()> { type Engine = E; fn layout (&self, area: E::Size) -> Perhaps { let mut w: E::Unit = 0.into(); let mut h: E::Unit = 0.into(); (self.0)(&mut |layer| { if let Some(layer_area) = layer.layout(area)? { w = w.max(layer_area.w()); h = h.max(layer_area.h()); } Ok(()) })?; Ok(Some([w, h].into())) } fn render (&self, to: &mut E::Output) -> Usually<()> { if let Some(size) = self.layout(to.area().wh().into())? { (self.0)(&mut |layer|to.render_in(to.area().clip(size).into(), &layer)) } else { Ok(()) } } } #[derive(Copy, Clone, PartialEq)] pub enum Direction { Up, Down, Left, Right, } impl Direction { pub fn is_up (&self) -> bool { match self { Self::Up => true, _ => false } } pub fn is_down (&self) -> bool { match self { Self::Down => true, _ => false } } pub fn is_left (&self) -> bool { match self { Self::Left => true, _ => false } } pub fn is_right (&self) -> bool { match self { Self::Right => true, _ => false } } /// Return next direction clockwise pub fn cw (&self) -> Self { match self { Self::Up => Self::Right, Self::Down => Self::Left, Self::Left => Self::Up, Self::Right => Self::Down, } } /// Return next direction counterclockwise pub fn ccw (&self) -> Self { match self { Self::Up => Self::Left, Self::Down => Self::Right, Self::Left => Self::Down, Self::Right => Self::Up, } } } /// Override X and Y coordinates, aligning to corner, side, or center of area pub enum Align { /// Draw at center of container Center(L), /// Draw at center of X axis X(L), /// Draw at center of Y axis Y(L), /// Draw at upper left corner of contaier NW(L), /// Draw at center of upper edge of container N(L), /// Draw at right left corner of contaier NE(L), /// Draw at center of left edge of container W(L), /// Draw at center of right edge of container E(L), /// Draw at lower left corner of container SW(L), /// Draw at center of lower edge of container S(L), /// Draw at lower right edge of container SE(L) } impl Align { pub fn inner (&self) -> &T { match self { Self::Center(inner) => inner, Self::X(inner) => inner, Self::Y(inner) => inner, Self::NW(inner) => inner, Self::N(inner) => inner, Self::NE(inner) => inner, Self::W(inner) => inner, Self::E(inner) => inner, Self::SW(inner) => inner, Self::S(inner) => inner, Self::SE(inner) => inner, } } } fn align + From<[N;4]>> (align: &Align, outer: R, inner: R) -> Option { if outer.w() < inner.w() || outer.h() < inner.h() { None } else { let [ox, oy, ow, oh] = outer.xywh(); let [ix, iy, iw, ih] = inner.xywh(); Some(match align { Align::Center(_) => [ox + (ow - iw) / 2.into(), oy + (oh - ih) / 2.into(), iw, ih,].into(), Align::X(_) => [ox + (ow - iw) / 2.into(), iy, iw, ih,].into(), Align::Y(_) => [ix, oy + (oh - ih) / 2.into(), iw, ih,].into(), Align::NW(_) => [ox, oy, iw, ih,].into(), Align::N(_) => [ox + (ow - iw) / 2.into(), oy, iw, ih,].into(), Align::NE(_) => [ox + ow - iw, oy, iw, ih,].into(), Align::W(_) => [ox, oy + (oh - ih) / 2.into(), iw, ih,].into(), Align::E(_) => [ox + ow - iw, oy + (oh - ih) / 2.into(), iw, ih,].into(), Align::SW(_) => [ox, oy + oh - ih, iw, ih,].into(), Align::S(_) => [ox + (ow - iw) / 2.into(), oy + oh - ih, iw, ih,].into(), Align::SE(_) => [ox + ow - iw, oy + oh - ih, iw, ih,].into(), }) } } impl> Widget for Align { type Engine = E; fn layout (&self, outer_area: E::Size) -> Perhaps { self.inner().layout(outer_area) } fn render (&self, to: &mut E::Output) -> Usually<()> { let outer_area = to.area(); Ok(if let Some(inner_size) = self.layout(outer_area.wh().into())? { let inner_area = outer_area.clip(inner_size); if let Some(aligned) = align(&self, outer_area.into(), inner_area.into()) { to.render_in(aligned, self.inner())? } else { () } } else { () }) } } /// Enforce fixed size of drawing area pub enum Fixed { /// Enforce fixed width X(U, T), /// Enforce fixed height Y(U, T), /// Enforce fixed width and height XY(U, U, T), } impl Fixed { pub fn inner (&self) -> &T { match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, } } } impl> Widget for Fixed { type Engine = E; fn layout (&self, to: E::Size) -> Perhaps { Ok(match self { Self::X(w, _) => if to.w() >= *w { Some([*w, to.h()].into()) } else { None }, Self::Y(h, _) => if to.h() >= *h { Some([to.w(), *h].into()) } else { None }, Self::XY(w, h, _) => if to.w() >= *w && to.h() >= *h { Some([*w, *h].into()) } else { None }, }) } fn render (&self, to: &mut E::Output) -> Usually<()> { // 🡘 🡙 ←🡙→ if let Some(size) = self.layout(to.area().wh().into())? { to.render_in(to.area().clip(size).into(), self.inner()) } else { Ok(()) } } } /// Enforce minimum size of drawing area pub enum Min { /// Enforce minimum width X(U, T), /// Enforce minimum height Y(U, T), /// Enforce minimum width and height XY(U, U, T), } impl Min { pub fn inner (&self) -> &T { match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, } } } impl> Widget for Min { type Engine = E; fn layout (&self, to: E::Size) -> Perhaps { Ok(self.inner().layout(to)?.map(|to|match *self { Self::X(w, _) => [to.w().max(w), to.h()], Self::Y(h, _) => [to.w(), to.h().max(h)], Self::XY(w, h, _) => [to.w().max(w), to.h().max(h)], }.into())) } // TODO: 🡘 🡙 ←🡙→ fn render (&self, to: &mut E::Output) -> Usually<()> { Ok(self.layout(to.area().wh().into())? .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) .transpose()?.unwrap_or(())) } } /// Enforce maximum size of drawing area pub enum Max { /// Enforce maximum width X(U, T), /// Enforce maximum height Y(U, T), /// Enforce maximum width and height XY(U, U, T), } impl Max { fn inner (&self) -> &T { match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, } } } impl> Widget for Max { type Engine = E; fn layout (&self, to: E::Size) -> Perhaps { Ok(self.inner().layout(to)?.map(|to|match *self { Self::X(w, _) => [to.w().min(w), to.h()], Self::Y(h, _) => [to.w(), to.h().min(h)], Self::XY(w, h, _) => [to.w().min(w), to.h().min(h)], }.into())) } fn render (&self, to: &mut E::Output) -> Usually<()> { Ok(self.layout(to.area().wh().into())? .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) .transpose()?.unwrap_or(())) } } /// Expand drawing area pub enum Grow { /// Increase width X(N, T), /// Increase height Y(N, T), /// Increase width and height XY(N, N, T) } impl Grow { fn inner (&self) -> &T { match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, } } } impl> Widget for Grow { type Engine = E; fn layout (&self, to: E::Size) -> Perhaps { Ok(self.inner().layout(to)?.map(|to|match *self { Self::X(w, _) => [to.w() + w, to.h()], Self::Y(h, _) => [to.w(), to.h() + h], Self::XY(w, h, _) => [to.w() + w, to.h() + h], }.into())) } fn render (&self, to: &mut E::Output) -> Usually<()> { Ok(self.layout(to.area().wh().into())? .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) .transpose()?.unwrap_or(())) } } /// Shrink drawing area pub enum Shrink { /// Decrease width X(N, T), /// Decrease height Y(N, T), /// Decrease width and height XY(N, N, T), } impl Shrink { fn inner (&self) -> &T { match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, } } } impl> Widget for Shrink { type Engine = E; fn layout (&self, to: E::Size) -> Perhaps { Ok(self.inner().layout(to)?.map(|to|match *self { Self::X(w, _) => [ if to.w() > w { to.w() - w } else { 0.into() }, to.h() ], Self::Y(h, _) => [ to.w(), if to.h() > h { to.h() - h } else { 0.into() } ], Self::XY(w, h, _) => [ if to.w() > w { to.w() - w } else { 0.into() }, if to.h() > h { to.h() - h } else { 0.into() } ] }.into())) } fn render (&self, to: &mut E::Output) -> Usually<()> { Ok(self.layout(to.area().wh().into())? .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) .transpose()?.unwrap_or(())) } } /// Shrink from each side pub enum Inset { /// Decrease width X(N, T), /// Decrease height Y(N, T), /// Decrease width and height XY(N, N, T), } impl Inset { pub fn inner (&self) -> &T { match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, } } } /// Grow on each side pub enum Outset { /// Increase width X(N, T), /// Increase height Y(N, T), /// Increase width and height XY(N, N, T), } impl Outset { pub fn inner (&self) -> &T { match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, } } } impl> Widget for Inset { type Engine = E; fn render (&self, to: &mut E::Output) -> Usually<()> { match *self { Self::X(x, ref inner) => (inner as &dyn Widget).shrink_x(x).push_x(x), Self::Y(y, ref inner) => (inner as &dyn Widget).shrink_y(y).push_y(y), Self::XY(x, y, ref inner) => (inner as &dyn Widget).shrink_xy(x, y).push_xy(x, y) }.render(to) } } impl> Widget for Outset { type Engine = E; fn layout (&self, to: E::Size) -> Perhaps { match *self { Self::X(x, ref inner) => (inner as &dyn Widget).grow_x(x + x), Self::Y(y, ref inner) => (inner as &dyn Widget).grow_y(y + y), Self::XY(x, y, ref inner) => (inner as &dyn Widget).grow_xy(x + x, y + y), }.layout(to) } fn render (&self, to: &mut E::Output) -> Usually<()> { match *self { Self::X(x, ref inner) => (inner as &dyn Widget).push_x(x), Self::Y(y, ref inner) => (inner as &dyn Widget).push_y(y), Self::XY(x, y, ref inner) => (inner as &dyn Widget).push_xy(x, y), }.render(to) } } /// Move origin point of drawing area pub enum Push { /// Move origin to the right X(N, T), /// Move origin downwards Y(N, T), /// Move origin to the right and downwards XY(N, N, T), } impl Push { pub fn inner (&self) -> &T { match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, } } pub fn x (&self) -> N { match self { Self::X(x, _) => *x, Self::Y(_, _) => N::default(), Self::XY(x, _, _) => *x } } pub fn y (&self) -> N { match self { Self::X(_, _) => N::default(), Self::Y(y, _) => *y, Self::XY(_, y, _) => *y } } } impl> Widget for Push { type Engine = E; fn layout (&self, to: E::Size) -> Perhaps { self.inner().layout(to) } fn render (&self, to: &mut E::Output) -> Usually<()> { let area = to.area(); Ok(self.layout(area.wh().into())? .map(|size|to.render_in(match *self { Self::X(x, _) => [area.x() + x, area.y(), size.w(), size.h()], Self::Y(y, _) => [area.x(), area.y() + y, size.w(), size.h()], Self::XY(x, y, _) => [area.x() + x, area.y() + y, size.w(), size.h()], }.into(), self.inner())).transpose()?.unwrap_or(())) } } /// Move origin point of drawing area pub enum Pull { /// Move origin to the right X(N, T), /// Move origin downwards Y(N, T), /// Move origin to the right and downwards XY(N, N, T), } impl Pull { pub fn inner (&self) -> &T { match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, } } pub fn x (&self) -> N { match self { Self::X(x, _) => *x, Self::Y(_, _) => N::default(), Self::XY(x, _, _) => *x } } pub fn y (&self) -> N { match self { Self::X(_, _) => N::default(), Self::Y(y, _) => *y, Self::XY(_, y, _) => *y } } } impl> Widget for Pull { type Engine = E; fn layout (&self, to: E::Size) -> Perhaps { self.inner().layout(to) } fn render (&self, to: &mut E::Output) -> Usually<()> { let area = to.area(); Ok(self.layout(area.wh().into())? .map(|size|to.render_in(match *self { Self::X(x, _) => [area.x().minus(x), area.y(), size.w(), size.h()], Self::Y(y, _) => [area.x(), area.y().minus(y), size.w(), size.h()], Self::XY(x, y, _) => [area.x().minus(x), area.y().minus(y), size.w(), size.h()], }.into(), self.inner())).transpose()?.unwrap_or(())) } } pub struct Stack< E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Widget)->Usually<()>)->Usually<()> >(pub F, pub Direction, PhantomData); impl< E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Widget)->Usually<()>)->Usually<()> > Stack { #[inline] pub fn new (direction: Direction, build: F) -> Self { Self(build, direction, Default::default()) } #[inline] pub fn right (build: F) -> Self { Self::new(Direction::Right, build) } #[inline] pub fn down (build: F) -> Self { Self::new(Direction::Down, build) } } impl Widget for Stack where F: Send + Sync + Fn(&mut dyn FnMut(&dyn Widget)->Usually<()>)->Usually<()> { type Engine = E; fn layout (&self, to: E::Size) -> Perhaps { let mut w = 0.into(); let mut h = 0.into(); match self.1 { Direction::Down => { (self.0)(&mut |component| { if h >= to.h() { return Ok(()) } let size = component.push_y(h).max_y(to.h() - h).layout(to)?; if let Some([width, height]) = size.map(|size|size.wh()) { h = h + height.into(); if width > w { w = width; } } Ok(()) })?; }, Direction::Right => { (self.0)(&mut |component| { if w >= to.w() { return Ok(()) } let size = component.push_x(w).max_x(to.w() - w).layout(to)?; if let Some([width, height]) = size.map(|size|size.wh()) { w = w + width.into(); if height > h { h = height } } Ok(()) })?; }, _ => todo!() }; Ok(Some([w, h].into())) } fn render (&self, to: &mut E::Output) -> Usually<()> { let area = to.area(); let mut w = 0.into(); let mut h = 0.into(); match self.1 { Direction::Down => { (self.0)(&mut |component| { if h >= area.h() { return Ok(()) } let item = component.push_y(h).max_y(area.h() - h); let size = item.layout(area.wh().into())?; if let Some([width, height]) = size.map(|size|size.wh()) { item.render(to)?; h = h + height; if width > w { w = width } }; Ok(()) })?; }, Direction::Right => { (self.0)(&mut |component| { if w >= area.w() { return Ok(()) } let item = component.push_x(w).max_x(area.w() - w); let size = item.layout(area.wh().into())?; if let Some([width, height]) = size.map(|size|size.wh()) { item.render(to)?; w = width + w; if height > h { h = height } }; Ok(()) })?; }, _ => todo!() }; Ok(()) } } #[macro_export] macro_rules! lay { ($($expr:expr),* $(,)?) => { Layers::new(move|add|{ $(add(&$expr)?;)* Ok(()) }) } } #[macro_export] macro_rules! col { ($($expr:expr),* $(,)?) => { Stack::down(move|add|{ $(add(&$expr)?;)* Ok(()) }) }; ($pat:pat in $collection:expr => $item:expr) => { Stack::down(move |add|{ for $pat in $collection { add(&$item)?; } Ok(()) }) } } #[macro_export] macro_rules! col_up { ($($expr:expr),* $(,)?) => { Stack::down(move|add|{ $(add(&$expr)?;)* Ok(()) }) }; ($pat:pat in $collection:expr => $item:expr) => { Stack::up(move |add|{ for $pat in $collection { add(&$item)?; } Ok(()) }) } } #[macro_export] macro_rules! row { ($($expr:expr),* $(,)?) => { Stack::right(move|add|{ $(add(&$expr)?;)* Ok(()) }) }; ($pat:pat in $collection:expr => $item:expr) => { Stack::right(move |add|{ for $pat in $collection { add(&$item)?; } Ok(()) }) } } /// A binary split with fixed proportion pub struct Split, B: Widget>( pub Direction, pub E::Unit, A, B, PhantomData ); impl, B: Widget> Split { pub fn new (direction: Direction, proportion: E::Unit, a: A, b: B) -> Self { Self(direction, proportion, a, b, Default::default()) } pub fn up (proportion: E::Unit, a: A, b: B) -> Self { Self(Direction::Up, proportion, a, b, Default::default()) } pub fn down (proportion: E::Unit, a: A, b: B) -> Self { Self(Direction::Down, proportion, a, b, Default::default()) } pub fn left (proportion: E::Unit, a: A, b: B) -> Self { Self(Direction::Left, proportion, a, b, Default::default()) } pub fn right (proportion: E::Unit, a: A, b: B) -> Self { Self(Direction::Right, proportion, a, b, Default::default()) } } impl, B: Widget> Widget for Split { type Engine = E; fn layout (&self, to: E::Size) -> Perhaps { Ok(Some(to)) } fn render (&self, to: &mut E::Output) -> Usually<()> { let (a, b) = to.area().split_fixed(self.0, self.1); to.render_in(a.into(), &self.2)?; to.render_in(b.into(), &self.3)?; Ok(()) } } /// A widget that tracks its render width and height pub struct Measure(PhantomData, AtomicUsize, AtomicUsize); impl Clone for Measure { fn clone (&self) -> Self { Self( Default::default(), AtomicUsize::from(self.1.load(Ordering::Relaxed)), AtomicUsize::from(self.2.load(Ordering::Relaxed)), ) } } impl std::fmt::Debug for Measure { fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { f.debug_struct("Measure") .field("width", &self.0) .field("height", &self.1) .finish() } } impl Measure { pub fn w (&self) -> usize { self.1.load(Ordering::Relaxed) } pub fn h (&self) -> usize { self.2.load(Ordering::Relaxed) } pub fn wh (&self) -> [usize;2] { [self.w(), self.h()] } pub fn set_w (&self, w: impl Into) { self.1.store(w.into(), Ordering::Relaxed) } pub fn set_h (&self, h: impl Into) { self.2.store(h.into(), Ordering::Relaxed) } pub fn set_wh (&self, w: impl Into, h: impl Into) { self.set_w(w); self.set_h(h); } pub fn new () -> Self { Self(PhantomData::default(), 0.into(), 0.into()) } pub fn format (&self) -> String { format!("{}x{}", self.w(), self.h()) } } impl Widget for Measure { type Engine = E; fn layout (&self, _: E::Size) -> Perhaps { Ok(Some([0u16.into(), 0u16.into()].into())) } fn render (&self, to: &mut E::Output) -> Usually<()> { self.set_w(to.area().w()); self.set_h(to.area().h()); Ok(()) } } /// A scrollable area. pub struct Scroll< E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Widget)->Usually<()>)->Usually<()> >(pub F, pub Direction, pub u64, PhantomData);