From a9f2fa2cdd5fac6ea0f7aee7e9c9d4acec8eac39 Mon Sep 17 00:00:00 2001 From: facile pop culture reference Date: Tue, 18 Aug 2026 10:46:20 +0300 Subject: [PATCH] add lifetime to Draw trait --- Cargo.lock | 58 +----- Cargo.toml | 1 + dizzle | 2 +- examples/mode_02.rs | 12 +- src/layout.rs | 129 ++++--------- src/layout/area.rs | 14 +- src/layout/axis.rs | 416 ++++++++++++++++++++++++++++++++++-------- src/layout/azimuth.rs | 217 +++++++++++----------- src/layout/cond.rs | 30 ++- src/layout/iter.rs | 53 +++--- src/lib.rs | 91 +++++---- src/term.rs | 47 ++--- src/term/border.rs | 12 +- src/term/keys.rs | 4 +- src/term/modify.rs | 48 ++--- src/term/phat.rs | 8 +- src/term/scroll.rs | 4 +- 17 files changed, 655 insertions(+), 491 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ee91f7e..1acd8b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -367,26 +367,13 @@ dependencies = [ [[package]] name = "const_panic" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e262cdaac42494e3ae34c43969f9cdeb7da178bdb4b66fa6a1ea2edb4c8ae652" +checksum = "9603f79528ece8163c496f8932121cb36cfe46259e9c907bb3d8205139d7caa3" dependencies = [ - "const_panic_proc_macros", "typewit", ] -[[package]] -name = "const_panic_proc_macros" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb0240417fe20ccf13397fa25e6f0a987dbbfaf27d9e13532419df7f593e65e8" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", - "unicode-xid", -] - [[package]] name = "convert_case" version = "0.10.0" @@ -617,12 +604,13 @@ checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" name = "dizzle" version = "0.1.0" dependencies = [ - "const_panic", "dizzle_proc", "itertools", - "konst", - "peg", + "profiling", "thiserror 2.0.20", + "tracing", + "tracing-flame", + "tracing-subscriber", ] [[package]] @@ -1698,33 +1686,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "peg" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aad070be5b63aa72103f2fcdd70a83adbd5e90112ce5b574171ff1c65501773" -dependencies = [ - "peg-macros", - "peg-runtime", -] - -[[package]] -name = "peg-macros" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd8ef6825cae95355031ae26a99b616a2a21f22ba2de0197c43dfb05acbe7ee" -dependencies = [ - "peg-runtime", - "proc-macro2", - "quote", -] - -[[package]] -name = "peg-runtime" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7011d97b484a5ebdc4b1fdb3b12d5e4bbbea56e9d22b688f2e79e04b65a7d8a6" - [[package]] name = "percent-encoding" version = "2.3.2" @@ -2651,6 +2612,7 @@ dependencies = [ "crossterm", "dizzle", "jack", + "konst", "midly", "palette", "profiling", @@ -3004,12 +2966,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - [[package]] name = "utf8parse" version = "0.2.2" diff --git a/Cargo.toml b/Cargo.toml index 8623115..fea5807 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ term = ["draw", "dep:ratatui", "dep:crossterm"] anyhow = { version = "1.0" } atomic_float = { version = "1" } better-panic = { version = "0.3.0" } +konst = { version = "0.3.16", features = [ "rust_1_83" ] } bumpalo = { optional = true, version = "3.19.0" } crossterm = { optional = true, version = "0.29.0" } diff --git a/dizzle b/dizzle index 947a2f3..648be30 160000 --- a/dizzle +++ b/dizzle @@ -1 +1 @@ -Subproject commit 947a2f36a60869ee9aab40aa43d06e1e1c100730 +Subproject commit 648be30918bef0a975bd225c0499911aaa88bc13 diff --git a/examples/mode_02.rs b/examples/mode_02.rs index 1ec2069..ab8b3e8 100644 --- a/examples/mode_02.rs +++ b/examples/mode_02.rs @@ -45,16 +45,20 @@ impl_keywords!(Tui, XYWH, State [ kw_tui_bg ]); -impl Interpret>> for State { - fn interpret_word (&self, to: &mut Tui, sym: &impl Language) -> Perhaps> { - match sym.src()? { +impl<'a> Interpret<'a, Tui, Option>> for State { + fn interpret_word + ?Sized> (&'a self, to: &mut Tui, lang: &'a L) + -> Drawn<'a, u16> + { + match lang.src()? { Some(":foo") => "foo".draw(to), Some(":bar") => "bar".draw(to), Some(":foobar") => "FOOBAR".draw(to), src => todo!("src: {src:?}") } } - fn interpret_expr (&self, to: &mut Tui, src: &impl Expression) -> Perhaps> { + fn interpret_expr + ?Sized> (&self, to: &mut Tui, src: &'a L) + -> Drawn<'a, u16> + { if let Some(area) = self.keyword(to, src)? { Ok(Some(area)) } else { diff --git a/src/layout.rs b/src/layout.rs index 3ae27fc..77d8658 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -1,44 +1,44 @@ use crate::*; -impl> Layout for T {} +impl<'a, S: Screen, T: Draw<'a, S>> Layout<'a, S> for T {} -pub trait Layout: Draw + Sized { - fn origin (self, azimuth: impl Into>) -> impl Draw { +pub trait Layout<'a, S: Screen>: Draw<'a, S> + Sized { + fn origin (self, azimuth: impl Into>) -> impl Draw<'a, S> { Origin(azimuth.into(), self) } - fn origin_c (self) -> impl Draw { + fn origin_c (self) -> impl Draw<'a, S> { Origin(Some(Azimuth::C), self) } - fn origin_x (self) -> impl Draw { + fn origin_x (self) -> impl Draw<'a, S> { Origin(Some(Azimuth::X), self) } - fn origin_y (self) -> impl Draw { + fn origin_y (self) -> impl Draw<'a, S> { Origin(Some(Azimuth::Y), self) } - fn origin_n (self) -> impl Draw { + fn origin_n (self) -> impl Draw<'a, S> { Origin(Some(Azimuth::N), self) } - fn origin_s (self) -> impl Draw { + fn origin_s (self) -> impl Draw<'a, S> { Origin(Some(Azimuth::S), self) } - fn origin_e (self) -> impl Draw { + fn origin_e (self) -> impl Draw<'a, S> { Origin(Some(Azimuth::E), self) } - fn origin_w (self) -> impl Draw { + fn origin_w (self) -> impl Draw<'a, S> { Origin(Some(Azimuth::W), self) } - fn origin_ne (self) -> impl Draw { + fn origin_ne (self) -> impl Draw<'a, S> { Origin(Some(Azimuth::NE), self) } - fn origin_se (self) -> impl Draw { + fn origin_se (self) -> impl Draw<'a, S> { Origin(Some(Azimuth::SE), self) } - fn origin_nw (self) -> impl Draw { + fn origin_nw (self) -> impl Draw<'a, S> { Origin(Some(Azimuth::NW), self) } - fn origin_sw (self) -> impl Draw { + fn origin_sw (self) -> impl Draw<'a, S> { Origin(Some(Azimuth::SW), self) } } @@ -76,7 +76,7 @@ impl PartialEq for Sizer { } impl Sizer { - pub const fn of (&self, of: impl Draw) -> impl Draw { + pub const fn of <'a, T: Screen> (&self, of: impl Draw<'a, T>) -> impl Draw<'a, T> { 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); @@ -99,70 +99,13 @@ impl Sizer { } }}); -/// 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>> + pub fn $name <'a, O: Screen, S> ( + $state: &'a S, $output: &mut O, $expr: &'a str + ) -> PerhapsRef<'a, XYWH> where + S: Interpret<'a, O, Option>> + Namespace + Namespace + Namespace> @@ -173,10 +116,10 @@ impl Sizer { #[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>> + pub fn $name <'a, S> ( + $state: &'a S, $output: &mut Tui, $expr: &'a str + ) -> PerhapsRef<'a, XYWH> where + S: Interpret<'a, Tui, Option>> + Namespace + Namespace + Namespace> @@ -191,9 +134,9 @@ impl Sizer { $Struct:ident { $X:ident $Y:ident $XY:ident } $kw_name:ident $name:literal |$self:ident, $to:ident| $body:block ) => { - impl> $Trait for T {} + impl> $Trait for T {} - pub trait $Trait: Draw + Sized { + pub trait $Trait: Draw<'a, S> + Sized { fn $fn_x > + Copy> (self, x: N) -> $Struct { $Struct::$X(self, x) } fn $fn_y > + Copy> (self, y: N) @@ -202,11 +145,11 @@ impl Sizer { -> $Struct { $Struct::$XY(self, x, y) } } - pub enum $Struct, X: Into>> { + pub enum $Struct, X: Into>> { __(PhantomData), $X(I, X), $Y(I, X), $XY(I, X, X), } - impl , X: Into> + Copy> Draw for $Struct { + impl , X: Into> + Copy> Draw<'a, S> for $Struct { fn draw (&$self, $to: &mut S) -> Perhaps> { $body } } @@ -216,16 +159,10 @@ impl Sizer { let mut frags = head.src()?.unwrap_or_default().split("/"); Ok(matches!(frags.next(), Some($name)).then(||{ #[cfg(feature = "prof")] profiling::scope!(stringify!(inner $kw_name)); - 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, + expr.nth(1), expr.nth(2), expr.nth(3), ) }).transpose()?.flatten()) }); @@ -238,9 +175,9 @@ impl Sizer { $Struct:ident { $X:ident $Y:ident $XY:ident } $kw_name:ident $name:literal |$self:ident, $to:ident| $body:block ) => { - impl> $Trait for T {} + impl> $Trait for T {} - pub trait $Trait: Draw + Sized { + pub trait $Trait: Draw<'a, S> + 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) } @@ -250,7 +187,7 @@ impl Sizer { __(PhantomData), $X(I), $Y(I), $XY(I), } - impl > Draw for $Struct { + impl <'a, S: Screen, I: Draw<'a, S>> Draw<'a, S> for $Struct { fn draw (&$self, $to: &mut S) -> Perhaps> { $body } } @@ -260,11 +197,9 @@ impl Sizer { let mut frags = head.src()?.unwrap_or_default().split("/"); Ok(matches!(frags.next(), Some($name)).then(||{ #[cfg(feature = "prof")] profiling::scope!(stringify!(inner $kw_name)); - let args = expr.tail(); - let arg0 = args.head(); eval_xy!( $name => expr, head, output, state, frags.next(), - $fn_xy, $fn_x, $fn_y, arg0, + $fn_xy, $fn_x, $fn_y, expr.nth(1), ) }).transpose()?.flatten()) }); diff --git a/src/layout/area.rs b/src/layout/area.rs index a806c92..09f827e 100644 --- a/src/layout/area.rs +++ b/src/layout/area.rs @@ -6,17 +6,19 @@ use crate::*; /// let _ = area(XYWH(1, 2, 3, 4), "southeast"); /// let _ = area(Some(XYWH(1, 2, 3, 4)), "southeast"); /// ``` -pub fn area , U: Into>>> ( +pub fn area <'a, S: Screen, T: Draw<'a, S>, U: Into>>> ( origin: U, it: T ) -> Area { Area(origin.into(), it) } -pub struct Area>( +pub struct Area( pub Option>, - pub T + pub T, ); -impl_draw!(,>|self: Area, to: S|{ - to.draw(self.0, &self.1) -}); +impl<'a, S: Screen, T: Draw<'a, S>> Draw<'a, S> for Area { + fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit> { + to.draw(self.0, &self.1) + } +} diff --git a/src/layout/axis.rs b/src/layout/axis.rs index 28002d5..600c8b6 100644 --- a/src/layout/axis.rs +++ b/src/layout/axis.rs @@ -1,8 +1,296 @@ use crate::*; -def_layout_modifier_flat!( - LayoutFull (full_w full_h full_wh), - Full { W H WH } kw_full "full" |self, to| { +pub enum Full<'a, S: Screen, I: Draw<'a, S>> { + __(PhantomData<&'a S>), W(I), H(I), WH(I), +} + +pub enum Exact<'a, S: Screen, I: Draw<'a, S>, X: Into>> { + __(PhantomData<&'a S>), W(I, X), H(I, X), WH(I, X, X), +} + +pub enum Push<'a, S: Screen, I: Draw<'a, S>, X: Into>> { + __(PhantomData<&'a S>), X(I, X), Y(I, X), XY(I, X, X), +} + +pub enum Pull<'a, S: Screen, I: Draw<'a, S>, X: Into>> { + __(PhantomData<&'a S>), X(I, X), Y(I, X), XY(I, X, X), +} + +pub enum Min<'a, S: Screen, I: Draw<'a, S>, X: Into>> { + __(PhantomData<&'a S>), W(I, X), H(I, X), WH(I, X, X), +} + +pub enum Max<'a, S: Screen, I: Draw<'a, S>, X: Into>> { + __(PhantomData<&'a S>), W(I, X), H(I, X), WH(I, X, X), +} + +pub enum Pad<'a, S: Screen, I: Draw<'a, S>, X: Into>> { + __(PhantomData<&'a S>), X(I, X), Y(I, X), XY(I, X, X), +} + +/// ``` +/// # fn test_layout_full () -> Usually<()> { +/// assert_eq!(check_layout("1")?, Some(XYWH(1u16, 1, 1, 1))); +/// assert_eq!(check_layout("1".full_w())?, Some(XYWH(1u16, 1, 80, 1))); +/// assert_eq!(check_layout("1".full_h())?, Some(XYWH(1u16, 1, 1, 25))); +/// assert_eq!(check_layout("1".full_wh())?, Some(XYWH(1u16, 1, 80, 25))); +/// # Ok(()) }} +/// ``` +pub trait LayoutFull<'a, S: Screen>: Draw<'a, S> + Sized { + fn full_w (self) -> Full<'a, S, Self> { Full::W(self) } + fn full_h (self) -> Full<'a, S, Self> { Full::H(self) } + fn full_wh (self) -> Full<'a, S, Self> { Full::WH(self) } +} + +/// ``` +/// # fn test_layout_exact () -> Usually<()> { +/// assert_eq!(check_layout("FOOBAR\nKILROY")?, Some(XYWH(1, 1, 6, 2))); +/// assert_eq!(check_layout("FOOBAR\nKILROY".exact_w(3))?, Some(XYWH(1, 1, 3, 2))); +/// assert_eq!(check_layout("FOOBAR\nKILROY".exact_h(1))?, Some(XYWH(1, 1, 6, 1))); +/// assert_eq!(check_layout("FOOBAR\nKILROY".exact_wh(2, 1))?, Some(XYWH(1, 1, 2, 1))); +/// # Ok(()) }} +/// ``` +pub trait LayoutExact<'a, S: Screen>: Draw<'a, S> + Sized { + fn exact_w > + Copy> (self, w: X) + -> Exact<'a, S, Self, X> { Exact::W(self, w) } + fn exact_h > + Copy> (self, h: X) + -> Exact<'a, S, Self, X> { Exact::H(self, h) } + fn exact_wh > + Copy> (self, w: X, h: X) + -> Exact<'a, S, Self, X> { Exact::WH(self, w, h) } +} + +/// ``` +/// # fn test_layout_min () -> Usually<()> { +/// assert_eq!(check_layout("1".min_w(5))?, Some(XYWH(1u16, 1, 5, 1))); +/// assert_eq!(check_layout("1".min_h(5))?, Some(XYWH(1u16, 1, 1, 5))); +/// assert_eq!(check_layout("1".min_wh(5, 5))?, Some(XYWH(1u16, 1, 5, 5))); +/// assert_eq!(check_layout("123456".min_w(5))?, Some(XYWH(1u16, 1, 6, 1))); +/// # Ok(()) }} +/// ``` +pub trait LayoutMin<'a, S: Screen>: Draw<'a, S> + Sized { + fn min_w > + Copy> (self, w: X) + -> Min<'a, S, Self, X> { Min::W(self, w) } + fn min_h > + Copy> (self, h: X) + -> Min<'a, S, Self, X> { Min::H(self, h) } + fn min_wh > + Copy> (self, w: X, h: X) + -> Min<'a, S, Self, X> { Min::WH(self, w, h) } +} + +pub trait LayoutMax<'a, S: Screen>: Draw<'a, S> + Sized { + fn max_w > + Copy> (self, w: X) + -> Max<'a, S, Self, X> { Max::W(self, w) } + fn max_h > + Copy> (self, h: X) + -> Max<'a, S, Self, X> { Max::H(self, h) } + fn max_wh > + Copy> (self, w: X, h: X) + -> Max<'a, S, Self, X> { Max::WH(self, w, h) } +} + +pub trait LayoutPad<'a, S: Screen>: Draw<'a, S> + Sized { + fn pad_w > + Copy> (self, w: X) + -> Pad<'a, S, Self, X> { Pad::X(self, w) } + fn pad_h > + Copy> (self, h: X) + -> Pad<'a, S, Self, X> { Pad::Y(self, h) } + fn pad_wh > + Copy> (self, w: X, h: X) + -> Pad<'a, S, Self, X> { Pad::XY(self, w, h) } +} + +/// ``` +/// # fn test_layout_pull () -> Usually<()> { +/// assert_eq!(check_layout("1")?, Some(XYWH(1u16, 1, 1, 1))); +/// assert_eq!(check_layout("1".push_x(1))?, Some(XYWH(2u16, 1, 1, 1))); +/// assert_eq!(check_layout("1".push_y(1))?, Some(XYWH(1u16, 2, 1, 1))); +/// assert_eq!(check_layout("1".push_xy(1, 1))?, Some(XYWH(2u16, 2, 1, 1))); +/// # Ok(()) } +/// ``` +pub trait LayoutPush<'a, S: Screen>: Draw<'a, S> + Sized { + fn push_x > + Copy> (self, w: X) + -> Push<'a, S, Self, X> { Push::X(self, w) } + fn push_y > + Copy> (self, h: X) + -> Push<'a, S, Self, X> { Push::Y(self, h) } + fn push_xy > + Copy> (self, w: X, h: X) + -> Push<'a, S, Self, X> { Push::XY(self, w, h) } +} + +/// ``` +/// # fn test_layout_push () -> Usually<()> { +/// assert_eq!(check_layout("1")?, Some(XYWH(1u16, 1, 1, 1))); +/// assert_eq!(check_layout("1".pull_x(1))?, Some(XYWH(0u16, 1, 1, 1))); +/// assert_eq!(check_layout("1".pull_y(1))?, Some(XYWH(1u16, 0, 1, 1))); +/// assert_eq!(check_layout("1".pull_xy(1, 1))?, Some(XYWH(0u16, 0, 1, 1))); +/// # Ok(()) } +/// ``` +pub trait LayoutPull<'a, S: Screen>: Draw<'a, S> + Sized { + fn pull_x > + Copy> (self, w: X) + -> Pull<'a, S, Self, X> { Pull::X(self, w) } + fn pull_y > + Copy> (self, h: X) + -> Pull<'a, S, Self, X> { Pull::Y(self, h) } + fn pull_xy > + Copy> (self, w: X, h: X) + -> Pull<'a, S, Self, X> { Pull::XY(self, w, h) } +} + +#[cfg(all(feature = "draw", feature = "eval"))] +pub fn kw_full <'a, O, S> (state: &'a S, to: &mut O, expr: &'a str) + -> PerhapsRef<'a, XYWH> +where + O: Screen + 'a, + S: Interpret<'a, O, Option>> + + Namespace + + Namespace + + Namespace> +{ + let thunk = draw(move|screen|ok_flat(expr.nth(1)?.map(|x: &'a str|state.interpret(screen, x)))); + Ok(match expr.head()? { + Some("full/x") | Some("full/w") => Full::W(thunk).draw(to)?, + Some("full/y") | Some("full/h") => Full::H(thunk).draw(to)?, + Some("full/xy") | Some("full/wh") => Full::WH(thunk).draw(to)?, + frag => return Err(format!("invalid variant: {frag:?}").into()) + }) +} + +#[cfg(all(feature = "draw", feature = "eval"))] +pub fn kw_exact <'a, O, S> (state: &'a S, to: &mut O, expr: &'a str) + -> PerhapsRef<'a, XYWH> +where + O: Screen + 'a, + S: Interpret<'a, O, Option>> + + Namespace + + Namespace + + Namespace> +{ + let thunk = draw(move|screen|ok_flat(expr.last()?.map(|x: &'a str|state.interpret(screen, x)))); + Ok(match expr.head()? { + Some("exact/w") => thunk.exact_w( + state.namespace(expr.nth(1)?)? + ).draw(to)?, + Some("exact/h") => thunk.exact_h( + state.namespace(expr.nth(1)?)? + ).draw(to)?, + Some("exact/wh") => thunk.exact_wh( + state.namespace(expr.nth(1)?)?, + state.namespace(expr.nth(2)?)?, + ).draw(to)?, + _ => return Err(format!("invalid variant: {:?}", expr.head()?).into()) + }) +} + +#[cfg(all(feature = "draw", feature = "eval"))] +pub fn kw_min <'a, O, S> (state: &'a S, to: &mut O, expr: &'a str) + -> PerhapsRef<'a, XYWH> +where + O: Screen + 'a, + S: Interpret<'a, O, Option>> + + Namespace + + Namespace + + Namespace> +{ + let thunk = draw(move|screen|ok_flat(expr.last()?.map(|x: &'a str|state.interpret(screen, x)))); + Ok(match expr.head()? { + Some("min/w") => thunk.min_w( + state.namespace(expr.nth(1)?)? + ).draw(to)?, + Some("min/h") => thunk.min_h( + state.namespace(expr.nth(1)?)? + ).draw(to)?, + Some("min/wh") => thunk.min_wh( + state.namespace(expr.nth(1)?)?, + state.namespace(expr.nth(2)?)?, + ).draw(to)?, + _ => return Err(format!("invalid variant: {:?}", expr.head()?).into()) + }) +} + +#[cfg(all(feature = "draw", feature = "eval"))] +pub fn kw_max <'a, O, S> (state: &'a S, to: &mut O, expr: &'a str) + -> PerhapsRef<'a, XYWH> +where + O: Screen + 'a, + S: Interpret<'a, O, Option>> + + Namespace + + Namespace + + Namespace> +{ + let thunk = draw(move|screen|ok_flat(expr.last()?.map(|x: &'a str|state.interpret(screen, x)))); + Ok(match expr.head()? { + Some("max/w") => thunk.max_w( + state.namespace(expr.nth(1)?)? + ).draw(to)?, + Some("max/h") => thunk.max_h( + state.namespace(expr.nth(1)?)? + ).draw(to)?, + Some("max/wh") => thunk.max_wh( + state.namespace(expr.nth(1)?)?, + state.namespace(expr.nth(2)?)?, + ).draw(to)?, + _ => return Err(format!("invalid variant: {:?}", expr.head()?).into()) + }) +} + +#[cfg(all(feature = "draw", feature = "eval"))] +pub fn kw_push <'a, O, S> (state: &'a S, to: &mut O, expr: &'a str) + -> PerhapsRef<'a, XYWH> +where + O: Screen + 'a, + S: Interpret<'a, O, Option>> + + Namespace + + Namespace + + Namespace> +{ + let thunk = draw(move|screen|ok_flat(expr.last()?.map(|x: &'a str|state.interpret(screen, x)))); + Ok(match expr.head()? { + Some("push/w") => thunk.push_x(state.namespace(expr.nth(1)?)?).draw(to)?, + Some("push/h") => thunk.push_y(state.namespace(expr.nth(1)?)?).draw(to)?, + Some("push/wh") => thunk.push_xy(state.namespace(expr.nth(1)?)?, state.namespace(expr.nth(2)?)?,).draw(to)?, + _ => return Err(format!("invalid variant: {:?}", expr.head()?).into()) + }) +} + +#[cfg(all(feature = "draw", feature = "eval"))] +pub fn kw_pull <'a, O, S> (state: &'a S, to: &mut O, expr: &'a str) + -> PerhapsRef<'a, XYWH> +where + O: Screen + 'a, + S: Interpret<'a, O, Option>> + + Namespace + + Namespace + + Namespace> +{ + let thunk = draw(move|screen|ok_flat(expr.last()?.map(|x: &'a str|state.interpret(screen, x)))); + Ok(match expr.head()? { + Some("pull/w") => thunk.pull_x(state.namespace(expr.nth(1)?)?).draw(to)?, + Some("pull/h") => thunk.pull_y(state.namespace(expr.nth(1)?)?).draw(to)?, + Some("pull/wh") => thunk.pull_xy(state.namespace(expr.nth(1)?)?, state.namespace(expr.nth(2)?)?,).draw(to)?, + _ => return Err(format!("invalid variant: {:?}", expr.head()?).into()) + }) +} + +#[cfg(all(feature = "draw", feature = "eval"))] +pub fn kw_pad <'a, O, S> (state: &'a S, to: &mut O, expr: &'a str) + -> PerhapsRef<'a, XYWH> +where + O: Screen + 'a, + S: Interpret<'a, O, Option>> + + Namespace + + Namespace + + Namespace> +{ + let thunk = draw(move|screen|ok_flat(expr.last()?.map(|x: &'a str|state.interpret(screen, x)))); + Ok(match expr.head()? { + Some("pad/w") => thunk.pad_w( + state.namespace(expr.nth(1)?)? + ).draw(to)?, + Some("pad/h") => thunk.pad_h( + state.namespace(expr.nth(1)?)? + ).draw(to)?, + Some("pad/wh") => thunk.pad_wh( + state.namespace(expr.nth(1)?)?, + state.namespace(expr.nth(2)?)?, + ).draw(to)?, + _ => return Err(format!("invalid variant: {:?}", expr.head()?).into()) + }) +} + +impl <'a, S: Screen, I: Draw<'a, S>> Draw<'a, S> for Full<'a, S, I> { + fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit> { let XYWH(x0, y0, w0, h0) = to.area(); let item = match self { Self::W(i) => i, Self::H(i) => i, Self::WH(i) => i, _ => unreachable!() @@ -17,19 +305,10 @@ def_layout_modifier_flat!( x1.unwrap_or(x2), y1.unwrap_or(y2), w1.unwrap_or(w2), h1.unwrap_or(h2), ))) } -); - -#[cfg(test)] #[test] fn test_layout_full () -> Usually<()> { - assert_eq!(check_layout("1")?, Some(XYWH(1u16, 1, 1, 1))); - assert_eq!(check_layout("1".full_w())?, Some(XYWH(1u16, 1, 80, 1))); - assert_eq!(check_layout("1".full_h())?, Some(XYWH(1u16, 1, 1, 25))); - assert_eq!(check_layout("1".full_wh())?, Some(XYWH(1u16, 1, 80, 25))); - Ok(()) } -def_layout_modifier!( - LayoutExact (exact_w exact_h exact_wh), - Exact { W H WH } kw_exact "exact" |self, to| { +impl <'a, S: Screen, I: Draw<'a, S>, X: Into> + Copy> Draw<'a, S> for Exact<'a, S, I, X> { + fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit> { let XYWH(x0, y0, w0, h0) = to.area(); let (item, w1, h1) = match self { Self::W(item, w1) => (item, (*w1).into(), None), @@ -48,19 +327,10 @@ def_layout_modifier!( _ => unreachable!() })) } -); - -#[cfg(test)] #[test] fn test_layout_exact () -> Usually<()> { - assert_eq!(check_layout("FOOBAR\nKILROY")?, Some(XYWH(1, 1, 6, 2))); - assert_eq!(check_layout("FOOBAR\nKILROY".exact_w(3))?, Some(XYWH(1, 1, 3, 2))); - assert_eq!(check_layout("FOOBAR\nKILROY".exact_h(1))?, Some(XYWH(1, 1, 6, 1))); - assert_eq!(check_layout("FOOBAR\nKILROY".exact_wh(2, 1))?, Some(XYWH(1, 1, 2, 1))); - Ok(()) } -def_layout_modifier!( - LayoutMin (min_w min_h min_wh), - Min { W H WH } kw_min "min" |self, to| { +impl <'a, S: Screen, I: Draw<'a, S>, X: Into> + Copy> Draw<'a, S> for Min<'a, S, I, X> { + fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit> { let XYWH(x0, y0, w0, h0) = to.area(); let (item, w1, h1) = match self { Self::W(item, w1) => (item, (*w1).into(), None), @@ -78,19 +348,10 @@ def_layout_modifier!( h1.map(|h1|h1.max(h2)).unwrap_or(h2) ))) } -); - -#[cfg(test)] #[test] fn test_layout_min () -> Usually<()> { - assert_eq!(check_layout("1".min_w(5))?, Some(XYWH(1u16, 1, 5, 1))); - assert_eq!(check_layout("1".min_h(5))?, Some(XYWH(1u16, 1, 1, 5))); - assert_eq!(check_layout("1".min_wh(5, 5))?, Some(XYWH(1u16, 1, 5, 5))); - assert_eq!(check_layout("123456".min_w(5))?, Some(XYWH(1u16, 1, 6, 1))); - Ok(()) } -def_layout_modifier!( - LayoutMax (max_w max_h max_wh), - Max { W H WH } kw_max "max" |self, to| { +impl <'a, S: Screen, I: Draw<'a, S>, X: Into> + Copy> Draw<'a, S> for Max<'a, S, I, X> { + fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit> { let XYWH(x, y, w0, h0) = to.area(); let (item, w, h) = match self { Self::W(item, w) => (item, (*w).into().unwrap_or(w0), h0), @@ -100,31 +361,10 @@ def_layout_modifier!( }; to.draw(XYWH(x, y, if w0 > w { w } else { w0 }, if h0 > h { h } else { h0 }), item) } -); +} -def_layout_modifier!( - LayoutPad (pad_w pad_h pad_wh), - Pad { X Y XY } kw_pad "pad" |self, to| { - let XYWH(x, y, w0, h0) = to.area(); - let (item, w, h) = match self { - Self::X(item, w) => (item, (*w).into().unwrap_or_default(), Default::default()), - Self::Y(item, h) => (item, Default::default(), (*h).into().unwrap_or_default()), - Self::XY(item, w, h) => (item, (*w).into().unwrap_or_default(), - (*h).into().unwrap_or_default()), - _ => return Ok(None) - }; - to.draw(XYWH( - x.plus(w), - y.plus(h), - w0.minus(w).minus(w), - h0.minus(h).minus(h), - ), item) - } -); - -def_layout_modifier!( - LayoutPush (push_x push_y push_xy), - Push { X Y XY } kw_push "push" |self, to| { +impl <'a, S: Screen, I: Draw<'a, S>, X: Into> + Copy> Draw<'a, S> for Push<'a, S, I, X> { + fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit> { match self { Self::__(_) => unreachable!(), Self::X(item, x1) if let Some(XYWH(x, y, w, h)) = to.size(None, item)? => { @@ -139,19 +379,10 @@ def_layout_modifier!( _ => Ok(None) } } -); - -#[cfg(test)] #[test] fn test_layout_push () -> Usually<()> { - assert_eq!(check_layout("1")?, Some(XYWH(1u16, 1, 1, 1))); - assert_eq!(check_layout("1".push_x(1))?, Some(XYWH(2u16, 1, 1, 1))); - assert_eq!(check_layout("1".push_y(1))?, Some(XYWH(1u16, 2, 1, 1))); - assert_eq!(check_layout("1".push_xy(1, 1))?, Some(XYWH(2u16, 2, 1, 1))); - Ok(()) } -def_layout_modifier!( - LayoutPull (pull_x pull_y pull_xy), - Pull { X Y XY } kw_pull "pull" |self, to| { +impl <'a, S: Screen, I: Draw<'a, S>, X: Into> + Copy> Draw<'a, S> for Pull<'a, S, I, X> { + fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit> { match self { Self::__(_) => unreachable!(), Self::X(item, x1) if let Some(XYWH(x, y, w, h)) = to.size(None, item)? => { @@ -172,16 +403,41 @@ def_layout_modifier!( _ => Ok(None) } } -); - -#[cfg(test)] #[test] fn test_layout_pull () -> Usually<()> { - assert_eq!(check_layout("1")?, Some(XYWH(1u16, 1, 1, 1))); - assert_eq!(check_layout("1".pull_x(1))?, Some(XYWH(0u16, 1, 1, 1))); - assert_eq!(check_layout("1".pull_y(1))?, Some(XYWH(1u16, 0, 1, 1))); - assert_eq!(check_layout("1".pull_xy(1, 1))?, Some(XYWH(0u16, 0, 1, 1))); - Ok(()) } -#[cfg(test)] fn check_layout (t: impl Draw) -> Perhaps> { +impl <'a, S: Screen, I: Draw<'a, S>, X: Into> + Copy> Draw<'a, S> for Pad<'a, S, I, X> { + fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit> { + let XYWH(x, y, w0, h0) = to.area(); + let (item, w, h) = match self { + Self::X(item, w) => (item, (*w).into().unwrap_or_default(), Default::default()), + Self::Y(item, h) => (item, Default::default(), (*h).into().unwrap_or_default()), + Self::XY(item, w, h) => (item, (*w).into().unwrap_or_default(), + (*h).into().unwrap_or_default()), + _ => return Ok(None) + }; + to.draw(XYWH( + x.plus(w), + y.plus(h), + w0.minus(w).minus(w), + h0.minus(h).minus(h), + ), item) + } +} + +impl<'a, S: Screen, T: Draw<'a, S>> LayoutFull<'a, S> for T {} + +impl<'a, S: Screen, T: Draw<'a, S>> LayoutExact<'a, S> for T {} + +impl<'a, S: Screen, T: Draw<'a, S>> LayoutMin<'a, S> for T {} + +impl<'a, S: Screen, T: Draw<'a, S>> LayoutMax<'a, S> for T {} + +impl<'a, S: Screen, T: Draw<'a, S>> LayoutPush<'a, S> for T {} + +impl<'a, S: Screen, T: Draw<'a, S>> LayoutPull<'a, S> for T {} + +impl<'a, S: Screen, T: Draw<'a, S>> LayoutPad<'a, S> for T {} + +#[cfg(test)] fn check_layout <'a> (t: impl Draw<'a, Tui>) -> Drawn<'a, u16> { Tui::Layout(XYWH(1u16, 1, 80, 25)).size(None, t) } diff --git a/src/layout/azimuth.rs b/src/layout/azimuth.rs index bfb4d80..484bfc7 100644 --- a/src/layout/azimuth.rs +++ b/src/layout/azimuth.rs @@ -18,9 +18,11 @@ pub struct Origin( pub(crate) T ); -impl_draw!(,>|self: Origin, _to: S|{ - todo!() -}); +impl<'a, S: Screen, T: Draw<'a, S>> Draw<'a, S> for Origin { + fn draw (&self, _to: &mut S) -> Drawn<'a, S::Unit> { + todo!() + } +} /// Something that has `[0, 0]` at a particular point. pub trait HasOrigin { @@ -33,25 +35,47 @@ impl> HasOrigin for T { } } -fn_kw_layout!(kw_align |state, output, expr| { - let head = expr.head(); - let mut frags = head.src()?.unwrap_or_default().split("/"); - Ok(matches!(frags.next(), Some("align")).then(||{ - draw(move|output: &mut O|{state.interpret(output, &expr.tail().head())}).align( - eval_enum!("align", output, state, - expr.head().src()?.unwrap_or_default().split("/").skip(1).next(), - Azimuth { - "c" => C, "x" => X, "y" => Y, - "n" => N, "s" => S, "e" => E, "w" => W, - "nw" => NW, "sw" => SW, "ne" => NE, "se" => SE, - }) - ).draw(output) - }).transpose()?.flatten()) -}); +#[cfg(all(feature = "draw", feature = "eval"))] +pub fn kw_align <'a, O, S> (state: &'a S, to: &mut O, expr: &'a str) + -> Drawn<'a, O::Unit> +where + O: Screen, + S: Interpret<'a, O, Option>> + + Namespace + + Namespace + + Namespace> +{ + let thunk = draw(move|screen|ok_flat(expr.nth(1)?.map(|x: &'a str|state.interpret(screen, x)))); + Ok(match expr.head()? { + Some("align/c") => Align(Some(Azimuth::C), thunk).draw(to)?, + Some("align/x") => Align(Some(Azimuth::X), thunk).draw(to)?, + Some("align/y") => Align(Some(Azimuth::Y), thunk).draw(to)?, + Some("align/n") => Align(Some(Azimuth::N), thunk).draw(to)?, + Some("align/s") => Align(Some(Azimuth::S), thunk).draw(to)?, + Some("align/e") => Align(Some(Azimuth::E), thunk).draw(to)?, + Some("align/w") => Align(Some(Azimuth::W), thunk).draw(to)?, + Some("align/nw") => Align(Some(Azimuth::NW), thunk).draw(to)?, + Some("align/sw") => Align(Some(Azimuth::SW), thunk).draw(to)?, + Some("align/ne") => Align(Some(Azimuth::NE), thunk).draw(to)?, + Some("align/se") => Align(Some(Azimuth::SE), thunk).draw(to)?, + frag => return Err(format!("invalid variant: {frag:?}").into()) + }) + //Ok(matches!(expr.head().src()?.unwrap_or_default().split("/").next(), Some("align")).then(||{ + //draw(move|output: &mut O|{state.interpret(output, &expr.nth(1))}).align( + //eval_enum!("align", output, state, + //expr.head().src()?.unwrap_or_default().split("/").skip(1).next(), + //Azimuth { + //"c" => C, "x" => X, "y" => Y, + //"n" => N, "s" => S, "e" => E, "w" => W, + //"nw" => NW, "sw" => SW, "ne" => NE, "se" => SE, + //}) + //).draw(output) + //}).transpose()?.flatten()) +} -impl> CanAlign for T {} +impl<'a, S: Screen, T: Draw<'a, S>> CanAlign<'a, S> for T {} -pub trait CanAlign: Draw + Sized { +pub trait CanAlign<'a, S: Screen>: Draw<'a, S> + Sized { fn align (self, azimuth: impl Into>) -> Align { Align(azimuth.into(), self) } @@ -95,8 +119,8 @@ pub struct Align( pub(crate) T, ); -impl> Draw for Align { - fn draw (&self, to: &mut S) -> Perhaps> { +impl<'a, S: Screen, T: Draw<'a, S>> Draw<'a, S> for Align { + fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit> { let Self(azimuth, item) = self; let area0 = to.area(); let size = to.size(area0, item)?; @@ -133,25 +157,17 @@ fn align ( } fn_kw_layout!(kw_split |state, output, expr| { - let head = expr.head(); - let mut frags = head.src()?.unwrap_or_default().split("/"); - Ok(matches!(frags.next(), Some("bsp")).then(||{ - eval_enum!("bsp", output, state, frags.next(), Split { - "n" => North, - "s" => South, - "e" => East, - "w" => West, - "a" => Above, - "b" => Below - }).stack( - draw(move|output: &mut O|{ - state.interpret(output, &expr.tail().head()?) - }), - draw(move|output: &mut O|{ - state.interpret(output, &expr.tail().tail().head()?) - }), - ).draw(output) - }).transpose()?.flatten()) + let thunk_a = draw(move|screen|ok_flat(expr.nth(1)?.map(|x: &'a str|state.interpret(screen, x)))); + let thunk_b = draw(move|screen|ok_flat(expr.nth(2)?.map(|x: &'a str|state.interpret(screen, x)))); + Ok(match expr.head()? { + Some("bsp/n") => Split::North.stack(thunk_a, thunk_b).draw(output)?, + Some("bsp/s") => Split::South.stack(thunk_a, thunk_b).draw(output)?, + Some("bsp/e") => Split::East.stack(thunk_a, thunk_b).draw(output)?, + Some("bsp/w") => Split::West.stack(thunk_a, thunk_b).draw(output)?, + Some("bsp/a") => Split::Above.stack(thunk_a, thunk_b).draw(output)?, + Some("bsp/b") => Split::Below.stack(thunk_a, thunk_b).draw(output)?, + frag => return Err(format!("invalid variant: {frag:?}").into()) + }) }); /// Split along an axis. Direction determines order. @@ -165,16 +181,28 @@ fn_kw_layout!(kw_split |state, output, expr| { #[default] Below } -pub struct Pair, B: Draw>(Split, A, B, PhantomData); +pub struct Pair(Split, A, B); -pub fn split , B: Draw> ( +/// ``` +/// # fn test_split_stack () -> Usually<()> { +/// use Split::*; +/// fn size_of , B: Draw> (stack: &Pair) -> Drawn<'a, S::Unit> { +/// Tui::Layout(XYWH(0, 0, 80, 25)).size(None, stack) +/// } +/// assert_eq!(size_of(&split(East, "foo", "bar"))?, Some(XYWH(0, 0, 6, 1))); +/// assert_eq!(size_of(&split(South, "foo", "bar"))?, Some(XYWH(0, 0, 3, 2))); +/// assert_eq!(size_of(&split(South, split(East, "foo", "bar"), "baz"))?, Some(XYWH(0, 0, 6, 2))); +/// assert_eq!(size_of(&split(East, split(South, "foo", "bar"), "baz"))?, Some(XYWH(0, 0, 6, 2))); +/// # return Ok(()); } +/// ``` +pub fn split <'a, S: Screen, A: Draw<'a, S>, B: Draw<'a, S>> ( split: Split, a: A, b: B -) -> Pair { - Pair(split, a, b, PhantomData) +) -> Pair { + Pair(split, a, b) } -impl, B: Draw> Draw for Pair { - fn draw (&self, to: &mut S) -> Drawn { +impl<'a, S: Screen, A: Draw<'a, S>, B: Draw<'a, S>> Draw<'a, S> for Pair { + fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit> { let Self(split, a, b, ..) = self; let (area_a, area_b) = stack_areas(split, to, a, b)?; let (drawn_a, drawn_b) = draw_stacks(split, to, a, area_a, None, b, area_b, None)?; @@ -182,16 +210,16 @@ impl, B: Draw> Draw for Pair { } } -fn draw_stacks ( +fn draw_stacks <'a, S: Screen> ( split: &Split, to: &mut S, - a: impl Draw, + a: impl Draw<'a, S>, area_a: impl Into>>, origin_a: impl Into>, - b: impl Draw, + b: impl Draw<'a, S>, area_b: impl Into>>, origin_b: impl Into>, -) -> Usually<(Option>, Option>)> { +) -> UsuallyRef<'a, (Option>, Option>)> { let draw_a = |to: &mut S|Ok::<_, Box>(match origin_a.into() { Some(origin_a) => to.draw(area_a.into(), a.align(origin_a))?, None => to.draw(area_a.into(), a)? @@ -209,12 +237,25 @@ fn draw_stacks ( }) } -pub fn stack_areas ( +/// ``` +/// # fn test_stack_areas () -> Usually<()> { +/// let area = XYWH(0u16, 0, 80, 25); +/// assert_eq!(stack_areas(&Split::East, &mut Tui::Layout(area), &"foo", &"bar")?, ( +/// Some(XYWH(0u16, 0, 3, 1)), +/// Some(XYWH(3u16, 0, 3, 1)), +/// )); +/// assert_eq!(stack_areas(&Split::South, &mut Tui::Layout(area), &"foo", &"bar")?, ( +/// Some(XYWH(0u16, 0, 3, 1)), +/// Some(XYWH(0u16, 1, 3, 1)), +/// )); +/// # Ok(()) } +/// ``` +pub fn stack_areas <'a, S: Screen> ( split: &Split, to: &mut S, - a: impl Draw, - b: impl Draw, -) -> Usually<(Option>, Option>)> { + a: impl Draw<'a, S>, + b: impl Draw<'a, S>, +) -> UsuallyRef<'a, (Option>, Option>)> { let area_a = to.size(None, a)?; Ok(match split { Split::South => ( @@ -289,8 +330,10 @@ impl Split { /// let _ = Split::East.stack(&"", &""); /// let _ = Split::West.stack(&"", &""); /// ``` - pub const fn stack , B: Draw> (&self, a: A, b: B) -> impl Draw { - Pair(*self, a, b, PhantomData) + pub const fn stack <'a, S: Screen, A: Draw<'a, S>, B: Draw<'a, S>> (&self, a: A, b: B) + -> impl Draw<'a, S> + { + Pair(*self, a, b) } /// ``` @@ -302,7 +345,9 @@ impl Split { /// let _ = Split::East.half(&"", &""); /// let _ = Split::West.half(&"", &""); /// ``` - pub const fn half , B: Draw> (&self, a: &A, b: &B) -> impl Draw { + pub const fn half <'a, S: Screen, A: Draw<'a, S>, B: Draw<'a, S>> (&self, a: &A, b: &B) + -> impl Draw<'a, S> + { draw(move|to: &mut S|{ let (area_a, area_b) = to.xywh().split_half(self); let (origin_a, origin_b) = self.origins(); @@ -356,28 +401,28 @@ impl Split { } -pub const fn east , B: Draw> (a: A, b: B) -> impl Draw { - Pair(Split::East, a, b, PhantomData) +pub const fn east <'a, S: Screen, A: Draw<'a, S>, B: Draw<'a, S>> (a: A, b: B) -> impl Draw<'a, S> { + Pair(Split::East, a, b) } -pub const fn north , B: Draw> (a: A, b: B) -> impl Draw { - Pair(Split::North, a, b, PhantomData) +pub const fn north <'a, S: Screen, A: Draw<'a, S>, B: Draw<'a, S>> (a: A, b: B) -> impl Draw<'a, S> { + Pair(Split::North, a, b) } -pub const fn west , B: Draw> (a: A, b: B) -> impl Draw { - Pair(Split::West, a, b, PhantomData) +pub const fn west <'a, S: Screen, A: Draw<'a, S>, B: Draw<'a, S>> (a: A, b: B) -> impl Draw<'a, S> { + Pair(Split::West, a, b) } -pub const fn south , B: Draw> (a: A, b: B) -> impl Draw { - Pair(Split::South, a, b, PhantomData) +pub const fn south <'a, S: Screen, A: Draw<'a, S>, B: Draw<'a, S>> (a: A, b: B) -> impl Draw<'a, S> { + Pair(Split::South, a, b) } -pub const fn above , B: Draw> (a: A, b: B) -> impl Draw { - Pair(Split::Above, a, b, PhantomData) +pub const fn above <'a, S: Screen, A: Draw<'a, S>, B: Draw<'a, S>> (a: A, b: B) -> impl Draw<'a, S> { + Pair(Split::Above, a, b) } -pub const fn below , B: Draw> (a: A, b: B) -> impl Draw { - Pair(Split::Below, a, b, PhantomData) +pub const fn below <'a, S: Screen, A: Draw<'a, S>, B: Draw<'a, S>> (a: A, b: B) -> impl Draw<'a, S> { + Pair(Split::Below, a, b) } #[macro_export] macro_rules! north { @@ -410,38 +455,6 @@ pub const fn below , B: Draw> (a: A, b: B) -> impl Draw ($head:expr, $($tail:expr),* $(,)?) => { below($head, below!($($tail,)*)) }; } -#[cfg(test)] #[test] fn test_stack_areas () -> Usually<()> { - let area = XYWH(0u16, 0, 80, 25); - - assert_eq!(stack_areas(&Split::East, &mut Tui::Layout(area), &"foo", &"bar")?, ( - Some(XYWH(0u16, 0, 3, 1)), - Some(XYWH(3u16, 0, 3, 1)), - )); - - assert_eq!(stack_areas(&Split::South, &mut Tui::Layout(area), &"foo", &"bar")?, ( - Some(XYWH(0u16, 0, 3, 1)), - Some(XYWH(0u16, 1, 3, 1)), - )); - - Ok(()) -} - -#[cfg(test)] #[test] fn test_split_stack () -> Usually<()> { - use Split::*; - fn size_of , B: Draw> (stack: &Pair) -> Perhaps> { - Tui::Layout(XYWH(0, 0, 80, 25)).size(None, stack) - } - assert_eq!(size_of(&split(East, "foo", "bar"))?, - Some(XYWH(0, 0, 6, 1))); - assert_eq!(size_of(&split(South, "foo", "bar"))?, - Some(XYWH(0, 0, 3, 2))); - assert_eq!(size_of(&split(South, split(East, "foo", "bar"), "baz"))?, - Some(XYWH(0, 0, 6, 2))); - assert_eq!(size_of(&split(East, split(South, "foo", "bar"), "baz"))?, - Some(XYWH(0, 0, 6, 2))); - return Ok(()); -} - #[cfg(test)] #[test] fn test_align () -> Usually<()> { let mut screen = Tui::Layout(XYWH(0, 0, 80, 25)); assert_eq!("FOOBAR\nKILROY".draw(&mut screen)?, Some(XYWH(0, 0, 6, 2))); diff --git a/src/layout/cond.rs b/src/layout/cond.rs index a95d8f1..6315d34 100644 --- a/src/layout/cond.rs +++ b/src/layout/cond.rs @@ -1,11 +1,10 @@ use crate::*; fn_kw_layout!(kw_when |state, output, expr| { - Ok(matches!(expr.head()?, Some("when")).then(||{ - when(state.namespace(expr.tail().head())?.unwrap(), - draw(move|output: &mut O|{state.interpret(output, &expr.tail().tail().head())}) - ).draw(output) - }).transpose()?.flatten()) + let thunk = draw(move|screen|ok_flat(expr.nth(2)?.map(|x: &'a str|state.interpret(screen, x)))); + ok_flat(matches!(expr.head()?, Some("when")).then(||{ + when(state.namespace(expr.nth(1)?)?.unwrap(), thunk) .draw(output) + })) }); /// Only render when condition is true. @@ -16,21 +15,16 @@ fn_kw_layout!(kw_when |state, output, expr| { /// when(true, "Yes") /// # } /// ``` -pub const fn when (condition: bool, item: impl Draw) -> impl Draw { +pub const fn when <'a, T: Screen> (condition: bool, item: impl Draw<'a, T>) -> impl Draw<'a, T> { draw(move|to: &mut T|if condition { item.draw(to) } else { Ok(Default::default()) }) } fn_kw_layout!(kw_either |state, output, expr| { - Ok(matches!(expr.head()?, Some("either")).then(||{ - either(state.namespace(expr.tail().head()?)?.unwrap(), - draw(move|output: &mut O|{ - state.interpret(output, &expr.tail().tail().head()?) - }), - draw(move|output: &mut O|{ - state.interpret(output, &expr.tail().tail().tail().head()?) - }), - ).draw(output) - }).transpose()?.flatten()) + let thunk_a = draw(move|screen|ok_flat(expr.nth(2)?.map(|x: &'a str|state.interpret(screen, x)))); + let thunk_b = draw(move|screen|ok_flat(expr.nth(3)?.map(|x: &'a str|state.interpret(screen, x)))); + ok_flat(matches!(expr.head()?, Some("either")).then(||{ + either(state.namespace(expr.nth(1)?)?.unwrap(), thunk_a, thunk_b).draw(output) + })) }); /// Render one thing if a condition is true and another false. @@ -41,6 +35,8 @@ fn_kw_layout!(kw_either |state, output, expr| { /// either(true, "Yes", "No") /// # } /// ``` -pub const fn either (condition: bool, a: impl Draw, b: impl Draw) -> impl Draw { +pub const fn either <'a, T: Screen> ( + condition: bool, a: impl Draw<'a, T>, b: impl Draw<'a, T> +) -> impl Draw<'a, T> { draw(move|to: &mut T|if condition { a.draw(to) } else { b.draw(to) }) } diff --git a/src/layout/iter.rs b/src/layout/iter.rs index 1662c0c..cede6d6 100644 --- a/src/layout/iter.rs +++ b/src/layout/iter.rs @@ -1,8 +1,8 @@ use super::*; -pub fn iter_south <'a, S: Screen, D: Draw, I: Iterator> ( +pub fn iter_south <'a, S: Screen, D: Draw<'a, S>, I: Iterator> ( iter: impl Fn()->I -) -> impl Draw { +) -> impl Draw<'a, S> { draw(move|to: &mut S|{ let XYWH(x, y, w, h) = to.area(); let mut y_next = y; @@ -22,9 +22,9 @@ pub fn iter_south <'a, S: Screen, D: Draw, I: Iterator> ( }) } -pub fn iter_east <'a, S: Screen, D: Draw, I: Iterator> ( +pub fn iter_east <'a, S: Screen, D: Draw<'a, S>, I: Iterator> ( iter: impl Fn()->I -) -> impl Draw { +) -> impl Draw<'a, S> { draw(move|to: &mut S|{ let XYWH(x, y, w, h) = to.area(); let mut x_next = x; @@ -45,74 +45,75 @@ pub fn iter_east <'a, S: Screen, D: Draw, I: Iterator> ( } /// Iterate over a collection of the same kind of [Draw]able: -pub fn iter <'a, S: Screen, D: 'a, I: Iterator, U: Draw> ( +pub fn iter <'a, S: Screen, D: 'a, I: Iterator, U: Draw<'a, S>> ( _iter: impl Fn()->I, _draw: impl Fn(D, usize)->U, -) -> impl Draw { +) -> impl Draw<'a, S> { draw(move|_to: &mut S|{ todo!() }) } /// Iterate over a collection of the same kind of [Draw]able: -pub fn iter_once <'a, S: Screen, D: 'a, U: Draw> ( +pub fn iter_once <'a, S: Screen, D: 'a, U: Draw<'a, S>> ( _iter: impl Iterator, _draw: impl Fn(D, usize)->U, -) -> impl Draw { +) -> impl Draw<'a, S> { draw(move|_to: &mut S|{ todo!() }) } /// Iterate over a collection of various [Draw]ables: pub fn iter_dyn < + 'a, S: Screen, // Target screen D, // Input doesn't need to be [Draw]able, output does V: Fn()->I, // Function that returns the iterator I: Iterator, // Type of the iterator - F: Fn(&D, usize)->dyn Draw, // Function that returns [Draw]able from iterator item -> (_items: V, _cb: F) -> impl Draw { + F: Fn(&D, usize)->dyn Draw<'a, S>, // Function that returns [Draw]able from iterator item +> (_items: V, _cb: F) -> impl Draw<'a, S> { draw(move|_to: &mut S|{ todo!() }) } -pub fn iter_north <'a, S: Screen, D: 'a, I: Iterator, U: Draw> ( +pub fn iter_north <'a, S: Screen, D: 'a, I: Iterator, U: Draw<'a, S>> ( _iter: impl Fn()->I, _draw: impl Fn(D, usize)->U, -) -> impl Draw { +) -> impl Draw<'a, S> { draw(move|_to: &mut S|{ todo!() }) } -pub fn iter_east_fixed <'a, S: Screen, D: 'a, I: Iterator, U: Draw> ( +pub fn iter_east_fixed <'a, S: Screen, D: 'a, I: Iterator, U: Draw<'a, S>> ( _height: S::Unit, _iter: impl Fn()->I, _draw: impl Fn(D, usize)->U, -) -> impl Draw { +) -> impl Draw<'a, S> { draw(move|_to: &mut S|{ todo!() }) } -pub fn iter_south_fixed <'a, S: Screen, D: 'a, I: Iterator, U: Draw> ( +pub fn iter_south_fixed <'a, S: Screen, D: 'a, I: Iterator, U: Draw<'a, S>> ( _height: S::Unit, _iter: impl Fn()->I, _draw: impl Fn(D, usize)->U, -) -> impl Draw { +) -> impl Draw<'a, S> { draw(move|_to: &mut S|{ todo!() }) } -pub fn iter_west <'a, S: Screen, D: 'a, I: Iterator, U: Draw> ( +pub fn iter_west <'a, S: Screen, D: 'a, I: Iterator, U: Draw<'a, S>> ( _iter: impl Fn()->I, _draw: impl Fn(D, usize)->U, -) -> impl Draw { +) -> impl Draw<'a, S> { draw(move|_to: &mut S|{ todo!() }) } -pub fn row_south (south: S::Unit, height: S::Unit, content: impl Draw) - -> impl Draw +pub fn row_south <'a, S: Screen + 'a> (south: S::Unit, height: S::Unit, content: impl Draw<'a, S>) + -> impl Draw<'a, S> { content.exact_h(height).push_y(south) } -pub fn row_north (north: S::Unit, height: S::Unit, content: impl Draw) - -> impl Draw +pub fn row_north <'a, S: Screen + 'a> (north: S::Unit, height: S::Unit, content: impl Draw<'a, S>) + -> impl Draw<'a, S> { content.exact_h(height).pull_y(north) } -pub fn col_east (east: S::Unit, width: S::Unit, content: impl Draw) - -> impl Draw +pub fn col_east <'a, S: Screen + 'a> (east: S::Unit, width: S::Unit, content: impl Draw<'a, S>) + -> impl Draw<'a, S> { content.exact_w(width).push_x(east) } -pub fn col_west (west: S::Unit, width: S::Unit, content: impl Draw) - -> impl Draw +pub fn col_west <'a, S: Screen + 'a> (west: S::Unit, width: S::Unit, content: impl Draw<'a, S>) + -> impl Draw<'a, S> { content.exact_w(width).pull_x(west) } diff --git a/src/lib.rs b/src/lib.rs index be344bc..ac7b155 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1265,17 +1265,17 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } draw: &impl Fn(&mut Self)->T ) -> T; /// Determine used area without drawing - fn size ( + fn size <'a> ( &mut self, area: impl Into>>, - draw: impl Draw - ) -> Perhaps>; + draw: impl Draw<'a, Self> + ) -> Drawn<'a, Self::Unit>; /// Draw - fn draw ( + fn draw <'a> ( &mut self, area: impl Into>>, - draw: impl Draw - ) -> Perhaps>; + draw: impl Draw<'a, Self> + ) -> Drawn<'a, Self::Unit>; } /// Implement the [Draw] trait for a particular drawable and [Screen]. @@ -1290,7 +1290,7 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } #[macro_export] macro_rules! impl_draw ( ($(<$($T:ident: $Trait:path,)+>)?| $self:ident:$Self:path, $to:ident:$To:ty - |$draw:block)=>{ impl$(<$($T:$Trait),+>)? Draw<$To> for $Self { + |$draw:block)=>{ impl$(<$($T:$Trait),+>)? Draw<'_, $To> for $Self { fn draw (&$self, $to: &mut $To) -> Perhaps::Unit>> $draw } }; ($(<$($T:ident: $Trait:path,)+>)?| @@ -1321,8 +1321,8 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } /// } /// } /// ``` - pub trait Draw { - fn draw (&self, to: &mut S) -> Drawn; + pub trait Draw<'a, S: Screen> { + fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit>; } /// Emit a [Draw]able. @@ -1345,14 +1345,14 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } /// let _ = view::(||"drawable"); /// let _ = view::(||Some("drawable")); /// ``` - pub const fn view , F: Fn()->T> (view: F) -> impl View { + pub const fn view Draw<'a, S>, F: Fn()->T> (view: F) -> impl View { ViewThunk(view, PhantomData) } /// Because we can't implement [Draw] for `F: FnOnce...` without conflicts. pub struct ViewThunk(pub F, std::marker::PhantomData); - impl, F: Fn()->T> View for ViewThunk { + impl Draw<'a, S>, F: Fn()->T> View for ViewThunk { fn view (&self) -> impl Draw { self.0() } @@ -1361,8 +1361,8 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } /// Because we can't implement [Draw] for `F: FnOnce...` without conflicts. pub struct DrawThunk(pub F, std::marker::PhantomData); - implPerhaps>> Draw for DrawThunk { - fn draw (&self, to: &mut T) -> Perhaps> { + impl<'a, T: Screen, F: Fn(&mut T)->PerhapsRef<'a, XYWH>> Draw<'a, T> for DrawThunk { + fn draw (&self, to: &mut T) -> PerhapsRef<'a, XYWH> { to.clip(None, &self.0) } } @@ -1373,23 +1373,25 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } /// # use tengri::*; /// let _ = draw(|to: &mut Tui|Ok(Some(to.area()))); // draws nothing /// ``` - pub const fn draw Perhaps>> ( + pub const fn draw <'a, T: Screen, F: Fn(&mut T)->PerhapsRef<'a, XYWH>> ( item: F ) -> DrawThunk { DrawThunk(item, std::marker::PhantomData) } - pub type Drawn = Perhaps>; + pub type Drawn<'a, U> = PerhapsRef<'a, XYWH>; - impl Draw for () { - fn draw (&self, _: &mut S) -> Drawn { + impl<'a, S: Screen> Draw<'a, S> for () { + fn draw (&self, _: &mut S) -> Drawn<'a, S::Unit> { Ok(None) } } - impl_draw!(,>|self: Option, to: S|{ - self.as_ref().map(|it|it.draw(to)).transpose().map(Option::unwrap_or_default) - }); + impl<'a, S: Screen, D: Draw<'a, S>> Draw<'a, S> for Option { + fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit> { + self.as_ref().map(|it|it.draw(to)).transpose().map(Option::unwrap_or_default) + } + } //impl> Draw for RwLock { //fn draw (&self, __: &mut S) -> Drawn { @@ -1403,8 +1405,8 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } //} //} - impl> Draw for &V { - fn draw (&self, to: &mut T) -> Perhaps> { + impl<'a, T: Screen, V: Draw<'a, T>> Draw<'a, T> for &V { + fn draw (&self, to: &mut T) -> Drawn<'a, T::Unit> { (*self).draw(to) } } @@ -1717,7 +1719,7 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } impl ItemTheme { #[cfg(feature = "term")] pub const G: [Self;256] = { - let mut builder = dizzle::konst::array::ArrayBuilder::new(); + let mut builder = ::konst::array::ArrayBuilder::new(); while !builder.is_full() { let index = builder.len() as u8; let light = (index as f64 * 1.15) as u8; @@ -1788,23 +1790,26 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } } } - pub trait ColorDsl: Sized { - fn new_g (expr: T, try_to_u8: impl Fn(Perhaps<&str>)->Perhaps) -> Usually; - fn new_rgb (expr: T, try_to_u8: impl Fn(Perhaps<&str>)->Perhaps) -> Usually; + pub trait ColorDsl<'a, T>: Sized { + fn new_g (expr: &'a T, try_to_u8: impl Fn(PerhapsRef<'a, &'a str>)->PerhapsRef<'a, u8>) + -> UsuallyRef<'a, Self>; + fn new_rgb (expr: &'a T, try_to_u8: impl Fn(PerhapsRef<'a, &str>)->PerhapsRef<'a, u8>) + -> UsuallyRef<'a, Self>; } - impl ColorDsl for Color { - fn new_g (expr: T, try_to_u8: impl Fn(Perhaps<&str>)->Perhaps) -> Usually { + impl<'a, T: Language<'a> + 'a> ColorDsl<'a, T> for Color { + fn new_g (expr: &'a T, try_to_u8: impl Fn(PerhapsRef<'a, &'a str>)->PerhapsRef<'a, u8>) + -> Result> + { let n = try_to_u8(expr.tail().map_err(Into::into))?.ok_or(Domain("not gray"))?; Ok(Self::Rgb(n, n, n)) } - fn new_rgb (expr: T, try_to_u8: impl Fn(Perhaps<&str>)->Perhaps) -> Usually { - let r = try_to_u8(expr.tail().map_err(Into::into))? - .ok_or(Domain("not red"))?; - let g = try_to_u8(expr.tail().tail().head().map_err(Into::into))? - .ok_or(Domain("not green"))?; - let b = try_to_u8(expr.tail().tail().tail().head().map_err(Into::into))? - .ok_or(Domain("not blue"))?; + fn new_rgb (expr: &'a T, try_to_u8: impl Fn(PerhapsRef<'a, &str>)->PerhapsRef<'a, u8>) + -> Result> + { + let r = try_to_u8(expr.nth(1).map_err(Into::into))?.ok_or(Domain("not red"))?; + let g = try_to_u8(expr.nth(2).map_err(Into::into))?.ok_or(Domain("not green"))?; + let b = try_to_u8(expr.nth(3).map_err(Into::into))?.ok_or(Domain("not blue"))?; Ok(Color::Rgb(r, g, b)) } } @@ -1902,7 +1907,7 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } /// } /// /// impl Interpret>> for App { - /// fn interpret_expr <'a> (&'a self, to: &mut Tui, lang: &'a impl Expression) + /// fn interpret_expr <'a> (&'a self, to: &mut Tui, lang: &'a impl Language) /// -> Usually>> /// { /// self.keyword(to, lang) @@ -1931,7 +1936,7 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } /// ``` pub trait Keywords : 'static { fn keywords () -> impl Iterator Perhaps>; - fn keyword (&self, to: &mut U, expr: &impl Expression) -> Perhaps { + fn keyword <'a> (&self, to: &mut U, expr: &'a impl Language<'a>) -> PerhapsRef<'a, V> { if let Some(expr) = expr.src()? { for keyword in Self::keywords() { if let Some(result) = keyword(self, to, &expr)? { @@ -1954,17 +1959,5 @@ pub trait AsMutOpt { fn as_mut_opt (&mut self) -> Option<&mut T>; } } } -pub fn invalid_variant ( - name: &str, - frag: impl Language, - expr: impl Language, - head: impl Language, -) -> Usually { - unimplemented!( - "{name}/{frag:?} ({expr:?}) ({head:?}) ({:?})", - head.src()?.unwrap_or_default().split("/").next() - ) -} - #[cfg(feature = "term")] pub use self::term::*; #[cfg(feature = "term")] mod term; diff --git a/src/term.rs b/src/term.rs index d41a823..97ba47d 100644 --- a/src/term.rs +++ b/src/term.rs @@ -96,14 +96,14 @@ impl Screen for Tui { fn area (&self) -> XYWH { self.into() } - fn size ( - &mut self, area: impl Into>>, draw: impl Draw, - ) -> Perhaps> { + fn size <'a> ( + &mut self, area: impl Into>>, draw: impl Draw<'a, Self>, + ) -> Drawn<'a, Self::Unit> { Self::Layout(self.area()).clip(area, &|to|draw.draw(to)) } - fn draw ( - &mut self, area: impl Into>>, draw: impl Draw - ) -> Perhaps> { + fn draw <'a> ( + &mut self, area: impl Into>>, draw: impl Draw<'a, Tui> + ) -> Drawn<'a, Self::Unit> { self.clip(area, &|to|draw.draw(to)) } fn clip ( @@ -289,7 +289,7 @@ impl Tui { /// let _ = tengri::Tui::catcher(&variant); /// } /// ``` - pub fn catcher > (result: &Usually) -> impl Draw { + pub fn catcher <'a, T: Draw<'a, Tui>> (result: &Usually) -> impl Draw<'a, Tui> { draw(move|to: &mut Tui|match result { Ok(content) => content.draw(to), Err(e) => { @@ -369,7 +369,7 @@ impl<'a: 'b, 'b> From<&'a mut Tui> for &'b mut XYWH { } } -pub const fn fill_char (c: char) -> impl Draw { +pub const fn fill_char <'a> (c: char) -> impl Draw<'a, Tui> { draw(move|to: &mut Tui|Ok(Some(to.update(&|cell,_,_|{ cell.set_char(c); })))) @@ -378,8 +378,8 @@ pub const fn fill_char (c: char) -> impl Draw { /// FIXME: Don't use `format!` but implement number blitting pub struct ShowSize; -impl Draw for ShowSize { - fn draw (&self, to: &mut Tui) -> Drawn { +impl<'a> Draw<'a, Tui> for ShowSize { + fn draw (&self, to: &mut Tui) -> Drawn<'a, u16> { let area = to.area(); let info = format!("{area:?}"); to.text(&info, area.0, area.1, info.len() as u16) @@ -388,8 +388,8 @@ impl Draw for ShowSize { pub struct ShowSizeOf(pub T); -impl> Draw for ShowSizeOf { - fn draw (&self, to: &mut Tui) -> Drawn { +impl<'a, T: Draw<'a, Tui>> Draw<'a, Tui> for ShowSizeOf { + fn draw (&self, to: &mut Tui) -> Drawn<'a, u16> { Ok(self.0.draw(to)?.map(|used|{ let _ = to.text(&format!("{used:?}"), used.0, used.1, u16::MAX); used @@ -398,13 +398,14 @@ impl> Draw for ShowSizeOf { } #[cfg(feature = "eval")] fn_kw_layout_tui!(kw_tui_text |_state, output, expr| { - Ok(matches!(expr.head()?, Some("text")).then(||{ - if let Some(src) = expr.tail().src()? && src.len() > 0 { - (&src[1..]).draw(output) - } else { - return Ok(None) - } - }).transpose()?.flatten()) + Ok(if matches!(expr.head()?, Some("text")) { + expr.tail()? + .and_then(|src|(src.len() > 0).then(||(&src[1..]).draw(output))) + .transpose()? + .flatten() + } else { + None + }) }); #[cfg(feature = "text")] pub use self::tui_text::*; @@ -473,8 +474,8 @@ impl> Draw for ShowSizeOf { self.as_ref().draw(to) }); - impl Draw for &str { - fn draw (&self, to: &mut Tui) -> Drawn { + impl<'a> Draw<'a, Tui> for &str { + fn draw (&self, to: &mut Tui) -> Drawn<'a, u16> { let XYWH(x, y, w, h) = to.area(); let mut max_w = 0u16; let mut max_h = 0u16; @@ -493,8 +494,8 @@ impl> Draw for ShowSizeOf { } } - impl<'t, T: AsRef> Draw for TrimStr<'_, T> { - fn draw (&self, to: &mut Tui) -> Drawn { + impl<'a, T: AsRef> Draw<'a, Tui> for TrimStr<'a, T> { + fn draw (&self, to: &mut Tui) -> Drawn<'a, u16> { let text = self.1.as_ref(); let area = layout_text_u16(text, to.area())?.unwrap(); let XYWH(x, y, w, ..) = to.area(); diff --git a/src/term/border.rs b/src/term/border.rs index 1edbc5a..c7167ab 100644 --- a/src/term/border.rs +++ b/src/term/border.rs @@ -5,7 +5,9 @@ use crate::*; /// ``` /// /// TODO /// ``` -pub fn border (on: bool, style: impl BorderStyle, item: impl Draw) -> impl Draw { +pub fn border <'a> ( + on: bool, style: impl BorderStyle<'a>, item: impl Draw<'a, Tui> +) -> impl Draw<'a, Tui> { let content = item.pad_wh(1, 1); let outline = when(on, draw(move|to: &mut Tui|{ let XYWH(x, y, w, h) = to.area(); @@ -33,7 +35,7 @@ macro_rules! border { $nw:literal $n:literal $ne:literal $w:literal $e:literal $sw:literal $s:literal $se:literal $($x:tt)* }),+) => {$( - impl BorderStyle for $T { + impl<'a> BorderStyle<'a> for $T { const NW: &'static str = $nw; const N: &'static str = $n; const NE: &'static str = $ne; @@ -47,7 +49,7 @@ macro_rules! border { } #[derive(Copy, Clone)] pub struct $T(pub bool, pub Style); //impl Layout for $T {} - impl Draw for $T { + impl<'a> Draw<'a, Tui> for $T { fn draw (&self, to: &mut Tui) -> Perhaps> { when(self.enabled(), draw(|to: &mut Tui|BorderStyle::draw(*self, to).map(Some))).draw(to) } @@ -161,7 +163,7 @@ border! { } } -pub trait BorderStyle: Draw + Copy { +pub trait BorderStyle<'a>: Draw<'a, Tui> + Copy { fn enabled (&self) -> bool; fn border_n (&self) -> &str { Self::N } fn border_s (&self) -> &str { Self::S } @@ -172,7 +174,7 @@ pub trait BorderStyle: Draw + Copy { fn border_sw (&self) -> &str { Self::SW } fn border_se (&self) -> &str { Self::SE } - #[inline] fn draw <'a> (self, to: &mut Tui) -> Usually> { + #[inline] fn draw (self, to: &mut Tui) -> Usually> { if self.enabled() { self.draw_h(to, None)?; self.draw_v(to, None)?; diff --git a/src/term/keys.rs b/src/term/keys.rs index f4ab04e..ace0cf6 100644 --- a/src/term/keys.rs +++ b/src/term/keys.rs @@ -1,4 +1,4 @@ -use ::dizzle::{Language, Symbol, Usually}; +use ::dizzle::*; use ::crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState}; /// TUI key spec. @@ -70,7 +70,7 @@ impl TuiKey { } #[cfg(feature = "lang")] - pub fn from_dsl (dsl: impl Language) -> Usually { + pub fn from_dsl <'a> (dsl: &'a impl Language<'a>) -> UsuallyRef<'a, Self> { if let Some(word) = dsl.word()? { let word = word.trim(); Ok(if word == ":char" { diff --git a/src/term/modify.rs b/src/term/modify.rs index 9542452..0ba0c37 100644 --- a/src/term/modify.rs +++ b/src/term/modify.rs @@ -2,36 +2,30 @@ use crate::*; use Color::*; fn_kw_layout_tui!(kw_tui_fg |state, output, expr| { + let thunk = draw(move|screen|ok_flat(expr.nth(2)?.map(|x: &'a str|state.interpret(screen, x)))); Ok(matches!(expr.head()?, Some("fg")).then(||{ - let args = expr.tail(); - let arg0 = args.head(); - let tail0 = args.tail(); - let arg1 = tail0.head(); - if let Some(color) = state.namespace(arg0?.expect("fg: expected arg 0 (color)"))? { - fg(color, draw(move|to: &mut Tui|state.interpret(to, &arg1))).draw(output) + if let Some(color) = state.namespace(expr.nth(1)?.expect("fg: expected arg 0 (color)"))? { + fg(color, thunk).draw(output) } else { - return Err(format!("fg: {arg0:?}: not a color").into()) + return Err(format!("fg: {:?}: not a color", expr.nth(1)).into()) } }).transpose()?.flatten()) }); fn_kw_layout_tui!(kw_tui_bg |state, output, expr| { + let thunk = draw(move|screen|ok_flat(expr.nth(2)?.map(|x: &'a str|state.interpret(screen, x)))); Ok(matches!(expr.head()?, Some("bg")).then(||{ - let args = expr.tail(); - let arg0 = args.head(); - let tail0 = args.tail(); - let arg1 = tail0.head(); - if let Some(color) = state.namespace(arg0?.expect("bg: expected arg 0 (color)"))? { - bg(color, draw(move|to: &mut Tui|state.interpret(to, &arg1))).draw(output) + if let Some(color) = state.namespace(expr.nth(1)?.expect("bg: expected arg 0 (color)"))? { + bg(color, thunk).draw(output) } else { - return Err(format!("bg: {arg0:?}: not a color").into()) + return Err(format!("bg: {:?}: not a color", expr.nth(1)?).into()) } }).transpose()?.flatten()) }); impl Tui { - pub fn eval_color_expr (state: &impl Namespace, src: impl Language) - -> Perhaps + pub fn eval_color_expr <'a> (state: &impl Namespace, src: &'a impl Language<'a>) + -> Result, Box> { if let Some(expr) = src.expr()? { match (expr.head()?, expr.tail()?) { @@ -63,7 +57,9 @@ impl Tui { } /// Apply foreground color. -pub const fn fg (fg: Color, item: impl Draw) -> impl Draw { +pub const fn fg <'a> (fg: Color, item: impl Draw<'a, Tui>) + -> impl Draw<'a, Tui> +{ draw(move|to: &mut Tui|{ if let Some(size) = to.size(None, &item)? { to.draw(Some(size), draw(|to: &mut Tui|{ @@ -77,7 +73,9 @@ pub const fn fg (fg: Color, item: impl Draw) -> impl Draw { } /// Apply background color. -pub const fn bg (bg: Color, item: impl Draw) -> impl Draw { +pub const fn bg <'a> (bg: Color, item: impl Draw<'a, Tui>) + -> impl Draw<'a, Tui> +{ draw(move|to: &mut Tui|{ if let Some(size) = to.size(None, &item)? { let _ = to.draw(Some(size), draw(|to: &mut Tui|{ @@ -90,7 +88,9 @@ pub const fn bg (bg: Color, item: impl Draw) -> impl Draw { }) } -pub const fn fg_bg (fg: Color, bg: Color, item: impl Draw) -> impl Draw { +pub const fn fg_bg <'a> (fg: Color, bg: Color, item: impl Draw<'a, Tui>) + -> impl Draw<'a, Tui> +{ draw(move|to: &mut Tui|{ if let Some(size) = to.size(None, &item)? { let _ = to.draw(Some(size), draw(|to: &mut Tui|{ @@ -104,14 +104,16 @@ pub const fn fg_bg (fg: Color, bg: Color, item: impl Draw) -> impl Draw) -> impl Draw { +pub const fn modify <'a> (on: bool, modifier: Modifier, item: impl Draw<'a, Tui>) + -> impl Draw<'a, Tui> +{ draw(move|to: &mut Tui|{ fill_mod(on, modifier).draw(to)?; item.draw(to) }) } -pub const fn fill_mod (on: bool, modifier: Modifier) -> impl Draw { +pub const fn fill_mod <'a> (on: bool, modifier: Modifier) -> impl Draw<'a, Tui> { draw(move|to: &mut Tui|Ok(Some({ if on { to.update(&|cell,_,_|cell.modifier.insert(modifier)) @@ -122,11 +124,11 @@ pub const fn fill_mod (on: bool, modifier: Modifier) -> impl Draw { } /// Draw contents with bold modifier applied. -pub const fn bold (on: bool, item: impl Draw) -> impl Draw { +pub const fn bold <'a> (on: bool, item: impl Draw<'a, Tui>) -> impl Draw<'a, Tui> { modify(on, Modifier::BOLD, item) } -pub const fn fill_ul (color: Option) -> impl Draw { +pub const fn fill_ul <'a> (color: Option) -> impl Draw<'a, Tui> { draw(move|to: &mut Tui|Ok(Some(if let Some(color) = color { to.update(&|cell,_,_|{ cell.modifier.insert(Modifier::UNDERLINED); diff --git a/src/term/phat.rs b/src/term/phat.rs index c430939..e494a20 100644 --- a/src/term/phat.rs +++ b/src/term/phat.rs @@ -5,7 +5,9 @@ use crate::*; /// ``` /// /// TODO /// ``` -pub fn phat (w: u16, h: u16, [fg, bg, hi, lo]: [Color;4], draw: impl Draw) -> impl Draw { +pub fn phat <'a> ( + w: u16, h: u16, [fg, bg, hi, lo]: [Color;4], draw: impl Draw<'a, Tui> +) -> impl Draw<'a, Tui> { let top = phat_lo(bg, hi).exact_h(1); let low = phat_hi(bg, lo).exact_h(1); let draw = fg_bg(fg, bg, draw); @@ -17,11 +19,11 @@ pub const LO: &'static str = "▄"; pub const HI: &'static str = "▀"; /// A phat line -fn phat_lo (fg: Color, bg: Color) -> impl Draw { +fn phat_lo <'a> (fg: Color, bg: Color) -> impl Draw<'a, Tui> { fg_bg(fg, bg, x_repeat(LO)).exact_h(1) } /// A phat line -fn phat_hi (fg: Color, bg: Color) -> impl Draw { +fn phat_hi <'a> (fg: Color, bg: Color) -> impl Draw<'a, Tui> { fg_bg(fg, bg, x_repeat(HI)).exact_h(1) } diff --git a/src/term/scroll.rs b/src/term/scroll.rs index 0c1bf75..8dc16a8 100644 --- a/src/term/scroll.rs +++ b/src/term/scroll.rs @@ -6,7 +6,7 @@ pub const ICON_INC_V: &[char] = &['▼']; pub const ICON_DEC_H: &[char] = &[' ', '🞀', ' ']; pub const ICON_INC_H: &[char] = &[' ', '🞂', ' ']; -pub fn x_scroll () -> impl Draw { +pub fn x_scroll <'a> () -> impl Draw<'a, Tui> { draw(|to: &mut Tui|{ let XYWH(x1, y1, w, _h) = to.area(); let x2 = x1 + w; @@ -37,7 +37,7 @@ pub fn x_scroll () -> impl Draw { }) } -pub fn y_scroll () -> impl Draw { +pub fn y_scroll <'a> () -> impl Draw<'a, Tui> { draw(|to: &mut Tui|{ let XYWH(x1, y1, _w, h) = to.area(); let y2 = y1 + h;