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 ::{
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<Tui, XYWH<u16>> for State {

View file

@ -3,32 +3,34 @@ use crate::*;
/// Output target.
///
/// ```
/// use tengri::{*, draw::*};
///
/// struct TestOut<T: Screen<Unit = u16>>(T);
///
/// impl<T: Screen<Unit = u16>> Screen for TestOut {
/// use tengri::*;
/// struct TestOut { w: u16, h: u16 };
/// impl Wide<u16> for TestOut {}
/// impl Tall<u16> for TestOut {}
/// impl Xy<u16> for TestOut { fn x (&self) -> u16 { 0 } fn y (&self) -> u16 { 0 } }
/// impl Screen for TestOut {
/// type Unit = u16;
/// fn show <D: Draw<Self> + ?Sized> (&mut self, _: D) {
/// fn show <D: Draw<Self>> (&mut self, _: D) -> Drawn<u16> {
/// println!("placed");
/// ()
/// Ok(None)
/// }
/// }
///
/// impl_draw!(|self: String, to: TestOut<u16>|{
/// 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<Self::Unit> + Wh<Self::Unit> + Send + Sync + Sized {
type Unit: Coord;
/// 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].
///
/// ```
/// 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<Self::Unit> + Wh<Self::Unit> + Send + Sync + Sized {
/// a [Draw]able.
///
/// ```
/// use tengri::{*, draw::*, term::*};
/// use tengri::*;
/// struct MyWidget(bool);
/// impl Draw<Tui> for MyWidget {
/// fn draw (self, to: &mut Tui) -> Perhaps<XYWH<u16>> {
@ -106,6 +108,12 @@ pub trait View<T: Screen> {
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 {
fn draw (self, to: &mut T) -> Perhaps<XYWH<T::Unit>> {
self.view().draw(to)
@ -114,15 +122,11 @@ impl<T: Screen, V: View<T>> Draw<T> for &V {
features! {
"draw": [
align,
area,
azimuth,
color,
coord,
iter,
layout,
lrtb,
origin,
sizer,
space,
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?
///
/// ```
/// use tengri::draw::Coord;
/// use tengri::*;
/// let a: u16 = Coord::zero();
/// let b: u16 = a.plus(1);
/// 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:
///
/// ```
/// 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

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> {
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 {}
/// 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>> {
__(PhantomData<T>),
W(I),
@ -82,14 +167,17 @@ pub enum Full<T: Screen, I: Draw<T>> {
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!()
});
/// 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>>> {
__(PhantomData<T>),
@ -98,14 +186,17 @@ pub enum Push<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
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!()
});
/// 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>>> {
__(PhantomData<T>),
@ -114,14 +205,17 @@ pub enum Pull<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
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!()
});
/// 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>>> {
__(PhantomData<T>),
@ -130,16 +224,17 @@ pub enum Min<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
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!()
});
/// 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<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
__(PhantomData<T>),
@ -148,16 +243,17 @@ pub enum Max<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
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!()
});
/// 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<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
__(PhantomData<T>),
@ -166,16 +262,17 @@ pub enum Exact<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
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!()
});
/// 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<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
__(PhantomData<T>),
@ -184,6 +281,59 @@ pub enum Pad<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
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!()
});
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 {
/// ```
/// use tengri::draw::Split::*;
/// let _ = Above.half("", "");
/// let _ = Below.half("", "");
/// let _ = North.half("", "");
/// let _ = South.half("", "");
/// let _ = East.half("", "");
/// let _ = West.half("", "");
/// use tengri::*;
/// let _ = Split::Above.half("", "");
/// let _ = Split::Below.half("", "");
/// let _ = Split::North.half("", "");
/// let _ = Split::South.half("", "");
/// let _ = Split::East.half("", "");
/// let _ = Split::West.half("", "");
/// ```
pub const fn half <T: Screen> (&self, a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
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 <S: Screen, T: Draw<S>> (&self, _: impl Iterator<Item = T>) {
//todo!()
//}
}
#[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.
///
/// ```
/// # use tengri::{draw::*, term::*};
/// # fn test () -> impl tengri::draw::Draw<tengri::term::Tui> {
/// thunk(|to: &mut Tui|Ok(to.1))
/// # use tengri::*;
/// # fn test () -> impl Draw<Tui> {
/// thunk(|to: &mut Tui|Ok(Some(to.1)))
/// # }
/// ```
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.
///
/// ```
/// # use tengri::{draw::*, term::*};
/// # fn test () -> impl tengri::draw::Draw<tengri::term::Tui> {
/// # use tengri::*;
/// # fn test () -> impl Draw<Tui> {
/// 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.
///
/// ```
/// # use tengri::{draw::*, term::*};
/// # fn test () -> impl tengri::draw::Draw<tengri::term::Tui> {
/// # use tengri::*;
/// # fn test () -> impl Draw<Tui> {
/// either(true, "Yes", "No")
/// # }
/// ```

View file

@ -31,9 +31,9 @@ pub trait Tall<N: Coord> {
/// 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)

View file

@ -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<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<()> {
/// 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<XYWH<u16>>: 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<Tui, ()> 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<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<()> {
/// 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)

View file

@ -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();
///

View file

@ -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<RwLock<JackState<'j>>>
@ -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

View file

@ -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<T: Fn(JackEvent) + Send>(pub T);

View file

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

View file

@ -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<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;
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<Tui>)
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|{
let x2 = *x1 + *w;
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|{
let y2 = *y1 + *h;
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.
///
/// ```
/// 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 <T: Draw<Tui>> (result: Usually<T>) -> impl Draw<Tui> {
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!() });