mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2026-01-31 19:06:41 +01:00
Compare commits
No commits in common. "3ebdf9e71f4ac50b680c3b2164a6a9361b2d3e4d" and "c5cdbf4f0741bc5b722847769e1f3f628fb95f6b" have entirely different histories.
3ebdf9e71f
...
c5cdbf4f07
64 changed files with 1424 additions and 1183 deletions
9
Justfile
9
Justfile
|
|
@ -2,9 +2,6 @@ export LLVM_PROFILE_FILE := "cov/cargo-test-%p-%m.profraw"
|
||||||
grcov-binary := "--binary-path ./target/coverage/deps/"
|
grcov-binary := "--binary-path ./target/coverage/deps/"
|
||||||
grcov-ignore := "--ignore-not-existing --ignore '../*' --ignore \"/*\" --ignore 'target/*'"
|
grcov-ignore := "--ignore-not-existing --ignore '../*' --ignore \"/*\" --ignore 'target/*'"
|
||||||
|
|
||||||
default:
|
|
||||||
just -l
|
|
||||||
|
|
||||||
bacon:
|
bacon:
|
||||||
bacon -s
|
bacon -s
|
||||||
|
|
||||||
|
|
@ -28,7 +25,5 @@ doc:
|
||||||
CARGO_INCREMENTAL=0 RUSTFLAGS='-Cinstrument-coverage' RUSTDOCFLAGS='-Cinstrument-coverage' \
|
CARGO_INCREMENTAL=0 RUSTFLAGS='-Cinstrument-coverage' RUSTDOCFLAGS='-Cinstrument-coverage' \
|
||||||
cargo doc
|
cargo doc
|
||||||
|
|
||||||
example-tui-00:
|
example-tui:
|
||||||
cargo run -p tengri_tui --example tui_00
|
cargo run -p tengri_tui --example tui
|
||||||
example-tui-01:
|
|
||||||
cargo run -p tengri_tui --example tui_01
|
|
||||||
|
|
|
||||||
44
bacon.toml
44
bacon.toml
|
|
@ -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
|
|
||||||
|
|
@ -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 (
|
#[macro_export] macro_rules! flex_trait (
|
||||||
($Trait:ident $(<$($A:ident:$T:ident),+>)? $(:$dep:ident $(<$dtt:tt>)? $(+$dep2:ident $(<$dtt2:tt>)?)*)? {
|
($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>)?)*)? {
|
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_ {
|
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_ {
|
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_> {
|
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Arc<_T_> {
|
||||||
$(fn $fn $(<$($fl),*>)? (& $($fl2)* $self $(,$arg:$ty)*) $(-> $ret)? { (*$self).$fn($($arg),*) })*
|
$(fn $fn (&$self $(,$arg:$ty)*) -> $ret { (*$self).$fn($($arg),*) })*
|
||||||
}
|
|
||||||
impl<$($($A: $T,)+)?> $Trait $(<$($A),+>)? for Box<dyn $Trait $(<$($A),+>)?> {
|
|
||||||
$(fn $fn $(<$($fl),*>)? (& $($fl2)* $self $(,$arg:$ty)*) $(-> $ret)? { (**$self).$fn($($arg),*) })*
|
|
||||||
}
|
}
|
||||||
//impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for Option<_T_> {
|
//impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for Option<_T_> {
|
||||||
//$(fn $fn (&$self $(,$arg:$ty)*) -> $ret {
|
//$(fn $fn (&$self $(,$arg:$ty)*) -> $ret {
|
||||||
|
|
@ -43,24 +40,21 @@
|
||||||
//}
|
//}
|
||||||
});
|
});
|
||||||
/// Define a trait an implement it for read-only wrapper types. */
|
/// 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>)?)*)? {
|
($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 {
|
pub trait $Trait $(<$($A: $T),+>)? : $($dep $(<$dtt>+)? $($dep2 $(<$dtt2>)?)*+)? ?Sized {
|
||||||
$(fn $fn (&$self $(,$arg:$ty)*) $(-> $ret)? $body)*
|
$(fn $fn (&$self $(,$arg:$ty)*) -> $ret $body)*
|
||||||
}
|
}
|
||||||
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for &_T_ {
|
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_ {
|
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_> {
|
impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Arc<_T_> {
|
||||||
$(fn $fn (&$self $(,$arg:$ty)*) $(-> $ret)? { (*$self).$fn($($arg),*) })*
|
$(fn $fn (&$self $(,$arg:$ty)*) -> $ret { (*$self).$fn($($arg),*) })*
|
||||||
}
|
|
||||||
impl<$($($A: $T,)+)?> $Trait $(<$($A),+>)? for Box<dyn $Trait $(<$($A),+>)?> {
|
|
||||||
$(fn $fn (&$self $(,$arg:$ty)*) $(-> $ret)? { (**$self).$fn($($arg),*) })*
|
|
||||||
}
|
}
|
||||||
//impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for Option<_T_> {
|
//impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for Option<_T_> {
|
||||||
//$(fn $fn (&$self $(,$arg:$ty)*) -> $ret {
|
//$(fn $fn (&$self $(,$arg:$ty)*) -> $ret {
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
macro_rules!is_some(($expr:expr, $val:expr)=>{assert_eq!($expr, Ok(Some($val)))};);
|
macro_rules!is_some(($exp:expr, $val:expr)=>{assert_eq!($exp, Ok(Some($val)))};);
|
||||||
macro_rules!is_none(($expr:expr)=>{assert_eq!($expr, Ok(None))};);
|
macro_rules!is_none(($exp:expr)=>{assert_eq!($exp, Ok(None))};);
|
||||||
macro_rules!is_err(($expr:expr)=>{assert!($expr.is_err())};
|
macro_rules!is_err(($exp:expr)=>{assert!($exp.is_err())};
|
||||||
($expr:expr, $err:expr)=>{assert_eq!($expr, Err($err))};);
|
($exp:expr, $err:expr)=>{assert_eq!($exp, Err($err))};);
|
||||||
#[test] fn test_expr () -> Result<(), DslError> {
|
#[test] fn test_exp () -> Result<(), DslError> {
|
||||||
let e0 = DslError::Unexpected('a', None, None);
|
let e0 = DslError::Unexpected('a');
|
||||||
let e1 = DslError::Unexpected('(', None, None);
|
let e1 = DslError::Unexpected('(');
|
||||||
let e2 = DslError::Unexpected('b', None, None);
|
let e2 = DslError::Unexpected('b');
|
||||||
let e3 = DslError::Unexpected('d', None, None);
|
let e3 = DslError::Unexpected('d');
|
||||||
let check = |src: &str, word, expr, head, tail|{
|
let check = |src: &str, key, exp, head, tail|{
|
||||||
assert_eq!(src.word(), word, "{src}");
|
assert_eq!(src.key(), key, "{src}");
|
||||||
assert_eq!(src.expr(), expr, "{src}");
|
assert_eq!(src.exp(), exp, "{src}");
|
||||||
assert_eq!(src.head(), head, "{src}");
|
assert_eq!(src.head(), head, "{src}");
|
||||||
assert_eq!(src.tail(), tail, "{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(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")));
|
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_whitespace(' '));
|
||||||
assert!(!is_word_char(' '));
|
assert!(!is_key_start(' '));
|
||||||
assert!(is_word_char('f'));
|
assert!(is_key_start('f'));
|
||||||
|
|
||||||
is_some!(word_seek_start("foo"), 0);
|
is_some!(key_seek_start("foo"), 0);
|
||||||
is_some!(word_seek_start("foo "), 0);
|
is_some!(key_seek_start("foo "), 0);
|
||||||
is_some!(word_seek_start(" foo "), 1);
|
is_some!(key_seek_start(" foo "), 1);
|
||||||
is_some!(word_seek_length(&" foo "[1..]), 3);
|
is_some!(key_seek_length(&" foo "[1..]), 3);
|
||||||
is_some!(word_seek("foo"), (0, 3));
|
is_some!(key_seek("foo"), (0, 3));
|
||||||
is_some!(word_peek("foo"), "foo");
|
is_some!(key_peek("foo"), "foo");
|
||||||
is_some!(word_seek("foo "), (0, 3));
|
is_some!(key_seek("foo "), (0, 3));
|
||||||
is_some!(word_peek("foo "), "foo");
|
is_some!(key_peek("foo "), "foo");
|
||||||
is_some!(word_seek(" foo "), (1, 3));
|
is_some!(key_seek(" foo "), (1, 3));
|
||||||
is_some!(word_peek(" foo "), "foo");
|
is_some!(key_peek(" foo "), "foo");
|
||||||
|
|
||||||
is_err!("(foo)".word());
|
is_err!("(foo)".key());
|
||||||
is_err!("foo".expr());
|
is_err!("foo".exp());
|
||||||
|
|
||||||
is_some!("(foo)".expr(), "foo");
|
is_some!("(foo)".exp(), "foo");
|
||||||
is_some!("(foo)".head(), "(foo)");
|
is_some!("(foo)".head(), "(foo)");
|
||||||
is_none!("(foo)".tail());
|
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_some!("(foo bar baz)".head(), "(foo bar baz)");
|
||||||
is_none!("(foo bar baz)".tail());
|
is_none!("(foo bar baz)".tail());
|
||||||
|
|
||||||
is_some!("(foo bar baz)".expr().head(), "foo");
|
is_some!("(foo bar baz)".exp().head(), "foo");
|
||||||
is_some!("(foo bar baz)".expr().tail(), "bar baz");
|
is_some!("(foo bar baz)".exp().tail(), "bar baz");
|
||||||
is_some!("(foo bar baz)".expr().tail().head(), "bar");
|
is_some!("(foo bar baz)".exp().tail().head(), "bar");
|
||||||
is_some!("(foo bar baz)".expr().tail().tail(), "baz");
|
is_some!("(foo bar baz)".exp().tail().tail(), "baz");
|
||||||
|
|
||||||
is_err!("foo".expr());
|
is_err!("foo".exp());
|
||||||
is_some!("foo".word(), "foo");
|
is_some!("foo".key(), "foo");
|
||||||
is_some!(" foo".word(), "foo");
|
is_some!(" foo".key(), "foo");
|
||||||
is_some!(" foo ".word(), "foo");
|
is_some!(" foo ".key(), "foo");
|
||||||
|
|
||||||
is_some!(" foo ".head(), "foo");
|
is_some!(" foo ".head(), "foo");
|
||||||
//assert_eq!(" foo ".head().head(), Ok(None));
|
//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(), " (bar) ");
|
||||||
is_some!(" (foo) (bar) ".tail().head(), "(bar)");
|
is_some!(" (foo) (bar) ".tail().head(), "(bar)");
|
||||||
is_some!(" (foo) (bar) ".tail().head().head(), "(bar)");
|
is_some!(" (foo) (bar) ".tail().head().head(), "(bar)");
|
||||||
is_some!(" (foo) (bar) ".tail().head().expr(), "bar");
|
is_some!(" (foo) (bar) ".tail().head().exp(), "bar");
|
||||||
is_some!(" (foo) (bar) ".tail().head().expr().head(), "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(), "(foo bar baz)");
|
||||||
is_some!(" (foo bar baz) ".head().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) ".exp(), "foo bar baz");
|
||||||
is_some!(" (foo bar baz) ".expr().head(), "foo");
|
is_some!(" (foo bar baz) ".exp().head(), "foo");
|
||||||
is_some!(" (foo bar baz) ".expr().tail(), "bar baz");
|
is_some!(" (foo bar baz) ".exp().tail(), "bar baz");
|
||||||
is_some!(" (foo bar baz) ".expr().tail().head(), "bar");
|
is_some!(" (foo bar baz) ".exp().tail().head(), "bar");
|
||||||
is_some!(" (foo bar baz) ".expr().tail().tail(), "baz");
|
is_some!(" (foo bar baz) ".exp().tail().tail(), "baz");
|
||||||
is_none!(" (foo bar baz) ".tail());
|
is_none!(" (foo bar baz) ".tail());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,110 +1,40 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
pub trait HasContent<O: Out> { fn content (&self) -> impl Content<O>; }
|
|
||||||
pub trait Content<O: Out>: Draw<O> + Layout<O> {}
|
/// Composable renderable with static dispatch.
|
||||||
impl<O: Out, T: Draw<O> + Layout<O>> Content<O> for T {}
|
pub trait Content<E: Out>: Sized {
|
||||||
impl<'a, O: Out> AsRef<dyn Draw<O> + 'a> for dyn Content<O> + 'a { fn as_ref (&self) -> &(dyn Draw<O> + 'a) { self } }
|
/// Return opaque [Draw]able.
|
||||||
impl<'a, O: Out> AsRef<dyn Layout<O> + 'a> for dyn Content<O> + 'a { fn as_ref (&self) -> &(dyn Layout<O> + 'a) { self } }
|
fn content (&self) -> impl Draw<E> + Layout<E> + '_ { () }
|
||||||
/// Drawable with dynamic dispatch.
|
|
||||||
pub trait Draw<O: Out> { fn draw (&self, to: &mut O); }
|
|
||||||
impl<O: Out> Draw<O> for () { fn draw (&self, to: &mut O) {} }
|
|
||||||
impl<O: Out> Draw<O> for fn(&mut O) { fn draw (&self, to: &mut O) { (*self)(to) } }
|
|
||||||
impl<O: Out> Draw<O> for Box<dyn Draw<O>> { fn draw (&self, to: &mut O) { (**self).draw(to) } }
|
|
||||||
impl<O: Out, D: Draw<O>> Draw<O> for &D { fn draw (&self, to: &mut O) { (*self).draw(to) } }
|
|
||||||
impl<O: Out, D: Draw<O>> Draw<O> for &mut D { fn draw (&self, to: &mut O) { (**self).draw(to) } }
|
|
||||||
/// Drawable area of display.
|
|
||||||
pub trait Layout<O: Out> {
|
|
||||||
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() }
|
/// The platonic ideal unit of [Content]:
|
||||||
fn h (&self, to: O::Area) -> O::Unit {
|
/// total emptiness at dead center (e=1vg^sqrt(-1))
|
||||||
to.h().max(self.min_h(to)).min(self.max_h(to))
|
impl<E: Out> Content<E> for () {}
|
||||||
}
|
|
||||||
fn layout (&self, to: O::Area) -> O::Area {
|
impl<E: Out, T: Draw<E> + Layout<E>> Content<E> for fn()->T {
|
||||||
[self.x(to), self.y(to), self.w(to), self.h(to)].into()
|
fn content (&self) -> impl Draw<E> + Layout<E> + '_ {
|
||||||
|
self()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<O: Out> Layout<O> for () {
|
/// Implement composable content for a struct.
|
||||||
fn x (&self, a: O::Area) -> O::Unit { a.x() }
|
#[macro_export] macro_rules! content {
|
||||||
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() }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<O: Out, L: Layout<O>> Layout<O> for &L {
|
// Implement for all [Out]s.
|
||||||
fn x (&self, a: O::Area) -> O::Unit { (*self).x(a) }
|
(|$self:ident:$Struct:ty| $content:expr) => {
|
||||||
fn y (&self, a: O::Area) -> O::Unit { (*self).y(a) }
|
impl<E: Out> Content<E> for $Struct {
|
||||||
fn w (&self, a: O::Area) -> O::Unit { (*self).w(a) }
|
fn content (&$self) -> impl Draw<E> + Layout<E> + '_ { Some($content) }
|
||||||
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<O: Out, L: Layout<O>> Layout<O> for &mut L {
|
// Implement for specific [Out].
|
||||||
fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) }
|
($Out:ty:|
|
||||||
fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) }
|
$self:ident:
|
||||||
fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) }
|
$Struct:ident$(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)?
|
||||||
fn min_w (&self, a: O::Area) -> O::Unit { (**self).min_w(a) }
|
|$content:expr) => {
|
||||||
fn max_w (&self, a: O::Area) -> O::Unit { (**self).max_w(a) }
|
impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Content<$Out>
|
||||||
fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) }
|
for $Struct $(<$($($L)? $($T)?),+>)? {
|
||||||
fn min_h (&self, a: O::Area) -> O::Unit { (**self).min_h(a) }
|
fn content (&$self) -> impl Draw<$Out> + Layout<$Out> + '_ { $content }
|
||||||
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<O: Out> Layout<O> for Box<dyn Layout<O>> {
|
|
||||||
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<O: Out, L: Layout<O>> Layout<O> for Option<L> {
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,94 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub struct Bound<O: Out, D>(pub O::Area, pub D);
|
/// Drawable with dynamic dispatch.
|
||||||
|
pub trait Draw<O: Out> {
|
||||||
impl<O: Out, D: Content<O>> HasContent<O> for Bound<O, D> {
|
/// Write data to display.
|
||||||
fn content (&self) -> &impl Content<O> { &self.1 }
|
fn draw (&self, to: &mut O);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<O: Out, T> Layout<O> for Bound<O, T> {
|
impl<O: Out> Draw<O> for fn(&mut O) {
|
||||||
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<O: Out, T: Draw<O>> Draw<O> for Bound<O, T> {
|
|
||||||
fn draw (&self, to: &mut O) {
|
fn draw (&self, to: &mut O) {
|
||||||
let area = to.area();
|
self(to)
|
||||||
*to.area_mut() = self.0;
|
}
|
||||||
self.1.draw(to);
|
}
|
||||||
*to.area_mut() = area;
|
|
||||||
|
impl<O: Out> Draw<O> for () { fn draw (&self, _: &mut O) {} }
|
||||||
|
|
||||||
|
impl<O: Out, T: Draw<O>> Draw<O> for &T {
|
||||||
|
fn draw (&self, to: &mut O) { (*self).draw(to) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'x, O: Out> Draw<O> for &(dyn Draw<O> + 'x) {
|
||||||
|
fn draw (&self, to: &mut O) {
|
||||||
|
(*self).draw(to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'x, O: Out> Draw<O> for &mut (dyn Draw<O> + 'x) {
|
||||||
|
fn draw (&self, to: &mut O) {
|
||||||
|
(**self).draw(to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<O: Out, T: Draw<O>> Draw<O> for RwLock<T> {
|
||||||
|
fn draw (&self, to: &mut O) { self.read().unwrap().draw(to) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<O: Out, T: Draw<O>> Draw<O> for [T] {
|
||||||
|
fn draw (&self, to: &mut O) {
|
||||||
|
for draw in self.iter() {
|
||||||
|
draw.draw(to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//impl<O: Out, T: Draw<O>> Draw<O> for &mut T {
|
||||||
|
//fn draw (&self, to: &mut O) {
|
||||||
|
//(**self).draw(to)
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
||||||
|
//impl<O: Out, T: Iterator<Item = U>, U: Draw<O>> Draw<O> 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<O>
|
||||||
|
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<T: Draw<O>>, to|(**self).draw(to));
|
||||||
|
draw!(|self: Box<T: Draw<O>>, to|(**self).draw(to));
|
||||||
|
//draw!(|self: Option<T: Draw<O>>, to|if let Some(draw) = self { draw.draw(to) });
|
||||||
|
|
||||||
|
impl<O: Out, T: Draw<O>> Draw<O> for Option<T> {
|
||||||
|
fn draw (&self, to: &mut O) {
|
||||||
|
if let Some(draw) = self {
|
||||||
|
draw.draw(to)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,98 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
pub trait Layout<O: Out> {
|
||||||
|
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<O>
|
||||||
|
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<O: Out> Layout<O> for () {}
|
||||||
|
|
||||||
|
impl<O: Out, L: Layout<O>> Layout<O> for &L { /*FIXME*/ }
|
||||||
|
|
||||||
|
impl<O: Out, L: Layout<O>> Layout<O> for RwLock<L> { /*FIXME*/ }
|
||||||
|
|
||||||
|
impl<O: Out, L: Layout<O>> Layout<O> for Option<L> { /*FIXME*/ }
|
||||||
|
|
||||||
|
//impl<O: Out> Layout<O> for fn(&mut O) {}
|
||||||
|
|
||||||
|
impl<O: Out, L: Layout<O>> Layout<O> for Arc<L> {
|
||||||
|
fn layout (&self, to: O::Area) -> O::Area {
|
||||||
|
(**self).layout(to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'x, O: Out> Layout<O> for &(dyn Draw<O> + 'x) {
|
||||||
|
fn layout (&self, to: O::Area) -> O::Area {
|
||||||
|
Fill::xy(self).layout(to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mod layout_align; pub use self::layout_align::*;
|
mod layout_align; pub use self::layout_align::*;
|
||||||
mod layout_bsp; pub use self::layout_bsp::*;
|
mod layout_bsp; pub use self::layout_bsp::*;
|
||||||
mod layout_cond; pub use self::layout_cond::*;
|
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_map; pub use self::layout_map::*;
|
||||||
mod layout_pad; pub use self::layout_pad::*;
|
mod layout_margin; pub use self::layout_margin::*;
|
||||||
mod layout_move; pub use self::layout_move::*;
|
mod layout_max; pub use self::layout_max::*;
|
||||||
mod layout_size; pub use self::layout_size::*;
|
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::*;
|
mod layout_stack; //pub use self::layout_stack::*;
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
//! assert_eq!(Draw::layout(item, area), expected);
|
//! 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::nw(four()), [10, 10, 4, 4]);
|
||||||
//! test(area, &Align::n(four()), [18, 10, 4, 4]);
|
//! test(area, &Align::n(four()), [18, 10, 4, 4]);
|
||||||
//! test(area, &Align::ne(four()), [26, 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::sw(four()), [10, 26, 4, 4]);
|
||||||
//! test(area, &Align::w(four()), [10, 18, 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::nw(two_by_four()), [10, 10, 4, 2]);
|
||||||
//! test(area, &Align::n(two_by_four()), [18, 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]);
|
//! 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]);
|
//! test(area, &Align::w(two_by_four()), [10, 19, 4, 2]);
|
||||||
//! ```
|
//! ```
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use Alignment::*;
|
|
||||||
|
|
||||||
/// 9th of area to place.
|
/// 9th of area to place.
|
||||||
#[derive(Debug, Copy, Clone, Default)]
|
#[derive(Debug, Copy, Clone, Default)]
|
||||||
pub enum Alignment { #[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W }
|
pub enum Alignment { #[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W }
|
||||||
|
|
||||||
pub struct Align<T>(Alignment, T);
|
pub struct Align<T>(Alignment, T);
|
||||||
|
|
||||||
impl<T> Align<T> {
|
impl<T> Align<T> {
|
||||||
#[inline] pub const fn c (a: T) -> Self { Self(Alignment::Center, a) }
|
#[inline] pub const fn c (a: T) -> Self { Self(Alignment::Center, a) }
|
||||||
#[inline] pub const fn x (a: T) -> Self { Self(Alignment::X, a) }
|
#[inline] pub const fn x (a: T) -> Self { Self(Alignment::X, a) }
|
||||||
|
|
@ -46,24 +47,39 @@ impl<T> Align<T> {
|
||||||
#[inline] pub const fn ne (a: T) -> Self { Self(Alignment::NE, a) }
|
#[inline] pub const fn ne (a: T) -> Self { Self(Alignment::NE, a) }
|
||||||
#[inline] pub const fn se (a: T) -> Self { Self(Alignment::SE, a) }
|
#[inline] pub const fn se (a: T) -> Self { Self(Alignment::SE, a) }
|
||||||
}
|
}
|
||||||
impl<O: Out, T: Content<O>> Draw<O> for Align<T> {
|
|
||||||
fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), &self.1).draw(to) }
|
impl<E: Out, T: Layout<E>> Layout<E> for Align<T> {
|
||||||
}
|
fn layout (&self, on: E::Area) -> E::Area {
|
||||||
impl<O: Out, T: Layout<O>> Layout<O> for Align<T> {
|
self.0.align(on, &self.1)
|
||||||
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 {
|
impl<E: Out, T: Draw<E> + Layout<E>> Draw<E> for Align<T> {
|
||||||
match self.0 {
|
fn draw (&self, to: &mut E) {
|
||||||
NW | N | NE => to.y(),
|
to.place_at(self.layout(to.area()), &self.1)
|
||||||
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 Alignment {
|
||||||
|
fn align <E: Out> (&self, on: E::Area, content: &impl Layout<E>) -> 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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use Direction::*;
|
||||||
|
|
||||||
/// A binary split or layer.
|
/// A split or layer.
|
||||||
pub struct Bsp<Head, Tail>(
|
pub struct Bsp<Head, Tail>(
|
||||||
pub(crate) Direction,
|
pub(crate) Direction,
|
||||||
/// First element.
|
/// First element.
|
||||||
|
|
@ -18,79 +19,31 @@ impl<Head, Tail> Bsp<Head, Tail> {
|
||||||
#[inline] pub const fn b (a: Head, b: Tail) -> Self { Self(Below, a, b) }
|
#[inline] pub const fn b (a: Head, b: Tail) -> Self { Self(Below, a, b) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<O: Out, Head: Content<O>, Tail: Content<O>> Draw<O> for Bsp<Head, Tail> {
|
impl<
|
||||||
|
O: Out,
|
||||||
|
Head: Draw<O> + Layout<O>,
|
||||||
|
Tail: Draw<O> + Layout<O>
|
||||||
|
> Draw<O> for Bsp<Head, Tail> {
|
||||||
fn draw (&self, to: &mut O) {
|
fn draw (&self, to: &mut O) {
|
||||||
match self.0 {
|
let [a, b, _] = bsp_areas(to.area(), self.0, &self.1, &self.2);
|
||||||
South => {
|
if self.0 == Below {
|
||||||
panic!("{}", self.1.h(to.area()));
|
to.place_at(a, &self.1);
|
||||||
let area_1 = self.1.layout(to.area());
|
to.place_at(b, &self.2);
|
||||||
let area_2 = self.2.layout([
|
} else {
|
||||||
to.area().x(),
|
to.place_at(b, &self.2);
|
||||||
to.area().y().plus(area_1.h()),
|
to.place_at(a, &self.1);
|
||||||
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);
|
|
||||||
//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<O: Out, Head: Layout<O>, Tail: Layout<O>> Layout<O> for Bsp<Head, Tail> {
|
impl<O: Out, Head: Layout<O>, Tail: Layout<O>> Layout<O> for Bsp<Head, Tail> {
|
||||||
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 {
|
fn layout (&self, area: O::Area) -> O::Area {
|
||||||
bsp_areas(area, self.0, &self.1, &self.2)[2]
|
bsp_areas(area, self.0, &self.1, &self.2)[2]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bsp_areas <O: Out, A: Layout<O>, B: Layout<O>> (
|
fn bsp_areas <O: Out> (
|
||||||
area: O::Area, direction: Direction, a: &A, b: &B,
|
area: O::Area, direction: Direction, a: &impl Layout<O>, b: &impl Layout<O>,
|
||||||
) -> [O::Area;3] {
|
) -> [O::Area;3] {
|
||||||
let [x, y, w, h] = area.xywh();
|
let [x, y, w, h] = area.xywh();
|
||||||
let [aw, ah] = a.layout(area).wh();
|
let [aw, ah] = a.layout(area).wh();
|
||||||
|
|
|
||||||
|
|
@ -1,46 +1,55 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
/// Show an item only when a condition is true.
|
/// Show an item only when a condition is true.
|
||||||
pub struct When<O, T>(bool, T, PhantomData<O>);
|
pub struct When<A>(pub bool, pub A);
|
||||||
impl<O: Out, T: Content<O>> When<O, T> {
|
impl<A> When<A> {
|
||||||
/// Create a binary condition.
|
/// 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<O: Out, T: Layout<O>> Layout<O> for When<O, T> {
|
impl<E: Out, A: Layout<E>> Layout<E> for When<A> {
|
||||||
fn layout (&self, to: O::Area) -> O::Area {
|
fn layout (&self, to: E::Area) -> E::Area {
|
||||||
let Self(cond, item, ..) = self;
|
let Self(cond, item) = self;
|
||||||
if *cond { item.layout(to) } else { O::Area::zero().into() }
|
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<O: Out, T: Content<O>> Draw<O> for When<O, T> {
|
impl<E: Out, A: Draw<E>> Draw<E> for When<A> {
|
||||||
fn draw (&self, to: &mut O) {
|
fn draw (&self, to: &mut E) {
|
||||||
let Self(cond, item, ..) = self;
|
let Self(cond, item) = self;
|
||||||
if *cond { Bound(self.layout(to.area()), item).draw(to) }
|
if *cond { item.draw(to) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show one item if a condition is true and another if the condition is false
|
/// Show one item if a condition is true and another if the condition is false
|
||||||
pub struct Either<E: Out, A, B>(pub bool, pub A, pub B, pub PhantomData<E>);
|
pub struct Either<E: Out, A: Draw<E> + Layout<E>, B: Draw<E> + Layout<E>>(pub bool, pub A, pub B, pub PhantomData<E>);
|
||||||
impl<E: Out, A: Content<E>, B: Content<E>> Either<E, A, B> {
|
impl<E: Out, A: Draw<E> + Layout<E>, B: Draw<E> + Layout<E>> Either<E, A, B> {
|
||||||
/// Create a ternary view condition.
|
/// Create a ternary view condition.
|
||||||
pub const fn new (c: bool, a: A, b: B) -> Self {
|
pub const fn new (c: bool, a: A, b: B) -> Self {
|
||||||
Self(c, a, b, PhantomData)
|
Self(c, a, b, PhantomData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<E: Out, A: Layout<E>, B: Layout<E>> Layout<E> for Either<E, A, B> {
|
impl<E: Out, A: Draw<E> + Layout<E>, B: Draw<E> + Layout<E>> Layout<E> for Either<E, A, B> {
|
||||||
fn layout (&self, to: E::Area) -> E::Area {
|
fn layout (&self, to: E::Area) -> E::Area {
|
||||||
let Self(cond, a, b, ..) = self;
|
let Self(cond, a, b, ..) = self;
|
||||||
if *cond { a.layout(to) } else { b.layout(to) }
|
if *cond { a.layout(to) } else { b.layout(to) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<E: Out, A: Content<E>, B: Content<E>> Draw<E> for Either<E, A, B> {
|
impl<E: Out, A: Draw<E> + Layout<E>, B: Draw<E> + Layout<E>> Draw<E> for Either<E, A, B> {
|
||||||
fn draw (&self, to: &mut E) {
|
fn draw (&self, to: &mut E) {
|
||||||
let Self(cond, a, b, ..) = self;
|
let Self(cond, a, b, ..) = self;
|
||||||
let area = self.layout(to.area());
|
if *cond { a.draw(to) } else { b.draw(to) }
|
||||||
if *cond { Bound(area, a).draw(to) } else { Bound(area, b).draw(to) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -107,7 +116,7 @@ impl<E: Out, A: Content<E>, B: Content<E>> Draw<E> for Either<E, A, B> {
|
||||||
//return Ok(Some(match words.next() {
|
//return Ok(Some(match words.next() {
|
||||||
//Some(Token{value: Key($x),..}) => Self::x(content),
|
//Some(Token{value: Key($x),..}) => Self::x(content),
|
||||||
//Some(Token{value: Key($y),..}) => Self::y(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!()
|
//_ => unreachable!()
|
||||||
//}))
|
//}))
|
||||||
//} else {
|
//} else {
|
||||||
|
|
@ -125,7 +134,7 @@ impl<E: Out, A: Content<E>, B: Content<E>> Draw<E> for Either<E, A, B> {
|
||||||
//state.give_or_fail(words, ||"y: no unit")?,
|
//state.give_or_fail(words, ||"y: no unit")?,
|
||||||
//state.give_or_fail(words, ||"y: no content")?,
|
//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 x")?,
|
||||||
//state.give_or_fail(words, ||"xy: no unit y")?,
|
//state.give_or_fail(words, ||"xy: no unit y")?,
|
||||||
//state.give_or_fail(words, ||"xy: no content")?
|
//state.give_or_fail(words, ||"xy: no content")?
|
||||||
|
|
|
||||||
39
output/src/layout/layout_expand.rs
Normal file
39
output/src/layout/layout_expand.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
pub enum Expand<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
||||||
|
|
||||||
|
impl<U, A> Expand<U, A> {
|
||||||
|
#[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<O: Out, T: Draw<O> + Layout<O>> Content<O> for Expand<O::Unit, T> {
|
||||||
|
fn content (&self) -> impl Draw<O> + Layout<O> + '_ {
|
||||||
|
use Expand::*;
|
||||||
|
match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<O: Out, T: Draw<O> + Layout<O>> Draw<O> for Expand<O::Unit, T> {
|
||||||
|
fn draw (&self, to: &mut O) {
|
||||||
|
to.place_at(self.layout(to.area()), &self.content())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U: Coordinate, T> Expand<U, T> {
|
||||||
|
#[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<O: Out, T: Layout<O>> Layout<O> for Expand<O::Unit, T> {
|
||||||
|
fn layout (&self, to: O::Area) -> O::Area {
|
||||||
|
[to.x(), to.y(), to.w().plus(self.dx()), to.h().plus(self.dy())].into()
|
||||||
|
}
|
||||||
|
}
|
||||||
55
output/src/layout/layout_fill.rs
Normal file
55
output/src/layout/layout_fill.rs
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
pub enum Fill<A> {
|
||||||
|
/// Use maximum width of area.
|
||||||
|
X(A),
|
||||||
|
/// Use maximum height of area.
|
||||||
|
Y(A),
|
||||||
|
/// Use maximum width and height of area.
|
||||||
|
XY(A)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Fill<T> {
|
||||||
|
#[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<O: Out, T: Draw<O>> Draw<O> for Fill<T> {
|
||||||
|
fn draw (&self, to: &mut O) {
|
||||||
|
to.place_at(self.layout(to.area()), &self.content())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<O: Out, T: Draw<O>> Layout<O> for Fill<T> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
output/src/layout/layout_fixed.rs
Normal file
47
output/src/layout/layout_fixed.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
pub enum Fixed<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
||||||
|
|
||||||
|
impl<U, A> Fixed<U, A> {
|
||||||
|
#[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<U: Coordinate, T> Fixed<U, T> {
|
||||||
|
#[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<O: Out, T: Draw<O>> Draw<O> for Fixed<O::Unit, T> {
|
||||||
|
fn draw (&self, to: &mut O) {
|
||||||
|
let area = Layout::<O>::layout(&self, to.area());
|
||||||
|
to.place_at(area, &self.content())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<O: Out, T> Layout<O> for Fixed<O::Unit, T> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -31,7 +31,7 @@ macro_rules! impl_map_direction (($name:ident, $axis:ident, $align:ident)=>{
|
||||||
O, A, Push<O::Unit, Align<Fixed<O::Unit, Fill<B>>>>, I, F, fn(A, usize)->B
|
O, A, Push<O::Unit, Align<Fixed<O::Unit, Fill<B>>>>, I, F, fn(A, usize)->B
|
||||||
> where
|
> where
|
||||||
O: Out,
|
O: Out,
|
||||||
B: Draw<O>,
|
B: Draw<O> + Layout<O>,
|
||||||
I: Iterator<Item = A> + Send + Sync + 'a,
|
I: Iterator<Item = A> + Send + Sync + 'a,
|
||||||
F: Fn() -> I + 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!(east, x, w);
|
||||||
impl_map_direction!(south, Y, n);
|
impl_map_direction!(south, y, n);
|
||||||
impl_map_direction!(west, X, e);
|
impl_map_direction!(west, x, e);
|
||||||
impl_map_direction!(north, Y, s);
|
impl_map_direction!(north, y, s);
|
||||||
|
|
||||||
impl<'a, O, A, B, I, F, G> Layout<O> for Map<O, A, B, I, F, G> where
|
impl<'a, O, A, B, I, F, G> Layout<O> for Map<O, A, B, I, F, G> where
|
||||||
O: Out,
|
O: Out,
|
||||||
B: Layout<O>,
|
B: Draw<O> + Layout<O>,
|
||||||
I: Iterator<Item = A> + Send + Sync + 'a,
|
I: Iterator<Item = A> + Send + Sync + 'a,
|
||||||
F: Fn() -> I + Send + Sync + 'a,
|
F: Fn() -> I + Send + Sync + 'a,
|
||||||
G: Fn(A, usize)->B + Send + Sync
|
G: Fn(A, usize)->B + Send + Sync
|
||||||
|
|
@ -80,21 +80,21 @@ impl<'a, O, A, B, I, F, G> Layout<O> for Map<O, A, B, I, F, G> where
|
||||||
let [mut max_x, mut max_y] = area.center();
|
let [mut max_x, mut max_y] = area.center();
|
||||||
for item in get_iter() {
|
for item in get_iter() {
|
||||||
let [x,y,w,h] = get_item(item, index).layout(area).xywh();
|
let [x,y,w,h] = get_item(item, index).layout(area).xywh();
|
||||||
min_x = min_x.min(x);
|
min_x = min_x.min(x.into());
|
||||||
min_y = min_y.min(y);
|
min_y = min_y.min(y.into());
|
||||||
max_x = max_x.max(x + w);
|
max_x = max_x.max((x + w).into());
|
||||||
max_y = max_y.max(y + h);
|
max_y = max_y.max((y + h).into());
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
let w = max_x - min_x;
|
let w = max_x - min_x;
|
||||||
let h = max_y - min_y;
|
let h = max_y - min_y;
|
||||||
//[min_x.into(), min_y.into(), w.into(), h.into()].into()
|
//[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<O> for Map<O, A, B, I, F, G> where
|
impl<'a, O, A, B, I, F, G> Draw<O> for Map<O, A, B, I, F, G> where
|
||||||
O: Out,
|
O: Out,
|
||||||
B: Content<O>,
|
B: Draw<O> + Layout<O>,
|
||||||
I: Iterator<Item = A> + Send + Sync + 'a,
|
I: Iterator<Item = A> + Send + Sync + 'a,
|
||||||
F: Fn() -> I + Send + Sync + 'a,
|
F: Fn() -> I + Send + Sync + 'a,
|
||||||
G: Fn(A, usize)->B + Send + Sync
|
G: Fn(A, usize)->B + Send + Sync
|
||||||
|
|
@ -115,25 +115,25 @@ impl<'a, O, A, B, I, F, G> Draw<O> for Map<O, A, B, I, F, G> where
|
||||||
#[inline] pub fn map_south<O: Out>(
|
#[inline] pub fn map_south<O: Out>(
|
||||||
item_offset: O::Unit,
|
item_offset: O::Unit,
|
||||||
item_height: O::Unit,
|
item_height: O::Unit,
|
||||||
item: impl Content<O>
|
item: impl Draw<O> + Layout<O>
|
||||||
) -> impl Content<O> {
|
) -> impl Draw<O> + Layout<O> {
|
||||||
Push::Y(item_offset, Fixed::Y(item_height, Fill::X(item)))
|
Push::y(item_offset, Fixed::y(item_height, Fill::x(item)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline] pub fn map_south_west<O: Out>(
|
#[inline] pub fn map_south_west<O: Out>(
|
||||||
item_offset: O::Unit,
|
item_offset: O::Unit,
|
||||||
item_height: O::Unit,
|
item_height: O::Unit,
|
||||||
item: impl Content<O>
|
item: impl Draw<O> + Layout<O>
|
||||||
) -> impl Content<O> {
|
) -> impl Draw<O> + Layout<O> {
|
||||||
Push::Y(item_offset, Align::nw(Fixed::Y(item_height, Fill::X(item))))
|
Push::y(item_offset, Align::nw(Fixed::y(item_height, Fill::x(item))))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline] pub fn map_east<O: Out>(
|
#[inline] pub fn map_east<O: Out>(
|
||||||
item_offset: O::Unit,
|
item_offset: O::Unit,
|
||||||
item_width: O::Unit,
|
item_width: O::Unit,
|
||||||
item: impl Content<O>
|
item: impl Draw<O> + Layout<O>
|
||||||
) -> impl Content<O> {
|
) -> impl Draw<O> + Layout<O> {
|
||||||
Push::X(item_offset, Align::w(Fixed::X(item_width, Fill::Y(item))))
|
Push::x(item_offset, Align::w(Fixed::x(item_width, Fill::y(item))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
50
output/src/layout/layout_margin.rs
Normal file
50
output/src/layout/layout_margin.rs
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
pub enum Margin<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
||||||
|
|
||||||
|
impl<U, A> Margin<U, A> {
|
||||||
|
#[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<E: Out, T: Draw<E> + Layout<E>> Draw<E> for Margin<E::Unit, T> {
|
||||||
|
fn draw (&self, to: &mut E) {
|
||||||
|
to.place_at(self.layout(to.area()), &self.content())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U: Coordinate, T> Margin<U, T> {
|
||||||
|
#[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<E: Out, T: Layout<E>> Layout<E> for Margin<E::Unit, T> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
47
output/src/layout/layout_max.rs
Normal file
47
output/src/layout/layout_max.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
pub enum Max<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
||||||
|
|
||||||
|
impl<U, A> Max<U, A> {
|
||||||
|
#[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<E: Out, T: Draw<E> + Layout<E>> Draw<E> for Max<E::Unit, T> {
|
||||||
|
fn draw (&self, to: &mut E) {
|
||||||
|
to.place_at(self.layout(to.area()), &self.content())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U: Coordinate, T> Max<U, T> {
|
||||||
|
#[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<E: Out, T: Layout<E>> Layout<E> for Max<E::Unit, T> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
47
output/src/layout/layout_min.rs
Normal file
47
output/src/layout/layout_min.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
pub enum Min<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
||||||
|
|
||||||
|
impl<U, A> Min<U, A> {
|
||||||
|
#[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<E: Out, T: Draw<E> + Layout<E>> Draw<E> for Min<E::Unit, T> {
|
||||||
|
fn draw (&self, to: &mut E) {
|
||||||
|
to.place_at(self.layout(to.area()), &self.content())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U: Coordinate, T> Min<U, T> {
|
||||||
|
#[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<E: Out, T: Layout<E>> Layout<E> for Min<E::Unit, T> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
/// Increment X and/or Y coordinate.
|
|
||||||
pub enum Push<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
||||||
impl<U, A> Push<U, A> {
|
|
||||||
#[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c, } }
|
|
||||||
}
|
|
||||||
impl<U: Coordinate, T> Push<U, T> {
|
|
||||||
#[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<O: Out, T: Content<O>> Draw<O> for Push<O::Unit, T> {
|
|
||||||
fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) }
|
|
||||||
}
|
|
||||||
impl<O: Out, T: Layout<O>> Layout<O> for Push<O::Unit, T> {
|
|
||||||
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<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
||||||
impl<U, A> Pull<U, A> {
|
|
||||||
#[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c, } }
|
|
||||||
}
|
|
||||||
impl<U: Coordinate, T> Pull<U, T> {
|
|
||||||
#[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<O: Out, T: Content<O>> Draw<O> for Pull<O::Unit, T> {
|
|
||||||
fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) }
|
|
||||||
}
|
|
||||||
impl<O: Out, T: Layout<O>> Layout<O> for Pull<O::Unit, T> {
|
|
||||||
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()) }
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
use Pad::*;
|
|
||||||
pub enum Pad<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
||||||
impl<U, A> Pad<U, A> {
|
|
||||||
#[inline] pub const fn inner (&self) -> &A { match self { X(_, c) | Y(_, c) | XY(_, _, c) => c, } }
|
|
||||||
}
|
|
||||||
impl<U: Coordinate, T> Pad<U, T> {
|
|
||||||
#[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<O: Out, T: Content<O>> Draw<O> for Pad<O::Unit, T> {
|
|
||||||
fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) }
|
|
||||||
}
|
|
||||||
impl<O: Out, T: Layout<O>> Layout<O> for Pad<O::Unit, T> {
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
50
output/src/layout/layout_padding.rs
Normal file
50
output/src/layout/layout_padding.rs
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
pub enum Padding<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
||||||
|
|
||||||
|
impl<U, A> Padding<U, A> {
|
||||||
|
#[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<E: Out, T: Draw<E> + Layout<E>> Draw<E> for Padding<E::Unit, T> {
|
||||||
|
fn draw (&self, to: &mut E) {
|
||||||
|
to.place_at(self.layout(to.area()), &self.content())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U: Coordinate, T> Padding<U, T> {
|
||||||
|
#[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<E: Out, T: Layout<E>> Layout<E> for Padding<E::Unit, T> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
48
output/src/layout/layout_pull.rs
Normal file
48
output/src/layout/layout_pull.rs
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
pub enum Pull<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
||||||
|
|
||||||
|
impl<U, A> Pull<U, A> {
|
||||||
|
#[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<E: Out, T: Draw<E> + Layout<E>> Draw<E> for Pull<E::Unit, T> {
|
||||||
|
fn draw (&self, to: &mut E) {
|
||||||
|
to.place_at(self.layout(to.area()), &self.content())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U: Coordinate, T> Pull<U, T> {
|
||||||
|
#[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<E: Out, T: Layout<E>> Layout<E> for Pull<E::Unit, T> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
43
output/src/layout/layout_push.rs
Normal file
43
output/src/layout/layout_push.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
pub enum Push<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
||||||
|
|
||||||
|
impl<U, A> Push<U, A> {
|
||||||
|
#[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<E: Out, T: Draw<E> + Layout<E>> Draw<E> for Push<E::Unit, T> {
|
||||||
|
fn draw (&self, to: &mut E) {
|
||||||
|
to.place_at(self.layout(to.area()), &self.content())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U: Coordinate, T> Push<U, T> {
|
||||||
|
#[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<E: Out, T: Layout<E>> Layout<E> for Push<E::Unit, T> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
43
output/src/layout/layout_shrink.rs
Normal file
43
output/src/layout/layout_shrink.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
pub enum Shrink<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
||||||
|
|
||||||
|
impl<U, A> Shrink<U, A> {
|
||||||
|
#[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<E: Out, T: Draw<E> + Layout<E>> Draw<E> for Shrink<E::Unit, T> {
|
||||||
|
fn draw (&self, to: &mut E) {
|
||||||
|
to.place_at(self.layout(to.area()), &self.content())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U: Coordinate, T> Shrink<U, T> {
|
||||||
|
#[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<E: Out, T: Layout<E>> Layout<E> for Shrink<E::Unit, T> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,121 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
pub enum Fill<A> { X(A), Y(A), XY(A) }
|
|
||||||
impl<A> Fill<A> {
|
|
||||||
#[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<O: Out, T: Content<O>> Draw<O> for Fill<T> {
|
|
||||||
fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) }
|
|
||||||
}
|
|
||||||
impl<O: Out, T: Layout<O>> Layout<O> for Fill<T> {
|
|
||||||
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<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
||||||
impl<U, A> Fixed<U, A> {
|
|
||||||
#[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c } }
|
|
||||||
}
|
|
||||||
impl<U: Copy, A> Fixed<U, A> {
|
|
||||||
#[inline] pub const fn dx (&self) -> Option<U> { match self { Self::X(x, _) | Self::XY(x, ..) => Some(*x), _ => None } }
|
|
||||||
#[inline] pub const fn dy (&self) -> Option<U> { match self { Self::Y(y, _) | Self::XY(y, ..) => Some(*y), _ => None } }
|
|
||||||
}
|
|
||||||
impl<O: Out, T: Content<O>> Draw<O> for Fixed<O::Unit, T> {
|
|
||||||
fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) }
|
|
||||||
}
|
|
||||||
impl<O: Out, T: Layout<O>> Layout<O> for Fixed<O::Unit, T> {
|
|
||||||
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<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
||||||
impl<U, A> Max<U, A> {
|
|
||||||
#[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c } }
|
|
||||||
}
|
|
||||||
impl<U: Copy, A> Max<U, A> {
|
|
||||||
#[inline] pub const fn dx (&self) -> Option<U> { match self { Self::X(x, _) | Self::XY(x, ..) => Some(*x), _ => None } }
|
|
||||||
#[inline] pub const fn dy (&self) -> Option<U> { match self { Self::Y(y, _) | Self::XY(y, ..) => Some(*y), _ => None } }
|
|
||||||
}
|
|
||||||
impl<O: Out, T: Content<O>> Draw<O> for Max<O::Unit, T> {
|
|
||||||
fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) }
|
|
||||||
}
|
|
||||||
impl<E: Out, T: Layout<E>> Layout<E> for Max<E::Unit, T> {
|
|
||||||
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<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
||||||
impl<U, A> Min<U, A> {
|
|
||||||
#[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c } }
|
|
||||||
}
|
|
||||||
impl<U: Copy, A> Min<U, A> {
|
|
||||||
#[inline] pub const fn dx (&self) -> Option<U> { match self { Self::X(x, _) | Self::XY(x, ..) => Some(*x), _ => None } }
|
|
||||||
#[inline] pub const fn dy (&self) -> Option<U> { match self { Self::Y(y, _) | Self::XY(y, ..) => Some(*y), _ => None } }
|
|
||||||
}
|
|
||||||
impl<O: Out, T: Content<O>> Draw<O> for Min<O::Unit, T> {
|
|
||||||
fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) }
|
|
||||||
}
|
|
||||||
impl<E: Out, T: Layout<E>> Layout<E> for Min<E::Unit, T> {
|
|
||||||
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<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
||||||
impl<U, A> Expand<U, A> {
|
|
||||||
#[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c } }
|
|
||||||
}
|
|
||||||
impl<U: Copy + Default, A> Expand<U, A> {
|
|
||||||
#[inline] pub const fn dx (&self) -> Option<U> { match self { Self::X(x, _) | Self::XY(x, ..) => Some(*x), _ => None } }
|
|
||||||
#[inline] pub const fn dy (&self) -> Option<U> { match self { Self::Y(y, _) | Self::XY(y, ..) => Some(*y), _ => None } }
|
|
||||||
}
|
|
||||||
impl<O: Out, T: Content<O>> Draw<O> for Expand<O::Unit, T> {
|
|
||||||
fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) }
|
|
||||||
}
|
|
||||||
impl<O: Out, T: Layout<O>> Layout<O> for Expand<O::Unit, T> {
|
|
||||||
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<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
|
|
||||||
impl<U, A> Shrink<U, A> {
|
|
||||||
#[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c } }
|
|
||||||
}
|
|
||||||
impl<U: Copy, A> Shrink<U, A> {
|
|
||||||
#[inline] pub const fn dx (&self) -> Option<U> { match self { Self::X(x, _) | Self::XY(x, ..) => Some(*x), _ => None } }
|
|
||||||
#[inline] pub const fn dy (&self) -> Option<U> { match self { Self::Y(y, _) | Self::XY(y, ..) => Some(*y), _ => None } }
|
|
||||||
}
|
|
||||||
impl<O: Out, T: Content<O>> Draw<O> for Shrink<O::Unit, T> {
|
|
||||||
fn draw (&self, to: &mut O) { Bound(self.layout(to.area()), self.inner()).draw(to) }
|
|
||||||
}
|
|
||||||
impl<E: Out, T: Layout<E>> Layout<E> for Shrink<E::Unit, T> {
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -3,55 +3,8 @@
|
||||||
#![feature(impl_trait_in_assoc_type)]
|
#![feature(impl_trait_in_assoc_type)]
|
||||||
#![feature(const_precise_live_drops)]
|
#![feature(const_precise_live_drops)]
|
||||||
#![feature(type_changing_struct_update)]
|
#![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)]
|
//#![feature(non_lifetime_binders)]
|
||||||
|
|
||||||
pub(crate) use self::Direction::*;
|
|
||||||
pub(crate) use std::fmt::{Debug, Display};
|
|
||||||
pub(crate) use std::marker::PhantomData;
|
|
||||||
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.
|
|
||||||
pub trait Out: Send + Sync + Sized {
|
|
||||||
/// Unit of length
|
|
||||||
type Unit: Coordinate;
|
|
||||||
|
|
||||||
/// Rectangle without offset
|
|
||||||
type Size: Size<Self::Unit>;
|
|
||||||
|
|
||||||
/// Rectangle with offset
|
|
||||||
type Area: Area<Self::Unit>;
|
|
||||||
|
|
||||||
/// Render drawable in area specified by `T::layout(self.area())`
|
|
||||||
#[inline] fn place <'t, T: Content<Self> + ?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<Self> + ?Sized> (&mut self, area: Self::Area, content: &'t T);
|
|
||||||
|
|
||||||
/// Current output area
|
|
||||||
fn area (&self) -> Self::Area;
|
|
||||||
#[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)] pub(crate) use proptest_derive::Arbitrary;
|
|
||||||
|
|
||||||
mod content; pub use self::content::*;
|
mod content; pub use self::content::*;
|
||||||
mod draw; pub use self::draw::*;
|
mod draw; pub use self::draw::*;
|
||||||
mod group; pub use self::group::*;
|
mod group; pub use self::group::*;
|
||||||
|
|
@ -59,6 +12,45 @@ mod layout; pub use self::layout::*;
|
||||||
mod space; pub use self::space::*;
|
mod space; pub use self::space::*;
|
||||||
mod thunk; pub use self::thunk::*;
|
mod thunk; pub use self::thunk::*;
|
||||||
mod widget; pub use self::widget::*;
|
mod widget; pub use self::widget::*;
|
||||||
|
pub(crate) use self::Direction::*;
|
||||||
|
pub(crate) use std::fmt::{Debug, Display};
|
||||||
|
pub(crate) use std::marker::PhantomData;
|
||||||
|
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::*;
|
||||||
|
|
||||||
#[cfg(feature = "dsl")] mod view;
|
/// Draw target.
|
||||||
#[cfg(feature = "dsl")] pub use self::view::*;
|
pub trait Out: Send + Sync + Sized {
|
||||||
|
/// Unit of length
|
||||||
|
type Unit: Coordinate;
|
||||||
|
/// Rectangle without offset
|
||||||
|
type Size: Size<Self::Unit>;
|
||||||
|
/// Rectangle with offset
|
||||||
|
type Area: Area<Self::Unit>;
|
||||||
|
/// 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<Self> + ?Sized> (&mut self, area: Self::Area, content: &'t T);
|
||||||
|
|
||||||
|
fn place <'t, T: Draw<Self> + Layout<Self> + ?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() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)] mod test;
|
||||||
|
#[cfg(test)] pub(crate) use proptest_derive::Arbitrary;
|
||||||
|
|
||||||
|
//impl<E: Out, C: Content<E> + Layout<E>> Draw<E> for C { // if only
|
||||||
|
//fn draw (&self, to: &mut E) {
|
||||||
|
//if let Some(content) = self.content() {
|
||||||
|
//to.place_at(self.layout(to.area()), &content);
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,6 @@ impl<E: Out> Measure<E> {
|
||||||
format!("{}x{}", self.w(), self.h()).into()
|
format!("{}x{}", self.w(), self.h()).into()
|
||||||
}
|
}
|
||||||
pub fn of <T: Draw<E>> (&self, item: T) -> Bsp<Fill<&Self>, T> {
|
pub fn of <T: Draw<E>> (&self, item: T) -> Bsp<Fill<&Self>, T> {
|
||||||
Bsp::b(Fill::XY(self), item)
|
Bsp::b(Fill::xy(self), item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,13 +84,13 @@ macro_rules! test_op_transform {
|
||||||
h in u16::MIN..u16::MAX,
|
h in u16::MIN..u16::MAX,
|
||||||
) {
|
) {
|
||||||
if let Some(op) = match (op_x, op_y) {
|
if let Some(op) = match (op_x, op_y) {
|
||||||
(Some(x), Some(y)) => Some($Op::XY(x, y, content)),
|
(Some(x), Some(y)) => Some($Op::xy(x, y, content)),
|
||||||
(Some(x), None) => Some($Op::X(x, content)),
|
(Some(x), None) => Some($Op::x(x, content)),
|
||||||
(None, Some(y)) => Some($Op::y(y, content)),
|
(None, Some(y)) => Some($Op::y(y, content)),
|
||||||
_ => None
|
_ => None
|
||||||
} {
|
} {
|
||||||
//assert_eq!(Content::layout(&op, [x, y, w, h]),
|
assert_eq!(Content::layout(&op, [x, y, w, h]),
|
||||||
//Draw::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_pull, Pull);
|
||||||
test_op_transform!(proptest_op_shrink, Shrink);
|
test_op_transform!(proptest_op_shrink, Shrink);
|
||||||
test_op_transform!(proptest_op_expand, Expand);
|
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! {
|
proptest! {
|
||||||
#[test] fn proptest_op_bsp (
|
#[test] fn proptest_op_bsp (
|
||||||
|
|
@ -121,10 +122,10 @@ proptest! {
|
||||||
h in u16::MIN..u16::MAX,
|
h in u16::MIN..u16::MAX,
|
||||||
) {
|
) {
|
||||||
let bsp = Bsp(d, a, b);
|
let bsp = Bsp(d, a, b);
|
||||||
//assert_eq!(
|
assert_eq!(
|
||||||
//Content::layout(&bsp, [x, y, w, h]),
|
Content::layout(&bsp, [x, y, w, h]),
|
||||||
//Draw::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] {
|
fn area_mut (&mut self) -> &mut [u16;4] {
|
||||||
&mut self.0
|
&mut self.0
|
||||||
}
|
}
|
||||||
fn place_at <T: Draw<Self> + ?Sized> (&mut self, area: [u16;4], _: &T) {
|
fn place <T: Draw<Self> + ?Sized> (&mut self, _: [u16;4], _: &T) {
|
||||||
println!("place_at: {area:?}");
|
|
||||||
()
|
()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,19 +1,43 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub struct Lazy<O, T, F>(F, PhantomData<(O, T)>);
|
/// Lazily-evaluated [Draw]able.
|
||||||
impl<O: Out, T: Content<O>, F: Fn()->T> Lazy<O, T, F> { pub const fn new (thunk: F) -> Self { Self(thunk, PhantomData) } }
|
pub struct Lazy<E, T, F>(PhantomData<(E, T)>, F);
|
||||||
|
impl<E: Out, T: Draw<E> + Layout<E>, F: Fn()->T> Lazy<E, T, F> {
|
||||||
pub struct Thunk<O: Out, F: Fn(&mut O)>(PhantomData<O>, F);
|
pub const fn new (thunk: F) -> Self {
|
||||||
impl<O: Out, F: Fn(&mut O)> Thunk<O, F> { pub const fn new (draw: F) -> Self { Self(PhantomData, draw) } }
|
Self(PhantomData, thunk)
|
||||||
impl<O: Out, F: Fn(&mut O)> Layout<O> for Thunk<O, F> {}
|
}
|
||||||
impl<O: Out, F: Fn(&mut O)> Draw<O> for Thunk<O, F> {
|
}
|
||||||
fn draw (&self, to: &mut O) { (self.1)(to) }
|
impl<E: Out, T: Draw<E> + Layout<E>, F: Fn()->T> Content<E> for Lazy<E, T, F> {
|
||||||
|
fn content (&self) -> impl Draw<E> + Layout<E> + '_ {
|
||||||
|
(self.1)()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Thunk<E: Out, F: Fn(&mut E)>(PhantomData<E>, F);
|
||||||
|
impl<E: Out, F: Fn(&mut E)> Thunk<E, F> {
|
||||||
|
pub const fn new (draw: F) -> Self { Self(PhantomData, draw) }
|
||||||
|
}
|
||||||
|
impl<E: Out, F: Fn(&mut E)> Draw<E> for Thunk<E, F> {
|
||||||
|
fn draw (&self, to: &mut E) { (self.1)(to) }
|
||||||
|
}
|
||||||
|
impl<E: Out, F: Fn(&mut E)> Layout<E> for Thunk<E, F> {
|
||||||
|
fn layout (&self, to: E::Area) -> E::Area { to }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)] pub struct Memo<T, U> {
|
||||||
|
pub value: T,
|
||||||
|
pub view: Arc<RwLock<U>>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)] pub struct Memo<T, U> { pub value: T, pub view: Arc<RwLock<U>> }
|
|
||||||
impl<T: PartialEq, U> Memo<T, U> {
|
impl<T: PartialEq, U> Memo<T, U> {
|
||||||
pub fn new (value: T, view: U) -> Self { Self { value, view: Arc::new(view.into()) } }
|
pub fn new (value: T, view: U) -> Self {
|
||||||
pub fn update <R> (&mut self, newval: T, draw: impl Fn(&mut U, &T, &T)->R) -> Option<R> {
|
Self { value, view: Arc::new(view.into()) }
|
||||||
|
}
|
||||||
|
pub fn update <R> (
|
||||||
|
&mut self,
|
||||||
|
newval: T,
|
||||||
|
draw: impl Fn(&mut U, &T, &T)->R
|
||||||
|
) -> Option<R> {
|
||||||
if newval != self.value {
|
if newval != self.value {
|
||||||
let result = draw(&mut*self.view.write().unwrap(), &newval, &self.value);
|
let result = draw(&mut*self.view.write().unwrap(), &newval, &self.value);
|
||||||
self.value = newval;
|
self.value = newval;
|
||||||
|
|
@ -24,4 +48,6 @@ impl<T: PartialEq, U> Memo<T, U> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear a pre-allocated buffer, then write into it.
|
/// 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)*) } }
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,147 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
use ::tengri_dsl::{Dsl, DslExpr, DslWord, DslNs};
|
|
||||||
|
|
||||||
pub trait View<O, U> {
|
|
||||||
fn view_expr <'a> (&'a self, output: &mut O, expr: &'a impl DslExpr) -> Usually<U> {
|
|
||||||
Err(format!("View::view_expr: no exprs defined: {expr:?}").into())
|
|
||||||
}
|
|
||||||
fn view_word <'a> (&'a self, output: &mut O, word: &'a impl DslWord) -> Usually<U> {
|
|
||||||
Err(format!("View::view_word: no words defined: {word:?}").into())
|
|
||||||
}
|
|
||||||
fn view <'a> (&'a self, output: &mut O, dsl: &'a impl Dsl) -> Usually<U> {
|
|
||||||
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<bool> where
|
|
||||||
S: View<O, ()>
|
|
||||||
+ 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 Perhaps<token>s 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)
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +1,25 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub struct FieldH<Theme, Label, Value>(pub Theme, pub Label, pub Value);
|
pub struct FieldH<Theme, Label, Value>(pub Theme, pub Label, pub Value);
|
||||||
impl<O: Out, T, L: Content<O>, V: Content<O>> HasContent<O> for FieldH<T, L, V> {
|
impl<O: Out, T, L: Draw<O>, V: Draw<O>> Layout<O> for FieldH<T, L, V> where Self: Content<O> {
|
||||||
fn content (&self) -> impl Content<O> { Bsp::e(&self.1, &self.2) }
|
fn layout (&self, to: O::Area) -> O::Area {
|
||||||
|
self.content().layout(to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<O: Out, T, L: Draw<O>, V: Draw<O>> Draw<O> for FieldH<T, L, V> where Self: Content<O> {
|
||||||
|
fn draw (&self, to: &mut O) {
|
||||||
|
self.content().draw(to)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FieldV<Theme, Label, Value>(pub Theme, pub Label, pub Value);
|
pub struct FieldV<Theme, Label, Value>(pub Theme, pub Label, pub Value);
|
||||||
impl<O: Out, T, L: Content<O>, V: Content<O>> HasContent<O> for FieldV<T, L, V> {
|
impl<O: Out, T, L: Draw<O>, V: Draw<O>> Layout<O> for FieldV<T, L, V> where Self: Content<O> {
|
||||||
fn content (&self) -> impl Content<O> { Bsp::s(&self.1, &self.2) }
|
fn layout (&self, to: O::Area) -> O::Area {
|
||||||
|
self.content().layout(to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<O: Out, T, L: Draw<O>, V: Draw<O>> Draw<O> for FieldV<T, L, V> where Self: Content<O> {
|
||||||
|
fn draw (&self, to: &mut O) {
|
||||||
|
self.content().draw(to)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,6 @@ description = "UI metaframework, Ratatui backend."
|
||||||
version = { workspace = true }
|
version = { workspace = true }
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
|
|
||||||
[lib]
|
|
||||||
path = "src/tui.rs"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
dsl = [ "dep:tengri_dsl", "tengri_output/dsl" ]
|
dsl = [ "dep:tengri_dsl", "tengri_output/dsl" ]
|
||||||
bumpalo = [ "dep:bumpalo" ]
|
bumpalo = [ "dep:bumpalo" ]
|
||||||
|
|
|
||||||
1
tui/examples/edn/edn01.edn
Normal file
1
tui/examples/edn/edn01.edn
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
:hello-world
|
||||||
1
tui/examples/edn/edn02.edn
Normal file
1
tui/examples/edn/edn02.edn
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
(bsp/s :hello :world)
|
||||||
1
tui/examples/edn/edn03.edn
Normal file
1
tui/examples/edn/edn03.edn
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
(fill/xy :hello-world)
|
||||||
1
tui/examples/edn/edn04.edn
Normal file
1
tui/examples/edn/edn04.edn
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
(fixed/xy 20 10 :hello-world)
|
||||||
1
tui/examples/edn/edn05.edn
Normal file
1
tui/examples/edn/edn05.edn
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
(bsp/s (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world))
|
||||||
1
tui/examples/edn/edn06.edn
Normal file
1
tui/examples/edn/edn06.edn
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
(bsp/e (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world))
|
||||||
1
tui/examples/edn/edn07.edn
Normal file
1
tui/examples/edn/edn07.edn
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
(bsp/n (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world))
|
||||||
1
tui/examples/edn/edn08.edn
Normal file
1
tui/examples/edn/edn08.edn
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
(bsp/w (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world))
|
||||||
1
tui/examples/edn/edn09.edn
Normal file
1
tui/examples/edn/edn09.edn
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
(bsp/a (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world))
|
||||||
1
tui/examples/edn/edn10.edn
Normal file
1
tui/examples/edn/edn10.edn
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
(bsp/b (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world))
|
||||||
11
tui/examples/edn/edn11.edn
Normal file
11
tui/examples/edn/edn11.edn
Normal file
|
|
@ -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))))))
|
||||||
11
tui/examples/edn/edn12.edn
Normal file
11
tui/examples/edn/edn12.edn
Normal file
|
|
@ -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))))))
|
||||||
11
tui/examples/edn/edn13.edn
Normal file
11
tui/examples/edn/edn13.edn
Normal file
|
|
@ -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)))))))
|
||||||
1
tui/examples/edn/edn14.edn
Normal file
1
tui/examples/edn/edn14.edn
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
:map-e
|
||||||
1
tui/examples/edn/edn15.edn
Normal file
1
tui/examples/edn/edn15.edn
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
(align/c :map-e)
|
||||||
1
tui/examples/edn/edn16.edn
Normal file
1
tui/examples/edn/edn16.edn
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
:map-s
|
||||||
1
tui/examples/edn/edn17.edn
Normal file
1
tui/examples/edn/edn17.edn
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
(align/c :map-s)
|
||||||
73
tui/examples/edn/edn99.edn
Normal file
73
tui/examples/edn/edn99.edn
Normal file
|
|
@ -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<Engine = Tui> {
|
||||||
|
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(())
|
||||||
|
//})))))
|
||||||
|
//}))
|
||||||
|
}
|
||||||
101
tui/examples/tui.rs
Normal file
101
tui/examples/tui.rs
Normal file
|
|
@ -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<TuiOut>);
|
||||||
|
|
||||||
|
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<Self> {
|
||||||
|
state.0 = (state.0 + 1) % EXAMPLES.len();
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
fn prev (state: &mut Example) -> Perhaps<Self> {
|
||||||
|
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<TuiOut> + 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<TuiOut> + use<'_> {
|
||||||
|
//Tui::bg(Color::Rgb(10, 60, 10), Push::y(2, Align::n(format!("{}", EXAMPLES[self.0])))).boxed()
|
||||||
|
//}
|
||||||
|
//pub fn hello (&self) -> impl Draw<TuiOut> + use<'_> {
|
||||||
|
//Tui::bg(Color::Rgb(10, 100, 10), "Hello").boxed()
|
||||||
|
//}
|
||||||
|
//pub fn world (&self) -> impl Draw<TuiOut> + use<'_> {
|
||||||
|
//Tui::bg(Color::Rgb(100, 10, 10), "world").boxed()
|
||||||
|
//}
|
||||||
|
//pub fn hello_world (&self) -> impl Draw<TuiOut> + use<'_> {
|
||||||
|
//"Hello world!".boxed()
|
||||||
|
//}
|
||||||
|
//pub fn map_e (&self) -> impl Draw<TuiOut> + use<'_> {
|
||||||
|
//Map::east(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed()
|
||||||
|
//}
|
||||||
|
//pub fn map_s (&self) -> impl Draw<TuiOut> + use<'_> {
|
||||||
|
//Map::south(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed()
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
@ -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<TuiOut>);
|
|
||||||
|
|
||||||
handle!(TuiIn: |self: Example, input|Ok(None));
|
|
||||||
enum ExampleCommand { Next, Prev }
|
|
||||||
impl ExampleCommand {
|
|
||||||
fn eval (&self, state: &mut Example) -> Perhaps<Self> {
|
|
||||||
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<TuiOut> for Example {
|
|
||||||
fn content (&self) -> impl Draw<TuiOut> {
|
|
||||||
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<TuiOut, ()> 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<RwLock<Self>> {
|
|
||||||
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)))))))
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
@ -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<TuiOut> + 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<TuiOut> + use<'_> {
|
|
||||||
//Tui::bg(Color::Rgb(10, 60, 10), Push::y(2, Align::n(format!("{}", VIEWS[self.0])))).boxed()
|
|
||||||
//}
|
|
||||||
//pub fn hello (&self) -> impl Draw<TuiOut> + use<'_> {
|
|
||||||
//Tui::bg(Color::Rgb(10, 100, 10), "Hello").boxed()
|
|
||||||
//}
|
|
||||||
//pub fn world (&self) -> impl Draw<TuiOut> + use<'_> {
|
|
||||||
//Tui::bg(Color::Rgb(100, 10, 10), "world").boxed()
|
|
||||||
//}
|
|
||||||
//pub fn hello_world (&self) -> impl Draw<TuiOut> + use<'_> {
|
|
||||||
//"Hello world!".boxed()
|
|
||||||
//}
|
|
||||||
//pub fn map_e (&self) -> impl Draw<TuiOut> + use<'_> {
|
|
||||||
//Map::east(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed()
|
|
||||||
//}
|
|
||||||
//pub fn map_s (&self) -> impl Draw<TuiOut> + use<'_> {
|
|
||||||
//Map::south(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed()
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
|
|
||||||
//fn content (&self) -> dyn Draw<Engine = Tui> {
|
|
||||||
//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(())
|
|
||||||
////})))))
|
|
||||||
////}))
|
|
||||||
//}
|
|
||||||
59
tui/src/lib.rs
Normal file
59
tui/src/lib.rs
Normal file
|
|
@ -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<TuiOut> for TestComponent {
|
||||||
|
fn content (&self) -> impl Draw<TuiOut> {
|
||||||
|
Some(self.0.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Handle<TuiIn> for TestComponent {
|
||||||
|
fn handle (&mut self, _from: &TuiIn) -> Perhaps<bool> {
|
||||||
|
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 ));
|
||||||
|
}
|
||||||
|
|
@ -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<bool> where
|
|
||||||
S: View<TuiOut, ()>
|
|
||||||
+ 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::<Color>::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::<Color>::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)
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +1,42 @@
|
||||||
#[allow(unused)] use crate::*;
|
#[allow(unused)] use crate::*;
|
||||||
|
|
||||||
impl Tui {
|
impl Tui {
|
||||||
pub const fn fg <T> (color: Color, w: T) -> Foreground<Color, T> { Foreground(color, w) }
|
pub const fn fg <T> (color: Color, w: T) -> TuiForeground<T> {
|
||||||
pub const fn bg <T> (color: Color, w: T) -> Background<Color, T> { Background(color, w) }
|
TuiForeground(Foreground(color, w))
|
||||||
pub const fn fg_bg <T> (fg: Color, bg: Color, w: T) -> Background<Color, Foreground<Color, T>> { Background(bg, Foreground(fg, w)) }
|
|
||||||
pub const fn modify <T> (enable: bool, modifier: Modifier, w: T) -> Modify<T> { Modify(enable, modifier, w) }
|
|
||||||
pub const fn bold <T> (enable: bool, w: T) -> Modify<T> { Self::modify(enable, Modifier::BOLD, w) }
|
|
||||||
pub const fn border <S, T> (enable: bool, style: S, w: T) -> Bordered<S, T> { Bordered(enable, style, w) }
|
|
||||||
}
|
}
|
||||||
|
pub const fn bg <T> (color: Color, w: T) -> TuiBackground<T> {
|
||||||
|
TuiBackground(Background(color, w))
|
||||||
|
}
|
||||||
|
pub const fn fg_bg <T> (fg: Color, bg: Color, w: T) -> TuiBackground<TuiForeground<T>> {
|
||||||
|
TuiBackground(Background(bg, TuiForeground(Foreground(fg, w))))
|
||||||
|
}
|
||||||
|
pub const fn modify <T> (enable: bool, modifier: Modifier, w: T) -> Modify<T> {
|
||||||
|
Modify(enable, modifier, w)
|
||||||
|
}
|
||||||
|
pub const fn bold <T> (enable: bool, w: T) -> Modify<T> {
|
||||||
|
Self::modify(enable, Modifier::BOLD, w)
|
||||||
|
}
|
||||||
|
pub const fn border <S, T> (enable: bool, style: S, w: T) -> Bordered<S, T> {
|
||||||
|
Bordered(enable, style, w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export] macro_rules! tui_layout ((|$self:ident:$Self:ty, $to:ident|$expr:expr)=>{
|
||||||
|
impl Layout<TuiOut> 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<TuiOut> for $Self {
|
||||||
|
fn draw (&$self, $to: &mut TuiOut) {
|
||||||
|
$expr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
mod tui_border; pub use self::tui_border::*;
|
mod tui_border; pub use self::tui_border::*;
|
||||||
mod tui_button; pub use self::tui_button::*;
|
mod tui_button; pub use self::tui_button::*;
|
||||||
mod tui_color; pub use self::tui_color::*;
|
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_repeat; pub use self::tui_repeat::*;
|
||||||
mod tui_scroll; pub use self::tui_scroll::*;
|
mod tui_scroll; pub use self::tui_scroll::*;
|
||||||
mod tui_string; pub use self::tui_string::*;
|
mod tui_string; pub use self::tui_string::*;
|
||||||
|
|
||||||
mod tui_number; //pub use self::tui_number::*;
|
mod tui_number; //pub use self::tui_number::*;
|
||||||
mod tui_tryptich; //pub use self::tui_tryptich::*;
|
mod tui_tryptich; //pub use self::tui_tryptich::*;
|
||||||
|
|
||||||
|
pub struct TuiForeground<T>(pub(crate) Foreground<Color, T>);
|
||||||
|
pub struct TuiBackground<T>(pub(crate) Background<Color, T>);
|
||||||
pub struct Modify<T>(pub bool, pub Modifier, pub T);
|
pub struct Modify<T>(pub bool, pub Modifier, pub T);
|
||||||
pub struct Styled<T>(pub Option<Style>, pub T);
|
pub struct Styled<T>(pub Option<Style>, pub T);
|
||||||
impl<T: Draw<TuiOut>> Draw<TuiOut> for Foreground<Color, T> {
|
|
||||||
|
impl<T: Layout<TuiOut>> Layout<TuiOut> for TuiForeground<T> {
|
||||||
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
|
self.0.layout(to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: Layout<TuiOut> + Draw<TuiOut>> Draw<TuiOut> for TuiForeground<T> {
|
||||||
fn draw (&self, to: &mut TuiOut) {
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
let area = self.layout(to.area());
|
let area = self.layout(to.area());
|
||||||
to.fill_fg(area, self.0.0);
|
to.fill_fg(area, self.0.0);
|
||||||
to.place_at(area, &self.0.1);
|
to.place_at(area, &self.0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: Draw<TuiOut>> Draw<TuiOut> for Background<Color, T> {
|
|
||||||
|
impl<T: Layout<TuiOut>> Layout<TuiOut> for TuiBackground<T> {
|
||||||
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
|
self.0.layout(to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: Layout<TuiOut> + Draw<TuiOut>> Draw<TuiOut> for TuiBackground<T> {
|
||||||
fn draw (&self, to: &mut TuiOut) {
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
let area = self.layout(to.area());
|
let area = self.layout(to.area());
|
||||||
to.fill_bg(area, self.0.0);
|
to.fill_bg(area, self.0.0);
|
||||||
to.place_at(area, &self.0.1);
|
to.place_at(area, &self.0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: Draw<TuiOut>> Layout<TuiOut> for Modify<T> {}
|
|
||||||
|
impl<T: Layout<TuiOut>> Layout<TuiOut> for Modify<T> {
|
||||||
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
|
self.2.layout(to)
|
||||||
|
}
|
||||||
|
}
|
||||||
impl<T: Draw<TuiOut>> Draw<TuiOut> for Modify<T> {
|
impl<T: Draw<TuiOut>> Draw<TuiOut> for Modify<T> {
|
||||||
fn draw (&self, to: &mut TuiOut) {
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
to.fill_mod(to.area(), self.0, self.1);
|
to.fill_mod(to.area(), self.0, self.1);
|
||||||
self.2.draw(to)
|
self.2.draw(to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: Draw<TuiOut>> Layout<TuiOut> for Styled<T> {}
|
|
||||||
impl<T: Draw<TuiOut>> Draw<TuiOut> for Styled<T> {
|
impl<T: Layout<TuiOut>> Layout<TuiOut> for Styled<T> {
|
||||||
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
|
self.1.layout(to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: Layout<TuiOut> + Draw<TuiOut>> Draw<TuiOut> for Styled<T> {
|
||||||
fn draw (&self, to: &mut TuiOut) {
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
to.place(&self.1);
|
to.place(&self.1);
|
||||||
// TODO write style over area
|
// TODO write style over area
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//impl<T: Draw<TuiOut>> Content<TuiOut> for Result<T, Box<dyn std::error::Error>> {
|
//impl<T: Draw<TuiOut> + Layout<TuiOut>> Content<TuiOut> for Result<T, Box<dyn std::error::Error>> {
|
||||||
//fn content (&self) -> impl Draw<TuiOut> + '_ {
|
//fn content (&self) -> impl Draw<TuiOut> + Layout<TuiOut> + '_ {
|
||||||
//Bsp::a(self.as_ref().ok(), self.as_ref().err().map(
|
//Bsp::a(self.as_ref().ok(), self.as_ref().err().map(
|
||||||
//|e|Tui::fg_bg(Color::Rgb(255,255,255), Color::Rgb(32,32,32), e.to_string())
|
//|e|Tui::fg_bg(Color::Rgb(255,255,255), Color::Rgb(32,32,32), e.to_string())
|
||||||
//))
|
//))
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,47 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
impl<S: BorderStyle, W: Content<TuiOut>> HasContent<TuiOut> for Bordered<S, W> {
|
impl<S: BorderStyle, W: Draw<TuiOut> + Layout<TuiOut>> Content<TuiOut> for Bordered<S, W> {
|
||||||
fn content (&self) -> impl Content<TuiOut> {
|
fn content (&self) -> impl Draw<TuiOut> + Layout<TuiOut> + '_ {
|
||||||
Fill::XY(lay!( When::new(self.0, Border(self.0, self.1)), Pad::XY(1, 1, &self.2) ))
|
Fill::xy(lay!(
|
||||||
|
When::new(self.0, Border(self.0, self.1)),
|
||||||
|
Padding::xy(1, 1, &self.2)
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: BorderStyle> Draw<TuiOut> for Border<S> {
|
impl<S: BorderStyle> Draw<TuiOut> for Border<S> {
|
||||||
fn draw (&self, to: &mut TuiOut) {
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
let Border(enabled, style) = self.0;
|
if self.0 {
|
||||||
if enabled {
|
|
||||||
let area = to.area();
|
let area = to.area();
|
||||||
if area.w() > 0 && area.y() > 0 {
|
if area.w() > 0 && area.y() > 0 {
|
||||||
to.blit(&style.border_nw(), area.x(), area.y(), style.style());
|
to.blit(&self.1.nw(), area.x(), area.y(), self.1.style());
|
||||||
to.blit(&style.border_ne(), area.x() + area.w() - 1, area.y(), style.style());
|
to.blit(&self.1.ne(), area.x() + area.w() - 1, area.y(), self.1.style());
|
||||||
to.blit(&style.border_sw(), area.x(), area.y() + area.h() - 1, style.style());
|
to.blit(&self.1.sw(), area.x(), area.y() + area.h() - 1, self.1.style());
|
||||||
to.blit(&style.border_se(), area.x() + area.w() - 1, area.y() + area.h() - 1, style.style());
|
to.blit(&self.1.se(), area.x() + area.w() - 1, area.y() + area.h() - 1, self.1.style());
|
||||||
for x in area.x()+1..area.x()+area.w()-1 {
|
for x in area.x()+1..area.x()+area.w()-1 {
|
||||||
to.blit(&style.border_n(), x, area.y(), style.style());
|
to.blit(&self.1.n(), x, area.y(), self.1.style());
|
||||||
to.blit(&style.border_s(), x, area.y() + area.h() - 1, style.style());
|
to.blit(&self.1.s(), x, area.y() + area.h() - 1, self.1.style());
|
||||||
}
|
}
|
||||||
for y in area.y()+1..area.y()+area.h()-1 {
|
for y in area.y()+1..area.y()+area.h()-1 {
|
||||||
to.blit(&style.border_w(), area.x(), y, style.style());
|
to.blit(&self.1.w(), area.x(), y, self.1.style());
|
||||||
to.blit(&style.border_e(), area.x() + area.w() - 1, y, style.style());
|
to.blit(&self.1.e(), area.x() + area.w() - 1, y, self.1.style());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait BorderStyle: Content<TuiOut> + Copy {
|
pub trait BorderStyle: Draw<TuiOut> + Layout<TuiOut> + Copy {
|
||||||
fn enabled (&self) -> bool;
|
fn enabled (&self) -> bool;
|
||||||
fn enclose (self, w: impl Draw<TuiOut>) -> impl Draw<TuiOut> {
|
fn enclose (self, w: impl Draw<TuiOut> + Layout<TuiOut>) -> impl Draw<TuiOut> + Layout<TuiOut> {
|
||||||
Bsp::b(Fill::XY(Border(self.enabled(), self)), w)
|
Bsp::b(Fill::xy(Border(self.enabled(), self)), w)
|
||||||
}
|
}
|
||||||
fn enclose2 (self, w: impl Draw<TuiOut>) -> impl Draw<TuiOut> {
|
fn enclose2 (self, w: impl Draw<TuiOut> + Layout<TuiOut>) -> impl Draw<TuiOut> + Layout<TuiOut> {
|
||||||
Bsp::b(Pad::XY(1, 1, Fill::XY(Border(self.enabled(), self))), w)
|
Bsp::b(Margin::xy(1, 1, Fill::xy(Border(self.enabled(), self))), w)
|
||||||
}
|
}
|
||||||
fn enclose_bg (self, w: impl Draw<TuiOut>) -> impl Draw<TuiOut> {
|
fn enclose_bg (self, w: impl Draw<TuiOut> + Layout<TuiOut>) -> impl Draw<TuiOut> + Layout<TuiOut> {
|
||||||
Tui::bg(self.style().unwrap().bg.unwrap_or(Color::Reset),
|
Tui::bg(self.style().unwrap().bg.unwrap_or(Color::Reset),
|
||||||
Bsp::b(Fill::XY(Border(self.enabled(), self)), w))
|
Bsp::b(Fill::xy(Border(self.enabled(), self)), w))
|
||||||
}
|
}
|
||||||
const NW: &'static str = "";
|
const NW: &'static str = "";
|
||||||
const N: &'static str = "";
|
const N: &'static str = "";
|
||||||
|
|
@ -55,14 +57,14 @@ pub trait BorderStyle: Content<TuiOut> + Copy {
|
||||||
const W0: &'static str = "";
|
const W0: &'static str = "";
|
||||||
const E0: &'static str = "";
|
const E0: &'static str = "";
|
||||||
|
|
||||||
fn border_n (&self) -> &str { Self::N }
|
fn n (&self) -> &str { Self::N }
|
||||||
fn border_s (&self) -> &str { Self::S }
|
fn s (&self) -> &str { Self::S }
|
||||||
fn border_e (&self) -> &str { Self::E }
|
fn e (&self) -> &str { Self::E }
|
||||||
fn border_w (&self) -> &str { Self::W }
|
fn w (&self) -> &str { Self::W }
|
||||||
fn border_nw (&self) -> &str { Self::NW }
|
fn nw (&self) -> &str { Self::NW }
|
||||||
fn border_ne (&self) -> &str { Self::NE }
|
fn ne (&self) -> &str { Self::NE }
|
||||||
fn border_sw (&self) -> &str { Self::SW }
|
fn sw (&self) -> &str { Self::SW }
|
||||||
fn border_se (&self) -> &str { Self::SE }
|
fn se (&self) -> &str { Self::SE }
|
||||||
#[inline] fn draw <'a> (
|
#[inline] fn draw <'a> (
|
||||||
&self, to: &mut TuiOut
|
&self, to: &mut TuiOut
|
||||||
) -> Usually<()> {
|
) -> Usually<()> {
|
||||||
|
|
@ -141,6 +143,7 @@ macro_rules! border {
|
||||||
fn enabled (&self) -> bool { self.0 }
|
fn enabled (&self) -> bool { self.0 }
|
||||||
}
|
}
|
||||||
#[derive(Copy, Clone)] pub struct $T(pub bool, pub Style);
|
#[derive(Copy, Clone)] pub struct $T(pub bool, pub Style);
|
||||||
|
impl Layout<TuiOut> for $T {}
|
||||||
impl Draw<TuiOut> for $T {
|
impl Draw<TuiOut> for $T {
|
||||||
fn draw (&self, to: &mut TuiOut) {
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
if self.enabled() { let _ = BorderStyle::draw(self, to); }
|
if self.enabled() { let _ = BorderStyle::draw(self, to); }
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,26 @@
|
||||||
use crate::{*, Color::*};
|
use crate::{*, Color::*};
|
||||||
|
|
||||||
pub fn button_2 <'a> (
|
pub fn button_2 <'a> (
|
||||||
key: impl Draw<TuiOut> + 'a,
|
key: impl Draw<TuiOut> + Layout<TuiOut> + 'a,
|
||||||
label: impl Draw<TuiOut> + 'a,
|
label: impl Draw<TuiOut> + Layout<TuiOut> + 'a,
|
||||||
editing: bool,
|
editing: bool,
|
||||||
) -> impl Draw<TuiOut> + 'a {
|
) -> impl Draw<TuiOut> + Layout<TuiOut> + 'a {
|
||||||
let key = Tui::fg_bg(Tui::orange(), Tui::g(0), Bsp::e(
|
let key = Tui::fg_bg(Tui::orange(), Tui::g(0), Bsp::e(
|
||||||
Tui::fg(Tui::g(0), &"▐"),
|
Tui::fg(Tui::g(0), "▐"),
|
||||||
Bsp::e(key, Tui::fg(Tui::g(96), &"▐"))
|
Bsp::e(key, Tui::fg(Tui::g(96), "▐"))
|
||||||
));
|
));
|
||||||
let label = When::new(!editing, Tui::fg_bg(Tui::g(255), Tui::g(96), label));
|
let label = When::new(!editing, Tui::fg_bg(Tui::g(255), Tui::g(96), label));
|
||||||
Tui::bold(true, Bsp::e(key, label))
|
Tui::bold(true, Bsp::e(key, label))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn button_3 <'a> (
|
pub fn button_3 <'a> (
|
||||||
key: impl Draw<TuiOut> + 'a,
|
key: impl Draw<TuiOut> + Layout<TuiOut> + 'a,
|
||||||
label: impl Draw<TuiOut> + 'a,
|
label: impl Draw<TuiOut> + Layout<TuiOut> + 'a,
|
||||||
value: impl Draw<TuiOut> + 'a,
|
value: impl Draw<TuiOut> + Layout<TuiOut> + 'a,
|
||||||
editing: bool,
|
editing: bool,
|
||||||
) -> impl Draw<TuiOut> + 'a {
|
) -> impl Draw<TuiOut> + Layout<TuiOut> + 'a {
|
||||||
let key = Tui::fg_bg(Tui::orange(), Tui::g(0),
|
let key = Tui::fg_bg(Tui::orange(), Tui::g(0),
|
||||||
Bsp::e(Tui::fg(Tui::g(0), &"▐"), Bsp::e(key, Tui::fg(if editing {
|
Bsp::e(Tui::fg(Tui::g(0), "▐"), Bsp::e(key, Tui::fg(if editing {
|
||||||
Tui::g(128)
|
Tui::g(128)
|
||||||
} else {
|
} else {
|
||||||
Tui::g(96)
|
Tui::g(96)
|
||||||
|
|
@ -28,11 +28,11 @@ pub fn button_3 <'a> (
|
||||||
let label = Bsp::e(
|
let label = Bsp::e(
|
||||||
When::new(!editing, Bsp::e(
|
When::new(!editing, Bsp::e(
|
||||||
Tui::fg_bg(Tui::g(255), Tui::g(96), label),
|
Tui::fg_bg(Tui::g(255), Tui::g(96), label),
|
||||||
Tui::fg_bg(Tui::g(128), Tui::g(96), &"▐"),
|
Tui::fg_bg(Tui::g(128), Tui::g(96), "▐"),
|
||||||
)),
|
)),
|
||||||
Bsp::e(
|
Bsp::e(
|
||||||
Tui::fg_bg(Tui::g(224), Tui::g(128), value),
|
Tui::fg_bg(Tui::g(224), Tui::g(128), value),
|
||||||
Tui::fg_bg(Tui::g(128), Reset, &"▌"),
|
Tui::fg_bg(Tui::g(128), Reset, "▌"),
|
||||||
));
|
));
|
||||||
Tui::bold(true, Bsp::e(key, label))
|
Tui::bold(true, Bsp::e(key, label))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,37 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
impl<
|
||||||
|
Label: Draw<TuiOut> + Layout<TuiOut>,
|
||||||
|
Value: Draw<TuiOut> + Layout<TuiOut>
|
||||||
|
> Content<TuiOut> for FieldH<ItemTheme, Label, Value> {
|
||||||
|
fn content (&self) -> impl Draw<TuiOut> + Layout<TuiOut> + '_ {
|
||||||
|
let Self(ItemTheme { darkest, dark, lightest, .. }, title, value) = self;
|
||||||
|
row!(
|
||||||
|
Tui::fg_bg(dark.rgb, darkest.rgb, "▐"),
|
||||||
|
Tui::fg_bg(lightest.rgb, dark.rgb, title),
|
||||||
|
Tui::fg_bg(dark.rgb, darkest.rgb, "▌"),
|
||||||
|
Tui::fg_bg(lightest.rgb, darkest.rgb, Tui::bold(true, value)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
Label: Draw<TuiOut> + Layout<TuiOut>,
|
||||||
|
Value: Draw<TuiOut> + Layout<TuiOut>
|
||||||
|
> Content<TuiOut> for FieldV<ItemTheme, Label, Value> {
|
||||||
|
fn content (&self) -> impl Draw<TuiOut> + Layout<TuiOut> + '_ {
|
||||||
|
let Self(ItemTheme { darkest, dark, lightest, .. }, title, value) = self;
|
||||||
|
Bsp::n(
|
||||||
|
Align::w(Tui::bg(darkest.rgb, Tui::fg(lightest.rgb, Tui::bold(true, value)))),
|
||||||
|
Fill::x(Align::w(row!(
|
||||||
|
Tui::bg(darkest.rgb, Tui::fg(dark.rgb, "▐")),
|
||||||
|
Tui::bg(dark.rgb, Tui::fg(lightest.rgb, title)),
|
||||||
|
Tui::bg(darkest.rgb, Tui::fg(dark.rgb, "▌")),
|
||||||
|
)))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
pub struct Field<T, U> {
|
pub struct Field<T, U> {
|
||||||
pub direction: Direction,
|
pub direction: Direction,
|
||||||
|
|
@ -12,8 +44,8 @@ pub struct Field<T, U> {
|
||||||
pub value_bg: Option<ItemColor>,
|
pub value_bg: Option<ItemColor>,
|
||||||
pub value_align: Option<Direction>,
|
pub value_align: Option<Direction>,
|
||||||
}
|
}
|
||||||
impl<T: Content<TuiOut>, U: Content<TuiOut>> HasContent<TuiOut> for Field<T, U> {
|
impl<T: Draw<TuiOut>, U: Draw<TuiOut>> Content<TuiOut> for Field<T, U> {
|
||||||
fn content (&self) -> impl Content<TuiOut> {
|
fn content (&self) -> impl Draw<TuiOut> + Layout<TuiOut> + '_ {
|
||||||
"TODO"
|
"TODO"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,21 +13,21 @@ impl<T> Phat<T> {
|
||||||
pub const LO: &'static str = "▄";
|
pub const LO: &'static str = "▄";
|
||||||
pub const HI: &'static str = "▀";
|
pub const HI: &'static str = "▀";
|
||||||
/// A phat line
|
/// A phat line
|
||||||
pub fn lo (fg: Color, bg: Color) -> impl Content<TuiOut> {
|
pub fn lo (fg: Color, bg: Color) -> impl Draw<TuiOut> + Layout<TuiOut> {
|
||||||
Fixed::Y(1, Tui::fg_bg(fg, bg, RepeatH(Self::LO)))
|
Fixed::y(1, Tui::fg_bg(fg, bg, RepeatH(Self::LO)))
|
||||||
}
|
}
|
||||||
/// A phat line
|
/// A phat line
|
||||||
pub fn hi (fg: Color, bg: Color) -> impl Content<TuiOut> {
|
pub fn hi (fg: Color, bg: Color) -> impl Draw<TuiOut> + Layout<TuiOut> {
|
||||||
Fixed::Y(1, Tui::fg_bg(fg, bg, RepeatH(Self::HI)))
|
Fixed::y(1, Tui::fg_bg(fg, bg, RepeatH(Self::HI)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Content<TuiOut>> HasContent<TuiOut> for Phat<T> {
|
impl<T: Layout<TuiOut> + Draw<TuiOut>> Content<TuiOut> for Phat<T> {
|
||||||
fn content (&self) -> impl Content<TuiOut> {
|
fn content (&self) -> impl Draw<TuiOut> + Layout<TuiOut> + '_ {
|
||||||
let [fg, bg, hi, lo] = self.colors;
|
let [fg, bg, hi, lo] = self.colors;
|
||||||
let top = Fixed::y(1, Self::lo(bg, hi));
|
let top = Fixed::y(1, Self::lo(bg, hi));
|
||||||
let low = Fixed::y(1, Self::hi(bg, lo));
|
let low = Fixed::y(1, Self::hi(bg, lo));
|
||||||
let content = Tui::fg_bg(fg, bg, &self.content);
|
let content = Tui::fg_bg(fg, bg, &self.content);
|
||||||
Min::XY(self.width, self.height, Bsp::s(top, Bsp::n(low, Fill::XY(content))))
|
Min::xy(self.width, self.height, Bsp::s(top, Bsp::n(low, Fill::xy(content))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,11 @@ use crate::*;
|
||||||
use ratatui::prelude::Position;
|
use ratatui::prelude::Position;
|
||||||
|
|
||||||
pub struct Repeat<'a>(pub &'a str);
|
pub struct Repeat<'a>(pub &'a str);
|
||||||
|
impl Layout<TuiOut> for Repeat<'_> {
|
||||||
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
|
to
|
||||||
|
}
|
||||||
|
}
|
||||||
impl Draw<TuiOut> for Repeat<'_> {
|
impl Draw<TuiOut> for Repeat<'_> {
|
||||||
fn draw (&self, to: &mut TuiOut) {
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
let [x, y, w, h] = to.area().xywh();
|
let [x, y, w, h] = to.area().xywh();
|
||||||
|
|
@ -18,6 +23,11 @@ impl Draw<TuiOut> for Repeat<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RepeatV<'a>(pub &'a str);
|
pub struct RepeatV<'a>(pub &'a str);
|
||||||
|
impl Layout<TuiOut> for RepeatV<'_> {
|
||||||
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
|
to
|
||||||
|
}
|
||||||
|
}
|
||||||
impl Draw<TuiOut> for RepeatV<'_> {
|
impl Draw<TuiOut> for RepeatV<'_> {
|
||||||
fn draw (&self, to: &mut TuiOut) {
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
let [x, y, _w, h] = to.area().xywh();
|
let [x, y, _w, h] = to.area().xywh();
|
||||||
|
|
@ -30,6 +40,11 @@ impl Draw<TuiOut> for RepeatV<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RepeatH<'a>(pub &'a str);
|
pub struct RepeatH<'a>(pub &'a str);
|
||||||
|
impl Layout<TuiOut> for RepeatH<'_> {
|
||||||
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
|
to
|
||||||
|
}
|
||||||
|
}
|
||||||
impl Draw<TuiOut> for RepeatH<'_> {
|
impl Draw<TuiOut> for RepeatH<'_> {
|
||||||
fn draw (&self, to: &mut TuiOut) {
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
let [x, y, w, _h] = to.area().xywh();
|
let [x, y, w, _h] = to.area().xywh();
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,18 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use crate::ratatui::prelude::Position;
|
use crate::ratatui::prelude::Position;
|
||||||
use unicode_width::{UnicodeWidthStr, UnicodeWidthChar};
|
use unicode_width::{UnicodeWidthStr, UnicodeWidthChar};
|
||||||
impl Layout<TuiOut> for &str { fn layout (&self, to: [u16;4]) -> [u16;4] { to.center_xy([width_chars_max(to.w(), self), 1]) } }
|
|
||||||
impl Draw<TuiOut> for &str { fn draw (&self, to: &mut TuiOut) { let [x, y, w, ..] = self.layout(to.area()); to.text(&self, x, y, w) } }
|
tui_layout!(|self: &str, to|to.center_xy([width_chars_max(to.w(), self), 1]));
|
||||||
impl Layout<TuiOut> for String { fn layout (&self, to: [u16;4]) -> [u16;4] { self.as_str().layout(to) } }
|
tui_draw!(|self: &str, to|{
|
||||||
impl Draw<TuiOut> for String { fn draw (&self, to: &mut TuiOut) { self.as_str().draw(to) } }
|
let [x, y, w, ..] = self.layout(to.area());
|
||||||
|
to.text(&self, x, y, w)
|
||||||
|
});
|
||||||
|
|
||||||
|
tui_layout!(|self: Arc<str>, to|self.as_ref().layout(to));
|
||||||
|
tui_draw!(|self: Arc<str>, to|self.as_ref().draw(to));
|
||||||
|
|
||||||
|
tui_layout!(|self: String, to|self.as_str().layout(to));
|
||||||
|
tui_draw!(|self: String, to|self.as_str().draw(to));
|
||||||
|
|
||||||
fn width_chars_max (max: u16, text: impl AsRef<str>) -> u16 {
|
fn width_chars_max (max: u16, text: impl AsRef<str>) -> u16 {
|
||||||
let mut width: u16 = 0;
|
let mut width: u16 = 0;
|
||||||
|
|
@ -40,13 +48,19 @@ pub fn trim_string (max_width: usize, input: impl AsRef<str>) -> String {
|
||||||
pub struct TrimString<T: AsRef<str>>(pub u16, pub T);
|
pub struct TrimString<T: AsRef<str>>(pub u16, pub T);
|
||||||
|
|
||||||
impl<'a, T: AsRef<str>> TrimString<T> {
|
impl<'a, T: AsRef<str>> TrimString<T> {
|
||||||
fn as_ref (&self) -> TrimStringRef<'_, T> { TrimStringRef(self.0, &self.1) }
|
fn as_ref (&self) -> TrimStringRef<'_, T> {
|
||||||
|
TrimStringRef(self.0, &self.1)
|
||||||
}
|
}
|
||||||
impl<'a, T: AsRef<str>> Draw<TuiOut> for TrimString<T> {
|
|
||||||
fn draw (&self, to: &mut TuiOut) { Draw::draw(&self.as_ref(), to) }
|
|
||||||
}
|
}
|
||||||
impl<'a, T: AsRef<str>> Layout<TuiOut> for TrimString<T> {
|
impl<'a, T: AsRef<str>> Layout<TuiOut> for TrimString<T> {
|
||||||
fn layout (&self, to: [u16; 4]) -> [u16;4] { Layout::layout(&self.as_ref(), to) }
|
fn layout (&self, to: [u16; 4]) -> [u16;4] {
|
||||||
|
Layout::layout(&self.as_ref(), to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a, T: AsRef<str>> Draw<TuiOut> for TrimString<T> {
|
||||||
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
|
Draw::draw(&self.as_ref(), to)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Displays a borrowed [str]-like with fixed maximum width
|
/// Displays a borrowed [str]-like with fixed maximum width
|
||||||
|
|
@ -54,7 +68,7 @@ impl<'a, T: AsRef<str>> Layout<TuiOut> for TrimString<T> {
|
||||||
/// Width is computed using [unicode_width].
|
/// Width is computed using [unicode_width].
|
||||||
pub struct TrimStringRef<'a, T: AsRef<str>>(pub u16, pub &'a T);
|
pub struct TrimStringRef<'a, T: AsRef<str>>(pub u16, pub &'a T);
|
||||||
|
|
||||||
impl<'a, T: AsRef<str>> Layout<TuiOut> for TrimStringRef<'a, T> {
|
impl<T: AsRef<str>> Layout<TuiOut> for TrimStringRef<'_, T> {
|
||||||
fn layout (&self, to: [u16; 4]) -> [u16;4] {
|
fn layout (&self, to: [u16; 4]) -> [u16;4] {
|
||||||
[to.x(), to.y(), to.w().min(self.0).min(self.1.as_ref().width() as u16), to.h()]
|
[to.x(), to.y(), to.w().min(self.0).min(self.1.as_ref().width() as u16), to.h()]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
A: Content<TuiOut>,
|
A: Draw<TuiOut> + Layout<TuiOut>,
|
||||||
B: Content<TuiOut>,
|
B: Draw<TuiOut> + Layout<TuiOut>,
|
||||||
C: Content<TuiOut>,
|
C: Draw<TuiOut> + Layout<TuiOut>,
|
||||||
> HasContent<TuiOut> for Tryptich<A, B, C> {
|
> Content<TuiOut> for Tryptich<A, B, C> {
|
||||||
fn content (&self) -> impl Content<TuiOut> {
|
fn content (&self) -> impl Draw<TuiOut> + Layout<TuiOut> + '_ {
|
||||||
let Self { top, h, left: (w_a, ref a), middle: (w_b, ref b), right: (w_c, ref c) } = *self;
|
let Self { top, h, left: (w_a, ref a), middle: (w_b, ref b), right: (w_c, ref c) } = *self;
|
||||||
Fixed::y(h, if top {
|
Fixed::y(h, if top {
|
||||||
Bsp::a(
|
Bsp::a(
|
||||||
|
|
@ -17,12 +17,13 @@ impl<
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Bsp::a(
|
Bsp::a(
|
||||||
Fill::XY(Align::c(Fixed::x(w_b, Align::x(Tui::bg(Color::Reset, b))))),
|
Fill::xy(Align::c(Fixed::x(w_b, Align::x(Tui::bg(Color::Reset, b))))),
|
||||||
Bsp::a(
|
Bsp::a(
|
||||||
Fill::XY(Align::w(Fixed::x(w_a, Tui::bg(Color::Reset, a)))),
|
Fill::xy(Align::w(Fixed::x(w_a, Tui::bg(Color::Reset, a)))),
|
||||||
Fill::XY(Align::e(Fixed::x(w_c, Tui::bg(Color::Reset, c)))),
|
Fill::xy(Align::e(Fixed::x(w_c, Tui::bg(Color::Reset, c)))),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,9 @@ impl Out for TuiOut {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Layout<TuiOut> for fn(&mut TuiOut) {}
|
||||||
|
|
||||||
impl TuiOut {
|
impl TuiOut {
|
||||||
/// Spawn the output thread.
|
/// Spawn the output thread.
|
||||||
pub fn run_output <T: Draw<TuiOut> + Send + Sync + 'static> (
|
pub fn run_output <T: Draw<TuiOut> + Send + Sync + 'static> (
|
||||||
|
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
#[test] fn test_tui_engine () -> Usually<()> {
|
|
||||||
//use std::sync::{Arc, RwLock};
|
|
||||||
struct TestComponent(String);
|
|
||||||
impl Content<TuiOut> for TestComponent {
|
|
||||||
fn content (&self) -> impl Draw<TuiOut> {
|
|
||||||
Some(self.0.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Handle<TuiIn> for TestComponent {
|
|
||||||
fn handle (&mut self, _from: &TuiIn) -> Perhaps<bool> {
|
|
||||||
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(())
|
|
||||||
}
|
|
||||||
//#[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 ));
|
|
||||||
//}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue