use crate::*; use std::ops::{Add, Sub, Mul, Div}; use std::fmt::{Display, Debug}; mod cond; pub use self::cond::*; mod coord; pub use self::coord::*; mod layers; pub use self::layers::*; mod measure; pub use self::measure::*; mod position; pub use self::position::*; mod scroll; pub use self::scroll::*; mod size; pub use self::size::*; mod split; pub use self::split::*; /// Has static methods for conditional rendering, /// in unary and binary forms. pub struct Cond; impl Cond { /// Render `item` when `cond` is true. pub fn when > (cond: bool, item: A) -> When { When(cond, item, Default::default()) } /// Render `item` if `cond` is true, otherwise render `other`. pub fn either , B: Render> (cond: bool, item: A, other: B) -> Either { Either(cond, item, other, Default::default()) } } /// Renders `self.1` when `self.0` is true. pub struct When>(bool, A, PhantomData); /// Renders `self.1` when `self.0` is true, otherwise renders `self.2` pub struct Either, B: Render>(bool, A, B, PhantomData); /// Renders multiple things on top of each other, /// in the order they are provided by the callback. /// Total size is largest width x largest height. pub struct Layers< E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)->Usually<()>)->Usually<()> >(pub F, PhantomData); /// Shorthand for defining an instance of [Layers]. #[macro_export] macro_rules! lay { ([$($expr:expr),* $(,)?]) => { Layers::new(move|add|{ $(add(&$expr)?;)* Ok(()) }) }; (![$($expr:expr),* $(,)?]) => { Layers::new(|add|{ $(add(&$expr)?;)* Ok(()) }) }; ($expr:expr) => { Layers::new($expr) }; } /// A linear coordinate. 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() } } fn zero () -> Self { 0.into() } } 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), self.h()] } #[inline] fn clip_h (&self, h: N) -> [N;2] { [self.w(), self.h().min(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) } } } 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().minus(x), self.h()] } #[inline] fn shrink_y (&self, y: N) -> [N;4] { [self.x(), self.y(), self.w(), self.h().minus(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)] } #[inline] fn clip_w (&self, w: N) -> [N;4] { [self.x(), self.y(), self.w().min(w), 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::North => ( [self.x(), (self.y()+self.h()).minus(a), self.w(), a], [self.x(), self.y(), self.w(), self.h().minus(a)], ), Direction::South => ( [self.x(), self.y(), self.w(), a], [self.x(), self.y() + a, self.w(), self.h().minus(a)], ), Direction::East => ( [self.x(), self.y(), a, self.h()], [self.x() + a, self.y(), self.w().minus(a), self.h()], ), Direction::West => ( [self.x() + self.w() - a, self.y(), a, self.h()], [self.x(), self.y(), self.w() - a, self.h()], ), } } } macro_rules! by_axis { (!$Enum:ident) => { impl> $Enum { pub fn x (item: T) -> Self { Self::X(item) } pub fn y (item: T) -> Self { Self::Y(item) } pub fn xy (item: T) -> Self { Self::XY(item) } } }; (+$Enum:ident) => { impl> $Enum { pub fn x (x: E::Unit, item: T) -> Self { Self::X(x, item) } pub fn y (y: E::Unit, item: T) -> Self { Self::Y(y, item) } pub fn xy (x: E::Unit, y: E::Unit, item: T) -> Self { Self::XY(x, y, item) } } }; } /// Shrink drawing area pub enum Shrink> { /// Decrease width X(E::Unit, T), /// Decrease height Y(E::Unit, T), /// Decrease width and height XY(E::Unit, E::Unit, T), } by_axis!(+Shrink); /// Expand drawing area pub enum Grow> { /// Increase width X(E::Unit, T), /// Increase height Y(E::Unit, T), /// Increase width and height XY(E::Unit, E::Unit, T) } by_axis!(+Grow); pub enum Fill> { _Unused(PhantomData), /// Maximize width X(W), /// Maximize height Y(W), /// Maximize width and height XY(W), } by_axis!(!Fill); /// Enforce fixed size of drawing area pub enum Fixed> { /// Set width X(E::Unit, T), /// Set height Y(E::Unit, T), /// Set width and height XY(E::Unit, E::Unit, T), } by_axis!(+Fixed); /// Enforce minimum size of drawing area pub enum Min> { /// Enforce minimum width X(E::Unit, T), /// Enforce minimum height Y(E::Unit, T), /// Enforce minimum width and height XY(E::Unit, E::Unit, T), } by_axis!(+Min); /// Enforce maximum size of drawing area pub enum Max> { /// Enforce maximum width X(E::Unit, T), /// Enforce maximum height Y(E::Unit, T), /// Enforce maximum width and height XY(E::Unit, E::Unit, T), } by_axis!(+Max); /// Increment origin point of drawing area pub enum Push> { /// Move origin to the right X(E::Unit, T), /// Move origin downwards Y(E::Unit, T), /// Move origin to the right and downwards XY(E::Unit, E::Unit, T), } by_axis!(+Push); /// Decrement origin point of drawing area pub enum Pull> { /// Move origin to the right X(E::Unit, T), /// Move origin downwards Y(E::Unit, T), /// Move origin to the right and downwards XY(E::Unit, E::Unit, T), } by_axis!(+Pull); /// Shrink from each side pub enum Inset { /// Decrease width X(E::Unit, T), /// Decrease height Y(E::Unit, T), /// Decrease width and height XY(E::Unit, E::Unit, T), } by_axis!(+Inset); /// Grow on each side pub enum Outset> { /// Increase width X(E::Unit, T), /// Increase height Y(E::Unit, T), /// Increase width and height XY(E::Unit, E::Unit, T), } by_axis!(+Outset); /// A cardinal direction. #[derive(Copy, Clone, PartialEq)] pub enum Direction { North, South, West, East, } /// A binary split with fixed proportion pub struct Split, B: Render>( pub bool, pub Direction, pub E::Unit, A, B, PhantomData ); pub enum Bsp, Y: Render> { /// X is north of Y N(Option, Option), /// X is south of Y S(Option, Option), /// X is east of Y E(Option, Option), /// X is west of Y W(Option, Option), /// X is above Y A(Option, Option), /// X is below Y B(Option, Option), /// Should be avoided. Null(PhantomData), } pub struct Stack< E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)->Usually<()>)->Usually<()> >(pub F, pub Direction, PhantomData); #[macro_export] macro_rules! col { ([$($expr:expr),* $(,)?]) => { Stack::down(move|add|{ $(add(&$expr)?;)* Ok(()) }) }; (![$($expr:expr),* $(,)?]) => { Stack::down(|add|{ $(add(&$expr)?;)* Ok(()) }) }; ($expr:expr) => { Stack::down($expr) }; ($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::up(move|add|{ $(add(&$expr)?;)* Ok(()) }) }; (![$($expr:expr),* $(,)?]) => { Stack::up(|add|{ $(add(&$expr)?;)* Ok(()) }) }; ($expr:expr) => { Stack::up(expr) }; ($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(()) }) }; (![$($expr:expr),* $(,)?]) => { Stack::right(|add|{ $(add(&$expr)?;)* Ok(()) }) }; ($expr:expr) => { Stack::right($expr) }; ($pat:pat in $collection:expr => $item:expr) => { Stack::right(move|add|{ for $pat in $collection { add(&$item)?; } Ok(()) }) }; } pub trait HasSize { fn size (&self) -> &Measure; } #[macro_export] macro_rules! has_size { (<$E:ty>|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => { impl $(<$($L),*$($T $(: $U)?),*>)? HasSize<$E> for $Struct $(<$($L),*$($T),*>)? { fn size (&$self) -> &Measure<$E> { $cb } } } } pub trait LayoutDebug { fn debug > (other: W) -> DebugOverlay { DebugOverlay(Default::default(), other) } } pub struct DebugOverlay>(PhantomData, pub W); /// A widget that tracks its render width and height #[derive(Default)] pub struct Measure { _engine: PhantomData, pub x: Arc, pub y: Arc, } pub struct ShowMeasure<'a>(&'a Measure); /// A scrollable area. pub struct Scroll< E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)->Usually<()>)->Usually<()> >(pub F, pub Direction, pub u64, PhantomData); /// Override X and Y coordinates, aligning to corner, side, or center of area pub enum Align> { _Unused(PhantomData), /// Draw at center of container Center(T), /// Draw at center of X axis X(T), /// Draw at center of Y axis Y(T), /// Draw at upper left corner of contaier NW(T), /// Draw at center of upper edge of container N(T), /// Draw at right left corner of contaier NE(T), /// Draw at center of left edge of container W(T), /// Draw at center of right edge of container E(T), /// Draw at lower left corner of container SW(T), /// Draw at center of lower edge of container S(T), /// Draw at lower right edge of container SE(T) }