Compare commits

..

No commits in common. "a9f2fa2cdd5fac6ea0f7aee7e9c9d4acec8eac39" and "95ace837565c65292d86694cb395d9ba7fa834eb" have entirely different histories.

17 changed files with 1096 additions and 1874 deletions

1865
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -6,8 +6,7 @@ description = "UI metaframework."
[features]
default = ["lang", "sing", "midi", "draw", "play", "term", "text", "time", "rand", "okhsl", "eval", "exit"]
prof = ["dep:profiling", "dep:tracing", "dep:tracing-flame", "dep:tracing-subscriber"]
bump = ["dep:bumpalo"]
bumpalo = ["dep:bumpalo"]
draw = []
gui = ["draw", "dep:winit"]
eval = []
@ -26,7 +25,6 @@ term = ["draw", "dep:ratatui", "dep:crossterm"]
anyhow = { version = "1.0" }
atomic_float = { version = "1" }
better-panic = { version = "0.3.0" }
konst = { version = "0.3.16", features = [ "rust_1_83" ] }
bumpalo = { optional = true, version = "3.19.0" }
crossterm = { optional = true, version = "0.29.0" }
@ -36,15 +34,10 @@ midly = { optional = true, version = "0.5" }
palette = { optional = true, version = "0.7.6", features = [ "random" ] }
quanta = { optional = true, version = "0.12.3" }
rand = { optional = true, version = "0.8.5" }
ratatui = { optional = true, version = "^0.30.2", features = [ "unstable-widget-ref", "underline-color" ] }
ratatui = { optional = true, version = "0.29.0", features = [ "unstable-widget-ref", "underline-color" ] }
unicode-width = { optional = true, version = "0.2" }
winit = { optional = true, version = "0.30.4", features = [ "x11" ]}
profiling = { version = "1.0.18", optional = true, features = ["profile-with-tracing"] }
tracing = { version = "0.1", optional = true, default-features = false, features = [ "std" ] }
tracing-subscriber = { version = "0.3", optional = true }
tracing-flame = { version = "0.2", optional = true }
[dev-dependencies]
proptest = { version = "^1" }
proptest-derive = { version = "^0.5.1" }

2
dizzle

@ -1 +1 @@
Subproject commit 648be30918bef0a975bd225c0499911aaa88bc13
Subproject commit ab177b0d0d1ccdff0c69f915847be9a23a364663

View file

@ -45,20 +45,16 @@ impl_keywords!(Tui, XYWH<u16>, State [
kw_tui_bg
]);
impl<'a> Interpret<'a, Tui, Option<XYWH<u16>>> for State {
fn interpret_word <L: Language<'a> + ?Sized> (&'a self, to: &mut Tui, lang: &'a L)
-> Drawn<'a, u16>
{
match lang.src()? {
impl Interpret<Tui, Option<XYWH<u16>>> for State {
fn interpret_word (&self, to: &mut Tui, sym: &impl Language) -> Perhaps<XYWH<u16>> {
match sym.src()? {
Some(":foo") => "foo".draw(to),
Some(":bar") => "bar".draw(to),
Some(":foobar") => "FOOBAR".draw(to),
src => todo!("src: {src:?}")
}
}
fn interpret_expr <L: Language<'a> + ?Sized> (&self, to: &mut Tui, src: &'a L)
-> Drawn<'a, u16>
{
fn interpret_expr (&self, to: &mut Tui, src: &impl Expression) -> Perhaps<XYWH<u16>> {
if let Some(area) = self.keyword(to, src)? {
Ok(Some(area))
} else {

View file

@ -1,44 +1,44 @@
use crate::*;
impl<'a, S: Screen, T: Draw<'a, S>> Layout<'a, S> for T {}
impl<S: Screen, T: Draw<S>> Layout<S> for T {}
pub trait Layout<'a, S: Screen>: Draw<'a, S> + Sized {
fn origin (self, azimuth: impl Into<Option<Azimuth>>) -> impl Draw<'a, S> {
pub trait Layout<S: Screen>: Draw<S> + Sized {
fn origin (self, azimuth: impl Into<Option<Azimuth>>) -> impl Draw<S> {
Origin(azimuth.into(), self)
}
fn origin_c (self) -> impl Draw<'a, S> {
fn origin_c (self) -> impl Draw<S> {
Origin(Some(Azimuth::C), self)
}
fn origin_x (self) -> impl Draw<'a, S> {
fn origin_x (self) -> impl Draw<S> {
Origin(Some(Azimuth::X), self)
}
fn origin_y (self) -> impl Draw<'a, S> {
fn origin_y (self) -> impl Draw<S> {
Origin(Some(Azimuth::Y), self)
}
fn origin_n (self) -> impl Draw<'a, S> {
fn origin_n (self) -> impl Draw<S> {
Origin(Some(Azimuth::N), self)
}
fn origin_s (self) -> impl Draw<'a, S> {
fn origin_s (self) -> impl Draw<S> {
Origin(Some(Azimuth::S), self)
}
fn origin_e (self) -> impl Draw<'a, S> {
fn origin_e (self) -> impl Draw<S> {
Origin(Some(Azimuth::E), self)
}
fn origin_w (self) -> impl Draw<'a, S> {
fn origin_w (self) -> impl Draw<S> {
Origin(Some(Azimuth::W), self)
}
fn origin_ne (self) -> impl Draw<'a, S> {
fn origin_ne (self) -> impl Draw<S> {
Origin(Some(Azimuth::NE), self)
}
fn origin_se (self) -> impl Draw<'a, S> {
fn origin_se (self) -> impl Draw<S> {
Origin(Some(Azimuth::SE), self)
}
fn origin_nw (self) -> impl Draw<'a, S> {
fn origin_nw (self) -> impl Draw<S> {
Origin(Some(Azimuth::NW), self)
}
fn origin_sw (self) -> impl Draw<'a, S> {
fn origin_sw (self) -> impl Draw<S> {
Origin(Some(Azimuth::SW), self)
}
}
@ -76,7 +76,7 @@ impl PartialEq for Sizer {
}
impl Sizer {
pub const fn of <'a, T: Screen> (&self, of: impl Draw<'a, T>) -> impl Draw<'a, T> {
pub const fn of <T: Screen> (&self, of: impl Draw<T>) -> impl Draw<T> {
draw(move|to: &mut T|{
let area = of.draw(to)?;
self.0.store(area.map(|a|a.w()).unwrap_or(T::Unit::zero()).into(), Relaxed);
@ -99,13 +99,70 @@ impl Sizer {
}
}});
/// Some layout operations exist in XY, X, and Y variants that take 3 or 2 arguments.
/// Their handling in [eval_view] is uniform and goes like this:
#[macro_export] macro_rules! eval_xy (
// Valueless variant:
// (fill/x ...)
// (fill/y ...)
// (fill/xy ...)
(
$name:expr => $expr:expr, $head:expr, $output:ident, $state:ident,
$variant:expr, $xy:ident, $x:ident, $y:ident, $arg:expr,
) => {{
// frags.next(): 2nd slash-delimited fragment: /x, /y, /xy
let variant = $variant;
let thunk = draw(move|screen|$state.interpret(screen, &$arg));
match variant {
// X variant
Some("x") => thunk.$x().draw($output),
// Y variant
Some("y") => thunk.$y().draw($output),
// XY variant (can be omitted)
Some("xy") | None => thunk.$xy().draw($output),
// Other namespace members are invalid
frag => invalid_variant($name, frag, $expr, $head)
}
}};
// Variadic variant:
// (push/x n ...)
// (push/y n ...)
// (push/xy n m ...)
(
$name:expr => $expr:expr, $head:expr, $output:ident, $state:ident,
$variant:expr, $xy:ident, $x:ident, $y:ident, $arg0:expr, $arg1:expr, $arg2:expr,
) => {{
// frags.next(): 2nd slash-delimited fragment: /x, /y, /xy
let variant = $variant;
let thunk = draw(move|screen|$state.interpret(screen, &match variant {
Some("x") | Some("y") => $arg1,
Some("xy") | None => $arg2,
_ => panic!("{}: unsupported axis {variant:?}; try /x, /y, /xy", $name)
}));
match variant {
// X variant
Some("x") => thunk.$x($state.namespace($arg0?)?)
.draw($output),
// Y variant
Some("y") => thunk.$y($state.namespace($arg0?)?)
.draw($output),
// XY variant (can be omitted)
Some("xy") | None => thunk.$xy($state.namespace($arg0?)?, $state.namespace($arg1?)?)
.draw($output),
// Other namespace members are invalid
frag => invalid_variant($name, frag, $expr, $head)
}
}};
);
#[macro_export] macro_rules! fn_kw_layout {
($name:ident |$state:ident, $output: ident, $expr:ident| $body:block) => {
#[cfg(all(feature = "draw", feature = "eval"))]
pub fn $name <'a, O: Screen, S> (
$state: &'a S, $output: &mut O, $expr: &'a str
) -> PerhapsRef<'a, XYWH<O::Unit>> where
S: Interpret<'a, O, Option<XYWH<O::Unit>>>
pub fn $name <O: Screen, S> (
$state: &S, $output: &mut O, $expr: &str
) -> Perhaps<XYWH<O::Unit>> where
S: Interpret<O, Option<XYWH<O::Unit>>>
+ Namespace<bool>
+ Namespace<O::Unit>
+ Namespace<Option<O::Unit>>
@ -116,10 +173,10 @@ impl Sizer {
#[macro_export] macro_rules! fn_kw_layout_tui {
($name:ident |$state:ident, $output: ident, $expr:ident| $body:block) => {
#[cfg(all(feature = "draw", feature = "eval", feature = "term"))]
pub fn $name <'a, S> (
$state: &'a S, $output: &mut Tui, $expr: &'a str
) -> PerhapsRef<'a, XYWH<u16>> where
S: Interpret<'a, Tui, Option<XYWH<u16>>>
pub fn $name <S> (
$state: &S, $output: &mut Tui, $expr: &str
) -> Perhaps<XYWH<u16>> where
S: Interpret<Tui, Option<XYWH<u16>>>
+ Namespace<bool>
+ Namespace<u16>
+ Namespace<Option<u16>>
@ -134,9 +191,9 @@ impl Sizer {
$Struct:ident { $X:ident $Y:ident $XY:ident } $kw_name:ident $name:literal
|$self:ident, $to:ident| $body:block
) => {
impl<S: Screen, T: Draw<'a, S>> $Trait<S> for T {}
impl<S: Screen, T: Draw<S>> $Trait<S> for T {}
pub trait $Trait<S: Screen>: Draw<'a, S> + Sized {
pub trait $Trait<S: Screen>: Draw<S> + Sized {
fn $fn_x <N: Into<Option<S::Unit>> + Copy> (self, x: N)
-> $Struct<S, Self, N> { $Struct::$X(self, x) }
fn $fn_y <N: Into<Option<S::Unit>> + Copy> (self, y: N)
@ -145,24 +202,28 @@ impl Sizer {
-> $Struct<S, Self, N> { $Struct::$XY(self, x, y) }
}
pub enum $Struct<S: Screen, I: Draw<'a, S>, X: Into<Option<S::Unit>>> {
pub enum $Struct<S: Screen, I: Draw<S>, X: Into<Option<S::Unit>>> {
__(PhantomData<S>), $X(I, X), $Y(I, X), $XY(I, X, X),
}
impl <S: Screen, I: Draw<'a, S>, X: Into<Option<S::Unit>> + Copy> Draw<'a, S> for $Struct<S, I, X> {
impl <S: Screen, I: Draw<S>, X: Into<Option<S::Unit>> + Copy> Draw<S> for $Struct<S, I, X> {
fn draw (&$self, $to: &mut S) -> Perhaps<XYWH<S::Unit>> { $body }
}
fn_kw_layout!($kw_name |state, output, expr| {
#[cfg(feature = "prof")] profiling::scope!(stringify!(match $kw_name));
let head = expr.head()?;
let mut frags = head.src()?.unwrap_or_default().split("/");
Ok(matches!(frags.next(), Some($name)).then(||{
#[cfg(feature = "prof")] profiling::scope!(stringify!(inner $kw_name));
let args = expr.tail();
let arg0 = args.head();
let tail0 = args.tail();
let arg1 = tail0.head();
let tail1 = tail0.tail();
let arg2 = tail1.head();
eval_xy!(
$name => expr, head, output, state, frags.next(),
$fn_xy, $fn_x, $fn_y,
expr.nth(1), expr.nth(2), expr.nth(3),
arg0, arg1, arg2,
)
}).transpose()?.flatten())
});
@ -175,9 +236,9 @@ impl Sizer {
$Struct:ident { $X:ident $Y:ident $XY:ident } $kw_name:ident $name:literal
|$self:ident, $to:ident| $body:block
) => {
impl<S: Screen, T: Draw<'a, S>> $Trait<S> for T {}
impl<S: Screen, T: Draw<S>> $Trait<S> for T {}
pub trait $Trait<S: Screen>: Draw<'a, S> + Sized {
pub trait $Trait<S: Screen>: Draw<S> + Sized {
fn $fn_x (self) -> $Struct<S, Self> { $Struct::$X(self) }
fn $fn_y (self) -> $Struct<S, Self> { $Struct::$Y(self) }
fn $fn_xy (self) -> $Struct<S, Self> { $Struct::$XY(self) }
@ -187,19 +248,19 @@ impl Sizer {
__(PhantomData<S>), $X(I), $Y(I), $XY(I),
}
impl <'a, S: Screen, I: Draw<'a, S>> Draw<'a, S> for $Struct<S, I> {
impl <S: Screen, I: Draw<S>> Draw<S> for $Struct<S, I> {
fn draw (&$self, $to: &mut S) -> Perhaps<XYWH<S::Unit>> { $body }
}
fn_kw_layout!($kw_name |state, output, expr| {
#[cfg(feature = "prof")] profiling::scope!(stringify!(match $kw_name));
let head = expr.head()?;
let mut frags = head.src()?.unwrap_or_default().split("/");
Ok(matches!(frags.next(), Some($name)).then(||{
#[cfg(feature = "prof")] profiling::scope!(stringify!(inner $kw_name));
let args = expr.tail();
let arg0 = args.head();
eval_xy!(
$name => expr, head, output, state, frags.next(),
$fn_xy, $fn_x, $fn_y, expr.nth(1),
$fn_xy, $fn_x, $fn_y, arg0,
)
}).transpose()?.flatten())
});

View file

@ -6,19 +6,17 @@ use crate::*;
/// let _ = area(XYWH(1, 2, 3, 4), "southeast");
/// let _ = area(Some(XYWH(1, 2, 3, 4)), "southeast");
/// ```
pub fn area <'a, S: Screen, T: Draw<'a, S>, U: Into<Option<XYWH<S::Unit>>>> (
pub fn area <S: Screen, T: Draw<S>, U: Into<Option<XYWH<S::Unit>>>> (
origin: U, it: T
) -> Area<S, T> {
Area(origin.into(), it)
}
pub struct Area<S: Screen, T>(
pub struct Area<S: Screen, T: Draw<S>>(
pub Option<XYWH<S::Unit>>,
pub T,
pub T
);
impl<'a, S: Screen, T: Draw<'a, S>> Draw<'a, S> for Area<S, T> {
fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit> {
to.draw(self.0, &self.1)
}
}
impl_draw!(<S: Screen, T: Draw<S>,>|self: Area<S, T>, to: S|{
to.draw(self.0, &self.1)
});

View file

@ -1,296 +1,8 @@
use crate::*;
pub enum Full<'a, S: Screen, I: Draw<'a, S>> {
__(PhantomData<&'a S>), W(I), H(I), WH(I),
}
pub enum Exact<'a, S: Screen, I: Draw<'a, S>, X: Into<Option<S::Unit>>> {
__(PhantomData<&'a S>), W(I, X), H(I, X), WH(I, X, X),
}
pub enum Push<'a, S: Screen, I: Draw<'a, S>, X: Into<Option<S::Unit>>> {
__(PhantomData<&'a S>), X(I, X), Y(I, X), XY(I, X, X),
}
pub enum Pull<'a, S: Screen, I: Draw<'a, S>, X: Into<Option<S::Unit>>> {
__(PhantomData<&'a S>), X(I, X), Y(I, X), XY(I, X, X),
}
pub enum Min<'a, S: Screen, I: Draw<'a, S>, X: Into<Option<S::Unit>>> {
__(PhantomData<&'a S>), W(I, X), H(I, X), WH(I, X, X),
}
pub enum Max<'a, S: Screen, I: Draw<'a, S>, X: Into<Option<S::Unit>>> {
__(PhantomData<&'a S>), W(I, X), H(I, X), WH(I, X, X),
}
pub enum Pad<'a, S: Screen, I: Draw<'a, S>, X: Into<Option<S::Unit>>> {
__(PhantomData<&'a S>), X(I, X), Y(I, X), XY(I, X, X),
}
/// ```
/// # fn test_layout_full () -> Usually<()> {
/// assert_eq!(check_layout("1")?, Some(XYWH(1u16, 1, 1, 1)));
/// assert_eq!(check_layout("1".full_w())?, Some(XYWH(1u16, 1, 80, 1)));
/// assert_eq!(check_layout("1".full_h())?, Some(XYWH(1u16, 1, 1, 25)));
/// assert_eq!(check_layout("1".full_wh())?, Some(XYWH(1u16, 1, 80, 25)));
/// # Ok(()) }}
/// ```
pub trait LayoutFull<'a, S: Screen>: Draw<'a, S> + Sized {
fn full_w (self) -> Full<'a, S, Self> { Full::W(self) }
fn full_h (self) -> Full<'a, S, Self> { Full::H(self) }
fn full_wh (self) -> Full<'a, S, Self> { Full::WH(self) }
}
/// ```
/// # fn test_layout_exact () -> Usually<()> {
/// assert_eq!(check_layout("FOOBAR\nKILROY")?, Some(XYWH(1, 1, 6, 2)));
/// assert_eq!(check_layout("FOOBAR\nKILROY".exact_w(3))?, Some(XYWH(1, 1, 3, 2)));
/// assert_eq!(check_layout("FOOBAR\nKILROY".exact_h(1))?, Some(XYWH(1, 1, 6, 1)));
/// assert_eq!(check_layout("FOOBAR\nKILROY".exact_wh(2, 1))?, Some(XYWH(1, 1, 2, 1)));
/// # Ok(()) }}
/// ```
pub trait LayoutExact<'a, S: Screen>: Draw<'a, S> + Sized {
fn exact_w <X: Into<Option<S::Unit>> + Copy> (self, w: X)
-> Exact<'a, S, Self, X> { Exact::W(self, w) }
fn exact_h <X: Into<Option<S::Unit>> + Copy> (self, h: X)
-> Exact<'a, S, Self, X> { Exact::H(self, h) }
fn exact_wh <X: Into<Option<S::Unit>> + Copy> (self, w: X, h: X)
-> Exact<'a, S, Self, X> { Exact::WH(self, w, h) }
}
/// ```
/// # fn test_layout_min () -> Usually<()> {
/// assert_eq!(check_layout("1".min_w(5))?, Some(XYWH(1u16, 1, 5, 1)));
/// assert_eq!(check_layout("1".min_h(5))?, Some(XYWH(1u16, 1, 1, 5)));
/// assert_eq!(check_layout("1".min_wh(5, 5))?, Some(XYWH(1u16, 1, 5, 5)));
/// assert_eq!(check_layout("123456".min_w(5))?, Some(XYWH(1u16, 1, 6, 1)));
/// # Ok(()) }}
/// ```
pub trait LayoutMin<'a, S: Screen>: Draw<'a, S> + Sized {
fn min_w <X: Into<Option<S::Unit>> + Copy> (self, w: X)
-> Min<'a, S, Self, X> { Min::W(self, w) }
fn min_h <X: Into<Option<S::Unit>> + Copy> (self, h: X)
-> Min<'a, S, Self, X> { Min::H(self, h) }
fn min_wh <X: Into<Option<S::Unit>> + Copy> (self, w: X, h: X)
-> Min<'a, S, Self, X> { Min::WH(self, w, h) }
}
pub trait LayoutMax<'a, S: Screen>: Draw<'a, S> + Sized {
fn max_w <X: Into<Option<S::Unit>> + Copy> (self, w: X)
-> Max<'a, S, Self, X> { Max::W(self, w) }
fn max_h <X: Into<Option<S::Unit>> + Copy> (self, h: X)
-> Max<'a, S, Self, X> { Max::H(self, h) }
fn max_wh <X: Into<Option<S::Unit>> + Copy> (self, w: X, h: X)
-> Max<'a, S, Self, X> { Max::WH(self, w, h) }
}
pub trait LayoutPad<'a, S: Screen>: Draw<'a, S> + Sized {
fn pad_w <X: Into<Option<S::Unit>> + Copy> (self, w: X)
-> Pad<'a, S, Self, X> { Pad::X(self, w) }
fn pad_h <X: Into<Option<S::Unit>> + Copy> (self, h: X)
-> Pad<'a, S, Self, X> { Pad::Y(self, h) }
fn pad_wh <X: Into<Option<S::Unit>> + Copy> (self, w: X, h: X)
-> Pad<'a, S, Self, X> { Pad::XY(self, w, h) }
}
/// ```
/// # fn test_layout_pull () -> Usually<()> {
/// assert_eq!(check_layout("1")?, Some(XYWH(1u16, 1, 1, 1)));
/// assert_eq!(check_layout("1".push_x(1))?, Some(XYWH(2u16, 1, 1, 1)));
/// assert_eq!(check_layout("1".push_y(1))?, Some(XYWH(1u16, 2, 1, 1)));
/// assert_eq!(check_layout("1".push_xy(1, 1))?, Some(XYWH(2u16, 2, 1, 1)));
/// # Ok(()) }
/// ```
pub trait LayoutPush<'a, S: Screen>: Draw<'a, S> + Sized {
fn push_x <X: Into<Option<S::Unit>> + Copy> (self, w: X)
-> Push<'a, S, Self, X> { Push::X(self, w) }
fn push_y <X: Into<Option<S::Unit>> + Copy> (self, h: X)
-> Push<'a, S, Self, X> { Push::Y(self, h) }
fn push_xy <X: Into<Option<S::Unit>> + Copy> (self, w: X, h: X)
-> Push<'a, S, Self, X> { Push::XY(self, w, h) }
}
/// ```
/// # fn test_layout_push () -> Usually<()> {
/// assert_eq!(check_layout("1")?, Some(XYWH(1u16, 1, 1, 1)));
/// assert_eq!(check_layout("1".pull_x(1))?, Some(XYWH(0u16, 1, 1, 1)));
/// assert_eq!(check_layout("1".pull_y(1))?, Some(XYWH(1u16, 0, 1, 1)));
/// assert_eq!(check_layout("1".pull_xy(1, 1))?, Some(XYWH(0u16, 0, 1, 1)));
/// # Ok(()) }
/// ```
pub trait LayoutPull<'a, S: Screen>: Draw<'a, S> + Sized {
fn pull_x <X: Into<Option<S::Unit>> + Copy> (self, w: X)
-> Pull<'a, S, Self, X> { Pull::X(self, w) }
fn pull_y <X: Into<Option<S::Unit>> + Copy> (self, h: X)
-> Pull<'a, S, Self, X> { Pull::Y(self, h) }
fn pull_xy <X: Into<Option<S::Unit>> + Copy> (self, w: X, h: X)
-> Pull<'a, S, Self, X> { Pull::XY(self, w, h) }
}
#[cfg(all(feature = "draw", feature = "eval"))]
pub fn kw_full <'a, O, S> (state: &'a S, to: &mut O, expr: &'a str)
-> PerhapsRef<'a, XYWH<O::Unit>>
where
O: Screen + 'a,
S: Interpret<'a, O, Option<XYWH<O::Unit>>>
+ Namespace<bool>
+ Namespace<O::Unit>
+ Namespace<Option<O::Unit>>
{
let thunk = draw(move|screen|ok_flat(expr.nth(1)?.map(|x: &'a str|state.interpret(screen, x))));
Ok(match expr.head()? {
Some("full/x") | Some("full/w") => Full::W(thunk).draw(to)?,
Some("full/y") | Some("full/h") => Full::H(thunk).draw(to)?,
Some("full/xy") | Some("full/wh") => Full::WH(thunk).draw(to)?,
frag => return Err(format!("invalid variant: {frag:?}").into())
})
}
#[cfg(all(feature = "draw", feature = "eval"))]
pub fn kw_exact <'a, O, S> (state: &'a S, to: &mut O, expr: &'a str)
-> PerhapsRef<'a, XYWH<O::Unit>>
where
O: Screen + 'a,
S: Interpret<'a, O, Option<XYWH<O::Unit>>>
+ Namespace<bool>
+ Namespace<O::Unit>
+ Namespace<Option<O::Unit>>
{
let thunk = draw(move|screen|ok_flat(expr.last()?.map(|x: &'a str|state.interpret(screen, x))));
Ok(match expr.head()? {
Some("exact/w") => thunk.exact_w(
state.namespace(expr.nth(1)?)?
).draw(to)?,
Some("exact/h") => thunk.exact_h(
state.namespace(expr.nth(1)?)?
).draw(to)?,
Some("exact/wh") => thunk.exact_wh(
state.namespace(expr.nth(1)?)?,
state.namespace(expr.nth(2)?)?,
).draw(to)?,
_ => return Err(format!("invalid variant: {:?}", expr.head()?).into())
})
}
#[cfg(all(feature = "draw", feature = "eval"))]
pub fn kw_min <'a, O, S> (state: &'a S, to: &mut O, expr: &'a str)
-> PerhapsRef<'a, XYWH<O::Unit>>
where
O: Screen + 'a,
S: Interpret<'a, O, Option<XYWH<O::Unit>>>
+ Namespace<bool>
+ Namespace<O::Unit>
+ Namespace<Option<O::Unit>>
{
let thunk = draw(move|screen|ok_flat(expr.last()?.map(|x: &'a str|state.interpret(screen, x))));
Ok(match expr.head()? {
Some("min/w") => thunk.min_w(
state.namespace(expr.nth(1)?)?
).draw(to)?,
Some("min/h") => thunk.min_h(
state.namespace(expr.nth(1)?)?
).draw(to)?,
Some("min/wh") => thunk.min_wh(
state.namespace(expr.nth(1)?)?,
state.namespace(expr.nth(2)?)?,
).draw(to)?,
_ => return Err(format!("invalid variant: {:?}", expr.head()?).into())
})
}
#[cfg(all(feature = "draw", feature = "eval"))]
pub fn kw_max <'a, O, S> (state: &'a S, to: &mut O, expr: &'a str)
-> PerhapsRef<'a, XYWH<O::Unit>>
where
O: Screen + 'a,
S: Interpret<'a, O, Option<XYWH<O::Unit>>>
+ Namespace<bool>
+ Namespace<O::Unit>
+ Namespace<Option<O::Unit>>
{
let thunk = draw(move|screen|ok_flat(expr.last()?.map(|x: &'a str|state.interpret(screen, x))));
Ok(match expr.head()? {
Some("max/w") => thunk.max_w(
state.namespace(expr.nth(1)?)?
).draw(to)?,
Some("max/h") => thunk.max_h(
state.namespace(expr.nth(1)?)?
).draw(to)?,
Some("max/wh") => thunk.max_wh(
state.namespace(expr.nth(1)?)?,
state.namespace(expr.nth(2)?)?,
).draw(to)?,
_ => return Err(format!("invalid variant: {:?}", expr.head()?).into())
})
}
#[cfg(all(feature = "draw", feature = "eval"))]
pub fn kw_push <'a, O, S> (state: &'a S, to: &mut O, expr: &'a str)
-> PerhapsRef<'a, XYWH<O::Unit>>
where
O: Screen + 'a,
S: Interpret<'a, O, Option<XYWH<O::Unit>>>
+ Namespace<bool>
+ Namespace<O::Unit>
+ Namespace<Option<O::Unit>>
{
let thunk = draw(move|screen|ok_flat(expr.last()?.map(|x: &'a str|state.interpret(screen, x))));
Ok(match expr.head()? {
Some("push/w") => thunk.push_x(state.namespace(expr.nth(1)?)?).draw(to)?,
Some("push/h") => thunk.push_y(state.namespace(expr.nth(1)?)?).draw(to)?,
Some("push/wh") => thunk.push_xy(state.namespace(expr.nth(1)?)?, state.namespace(expr.nth(2)?)?,).draw(to)?,
_ => return Err(format!("invalid variant: {:?}", expr.head()?).into())
})
}
#[cfg(all(feature = "draw", feature = "eval"))]
pub fn kw_pull <'a, O, S> (state: &'a S, to: &mut O, expr: &'a str)
-> PerhapsRef<'a, XYWH<O::Unit>>
where
O: Screen + 'a,
S: Interpret<'a, O, Option<XYWH<O::Unit>>>
+ Namespace<bool>
+ Namespace<O::Unit>
+ Namespace<Option<O::Unit>>
{
let thunk = draw(move|screen|ok_flat(expr.last()?.map(|x: &'a str|state.interpret(screen, x))));
Ok(match expr.head()? {
Some("pull/w") => thunk.pull_x(state.namespace(expr.nth(1)?)?).draw(to)?,
Some("pull/h") => thunk.pull_y(state.namespace(expr.nth(1)?)?).draw(to)?,
Some("pull/wh") => thunk.pull_xy(state.namespace(expr.nth(1)?)?, state.namespace(expr.nth(2)?)?,).draw(to)?,
_ => return Err(format!("invalid variant: {:?}", expr.head()?).into())
})
}
#[cfg(all(feature = "draw", feature = "eval"))]
pub fn kw_pad <'a, O, S> (state: &'a S, to: &mut O, expr: &'a str)
-> PerhapsRef<'a, XYWH<O::Unit>>
where
O: Screen + 'a,
S: Interpret<'a, O, Option<XYWH<O::Unit>>>
+ Namespace<bool>
+ Namespace<O::Unit>
+ Namespace<Option<O::Unit>>
{
let thunk = draw(move|screen|ok_flat(expr.last()?.map(|x: &'a str|state.interpret(screen, x))));
Ok(match expr.head()? {
Some("pad/w") => thunk.pad_w(
state.namespace(expr.nth(1)?)?
).draw(to)?,
Some("pad/h") => thunk.pad_h(
state.namespace(expr.nth(1)?)?
).draw(to)?,
Some("pad/wh") => thunk.pad_wh(
state.namespace(expr.nth(1)?)?,
state.namespace(expr.nth(2)?)?,
).draw(to)?,
_ => return Err(format!("invalid variant: {:?}", expr.head()?).into())
})
}
impl <'a, S: Screen, I: Draw<'a, S>> Draw<'a, S> for Full<'a, S, I> {
fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit> {
def_layout_modifier_flat!(
LayoutFull (full_w full_h full_wh),
Full { W H WH } kw_full "full" |self, to| {
let XYWH(x0, y0, w0, h0) = to.area();
let item = match self {
Self::W(i) => i, Self::H(i) => i, Self::WH(i) => i, _ => unreachable!()
@ -305,10 +17,19 @@ impl <'a, S: Screen, I: Draw<'a, S>> Draw<'a, S> for Full<'a, S, I> {
x1.unwrap_or(x2), y1.unwrap_or(y2), w1.unwrap_or(w2), h1.unwrap_or(h2),
)))
}
);
#[cfg(test)] #[test] fn test_layout_full () -> Usually<()> {
assert_eq!(check_layout("1")?, Some(XYWH(1u16, 1, 1, 1)));
assert_eq!(check_layout("1".full_w())?, Some(XYWH(1u16, 1, 80, 1)));
assert_eq!(check_layout("1".full_h())?, Some(XYWH(1u16, 1, 1, 25)));
assert_eq!(check_layout("1".full_wh())?, Some(XYWH(1u16, 1, 80, 25)));
Ok(())
}
impl <'a, S: Screen, I: Draw<'a, S>, X: Into<Option<S::Unit>> + Copy> Draw<'a, S> for Exact<'a, S, I, X> {
fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit> {
def_layout_modifier!(
LayoutExact (exact_w exact_h exact_wh),
Exact { W H WH } kw_exact "exact" |self, to| {
let XYWH(x0, y0, w0, h0) = to.area();
let (item, w1, h1) = match self {
Self::W(item, w1) => (item, (*w1).into(), None),
@ -327,10 +48,19 @@ impl <'a, S: Screen, I: Draw<'a, S>, X: Into<Option<S::Unit>> + Copy> Draw<'a, S
_ => unreachable!()
}))
}
);
#[cfg(test)] #[test] fn test_layout_exact () -> Usually<()> {
assert_eq!(check_layout("FOOBAR\nKILROY")?, Some(XYWH(1, 1, 6, 2)));
assert_eq!(check_layout("FOOBAR\nKILROY".exact_w(3))?, Some(XYWH(1, 1, 3, 2)));
assert_eq!(check_layout("FOOBAR\nKILROY".exact_h(1))?, Some(XYWH(1, 1, 6, 1)));
assert_eq!(check_layout("FOOBAR\nKILROY".exact_wh(2, 1))?, Some(XYWH(1, 1, 2, 1)));
Ok(())
}
impl <'a, S: Screen, I: Draw<'a, S>, X: Into<Option<S::Unit>> + Copy> Draw<'a, S> for Min<'a, S, I, X> {
fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit> {
def_layout_modifier!(
LayoutMin (min_w min_h min_wh),
Min { W H WH } kw_min "min" |self, to| {
let XYWH(x0, y0, w0, h0) = to.area();
let (item, w1, h1) = match self {
Self::W(item, w1) => (item, (*w1).into(), None),
@ -348,10 +78,19 @@ impl <'a, S: Screen, I: Draw<'a, S>, X: Into<Option<S::Unit>> + Copy> Draw<'a, S
h1.map(|h1|h1.max(h2)).unwrap_or(h2)
)))
}
);
#[cfg(test)] #[test] fn test_layout_min () -> Usually<()> {
assert_eq!(check_layout("1".min_w(5))?, Some(XYWH(1u16, 1, 5, 1)));
assert_eq!(check_layout("1".min_h(5))?, Some(XYWH(1u16, 1, 1, 5)));
assert_eq!(check_layout("1".min_wh(5, 5))?, Some(XYWH(1u16, 1, 5, 5)));
assert_eq!(check_layout("123456".min_w(5))?, Some(XYWH(1u16, 1, 6, 1)));
Ok(())
}
impl <'a, S: Screen, I: Draw<'a, S>, X: Into<Option<S::Unit>> + Copy> Draw<'a, S> for Max<'a, S, I, X> {
fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit> {
def_layout_modifier!(
LayoutMax (max_w max_h max_wh),
Max { W H WH } kw_max "max" |self, to| {
let XYWH(x, y, w0, h0) = to.area();
let (item, w, h) = match self {
Self::W(item, w) => (item, (*w).into().unwrap_or(w0), h0),
@ -361,10 +100,31 @@ impl <'a, S: Screen, I: Draw<'a, S>, X: Into<Option<S::Unit>> + Copy> Draw<'a, S
};
to.draw(XYWH(x, y, if w0 > w { w } else { w0 }, if h0 > h { h } else { h0 }), item)
}
}
);
impl <'a, S: Screen, I: Draw<'a, S>, X: Into<Option<S::Unit>> + Copy> Draw<'a, S> for Push<'a, S, I, X> {
fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit> {
def_layout_modifier!(
LayoutPad (pad_w pad_h pad_wh),
Pad { X Y XY } kw_pad "pad" |self, to| {
let XYWH(x, y, w0, h0) = to.area();
let (item, w, h) = match self {
Self::X(item, w) => (item, (*w).into().unwrap_or_default(), Default::default()),
Self::Y(item, h) => (item, Default::default(), (*h).into().unwrap_or_default()),
Self::XY(item, w, h) => (item, (*w).into().unwrap_or_default(),
(*h).into().unwrap_or_default()),
_ => return Ok(None)
};
to.draw(XYWH(
x.plus(w),
y.plus(h),
w0.minus(w).minus(w),
h0.minus(h).minus(h),
), item)
}
);
def_layout_modifier!(
LayoutPush (push_x push_y push_xy),
Push { X Y XY } kw_push "push" |self, to| {
match self {
Self::__(_) => unreachable!(),
Self::X(item, x1) if let Some(XYWH(x, y, w, h)) = to.size(None, item)? => {
@ -379,10 +139,19 @@ impl <'a, S: Screen, I: Draw<'a, S>, X: Into<Option<S::Unit>> + Copy> Draw<'a, S
_ => Ok(None)
}
}
);
#[cfg(test)] #[test] fn test_layout_push () -> Usually<()> {
assert_eq!(check_layout("1")?, Some(XYWH(1u16, 1, 1, 1)));
assert_eq!(check_layout("1".push_x(1))?, Some(XYWH(2u16, 1, 1, 1)));
assert_eq!(check_layout("1".push_y(1))?, Some(XYWH(1u16, 2, 1, 1)));
assert_eq!(check_layout("1".push_xy(1, 1))?, Some(XYWH(2u16, 2, 1, 1)));
Ok(())
}
impl <'a, S: Screen, I: Draw<'a, S>, X: Into<Option<S::Unit>> + Copy> Draw<'a, S> for Pull<'a, S, I, X> {
fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit> {
def_layout_modifier!(
LayoutPull (pull_x pull_y pull_xy),
Pull { X Y XY } kw_pull "pull" |self, to| {
match self {
Self::__(_) => unreachable!(),
Self::X(item, x1) if let Some(XYWH(x, y, w, h)) = to.size(None, item)? => {
@ -403,41 +172,16 @@ impl <'a, S: Screen, I: Draw<'a, S>, X: Into<Option<S::Unit>> + Copy> Draw<'a, S
_ => Ok(None)
}
}
);
#[cfg(test)] #[test] fn test_layout_pull () -> Usually<()> {
assert_eq!(check_layout("1")?, Some(XYWH(1u16, 1, 1, 1)));
assert_eq!(check_layout("1".pull_x(1))?, Some(XYWH(0u16, 1, 1, 1)));
assert_eq!(check_layout("1".pull_y(1))?, Some(XYWH(1u16, 0, 1, 1)));
assert_eq!(check_layout("1".pull_xy(1, 1))?, Some(XYWH(0u16, 0, 1, 1)));
Ok(())
}
impl <'a, S: Screen, I: Draw<'a, S>, X: Into<Option<S::Unit>> + Copy> Draw<'a, S> for Pad<'a, S, I, X> {
fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit> {
let XYWH(x, y, w0, h0) = to.area();
let (item, w, h) = match self {
Self::X(item, w) => (item, (*w).into().unwrap_or_default(), Default::default()),
Self::Y(item, h) => (item, Default::default(), (*h).into().unwrap_or_default()),
Self::XY(item, w, h) => (item, (*w).into().unwrap_or_default(),
(*h).into().unwrap_or_default()),
_ => return Ok(None)
};
to.draw(XYWH(
x.plus(w),
y.plus(h),
w0.minus(w).minus(w),
h0.minus(h).minus(h),
), item)
}
}
impl<'a, S: Screen, T: Draw<'a, S>> LayoutFull<'a, S> for T {}
impl<'a, S: Screen, T: Draw<'a, S>> LayoutExact<'a, S> for T {}
impl<'a, S: Screen, T: Draw<'a, S>> LayoutMin<'a, S> for T {}
impl<'a, S: Screen, T: Draw<'a, S>> LayoutMax<'a, S> for T {}
impl<'a, S: Screen, T: Draw<'a, S>> LayoutPush<'a, S> for T {}
impl<'a, S: Screen, T: Draw<'a, S>> LayoutPull<'a, S> for T {}
impl<'a, S: Screen, T: Draw<'a, S>> LayoutPad<'a, S> for T {}
#[cfg(test)] fn check_layout <'a> (t: impl Draw<'a, Tui>) -> Drawn<'a, u16> {
#[cfg(test)] fn check_layout (t: impl Draw<Tui>) -> Perhaps<XYWH<u16>> {
Tui::Layout(XYWH(1u16, 1, 80, 25)).size(None, t)
}

View file

@ -18,11 +18,9 @@ pub struct Origin<T>(
pub(crate) T
);
impl<'a, S: Screen, T: Draw<'a, S>> Draw<'a, S> for Origin<T> {
fn draw (&self, _to: &mut S) -> Drawn<'a, S::Unit> {
todo!()
}
}
impl_draw!(<S: Screen, T: Draw<S>,>|self: Origin<T>, _to: S|{
todo!()
});
/// Something that has `[0, 0]` at a particular point.
pub trait HasOrigin {
@ -35,47 +33,25 @@ impl<T: AsRef<Azimuth>> HasOrigin for T {
}
}
#[cfg(all(feature = "draw", feature = "eval"))]
pub fn kw_align <'a, O, S> (state: &'a S, to: &mut O, expr: &'a str)
-> Drawn<'a, O::Unit>
where
O: Screen,
S: Interpret<'a, O, Option<XYWH<O::Unit>>>
+ Namespace<bool>
+ Namespace<O::Unit>
+ Namespace<Option<O::Unit>>
{
let thunk = draw(move|screen|ok_flat(expr.nth(1)?.map(|x: &'a str|state.interpret(screen, x))));
Ok(match expr.head()? {
Some("align/c") => Align(Some(Azimuth::C), thunk).draw(to)?,
Some("align/x") => Align(Some(Azimuth::X), thunk).draw(to)?,
Some("align/y") => Align(Some(Azimuth::Y), thunk).draw(to)?,
Some("align/n") => Align(Some(Azimuth::N), thunk).draw(to)?,
Some("align/s") => Align(Some(Azimuth::S), thunk).draw(to)?,
Some("align/e") => Align(Some(Azimuth::E), thunk).draw(to)?,
Some("align/w") => Align(Some(Azimuth::W), thunk).draw(to)?,
Some("align/nw") => Align(Some(Azimuth::NW), thunk).draw(to)?,
Some("align/sw") => Align(Some(Azimuth::SW), thunk).draw(to)?,
Some("align/ne") => Align(Some(Azimuth::NE), thunk).draw(to)?,
Some("align/se") => Align(Some(Azimuth::SE), thunk).draw(to)?,
frag => return Err(format!("invalid variant: {frag:?}").into())
})
//Ok(matches!(expr.head().src()?.unwrap_or_default().split("/").next(), Some("align")).then(||{
//draw(move|output: &mut O|{state.interpret(output, &expr.nth(1))}).align(
//eval_enum!("align", output, state,
//expr.head().src()?.unwrap_or_default().split("/").skip(1).next(),
//Azimuth {
//"c" => C, "x" => X, "y" => Y,
//"n" => N, "s" => S, "e" => E, "w" => W,
//"nw" => NW, "sw" => SW, "ne" => NE, "se" => SE,
//})
//).draw(output)
//}).transpose()?.flatten())
}
fn_kw_layout!(kw_align |state, output, expr| {
let head = expr.head();
let mut frags = head.src()?.unwrap_or_default().split("/");
Ok(matches!(frags.next(), Some("align")).then(||{
draw(move|output: &mut O|{state.interpret(output, &expr.tail().head())}).align(
eval_enum!("align", output, state,
expr.head().src()?.unwrap_or_default().split("/").skip(1).next(),
Azimuth {
"c" => C, "x" => X, "y" => Y,
"n" => N, "s" => S, "e" => E, "w" => W,
"nw" => NW, "sw" => SW, "ne" => NE, "se" => SE,
})
).draw(output)
}).transpose()?.flatten())
});
impl<'a, S: Screen, T: Draw<'a, S>> CanAlign<'a, S> for T {}
impl<S: Screen, T: Draw<S>> CanAlign<S> for T {}
pub trait CanAlign<'a, S: Screen>: Draw<'a, S> + Sized {
pub trait CanAlign<S: Screen>: Draw<S> + Sized {
fn align (self, azimuth: impl Into<Option<Azimuth>>) -> Align<Self> {
Align(azimuth.into(), self)
}
@ -119,8 +95,8 @@ pub struct Align<T>(
pub(crate) T,
);
impl<'a, S: Screen, T: Draw<'a, S>> Draw<'a, S> for Align<T> {
fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit> {
impl<S: Screen, T: Draw<S>> Draw<S> for Align<T> {
fn draw (&self, to: &mut S) -> Perhaps<XYWH<S::Unit>> {
let Self(azimuth, item) = self;
let area0 = to.area();
let size = to.size(area0, item)?;
@ -157,17 +133,25 @@ fn align <S: Screen> (
}
fn_kw_layout!(kw_split |state, output, expr| {
let thunk_a = draw(move|screen|ok_flat(expr.nth(1)?.map(|x: &'a str|state.interpret(screen, x))));
let thunk_b = draw(move|screen|ok_flat(expr.nth(2)?.map(|x: &'a str|state.interpret(screen, x))));
Ok(match expr.head()? {
Some("bsp/n") => Split::North.stack(thunk_a, thunk_b).draw(output)?,
Some("bsp/s") => Split::South.stack(thunk_a, thunk_b).draw(output)?,
Some("bsp/e") => Split::East.stack(thunk_a, thunk_b).draw(output)?,
Some("bsp/w") => Split::West.stack(thunk_a, thunk_b).draw(output)?,
Some("bsp/a") => Split::Above.stack(thunk_a, thunk_b).draw(output)?,
Some("bsp/b") => Split::Below.stack(thunk_a, thunk_b).draw(output)?,
frag => return Err(format!("invalid variant: {frag:?}").into())
})
let head = expr.head();
let mut frags = head.src()?.unwrap_or_default().split("/");
Ok(matches!(frags.next(), Some("bsp")).then(||{
eval_enum!("bsp", output, state, frags.next(), Split {
"n" => North,
"s" => South,
"e" => East,
"w" => West,
"a" => Above,
"b" => Below
}).stack(
draw(move|output: &mut O|{
state.interpret(output, &expr.tail().head()?)
}),
draw(move|output: &mut O|{
state.interpret(output, &expr.tail().tail().head()?)
}),
).draw(output)
}).transpose()?.flatten())
});
/// Split along an axis. Direction determines order.
@ -181,28 +165,16 @@ fn_kw_layout!(kw_split |state, output, expr| {
#[default] Below
}
pub struct Pair<A, B>(Split, A, B);
pub struct Pair<S: Screen, A: Draw<S>, B: Draw<S>>(Split, A, B, PhantomData<S>);
/// ```
/// # fn test_split_stack () -> Usually<()> {
/// use Split::*;
/// fn size_of <A: Draw<Tui>, B: Draw<Tui>> (stack: &Pair<Tui, A, B>) -> Drawn<'a, S::Unit> {
/// Tui::Layout(XYWH(0, 0, 80, 25)).size(None, stack)
/// }
/// assert_eq!(size_of(&split(East, "foo", "bar"))?, Some(XYWH(0, 0, 6, 1)));
/// assert_eq!(size_of(&split(South, "foo", "bar"))?, Some(XYWH(0, 0, 3, 2)));
/// assert_eq!(size_of(&split(South, split(East, "foo", "bar"), "baz"))?, Some(XYWH(0, 0, 6, 2)));
/// assert_eq!(size_of(&split(East, split(South, "foo", "bar"), "baz"))?, Some(XYWH(0, 0, 6, 2)));
/// # return Ok(()); }
/// ```
pub fn split <'a, S: Screen, A: Draw<'a, S>, B: Draw<'a, S>> (
pub fn split <S: Screen, A: Draw<S>, B: Draw<S>> (
split: Split, a: A, b: B
) -> Pair<A, B> {
Pair(split, a, b)
) -> Pair<S, A, B> {
Pair(split, a, b, PhantomData)
}
impl<'a, S: Screen, A: Draw<'a, S>, B: Draw<'a, S>> Draw<'a, S> for Pair<A, B> {
fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit> {
impl<S: Screen, A: Draw<S>, B: Draw<S>> Draw<S> for Pair<S, A, B> {
fn draw (&self, to: &mut S) -> Drawn<S::Unit> {
let Self(split, a, b, ..) = self;
let (area_a, area_b) = stack_areas(split, to, a, b)?;
let (drawn_a, drawn_b) = draw_stacks(split, to, a, area_a, None, b, area_b, None)?;
@ -210,16 +182,16 @@ impl<'a, S: Screen, A: Draw<'a, S>, B: Draw<'a, S>> Draw<'a, S> for Pair<A, B> {
}
}
fn draw_stacks <'a, S: Screen> (
fn draw_stacks <S: Screen> (
split: &Split,
to: &mut S,
a: impl Draw<'a, S>,
a: impl Draw<S>,
area_a: impl Into<Option<XYWH<S::Unit>>>,
origin_a: impl Into<Option<Azimuth>>,
b: impl Draw<'a, S>,
b: impl Draw<S>,
area_b: impl Into<Option<XYWH<S::Unit>>>,
origin_b: impl Into<Option<Azimuth>>,
) -> UsuallyRef<'a, (Option<XYWH<S::Unit>>, Option<XYWH<S::Unit>>)> {
) -> Usually<(Option<XYWH<S::Unit>>, Option<XYWH<S::Unit>>)> {
let draw_a = |to: &mut S|Ok::<_, Box<dyn Error>>(match origin_a.into() {
Some(origin_a) => to.draw(area_a.into(), a.align(origin_a))?,
None => to.draw(area_a.into(), a)?
@ -237,25 +209,12 @@ fn draw_stacks <'a, S: Screen> (
})
}
/// ```
/// # fn test_stack_areas () -> Usually<()> {
/// let area = XYWH(0u16, 0, 80, 25);
/// assert_eq!(stack_areas(&Split::East, &mut Tui::Layout(area), &"foo", &"bar")?, (
/// Some(XYWH(0u16, 0, 3, 1)),
/// Some(XYWH(3u16, 0, 3, 1)),
/// ));
/// assert_eq!(stack_areas(&Split::South, &mut Tui::Layout(area), &"foo", &"bar")?, (
/// Some(XYWH(0u16, 0, 3, 1)),
/// Some(XYWH(0u16, 1, 3, 1)),
/// ));
/// # Ok(()) }
/// ```
pub fn stack_areas <'a, S: Screen> (
pub fn stack_areas <S: Screen> (
split: &Split,
to: &mut S,
a: impl Draw<'a, S>,
b: impl Draw<'a, S>,
) -> UsuallyRef<'a, (Option<XYWH<S::Unit>>, Option<XYWH<S::Unit>>)> {
a: impl Draw<S>,
b: impl Draw<S>,
) -> Usually<(Option<XYWH<S::Unit>>, Option<XYWH<S::Unit>>)> {
let area_a = to.size(None, a)?;
Ok(match split {
Split::South => (
@ -330,10 +289,8 @@ impl Split {
/// let _ = Split::East.stack(&"", &"");
/// let _ = Split::West.stack(&"", &"");
/// ```
pub const fn stack <'a, S: Screen, A: Draw<'a, S>, B: Draw<'a, S>> (&self, a: A, b: B)
-> impl Draw<'a, S>
{
Pair(*self, a, b)
pub const fn stack <S: Screen, A: Draw<S>, B: Draw<S>> (&self, a: A, b: B) -> impl Draw<S> {
Pair(*self, a, b, PhantomData)
}
/// ```
@ -345,9 +302,7 @@ impl Split {
/// let _ = Split::East.half(&"", &"");
/// let _ = Split::West.half(&"", &"");
/// ```
pub const fn half <'a, S: Screen, A: Draw<'a, S>, B: Draw<'a, S>> (&self, a: &A, b: &B)
-> impl Draw<'a, S>
{
pub const fn half <S: Screen, A: Draw<S>, B: Draw<S>> (&self, a: &A, b: &B) -> impl Draw<S> {
draw(move|to: &mut S|{
let (area_a, area_b) = to.xywh().split_half(self);
let (origin_a, origin_b) = self.origins();
@ -401,28 +356,28 @@ impl Split {
}
pub const fn east <'a, S: Screen, A: Draw<'a, S>, B: Draw<'a, S>> (a: A, b: B) -> impl Draw<'a, S> {
Pair(Split::East, a, b)
pub const fn east <S: Screen, A: Draw<S>, B: Draw<S>> (a: A, b: B) -> impl Draw<S> {
Pair(Split::East, a, b, PhantomData)
}
pub const fn north <'a, S: Screen, A: Draw<'a, S>, B: Draw<'a, S>> (a: A, b: B) -> impl Draw<'a, S> {
Pair(Split::North, a, b)
pub const fn north <S: Screen, A: Draw<S>, B: Draw<S>> (a: A, b: B) -> impl Draw<S> {
Pair(Split::North, a, b, PhantomData)
}
pub const fn west <'a, S: Screen, A: Draw<'a, S>, B: Draw<'a, S>> (a: A, b: B) -> impl Draw<'a, S> {
Pair(Split::West, a, b)
pub const fn west <S: Screen, A: Draw<S>, B: Draw<S>> (a: A, b: B) -> impl Draw<S> {
Pair(Split::West, a, b, PhantomData)
}
pub const fn south <'a, S: Screen, A: Draw<'a, S>, B: Draw<'a, S>> (a: A, b: B) -> impl Draw<'a, S> {
Pair(Split::South, a, b)
pub const fn south <S: Screen, A: Draw<S>, B: Draw<S>> (a: A, b: B) -> impl Draw<S> {
Pair(Split::South, a, b, PhantomData)
}
pub const fn above <'a, S: Screen, A: Draw<'a, S>, B: Draw<'a, S>> (a: A, b: B) -> impl Draw<'a, S> {
Pair(Split::Above, a, b)
pub const fn above <S: Screen, A: Draw<S>, B: Draw<S>> (a: A, b: B) -> impl Draw<S> {
Pair(Split::Above, a, b, PhantomData)
}
pub const fn below <'a, S: Screen, A: Draw<'a, S>, B: Draw<'a, S>> (a: A, b: B) -> impl Draw<'a, S> {
Pair(Split::Below, a, b)
pub const fn below <S: Screen, A: Draw<S>, B: Draw<S>> (a: A, b: B) -> impl Draw<S> {
Pair(Split::Below, a, b, PhantomData)
}
#[macro_export] macro_rules! north {
@ -455,6 +410,38 @@ pub const fn below <'a, S: Screen, A: Draw<'a, S>, B: Draw<'a, S>> (a: A, b: B)
($head:expr, $($tail:expr),* $(,)?) => { below($head, below!($($tail,)*)) };
}
#[cfg(test)] #[test] fn test_stack_areas () -> Usually<()> {
let area = XYWH(0u16, 0, 80, 25);
assert_eq!(stack_areas(&Split::East, &mut Tui::Layout(area), &"foo", &"bar")?, (
Some(XYWH(0u16, 0, 3, 1)),
Some(XYWH(3u16, 0, 3, 1)),
));
assert_eq!(stack_areas(&Split::South, &mut Tui::Layout(area), &"foo", &"bar")?, (
Some(XYWH(0u16, 0, 3, 1)),
Some(XYWH(0u16, 1, 3, 1)),
));
Ok(())
}
#[cfg(test)] #[test] fn test_split_stack () -> Usually<()> {
use Split::*;
fn size_of <A: Draw<Tui>, B: Draw<Tui>> (stack: &Pair<Tui, A, B>) -> Perhaps<XYWH<u16>> {
Tui::Layout(XYWH(0, 0, 80, 25)).size(None, stack)
}
assert_eq!(size_of(&split(East, "foo", "bar"))?,
Some(XYWH(0, 0, 6, 1)));
assert_eq!(size_of(&split(South, "foo", "bar"))?,
Some(XYWH(0, 0, 3, 2)));
assert_eq!(size_of(&split(South, split(East, "foo", "bar"), "baz"))?,
Some(XYWH(0, 0, 6, 2)));
assert_eq!(size_of(&split(East, split(South, "foo", "bar"), "baz"))?,
Some(XYWH(0, 0, 6, 2)));
return Ok(());
}
#[cfg(test)] #[test] fn test_align () -> Usually<()> {
let mut screen = Tui::Layout(XYWH(0, 0, 80, 25));
assert_eq!("FOOBAR\nKILROY".draw(&mut screen)?, Some(XYWH(0, 0, 6, 2)));

View file

@ -1,10 +1,11 @@
use crate::*;
fn_kw_layout!(kw_when |state, output, expr| {
let thunk = draw(move|screen|ok_flat(expr.nth(2)?.map(|x: &'a str|state.interpret(screen, x))));
ok_flat(matches!(expr.head()?, Some("when")).then(||{
when(state.namespace(expr.nth(1)?)?.unwrap(), thunk) .draw(output)
}))
Ok(matches!(expr.head()?, Some("when")).then(||{
when(state.namespace(expr.tail().head())?.unwrap(),
draw(move|output: &mut O|{state.interpret(output, &expr.tail().tail().head())})
).draw(output)
}).transpose()?.flatten())
});
/// Only render when condition is true.
@ -15,16 +16,21 @@ fn_kw_layout!(kw_when |state, output, expr| {
/// when(true, "Yes")
/// # }
/// ```
pub const fn when <'a, T: Screen> (condition: bool, item: impl Draw<'a, T>) -> impl Draw<'a, T> {
pub const fn when <T: Screen> (condition: bool, item: impl Draw<T>) -> impl Draw<T> {
draw(move|to: &mut T|if condition { item.draw(to) } else { Ok(Default::default()) })
}
fn_kw_layout!(kw_either |state, output, expr| {
let thunk_a = draw(move|screen|ok_flat(expr.nth(2)?.map(|x: &'a str|state.interpret(screen, x))));
let thunk_b = draw(move|screen|ok_flat(expr.nth(3)?.map(|x: &'a str|state.interpret(screen, x))));
ok_flat(matches!(expr.head()?, Some("either")).then(||{
either(state.namespace(expr.nth(1)?)?.unwrap(), thunk_a, thunk_b).draw(output)
}))
Ok(matches!(expr.head()?, Some("either")).then(||{
either(state.namespace(expr.tail().head()?)?.unwrap(),
draw(move|output: &mut O|{
state.interpret(output, &expr.tail().tail().head()?)
}),
draw(move|output: &mut O|{
state.interpret(output, &expr.tail().tail().tail().head()?)
}),
).draw(output)
}).transpose()?.flatten())
});
/// Render one thing if a condition is true and another false.
@ -35,8 +41,6 @@ fn_kw_layout!(kw_either |state, output, expr| {
/// either(true, "Yes", "No")
/// # }
/// ```
pub const fn either <'a, T: Screen> (
condition: bool, a: impl Draw<'a, T>, b: impl Draw<'a, T>
) -> impl Draw<'a, T> {
pub const fn either <T: Screen> (condition: bool, a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
draw(move|to: &mut T|if condition { a.draw(to) } else { b.draw(to) })
}

View file

@ -1,8 +1,8 @@
use super::*;
pub fn iter_south <'a, S: Screen, D: Draw<'a, S>, I: Iterator<Item = D>> (
pub fn iter_south <'a, S: Screen, D: Draw<S>, I: Iterator<Item = D>> (
iter: impl Fn()->I
) -> impl Draw<'a, S> {
) -> impl Draw<S> {
draw(move|to: &mut S|{
let XYWH(x, y, w, h) = to.area();
let mut y_next = y;
@ -22,9 +22,9 @@ pub fn iter_south <'a, S: Screen, D: Draw<'a, S>, I: Iterator<Item = D>> (
})
}
pub fn iter_east <'a, S: Screen, D: Draw<'a, S>, I: Iterator<Item = D>> (
pub fn iter_east <'a, S: Screen, D: Draw<S>, I: Iterator<Item = D>> (
iter: impl Fn()->I
) -> impl Draw<'a, S> {
) -> impl Draw<S> {
draw(move|to: &mut S|{
let XYWH(x, y, w, h) = to.area();
let mut x_next = x;
@ -45,75 +45,74 @@ pub fn iter_east <'a, S: Screen, D: Draw<'a, S>, I: Iterator<Item = D>> (
}
/// Iterate over a collection of the same kind of [Draw]able:
pub fn iter <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<'a, S>> (
pub fn iter <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> (
_iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
) -> impl Draw<'a, S> {
) -> impl Draw<S> {
draw(move|_to: &mut S|{ todo!() })
}
/// Iterate over a collection of the same kind of [Draw]able:
pub fn iter_once <'a, S: Screen, D: 'a, U: Draw<'a, S>> (
pub fn iter_once <'a, S: Screen, D: 'a, U: Draw<S>> (
_iter: impl Iterator<Item = D>, _draw: impl Fn(D, usize)->U,
) -> impl Draw<'a, S> {
) -> impl Draw<S> {
draw(move|_to: &mut S|{ todo!() })
}
/// Iterate over a collection of various [Draw]ables:
pub fn iter_dyn <
'a,
S: Screen, // Target screen
D, // Input doesn't need to be [Draw]able, output does
V: Fn()->I, // Function that returns the iterator
I: Iterator<Item = D>, // Type of the iterator
F: Fn(&D, usize)->dyn Draw<'a, S>, // Function that returns [Draw]able from iterator item
> (_items: V, _cb: F) -> impl Draw<'a, S> {
F: Fn(&D, usize)->dyn Draw<S>, // Function that returns [Draw]able from iterator item
> (_items: V, _cb: F) -> impl Draw<S> {
draw(move|_to: &mut S|{ todo!() })
}
pub fn iter_north <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<'a, S>> (
pub fn iter_north <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> (
_iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
) -> impl Draw<'a, S> {
) -> impl Draw<S> {
draw(move|_to: &mut S|{ todo!() })
}
pub fn iter_east_fixed <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<'a, S>> (
pub fn iter_east_fixed <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> (
_height: S::Unit, _iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
) -> impl Draw<'a, S> {
) -> impl Draw<S> {
draw(move|_to: &mut S|{ todo!() })
}
pub fn iter_south_fixed <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<'a, S>> (
pub fn iter_south_fixed <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> (
_height: S::Unit, _iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
) -> impl Draw<'a, S> {
) -> impl Draw<S> {
draw(move|_to: &mut S|{ todo!() })
}
pub fn iter_west <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<'a, S>> (
pub fn iter_west <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> (
_iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
) -> impl Draw<'a, S> {
) -> impl Draw<S> {
draw(move|_to: &mut S|{ todo!() })
}
pub fn row_south <'a, S: Screen + 'a> (south: S::Unit, height: S::Unit, content: impl Draw<'a, S>)
-> impl Draw<'a, S>
pub fn row_south <S: Screen> (south: S::Unit, height: S::Unit, content: impl Draw<S>)
-> impl Draw<S>
{
content.exact_h(height).push_y(south)
}
pub fn row_north <'a, S: Screen + 'a> (north: S::Unit, height: S::Unit, content: impl Draw<'a, S>)
-> impl Draw<'a, S>
pub fn row_north <S: Screen> (north: S::Unit, height: S::Unit, content: impl Draw<S>)
-> impl Draw<S>
{
content.exact_h(height).pull_y(north)
}
pub fn col_east <'a, S: Screen + 'a> (east: S::Unit, width: S::Unit, content: impl Draw<'a, S>)
-> impl Draw<'a, S>
pub fn col_east <S: Screen> (east: S::Unit, width: S::Unit, content: impl Draw<S>)
-> impl Draw<S>
{
content.exact_w(width).push_x(east)
}
pub fn col_west <'a, S: Screen + 'a> (west: S::Unit, width: S::Unit, content: impl Draw<'a, S>)
-> impl Draw<'a, S>
pub fn col_west <S: Screen> (west: S::Unit, width: S::Unit, content: impl Draw<S>)
-> impl Draw<S>
{
content.exact_w(width).pull_x(west)
}

View file

@ -1205,8 +1205,7 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
Ok(Self {
exit: exit.clone(),
perf: perf.clone(),
join: Builder::new().name("tengri task".into()).spawn(move || {
#[cfg(feature = "prof")] profiling::register_thread!();
join: Builder::new().name("tengri tui output".into()).spawn(move || {
while !exit.fetch_and(true, Relaxed) {
let _ = perf.cycle(&mut call);
}
@ -1265,17 +1264,17 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
draw: &impl Fn(&mut Self)->T
) -> T;
/// Determine used area without drawing
fn size <'a> (
fn size (
&mut self,
area: impl Into<Option<XYWH<Self::Unit>>>,
draw: impl Draw<'a, Self>
) -> Drawn<'a, Self::Unit>;
draw: impl Draw<Self>
) -> Perhaps<XYWH<Self::Unit>>;
/// Draw
fn draw <'a> (
fn draw (
&mut self,
area: impl Into<Option<XYWH<Self::Unit>>>,
draw: impl Draw<'a, Self>
) -> Drawn<'a, Self::Unit>;
draw: impl Draw<Self>
) -> Perhaps<XYWH<Self::Unit>>;
}
/// Implement the [Draw] trait for a particular drawable and [Screen].
@ -1290,7 +1289,7 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
#[macro_export] macro_rules! impl_draw (
($(<$($T:ident: $Trait:path,)+>)?|
$self:ident:$Self:path, $to:ident:$To:ty
|$draw:block)=>{ impl$(<$($T:$Trait),+>)? Draw<'_, $To> for $Self {
|$draw:block)=>{ impl$(<$($T:$Trait),+>)? Draw<$To> for $Self {
fn draw (&$self, $to: &mut $To) -> Perhaps<XYWH<<$To as Screen>::Unit>> $draw
} };
($(<$($T:ident: $Trait:path,)+>)?|
@ -1321,8 +1320,8 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
/// }
/// }
/// ```
pub trait Draw<'a, S: Screen> {
fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit>;
pub trait Draw<S: Screen> {
fn draw (&self, to: &mut S) -> Drawn<S::Unit>;
}
/// Emit a [Draw]able.
@ -1345,14 +1344,14 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
/// let _ = view::<Tui, _, _>(||"drawable");
/// let _ = view::<Tui, _, _>(||Some("drawable"));
/// ```
pub const fn view <S: Screen, T: for<'a> Draw<'a, S>, F: Fn()->T> (view: F) -> impl View<S> {
pub const fn view <S: Screen, T: Draw<S>, F: Fn()->T> (view: F) -> impl View<S> {
ViewThunk(view, PhantomData)
}
/// Because we can't implement [Draw] for `F: FnOnce...` without conflicts.
pub struct ViewThunk<S: Screen, F>(pub F, std::marker::PhantomData<S>);
impl<S: Screen, T: for<'a> Draw<'a, S>, F: Fn()->T> View<S> for ViewThunk<S, F> {
impl<S: Screen, T: Draw<S>, F: Fn()->T> View<S> for ViewThunk<S, F> {
fn view (&self) -> impl Draw<S> {
self.0()
}
@ -1361,8 +1360,8 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
/// Because we can't implement [Draw] for `F: FnOnce...` without conflicts.
pub struct DrawThunk<S: Screen, F>(pub F, std::marker::PhantomData<S>);
impl<'a, T: Screen, F: Fn(&mut T)->PerhapsRef<'a, XYWH<T::Unit>>> Draw<'a, T> for DrawThunk<T, F> {
fn draw (&self, to: &mut T) -> PerhapsRef<'a, XYWH<T::Unit>> {
impl<T: Screen, F: Fn(&mut T)->Perhaps<XYWH<T::Unit>>> Draw<T> for DrawThunk<T, F> {
fn draw (&self, to: &mut T) -> Perhaps<XYWH<T::Unit>> {
to.clip(None, &self.0)
}
}
@ -1373,25 +1372,23 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
/// # use tengri::*;
/// let _ = draw(|to: &mut Tui|Ok(Some(to.area()))); // draws nothing
/// ```
pub const fn draw <'a, T: Screen, F: Fn(&mut T)->PerhapsRef<'a, XYWH<T::Unit>>> (
pub const fn draw <T: Screen, F: Fn(&mut T)->Perhaps<XYWH<T::Unit>>> (
item: F
) -> DrawThunk<T, F> {
DrawThunk(item, std::marker::PhantomData)
}
pub type Drawn<'a, U> = PerhapsRef<'a, XYWH<U>>;
pub type Drawn<U> = Perhaps<XYWH<U>>;
impl<'a, S: Screen> Draw<'a, S> for () {
fn draw (&self, _: &mut S) -> Drawn<'a, S::Unit> {
impl<S: Screen> Draw<S> for () {
fn draw (&self, _: &mut S) -> Drawn<S::Unit> {
Ok(None)
}
}
impl<'a, S: Screen, D: Draw<'a, S>> Draw<'a, S> for Option<D> {
fn draw (&self, to: &mut S) -> Drawn<'a, S::Unit> {
self.as_ref().map(|it|it.draw(to)).transpose().map(Option::unwrap_or_default)
}
}
impl_draw!(<S: Screen, D: Draw<S>,>|self: Option<D>, to: S|{
self.as_ref().map(|it|it.draw(to)).transpose().map(Option::unwrap_or_default)
});
//impl<S: Screen, D: Draw<S>> Draw<S> for RwLock<D> {
//fn draw (&self, __: &mut S) -> Drawn<S::Unit> {
@ -1405,8 +1402,8 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
//}
//}
impl<'a, T: Screen, V: Draw<'a, T>> Draw<'a, T> for &V {
fn draw (&self, to: &mut T) -> Drawn<'a, T::Unit> {
impl<T: Screen, V: Draw<T>> Draw<T> for &V {
fn draw (&self, to: &mut T) -> Perhaps<XYWH<T::Unit>> {
(*self).draw(to)
}
}
@ -1719,7 +1716,7 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
impl ItemTheme {
#[cfg(feature = "term")] pub const G: [Self;256] = {
let mut builder = ::konst::array::ArrayBuilder::new();
let mut builder = dizzle::konst::array::ArrayBuilder::new();
while !builder.is_full() {
let index = builder.len() as u8;
let light = (index as f64 * 1.15) as u8;
@ -1790,26 +1787,23 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
}
}
pub trait ColorDsl<'a, T>: Sized {
fn new_g (expr: &'a T, try_to_u8: impl Fn(PerhapsRef<'a, &'a str>)->PerhapsRef<'a, u8>)
-> UsuallyRef<'a, Self>;
fn new_rgb (expr: &'a T, try_to_u8: impl Fn(PerhapsRef<'a, &str>)->PerhapsRef<'a, u8>)
-> UsuallyRef<'a, Self>;
pub trait ColorDsl<T>: Sized {
fn new_g (expr: T, try_to_u8: impl Fn(Perhaps<&str>)->Perhaps<u8>) -> Usually<Self>;
fn new_rgb (expr: T, try_to_u8: impl Fn(Perhaps<&str>)->Perhaps<u8>) -> Usually<Self>;
}
impl<'a, T: Language<'a> + 'a> ColorDsl<'a, T> for Color {
fn new_g (expr: &'a T, try_to_u8: impl Fn(PerhapsRef<'a, &'a str>)->PerhapsRef<'a, u8>)
-> Result<Self, Box<dyn Error + 'a>>
{
impl<T: Expression> ColorDsl<T> for Color {
fn new_g (expr: T, try_to_u8: impl Fn(Perhaps<&str>)->Perhaps<u8>) -> Usually<Self> {
let n = try_to_u8(expr.tail().map_err(Into::into))?.ok_or(Domain("not gray"))?;
Ok(Self::Rgb(n, n, n))
}
fn new_rgb (expr: &'a T, try_to_u8: impl Fn(PerhapsRef<'a, &str>)->PerhapsRef<'a, u8>)
-> Result<Self, Box<dyn Error + 'a>>
{
let r = try_to_u8(expr.nth(1).map_err(Into::into))?.ok_or(Domain("not red"))?;
let g = try_to_u8(expr.nth(2).map_err(Into::into))?.ok_or(Domain("not green"))?;
let b = try_to_u8(expr.nth(3).map_err(Into::into))?.ok_or(Domain("not blue"))?;
fn new_rgb (expr: T, try_to_u8: impl Fn(Perhaps<&str>)->Perhaps<u8>) -> Usually<Self> {
let r = try_to_u8(expr.tail().map_err(Into::into))?
.ok_or(Domain("not red"))?;
let g = try_to_u8(expr.tail().tail().head().map_err(Into::into))?
.ok_or(Domain("not green"))?;
let b = try_to_u8(expr.tail().tail().tail().head().map_err(Into::into))?
.ok_or(Domain("not blue"))?;
Ok(Color::Rgb(r, g, b))
}
}
@ -1907,7 +1901,7 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
/// }
///
/// impl Interpret<Tui, Option<XYWH<u16>>> for App {
/// fn interpret_expr <'a> (&'a self, to: &mut Tui, lang: &'a impl Language)
/// fn interpret_expr <'a> (&'a self, to: &mut Tui, lang: &'a impl Expression)
/// -> Usually<Option<XYWH<u16>>>
/// {
/// self.keyword(to, lang)
@ -1936,7 +1930,7 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
/// ```
pub trait Keywords <U, V>: 'static {
fn keywords () -> impl Iterator<Item = fn(&Self, &mut U, &str) -> Perhaps<V>>;
fn keyword <'a> (&self, to: &mut U, expr: &'a impl Language<'a>) -> PerhapsRef<'a, V> {
fn keyword (&self, to: &mut U, expr: &impl Expression) -> Perhaps<V> {
if let Some(expr) = expr.src()? {
for keyword in Self::keywords() {
if let Some(result) = keyword(self, to, &expr)? {
@ -1959,5 +1953,17 @@ pub trait AsMutOpt<T> { fn as_mut_opt (&mut self) -> Option<&mut T>; }
}
}
pub fn invalid_variant <T> (
name: &str,
frag: impl Language,
expr: impl Language,
head: impl Language,
) -> Usually<T> {
unimplemented!(
"{name}/{frag:?} ({expr:?}) ({head:?}) ({:?})",
head.src()?.unwrap_or_default().split("/").next()
)
}
#[cfg(feature = "term")] pub use self::term::*;
#[cfg(feature = "term")] mod term;

View file

@ -96,14 +96,14 @@ impl Screen for Tui {
fn area (&self) -> XYWH<Self::Unit> {
self.into()
}
fn size <'a> (
&mut self, area: impl Into<Option<XYWH<u16>>>, draw: impl Draw<'a, Self>,
) -> Drawn<'a, Self::Unit> {
fn size (
&mut self, area: impl Into<Option<XYWH<u16>>>, draw: impl Draw<Self>,
) -> Perhaps<XYWH<Self::Unit>> {
Self::Layout(self.area()).clip(area, &|to|draw.draw(to))
}
fn draw <'a> (
&mut self, area: impl Into<Option<XYWH<u16>>>, draw: impl Draw<'a, Tui>
) -> Drawn<'a, Self::Unit> {
fn draw (
&mut self, area: impl Into<Option<XYWH<u16>>>, draw: impl Draw<Tui>
) -> Perhaps<XYWH<Self::Unit>> {
self.clip(area, &|to|draw.draw(to))
}
fn clip <T> (
@ -289,7 +289,7 @@ impl Tui {
/// let _ = tengri::Tui::catcher(&variant);
/// }
/// ```
pub fn catcher <'a, T: Draw<'a, Tui>> (result: &Usually<T>) -> impl Draw<'a, Tui> {
pub fn catcher <T: Draw<Tui>> (result: &Usually<T>) -> impl Draw<Tui> {
draw(move|to: &mut Tui|match result {
Ok(content) => content.draw(to),
Err(e) => {
@ -369,7 +369,7 @@ impl<'a: 'b, 'b> From<&'a mut Tui> for &'b mut XYWH<u16> {
}
}
pub const fn fill_char <'a> (c: char) -> impl Draw<'a, Tui> {
pub const fn fill_char (c: char) -> impl Draw<Tui> {
draw(move|to: &mut Tui|Ok(Some(to.update(&|cell,_,_|{
cell.set_char(c);
}))))
@ -378,8 +378,8 @@ pub const fn fill_char <'a> (c: char) -> impl Draw<'a, Tui> {
/// FIXME: Don't use `format!` but implement number blitting
pub struct ShowSize;
impl<'a> Draw<'a, Tui> for ShowSize {
fn draw (&self, to: &mut Tui) -> Drawn<'a, u16> {
impl Draw<Tui> for ShowSize {
fn draw (&self, to: &mut Tui) -> Drawn<u16> {
let area = to.area();
let info = format!("{area:?}");
to.text(&info, area.0, area.1, info.len() as u16)
@ -388,8 +388,8 @@ impl<'a> Draw<'a, Tui> for ShowSize {
pub struct ShowSizeOf<T>(pub T);
impl<'a, T: Draw<'a, Tui>> Draw<'a, Tui> for ShowSizeOf<T> {
fn draw (&self, to: &mut Tui) -> Drawn<'a, u16> {
impl<T: Draw<Tui>> Draw<Tui> for ShowSizeOf<T> {
fn draw (&self, to: &mut Tui) -> Drawn<u16> {
Ok(self.0.draw(to)?.map(|used|{
let _ = to.text(&format!("{used:?}"), used.0, used.1, u16::MAX);
used
@ -398,14 +398,13 @@ impl<'a, T: Draw<'a, Tui>> Draw<'a, Tui> for ShowSizeOf<T> {
}
#[cfg(feature = "eval")] fn_kw_layout_tui!(kw_tui_text |_state, output, expr| {
Ok(if matches!(expr.head()?, Some("text")) {
expr.tail()?
.and_then(|src|(src.len() > 0).then(||(&src[1..]).draw(output)))
.transpose()?
.flatten()
} else {
None
})
Ok(matches!(expr.head()?, Some("text")).then(||{
if let Some(src) = expr.tail().src()? && src.len() > 0 {
(&src[1..]).draw(output)
} else {
return Ok(None)
}
}).transpose()?.flatten())
});
#[cfg(feature = "text")] pub use self::tui_text::*;
@ -474,8 +473,8 @@ impl<'a, T: Draw<'a, Tui>> Draw<'a, Tui> for ShowSizeOf<T> {
self.as_ref().draw(to)
});
impl<'a> Draw<'a, Tui> for &str {
fn draw (&self, to: &mut Tui) -> Drawn<'a, u16> {
impl Draw<Tui> for &str {
fn draw (&self, to: &mut Tui) -> Drawn<u16> {
let XYWH(x, y, w, h) = to.area();
let mut max_w = 0u16;
let mut max_h = 0u16;
@ -494,8 +493,8 @@ impl<'a, T: Draw<'a, Tui>> Draw<'a, Tui> for ShowSizeOf<T> {
}
}
impl<'a, T: AsRef<str>> Draw<'a, Tui> for TrimStr<'a, T> {
fn draw (&self, to: &mut Tui) -> Drawn<'a, u16> {
impl<'t, T: AsRef<str>> Draw<Tui> for TrimStr<'_, T> {
fn draw (&self, to: &mut Tui) -> Drawn<u16> {
let text = self.1.as_ref();
let area = layout_text_u16(text, to.area())?.unwrap();
let XYWH(x, y, w, ..) = to.area();

View file

@ -5,9 +5,7 @@ use crate::*;
/// ```
/// /// TODO
/// ```
pub fn border <'a> (
on: bool, style: impl BorderStyle<'a>, item: impl Draw<'a, Tui>
) -> impl Draw<'a, Tui> {
pub fn border (on: bool, style: impl BorderStyle, item: impl Draw<Tui>) -> impl Draw<Tui> {
let content = item.pad_wh(1, 1);
let outline = when(on, draw(move|to: &mut Tui|{
let XYWH(x, y, w, h) = to.area();
@ -35,7 +33,7 @@ macro_rules! border {
$nw:literal $n:literal $ne:literal $w:literal $e:literal $sw:literal $s:literal $se:literal
$($x:tt)*
}),+) => {$(
impl<'a> BorderStyle<'a> for $T {
impl BorderStyle for $T {
const NW: &'static str = $nw;
const N: &'static str = $n;
const NE: &'static str = $ne;
@ -49,7 +47,7 @@ macro_rules! border {
}
#[derive(Copy, Clone)] pub struct $T(pub bool, pub Style);
//impl Layout<Tui> for $T {}
impl<'a> Draw<'a, Tui> for $T {
impl Draw<Tui> for $T {
fn draw (&self, to: &mut Tui) -> Perhaps<XYWH<u16>> {
when(self.enabled(), draw(|to: &mut Tui|BorderStyle::draw(*self, to).map(Some))).draw(to)
}
@ -163,7 +161,7 @@ border! {
}
}
pub trait BorderStyle<'a>: Draw<'a, Tui> + Copy {
pub trait BorderStyle: Draw<Tui> + Copy {
fn enabled (&self) -> bool;
fn border_n (&self) -> &str { Self::N }
fn border_s (&self) -> &str { Self::S }
@ -174,7 +172,7 @@ pub trait BorderStyle<'a>: Draw<'a, Tui> + Copy {
fn border_sw (&self) -> &str { Self::SW }
fn border_se (&self) -> &str { Self::SE }
#[inline] fn draw (self, to: &mut Tui) -> Usually<XYWH<u16>> {
#[inline] fn draw <'a> (self, to: &mut Tui) -> Usually<XYWH<u16>> {
if self.enabled() {
self.draw_h(to, None)?;
self.draw_v(to, None)?;

View file

@ -1,4 +1,4 @@
use ::dizzle::*;
use ::dizzle::{Language, Symbol, Usually};
use ::crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState};
/// TUI key spec.
@ -70,7 +70,7 @@ impl TuiKey {
}
#[cfg(feature = "lang")]
pub fn from_dsl <'a> (dsl: &'a impl Language<'a>) -> UsuallyRef<'a, Self> {
pub fn from_dsl (dsl: impl Language) -> Usually<Self> {
if let Some(word) = dsl.word()? {
let word = word.trim();
Ok(if word == ":char" {

View file

@ -2,30 +2,36 @@ use crate::*;
use Color::*;
fn_kw_layout_tui!(kw_tui_fg |state, output, expr| {
let thunk = draw(move|screen|ok_flat(expr.nth(2)?.map(|x: &'a str|state.interpret(screen, x))));
Ok(matches!(expr.head()?, Some("fg")).then(||{
if let Some(color) = state.namespace(expr.nth(1)?.expect("fg: expected arg 0 (color)"))? {
fg(color, thunk).draw(output)
let args = expr.tail();
let arg0 = args.head();
let tail0 = args.tail();
let arg1 = tail0.head();
if let Some(color) = state.namespace(arg0?.expect("fg: expected arg 0 (color)"))? {
fg(color, draw(move|to: &mut Tui|state.interpret(to, &arg1))).draw(output)
} else {
return Err(format!("fg: {:?}: not a color", expr.nth(1)).into())
return Err(format!("fg: {arg0:?}: not a color").into())
}
}).transpose()?.flatten())
});
fn_kw_layout_tui!(kw_tui_bg |state, output, expr| {
let thunk = draw(move|screen|ok_flat(expr.nth(2)?.map(|x: &'a str|state.interpret(screen, x))));
Ok(matches!(expr.head()?, Some("bg")).then(||{
if let Some(color) = state.namespace(expr.nth(1)?.expect("bg: expected arg 0 (color)"))? {
bg(color, thunk).draw(output)
let args = expr.tail();
let arg0 = args.head();
let tail0 = args.tail();
let arg1 = tail0.head();
if let Some(color) = state.namespace(arg0?.expect("bg: expected arg 0 (color)"))? {
bg(color, draw(move|to: &mut Tui|state.interpret(to, &arg1))).draw(output)
} else {
return Err(format!("bg: {:?}: not a color", expr.nth(1)?).into())
return Err(format!("bg: {arg0:?}: not a color").into())
}
}).transpose()?.flatten())
});
impl Tui {
pub fn eval_color_expr <'a> (state: &impl Namespace<u8>, src: &'a impl Language<'a>)
-> Result<Option<Color>, Box<dyn Error + 'a>>
pub fn eval_color_expr (state: &impl Namespace<u8>, src: impl Language)
-> Perhaps<Color>
{
if let Some(expr) = src.expr()? {
match (expr.head()?, expr.tail()?) {
@ -57,9 +63,7 @@ impl Tui {
}
/// Apply foreground color.
pub const fn fg <'a> (fg: Color, item: impl Draw<'a, Tui>)
-> impl Draw<'a, Tui>
{
pub const fn fg (fg: Color, item: impl Draw<Tui>) -> impl Draw<Tui> {
draw(move|to: &mut Tui|{
if let Some(size) = to.size(None, &item)? {
to.draw(Some(size), draw(|to: &mut Tui|{
@ -73,9 +77,7 @@ pub const fn fg <'a> (fg: Color, item: impl Draw<'a, Tui>)
}
/// Apply background color.
pub const fn bg <'a> (bg: Color, item: impl Draw<'a, Tui>)
-> impl Draw<'a, Tui>
{
pub const fn bg (bg: Color, item: impl Draw<Tui>) -> impl Draw<Tui> {
draw(move|to: &mut Tui|{
if let Some(size) = to.size(None, &item)? {
let _ = to.draw(Some(size), draw(|to: &mut Tui|{
@ -88,9 +90,7 @@ pub const fn bg <'a> (bg: Color, item: impl Draw<'a, Tui>)
})
}
pub const fn fg_bg <'a> (fg: Color, bg: Color, item: impl Draw<'a, Tui>)
-> impl Draw<'a, Tui>
{
pub const fn fg_bg (fg: Color, bg: Color, item: impl Draw<Tui>) -> impl Draw<Tui> {
draw(move|to: &mut Tui|{
if let Some(size) = to.size(None, &item)? {
let _ = to.draw(Some(size), draw(|to: &mut Tui|{
@ -104,16 +104,14 @@ pub const fn fg_bg <'a> (fg: Color, bg: Color, item: impl Draw<'a, Tui>)
}
/// Draw contents with modifier applied.
pub const fn modify <'a> (on: bool, modifier: Modifier, item: impl Draw<'a, Tui>)
-> impl Draw<'a, Tui>
{
pub const fn modify (on: bool, modifier: Modifier, item: impl Draw<Tui>) -> impl Draw<Tui> {
draw(move|to: &mut Tui|{
fill_mod(on, modifier).draw(to)?;
item.draw(to)
})
}
pub const fn fill_mod <'a> (on: bool, modifier: Modifier) -> impl Draw<'a, Tui> {
pub const fn fill_mod (on: bool, modifier: Modifier) -> impl Draw<Tui> {
draw(move|to: &mut Tui|Ok(Some({
if on {
to.update(&|cell,_,_|cell.modifier.insert(modifier))
@ -124,11 +122,11 @@ pub const fn fill_mod <'a> (on: bool, modifier: Modifier) -> impl Draw<'a, Tui>
}
/// Draw contents with bold modifier applied.
pub const fn bold <'a> (on: bool, item: impl Draw<'a, Tui>) -> impl Draw<'a, Tui> {
pub const fn bold (on: bool, item: impl Draw<Tui>) -> impl Draw<Tui> {
modify(on, Modifier::BOLD, item)
}
pub const fn fill_ul <'a> (color: Option<Color>) -> impl Draw<'a, Tui> {
pub const fn fill_ul (color: Option<Color>) -> impl Draw<Tui> {
draw(move|to: &mut Tui|Ok(Some(if let Some(color) = color {
to.update(&|cell,_,_|{
cell.modifier.insert(Modifier::UNDERLINED);

View file

@ -5,9 +5,7 @@ use crate::*;
/// ```
/// /// TODO
/// ```
pub fn phat <'a> (
w: u16, h: u16, [fg, bg, hi, lo]: [Color;4], draw: impl Draw<'a, Tui>
) -> impl Draw<'a, Tui> {
pub fn phat (w: u16, h: u16, [fg, bg, hi, lo]: [Color;4], draw: impl Draw<Tui>) -> impl Draw<Tui> {
let top = phat_lo(bg, hi).exact_h(1);
let low = phat_hi(bg, lo).exact_h(1);
let draw = fg_bg(fg, bg, draw);
@ -19,11 +17,11 @@ pub const LO: &'static str = "▄";
pub const HI: &'static str = "";
/// A phat line
fn phat_lo <'a> (fg: Color, bg: Color) -> impl Draw<'a, Tui> {
fn phat_lo (fg: Color, bg: Color) -> impl Draw<Tui> {
fg_bg(fg, bg, x_repeat(LO)).exact_h(1)
}
/// A phat line
fn phat_hi <'a> (fg: Color, bg: Color) -> impl Draw<'a, Tui> {
fn phat_hi (fg: Color, bg: Color) -> impl Draw<Tui> {
fg_bg(fg, bg, x_repeat(HI)).exact_h(1)
}

View file

@ -6,7 +6,7 @@ pub const ICON_INC_V: &[char] = &['▼'];
pub const ICON_DEC_H: &[char] = &[' ', '🞀', ' '];
pub const ICON_INC_H: &[char] = &[' ', '🞂', ' '];
pub fn x_scroll <'a> () -> impl Draw<'a, Tui> {
pub fn x_scroll () -> impl Draw<Tui> {
draw(|to: &mut Tui|{
let XYWH(x1, y1, w, _h) = to.area();
let x2 = x1 + w;
@ -37,7 +37,7 @@ pub fn x_scroll <'a> () -> impl Draw<'a, Tui> {
})
}
pub fn y_scroll <'a> () -> impl Draw<'a, Tui> {
pub fn y_scroll () -> impl Draw<Tui> {
draw(|to: &mut Tui|{
let XYWH(x1, y1, _w, h) = to.area();
let y2 = y1 + h;