diff --git a/Justfile b/Justfile index 1d5f794..3dbff77 100644 --- a/Justfile +++ b/Justfile @@ -2,9 +2,6 @@ export LLVM_PROFILE_FILE := "cov/cargo-test-%p-%m.profraw" grcov-binary := "--binary-path ./target/coverage/deps/" grcov-ignore := "--ignore-not-existing --ignore '../*' --ignore \"/*\" --ignore 'target/*'" -default: - just -l - bacon: bacon -s @@ -28,7 +25,5 @@ doc: CARGO_INCREMENTAL=0 RUSTFLAGS='-Cinstrument-coverage' RUSTDOCFLAGS='-Cinstrument-coverage' \ cargo doc -example-tui-00: - cargo run -p tengri_tui --example tui_00 -example-tui-01: - cargo run -p tengri_tui --example tui_01 +example-tui: + cargo run -p tengri_tui --example tui diff --git a/bacon.toml b/bacon.toml deleted file mode 100644 index 8a5516f..0000000 --- a/bacon.toml +++ /dev/null @@ -1,44 +0,0 @@ -default_job = "check-all" - -env.CARGO_TERM_COLOR = "always" - -[keybindings] -c = "job:check" -d = "job:doc-open" -t = "job:test" -n = "job:nextest" -l = "job:clippy" - -[jobs] - -[jobs.check] -command = ["cargo", "check"] -need_stdout = false -watch = ["core","dsl","editor","input","output","proc","tengri","tui"] - -[jobs.clippy-all] -command = ["cargo", "clippy"] -need_stdout = false -watch = ["tek", "deps"] - -[jobs.test] -command = ["cargo", "test"] -need_stdout = true -watch = ["tek", "deps"] - -[jobs.doc] -command = ["cargo", "doc", "--no-deps"] -need_stdout = false - -[jobs.doc-open] -command = ["cargo", "doc", "--no-deps", "--open"] -need_stdout = false -on_success = "back" # so that we don't open the browser at each change - -[skin] -status_fg = 251 -status_bg = 200 -key_fg = 11 -status_key_fg = 11 -project_name_badge_fg = 11 -project_name_badge_bg = 69 diff --git a/core/src/core_macros.rs b/core/src/core_macros.rs index ef56421..a8fdda4 100644 --- a/core/src/core_macros.rs +++ b/core/src/core_macros.rs @@ -10,25 +10,22 @@ )* }; ); -/// Define a trait and implement it for read-only wrapper types. +/// Define a trait an implement it for read-only wrapper types. */ #[macro_export] macro_rules! flex_trait ( ($Trait:ident $(<$($A:ident:$T:ident),+>)? $(:$dep:ident $(<$dtt:tt>)? $(+$dep2:ident $(<$dtt2:tt>)?)*)? { - $(fn $fn:ident $(<$($fl:lifetime),*>)? (& $($fl2:lifetime)* $self:ident $(, $arg:ident:$ty:ty)*) $(-> $ret:ty)? $body:block)* + $(fn $fn:ident (&$self:ident $(, $arg:ident:$ty:ty)*) -> $ret:ty $body:block)* }) => { pub trait $Trait $(<$($A: $T),+>)? $(:$dep $(<$dtt>)? $(+$dep2 $(<$dtt2>)?)*)? { - $(fn $fn $(<$($fl),*>)? (& $($fl2)* $self $(,$arg:$ty)*) $(-> $ret)? $body)* + $(fn $fn (&$self $(,$arg:$ty)*) -> $ret $body)* } impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for &_T_ { - $(fn $fn $(<$($fl),*>)? (& $($fl2)* $self $(,$arg:$ty)*) $(-> $ret)? { (*$self).$fn($($arg),*) })* + $(fn $fn (&$self $(,$arg:$ty)*) -> $ret { (*$self).$fn($($arg),*) })* } impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for &mut _T_ { - $(fn $fn $(<$($fl),*>)? (& $($fl2)* $self $(,$arg:$ty)*) $(-> $ret)? { (**$self).$fn($($arg),*) })* + $(fn $fn (&$self $(,$arg:$ty)*) -> $ret { (**$self).$fn($($arg),*) })* } impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Arc<_T_> { - $(fn $fn $(<$($fl),*>)? (& $($fl2)* $self $(,$arg:$ty)*) $(-> $ret)? { (*$self).$fn($($arg),*) })* - } - impl<$($($A: $T,)+)?> $Trait $(<$($A),+>)? for Box)?> { - $(fn $fn $(<$($fl),*>)? (& $($fl2)* $self $(,$arg:$ty)*) $(-> $ret)? { (**$self).$fn($($arg),*) })* + $(fn $fn (&$self $(,$arg:$ty)*) -> $ret { (*$self).$fn($($arg),*) })* } //impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for Option<_T_> { //$(fn $fn (&$self $(,$arg:$ty)*) -> $ret { @@ -43,24 +40,21 @@ //} }); /// Define a trait an implement it for read-only wrapper types. */ -#[macro_export] macro_rules! flex_trait_sized ( +#[macro_export] macro_rules! flex_trait_unsized ( ($Trait:ident $(<$($A:ident:$T:ident),+>)? $(:$dep:ident $(<$dtt:tt>)? $(+$dep2:ident $(<$dtt2:tt>)?)*)? { - $(fn $fn:ident (&$self:ident $(, $arg:ident:$ty:ty)*) $(-> $ret:ty)? $body:block)* + $(fn $fn:ident (&$self:ident $(, $arg:ident:$ty:ty)*) -> $ret:ty $body:block)* }) => { - pub trait $Trait $(<$($A: $T),+>)? : $($dep $(<$dtt>+)? $($dep2 $(<$dtt2>)?)*+)? Sized { - $(fn $fn (&$self $(,$arg:$ty)*) $(-> $ret)? $body)* + pub trait $Trait $(<$($A: $T),+>)? : $($dep $(<$dtt>+)? $($dep2 $(<$dtt2>)?)*+)? ?Sized { + $(fn $fn (&$self $(,$arg:$ty)*) -> $ret $body)* } impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for &_T_ { - $(fn $fn (&$self $(,$arg:$ty)*) $(-> $ret)? { (*$self).$fn($($arg),*) })* + $(fn $fn (&$self $(,$arg:$ty)*) -> $ret { (*$self).$fn($($arg),*) })* } impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for &mut _T_ { - $(fn $fn (&$self $(,$arg:$ty)*) $(-> $ret)? { (**$self).$fn($($arg),*) })* + $(fn $fn (&$self $(,$arg:$ty)*) -> $ret { (**$self).$fn($($arg),*) })* } impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Arc<_T_> { - $(fn $fn (&$self $(,$arg:$ty)*) $(-> $ret)? { (*$self).$fn($($arg),*) })* - } - impl<$($($A: $T,)+)?> $Trait $(<$($A),+>)? for Box)?> { - $(fn $fn (&$self $(,$arg:$ty)*) $(-> $ret)? { (**$self).$fn($($arg),*) })* + $(fn $fn (&$self $(,$arg:$ty)*) -> $ret { (*$self).$fn($($arg),*) })* } //impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for Option<_T_> { //$(fn $fn (&$self $(,$arg:$ty)*) -> $ret { diff --git a/dsl/src/dsl_test.rs b/dsl/src/dsl_test.rs index fb6d6f7..253b4c9 100644 --- a/dsl/src/dsl_test.rs +++ b/dsl/src/dsl_test.rs @@ -1,16 +1,16 @@ use crate::*; -macro_rules!is_some(($expr:expr, $val:expr)=>{assert_eq!($expr, Ok(Some($val)))};); -macro_rules!is_none(($expr:expr)=>{assert_eq!($expr, Ok(None))};); -macro_rules!is_err(($expr:expr)=>{assert!($expr.is_err())}; - ($expr:expr, $err:expr)=>{assert_eq!($expr, Err($err))};); -#[test] fn test_expr () -> Result<(), DslError> { - let e0 = DslError::Unexpected('a', None, None); - let e1 = DslError::Unexpected('(', None, None); - let e2 = DslError::Unexpected('b', None, None); - let e3 = DslError::Unexpected('d', None, None); - let check = |src: &str, word, expr, head, tail|{ - assert_eq!(src.word(), word, "{src}"); - assert_eq!(src.expr(), expr, "{src}"); +macro_rules!is_some(($exp:expr, $val:expr)=>{assert_eq!($exp, Ok(Some($val)))};); +macro_rules!is_none(($exp:expr)=>{assert_eq!($exp, Ok(None))};); +macro_rules!is_err(($exp:expr)=>{assert!($exp.is_err())}; + ($exp:expr, $err:expr)=>{assert_eq!($exp, Err($err))};); +#[test] fn test_exp () -> Result<(), DslError> { + let e0 = DslError::Unexpected('a'); + let e1 = DslError::Unexpected('('); + let e2 = DslError::Unexpected('b'); + let e3 = DslError::Unexpected('d'); + let check = |src: &str, key, exp, head, tail|{ + assert_eq!(src.key(), key, "{src}"); + assert_eq!(src.exp(), exp, "{src}"); assert_eq!(src.head(), head, "{src}"); assert_eq!(src.tail(), tail, "{src}"); }; @@ -21,43 +21,43 @@ macro_rules!is_err(($expr:expr)=>{assert!($expr.is_err())}; check("(a b c) d e f", Err(e1), Err(e3), Ok(Some("(a b c)")), Ok(Some("d e f"))); check("a (b c d) e f", Err(e1), Err(e0), Ok(Some("a")), Ok(Some("(b c d) e f"))); - is_some!(word_peek("\n :view/transport"), ":view/transport"); + is_some!(sym_peek("\n :view/transport"), ":view/transport"); - assert!(is_space(' ')); - assert!(!is_word_char(' ')); - assert!(is_word_char('f')); + assert!(is_whitespace(' ')); + assert!(!is_key_start(' ')); + assert!(is_key_start('f')); - is_some!(word_seek_start("foo"), 0); - is_some!(word_seek_start("foo "), 0); - is_some!(word_seek_start(" foo "), 1); - is_some!(word_seek_length(&" foo "[1..]), 3); - is_some!(word_seek("foo"), (0, 3)); - is_some!(word_peek("foo"), "foo"); - is_some!(word_seek("foo "), (0, 3)); - is_some!(word_peek("foo "), "foo"); - is_some!(word_seek(" foo "), (1, 3)); - is_some!(word_peek(" foo "), "foo"); + is_some!(key_seek_start("foo"), 0); + is_some!(key_seek_start("foo "), 0); + is_some!(key_seek_start(" foo "), 1); + is_some!(key_seek_length(&" foo "[1..]), 3); + is_some!(key_seek("foo"), (0, 3)); + is_some!(key_peek("foo"), "foo"); + is_some!(key_seek("foo "), (0, 3)); + is_some!(key_peek("foo "), "foo"); + is_some!(key_seek(" foo "), (1, 3)); + is_some!(key_peek(" foo "), "foo"); - is_err!("(foo)".word()); - is_err!("foo".expr()); + is_err!("(foo)".key()); + is_err!("foo".exp()); - is_some!("(foo)".expr(), "foo"); + is_some!("(foo)".exp(), "foo"); is_some!("(foo)".head(), "(foo)"); is_none!("(foo)".tail()); - is_some!("(foo bar baz)".expr(), "foo bar baz"); + is_some!("(foo bar baz)".exp(), "foo bar baz"); is_some!("(foo bar baz)".head(), "(foo bar baz)"); is_none!("(foo bar baz)".tail()); - is_some!("(foo bar baz)".expr().head(), "foo"); - is_some!("(foo bar baz)".expr().tail(), "bar baz"); - is_some!("(foo bar baz)".expr().tail().head(), "bar"); - is_some!("(foo bar baz)".expr().tail().tail(), "baz"); + is_some!("(foo bar baz)".exp().head(), "foo"); + is_some!("(foo bar baz)".exp().tail(), "bar baz"); + is_some!("(foo bar baz)".exp().tail().head(), "bar"); + is_some!("(foo bar baz)".exp().tail().tail(), "baz"); - is_err!("foo".expr()); - is_some!("foo".word(), "foo"); - is_some!(" foo".word(), "foo"); - is_some!(" foo ".word(), "foo"); + is_err!("foo".exp()); + is_some!("foo".key(), "foo"); + is_some!(" foo".key(), "foo"); + is_some!(" foo ".key(), "foo"); is_some!(" foo ".head(), "foo"); //assert_eq!(" foo ".head().head(), Ok(None)); @@ -84,16 +84,16 @@ macro_rules!is_err(($expr:expr)=>{assert!($expr.is_err())}; is_some!(" (foo) (bar) ".tail(), " (bar) "); is_some!(" (foo) (bar) ".tail().head(), "(bar)"); is_some!(" (foo) (bar) ".tail().head().head(), "(bar)"); - is_some!(" (foo) (bar) ".tail().head().expr(), "bar"); - is_some!(" (foo) (bar) ".tail().head().expr().head(), "bar"); + is_some!(" (foo) (bar) ".tail().head().exp(), "bar"); + is_some!(" (foo) (bar) ".tail().head().exp().head(), "bar"); is_some!(" (foo bar baz) ".head(), "(foo bar baz)"); is_some!(" (foo bar baz) ".head().head(), "(foo bar baz)"); - is_some!(" (foo bar baz) ".expr(), "foo bar baz"); - is_some!(" (foo bar baz) ".expr().head(), "foo"); - is_some!(" (foo bar baz) ".expr().tail(), "bar baz"); - is_some!(" (foo bar baz) ".expr().tail().head(), "bar"); - is_some!(" (foo bar baz) ".expr().tail().tail(), "baz"); + is_some!(" (foo bar baz) ".exp(), "foo bar baz"); + is_some!(" (foo bar baz) ".exp().head(), "foo"); + is_some!(" (foo bar baz) ".exp().tail(), "bar baz"); + is_some!(" (foo bar baz) ".exp().tail().head(), "bar"); + is_some!(" (foo bar baz) ".exp().tail().tail(), "baz"); is_none!(" (foo bar baz) ".tail()); Ok(()) } diff --git a/output/src/content.rs b/output/src/content.rs index 4407023..f797f98 100644 --- a/output/src/content.rs +++ b/output/src/content.rs @@ -1,110 +1,40 @@ use crate::*; -pub trait HasContent { fn content (&self) -> impl Content; } -pub trait Content: Draw + Layout {} -impl + Layout> Content for T {} -impl<'a, O: Out> AsRef + 'a> for dyn Content + 'a { fn as_ref (&self) -> &(dyn Draw + 'a) { self } } -impl<'a, O: Out> AsRef + 'a> for dyn Content + 'a { fn as_ref (&self) -> &(dyn Layout + 'a) { self } } -/// Drawable with dynamic dispatch. -pub trait Draw { fn draw (&self, to: &mut O); } -impl Draw for () { fn draw (&self, to: &mut O) {} } -impl Draw for fn(&mut O) { fn draw (&self, to: &mut O) { (*self)(to) } } -impl Draw for Box> { fn draw (&self, to: &mut O) { (**self).draw(to) } } -impl> Draw for &D { fn draw (&self, to: &mut O) { (*self).draw(to) } } -impl> Draw for &mut D { fn draw (&self, to: &mut O) { (**self).draw(to) } } -/// Drawable area of display. -pub trait Layout { - fn x (&self, to: O::Area) -> O::Unit { to.x() } - fn y (&self, to: O::Area) -> O::Unit { to.y() } - fn min_w (&self, to: O::Area) -> O::Unit { 0.into() } - fn max_w (&self, to: O::Area) -> O::Unit { to.w() } - fn w (&self, to: O::Area) -> O::Unit { - to.w().max(self.min_w(to)).min(self.max_w(to)) - } - fn min_h (&self, to: O::Area) -> O::Unit { 0.into() } - fn max_h (&self, to: O::Area) -> O::Unit { to.h() } - fn h (&self, to: O::Area) -> O::Unit { - to.h().max(self.min_h(to)).min(self.max_h(to)) - } - fn layout (&self, to: O::Area) -> O::Area { - [self.x(to), self.y(to), self.w(to), self.h(to)].into() + +/// Composable renderable with static dispatch. +pub trait Content: Sized { + /// Return opaque [Draw]able. + fn content (&self) -> impl Draw + Layout + '_ { () } +} + +/// The platonic ideal unit of [Content]: +/// total emptiness at dead center (e=1vg^sqrt(-1)) +impl Content for () {} + +impl + Layout> Content for fn()->T { + fn content (&self) -> impl Draw + Layout + '_ { + self() } } -impl Layout for () { - fn x (&self, a: O::Area) -> O::Unit { a.x() } - fn y (&self, a: O::Area) -> O::Unit { a.y() } - fn w (&self, _: O::Area) -> O::Unit { 0.into() } - fn min_w (&self, _: O::Area) -> O::Unit { 0.into() } - fn max_w (&self, _: O::Area) -> O::Unit { 0.into() } - fn h (&self, _: O::Area) -> O::Unit { 0.into() } - fn min_h (&self, _: O::Area) -> O::Unit { 0.into() } - fn max_h (&self, _: O::Area) -> O::Unit { 0.into() } - fn layout (&self, a: O::Area) -> O::Area { [a.x(), a.y(), 0.into(), 0.into()].into() } -} +/// Implement composable content for a struct. +#[macro_export] macro_rules! content { -impl> Layout for &L { - fn x (&self, a: O::Area) -> O::Unit { (*self).x(a) } - fn y (&self, a: O::Area) -> O::Unit { (*self).y(a) } - fn w (&self, a: O::Area) -> O::Unit { (*self).w(a) } - fn min_w (&self, a: O::Area) -> O::Unit { (*self).min_w(a) } - fn max_w (&self, a: O::Area) -> O::Unit { (*self).max_w(a) } - fn h (&self, a: O::Area) -> O::Unit { (*self).h(a) } - fn min_h (&self, a: O::Area) -> O::Unit { (*self).min_h(a) } - fn max_h (&self, a: O::Area) -> O::Unit { (*self).max_h(a) } - fn layout (&self, a: O::Area) -> O::Area { (*self).layout(a) } -} + // Implement for all [Out]s. + (|$self:ident:$Struct:ty| $content:expr) => { + impl Content for $Struct { + fn content (&$self) -> impl Draw + Layout + '_ { Some($content) } + } + }; -impl> Layout for &mut L { - fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) } - fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) } - fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) } - fn min_w (&self, a: O::Area) -> O::Unit { (**self).min_w(a) } - fn max_w (&self, a: O::Area) -> O::Unit { (**self).max_w(a) } - fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) } - fn min_h (&self, a: O::Area) -> O::Unit { (**self).min_h(a) } - fn max_h (&self, a: O::Area) -> O::Unit { (**self).max_h(a) } - fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) } -} + // Implement for specific [Out]. + ($Out:ty:| + $self:ident: + $Struct:ident$(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)? + |$content:expr) => { + impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Content<$Out> + for $Struct $(<$($($L)? $($T)?),+>)? { + fn content (&$self) -> impl Draw<$Out> + Layout<$Out> + '_ { $content } + } + }; -impl Layout for Box> { - fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) } - fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) } - fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) } - fn min_w (&self, a: O::Area) -> O::Unit { (**self).min_w(a) } - fn max_w (&self, a: O::Area) -> O::Unit { (**self).max_w(a) } - fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) } - fn min_h (&self, a: O::Area) -> O::Unit { (**self).min_h(a) } - fn max_h (&self, a: O::Area) -> O::Unit { (**self).max_h(a) } - fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) } -} - -impl> Layout for Option { - fn x (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.x(to)).unwrap_or(to.x()) - } - fn y (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.y(to)).unwrap_or(to.y()) - } - fn min_w (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.min_w(to)).unwrap_or(0.into()) - } - fn max_w (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.max_w(to)).unwrap_or(0.into()) - } - fn w (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.w(to)).unwrap_or(0.into()) - } - fn min_h (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.min_h(to)).unwrap_or(0.into()) - } - fn max_h (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.max_h(to)).unwrap_or(0.into()) - } - fn h (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.h(to)).unwrap_or(0.into()) - } - fn layout (&self, to: O::Area) -> O::Area { - self.as_ref().map(|c|c.layout([self.x(to), self.y(to), self.w(to), self.h(to)].into())) - .unwrap_or([to.x(), to.y(), 0.into(), 0.into()].into()) - } } diff --git a/output/src/draw.rs b/output/src/draw.rs index 9826267..f78574d 100644 --- a/output/src/draw.rs +++ b/output/src/draw.rs @@ -1,27 +1,94 @@ use crate::*; -pub struct Bound(pub O::Area, pub D); - -impl> HasContent for Bound { - fn content (&self) -> &impl Content { &self.1 } +/// Drawable with dynamic dispatch. +pub trait Draw { + /// Write data to display. + fn draw (&self, to: &mut O); } -impl Layout for Bound { - fn x (&self, _: O::Area) -> O::Unit { self.0.x() } - fn y (&self, _: O::Area) -> O::Unit { self.0.y() } - fn w (&self, _: O::Area) -> O::Unit { self.0.w() } - fn min_w (&self, _: O::Area) -> O::Unit { self.0.w() } - fn max_w (&self, _: O::Area) -> O::Unit { self.0.w() } - fn h (&self, _: O::Area) -> O::Unit { self.0.h() } - fn min_h (&self, _: O::Area) -> O::Unit { self.0.w() } - fn max_h (&self, _: O::Area) -> O::Unit { self.0.w() } -} - -impl> Draw for Bound { +impl Draw for fn(&mut O) { fn draw (&self, to: &mut O) { - let area = to.area(); - *to.area_mut() = self.0; - self.1.draw(to); - *to.area_mut() = area; + self(to) + } +} + +impl Draw for () { fn draw (&self, _: &mut O) {} } + +impl> Draw for &T { + fn draw (&self, to: &mut O) { (*self).draw(to) } +} + +impl<'x, O: Out> Draw for &(dyn Draw + 'x) { + fn draw (&self, to: &mut O) { + (*self).draw(to) + } +} + +impl<'x, O: Out> Draw for &mut (dyn Draw + 'x) { + fn draw (&self, to: &mut O) { + (**self).draw(to) + } +} + +impl> Draw for RwLock { + fn draw (&self, to: &mut O) { self.read().unwrap().draw(to) } +} + +impl> Draw for [T] { + fn draw (&self, to: &mut O) { + for draw in self.iter() { + draw.draw(to) + } + } +} + +//impl> Draw for &mut T { + //fn draw (&self, to: &mut O) { + //(**self).draw(to) + //} +//} + +//impl, U: Draw> Draw for &mut T { + //fn draw (&self, to: &mut O) { + //for draw in *self { + //draw.draw(to) + //} + //} +//} + +/// Implement custom drawing for a struct. +#[macro_export] macro_rules! draw { + + // Implement for all [Out] backends. + (|$self:ident:$Struct:ident $(< + $($L:lifetime),* $($T:ident $(:$Trait:path)?),* + >)?, $to:ident | $draw:expr) => { + impl <$($($L),*)? O: Out, $($($T$(:$Trait)?),*)?> Draw + for $Struct $(<$($L),* $($T),*>)? { + fn draw (&$self, $to: &mut O) { $draw } + } + }; + + // Implement for a specific [Out] backend. + ($O:ty:| + $self:ident: + $Struct:ident $(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)?, $to:ident + |$draw:expr) => { + impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Draw<$O> + for $Struct $(<$($($L)? $($T)?),+>)? { + fn draw (&$self, $to: &mut $O) { $draw } + } + }; +} + +draw!(|self: Arc>, to|(**self).draw(to)); +draw!(|self: Box>, to|(**self).draw(to)); +//draw!(|self: Option>, to|if let Some(draw) = self { draw.draw(to) }); + +impl> Draw for Option { + fn draw (&self, to: &mut O) { + if let Some(draw) = self { + draw.draw(to) + } } } diff --git a/output/src/layout.rs b/output/src/layout.rs index 85ca20a..ec42644 100644 --- a/output/src/layout.rs +++ b/output/src/layout.rs @@ -1,10 +1,98 @@ use crate::*; -mod layout_align; pub use self::layout_align::*; -mod layout_bsp; pub use self::layout_bsp::*; -mod layout_cond; pub use self::layout_cond::*; -mod layout_map; pub use self::layout_map::*; -mod layout_pad; pub use self::layout_pad::*; -mod layout_move; pub use self::layout_move::*; -mod layout_size; pub use self::layout_size::*; -mod layout_stack; //pub use self::layout_stack::*; +pub trait Layout { + fn x (&self, area: O::Area) -> O::Unit { + area.x() + } + fn y (&self, area: O::Area) -> O::Unit { + area.y() + } + + fn min_w (&self, _area: O::Area) -> O::Unit { + 0.into() + } + fn max_w (&self, area: O::Area) -> O::Unit { + area.w() + } + + fn min_h (&self, _area: O::Area) -> O::Unit { + 0.into() + } + fn max_h (&self, area: O::Area) -> O::Unit { + area.h() + } + + fn layout (&self, area: O::Area) -> O::Area { + O::Area::from([ + self.x(area), + self.y(area), + area.w().max(self.min_w(area)).min(self.max_w(area)), + area.h().max(self.min_h(area)).min(self.max_h(area)), + ]) + } +} + +#[macro_export] macro_rules! layout { + // Implement for all [Out] backends. + (|$self:ident:$Struct:ident $(< + $($L:lifetime),* $($T:ident $(:$Trait:path)?),* + >)?, $to:ident|$($method:ident = |$area:ident|$body:expr;)*) => { + impl <$($($L),*)? O: Out, $($($T$(:$Trait)?),*)?> Layout + for $Struct $(<$($L),* $($T),*>)? { + $(fn $method (&$self, $area: O::Area) -> O::Area { + $body + })* + } + }; + // Implement for a specific [Out] backend. + ($O:ty:| + $self:ident: + $Struct:ident $(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)?, + $to:ident + |$($method:ident = |$area:ident|$body:expr;)*) => { + impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Layout<$O> + for $Struct $(<$($($L)? $($T)?),+>)? { + $(fn $method (&$self, $area: <$O as Out>::Area) -> <$O as Out>::Area { + $body + })* + } + }; +} + +impl Layout for () {} + +impl> Layout for &L { /*FIXME*/ } + +impl> Layout for RwLock { /*FIXME*/ } + +impl> Layout for Option { /*FIXME*/ } + +//impl Layout for fn(&mut O) {} + +impl> Layout for Arc { + fn layout (&self, to: O::Area) -> O::Area { + (**self).layout(to) + } +} + +impl<'x, O: Out> Layout for &(dyn Draw + 'x) { + fn layout (&self, to: O::Area) -> O::Area { + Fill::xy(self).layout(to) + } +} + +mod layout_align; pub use self::layout_align::*; +mod layout_bsp; pub use self::layout_bsp::*; +mod layout_cond; pub use self::layout_cond::*; +mod layout_expand; pub use self::layout_expand::*; +mod layout_fill; pub use self::layout_fill::*; +mod layout_fixed; pub use self::layout_fixed::*; +mod layout_map; pub use self::layout_map::*; +mod layout_margin; pub use self::layout_margin::*; +mod layout_max; pub use self::layout_max::*; +mod layout_min; pub use self::layout_min::*; +mod layout_padding; pub use self::layout_padding::*; +mod layout_pull; pub use self::layout_pull::*; +mod layout_push; pub use self::layout_push::*; +mod layout_shrink; pub use self::layout_shrink::*; +mod layout_stack; //pub use self::layout_stack::*; diff --git a/output/src/layout/layout_align.rs b/output/src/layout/layout_align.rs index 6fab1b7..c8f0922 100644 --- a/output/src/layout/layout_align.rs +++ b/output/src/layout/layout_align.rs @@ -6,7 +6,7 @@ //! assert_eq!(Draw::layout(item, area), expected); //! }; //! -//! let four = ||Fixed::XY(4, 4, ""); +//! let four = ||Fixed::xy(4, 4, ""); //! test(area, &Align::nw(four()), [10, 10, 4, 4]); //! test(area, &Align::n(four()), [18, 10, 4, 4]); //! test(area, &Align::ne(four()), [26, 10, 4, 4]); @@ -16,7 +16,7 @@ //! test(area, &Align::sw(four()), [10, 26, 4, 4]); //! test(area, &Align::w(four()), [10, 18, 4, 4]); //! -//! let two_by_four = ||Fixed::XY(4, 2, ""); +//! let two_by_four = ||Fixed::xy(4, 2, ""); //! test(area, &Align::nw(two_by_four()), [10, 10, 4, 2]); //! test(area, &Align::n(two_by_four()), [18, 10, 4, 2]); //! test(area, &Align::ne(two_by_four()), [26, 10, 4, 2]); @@ -27,12 +27,13 @@ //! test(area, &Align::w(two_by_four()), [10, 19, 4, 2]); //! ``` use crate::*; -use Alignment::*; /// 9th of area to place. #[derive(Debug, Copy, Clone, Default)] pub enum Alignment { #[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W } + pub struct Align(Alignment, T); + impl Align { #[inline] pub const fn c (a: T) -> Self { Self(Alignment::Center, a) } #[inline] pub const fn x (a: T) -> Self { Self(Alignment::X, a) } @@ -46,24 +47,39 @@ impl Align { #[inline] pub const fn ne (a: T) -> Self { Self(Alignment::NE, a) } #[inline] pub const fn se (a: T) -> Self { Self(Alignment::SE, a) } } -impl> Draw for Align { - fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), &self.1).draw(to) } -} -impl> Layout for Align { - fn x (&self, to: O::Area) -> O::Unit { - match self.0 { - NW | W | SW => to.x(), - N | Center | S => to.x().plus(to.w() / 2.into()).minus(self.1.w(to) / 2.into()), - NE | E | SE => to.x().plus(to.w()).minus(self.1.w(to)), - _ => todo!(), - } - } - fn y (&self, to: O::Area) -> O::Unit { - match self.0 { - NW | N | NE => to.y(), - W | Center | E => to.y().plus(to.h() / 2.into()).minus(self.1.h(to) / 2.into()), - SW | S | SE => to.y().plus(to.h()).minus(self.1.h(to)), - _ => todo!(), - } + +impl> Layout for Align { + fn layout (&self, on: E::Area) -> E::Area { + self.0.align(on, &self.1) + } +} +impl + Layout> Draw for Align { + fn draw (&self, to: &mut E) { + to.place_at(self.layout(to.area()), &self.1) + } +} + +impl Alignment { + fn align (&self, on: E::Area, content: &impl Layout) -> E::Area { + use Alignment::*; + let it = content.layout(on).xywh(); + let cx = on.x()+(on.w().minus(it.w())/2.into()); + let cy = on.y()+(on.h().minus(it.h())/2.into()); + let fx = (on.x()+on.w()).minus(it.w()); + let fy = (on.y()+on.h()).minus(it.h()); + let [x, y] = match self { + Center => [cx, cy], + X => [cx, it.y()], + Y => [it.x(), cy], + NW => [on.x(), on.y()], + N => [cx, on.y()], + NE => [fx, on.y()], + W => [on.x(), cy], + E => [fx, cy], + SW => [on.x(), fy], + S => [cx, fy], + SE => [fx, fy], + }; + [x, y, it.w(), it.h()].into() } } diff --git a/output/src/layout/layout_bsp.rs b/output/src/layout/layout_bsp.rs index 3de4cfc..ff3e4aa 100644 --- a/output/src/layout/layout_bsp.rs +++ b/output/src/layout/layout_bsp.rs @@ -1,6 +1,7 @@ use crate::*; +use Direction::*; -/// A binary split or layer. +/// A split or layer. pub struct Bsp( pub(crate) Direction, /// First element. @@ -18,79 +19,31 @@ impl Bsp { #[inline] pub const fn b (a: Head, b: Tail) -> Self { Self(Below, a, b) } } -impl, Tail: Content> Draw for Bsp { +impl< + O: Out, + Head: Draw + Layout, + Tail: Draw + Layout +> Draw for Bsp { fn draw (&self, to: &mut O) { - match self.0 { - South => { - panic!("{}", self.1.h(to.area())); - let area_1 = self.1.layout(to.area()); - let area_2 = self.2.layout([ - to.area().x(), - to.area().y().plus(area_1.h()), - to.area().w(), - to.area().h().minus(area_1.h()) - ].into()); - panic!("{area_1:?} {area_2:?}"); - to.place_at(area_1, &self.1); - to.place_at(area_2, &self.2); - }, - _ => todo!("{:?}", self.0) + let [a, b, _] = bsp_areas(to.area(), self.0, &self.1, &self.2); + if self.0 == Below { + to.place_at(a, &self.1); + to.place_at(b, &self.2); + } else { + to.place_at(b, &self.2); + to.place_at(a, &self.1); } - //let [a, b, _] = bsp_areas(to.area(), self.0, &self.1, &self.2); - //panic!("{a:?} {b:?}"); - //if self.0 == Below { - //to.place_at(a, &self.1); - //to.place_at(b, &self.2); - //} else { - //to.place_at(b, &self.2); - //to.place_at(a, &self.1); - //} } } + impl, Tail: Layout> Layout for Bsp { - fn w (&self, area: O::Area) -> O::Unit { - match self.0 { - North | South | Above | Below => self.1.w(area).max(self.2.w(area)), - East | West => self.1.min_w(area).plus(self.2.w(area)), - } - } - fn min_w (&self, area: O::Area) -> O::Unit { - match self.0 { - North | South | Above | Below => self.1.min_w(area).max(self.2.min_w(area)), - East | West => self.1.min_w(area).plus(self.2.min_w(area)), - } - } - fn max_w (&self, area: O::Area) -> O::Unit { - match self.0 { - North | South | Above | Below => self.1.max_w(area).max(self.2.max_w(area)), - East | West => self.1.max_w(area).plus(self.2.max_w(area)), - } - } - fn h (&self, area: O::Area) -> O::Unit { - match self.0 { - East | West | Above | Below => self.1.h(area).max(self.2.h(area)), - North | South => self.1.h(area).plus(self.2.h(area)), - } - } - fn min_h (&self, area: O::Area) -> O::Unit { - match self.0 { - East | West | Above | Below => self.1.min_h(area).max(self.2.min_h(area)), - North | South => self.1.min_h(area).plus(self.2.min_h(area)), - } - } - fn max_h (&self, area: O::Area) -> O::Unit { - match self.0 { - North | South | Above | Below => self.1.max_h(area).max(self.2.max_h(area)), - East | West => self.1.max_h(area).plus(self.2.max_h(area)), - } - } fn layout (&self, area: O::Area) -> O::Area { bsp_areas(area, self.0, &self.1, &self.2)[2] } } -fn bsp_areas , B: Layout> ( - area: O::Area, direction: Direction, a: &A, b: &B, +fn bsp_areas ( + area: O::Area, direction: Direction, a: &impl Layout, b: &impl Layout, ) -> [O::Area;3] { let [x, y, w, h] = area.xywh(); let [aw, ah] = a.layout(area).wh(); diff --git a/output/src/layout/layout_cond.rs b/output/src/layout/layout_cond.rs index 65ebc5b..103166f 100644 --- a/output/src/layout/layout_cond.rs +++ b/output/src/layout/layout_cond.rs @@ -1,46 +1,55 @@ use crate::*; /// Show an item only when a condition is true. -pub struct When(bool, T, PhantomData); -impl> When { +pub struct When(pub bool, pub A); +impl When { /// Create a binary condition. - pub const fn new (c: bool, a: T) -> Self { Self(c, a, PhantomData) } + pub const fn new (c: bool, a: A) -> Self { Self(c, a) } } -impl> Layout for When { - fn layout (&self, to: O::Area) -> O::Area { - let Self(cond, item, ..) = self; - if *cond { item.layout(to) } else { O::Area::zero().into() } +impl> Layout for When { + fn layout (&self, to: E::Area) -> E::Area { + let Self(cond, item) = self; + let mut area = E::Area::zero(); + if *cond { + let item_area = item.layout(to); + area[0] = item_area.x(); + area[1] = item_area.y(); + area[2] = item_area.w(); + area[3] = item_area.h(); + } + area.into() } } -impl> Draw for When { - fn draw (&self, to: &mut O) { - let Self(cond, item, ..) = self; - if *cond { Bound(self.layout(to.area()), item).draw(to) } +impl> Draw for When { + fn draw (&self, to: &mut E) { + let Self(cond, item) = self; + if *cond { item.draw(to) } } } /// Show one item if a condition is true and another if the condition is false -pub struct Either(pub bool, pub A, pub B, pub PhantomData); -impl, B: Content> Either { +pub struct Either + Layout, B: Draw + Layout>(pub bool, pub A, pub B, pub PhantomData); +impl + Layout, B: Draw + Layout> Either { /// Create a ternary view condition. pub const fn new (c: bool, a: A, b: B) -> Self { Self(c, a, b, PhantomData) } } -impl, B: Layout> Layout for Either { +impl + Layout, B: Draw + Layout> Layout for Either { fn layout (&self, to: E::Area) -> E::Area { let Self(cond, a, b, ..) = self; if *cond { a.layout(to) } else { b.layout(to) } } } -impl, B: Content> Draw for Either { +impl + Layout, B: Draw + Layout> Draw for Either { fn draw (&self, to: &mut E) { let Self(cond, a, b, ..) = self; - let area = self.layout(to.area()); - if *cond { Bound(area, a).draw(to) } else { Bound(area, b).draw(to) } + if *cond { a.draw(to) } else { b.draw(to) } } } + + /////////////////////////////////////////////////////////////////////////////// @@ -107,7 +116,7 @@ impl, B: Content> Draw for Either { //return Ok(Some(match words.next() { //Some(Token{value: Key($x),..}) => Self::x(content), //Some(Token{value: Key($y),..}) => Self::y(content), - //Some(Token{value: Key($xy),..}) => Self::XY(content), + //Some(Token{value: Key($xy),..}) => Self::xy(content), //_ => unreachable!() //})) //} else { @@ -125,7 +134,7 @@ impl, B: Content> Draw for Either { //state.give_or_fail(words, ||"y: no unit")?, //state.give_or_fail(words, ||"y: no content")?, //), - //Some(Token { value: Key($x), .. }) => Self::XY( + //Some(Token { value: Key($x), .. }) => Self::xy( //state.give_or_fail(words, ||"xy: no unit x")?, //state.give_or_fail(words, ||"xy: no unit y")?, //state.give_or_fail(words, ||"xy: no content")? diff --git a/output/src/layout/layout_expand.rs b/output/src/layout/layout_expand.rs new file mode 100644 index 0000000..8194fe2 --- /dev/null +++ b/output/src/layout/layout_expand.rs @@ -0,0 +1,39 @@ +use crate::*; + +pub enum Expand { X(U, A), Y(U, A), XY(U, U, A), } + +impl Expand { + #[inline] pub const fn x (x: U, item: A) -> Self { Self::X(x, item) } + #[inline] pub const fn y (y: U, item: A) -> Self { Self::Y(y, item) } + #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { Self::XY(x, y, item) } +} + +impl + Layout> Content for Expand { + fn content (&self) -> impl Draw + Layout + '_ { + use Expand::*; + match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } + } +} + +impl + Layout> Draw for Expand { + fn draw (&self, to: &mut O) { + to.place_at(self.layout(to.area()), &self.content()) + } +} + +impl Expand { + #[inline] pub fn dx (&self) -> U { + use Expand::*; + match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } + } + #[inline] pub fn dy (&self) -> U { + use Expand::*; + match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } + } +} + +impl> Layout for Expand { + fn layout (&self, to: O::Area) -> O::Area { + [to.x(), to.y(), to.w().plus(self.dx()), to.h().plus(self.dy())].into() + } +} diff --git a/output/src/layout/layout_fill.rs b/output/src/layout/layout_fill.rs new file mode 100644 index 0000000..6287714 --- /dev/null +++ b/output/src/layout/layout_fill.rs @@ -0,0 +1,55 @@ +use crate::*; + +pub enum Fill { + /// Use maximum width of area. + X(A), + /// Use maximum height of area. + Y(A), + /// Use maximum width and height of area. + XY(A) +} + +impl Fill { + #[inline] pub const fn x (item: T) -> Self { + Self::X(item) + } + #[inline] pub const fn y (item: T) -> Self { + Self::Y(item) + } + #[inline] pub const fn xy (item: T) -> Self { + Self::XY(item) + } + #[inline] pub const fn has_x (&self) -> bool { + matches!(self, Self::X(_) | Self::XY(_)) + } + #[inline] pub const fn has_y (&self) -> bool { + matches!(self, Self::Y(_) | Self::XY(_)) + } + #[inline] pub const fn content (&self) -> &T { + use Fill::*; + match self { X(item) | Y(item) | XY(item) => item } + } +} + +impl> Draw for Fill { + fn draw (&self, to: &mut O) { + to.place_at(self.layout(to.area()), &self.content()) + } +} + +impl> Layout for Fill { + fn min_w (&self, area: O::Area) -> O::Unit { + if self.has_x() { + area.w() + } else { + 0.into() + } + } + fn min_h (&self, area: O::Area) -> O::Unit { + if self.has_y() { + area.h() + } else { + 0.into() + } + } +} diff --git a/output/src/layout/layout_fixed.rs b/output/src/layout/layout_fixed.rs new file mode 100644 index 0000000..1d6eb1d --- /dev/null +++ b/output/src/layout/layout_fixed.rs @@ -0,0 +1,47 @@ +use crate::*; + +pub enum Fixed { X(U, A), Y(U, A), XY(U, U, A), } + +impl Fixed { + #[inline] pub const fn x (x: U, item: A) -> Self { + Self::X(x, item) + } + #[inline] pub const fn y (y: U, item: A) -> Self { + Self::Y(y, item) + } + #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { + Self::XY(x, y, item) + } + #[inline] pub const fn content (&self) -> &A { + use Fixed::*; + match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } + } +} + +impl Fixed { + #[inline] pub fn dx (&self) -> U { + use Fixed::*; + match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } + } + #[inline] pub fn dy (&self) -> U { + use Fixed::*; + match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } + } +} + +impl> Draw for Fixed { + fn draw (&self, to: &mut O) { + let area = Layout::::layout(&self, to.area()); + to.place_at(area, &self.content()) + } +} + +impl Layout for Fixed { + fn layout (&self, area: O::Area) -> O::Area { + [area.x(), area.y(), match self { + Fixed::X(w, _) | Fixed::XY(w, _, _) => *w, _ => area.w() + }, match self { + Fixed::Y(h, _) | Fixed::XY(_, h, _) => *h, _ => area.h() + }].into() + } +} diff --git a/output/src/layout/layout_map.rs b/output/src/layout/layout_map.rs index ffd7005..cb230e2 100644 --- a/output/src/layout/layout_map.rs +++ b/output/src/layout/layout_map.rs @@ -31,7 +31,7 @@ macro_rules! impl_map_direction (($name:ident, $axis:ident, $align:ident)=>{ O, A, Push>>>, I, F, fn(A, usize)->B > where O: Out, - B: Draw, + B: Draw + Layout, I: Iterator + Send + Sync + 'a, F: Fn() -> I + Send + Sync + 'a { @@ -61,14 +61,14 @@ macro_rules! impl_map_direction (($name:ident, $axis:ident, $align:ident)=>{ } }); -impl_map_direction!(east, X, w); -impl_map_direction!(south, Y, n); -impl_map_direction!(west, X, e); -impl_map_direction!(north, Y, s); +impl_map_direction!(east, x, w); +impl_map_direction!(south, y, n); +impl_map_direction!(west, x, e); +impl_map_direction!(north, y, s); impl<'a, O, A, B, I, F, G> Layout for Map where O: Out, - B: Layout, + B: Draw + Layout, I: Iterator + Send + Sync + 'a, F: Fn() -> I + Send + Sync + 'a, G: Fn(A, usize)->B + Send + Sync @@ -80,21 +80,21 @@ impl<'a, O, A, B, I, F, G> Layout for Map where let [mut max_x, mut max_y] = area.center(); for item in get_iter() { let [x,y,w,h] = get_item(item, index).layout(area).xywh(); - min_x = min_x.min(x); - min_y = min_y.min(y); - max_x = max_x.max(x + w); - max_y = max_y.max(y + h); + min_x = min_x.min(x.into()); + min_y = min_y.min(y.into()); + max_x = max_x.max((x + w).into()); + max_y = max_y.max((y + h).into()); index += 1; } let w = max_x - min_x; let h = max_y - min_y; //[min_x.into(), min_y.into(), w.into(), h.into()].into() - area.center_xy([w.into(), h.into()]).into() + area.center_xy([w.into(), h.into()].into()).into() } } impl<'a, O, A, B, I, F, G> Draw for Map where O: Out, - B: Content, + B: Draw + Layout, I: Iterator + Send + Sync + 'a, F: Fn() -> I + Send + Sync + 'a, G: Fn(A, usize)->B + Send + Sync @@ -115,25 +115,25 @@ impl<'a, O, A, B, I, F, G> Draw for Map where #[inline] pub fn map_south( item_offset: O::Unit, item_height: O::Unit, - item: impl Content -) -> impl Content { - Push::Y(item_offset, Fixed::Y(item_height, Fill::X(item))) + item: impl Draw + Layout +) -> impl Draw + Layout { + Push::y(item_offset, Fixed::y(item_height, Fill::x(item))) } #[inline] pub fn map_south_west( item_offset: O::Unit, item_height: O::Unit, - item: impl Content -) -> impl Content { - Push::Y(item_offset, Align::nw(Fixed::Y(item_height, Fill::X(item)))) + item: impl Draw + Layout +) -> impl Draw + Layout { + Push::y(item_offset, Align::nw(Fixed::y(item_height, Fill::x(item)))) } #[inline] pub fn map_east( item_offset: O::Unit, item_width: O::Unit, - item: impl Content -) -> impl Content { - Push::X(item_offset, Align::w(Fixed::X(item_width, Fill::Y(item)))) + item: impl Draw + Layout +) -> impl Draw + Layout { + Push::x(item_offset, Align::w(Fixed::x(item_width, Fill::y(item)))) } diff --git a/output/src/layout/layout_margin.rs b/output/src/layout/layout_margin.rs new file mode 100644 index 0000000..cee3800 --- /dev/null +++ b/output/src/layout/layout_margin.rs @@ -0,0 +1,50 @@ +use crate::*; + +pub enum Margin { X(U, A), Y(U, A), XY(U, U, A), } + +impl Margin { + #[inline] pub const fn x (x: U, item: A) -> Self { + Self::X(x, item) + } + #[inline] pub const fn y (y: U, item: A) -> Self { + Self::Y(y, item) + } + #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { + Self::XY(x, y, item) + } + #[inline] pub const fn content (&self) -> &A { + use Margin::*; + match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } + } +} + +impl + Layout> Draw for Margin { + fn draw (&self, to: &mut E) { + to.place_at(self.layout(to.area()), &self.content()) + } +} + +impl Margin { + #[inline] pub fn dx (&self) -> U { + use Margin::*; + match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } + } + #[inline] pub fn dy (&self) -> U { + use Margin::*; + match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } + } +} + +impl> Layout for Margin { + fn layout (&self, area: E::Area) -> E::Area { + let area = self.content().layout(area); + let dx = self.dx(); + let dy = self.dy(); + [ + area.x().minus(dx), + area.y().minus(dy), + area.w().plus(dy.plus(dy)), + area.h().plus(dy.plus(dy)), + ].into() + } +} diff --git a/output/src/layout/layout_max.rs b/output/src/layout/layout_max.rs new file mode 100644 index 0000000..81467ae --- /dev/null +++ b/output/src/layout/layout_max.rs @@ -0,0 +1,47 @@ +use crate::*; + +pub enum Max { X(U, A), Y(U, A), XY(U, U, A), } + +impl Max { + #[inline] pub const fn x (x: U, item: A) -> Self { + Self::X(x, item) + } + #[inline] pub const fn y (y: U, item: A) -> Self { + Self::Y(y, item) + } + #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { + Self::XY(x, y, item) + } + #[inline] pub const fn content (&self) -> &A { + use Max::*; + match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } + } +} + +impl + Layout> Draw for Max { + fn draw (&self, to: &mut E) { + to.place_at(self.layout(to.area()), &self.content()) + } +} + +impl Max { + #[inline] pub fn dx (&self) -> U { + use Max::*; + match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } + } + #[inline] pub fn dy (&self) -> U { + use Max::*; + match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } + } +} + +impl> Layout for Max { + fn layout (&self, area: E::Area) -> E::Area { + let [x, y, w, h] = self.content().layout(area).xywh(); + match self { + Self::X(mw, _) => [x, y, w.min(*mw), h], + Self::Y(mh, _) => [x, y, w, h.min(*mh)], + Self::XY(mw, mh, _) => [x, y, w.min(*mw), h.min(*mh)], + }.into() + } +} diff --git a/output/src/layout/layout_min.rs b/output/src/layout/layout_min.rs new file mode 100644 index 0000000..73f8209 --- /dev/null +++ b/output/src/layout/layout_min.rs @@ -0,0 +1,47 @@ +use crate::*; + +pub enum Min { X(U, A), Y(U, A), XY(U, U, A), } + +impl Min { + #[inline] pub const fn x (x: U, item: A) -> Self { + Self::X(x, item) + } + #[inline] pub const fn y (y: U, item: A) -> Self { + Self::Y(y, item) + } + #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { + Self::XY(x, y, item) + } + #[inline] pub const fn content (&self) -> &A { + use Min::*; + match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } + } +} + +impl + Layout> Draw for Min { + fn draw (&self, to: &mut E) { + to.place_at(self.layout(to.area()), &self.content()) + } +} + +impl Min { + #[inline] pub fn dx (&self) -> U { + use Min::*; + match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } + } + #[inline] pub fn dy (&self) -> U { + use Min::*; + match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } + } +} + +impl> Layout for Min { + fn layout (&self, area: E::Area) -> E::Area { + let [x, y, w, h] = self.content().layout(area).xywh(); + match self { + Self::X(mw, _) => [x, y, w.max(*mw), h], + Self::Y(mh, _) => [x, y, w, h.max(*mh)], + Self::XY(mw, mh, _) => [x, y, w.max(*mw), h.max(*mh)], + }.into() + } +} diff --git a/output/src/layout/layout_move.rs b/output/src/layout/layout_move.rs deleted file mode 100644 index 163d2e3..0000000 --- a/output/src/layout/layout_move.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::*; -/// Increment X and/or Y coordinate. -pub enum Push { X(U, A), Y(U, A), XY(U, U, A), } -impl Push { - #[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c, } } -} -impl Push { - #[inline] pub fn dx (&self) -> U { match self { Self::X(x, ..) | Self::XY(x, _, _) => *x, Self::Y(_, _) => 0.into() } } - #[inline] pub fn dy (&self) -> U { match self { Self::Y(y, ..) | Self::XY(_, y, _) => *y, Self::X(_, _) => 0.into() } } -} -impl> Draw for Push { - fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) } -} -impl> Layout for Push { - fn x (&self, area: O::Area) -> O::Unit { area.x().plus(self.dx()) } - fn y (&self, area: O::Area) -> O::Unit { area.y().plus(self.dy()) } -} -/// Decrement X and/or Y coordinate. -pub enum Pull { X(U, A), Y(U, A), XY(U, U, A), } -impl Pull { - #[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c, } } -} -impl Pull { - #[inline] pub fn dx (&self) -> U { match self { Self::X(x, ..) | Self::XY(x, _, _) => *x, Self::Y(_, _) => 0.into() } } - #[inline] pub fn dy (&self) -> U { match self { Self::Y(y, ..) | Self::XY(_, y, _) => *y, Self::X(_, _) => 0.into() } } -} -impl> Draw for Pull { - fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) } -} -impl> Layout for Pull { - fn x (&self, area: O::Area) -> O::Unit { area.x().minus(self.dx()) } - fn y (&self, area: O::Area) -> O::Unit { area.y().minus(self.dy()) } -} diff --git a/output/src/layout/layout_pad.rs b/output/src/layout/layout_pad.rs deleted file mode 100644 index 0570c23..0000000 --- a/output/src/layout/layout_pad.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::*; -use Pad::*; -pub enum Pad { X(U, A), Y(U, A), XY(U, U, A), } -impl Pad { - #[inline] pub const fn inner (&self) -> &A { match self { X(_, c) | Y(_, c) | XY(_, _, c) => c, } } -} -impl Pad { - #[inline] pub fn dx (&self) -> U { match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } } - #[inline] pub fn dy (&self) -> U { match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } } -} -impl> Draw for Pad { - fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) } -} -impl> Layout for Pad { - fn x (&self, area: O::Area) -> O::Unit { - area.x().plus(self.dx()) - } - fn y (&self, area: O::Area) -> O::Unit { - area.x().plus(self.dx()) - } - fn w (&self, area: O::Area) -> O::Unit { - area.w().minus(self.dx() * 2.into()) - } - fn h (&self, area: O::Area) -> O::Unit { - area.h().minus(self.dy() * 2.into()) - } -} diff --git a/output/src/layout/layout_padding.rs b/output/src/layout/layout_padding.rs new file mode 100644 index 0000000..5295fdf --- /dev/null +++ b/output/src/layout/layout_padding.rs @@ -0,0 +1,50 @@ +use crate::*; + +pub enum Padding { X(U, A), Y(U, A), XY(U, U, A), } + +impl Padding { + #[inline] pub const fn x (x: U, item: A) -> Self { + Self::X(x, item) + } + #[inline] pub const fn y (y: U, item: A) -> Self { + Self::Y(y, item) + } + #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { + Self::XY(x, y, item) + } + #[inline] pub const fn content (&self) -> &A { + use Padding::*; + match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } + } +} + +impl + Layout> Draw for Padding { + fn draw (&self, to: &mut E) { + to.place_at(self.layout(to.area()), &self.content()) + } +} + +impl Padding { + #[inline] pub fn dx (&self) -> U { + use Padding::*; + match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } + } + #[inline] pub fn dy (&self) -> U { + use Padding::*; + match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } + } +} + +impl> Layout for Padding { + fn layout (&self, area: E::Area) -> E::Area { + let area = self.content().layout(area); + let dx = self.dx(); + let dy = self.dy(); + [ + area.x().plus(dx), + area.y().plus(dy), + area.w().minus(dy.plus(dy)), + area.h().minus(dy.plus(dy)) + ].into() + } +} diff --git a/output/src/layout/layout_pull.rs b/output/src/layout/layout_pull.rs new file mode 100644 index 0000000..9fdda7f --- /dev/null +++ b/output/src/layout/layout_pull.rs @@ -0,0 +1,48 @@ +use crate::*; + +pub enum Pull { X(U, A), Y(U, A), XY(U, U, A), } + +impl Pull { + #[inline] pub const fn x (x: U, item: A) -> Self { + Self::X(x, item) + } + #[inline] pub const fn y (y: U, item: A) -> Self { + Self::Y(y, item) + } + #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { + Self::XY(x, y, item) + } + #[inline] pub const fn content (&self) -> &A { + use Pull::*; + match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } + } +} + +impl + Layout> Draw for Pull { + fn draw (&self, to: &mut E) { + to.place_at(self.layout(to.area()), &self.content()) + } +} + +impl Pull { + #[inline] pub fn dx (&self) -> U { + use Pull::*; + match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } + } + #[inline] pub fn dy (&self) -> U { + use Pull::*; + match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } + } +} + +impl> Layout for Pull { + fn layout (&self, to: E::Area) -> E::Area { + let area = self.content().layout(to); + [ + area.x().minus(self.dx()), + area.y().minus(self.dy()), + area.w(), + area.h() + ].into() + } +} diff --git a/output/src/layout/layout_push.rs b/output/src/layout/layout_push.rs new file mode 100644 index 0000000..4c507b3 --- /dev/null +++ b/output/src/layout/layout_push.rs @@ -0,0 +1,43 @@ +use crate::*; + +pub enum Push { X(U, A), Y(U, A), XY(U, U, A), } + +impl Push { + #[inline] pub const fn x (x: U, item: A) -> Self { + Self::X(x, item) + } + #[inline] pub const fn y (y: U, item: A) -> Self { + Self::Y(y, item) + } + #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { + Self::XY(x, y, item) + } + #[inline] pub const fn content (&self) -> &A { + use Push::*; + match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } + } +} + +impl + Layout> Draw for Push { + fn draw (&self, to: &mut E) { + to.place_at(self.layout(to.area()), &self.content()) + } +} + +impl Push { + #[inline] pub fn dx (&self) -> U { + use Push::*; + match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } + } + #[inline] pub fn dy (&self) -> U { + use Push::*; + match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } + } +} + +impl> Layout for Push { + fn layout (&self, area: E::Area) -> E::Area { + let area = self.content().layout(area); + [area.x().plus(self.dx()), area.y().plus(self.dy()), area.w(), area.h()].into() + } +} diff --git a/output/src/layout/layout_shrink.rs b/output/src/layout/layout_shrink.rs new file mode 100644 index 0000000..6a64425 --- /dev/null +++ b/output/src/layout/layout_shrink.rs @@ -0,0 +1,43 @@ +use crate::*; + +pub enum Shrink { X(U, A), Y(U, A), XY(U, U, A), } + +impl Shrink { + #[inline] pub const fn x (x: U, item: A) -> Self { + Self::X(x, item) + } + #[inline] pub const fn y (y: U, item: A) -> Self { + Self::Y(y, item) + } + #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { + Self::XY(x, y, item) + } + #[inline] pub const fn content (&self) -> &A { + use Shrink::*; + match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } + } +} + +impl + Layout> Draw for Shrink { + fn draw (&self, to: &mut E) { + to.place_at(self.layout(to.area()), &self.content()) + } +} + +impl Shrink { + #[inline] pub fn dx (&self) -> U { + use Shrink::*; + match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } + } + #[inline] pub fn dy (&self) -> U { + use Shrink::*; + match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } + } +} + +impl> Layout for Shrink { + fn layout (&self, to: E::Area) -> E::Area { + let area = self.content().layout(to); + [area.x(), area.y(), area.w().minus(self.dx()), area.h().minus(self.dy())].into() + } +} diff --git a/output/src/layout/layout_size.rs b/output/src/layout/layout_size.rs deleted file mode 100644 index a9d4932..0000000 --- a/output/src/layout/layout_size.rs +++ /dev/null @@ -1,121 +0,0 @@ -use crate::*; - -pub enum Fill { X(A), Y(A), XY(A) } -impl Fill { - #[inline] pub const fn inner (&self) -> &A { match self { Self::X(c) | Self::Y(c) | Self::XY(c) => c } } - #[inline] pub const fn dx (&self) -> bool { match self { Self::X(_) | Self::XY(_) => true, _ => false } } - #[inline] pub const fn dy (&self) -> bool { match self { Self::Y(_) | Self::XY(_) => true, _ => false } } -} -impl> Draw for Fill { - fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) } -} -impl> Layout for Fill { - fn x (&self, area: O::Area) -> O::Unit { if self.dx() { area.x() } else { self.inner().x(area) } } - fn y (&self, area: O::Area) -> O::Unit { if self.dy() { area.y() } else { self.inner().y(area) } } - fn w (&self, area: O::Area) -> O::Unit { if self.dx() { area.w() } else { self.inner().w(area) } } - fn min_w (&self, area: O::Area) -> O::Unit { if self.dx() { area.w() } else { self.inner().min_w(area) } } - fn max_w (&self, area: O::Area) -> O::Unit { if self.dx() { area.w() } else { self.inner().max_w(area) } } - fn h (&self, area: O::Area) -> O::Unit { if self.dy() { area.h() } else { self.inner().h(area) } } - fn min_h (&self, area: O::Area) -> O::Unit { if self.dy() { area.h() } else { self.inner().min_h(area) } } - fn max_h (&self, area: O::Area) -> O::Unit { if self.dy() { area.h() } else { self.inner().max_h(area) } } -} -/// Set fixed size for content. -pub enum Fixed { X(U, A), Y(U, A), XY(U, U, A), } -impl Fixed { - #[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c } } -} -impl Fixed { - #[inline] pub const fn dx (&self) -> Option { match self { Self::X(x, _) | Self::XY(x, ..) => Some(*x), _ => None } } - #[inline] pub const fn dy (&self) -> Option { match self { Self::Y(y, _) | Self::XY(y, ..) => Some(*y), _ => None } } -} -impl> Draw for Fixed { - fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) } -} -impl> Layout for Fixed { - fn w (&self, area: O::Area) -> O::Unit { self.dx().unwrap_or(self.inner().w(area)) } - fn min_w (&self, area: O::Area) -> O::Unit { self.dx().unwrap_or(self.inner().min_w(area)) } - fn max_w (&self, area: O::Area) -> O::Unit { self.dx().unwrap_or(self.inner().max_w(area)) } - fn h (&self, area: O::Area) -> O::Unit { self.dy().unwrap_or(self.inner().h(area)) } - fn min_h (&self, area: O::Area) -> O::Unit { self.dy().unwrap_or(self.inner().min_h(area)) } - fn max_h (&self, area: O::Area) -> O::Unit { self.dy().unwrap_or(self.inner().max_h(area)) } -} - -pub enum Max { X(U, A), Y(U, A), XY(U, U, A), } -impl Max { - #[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c } } -} -impl Max { - #[inline] pub const fn dx (&self) -> Option { match self { Self::X(x, _) | Self::XY(x, ..) => Some(*x), _ => None } } - #[inline] pub const fn dy (&self) -> Option { match self { Self::Y(y, _) | Self::XY(y, ..) => Some(*y), _ => None } } -} -impl> Draw for Max { - fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) } -} -impl> Layout for Max { - fn layout (&self, area: E::Area) -> E::Area { - let [x, y, w, h] = self.inner().layout(area).xywh(); - match self { - Self::X(mw, _) => [x, y, w.min(*mw), h], - Self::Y(mh, _) => [x, y, w, h.min(*mh)], - Self::XY(mw, mh, _) => [x, y, w.min(*mw), h.min(*mh)], - }.into() - } -} - -pub enum Min { X(U, A), Y(U, A), XY(U, U, A), } -impl Min { - #[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c } } -} -impl Min { - #[inline] pub const fn dx (&self) -> Option { match self { Self::X(x, _) | Self::XY(x, ..) => Some(*x), _ => None } } - #[inline] pub const fn dy (&self) -> Option { match self { Self::Y(y, _) | Self::XY(y, ..) => Some(*y), _ => None } } -} -impl> Draw for Min { - fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) } -} -impl> Layout for Min { - fn layout (&self, area: E::Area) -> E::Area { - let [x, y, w, h] = self.inner().layout(area).xywh(); - match self { - Self::X(mw, _) => [x, y, w.max(*mw), h], - Self::Y(mh, _) => [x, y, w, h.max(*mh)], - Self::XY(mw, mh, _) => [x, y, w.max(*mw), h.max(*mh)], - }.into() - } -} - -pub enum Expand { X(U, A), Y(U, A), XY(U, U, A), } -impl Expand { - #[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c } } -} -impl Expand { - #[inline] pub const fn dx (&self) -> Option { match self { Self::X(x, _) | Self::XY(x, ..) => Some(*x), _ => None } } - #[inline] pub const fn dy (&self) -> Option { match self { Self::Y(y, _) | Self::XY(y, ..) => Some(*y), _ => None } } -} -impl> Draw for Expand { - fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) } -} -impl> Layout for Expand { - fn w (&self, to: O::Area) -> O::Unit { self.inner().w(to).plus(self.dx().unwrap_or_default()) } - fn h (&self, to: O::Area) -> O::Unit { self.inner().w(to).plus(self.dy().unwrap_or_default()) } -} - -pub enum Shrink { X(U, A), Y(U, A), XY(U, U, A), } -impl Shrink { - #[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c } } -} -impl Shrink { - #[inline] pub const fn dx (&self) -> Option { match self { Self::X(x, _) | Self::XY(x, ..) => Some(*x), _ => None } } - #[inline] pub const fn dy (&self) -> Option { match self { Self::Y(y, _) | Self::XY(y, ..) => Some(*y), _ => None } } -} -impl> Draw for Shrink { - fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) } -} -impl> Layout for Shrink { - fn layout (&self, to: E::Area) -> E::Area { - let area = self.inner().layout(to); - let dx = self.dx().unwrap_or_default(); - let dy = self.dy().unwrap_or_default(); - [area.x(), area.y(), area.w().minus(dx), area.h().minus(dy)].into() - } -} diff --git a/output/src/output.rs b/output/src/output.rs index 6a32109..29ff6b2 100644 --- a/output/src/output.rs +++ b/output/src/output.rs @@ -3,12 +3,15 @@ #![feature(impl_trait_in_assoc_type)] #![feature(const_precise_live_drops)] #![feature(type_changing_struct_update)] -#![feature(anonymous_lifetime_in_impl_trait)] -#![feature(const_option_ops)] -#![feature(const_trait_impl)] -#![feature(const_default)] //#![feature(non_lifetime_binders)] +mod content; pub use self::content::*; +mod draw; pub use self::draw::*; +mod group; pub use self::group::*; +mod layout; pub use self::layout::*; +mod space; pub use self::space::*; +mod thunk; pub use self::thunk::*; +mod widget; pub use self::widget::*; pub(crate) use self::Direction::*; pub(crate) use std::fmt::{Debug, Display}; pub(crate) use std::marker::PhantomData; @@ -16,49 +19,38 @@ pub(crate) use std::ops::{Add, Sub, Mul, Div}; pub(crate) use std::sync::{Arc, RwLock, atomic::{AtomicUsize, Ordering::Relaxed}}; pub(crate) use tengri_core::*; -/// Drawing target. +/// Draw target. pub trait Out: Send + Sync + Sized { /// Unit of length type Unit: Coordinate; - /// Rectangle without offset type Size: Size; - /// Rectangle with offset type Area: Area; - - /// Render drawable in area specified by `T::layout(self.area())` - #[inline] fn place <'t, T: Content + ?Sized> ( - &mut self, content: &'t T - ) { - self.place_at(content.layout(self.area()), content) - } - - /// Render drawable in area specified by `area` - fn place_at <'t, T: Draw + ?Sized> (&mut self, area: Self::Area, content: &'t T); - /// Current output area fn area (&self) -> Self::Area; + /// Mutable pointer to area + fn area_mut (&mut self) -> &mut Self::Area; + /// Draw widget in area + fn place_at <'t, T: Draw + ?Sized> (&mut self, area: Self::Area, content: &'t T); + + fn place <'t, T: Draw + Layout + ?Sized> (&mut self, content: &'t T) { + self.place_at(content.layout(self.area()), content) + } #[inline] fn x (&self) -> Self::Unit { self.area().x() } #[inline] fn y (&self) -> Self::Unit { self.area().y() } #[inline] fn w (&self) -> Self::Unit { self.area().w() } #[inline] fn h (&self) -> Self::Unit { self.area().h() } #[inline] fn wh (&self) -> Self::Size { self.area().wh().into() } - - /// Mutable pointer to area. - fn area_mut (&mut self) -> &mut Self::Area; } -#[cfg(test)] mod output_test; +#[cfg(test)] mod test; #[cfg(test)] pub(crate) use proptest_derive::Arbitrary; -mod content; pub use self::content::*; -mod draw; pub use self::draw::*; -mod group; pub use self::group::*; -mod layout; pub use self::layout::*; -mod space; pub use self::space::*; -mod thunk; pub use self::thunk::*; -mod widget; pub use self::widget::*; - -#[cfg(feature = "dsl")] mod view; -#[cfg(feature = "dsl")] pub use self::view::*; +//impl + Layout> Draw for C { // if only + //fn draw (&self, to: &mut E) { + //if let Some(content) = self.content() { + //to.place_at(self.layout(to.area()), &content); + //} + //} +//} diff --git a/output/src/space/space_measure.rs b/output/src/space/space_measure.rs index f508554..d1bdb30 100644 --- a/output/src/space/space_measure.rs +++ b/output/src/space/space_measure.rs @@ -76,6 +76,6 @@ impl Measure { format!("{}x{}", self.w(), self.h()).into() } pub fn of > (&self, item: T) -> Bsp, T> { - Bsp::b(Fill::XY(self), item) + Bsp::b(Fill::xy(self), item) } } diff --git a/output/src/output_test.rs b/output/src/test.rs similarity index 88% rename from output/src/output_test.rs rename to output/src/test.rs index 932e87a..ce859ed 100644 --- a/output/src/output_test.rs +++ b/output/src/test.rs @@ -84,13 +84,13 @@ macro_rules! test_op_transform { h in u16::MIN..u16::MAX, ) { if let Some(op) = match (op_x, op_y) { - (Some(x), Some(y)) => Some($Op::XY(x, y, content)), - (Some(x), None) => Some($Op::X(x, content)), + (Some(x), Some(y)) => Some($Op::xy(x, y, content)), + (Some(x), None) => Some($Op::x(x, content)), (None, Some(y)) => Some($Op::y(y, content)), _ => None } { - //assert_eq!(Content::layout(&op, [x, y, w, h]), - //Draw::layout(&op, [x, y, w, h])); + assert_eq!(Content::layout(&op, [x, y, w, h]), + Draw::layout(&op, [x, y, w, h])); } } } @@ -104,7 +104,8 @@ test_op_transform!(proptest_op_push, Push); test_op_transform!(proptest_op_pull, Pull); test_op_transform!(proptest_op_shrink, Shrink); test_op_transform!(proptest_op_expand, Expand); -test_op_transform!(proptest_op_padding, Pad); +test_op_transform!(proptest_op_margin, Margin); +test_op_transform!(proptest_op_padding, Padding); proptest! { #[test] fn proptest_op_bsp ( @@ -121,10 +122,10 @@ proptest! { h in u16::MIN..u16::MAX, ) { let bsp = Bsp(d, a, b); - //assert_eq!( - //Content::layout(&bsp, [x, y, w, h]), - //Draw::layout(&bsp, [x, y, w, h]), - //); + assert_eq!( + Content::layout(&bsp, [x, y, w, h]), + Draw::layout(&bsp, [x, y, w, h]), + ); } } @@ -141,8 +142,7 @@ proptest! { fn area_mut (&mut self) -> &mut [u16;4] { &mut self.0 } - fn place_at + ?Sized> (&mut self, area: [u16;4], _: &T) { - println!("place_at: {area:?}"); + fn place + ?Sized> (&mut self, _: [u16;4], _: &T) { () } } diff --git a/output/src/thunk.rs b/output/src/thunk.rs index 29b7321..da2d84e 100644 --- a/output/src/thunk.rs +++ b/output/src/thunk.rs @@ -1,19 +1,43 @@ use crate::*; -pub struct Lazy(F, PhantomData<(O, T)>); -impl, F: Fn()->T> Lazy { pub const fn new (thunk: F) -> Self { Self(thunk, PhantomData) } } - -pub struct Thunk(PhantomData, F); -impl Thunk { pub const fn new (draw: F) -> Self { Self(PhantomData, draw) } } -impl Layout for Thunk {} -impl Draw for Thunk { - fn draw (&self, to: &mut O) { (self.1)(to) } +/// Lazily-evaluated [Draw]able. +pub struct Lazy(PhantomData<(E, T)>, F); +impl + Layout, F: Fn()->T> Lazy { + pub const fn new (thunk: F) -> Self { + Self(PhantomData, thunk) + } +} +impl + Layout, F: Fn()->T> Content for Lazy { + fn content (&self) -> impl Draw + Layout + '_ { + (self.1)() + } +} + +pub struct Thunk(PhantomData, F); +impl Thunk { + pub const fn new (draw: F) -> Self { Self(PhantomData, draw) } +} +impl Draw for Thunk { + fn draw (&self, to: &mut E) { (self.1)(to) } +} +impl Layout for Thunk { + fn layout (&self, to: E::Area) -> E::Area { to } +} + +#[derive(Debug, Default)] pub struct Memo { + pub value: T, + pub view: Arc> } -#[derive(Debug, Default)] pub struct Memo { pub value: T, pub view: Arc> } impl Memo { - pub fn new (value: T, view: U) -> Self { Self { value, view: Arc::new(view.into()) } } - pub fn update (&mut self, newval: T, draw: impl Fn(&mut U, &T, &T)->R) -> Option { + pub fn new (value: T, view: U) -> Self { + Self { value, view: Arc::new(view.into()) } + } + pub fn update ( + &mut self, + newval: T, + draw: impl Fn(&mut U, &T, &T)->R + ) -> Option { if newval != self.value { let result = draw(&mut*self.view.write().unwrap(), &newval, &self.value); self.value = newval; @@ -24,4 +48,6 @@ impl Memo { } /// Clear a pre-allocated buffer, then write into it. -#[macro_export] macro_rules! rewrite { ($buf:ident, $($rest:tt)*) => { |$buf,_,_|{ $buf.clear(); write!($buf, $($rest)*) } } } +#[macro_export] macro_rules! rewrite { + ($buf:ident, $($rest:tt)*) => { |$buf,_,_|{ $buf.clear(); write!($buf, $($rest)*) } } +} diff --git a/output/src/view.rs b/output/src/view.rs deleted file mode 100644 index f8a182f..0000000 --- a/output/src/view.rs +++ /dev/null @@ -1,147 +0,0 @@ -use crate::*; -use ::tengri_dsl::{Dsl, DslExpr, DslWord, DslNs}; - -pub trait View { - fn view_expr <'a> (&'a self, output: &mut O, expr: &'a impl DslExpr) -> Usually { - Err(format!("View::view_expr: no exprs defined: {expr:?}").into()) - } - fn view_word <'a> (&'a self, output: &mut O, word: &'a impl DslWord) -> Usually { - Err(format!("View::view_word: no words defined: {word:?}").into()) - } - fn view <'a> (&'a self, output: &mut O, dsl: &'a impl Dsl) -> Usually { - if let Ok(Some(expr)) = dsl.expr() { - self.view_expr(output, &expr) - } else if let Ok(Some(word)) = dsl.word() { - self.view_word(output, &word) - } else { - panic!("{dsl:?}: invalid") - } - } -} - -pub fn evaluate_output_expression <'a, O: Out + 'a, S> ( - state: &S, output: &mut O, expr: &'a impl DslExpr -) -> Usually where - S: View - + for<'b>DslNs<'b, bool> - + for<'b>DslNs<'b, O::Unit> -{ - // First element of expression is used for dispatch. - // Dispatch is proto-namespaced using separator character - let head = expr.head()?; - let mut frags = head.src()?.unwrap_or_default().split("/"); - // The rest of the tokens in the expr are arguments. - // Their meanings depend on the dispatched operation - let args = expr.tail(); - let arg0 = args.head(); - let tail0 = args.tail(); - let arg1 = tail0.head(); - let tail1 = tail0.tail(); - let arg2 = tail1.head(); - // And we also have to do the above binding dance - // so that the Perhapss remain in scope. - match frags.next() { - - Some("when") => output.place(&When::new( - state.from(arg0?)?.unwrap(), - Thunk::new(move|output: &mut O|state.view(output, &arg1).unwrap()) - )), - - Some("either") => output.place(&Either::new( - state.from(arg0?)?.unwrap(), - Thunk::new(move|output: &mut O|state.view(output, &arg1).unwrap()), - Thunk::new(move|output: &mut O|state.view(output, &arg2).unwrap()) - )), - - Some("bsp") => output.place(&{ - let a = Thunk::new(move|output: &mut O|state.view(output, &arg0).unwrap()); - let b = Thunk::new(move|output: &mut O|state.view(output, &arg1).unwrap()); - match frags.next() { - Some("n") => Bsp::n(a, b), - Some("s") => Bsp::s(a, b), - Some("e") => Bsp::e(a, b), - Some("w") => Bsp::w(a, b), - Some("a") => Bsp::a(a, b), - Some("b") => Bsp::b(a, b), - frag => unimplemented!("bsp/{frag:?}") - } - }), - - Some("align") => output.place(&{ - let a = Thunk::new(move|output: &mut O|state.view(output, &arg0).unwrap()); - match frags.next() { - Some("n") => Align::n(a), - Some("s") => Align::s(a), - Some("e") => Align::e(a), - Some("w") => Align::w(a), - Some("x") => Align::x(a), - Some("y") => Align::y(a), - Some("c") => Align::c(a), - frag => unimplemented!("align/{frag:?}") - } - }), - - Some("fill") => output.place(&{ - let a = Thunk::new(move|output: &mut O|state.view(output, &arg0).unwrap()); - match frags.next() { - Some("xy") | None => Fill::XY(a), - Some("x") => Fill::X(a), - Some("y") => Fill::Y(a), - frag => unimplemented!("fill/{frag:?}") - } - }), - - Some("fixed") => output.place(&{ - let axis = frags.next(); - let arg = match axis { Some("x") | Some("y") => arg1, Some("xy") | None => arg2, _ => panic!("fixed: unsupported axis {axis:?}") }; - let cb = Thunk::new(move|output: &mut O|state.view(output, &arg).unwrap()); - match axis { - Some("xy") | None => Fixed::XY(state.from(arg0?)?.unwrap(), state.from(arg1?)?.unwrap(), cb), - Some("x") => Fixed::X(state.from(arg0?)?.unwrap(), cb), - Some("y") => Fixed::Y(state.from(arg0?)?.unwrap(), cb), - frag => unimplemented!("fixed/{frag:?} ({expr:?}) ({head:?}) ({:?})", - head.src()?.unwrap_or_default().split("/").next()) - } - }), - - Some("min") => output.place(&{ - let axis = frags.next(); - let arg = match axis { Some("x") | Some("y") => arg1, Some("xy") | None => arg2, _ => panic!("fixed: unsupported axis {axis:?}") }; - let cb = Thunk::new(move|output: &mut O|state.view(output, &arg).unwrap()); - match axis { - Some("xy") | None => Min::XY(state.from(arg0?)?.unwrap(), state.from(arg1?)?.unwrap(), cb), - Some("x") => Min::X(state.from(arg0?)?.unwrap(), cb), - Some("y") => Min::Y(state.from(arg0?)?.unwrap(), cb), - frag => unimplemented!("min/{frag:?}") - } - }), - - Some("max") => output.place(&{ - let axis = frags.next(); - let arg = match axis { Some("x") | Some("y") => arg1, Some("xy") | None => arg2, _ => panic!("fixed: unsupported axis {axis:?}") }; - let cb = Thunk::new(move|output: &mut O|state.view(output, &arg).unwrap()); - match axis { - Some("xy") | None => Max::XY(state.from(arg0?)?.unwrap(), state.from(arg1?)?.unwrap(), cb), - Some("x") => Max::X(state.from(arg0?)?.unwrap(), cb), - Some("y") => Max::Y(state.from(arg0?)?.unwrap(), cb), - frag => unimplemented!("max/{frag:?}") - } - }), - - Some("push") => output.place(&{ - let axis = frags.next(); - let arg = match axis { Some("x") | Some("y") => arg1, Some("xy") | None => arg2, _ => panic!("fixed: unsupported axis {axis:?}") }; - let cb = Thunk::new(move|output: &mut O|state.view(output, &arg).unwrap()); - match axis { - Some("xy") | None => Push::XY(state.from(arg0?)?.unwrap(), state.from(arg1?)?.unwrap(), cb), - Some("x") => Push::X(state.from(arg0?)?.unwrap(), cb), - Some("y") => Push::Y(state.from(arg0?)?.unwrap(), cb), - frag => unimplemented!("push/{frag:?}") - } - }), - - _ => return Ok(false) - - }; - Ok(true) -} diff --git a/output/src/widget/widget_field.rs b/output/src/widget/widget_field.rs index 6bedc02..8de2515 100644 --- a/output/src/widget/widget_field.rs +++ b/output/src/widget/widget_field.rs @@ -1,11 +1,25 @@ use crate::*; pub struct FieldH(pub Theme, pub Label, pub Value); -impl, V: Content> HasContent for FieldH { - fn content (&self) -> impl Content { Bsp::e(&self.1, &self.2) } +impl, V: Draw> Layout for FieldH where Self: Content { + fn layout (&self, to: O::Area) -> O::Area { + self.content().layout(to) + } +} +impl, V: Draw> Draw for FieldH where Self: Content { + fn draw (&self, to: &mut O) { + self.content().draw(to) + } } pub struct FieldV(pub Theme, pub Label, pub Value); -impl, V: Content> HasContent for FieldV { - fn content (&self) -> impl Content { Bsp::s(&self.1, &self.2) } +impl, V: Draw> Layout for FieldV where Self: Content { + fn layout (&self, to: O::Area) -> O::Area { + self.content().layout(to) + } +} +impl, V: Draw> Draw for FieldV where Self: Content { + fn draw (&self, to: &mut O) { + self.content().draw(to) + } } diff --git a/tui/Cargo.toml b/tui/Cargo.toml index 3e9ab61..9dfa085 100644 --- a/tui/Cargo.toml +++ b/tui/Cargo.toml @@ -4,9 +4,6 @@ description = "UI metaframework, Ratatui backend." version = { workspace = true } edition = { workspace = true } -[lib] -path = "src/tui.rs" - [features] dsl = [ "dep:tengri_dsl", "tengri_output/dsl" ] bumpalo = [ "dep:bumpalo" ] diff --git a/tui/examples/edn/edn01.edn b/tui/examples/edn/edn01.edn new file mode 100644 index 0000000..7ff93d1 --- /dev/null +++ b/tui/examples/edn/edn01.edn @@ -0,0 +1 @@ +:hello-world diff --git a/tui/examples/edn/edn02.edn b/tui/examples/edn/edn02.edn new file mode 100644 index 0000000..4f352a6 --- /dev/null +++ b/tui/examples/edn/edn02.edn @@ -0,0 +1 @@ +(bsp/s :hello :world) diff --git a/tui/examples/edn/edn03.edn b/tui/examples/edn/edn03.edn new file mode 100644 index 0000000..1622275 --- /dev/null +++ b/tui/examples/edn/edn03.edn @@ -0,0 +1 @@ +(fill/xy :hello-world) diff --git a/tui/examples/edn/edn04.edn b/tui/examples/edn/edn04.edn new file mode 100644 index 0000000..9393669 --- /dev/null +++ b/tui/examples/edn/edn04.edn @@ -0,0 +1 @@ +(fixed/xy 20 10 :hello-world) diff --git a/tui/examples/edn/edn05.edn b/tui/examples/edn/edn05.edn new file mode 100644 index 0000000..90313ad --- /dev/null +++ b/tui/examples/edn/edn05.edn @@ -0,0 +1 @@ +(bsp/s (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) diff --git a/tui/examples/edn/edn06.edn b/tui/examples/edn/edn06.edn new file mode 100644 index 0000000..e35abf0 --- /dev/null +++ b/tui/examples/edn/edn06.edn @@ -0,0 +1 @@ +(bsp/e (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) diff --git a/tui/examples/edn/edn07.edn b/tui/examples/edn/edn07.edn new file mode 100644 index 0000000..7370353 --- /dev/null +++ b/tui/examples/edn/edn07.edn @@ -0,0 +1 @@ +(bsp/n (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) diff --git a/tui/examples/edn/edn08.edn b/tui/examples/edn/edn08.edn new file mode 100644 index 0000000..afcde46 --- /dev/null +++ b/tui/examples/edn/edn08.edn @@ -0,0 +1 @@ +(bsp/w (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) diff --git a/tui/examples/edn/edn09.edn b/tui/examples/edn/edn09.edn new file mode 100644 index 0000000..3ced769 --- /dev/null +++ b/tui/examples/edn/edn09.edn @@ -0,0 +1 @@ +(bsp/a (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) diff --git a/tui/examples/edn/edn10.edn b/tui/examples/edn/edn10.edn new file mode 100644 index 0000000..07af31e --- /dev/null +++ b/tui/examples/edn/edn10.edn @@ -0,0 +1 @@ +(bsp/b (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) diff --git a/tui/examples/edn/edn11.edn b/tui/examples/edn/edn11.edn new file mode 100644 index 0000000..1c47cb5 --- /dev/null +++ b/tui/examples/edn/edn11.edn @@ -0,0 +1,11 @@ +(bsp/s + (bsp/e (align/nw (fixed/xy 5 3 :hello)) + (bsp/e (align/n (fixed/xy 5 3 :hello)) + (align/ne (fixed/xy 5 3 :hello)))) + (bsp/s + (bsp/e (align/w (fixed/xy 5 3 :hello)) + (bsp/e (align/c (fixed/xy 5 3 :hello)) + (align/e (fixed/xy 5 3 :hello)))) + (bsp/e (align/sw (fixed/xy 5 3 :hello)) + (bsp/e (align/s (fixed/xy 5 3 :hello)) + (align/se (fixed/xy 5 3 :hello)))))) diff --git a/tui/examples/edn/edn12.edn b/tui/examples/edn/edn12.edn new file mode 100644 index 0000000..26e3f5f --- /dev/null +++ b/tui/examples/edn/edn12.edn @@ -0,0 +1,11 @@ +(bsp/s + (bsp/e (fixed/xy 8 5 (align/nw :hello)) + (bsp/e (fixed/xy 8 5 (align/n :hello)) + (fixed/xy 8 5 (align/ne :hello)))) + (bsp/s + (bsp/e (fixed/xy 8 5 (align/w :hello)) + (bsp/e (fixed/xy 8 5 (align/c :hello)) + (fixed/xy 8 5 (align/e :hello)))) + (bsp/e (fixed/xy 8 5 (align/sw :hello)) + (bsp/e (fixed/xy 8 5 (align/s :hello)) + (fixed/xy 8 5 (align/se :hello)))))) diff --git a/tui/examples/edn/edn13.edn b/tui/examples/edn/edn13.edn new file mode 100644 index 0000000..8fe81c3 --- /dev/null +++ b/tui/examples/edn/edn13.edn @@ -0,0 +1,11 @@ +(bsp/s + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/nw :hello))) + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/n :hello))) + (grow/xy 1 1 (fixed/xy 8 5 (align/ne :hello))))) + (bsp/s + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/w :hello))) + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/c :hello))) + (grow/xy 1 1 (fixed/xy 8 5 (align/e :hello))))) + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/sw :hello))) + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/s :hello))) + (grow/xy 1 1 (fixed/xy 8 5 (align/se :hello))))))) diff --git a/tui/examples/edn/edn14.edn b/tui/examples/edn/edn14.edn new file mode 100644 index 0000000..7aab387 --- /dev/null +++ b/tui/examples/edn/edn14.edn @@ -0,0 +1 @@ +:map-e diff --git a/tui/examples/edn/edn15.edn b/tui/examples/edn/edn15.edn new file mode 100644 index 0000000..ddd4d58 --- /dev/null +++ b/tui/examples/edn/edn15.edn @@ -0,0 +1 @@ +(align/c :map-e) diff --git a/tui/examples/edn/edn16.edn b/tui/examples/edn/edn16.edn new file mode 100644 index 0000000..9c6b9ad --- /dev/null +++ b/tui/examples/edn/edn16.edn @@ -0,0 +1 @@ +:map-s diff --git a/tui/examples/edn/edn17.edn b/tui/examples/edn/edn17.edn new file mode 100644 index 0000000..efd092f --- /dev/null +++ b/tui/examples/edn/edn17.edn @@ -0,0 +1 @@ +(align/c :map-s) diff --git a/tui/examples/edn/edn99.edn b/tui/examples/edn/edn99.edn new file mode 100644 index 0000000..933db8c --- /dev/null +++ b/tui/examples/edn/edn99.edn @@ -0,0 +1,73 @@ +(align/c (bg/behind :bg0 (margin/xy 1 1 (col + (bg/behind :bg1 (border/around :border1 (margin/xy 2 1 :label1))) + (bg/behind :bg2 (border/around :border2 (margin/xy 4 2 :label2))) + (bg/behind :bg3 (border/around :border3 (margin/xy 6 3 :label3))))))) + + fn content (&self) -> dyn Draw { + let border_style = Style::default().fg(Color::Rgb(0,0,0)); + Align::Center(Layers::new(move|add|{ + + add(&Background(Color::Rgb(0,128,128)))?; + + add(&Margin::XY(1, 1, Stack::down(|add|{ + + add(&Layers::new(|add|{ + add(&Background(Color::Rgb(128,96,0)))?; + add(&Border(Square(border_style)))?; + add(&Margin::XY(2, 1, "..."))?; + Ok(()) + }).debug())?; + + add(&Layers::new(|add|{ + add(&Background(Color::Rgb(128,64,0)))?; + add(&Border(Lozenge(border_style)))?; + add(&Margin::XY(4, 2, "---"))?; + Ok(()) + }).debug())?; + + add(&Layers::new(|add|{ + add(&Background(Color::Rgb(96,64,0)))?; + add(&Border(SquareBold(border_style)))?; + add(&Margin::XY(6, 3, "~~~"))?; + Ok(()) + }).debug())?; + + Ok(()) + })).debug())?; + + Ok(()) + + })) + //Align::Center(Margin::X(1, Layers::new(|add|{ + //add(&Background(Color::Rgb(128,0,0)))?; + //add(&Stack::down(|add|{ + //add(&Margin::Y(1, Layers::new(|add|{ + //add(&Background(Color::Rgb(0,128,0)))?; + //add(&Align::Center("12345"))?; + //add(&Align::Center("FOO")) + //})))?; + //add(&Margin::XY(1, 1, Layers::new(|add|{ + //add(&Align::Center("1234567"))?; + //add(&Align::Center("BAR"))?; + //add(&Background(Color::Rgb(0,0,128))) + //}))) + //})) + //}))) + + //Align::Y(Layers::new(|add|{ + //add(&Background(Color::Rgb(128,0,0)))?; + //add(&Margin::X(1, Align::Center(Stack::down(|add|{ + //add(&Align::X(Margin::Y(1, Layers::new(|add|{ + //add(&Background(Color::Rgb(0,128,0)))?; + //add(&Align::Center("12345"))?; + //add(&Align::Center("FOO")) + //})))?; + //add(&Margin::XY(1, 1, Layers::new(|add|{ + //add(&Align::Center("1234567"))?; + //add(&Align::Center("BAR"))?; + //add(&Background(Color::Rgb(0,0,128))) + //})))?; + //Ok(()) + //}))))) + //})) + } diff --git a/tui/examples/tui.rs b/tui/examples/tui.rs new file mode 100644 index 0000000..323dc9b --- /dev/null +++ b/tui/examples/tui.rs @@ -0,0 +1,101 @@ +use tengri::{self, Usually, Perhaps, input::*, output::*, tui::*, dsl::*}; +use std::sync::{Arc, RwLock}; +//use crossterm::event::{*, KeyCode::*}; +use crate::ratatui::style::Color; + +fn main () -> Usually<()> { + let state = Arc::new(RwLock::new(Example(10, Measure::new()))); + Tui::new().unwrap().run(&state)?; + Ok(()) +} + +#[derive(Debug)] pub struct Example(usize, Measure); + +const KEYMAP: &str = "(@left prev) (@right next)"; +const EXAMPLES: &'static [&'static str] = &[ + include_str!("edn/edn01.edn"), + include_str!("edn/edn02.edn"), + include_str!("edn/edn03.edn"), + include_str!("edn/edn04.edn"), + include_str!("edn/edn05.edn"), + include_str!("edn/edn06.edn"), + include_str!("edn/edn07.edn"), + include_str!("edn/edn08.edn"), + include_str!("edn/edn09.edn"), + include_str!("edn/edn10.edn"), + include_str!("edn/edn11.edn"), + include_str!("edn/edn12.edn"), + //include_str!("edn/edn13.edn"), + include_str!("edn/edn14.edn"), + include_str!("edn/edn15.edn"), + include_str!("edn/edn16.edn"), + include_str!("edn/edn17.edn"), +]; + +handle!(TuiIn: |self: Example, input|{ + Ok(None)/*if let Some(command) = CstIter::new(KEYMAP).command::<_, ExampleCommand, _>(self, input) { + command.execute(self)?; + Some(true) + } else { + None + })*/ +}); + +//#[tengri_proc::expose] +//impl Example { + //fn _todo_u16_stub (&self) -> u16 { todo!() } + //fn _todo_bool_stub (&self) -> bool { todo!() } + //fn _todo_usize_stub (&self) -> usize { todo!() } + ////[bool] => {} + ////[u16] => {} + ////[usize] => {} +//} + +#[tengri_proc::command(Example)] +impl ExampleCommand { + fn next (state: &mut Example) -> Perhaps { + state.0 = (state.0 + 1) % EXAMPLES.len(); + Ok(None) + } + fn prev (state: &mut Example) -> Perhaps { + state.0 = if state.0 > 0 { state.0 - 1 } else { EXAMPLES.len() - 1 }; + Ok(None) + } +} + +tui_draw!(|self: Example, to|to.place(&self.content())); +content!(TuiOut: |self: Example|{ + let index = self.0 + 1; + let wh = self.1.wh(); + let src = EXAMPLES.get(self.0).unwrap_or(&""); + let heading = format!("Example {}/{} in {:?}", index, EXAMPLES.len(), &wh); + let title = Tui::bg(Color::Rgb(60, 10, 10), Push::y(1, Align::n(heading))); + let code = Tui::bg(Color::Rgb(10, 60, 10), Push::y(2, Align::n(format!("{}", src)))); + let content = ();//Tui::bg(Color::Rgb(10, 10, 60), View(self, CstIter::new(src))); + self.1.of(Bsp::s(title, Bsp::n(""/*code*/, content))) +}); + +//#[tengri_proc::view(TuiOut)] +//impl Example { + //pub fn title (&self) -> impl Draw + use<'_> { + //Tui::bg(Color::Rgb(60, 10, 10), Push::y(1, Align::n(format!("Example {}/{}:", self.0 + 1, EXAMPLES.len())))).boxed() + //} + //pub fn code (&self) -> impl Draw + use<'_> { + //Tui::bg(Color::Rgb(10, 60, 10), Push::y(2, Align::n(format!("{}", EXAMPLES[self.0])))).boxed() + //} + //pub fn hello (&self) -> impl Draw + use<'_> { + //Tui::bg(Color::Rgb(10, 100, 10), "Hello").boxed() + //} + //pub fn world (&self) -> impl Draw + use<'_> { + //Tui::bg(Color::Rgb(100, 10, 10), "world").boxed() + //} + //pub fn hello_world (&self) -> impl Draw + use<'_> { + //"Hello world!".boxed() + //} + //pub fn map_e (&self) -> impl Draw + use<'_> { + //Map::east(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed() + //} + //pub fn map_s (&self) -> impl Draw + use<'_> { + //Map::south(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed() + //} +//} diff --git a/tui/examples/tui_00.rs b/tui/examples/tui_00.rs deleted file mode 100644 index f13049e..0000000 --- a/tui/examples/tui_00.rs +++ /dev/null @@ -1,123 +0,0 @@ -use tengri::{self, Usually, Perhaps, input::*, output::*, tui::*, dsl::*}; -use std::sync::{Arc, RwLock}; -use crate::ratatui::style::Color; -//use crossterm::event::{*, KeyCode::*}; - -fn main () -> Usually<()> { - let state = Example::new(); - Tui::new().unwrap().run(&state)?; - Ok(()) -} - -#[derive(Debug)] struct Example(usize, Measure); - -handle!(TuiIn: |self: Example, input|Ok(None)); -enum ExampleCommand { Next, Prev } -impl ExampleCommand { - fn eval (&self, state: &mut Example) -> Perhaps { - match self { - Self::Next => { - state.0 = (state.0 + 1) % Example::VIEWS.len(); - Ok(Some(Self::Prev)) - }, - Self::Prev => { - state.0 = if state.0 > 0 { state.0 - 1 } else { Example::VIEWS.len() - 1 }; - Ok(Some(Self::Next)) - } - } - } -} - -impl Draw for Example { - fn content (&self) -> impl Draw { - let index = self.0 + 1; - let wh = self.1.wh(); - let src = Self::VIEWS.get(self.0).unwrap_or(&""); - let heading = format!("Example {}/{} in {:?}", index, Self::VIEWS.len(), &wh); - let title = Tui::bg(Color::Rgb(60, 10, 10), Push::y(1, Align::n(heading))); - let code = Tui::bg(Color::Rgb(10, 60, 10), Push::y(2, Align::n(format!("{}", src)))); - let content = ();//Tui::bg(Color::Rgb(10, 10, 60), View(self, CstIter::new(src))); - self.1.of(Bsp::s(title, Bsp::n(""/*code*/, content))) - } -} -impl View for Example { - fn view_expr <'a> (&'a self, to: &mut TuiOut, expr: &'a impl DslExpr) -> Usually<()> { - if evaluate_output_expression(self, to, expr)? - || evaluate_output_expression_tui(self, to, expr)? { - Ok(()) - } else { - Err(format!("Example::view_expr: unexpected: {expr:?}").into()) - } - } -} - -impl Example { - fn new () -> Arc> { - Arc::new(RwLock::new(Example(10, Measure::new()))) - } - const BINDS: &'static str = stringify! { - (@left prev) - (@right next) - }; - const VIEWS: &'static [&'static str] = &[ - stringify! { :hello-world }, - stringify! { (fill/xy :hello-world) }, - stringify! { (bsp/s :hello :world) }, - stringify! { (fixed/xy 20 10 :hello-world) }, - stringify! { (bsp/s (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, - stringify! { (bsp/e (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, - stringify! { (bsp/n (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, - stringify! { (bsp/w (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, - stringify! { (bsp/a (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, - stringify! { (bsp/b (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, - stringify! { - (bsp/s - (bsp/e (align/nw (fixed/xy 5 3 :hello)) - (bsp/e (align/n (fixed/xy 5 3 :hello)) - (align/ne (fixed/xy 5 3 :hello)))) - (bsp/s - (bsp/e (align/w (fixed/xy 5 3 :hello)) - (bsp/e (align/c (fixed/xy 5 3 :hello)) - (align/e (fixed/xy 5 3 :hello)))) - (bsp/e (align/sw (fixed/xy 5 3 :hello)) - (bsp/e (align/s (fixed/xy 5 3 :hello)) - (align/se (fixed/xy 5 3 :hello)))))) - }, - stringify! { - (bsp/s - (bsp/e (fixed/xy 8 5 (align/nw :hello)) - (bsp/e (fixed/xy 8 5 (align/n :hello)) - (fixed/xy 8 5 (align/ne :hello)))) - (bsp/s - (bsp/e (fixed/xy 8 5 (align/w :hello)) - (bsp/e (fixed/xy 8 5 (align/c :hello)) - (fixed/xy 8 5 (align/e :hello)))) - (bsp/e (fixed/xy 8 5 (align/sw :hello)) - (bsp/e (fixed/xy 8 5 (align/s :hello)) - (fixed/xy 8 5 (align/se :hello)))))) - }, - stringify! { - (bsp/s - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/nw :hello))) - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/n :hello))) - (grow/xy 1 1 (fixed/xy 8 5 (align/ne :hello))))) - (bsp/s - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/w :hello))) - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/c :hello))) - (grow/xy 1 1 (fixed/xy 8 5 (align/e :hello))))) - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/sw :hello))) - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/s :hello))) - (grow/xy 1 1 (fixed/xy 8 5 (align/se :hello))))))) - }, - stringify! { :map-e }, - stringify! { (align/c :map-e) }, - stringify! { :map-s }, - stringify! { (align/c :map-s) }, - stringify! { - (align/c (bg/behind :bg0 (margin/xy 1 1 (col - (bg/behind :bg1 (border/around :border1 (margin/xy 2 1 :label1))) - (bg/behind :bg2 (border/around :border2 (margin/xy 4 2 :label2))) - (bg/behind :bg3 (border/around :border3 (margin/xy 6 3 :label3))))))) - }, - ]; -} diff --git a/tui/examples/tui_01.rs b/tui/examples/tui_01.rs deleted file mode 100644 index 58d505e..0000000 --- a/tui/examples/tui_01.rs +++ /dev/null @@ -1,105 +0,0 @@ -fn main () {} - -//#[tengri_proc::expose] -//impl Example { - //fn _todo_u16_stub (&self) -> u16 { todo!() } - //fn _todo_bool_stub (&self) -> bool { todo!() } - //fn _todo_usize_stub (&self) -> usize { todo!() } - ////[bool] => {} - ////[u16] => {} - ////[usize] => {} -//} - -//#[tengri_proc::view(TuiOut)] -//impl Example { - //pub fn title (&self) -> impl Draw + use<'_> { - //Tui::bg(Color::Rgb(60, 10, 10), Push::y(1, Align::n(format!("Example {}/{}:", self.0 + 1, VIEWS.len())))).boxed() - //} - //pub fn code (&self) -> impl Draw + use<'_> { - //Tui::bg(Color::Rgb(10, 60, 10), Push::y(2, Align::n(format!("{}", VIEWS[self.0])))).boxed() - //} - //pub fn hello (&self) -> impl Draw + use<'_> { - //Tui::bg(Color::Rgb(10, 100, 10), "Hello").boxed() - //} - //pub fn world (&self) -> impl Draw + use<'_> { - //Tui::bg(Color::Rgb(100, 10, 10), "world").boxed() - //} - //pub fn hello_world (&self) -> impl Draw + use<'_> { - //"Hello world!".boxed() - //} - //pub fn map_e (&self) -> impl Draw + use<'_> { - //Map::east(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed() - //} - //pub fn map_s (&self) -> impl Draw + use<'_> { - //Map::south(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed() - //} -//} - - //fn content (&self) -> dyn Draw { - //let border_style = Style::default().fg(Color::Rgb(0,0,0)); - //Align::Center(Layers::new(move|add|{ - - //add(&Background(Color::Rgb(0,128,128)))?; - - //add(&Margin::XY(1, 1, Stack::down(|add|{ - - //add(&Layers::new(|add|{ - //add(&Background(Color::Rgb(128,96,0)))?; - //add(&Border(Square(border_style)))?; - //add(&Margin::XY(2, 1, "..."))?; - //Ok(()) - //}).debug())?; - - //add(&Layers::new(|add|{ - //add(&Background(Color::Rgb(128,64,0)))?; - //add(&Border(Lozenge(border_style)))?; - //add(&Margin::XY(4, 2, "---"))?; - //Ok(()) - //}).debug())?; - - //add(&Layers::new(|add|{ - //add(&Background(Color::Rgb(96,64,0)))?; - //add(&Border(SquareBold(border_style)))?; - //add(&Margin::XY(6, 3, "~~~"))?; - //Ok(()) - //}).debug())?; - - //Ok(()) - //})).debug())?; - - //Ok(()) - - //})) - ////Align::Center(Margin::X(1, Layers::new(|add|{ - ////add(&Background(Color::Rgb(128,0,0)))?; - ////add(&Stack::down(|add|{ - ////add(&Margin::Y(1, Layers::new(|add|{ - ////add(&Background(Color::Rgb(0,128,0)))?; - ////add(&Align::Center("12345"))?; - ////add(&Align::Center("FOO")) - ////})))?; - ////add(&Margin::XY(1, 1, Layers::new(|add|{ - ////add(&Align::Center("1234567"))?; - ////add(&Align::Center("BAR"))?; - ////add(&Background(Color::Rgb(0,0,128))) - ////}))) - ////})) - ////}))) - - ////Align::Y(Layers::new(|add|{ - ////add(&Background(Color::Rgb(128,0,0)))?; - ////add(&Margin::X(1, Align::Center(Stack::down(|add|{ - ////add(&Align::X(Margin::Y(1, Layers::new(|add|{ - ////add(&Background(Color::Rgb(0,128,0)))?; - ////add(&Align::Center("12345"))?; - ////add(&Align::Center("FOO")) - ////})))?; - ////add(&Margin::XY(1, 1, Layers::new(|add|{ - ////add(&Align::Center("1234567"))?; - ////add(&Align::Center("BAR"))?; - ////add(&Background(Color::Rgb(0,0,128))) - ////})))?; - ////Ok(()) - ////}))))) - ////})) - //} diff --git a/tui/src/lib.rs b/tui/src/lib.rs new file mode 100644 index 0000000..a2ca42b --- /dev/null +++ b/tui/src/lib.rs @@ -0,0 +1,59 @@ +#![feature(type_changing_struct_update)] +mod tui_engine; pub use self::tui_engine::*; +mod tui_content; pub use self::tui_content::*; +pub(crate) use ::tengri_core::*; +#[cfg(feature = "dsl")] pub use ::tengri_dsl::*; +pub use ::tengri_input as input; pub(crate) use ::tengri_input::*; +pub use ::tengri_output as output; pub(crate) use ::tengri_output::*; +pub(crate) use atomic_float::AtomicF64; +pub use ::better_panic; pub(crate) use ::better_panic::{Settings, Verbosity}; +pub use ::palette; pub(crate) use ::palette::{*, convert::*, okhsl::*}; +pub use ::crossterm; pub(crate) use ::crossterm::{ + ExecutableCommand, + terminal::{EnterAlternateScreen, LeaveAlternateScreen, enable_raw_mode, disable_raw_mode}, + event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState}, +}; +pub use ::ratatui; pub(crate) use ratatui::{ + prelude::{Color, Style, Buffer}, + style::Modifier, + backend::{Backend, CrosstermBackend, ClearType}, + layout::{Size, Rect}, + buffer::Cell +}; +pub(crate) use std::sync::{Arc, RwLock, atomic::{AtomicBool, Ordering::*}}; +pub(crate) use std::io::{stdout, Stdout}; +#[cfg(test)] #[test] fn test_tui_engine () -> Usually<()> { + use crate::*; + //use std::sync::{Arc, RwLock}; + struct TestComponent(String); + impl Content for TestComponent { + fn content (&self) -> impl Draw { + Some(self.0.as_str()) + } + } + impl Handle for TestComponent { + fn handle (&mut self, _from: &TuiIn) -> Perhaps { + Ok(None) + } + } + let engine = Tui::new()?; + engine.read().unwrap().exited.store(true, std::sync::atomic::Ordering::Relaxed); + let state = TestComponent("hello world".into()); + let _state = std::sync::Arc::new(std::sync::RwLock::new(state)); + //engine.run(&state)?; + Ok(()) +} +#[cfg(test)] #[test] fn test_parse_key () { + //use KeyModifiers as Mods; + let _test = |x: &str, y|assert_eq!(KeyMatcher::new(x).build(), Some(Event::Key(y))); + //test(":x", + //KeyEvent::new(KeyCode::Char('x'), Mods::NONE)); + //test(":ctrl-x", + //KeyEvent::new(KeyCode::Char('x'), Mods::CONTROL)); + //test(":alt-x", + //KeyEvent::new(KeyCode::Char('x'), Mods::ALT)); + //test(":shift-x", + //KeyEvent::new(KeyCode::Char('x'), Mods::SHIFT)); + //test(":ctrl-alt-shift-x", + //KeyEvent::new(KeyCode::Char('x'), Mods::CONTROL | Mods::ALT | Mods::SHIFT )); +} diff --git a/tui/src/tui.rs b/tui/src/tui.rs deleted file mode 100644 index c54a44e..0000000 --- a/tui/src/tui.rs +++ /dev/null @@ -1,78 +0,0 @@ -#![feature(type_changing_struct_update)] -#[cfg(test)] mod tui_test; -mod tui_engine; pub use self::tui_engine::*; -mod tui_content; pub use self::tui_content::*; -pub use ::{ - tengri_input, - tengri_output, - ratatui, - crossterm, - palette, - better_panic -}; -pub(crate) use ::{ - tengri_core::*, - tengri_input::*, - tengri_output::*, - atomic_float::AtomicF64, - std::{io::{stdout, Stdout}, sync::{Arc, RwLock, atomic::{AtomicBool, Ordering::*}}}, - better_panic::{Settings, Verbosity}, - palette::{*, convert::*, okhsl::*}, - ratatui::{ - prelude::{Color, Style, Buffer}, - style::Modifier, - backend::{Backend, CrosstermBackend, ClearType}, - layout::{Size, Rect}, - buffer::Cell - }, - crossterm::{ - ExecutableCommand, - terminal::{EnterAlternateScreen, LeaveAlternateScreen, enable_raw_mode, disable_raw_mode}, - event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState}, - } -}; - -#[cfg(feature = "dsl")] use tengri_dsl::*; -#[cfg(feature = "dsl")] -pub fn evaluate_output_expression_tui <'a, S> ( - state: &S, mut output: &mut TuiOut, expr: impl DslExpr + 'a -) -> Usually where - S: View - + for<'b>DslNs<'b, bool> - + for<'b>DslNs<'b, u16> - + for<'b>DslNs<'b, Color> -{ - // See `tengri_output::evaluate_output_expression` - let head = expr.head()?; - let mut frags = head.src()?.unwrap_or_default().split("/"); - let args = expr.tail(); - let arg0 = args.head(); - let tail0 = args.tail(); - let arg1 = tail0.head(); - let tail1 = tail0.tail(); - let arg2 = tail1.head(); - match frags.next() { - - Some("text") => if let Some(src) = args?.src()? { output.place(&src) }, - - Some("fg") => { - let arg0 = arg0?.expect("fg: expected arg 0 (color)"); - output.place(&Tui::fg( - DslNs::::from(state, arg0)?.unwrap_or_else(||panic!("fg: {arg0:?}: not a color")), - Thunk::new(move|output: &mut TuiOut|state.view(output, &arg1).unwrap()), - )) - }, - - Some("bg") => { - let arg0 = arg0?.expect("bg: expected arg 0 (color)"); - output.place(&Tui::bg( - DslNs::::from(state, arg0)?.unwrap_or_else(||panic!("bg: {arg0:?}: not a color")), - Thunk::new(move|output: &mut TuiOut|state.view(output, &arg1).unwrap()), - )) - }, - - _ => return Ok(false) - - }; - Ok(true) -} diff --git a/tui/src/tui_content.rs b/tui/src/tui_content.rs index 38cf5ba..184d7ea 100644 --- a/tui/src/tui_content.rs +++ b/tui/src/tui_content.rs @@ -1,12 +1,42 @@ #[allow(unused)] use crate::*; + impl Tui { - pub const fn fg (color: Color, w: T) -> Foreground { Foreground(color, w) } - pub const fn bg (color: Color, w: T) -> Background { Background(color, w) } - pub const fn fg_bg (fg: Color, bg: Color, w: T) -> Background> { Background(bg, Foreground(fg, w)) } - pub const fn modify (enable: bool, modifier: Modifier, w: T) -> Modify { Modify(enable, modifier, w) } - pub const fn bold (enable: bool, w: T) -> Modify { Self::modify(enable, Modifier::BOLD, w) } - pub const fn border (enable: bool, style: S, w: T) -> Bordered { Bordered(enable, style, w) } + pub const fn fg (color: Color, w: T) -> TuiForeground { + TuiForeground(Foreground(color, w)) + } + pub const fn bg (color: Color, w: T) -> TuiBackground { + TuiBackground(Background(color, w)) + } + pub const fn fg_bg (fg: Color, bg: Color, w: T) -> TuiBackground> { + TuiBackground(Background(bg, TuiForeground(Foreground(fg, w)))) + } + pub const fn modify (enable: bool, modifier: Modifier, w: T) -> Modify { + Modify(enable, modifier, w) + } + pub const fn bold (enable: bool, w: T) -> Modify { + Self::modify(enable, Modifier::BOLD, w) + } + pub const fn border (enable: bool, style: S, w: T) -> Bordered { + Bordered(enable, style, w) + } } + +#[macro_export] macro_rules! tui_layout ((|$self:ident:$Self:ty, $to:ident|$expr:expr)=>{ + impl Layout for $Self { + fn layout (&$self, $to: [u16;4]) -> [u16;4] { + $expr + } + } +}); + +#[macro_export] macro_rules! tui_draw ((|$self:ident:$Self:ty, $to:ident|$expr:expr)=>{ + impl Draw for $Self { + fn draw (&$self, $to: &mut TuiOut) { + $expr + } + } +}); + mod tui_border; pub use self::tui_border::*; mod tui_button; pub use self::tui_button::*; mod tui_color; pub use self::tui_color::*; @@ -16,41 +46,67 @@ mod tui_phat; pub use self::tui_phat::*; mod tui_repeat; pub use self::tui_repeat::*; mod tui_scroll; pub use self::tui_scroll::*; mod tui_string; pub use self::tui_string::*; + mod tui_number; //pub use self::tui_number::*; mod tui_tryptich; //pub use self::tui_tryptich::*; + +pub struct TuiForeground(pub(crate) Foreground); +pub struct TuiBackground(pub(crate) Background); pub struct Modify(pub bool, pub Modifier, pub T); pub struct Styled(pub Option