use crate::*; impl> Layout for T {} pub trait Layout: Draw + Sized { fn origin (self, azimuth: impl Into>) -> impl Draw { Origin(azimuth.into(), self) } fn origin_c (self) -> impl Draw { Origin(Some(Azimuth::C), self) } fn origin_x (self) -> impl Draw { Origin(Some(Azimuth::X), self) } fn origin_y (self) -> impl Draw { Origin(Some(Azimuth::Y), self) } fn origin_n (self) -> impl Draw { Origin(Some(Azimuth::N), self) } fn origin_s (self) -> impl Draw { Origin(Some(Azimuth::S), self) } fn origin_e (self) -> impl Draw { Origin(Some(Azimuth::E), self) } fn origin_w (self) -> impl Draw { Origin(Some(Azimuth::W), self) } fn origin_ne (self) -> impl Draw { Origin(Some(Azimuth::NE), self) } fn origin_se (self) -> impl Draw { Origin(Some(Azimuth::SE), self) } fn origin_nw (self) -> impl Draw { Origin(Some(Azimuth::NW), self) } fn origin_sw (self) -> impl Draw { Origin(Some(Azimuth::SW), self) } } /// Uses [AtomicUsize] to measure size during\ /// rendering (which is normally read-only). #[derive(Default, Debug, Clone)] pub struct Sizer( /// Width pub Arc, /// Height pub Arc, ); impl Xy for Sizer { fn x (&self) -> u16 { self.0.load(Relaxed) as u16 } fn y (&self) -> u16 { self.1.load(Relaxed) as u16 } } impl Wide for Sizer { fn w (&self) -> u16 { self.0.load(Relaxed) as u16 } } impl Tall for Sizer { fn h (&self) -> u16 { self.1.load(Relaxed) as u16 } } impl PartialEq for Sizer { fn eq (&self, _: &Self) -> bool { todo!() } } impl Sizer { pub const fn of (&self, of: impl Draw) -> impl Draw { draw(move|to: &mut T|{ let area = of.draw(to)?; self.0.store(area.map(|a|a.w()).unwrap_or(T::Unit::zero()).into(), Relaxed); self.1.store(area.map(|a|a.h()).unwrap_or(T::Unit::zero()).into(), Relaxed); Ok(area) }) } } /// Some layout operations exist in multiple variants that take a single argument. /// Their handling in [eval_view] is uniform and goes like this: #[macro_export] macro_rules! eval_enum (( $name:literal, $output:ident, $state:ident, $value:expr, $Enum:ident { $($v:literal => $V:ident),* $(,)? } ) => {{ match $value { $(Some($v) => $Enum::$V,)* frag => return Err(format!("invalid variant: {}/{frag:?}", $name).into()) } }}); /// Some layout operations exist in XY, X, and Y variants that take 3 or 2 arguments. /// Their handling in [eval_view] is uniform and goes like this: #[macro_export] macro_rules! eval_xy ( // Valueless variant: // (fill/x ...) // (fill/y ...) // (fill/xy ...) ( $name:expr => $expr:expr, $head:expr, $output:ident, $state:ident, $variant:expr, $xy:ident, $x:ident, $y:ident, $arg:expr, ) => {{ // frags.next(): 2nd slash-delimited fragment: /x, /y, /xy let variant = $variant; let thunk = draw(move|screen|$state.interpret(screen, &$arg)); match variant { // X variant Some("x") => thunk.$x().draw($output), // Y variant Some("y") => thunk.$y().draw($output), // XY variant (can be omitted) Some("xy") | None => thunk.$xy().draw($output), // Other namespace members are invalid frag => invalid_variant($name, frag, $expr, $head) } }}; // Variadic variant: // (push/x n ...) // (push/y n ...) // (push/xy n m ...) ( $name:expr => $expr:expr, $head:expr, $output:ident, $state:ident, $variant:expr, $xy:ident, $x:ident, $y:ident, $arg0:expr, $arg1:expr, $arg2:expr, ) => {{ // frags.next(): 2nd slash-delimited fragment: /x, /y, /xy let variant = $variant; let thunk = draw(move|screen|$state.interpret(screen, &match variant { Some("x") | Some("y") => $arg1, Some("xy") | None => $arg2, _ => panic!("{}: unsupported axis {variant:?}; try /x, /y, /xy", $name) })); match variant { // X variant Some("x") => thunk.$x($state.namespace($arg0?)?) .draw($output), // Y variant Some("y") => thunk.$y($state.namespace($arg0?)?) .draw($output), // XY variant (can be omitted) Some("xy") | None => thunk.$xy($state.namespace($arg0?)?, $state.namespace($arg1?)?) .draw($output), // Other namespace members are invalid frag => invalid_variant($name, frag, $expr, $head) } }}; ); #[macro_export] macro_rules! fn_kw_layout { ($name:ident |$state:ident, $output: ident, $expr:ident| $body:block) => { #[cfg(all(feature = "draw", feature = "eval"))] pub fn $name ( $state: &S, $output: &mut O, $expr: &str ) -> Perhaps> where S: Interpret>> + for<'b> Namespace<'b, bool> + for<'b> Namespace<'b, O::Unit> + for<'b> Namespace<'b, Option> $body } } #[macro_export] macro_rules! fn_kw_layout_tui { ($name:ident |$state:ident, $output: ident, $expr:ident| $body:block) => { #[cfg(all(feature = "draw", feature = "eval", feature = "term"))] pub fn $name ( $state: &S, $output: &mut Tui, $expr: &str ) -> Perhaps> where S: Interpret>> + for<'b> Namespace<'b, bool> + for<'b> Namespace<'b, u16> + for<'b> Namespace<'b, Option> + for<'b> Namespace<'b, Color> $body } } #[macro_export] macro_rules! def_layout_modifier { ( $Trait:ident ($fn_x:ident $fn_y:ident $fn_xy:ident), $Struct:ident { $X:ident $Y:ident $XY:ident } $kw_name:ident $name:literal |$self:ident, $to:ident| $body:block ) => { impl> $Trait for T {} pub trait $Trait: Draw + Sized { fn $fn_x > + Copy> (self, x: N) -> $Struct { $Struct::$X(self, x) } fn $fn_y > + Copy> (self, y: N) -> $Struct { $Struct::$Y(self, y) } fn $fn_xy > + Copy> (self, x: N, y: N) -> $Struct { $Struct::$XY(self, x, y) } } pub enum $Struct, X: Into>> { __(PhantomData), $X(I, X), $Y(I, X), $XY(I, X, X), } impl , X: Into> + Copy> Draw for $Struct { fn draw (&$self, $to: &mut S) -> Perhaps> { $body } } fn_kw_layout!($kw_name |state, output, expr| { let head = expr.head()?; let mut frags = head.src()?.unwrap_or_default().split("/"); Ok(matches!(frags.next(), Some($name)).then(||{ let args = expr.tail(); let arg0 = args.head(); let tail0 = args.tail(); let arg1 = tail0.head(); let tail1 = tail0.tail(); let arg2 = tail1.head(); eval_xy!( $name => expr, head, output, state, frags.next(), $fn_xy, $fn_x, $fn_y, arg0, arg1, arg2, ) }).transpose()?.flatten()) }); } } #[macro_export] macro_rules! def_layout_modifier_flat { ( $Trait:ident ($fn_x:ident $fn_y:ident $fn_xy:ident), $Struct:ident { $X:ident $Y:ident $XY:ident } $kw_name:ident $name:literal |$self:ident, $to:ident| $body:block ) => { impl> $Trait for T {} pub trait $Trait: Draw + Sized { fn $fn_x (self) -> $Struct { $Struct::$X(self) } fn $fn_y (self) -> $Struct { $Struct::$Y(self) } fn $fn_xy (self) -> $Struct { $Struct::$XY(self) } } pub enum $Struct> { __(PhantomData), $X(I), $Y(I), $XY(I), } impl > Draw for $Struct { fn draw (&$self, $to: &mut S) -> Perhaps> { $body } } fn_kw_layout!($kw_name |state, output, expr| { let head = expr.head()?; let mut frags = head.src()?.unwrap_or_default().split("/"); Ok(matches!(frags.next(), Some($name)).then(||{ let args = expr.tail(); let arg0 = args.head(); eval_xy!( $name => expr, head, output, state, frags.next(), $fn_xy, $fn_x, $fn_y, arg0, ) }).transpose()?.flatten()) }); } } mod area; pub use self::area::*; mod axis; pub use self::axis::*; mod azimuth; pub use self::azimuth::*; mod cond; pub use self::cond::*; mod iter; pub use self::iter::*;