From 13c886d9e05a8df712461d44025196ae9be51b78 Mon Sep 17 00:00:00 2001 From: facile pop culture reference Date: Fri, 10 Jul 2026 18:24:44 +0300 Subject: [PATCH] add origin and align methods to Layout trait --- examples/tui_00.rs | 10 +-- src/draw.rs | 36 ++++---- src/draw/align.rs | 31 ------- src/draw/area.rs | 17 ---- src/draw/azimuth.rs | 12 --- src/draw/coord.rs | 2 +- src/draw/iter.rs | 9 -- src/draw/layout.rs | 192 ++++++++++++++++++++++++++++++++++++----- src/draw/origin.rs | 42 --------- src/draw/split.rs | 33 +++++-- src/draw/thunk.rs | 14 +-- src/draw/xywh.rs | 4 +- src/eval.rs | 124 +++++++++++++++----------- src/sing.rs | 2 +- src/sing/jack.rs | 6 +- src/sing/jack_event.rs | 4 +- src/term.rs | 2 +- src/term/output.rs | 41 +++++---- 18 files changed, 333 insertions(+), 248 deletions(-) delete mode 100644 src/draw/align.rs delete mode 100644 src/draw/area.rs delete mode 100644 src/draw/azimuth.rs delete mode 100644 src/draw/origin.rs diff --git a/examples/tui_00.rs b/examples/tui_00.rs index 1ac73c5..8aedd9f 100644 --- a/examples/tui_00.rs +++ b/examples/tui_00.rs @@ -1,7 +1,7 @@ use ::{ std::{io::stdout, sync::{Arc, RwLock}}, - tengri::{*, term::*, lang::*, keys::*, draw::*, space::*, lang::*}, ratatui::style::Color, + tengri::{*, lang::*}, }; tui_main!(State { @@ -18,10 +18,10 @@ tui_view!(|self: State| { let wh = (self.size.w(), self.size.h()); let src = VIEWS.get(self.cursor).unwrap_or(&""); let heading = format!("State {}/{} in {:?}", index, VIEWS.len(), &wh); - let title = bg(Color::Rgb(60, 10, 10), y_push(1, align_n(heading))); - let code = bg(Color::Rgb(10, 60, 10), y_push(2, align_n(format!("{}", src)))); + let title = bg(Color::Rgb(60, 10, 10), heading.align_n().push_y(1)); + let code = bg(Color::Rgb(10, 60, 10), format!("{}", src).align_n().push_y(2)); //let content = ;//();//bg(Color::Rgb(10, 10, 60), View(self, CstIter::new(src))); - let widget = thunk(move|to: &mut Tui|self.interpret(to, &src)); + let widget = thunk(move|to: &mut Tui|self.interpret(to, &src).map(Some)); self.size.of(south(title, north(code, widget))) }); @@ -32,7 +32,7 @@ struct State { /** User-controllable value. */ cursor: usize, /** Rendered window size. */ - size: crate::space::Size, + size: Sizer, } impl Interpret> for State { diff --git a/src/draw.rs b/src/draw.rs index c5e895a..64738bd 100644 --- a/src/draw.rs +++ b/src/draw.rs @@ -3,32 +3,34 @@ use crate::*; /// Output target. /// /// ``` -/// use tengri::{*, draw::*}; -/// -/// struct TestOut>(T); -/// -/// impl> Screen for TestOut { +/// use tengri::*; +/// struct TestOut { w: u16, h: u16 }; +/// impl Wide for TestOut {} +/// impl Tall for TestOut {} +/// impl Xy for TestOut { fn x (&self) -> u16 { 0 } fn y (&self) -> u16 { 0 } } +/// impl Screen for TestOut { /// type Unit = u16; -/// fn show + ?Sized> (&mut self, _: D) { +/// fn show > (&mut self, _: D) -> Drawn { /// println!("placed"); -/// () +/// Ok(None) /// } /// } /// -/// impl_draw!(|self: String, to: TestOut|{ -/// to.area_mut().set_w(self.len() as u16); +/// impl_draw!(|self: String, to: TestOut|{ +/// to.w = self.len() as u16; +/// Ok(None) /// }); /// ``` pub trait Screen: Xy + Wh + Send + Sync + Sized { type Unit: Coord; /// Render drawable in subarea specified by `area` - fn show <'t, T: Draw> (&mut self, content: T) -> Perhaps>; + fn show > (&mut self, content: T) -> Perhaps>; } /// Implement the [Draw] trait for a particular drawable and [Screen]. /// /// ``` -/// use tengri::{*, draw::*, term::*}; +/// use tengri::*; /// struct MyDrawable; /// impl_draw!(|self: MyDrawable, to: Tui|{ /// todo!("your draw logic") @@ -60,7 +62,7 @@ pub trait Screen: Xy + Wh + Send + Sync + Sized { /// a [Draw]able. /// /// ``` -/// use tengri::{*, draw::*, term::*}; +/// use tengri::*; /// struct MyWidget(bool); /// impl Draw for MyWidget { /// fn draw (self, to: &mut Tui) -> Perhaps> { @@ -106,6 +108,12 @@ pub trait View { fn view (&self) -> impl Draw; } +impl View for () { + fn view (&self) -> impl Draw { + () + } +} + impl> Draw for &V { fn draw (self, to: &mut T) -> Perhaps> { self.view().draw(to) @@ -114,15 +122,11 @@ impl> Draw for &V { features! { "draw": [ - align, - area, - azimuth, color, coord, iter, layout, lrtb, - origin, sizer, space, split, diff --git a/src/draw/align.rs b/src/draw/align.rs deleted file mode 100644 index 8e4ebb1..0000000 --- a/src/draw/align.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::*; - -pub struct Align(Option, T); - -impl_draw!(,>|self: Align, to: S|{ - todo!() -}); - -/// ``` -/// use tengri::*; -/// let _ = align(None, "unaligned"); -/// let _ = align(Azimuth::SE, "southeast"); -/// let _ = align(Some(Azimuth::SE), "southeast"); -/// ``` -pub fn align , U: Into>> (origin: U, it: T) -> Align { - Align(origin.into(), it) -} - -pub fn align_c > (it: T) -> Align { Align(Some(Azimuth::C), it) } -pub fn align_x > (it: T) -> Align { Align(Some(Azimuth::X), it) } -pub fn align_y > (it: T) -> Align { Align(Some(Azimuth::Y), it) } - -pub fn align_n > (it: T) -> Align { Align(Some(Azimuth::N), it) } -pub fn align_s > (it: T) -> Align { Align(Some(Azimuth::S), it) } -pub fn align_e > (it: T) -> Align { Align(Some(Azimuth::E), it) } -pub fn align_w > (it: T) -> Align { Align(Some(Azimuth::W), it) } - -pub fn align_ne > (it: T) -> Align { Align(Some(Azimuth::NE), it) } -pub fn align_se > (it: T) -> Align { Align(Some(Azimuth::SE), it) } -pub fn align_nw > (it: T) -> Align { Align(Some(Azimuth::NW), it) } -pub fn align_sw > (it: T) -> Align { Align(Some(Azimuth::SW), it) } diff --git a/src/draw/area.rs b/src/draw/area.rs deleted file mode 100644 index 58e8b6c..0000000 --- a/src/draw/area.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::*; - -/// ``` -/// use tengri::*; -/// let _ = area(None, "unareaed"); -/// let _ = area([1, 2, 3, 4], "southeast"); -/// let _ = area(Some([1, 2, 3, 4]), "southeast"); -/// ``` -pub fn area , U: Into>>> ( - origin: U, it: T -) -> Area { - Area(origin.into(), it) -} - -pub struct Area>(Option>, T); - -impl_draw!(,>|self: Area, to: S|{ todo!() }); diff --git a/src/draw/azimuth.rs b/src/draw/azimuth.rs deleted file mode 100644 index de72abc..0000000 --- a/src/draw/azimuth.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::*; - -/// Where is [0, 0] located? -/// -/// ``` -/// use tengri::draw::Azimuth; -/// let _ = Azimuth::NW.align(()); -/// ``` -#[cfg_attr(test, derive(Arbitrary))] -#[derive(Debug, Copy, Clone, Default)] pub enum Azimuth { - #[default] C, X, Y, NW, N, NE, E, SE, S, SW, W -} diff --git a/src/draw/coord.rs b/src/draw/coord.rs index f5e6974..40d784d 100644 --- a/src/draw/coord.rs +++ b/src/draw/coord.rs @@ -6,7 +6,7 @@ use super::*; /// FIXME: Use AsRef/AsMut? /// /// ``` -/// use tengri::draw::Coord; +/// use tengri::*; /// let a: u16 = Coord::zero(); /// let b: u16 = a.plus(1); /// let c: u16 = a.minus(2); diff --git a/src/draw/iter.rs b/src/draw/iter.rs index 1e5fa38..1bbdcfa 100644 --- a/src/draw/iter.rs +++ b/src/draw/iter.rs @@ -15,15 +15,6 @@ pub fn iter_once <'a, S: Screen, D: 'a, U: Draw> ( } /// Iterate over a collection of various [Draw]ables: -/// -/// ``` -/// use tengri::draw::{Origin::*, Split::*}; -/// let _ = Below.iter([ -/// NW.align(W(15).max(W(10).min("Leftbar"))), -/// NE.align(W(12).max(W(10).min("Rightbar"))), -/// Center.align(W(40).max(H(20.max("Center")))) -/// ].iter(), |x|x); -/// ``` pub fn iter_dyn < S: Screen, // Target screen D, // Input doesn't need to be [Draw]able, output does diff --git a/src/draw/layout.rs b/src/draw/layout.rs index d1728bb..71dca39 100644 --- a/src/draw/layout.rs +++ b/src/draw/layout.rs @@ -70,11 +70,96 @@ pub trait Layout: Draw + Sized { fn push_xy >> (self, x: N, y: N) -> impl Draw { Push::XY(self, x.into(), y.into()) } + + fn align (self, azimuth: impl Into>) -> impl Draw { + Align(azimuth.into(), self) + } + fn align_c (self) -> impl Draw { + Align(Some(Azimuth::C), self) + } + fn align_x (self) -> impl Draw { + Align(Some(Azimuth::X), self) + } + fn align_y (self) -> impl Draw { + Align(Some(Azimuth::Y), self) + } + + fn align_n (self) -> impl Draw { + Align(Some(Azimuth::N), self) + } + fn align_s (self) -> impl Draw { + Align(Some(Azimuth::S), self) + } + fn align_e (self) -> impl Draw { + Align(Some(Azimuth::E), self) + } + fn align_w (self) -> impl Draw { + Align(Some(Azimuth::W), self) + } + + fn align_ne (self) -> impl Draw { + Align(Some(Azimuth::NE), self) + } + fn align_se (self) -> impl Draw { + Align(Some(Azimuth::SE), self) + } + fn align_nw (self) -> impl Draw { + Align(Some(Azimuth::NW), self) + } + fn align_sw (self) -> impl Draw { + Align(Some(Azimuth::SW), self) + } + + fn origin (self, azimuth: impl Into>) -> impl Draw { + Origin(azimuth.into(), self) + } + fn origin_c (self) -> impl Draw { + Origin(Some(Azimuth::C), self) + } + fn origin_x (self) -> impl Draw { + Origin(Some(Azimuth::X), self) + } + fn origin_y (self) -> impl Draw { + Origin(Some(Azimuth::Y), self) + } + + fn origin_n (self) -> impl Draw { + Origin(Some(Azimuth::N), self) + } + fn origin_s (self) -> impl Draw { + Origin(Some(Azimuth::S), self) + } + fn origin_e (self) -> impl Draw { + Origin(Some(Azimuth::E), self) + } + fn origin_w (self) -> impl Draw { + Origin(Some(Azimuth::W), self) + } + + fn origin_ne (self) -> impl Draw { + Origin(Some(Azimuth::NE), self) + } + fn origin_se (self) -> impl Draw { + Origin(Some(Azimuth::SE), self) + } + fn origin_nw (self) -> impl Draw { + Origin(Some(Azimuth::NW), self) + } + fn origin_sw (self) -> impl Draw { + Origin(Some(Azimuth::SW), self) + } } impl> Layout for T {} /// Use whole drawing area along one or both axes. +/// +/// ``` +/// use tengri::Layout; +/// let _ = "".full_w(); +/// let _ = "".full_h(); +/// let _ = "".full_wh(); +/// ``` pub enum Full> { __(PhantomData), W(I), @@ -82,14 +167,17 @@ pub enum Full> { WH(I), } -impl_draw!(,>|self: Full, to: T|{ +impl_draw!(,>|self: Full, _to: T|{ todo!() }); -/// Only draw content if area is above a certain size. +/// Move content in the positive direction of one or both axes. /// /// ``` -/// let min = tengri::wh_min(3, 5, "Hello"); // 5x5 +/// use tengri::Layout; +/// let _ = "".push_x(1); +/// let _ = "".push_y(1); +/// let _ = "".push_xy(1, 1); /// ``` pub enum Push, X: Into>> { __(PhantomData), @@ -98,14 +186,17 @@ pub enum Push, X: Into>> { XY(I, X, X), } -impl_draw!(, X: Into>,>|self: Push, to: T|{ +impl_draw!(, X: Into>,>|self: Push, _to: T|{ todo!() }); -/// Only draw content if area is above a certain size. +/// Move content in the negative direction of one or both axes. /// /// ``` -/// let min = tengri::wh_min(3, 5, "Hello"); // 5x5 +/// use tengri::Layout; +/// let _ = "".pull_x(1); +/// let _ = "".pull_y(1); +/// let _ = "".pull_xy(1, 1); /// ``` pub enum Pull, X: Into>> { __(PhantomData), @@ -114,14 +205,17 @@ pub enum Pull, X: Into>> { XY(I, X, X), } -impl_draw!(, X: Into>,>|self: Pull, to: T|{ +impl_draw!(, X: Into>,>|self: Pull, _to: T|{ todo!() }); /// Only draw content if area is above a certain size. /// /// ``` -/// let min = tengri::wh_min(3, 5, "Hello"); // 5x5 +/// use tengri::Layout; +/// let _ = "".min_w(1); +/// let _ = "".min_h(1); +/// let _ = "".min_wh(1, 1); /// ``` pub enum Min, X: Into>> { __(PhantomData), @@ -130,16 +224,17 @@ pub enum Min, X: Into>> { WH(I, X, X), } -impl_draw!(, X: Into>,>|self: Min, to: T|{ +impl_draw!(, X: Into>,>|self: Min, _to: T|{ todo!() }); /// Set maximum size of of drawing area. /// /// ``` -/// let max = tengri::w_max(3, "Hello"); -/// let max = tengri::w_max(None, "Hello"); -/// let max = tengri::w_max(Some(3), "Hello"); +/// use tengri::Layout; +/// let _ = "".max_w(1); +/// let _ = "".max_h(1); +/// let _ = "".max_wh(1, 1); /// ``` pub enum Max, X: Into>> { __(PhantomData), @@ -148,16 +243,17 @@ pub enum Max, X: Into>> { WH(I, X, X), } -impl_draw!(, X: Into>,>|self: Max, to: T|{ +impl_draw!(, X: Into>,>|self: Max, _to: T|{ todo!() }); /// Set size of of drawing area. /// /// ``` -/// let max = tengri::w_max(3, "Hello"); -/// let max = tengri::w_max(None, "Hello"); -/// let max = tengri::w_max(Some(3), "Hello"); +/// use tengri::Layout; +/// let _ = "".exact_w(1); +/// let _ = "".exact_h(1); +/// let _ = "".exact_wh(1, 1); /// ``` pub enum Exact, X: Into>> { __(PhantomData), @@ -166,16 +262,17 @@ pub enum Exact, X: Into>> { WH(I, X, X), } -impl_draw!(, X: Into>,>|self: Exact, to: T|{ +impl_draw!(, X: Into>,>|self: Exact, _to: T|{ todo!() }); /// Define inner drawing area. /// /// ``` -/// let max = tengri::w_max(3, "Hello"); -/// let max = tengri::w_max(None, "Hello"); -/// let max = tengri::w_max(Some(3), "Hello"); +/// use tengri::Layout; +/// let _ = "".pad_w(1); +/// let _ = "".pad_h(1); +/// let _ = "".pad_wh(1, 1); /// ``` pub enum Pad, X: Into>> { __(PhantomData), @@ -184,6 +281,59 @@ pub enum Pad, X: Into>> { WH(I, X, X), } -impl_draw!(, X: Into>,>|self: Pad, to: T|{ +impl_draw!(, X: Into>,>|self: Pad, _to: T|{ todo!() }); + +pub struct Align(Option, T); + +impl_draw!(,>|self: Align, _to: S|{ + todo!() +}); + +/// Where is [0, 0] located? +/// +/// ``` +/// use tengri::*; +/// use Azimuth::*; +/// let _ = "".align(NW); +/// ``` +#[cfg_attr(test, derive(Arbitrary))] +#[derive(Debug, Copy, Clone, Default)] pub enum Azimuth { + #[default] C, X, Y, NW, N, NE, E, SE, S, SW, W +} + +/// ``` +/// use tengri::*; +/// let _ = area(None, "unareaed"); +/// let _ = area(XYWH(1, 2, 3, 4), "southeast"); +/// let _ = area(Some(XYWH(1, 2, 3, 4)), "southeast"); +/// ``` +pub fn area , U: Into>>> ( + origin: U, it: T +) -> Area { + Area(origin.into(), it) +} + +pub struct Area>(Option>, T); + +impl_draw!(,>|self: Area, _to: S|{ + todo!() +}); + +pub struct Origin(Option, T); + +impl_draw!(,>|self: Origin, _to: S|{ + todo!() +}); + +/// Something that has `[0, 0]` at a particular point. +pub trait HasOrigin { + fn origin (&self) -> Azimuth; +} + +impl> HasOrigin for T { + fn origin (&self) -> Azimuth { + *self.as_ref() + } +} diff --git a/src/draw/origin.rs b/src/draw/origin.rs deleted file mode 100644 index da47996..0000000 --- a/src/draw/origin.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::*; - -pub struct Origin(Option, T); - -impl_draw!(,>|self: Origin, to: S|{ - todo!() -}); - -/// ``` -/// use tengri::*; -/// let _ = origin(None, "origin unspecified"); -/// let _ = origin(Azimuth::SE, "southeast"); -/// let _ = origin(Some(Azimuth::SE), "southeast"); -/// ``` -pub fn origin , U: Into>> (origin: U, it: T) -> Origin { - Origin(origin.into(), it) -} - -pub fn origin_c > (it: T) -> Origin { Origin(Some(Azimuth::C), it) } -pub fn origin_x > (it: T) -> Origin { Origin(Some(Azimuth::X), it) } -pub fn origin_y > (it: T) -> Origin { Origin(Some(Azimuth::Y), it) } - -pub fn origin_n > (it: T) -> Origin { Origin(Some(Azimuth::N), it) } -pub fn origin_s > (it: T) -> Origin { Origin(Some(Azimuth::S), it) } -pub fn origin_e > (it: T) -> Origin { Origin(Some(Azimuth::E), it) } -pub fn origin_w > (it: T) -> Origin { Origin(Some(Azimuth::W), it) } - -pub fn origin_ne > (it: T) -> Origin { Origin(Some(Azimuth::NE), it) } -pub fn origin_se > (it: T) -> Origin { Origin(Some(Azimuth::SE), it) } -pub fn origin_nw > (it: T) -> Origin { Origin(Some(Azimuth::NW), it) } -pub fn origin_sw > (it: T) -> Origin { Origin(Some(Azimuth::SW), it) } - -/// Something that has `[0, 0]` at a particular point. -pub trait HasOrigin { - fn origin (&self) -> Azimuth; -} - -impl> HasOrigin for T { - fn origin (&self) -> Azimuth { - *self.as_ref() - } -} diff --git a/src/draw/split.rs b/src/draw/split.rs index 3d64a75..37ef914 100644 --- a/src/draw/split.rs +++ b/src/draw/split.rs @@ -39,20 +39,20 @@ pub const fn below (a: impl Draw, b: impl Draw) -> impl Draw (&self, a: impl Draw, b: impl Draw) -> impl Draw { thunk(move|to: &mut T|{ let (area_a, area_b) = to.xywh().split_half(self); let (origin_a, origin_b) = self.origins(); - let a = align(origin_a, a); - let b = align(origin_b, b); + let a = a.align(origin_a); + let b = b.align(origin_b); match self { Self::Below => { to.show(area(area_b, b))?; @@ -95,6 +95,21 @@ impl Split { } } + ///// ``` + ///// use tengri::*; + ///// let _ = Split::Below.iter([ + ///// "Leftbar" + ///// .min_w(10).max_w(15).align(Azimuth::NW), + ///// "Rightbar" + ///// .min_w(10).max_w(12).align(Azimuth::NE), + ///// "Center" + ///// .min_w(20).max_w(40).align(Azimuth::C), + ///// ].iter()); + ///// ``` + //pub fn iter > (&self, _: impl Iterator) { + //todo!() + //} + } #[macro_export] macro_rules! north { diff --git a/src/draw/thunk.rs b/src/draw/thunk.rs index 7e98b20..af27f5b 100644 --- a/src/draw/thunk.rs +++ b/src/draw/thunk.rs @@ -12,9 +12,9 @@ implPerhaps>> Draw for Thunk impl tengri::draw::Draw { -/// thunk(|to: &mut Tui|Ok(to.1)) +/// # use tengri::*; +/// # fn test () -> impl Draw { +/// thunk(|to: &mut Tui|Ok(Some(to.1))) /// # } /// ``` pub const fn thunk Perhaps>> ( @@ -26,8 +26,8 @@ pub const fn thunk Perhaps>> ( /// Only render when condition is true. /// /// ``` -/// # use tengri::{draw::*, term::*}; -/// # fn test () -> impl tengri::draw::Draw { +/// # use tengri::*; +/// # fn test () -> impl Draw { /// when(true, "Yes") /// # } /// ``` @@ -38,8 +38,8 @@ pub const fn when (condition: bool, draw: impl Draw) -> impl Draw /// Render one thing if a condition is true and another false. /// /// ``` -/// # use tengri::{draw::*, term::*}; -/// # fn test () -> impl tengri::draw::Draw { +/// # use tengri::*; +/// # fn test () -> impl Draw { /// either(true, "Yes", "No") /// # } /// ``` diff --git a/src/draw/xywh.rs b/src/draw/xywh.rs index ebe866f..5b308d1 100644 --- a/src/draw/xywh.rs +++ b/src/draw/xywh.rs @@ -31,9 +31,9 @@ pub trait Tall { /// Point with size. /// /// ``` -/// use tengri::space::{XY, XYWH}; +/// # use tengri::*; /// let xywh = XYWH(0u16, 0, 0, 0); -/// assert_eq!(XYWH(10u16, 10, 20, 20).center(), XY(20, 20)); +/// assert_eq!(XYWH(10u16, 10, 20, 20).center(), (20, 20)); /// ``` /// /// * [ ] TODO: origin field (determines at which corner/side is X0 Y0) diff --git a/src/eval.rs b/src/eval.rs index 6e6b8c4..423cb64 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -63,18 +63,19 @@ macro_rules! eval_enum (( /// Interpret layout operation. /// /// ``` -/// # use tengri::{lang::*, draw::*, eval::*, term::*}; +/// # use tengri::{*, lang::*}; /// /// struct State {/*app-specific*/} -/// impl<'b> Namespace<'b, bool> for State {} /// impl<'b> Namespace<'b, u16> for State {} -/// impl Interpret> for State {} +/// impl<'b> Namespace<'b, bool> for State {} +/// impl<'b> Namespace<'b, Option> for State {} +/// impl Interpret>> for State {} /// /// # fn main () -> tengri::Usually<()> { /// let state = State {}; /// let mut target = Tui::new(80, 25); /// eval_view(&state, &mut target, &"")?; -/// eval_view(&state, &mut target, &"(when true (text hello))")?; +/// eval_view(&state, &mut target, &"(whe true (text hello))")?; /// eval_view(&state, &mut target, &"(either true (text hello) (text world))")?; /// // TODO test all /// # Ok(()) } @@ -108,43 +109,33 @@ pub fn eval_view <'a, O: Screen + 'a, S> ( Some("when") => when( state.namespace(arg0?)?.unwrap(), - thunk(move|output: &mut O|{ - state.interpret(output, &arg1) - }) + thunk(move|output: &mut O|{state.interpret(output, &arg1)}) ).draw(output), Some("either") => either( state.namespace(arg0?)?.unwrap(), - thunk(move|output: &mut O|{ - state.interpret(output, &arg1) - }), - thunk(move|output: &mut O|{ - state.interpret(output, &arg2) - }), + thunk(move|output: &mut O|{state.interpret(output, &arg1)}), + thunk(move|output: &mut O|{state.interpret(output, &arg2)}), ).draw(output), // Second `frags.next()` calls returns the namespace member. Some("bsp") => { - let direction = eval_enum!("bsp", output, state, frags.next(), arg0, Split { + eval_enum!("bsp", output, state, frags.next(), arg0, Split { "n" => North, "s" => South, "e" => East, "w" => West, "a" => Above, "b" => Below - }); - direction.half( - thunk(move|output: &mut O|{ - state.interpret(output, &arg0) - }), - thunk(move|output: &mut O|{ - state.interpret(output, &arg1) - }), + }).half( + thunk(move|output: &mut O|{state.interpret(output, &arg0)}), + thunk(move|output: &mut O|{state.interpret(output, &arg1)}), ).draw(output) }, Some("align") => { - let alignment = eval_enum!("align", output, state, frags.next(), arg0, Azimuth { + let content = thunk(move|output: &mut O|{state.interpret(output, &arg0)}); + content.align(eval_enum!("align", output, state, frags.next(), arg0, Azimuth { "c" => C, "n" => N, "s" => S, @@ -152,11 +143,7 @@ pub fn eval_view <'a, O: Screen + 'a, S> ( "w" => W, "x" => X, "y" => Y - }); - let content = thunk(move|output: &mut O|{ - state.interpret(output, &arg0) - }); - align(alignment, content).draw(output) + })).draw(output) }, Some("exact") => eval_xy!( @@ -187,21 +174,50 @@ pub fn eval_view <'a, O: Screen + 'a, S> ( /// Interpret TUI-specific layout operation. /// /// ``` -/// use tengri::{lang::*, term::*, eval::*, ratatui::prelude::Color}; +/// use tengri::{*, lang::*, ratatui::prelude::Color}; /// +/// #[namespace(bool {})] +/// #[namespace(u8: try_to_u8)] +/// #[namespace(u16: try_to_u16)] +/// #[namespace(Color: try_to_color)] +/// #[interpret(Tui -> Option>: try_eval_tui)] /// struct State; -/// impl<'b> Namespace<'b, bool> for State {} -/// impl<'b> Namespace<'b, u16> for State {} -/// impl<'b> Namespace<'b, Color> for State {} -/// impl Interpret for State {} +/// tengri::lang::primitive!(u8: try_to_u8); +/// tengri::lang::primitive!(u16: try_to_u16); +/// tengri::lang::namespace!(State: bool { +/// }); +/// tengri::lang::namespace!(State: u8 { +/// literal = |x|try_to_u8(x); +/// }); +/// tengri::lang::namespace!(State: u16 { +/// literal = |x|try_to_u16(x); +/// }); +/// tengri::lang::namespace!(State: Color { +/// expression = |_state| { +/// "g" (x: u8) => { ItemTheme::G[x as usize].base.term } +/// }; +/// }); +/// tengri::lang::interpret!(|self: State, context: Tui, lang|->Option>{ +/// expression = { +/// "text" (...rest) => { todo!() } +/// } +/// }); +/// impl Interpret>> for State { +/// fn interpret_expr <'a> (&'a self, _: &mut Tui, lang: &'a impl Expression) +/// -> Usually>> +/// { +/// Ok(None) +/// } +/// } +/// /// # fn main () -> tengri::Usually<()> { /// let state = State; -/// let mut out = Tui::default(); -/// tengri::eval::eval_view_tui(&state, &mut out, "")?; -/// tengri::eval::eval_view_tui(&state, &mut out, "text Hello world!")?; -/// tengri::eval::eval_view_tui(&state, &mut out, "fg (g 0) (text Hello world!)")?; -/// tengri::eval::eval_view_tui(&state, &mut out, "bg (g 2) (text Hello world!)")?; -/// tengri::eval::eval_view_tui(&state, &mut out, "(bg (g 3) (fg (g 4) (text Hello world!)))")?; +/// let mut out = Tui::new(80, 25); +/// eval_view_tui(&state, &mut out, "")?; +/// eval_view_tui(&state, &mut out, "text Hello world!")?; +/// eval_view_tui(&state, &mut out, "fg (g 0) (text Hello world!)")?; +/// eval_view_tui(&state, &mut out, "bg (g 2) (text Hello world!)")?; +/// eval_view_tui(&state, &mut out, "(bg (g 3) (fg (g 4) (text Hello world!)))")?; /// # Ok(()) } /// ``` pub fn eval_view_tui <'a, S> ( @@ -231,22 +247,28 @@ pub fn eval_view_tui <'a, S> ( Some("fg") => { let arg0 = arg0?.expect("fg: expected arg 0 (color)"); - let color = Namespace::namespace(state, arg0)?.unwrap_or_else(||panic!("fg: {arg0:?}: not a color")); - output.show(fg(color, thunk(move|output: &mut Tui|{ - state.interpret(output, &arg1)?; - // FIXME?: don't max out the used area? - Ok(Some(output.area().into())) - }))) + if let Some(color) = Namespace::namespace(state, arg0)? { + output.show(fg(color, thunk(move|output: &mut Tui|{ + state.interpret(output, &arg1)?; + // FIXME?: don't max out the used area? + Ok(Some(output.area().into())) + }))) + } else { + return Err(format!("fg: {arg0:?}: not a color").into()) + } }, Some("bg") => { let arg0 = arg0?.expect("bg: expected arg 0 (color)"); - let color = Namespace::namespace(state, arg0)?.unwrap_or_else(||panic!("bg: {arg0:?}: not a color")); - output.show(bg(color, thunk(move|output: &mut Tui|{ - state.interpret(output, &arg1)?; - // FIXME?: don't max out the used area? - Ok(Some(output.area().into())) - }))) + if let Some(color) = Namespace::namespace(state, arg0)? { + output.show(bg(color, thunk(move|output: &mut Tui|{ + state.interpret(output, &arg1)?; + // FIXME?: don't max out the used area? + Ok(Some(output.area().into())) + }))) + } else { + return Err(format!("bg: {arg0:?}: not a color").into()) + } }, _ => return Ok(None) diff --git a/src/sing.rs b/src/sing.rs index 9899fbe..ce776a6 100644 --- a/src/sing.rs +++ b/src/sing.rs @@ -46,7 +46,7 @@ pub type BoxedAudioHandler<'j> = /// Things that can provide a [jack::Client] reference. /// /// ``` -/// use tengri::sing::{Jack, HasJack}; +/// use tengri::*; /// /// let jack: &Jack = Jacked::default().jack(); /// diff --git a/src/sing/jack.rs b/src/sing/jack.rs index 20f5c23..0100fed 100644 --- a/src/sing/jack.rs +++ b/src/sing/jack.rs @@ -1,4 +1,4 @@ -use crate::{*, sing::*, time::PerfModel}; +use crate::{*, PerfModel}; pub use ::jack::{*, contrib::{*, ClosureProcessHandler}}; use JackState::*; @@ -6,7 +6,7 @@ use JackState::*; /// Wraps [JackState], and through it [jack::Client] when connected. /// /// ``` -/// let jack = tengri::sing::Jack::default(); +/// let jack = tengri::Jack::default(); /// ``` #[derive(Clone, Debug, Default)] pub struct Jack<'j> ( pub(crate) Arc>> @@ -17,7 +17,7 @@ use JackState::*; /// [jack::Client], which you can use to talk to the JACK API. /// /// ``` -/// let state = tengri::sing::JackState::default(); +/// let state = tengri::JackState::default(); /// ``` #[derive(Debug, Default)] pub enum JackState<'j> { /// Unused diff --git a/src/sing/jack_event.rs b/src/sing/jack_event.rs index 7802842..cfeae6c 100644 --- a/src/sing/jack_event.rs +++ b/src/sing/jack_event.rs @@ -4,7 +4,7 @@ pub use ::jack::{*, contrib::{*, ClosureProcessHandler}}; /// Event enum for JACK events. /// /// ``` -/// let event = tengri::sing::JackEvent::XRun; // kerpop +/// let event = tengri::JackEvent::XRun; // kerpop /// ``` #[derive(Debug, Clone, PartialEq)] pub enum JackEvent { ThreadInit, @@ -22,7 +22,7 @@ pub use ::jack::{*, contrib::{*, ClosureProcessHandler}}; /// Generic notification handler that emits [JackEvent] /// /// ``` -/// let notify = tengri::sing::JackNotify(|_|{}); +/// let notify = tengri::JackNotify(|_|{}); /// ``` pub struct JackNotify(pub T); diff --git a/src/term.rs b/src/term.rs index fb2b207..a289e66 100644 --- a/src/term.rs +++ b/src/term.rs @@ -103,7 +103,7 @@ impl Tui { /// Implement standard [main] entrypoint for TUI apps. #[macro_export] macro_rules! tui_main { ($state:expr) => { - pub fn main () -> Usually<()> { tui_run_main($state) } + pub fn main () -> Usually<()> { tui_run_main(Arc::new(RwLock::new($state))) } } } diff --git a/src/term/output.rs b/src/term/output.rs index e0ff044..b91f919 100644 --- a/src/term/output.rs +++ b/src/term/output.rs @@ -1,10 +1,8 @@ -use crate::{*, lang::*}; +use crate::*; use ratatui::{prelude::{Style, Position, Backend, Color}}; /// TUI works in u16 coordinates. impl Coord for u16 { fn plus (self, other: Self) -> Self { self.saturating_add(other) } } -impl_draw!(|self: u64, _to: Tui|{ todo!() }); -impl_draw!(|self: f64, _to: Tui|{ todo!() }); impl Screen for Tui { type Unit = u16; @@ -37,8 +35,15 @@ impl Screen for Tui { /// Spawn the TUI output thread which writes colored characters to the terminal. /// /// ``` -/// let state = Arc::new(RwLock::new(())); -/// Exit::run(|exit|tui_output(exit, state, sleep, stdout())) +/// let state = std::sync::Arc::new(std::sync::RwLock::new(())); +/// let _ = tengri::Exit::run(|exit|{ +/// tengri::tui_output( +/// exit.as_ref(), +/// &state, +/// std::time::Duration::from_millis(10), +/// std::io::stdout() +/// ) +/// }); /// ``` pub fn tui_output < W: Write + Send + Sync + 'static, T: View + Send + Sync + 'static @@ -68,7 +73,7 @@ pub fn tui_output < })?) } -use self::colors::*; mod colors { +pub use self::colors::*; mod colors { use ratatui::prelude::Color; pub const fn tui_color_bg () -> Color { Color::Rgb(28, 35, 25) } pub const fn tui_bg0 () -> Color { Color::Rgb(20, 20, 20) } @@ -259,7 +264,7 @@ pub fn phat (w: u16, h: u16, [fg, bg, hi, lo]: [Color;4], draw: impl Draw) south(top, north(low, draw)).min_wh(w, h) } -fn x_scroll () -> impl Draw { +pub fn x_scroll () -> impl Draw { thunk(|Tui(buf, XYWH(x1, y1, w, h)): &mut Tui|{ let x2 = *x1 + *w; for (i, x) in (*x1..=x2).enumerate() { @@ -287,7 +292,7 @@ fn x_scroll () -> impl Draw { }) } -fn y_scroll () -> impl Draw { +pub fn y_scroll () -> impl Draw { thunk(|Tui(buf, XYWH(x1, y1, w, h)): &mut Tui|{ let y2 = *y1 + *h; for (i, y) in (*y1..=y2).enumerate() { @@ -315,19 +320,16 @@ fn y_scroll () -> impl Draw { }) } -pub fn tui_update ( - Tui(buf, ..): &mut Tui, - area: XYWH, - callback: &impl Fn(&mut Cell, u16, u16) -) { -} - /// Draw TUI content or its error message. /// /// ``` -/// let _ = tengri::term::catcher(Ok(Some("hello"))); -/// let _ = tengri::term::catcher(Ok(None)); -/// let _ = tengri::term::catcher(Err("draw fail".into())); +/// for variant in [ +/// Ok(Some("hello")), +/// Ok(None), +/// Err("fail".into()), +/// ] { +/// let _ = tengri::catcher(variant); +/// } /// ``` pub fn catcher > (result: Usually) -> impl Draw { thunk(move|to: &mut Tui|match result { @@ -341,3 +343,6 @@ pub fn catcher > (result: Usually) -> impl Draw { } }) } + +impl_draw!(|self: u64, _to: Tui|{ todo!() }); +impl_draw!(|self: f64, _to: Tui|{ todo!() });