add origin and align methods to Layout trait

This commit is contained in:
facile pop culture reference 2026-07-10 18:24:44 +03:00
parent 09463649c6
commit 13c886d9e0
18 changed files with 333 additions and 248 deletions

View file

@ -1,7 +1,7 @@
use ::{ use ::{
std::{io::stdout, sync::{Arc, RwLock}}, std::{io::stdout, sync::{Arc, RwLock}},
tengri::{*, term::*, lang::*, keys::*, draw::*, space::*, lang::*},
ratatui::style::Color, ratatui::style::Color,
tengri::{*, lang::*},
}; };
tui_main!(State { tui_main!(State {
@ -18,10 +18,10 @@ tui_view!(|self: State| {
let wh = (self.size.w(), self.size.h()); let wh = (self.size.w(), self.size.h());
let src = VIEWS.get(self.cursor).unwrap_or(&""); let src = VIEWS.get(self.cursor).unwrap_or(&"");
let heading = format!("State {}/{} in {:?}", index, VIEWS.len(), &wh); let heading = format!("State {}/{} in {:?}", index, VIEWS.len(), &wh);
let title = bg(Color::Rgb(60, 10, 10), y_push(1, align_n(heading))); let title = bg(Color::Rgb(60, 10, 10), heading.align_n().push_y(1));
let code = bg(Color::Rgb(10, 60, 10), y_push(2, align_n(format!("{}", src)))); 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 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))) self.size.of(south(title, north(code, widget)))
}); });
@ -32,7 +32,7 @@ struct State {
/** User-controllable value. */ /** User-controllable value. */
cursor: usize, cursor: usize,
/** Rendered window size. */ /** Rendered window size. */
size: crate::space::Size, size: Sizer,
} }
impl Interpret<Tui, XYWH<u16>> for State { impl Interpret<Tui, XYWH<u16>> for State {

View file

@ -3,32 +3,34 @@ use crate::*;
/// Output target. /// Output target.
/// ///
/// ``` /// ```
/// use tengri::{*, draw::*}; /// use tengri::*;
/// /// struct TestOut { w: u16, h: u16 };
/// struct TestOut<T: Screen<Unit = u16>>(T); /// impl Wide<u16> for TestOut {}
/// /// impl Tall<u16> for TestOut {}
/// impl<T: Screen<Unit = u16>> Screen for TestOut { /// impl Xy<u16> for TestOut { fn x (&self) -> u16 { 0 } fn y (&self) -> u16 { 0 } }
/// impl Screen for TestOut {
/// type Unit = u16; /// type Unit = u16;
/// fn show <D: Draw<Self> + ?Sized> (&mut self, _: D) { /// fn show <D: Draw<Self>> (&mut self, _: D) -> Drawn<u16> {
/// println!("placed"); /// println!("placed");
/// () /// Ok(None)
/// } /// }
/// } /// }
/// ///
/// impl_draw!(|self: String, to: TestOut<u16>|{ /// impl_draw!(|self: String, to: TestOut|{
/// to.area_mut().set_w(self.len() as u16); /// to.w = self.len() as u16;
/// Ok(None)
/// }); /// });
/// ``` /// ```
pub trait Screen: Xy<Self::Unit> + Wh<Self::Unit> + Send + Sync + Sized { pub trait Screen: Xy<Self::Unit> + Wh<Self::Unit> + Send + Sync + Sized {
type Unit: Coord; type Unit: Coord;
/// Render drawable in subarea specified by `area` /// Render drawable in subarea specified by `area`
fn show <'t, T: Draw<Self>> (&mut self, content: T) -> Perhaps<XYWH<Self::Unit>>; fn show <T: Draw<Self>> (&mut self, content: T) -> Perhaps<XYWH<Self::Unit>>;
} }
/// Implement the [Draw] trait for a particular drawable and [Screen]. /// Implement the [Draw] trait for a particular drawable and [Screen].
/// ///
/// ``` /// ```
/// use tengri::{*, draw::*, term::*}; /// use tengri::*;
/// struct MyDrawable; /// struct MyDrawable;
/// impl_draw!(|self: MyDrawable, to: Tui|{ /// impl_draw!(|self: MyDrawable, to: Tui|{
/// todo!("your draw logic") /// todo!("your draw logic")
@ -60,7 +62,7 @@ pub trait Screen: Xy<Self::Unit> + Wh<Self::Unit> + Send + Sync + Sized {
/// a [Draw]able. /// a [Draw]able.
/// ///
/// ``` /// ```
/// use tengri::{*, draw::*, term::*}; /// use tengri::*;
/// struct MyWidget(bool); /// struct MyWidget(bool);
/// impl Draw<Tui> for MyWidget { /// impl Draw<Tui> for MyWidget {
/// fn draw (self, to: &mut Tui) -> Perhaps<XYWH<u16>> { /// fn draw (self, to: &mut Tui) -> Perhaps<XYWH<u16>> {
@ -106,6 +108,12 @@ pub trait View<T: Screen> {
fn view (&self) -> impl Draw<T>; fn view (&self) -> impl Draw<T>;
} }
impl<T: Screen> View<T> for () {
fn view (&self) -> impl Draw<T> {
()
}
}
impl<T: Screen, V: View<T>> Draw<T> for &V { impl<T: Screen, V: View<T>> Draw<T> for &V {
fn draw (self, to: &mut T) -> Perhaps<XYWH<T::Unit>> { fn draw (self, to: &mut T) -> Perhaps<XYWH<T::Unit>> {
self.view().draw(to) self.view().draw(to)
@ -114,15 +122,11 @@ impl<T: Screen, V: View<T>> Draw<T> for &V {
features! { features! {
"draw": [ "draw": [
align,
area,
azimuth,
color, color,
coord, coord,
iter, iter,
layout, layout,
lrtb, lrtb,
origin,
sizer, sizer,
space, space,
split, split,

View file

@ -1,31 +0,0 @@
use crate::*;
pub struct Align<T>(Option<Azimuth>, T);
impl_draw!(<S: Screen, T: Draw<S>,>|self: Align<T>, to: S|{
todo!()
});
/// ```
/// use tengri::*;
/// let _ = align(None, "unaligned");
/// let _ = align(Azimuth::SE, "southeast");
/// let _ = align(Some(Azimuth::SE), "southeast");
/// ```
pub fn align <S: Screen, T: Draw<S>, U: Into<Option<Azimuth>>> (origin: U, it: T) -> Align<T> {
Align(origin.into(), it)
}
pub fn align_c <S: Screen, T: Draw<S>> (it: T) -> Align<T> { Align(Some(Azimuth::C), it) }
pub fn align_x <S: Screen, T: Draw<S>> (it: T) -> Align<T> { Align(Some(Azimuth::X), it) }
pub fn align_y <S: Screen, T: Draw<S>> (it: T) -> Align<T> { Align(Some(Azimuth::Y), it) }
pub fn align_n <S: Screen, T: Draw<S>> (it: T) -> Align<T> { Align(Some(Azimuth::N), it) }
pub fn align_s <S: Screen, T: Draw<S>> (it: T) -> Align<T> { Align(Some(Azimuth::S), it) }
pub fn align_e <S: Screen, T: Draw<S>> (it: T) -> Align<T> { Align(Some(Azimuth::E), it) }
pub fn align_w <S: Screen, T: Draw<S>> (it: T) -> Align<T> { Align(Some(Azimuth::W), it) }
pub fn align_ne <S: Screen, T: Draw<S>> (it: T) -> Align<T> { Align(Some(Azimuth::NE), it) }
pub fn align_se <S: Screen, T: Draw<S>> (it: T) -> Align<T> { Align(Some(Azimuth::SE), it) }
pub fn align_nw <S: Screen, T: Draw<S>> (it: T) -> Align<T> { Align(Some(Azimuth::NW), it) }
pub fn align_sw <S: Screen, T: Draw<S>> (it: T) -> Align<T> { Align(Some(Azimuth::SW), it) }

View file

@ -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 <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: Draw<S>>(Option<XYWH<S::Unit>>, T);
impl_draw!(<S: Screen, T: Draw<S>,>|self: Area<S, T>, to: S|{ todo!() });

View file

@ -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
}

View file

@ -6,7 +6,7 @@ use super::*;
/// FIXME: Use AsRef/AsMut? /// FIXME: Use AsRef/AsMut?
/// ///
/// ``` /// ```
/// use tengri::draw::Coord; /// use tengri::*;
/// let a: u16 = Coord::zero(); /// let a: u16 = Coord::zero();
/// let b: u16 = a.plus(1); /// let b: u16 = a.plus(1);
/// let c: u16 = a.minus(2); /// let c: u16 = a.minus(2);

View file

@ -15,15 +15,6 @@ pub fn iter_once <'a, S: Screen, D: 'a, U: Draw<S>> (
} }
/// Iterate over a collection of various [Draw]ables: /// 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 < pub fn iter_dyn <
S: Screen, // Target screen S: Screen, // Target screen
D, // Input doesn't need to be [Draw]able, output does D, // Input doesn't need to be [Draw]able, output does

View file

@ -70,11 +70,96 @@ pub trait Layout<S: Screen>: Draw<S> + Sized {
fn push_xy <N: Into<Option<S::Unit>>> (self, x: N, y: N) -> impl Draw<S> { fn push_xy <N: Into<Option<S::Unit>>> (self, x: N, y: N) -> impl Draw<S> {
Push::XY(self, x.into(), y.into()) Push::XY(self, x.into(), y.into())
} }
fn align (self, azimuth: impl Into<Option<Azimuth>>) -> impl Draw<S> {
Align(azimuth.into(), self)
}
fn align_c (self) -> impl Draw<S> {
Align(Some(Azimuth::C), self)
}
fn align_x (self) -> impl Draw<S> {
Align(Some(Azimuth::X), self)
}
fn align_y (self) -> impl Draw<S> {
Align(Some(Azimuth::Y), self)
}
fn align_n (self) -> impl Draw<S> {
Align(Some(Azimuth::N), self)
}
fn align_s (self) -> impl Draw<S> {
Align(Some(Azimuth::S), self)
}
fn align_e (self) -> impl Draw<S> {
Align(Some(Azimuth::E), self)
}
fn align_w (self) -> impl Draw<S> {
Align(Some(Azimuth::W), self)
}
fn align_ne (self) -> impl Draw<S> {
Align(Some(Azimuth::NE), self)
}
fn align_se (self) -> impl Draw<S> {
Align(Some(Azimuth::SE), self)
}
fn align_nw (self) -> impl Draw<S> {
Align(Some(Azimuth::NW), self)
}
fn align_sw (self) -> impl Draw<S> {
Align(Some(Azimuth::SW), self)
}
fn origin (self, azimuth: impl Into<Option<Azimuth>>) -> impl Draw<S> {
Origin(azimuth.into(), self)
}
fn origin_c (self) -> impl Draw<S> {
Origin(Some(Azimuth::C), self)
}
fn origin_x (self) -> impl Draw<S> {
Origin(Some(Azimuth::X), self)
}
fn origin_y (self) -> impl Draw<S> {
Origin(Some(Azimuth::Y), self)
}
fn origin_n (self) -> impl Draw<S> {
Origin(Some(Azimuth::N), self)
}
fn origin_s (self) -> impl Draw<S> {
Origin(Some(Azimuth::S), self)
}
fn origin_e (self) -> impl Draw<S> {
Origin(Some(Azimuth::E), self)
}
fn origin_w (self) -> impl Draw<S> {
Origin(Some(Azimuth::W), self)
}
fn origin_ne (self) -> impl Draw<S> {
Origin(Some(Azimuth::NE), self)
}
fn origin_se (self) -> impl Draw<S> {
Origin(Some(Azimuth::SE), self)
}
fn origin_nw (self) -> impl Draw<S> {
Origin(Some(Azimuth::NW), self)
}
fn origin_sw (self) -> impl Draw<S> {
Origin(Some(Azimuth::SW), self)
}
} }
impl<S: Screen, T: Draw<S>> Layout<S> for T {} impl<S: Screen, T: Draw<S>> Layout<S> for T {}
/// Use whole drawing area along one or both axes. /// Use whole drawing area along one or both axes.
///
/// ```
/// use tengri::Layout;
/// let _ = "".full_w();
/// let _ = "".full_h();
/// let _ = "".full_wh();
/// ```
pub enum Full<T: Screen, I: Draw<T>> { pub enum Full<T: Screen, I: Draw<T>> {
__(PhantomData<T>), __(PhantomData<T>),
W(I), W(I),
@ -82,14 +167,17 @@ pub enum Full<T: Screen, I: Draw<T>> {
WH(I), WH(I),
} }
impl_draw!(<T: Screen, I: Draw<T>,>|self: Full<T, I>, to: T|{ impl_draw!(<T: Screen, I: Draw<T>,>|self: Full<T, I>, _to: T|{
todo!() 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<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> { pub enum Push<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
__(PhantomData<T>), __(PhantomData<T>),
@ -98,14 +186,17 @@ pub enum Push<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
XY(I, X, X), XY(I, X, X),
} }
impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Push<T, I, X>, to: T|{ impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Push<T, I, X>, _to: T|{
todo!() 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<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> { pub enum Pull<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
__(PhantomData<T>), __(PhantomData<T>),
@ -114,14 +205,17 @@ pub enum Pull<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
XY(I, X, X), XY(I, X, X),
} }
impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Pull<T, I, X>, to: T|{ impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Pull<T, I, X>, _to: T|{
todo!() todo!()
}); });
/// Only draw content if area is above a certain size. /// 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<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> { pub enum Min<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
__(PhantomData<T>), __(PhantomData<T>),
@ -130,16 +224,17 @@ pub enum Min<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
WH(I, X, X), WH(I, X, X),
} }
impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Min<T, I, X>, to: T|{ impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Min<T, I, X>, _to: T|{
todo!() todo!()
}); });
/// Set maximum size of of drawing area. /// Set maximum size of of drawing area.
/// ///
/// ``` /// ```
/// let max = tengri::w_max(3, "Hello"); /// use tengri::Layout;
/// let max = tengri::w_max(None, "Hello"); /// let _ = "".max_w(1);
/// let max = tengri::w_max(Some(3), "Hello"); /// let _ = "".max_h(1);
/// let _ = "".max_wh(1, 1);
/// ``` /// ```
pub enum Max<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> { pub enum Max<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
__(PhantomData<T>), __(PhantomData<T>),
@ -148,16 +243,17 @@ pub enum Max<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
WH(I, X, X), WH(I, X, X),
} }
impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Max<T, I, X>, to: T|{ impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Max<T, I, X>, _to: T|{
todo!() todo!()
}); });
/// Set size of of drawing area. /// Set size of of drawing area.
/// ///
/// ``` /// ```
/// let max = tengri::w_max(3, "Hello"); /// use tengri::Layout;
/// let max = tengri::w_max(None, "Hello"); /// let _ = "".exact_w(1);
/// let max = tengri::w_max(Some(3), "Hello"); /// let _ = "".exact_h(1);
/// let _ = "".exact_wh(1, 1);
/// ``` /// ```
pub enum Exact<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> { pub enum Exact<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
__(PhantomData<T>), __(PhantomData<T>),
@ -166,16 +262,17 @@ pub enum Exact<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
WH(I, X, X), WH(I, X, X),
} }
impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Exact<T, I, X>, to: T|{ impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Exact<T, I, X>, _to: T|{
todo!() todo!()
}); });
/// Define inner drawing area. /// Define inner drawing area.
/// ///
/// ``` /// ```
/// let max = tengri::w_max(3, "Hello"); /// use tengri::Layout;
/// let max = tengri::w_max(None, "Hello"); /// let _ = "".pad_w(1);
/// let max = tengri::w_max(Some(3), "Hello"); /// let _ = "".pad_h(1);
/// let _ = "".pad_wh(1, 1);
/// ``` /// ```
pub enum Pad<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> { pub enum Pad<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
__(PhantomData<T>), __(PhantomData<T>),
@ -184,6 +281,59 @@ pub enum Pad<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
WH(I, X, X), WH(I, X, X),
} }
impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Pad<T, I, X>, to: T|{ impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Pad<T, I, X>, _to: T|{
todo!() todo!()
}); });
pub struct Align<T>(Option<Azimuth>, T);
impl_draw!(<S: Screen, T: Draw<S>,>|self: Align<T>, _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 <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: Draw<S>>(Option<XYWH<S::Unit>>, T);
impl_draw!(<S: Screen, T: Draw<S>,>|self: Area<S, T>, _to: S|{
todo!()
});
pub struct Origin<T>(Option<Azimuth>, T);
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 {
fn origin (&self) -> Azimuth;
}
impl<T: AsRef<Azimuth>> HasOrigin for T {
fn origin (&self) -> Azimuth {
*self.as_ref()
}
}

View file

@ -1,42 +0,0 @@
use crate::*;
pub struct Origin<T>(Option<Azimuth>, T);
impl_draw!(<S: Screen, T: Draw<S>,>|self: Origin<T>, to: S|{
todo!()
});
/// ```
/// use tengri::*;
/// let _ = origin(None, "origin unspecified");
/// let _ = origin(Azimuth::SE, "southeast");
/// let _ = origin(Some(Azimuth::SE), "southeast");
/// ```
pub fn origin <S: Screen, T: Draw<S>, U: Into<Option<Azimuth>>> (origin: U, it: T) -> Origin<T> {
Origin(origin.into(), it)
}
pub fn origin_c <S: Screen, T: Draw<S>> (it: T) -> Origin<T> { Origin(Some(Azimuth::C), it) }
pub fn origin_x <S: Screen, T: Draw<S>> (it: T) -> Origin<T> { Origin(Some(Azimuth::X), it) }
pub fn origin_y <S: Screen, T: Draw<S>> (it: T) -> Origin<T> { Origin(Some(Azimuth::Y), it) }
pub fn origin_n <S: Screen, T: Draw<S>> (it: T) -> Origin<T> { Origin(Some(Azimuth::N), it) }
pub fn origin_s <S: Screen, T: Draw<S>> (it: T) -> Origin<T> { Origin(Some(Azimuth::S), it) }
pub fn origin_e <S: Screen, T: Draw<S>> (it: T) -> Origin<T> { Origin(Some(Azimuth::E), it) }
pub fn origin_w <S: Screen, T: Draw<S>> (it: T) -> Origin<T> { Origin(Some(Azimuth::W), it) }
pub fn origin_ne <S: Screen, T: Draw<S>> (it: T) -> Origin<T> { Origin(Some(Azimuth::NE), it) }
pub fn origin_se <S: Screen, T: Draw<S>> (it: T) -> Origin<T> { Origin(Some(Azimuth::SE), it) }
pub fn origin_nw <S: Screen, T: Draw<S>> (it: T) -> Origin<T> { Origin(Some(Azimuth::NW), it) }
pub fn origin_sw <S: Screen, T: Draw<S>> (it: T) -> Origin<T> { Origin(Some(Azimuth::SW), it) }
/// Something that has `[0, 0]` at a particular point.
pub trait HasOrigin {
fn origin (&self) -> Azimuth;
}
impl<T: AsRef<Azimuth>> HasOrigin for T {
fn origin (&self) -> Azimuth {
*self.as_ref()
}
}

View file

@ -39,20 +39,20 @@ pub const fn below <T: Screen> (a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T
impl Split { impl Split {
/// ``` /// ```
/// use tengri::draw::Split::*; /// use tengri::*;
/// let _ = Above.half("", ""); /// let _ = Split::Above.half("", "");
/// let _ = Below.half("", ""); /// let _ = Split::Below.half("", "");
/// let _ = North.half("", ""); /// let _ = Split::North.half("", "");
/// let _ = South.half("", ""); /// let _ = Split::South.half("", "");
/// let _ = East.half("", ""); /// let _ = Split::East.half("", "");
/// let _ = West.half("", ""); /// let _ = Split::West.half("", "");
/// ``` /// ```
pub const fn half <T: Screen> (&self, a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> { pub const fn half <T: Screen> (&self, a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
thunk(move|to: &mut T|{ thunk(move|to: &mut T|{
let (area_a, area_b) = to.xywh().split_half(self); let (area_a, area_b) = to.xywh().split_half(self);
let (origin_a, origin_b) = self.origins(); let (origin_a, origin_b) = self.origins();
let a = align(origin_a, a); let a = a.align(origin_a);
let b = align(origin_b, b); let b = b.align(origin_b);
match self { match self {
Self::Below => { Self::Below => {
to.show(area(area_b, b))?; 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 <S: Screen, T: Draw<S>> (&self, _: impl Iterator<Item = T>) {
//todo!()
//}
} }
#[macro_export] macro_rules! north { #[macro_export] macro_rules! north {

View file

@ -12,9 +12,9 @@ impl<T: Screen, F: FnOnce(&mut T)->Perhaps<XYWH<T::Unit>>> Draw<T> for Thunk<T,
/// Basic [Draw]able closure. /// Basic [Draw]able closure.
/// ///
/// ``` /// ```
/// # use tengri::{draw::*, term::*}; /// # use tengri::*;
/// # fn test () -> impl tengri::draw::Draw<tengri::term::Tui> { /// # fn test () -> impl Draw<Tui> {
/// thunk(|to: &mut Tui|Ok(to.1)) /// thunk(|to: &mut Tui|Ok(Some(to.1)))
/// # } /// # }
/// ``` /// ```
pub const fn thunk <T: Screen, F: FnOnce(&mut T)->Perhaps<XYWH<T::Unit>>> ( pub const fn thunk <T: Screen, F: FnOnce(&mut T)->Perhaps<XYWH<T::Unit>>> (
@ -26,8 +26,8 @@ pub const fn thunk <T: Screen, F: FnOnce(&mut T)->Perhaps<XYWH<T::Unit>>> (
/// Only render when condition is true. /// Only render when condition is true.
/// ///
/// ``` /// ```
/// # use tengri::{draw::*, term::*}; /// # use tengri::*;
/// # fn test () -> impl tengri::draw::Draw<tengri::term::Tui> { /// # fn test () -> impl Draw<Tui> {
/// when(true, "Yes") /// when(true, "Yes")
/// # } /// # }
/// ``` /// ```
@ -38,8 +38,8 @@ pub const fn when <T: Screen> (condition: bool, draw: impl Draw<T>) -> impl Draw
/// Render one thing if a condition is true and another false. /// Render one thing if a condition is true and another false.
/// ///
/// ``` /// ```
/// # use tengri::{draw::*, term::*}; /// # use tengri::*;
/// # fn test () -> impl tengri::draw::Draw<tengri::term::Tui> { /// # fn test () -> impl Draw<Tui> {
/// either(true, "Yes", "No") /// either(true, "Yes", "No")
/// # } /// # }
/// ``` /// ```

View file

@ -31,9 +31,9 @@ pub trait Tall<N: Coord> {
/// Point with size. /// Point with size.
/// ///
/// ``` /// ```
/// use tengri::space::{XY, XYWH}; /// # use tengri::*;
/// let xywh = XYWH(0u16, 0, 0, 0); /// 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) /// * [ ] TODO: origin field (determines at which corner/side is X0 Y0)

View file

@ -63,18 +63,19 @@ macro_rules! eval_enum ((
/// Interpret layout operation. /// Interpret layout operation.
/// ///
/// ``` /// ```
/// # use tengri::{lang::*, draw::*, eval::*, term::*}; /// # use tengri::{*, lang::*};
/// ///
/// struct State {/*app-specific*/} /// struct State {/*app-specific*/}
/// impl<'b> Namespace<'b, bool> for State {}
/// impl<'b> Namespace<'b, u16> for State {} /// impl<'b> Namespace<'b, u16> for State {}
/// impl Interpret<Tui, XYWH<u16>> for State {} /// impl<'b> Namespace<'b, bool> for State {}
/// impl<'b> Namespace<'b, Option<u16>> for State {}
/// impl Interpret<Tui, Option<XYWH<u16>>> for State {}
/// ///
/// # fn main () -> tengri::Usually<()> { /// # fn main () -> tengri::Usually<()> {
/// let state = State {}; /// let state = State {};
/// let mut target = Tui::new(80, 25); /// let mut target = Tui::new(80, 25);
/// eval_view(&state, &mut target, &"")?; /// 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))")?; /// eval_view(&state, &mut target, &"(either true (text hello) (text world))")?;
/// // TODO test all /// // TODO test all
/// # Ok(()) } /// # Ok(()) }
@ -108,43 +109,33 @@ pub fn eval_view <'a, O: Screen + 'a, S> (
Some("when") => when( Some("when") => when(
state.namespace(arg0?)?.unwrap(), state.namespace(arg0?)?.unwrap(),
thunk(move|output: &mut O|{ thunk(move|output: &mut O|{state.interpret(output, &arg1)})
state.interpret(output, &arg1)
})
).draw(output), ).draw(output),
Some("either") => either( Some("either") => either(
state.namespace(arg0?)?.unwrap(), state.namespace(arg0?)?.unwrap(),
thunk(move|output: &mut O|{ thunk(move|output: &mut O|{state.interpret(output, &arg1)}),
state.interpret(output, &arg1) thunk(move|output: &mut O|{state.interpret(output, &arg2)}),
}),
thunk(move|output: &mut O|{
state.interpret(output, &arg2)
}),
).draw(output), ).draw(output),
// Second `frags.next()` calls returns the namespace member. // Second `frags.next()` calls returns the namespace member.
Some("bsp") => { Some("bsp") => {
let direction = eval_enum!("bsp", output, state, frags.next(), arg0, Split { eval_enum!("bsp", output, state, frags.next(), arg0, Split {
"n" => North, "n" => North,
"s" => South, "s" => South,
"e" => East, "e" => East,
"w" => West, "w" => West,
"a" => Above, "a" => Above,
"b" => Below "b" => Below
}); }).half(
direction.half( thunk(move|output: &mut O|{state.interpret(output, &arg0)}),
thunk(move|output: &mut O|{ thunk(move|output: &mut O|{state.interpret(output, &arg1)}),
state.interpret(output, &arg0)
}),
thunk(move|output: &mut O|{
state.interpret(output, &arg1)
}),
).draw(output) ).draw(output)
}, },
Some("align") => { 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, "c" => C,
"n" => N, "n" => N,
"s" => S, "s" => S,
@ -152,11 +143,7 @@ pub fn eval_view <'a, O: Screen + 'a, S> (
"w" => W, "w" => W,
"x" => X, "x" => X,
"y" => Y "y" => Y
}); })).draw(output)
let content = thunk(move|output: &mut O|{
state.interpret(output, &arg0)
});
align(alignment, content).draw(output)
}, },
Some("exact") => eval_xy!( Some("exact") => eval_xy!(
@ -187,21 +174,50 @@ pub fn eval_view <'a, O: Screen + 'a, S> (
/// Interpret TUI-specific layout operation. /// 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<XYWH<u16>>: try_eval_tui)]
/// struct State; /// struct State;
/// impl<'b> Namespace<'b, bool> for State {} /// tengri::lang::primitive!(u8: try_to_u8);
/// impl<'b> Namespace<'b, u16> for State {} /// tengri::lang::primitive!(u16: try_to_u16);
/// impl<'b> Namespace<'b, Color> for State {} /// tengri::lang::namespace!(State: bool {
/// impl Interpret<Tui, ()> for State {} /// });
/// 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<XYWH<u16>>{
/// expression = {
/// "text" (...rest) => { todo!() }
/// }
/// });
/// impl Interpret<Tui, Option<XYWH<u16>>> for State {
/// fn interpret_expr <'a> (&'a self, _: &mut Tui, lang: &'a impl Expression)
/// -> Usually<Option<XYWH<u16>>>
/// {
/// Ok(None)
/// }
/// }
///
/// # fn main () -> tengri::Usually<()> { /// # fn main () -> tengri::Usually<()> {
/// let state = State; /// let state = State;
/// let mut out = Tui::default(); /// let mut out = Tui::new(80, 25);
/// tengri::eval::eval_view_tui(&state, &mut out, "")?; /// eval_view_tui(&state, &mut out, "")?;
/// tengri::eval::eval_view_tui(&state, &mut out, "text Hello world!")?; /// eval_view_tui(&state, &mut out, "text Hello world!")?;
/// tengri::eval::eval_view_tui(&state, &mut out, "fg (g 0) (text Hello world!)")?; /// 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!)")?; /// 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!)))")?; /// eval_view_tui(&state, &mut out, "(bg (g 3) (fg (g 4) (text Hello world!)))")?;
/// # Ok(()) } /// # Ok(()) }
/// ``` /// ```
pub fn eval_view_tui <'a, S> ( pub fn eval_view_tui <'a, S> (
@ -231,22 +247,28 @@ pub fn eval_view_tui <'a, S> (
Some("fg") => { Some("fg") => {
let arg0 = arg0?.expect("fg: expected arg 0 (color)"); let arg0 = arg0?.expect("fg: expected arg 0 (color)");
let color = Namespace::namespace(state, arg0)?.unwrap_or_else(||panic!("fg: {arg0:?}: not a color")); if let Some(color) = Namespace::namespace(state, arg0)? {
output.show(fg(color, thunk(move|output: &mut Tui|{ output.show(fg(color, thunk(move|output: &mut Tui|{
state.interpret(output, &arg1)?; state.interpret(output, &arg1)?;
// FIXME?: don't max out the used area? // FIXME?: don't max out the used area?
Ok(Some(output.area().into())) Ok(Some(output.area().into()))
}))) })))
} else {
return Err(format!("fg: {arg0:?}: not a color").into())
}
}, },
Some("bg") => { Some("bg") => {
let arg0 = arg0?.expect("bg: expected arg 0 (color)"); let arg0 = arg0?.expect("bg: expected arg 0 (color)");
let color = Namespace::namespace(state, arg0)?.unwrap_or_else(||panic!("bg: {arg0:?}: not a color")); if let Some(color) = Namespace::namespace(state, arg0)? {
output.show(bg(color, thunk(move|output: &mut Tui|{ output.show(bg(color, thunk(move|output: &mut Tui|{
state.interpret(output, &arg1)?; state.interpret(output, &arg1)?;
// FIXME?: don't max out the used area? // FIXME?: don't max out the used area?
Ok(Some(output.area().into())) Ok(Some(output.area().into()))
}))) })))
} else {
return Err(format!("bg: {arg0:?}: not a color").into())
}
}, },
_ => return Ok(None) _ => return Ok(None)

View file

@ -46,7 +46,7 @@ pub type BoxedAudioHandler<'j> =
/// Things that can provide a [jack::Client] reference. /// Things that can provide a [jack::Client] reference.
/// ///
/// ``` /// ```
/// use tengri::sing::{Jack, HasJack}; /// use tengri::*;
/// ///
/// let jack: &Jack = Jacked::default().jack(); /// let jack: &Jack = Jacked::default().jack();
/// ///

View file

@ -1,4 +1,4 @@
use crate::{*, sing::*, time::PerfModel}; use crate::{*, PerfModel};
pub use ::jack::{*, contrib::{*, ClosureProcessHandler}}; pub use ::jack::{*, contrib::{*, ClosureProcessHandler}};
use JackState::*; use JackState::*;
@ -6,7 +6,7 @@ use JackState::*;
/// Wraps [JackState], and through it [jack::Client] when connected. /// 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> ( #[derive(Clone, Debug, Default)] pub struct Jack<'j> (
pub(crate) Arc<RwLock<JackState<'j>>> pub(crate) Arc<RwLock<JackState<'j>>>
@ -17,7 +17,7 @@ use JackState::*;
/// [jack::Client], which you can use to talk to the JACK API. /// [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> { #[derive(Debug, Default)] pub enum JackState<'j> {
/// Unused /// Unused

View file

@ -4,7 +4,7 @@ pub use ::jack::{*, contrib::{*, ClosureProcessHandler}};
/// Event enum for JACK events. /// 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 { #[derive(Debug, Clone, PartialEq)] pub enum JackEvent {
ThreadInit, ThreadInit,
@ -22,7 +22,7 @@ pub use ::jack::{*, contrib::{*, ClosureProcessHandler}};
/// Generic notification handler that emits [JackEvent] /// Generic notification handler that emits [JackEvent]
/// ///
/// ``` /// ```
/// let notify = tengri::sing::JackNotify(|_|{}); /// let notify = tengri::JackNotify(|_|{});
/// ``` /// ```
pub struct JackNotify<T: Fn(JackEvent) + Send>(pub T); pub struct JackNotify<T: Fn(JackEvent) + Send>(pub T);

View file

@ -103,7 +103,7 @@ impl Tui {
/// Implement standard [main] entrypoint for TUI apps. /// Implement standard [main] entrypoint for TUI apps.
#[macro_export] macro_rules! tui_main { #[macro_export] macro_rules! tui_main {
($state:expr) => { ($state:expr) => {
pub fn main () -> Usually<()> { tui_run_main($state) } pub fn main () -> Usually<()> { tui_run_main(Arc::new(RwLock::new($state))) }
} }
} }

View file

@ -1,10 +1,8 @@
use crate::{*, lang::*}; use crate::*;
use ratatui::{prelude::{Style, Position, Backend, Color}}; use ratatui::{prelude::{Style, Position, Backend, Color}};
/// TUI works in u16 coordinates. /// TUI works in u16 coordinates.
impl Coord for u16 { fn plus (self, other: Self) -> Self { self.saturating_add(other) } } 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 { impl Screen for Tui {
type Unit = u16; type Unit = u16;
@ -37,8 +35,15 @@ impl Screen for Tui {
/// Spawn the TUI output thread which writes colored characters to the terminal. /// Spawn the TUI output thread which writes colored characters to the terminal.
/// ///
/// ``` /// ```
/// let state = Arc::new(RwLock::new(())); /// let state = std::sync::Arc::new(std::sync::RwLock::new(()));
/// Exit::run(|exit|tui_output(exit, state, sleep, stdout())) /// 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 < pub fn tui_output <
W: Write + Send + Sync + 'static, T: View<Tui> + Send + Sync + 'static W: Write + Send + Sync + 'static, T: View<Tui> + 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; use ratatui::prelude::Color;
pub const fn tui_color_bg () -> Color { Color::Rgb(28, 35, 25) } pub const fn tui_color_bg () -> Color { Color::Rgb(28, 35, 25) }
pub const fn tui_bg0 () -> Color { Color::Rgb(20, 20, 20) } 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<Tui>)
south(top, north(low, draw)).min_wh(w, h) south(top, north(low, draw)).min_wh(w, h)
} }
fn x_scroll () -> impl Draw<Tui> { pub fn x_scroll () -> impl Draw<Tui> {
thunk(|Tui(buf, XYWH(x1, y1, w, h)): &mut Tui|{ thunk(|Tui(buf, XYWH(x1, y1, w, h)): &mut Tui|{
let x2 = *x1 + *w; let x2 = *x1 + *w;
for (i, x) in (*x1..=x2).enumerate() { for (i, x) in (*x1..=x2).enumerate() {
@ -287,7 +292,7 @@ fn x_scroll () -> impl Draw<Tui> {
}) })
} }
fn y_scroll () -> impl Draw<Tui> { pub fn y_scroll () -> impl Draw<Tui> {
thunk(|Tui(buf, XYWH(x1, y1, w, h)): &mut Tui|{ thunk(|Tui(buf, XYWH(x1, y1, w, h)): &mut Tui|{
let y2 = *y1 + *h; let y2 = *y1 + *h;
for (i, y) in (*y1..=y2).enumerate() { for (i, y) in (*y1..=y2).enumerate() {
@ -315,19 +320,16 @@ fn y_scroll () -> impl Draw<Tui> {
}) })
} }
pub fn tui_update (
Tui(buf, ..): &mut Tui,
area: XYWH<u16>,
callback: &impl Fn(&mut Cell, u16, u16)
) {
}
/// Draw TUI content or its error message. /// Draw TUI content or its error message.
/// ///
/// ``` /// ```
/// let _ = tengri::term::catcher(Ok(Some("hello"))); /// for variant in [
/// let _ = tengri::term::catcher(Ok(None)); /// Ok(Some("hello")),
/// let _ = tengri::term::catcher(Err("draw fail".into())); /// Ok(None),
/// Err("fail".into()),
/// ] {
/// let _ = tengri::catcher(variant);
/// }
/// ``` /// ```
pub fn catcher <T: Draw<Tui>> (result: Usually<T>) -> impl Draw<Tui> { pub fn catcher <T: Draw<Tui>> (result: Usually<T>) -> impl Draw<Tui> {
thunk(move|to: &mut Tui|match result { thunk(move|to: &mut Tui|match result {
@ -341,3 +343,6 @@ pub fn catcher <T: Draw<Tui>> (result: Usually<T>) -> impl Draw<Tui> {
} }
}) })
} }
impl_draw!(|self: u64, _to: Tui|{ todo!() });
impl_draw!(|self: f64, _to: Tui|{ todo!() });