diff --git a/Cargo.lock b/Cargo.lock index d717471..e5500fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1084,6 +1084,7 @@ dependencies = [ name = "tengri_output" version = "0.14.0" dependencies = [ + "bumpalo", "proptest", "proptest-derive", "tengri", @@ -1109,6 +1110,7 @@ version = "0.14.0" dependencies = [ "atomic_float", "better-panic", + "bumpalo", "crossterm 0.29.0", "konst", "palette", diff --git a/Cargo.toml b/Cargo.toml index 934b268..f2280f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ tengri_proc = { path = "./proc" } anyhow = { version = "1.0" } atomic_float = { version = "1" } better-panic = { version = "0.3.0" } +bumpalo = { version = "3.19.0" } const_panic = { version = "0.2.12", features = [ "derive" ] } crossterm = { version = "0.29.0" } heck = { version = "0.5" } diff --git a/dsl/src/dsl_expr.rs b/dsl/src/dsl_expr.rs index 4179f94..92b093e 100644 --- a/dsl/src/dsl_expr.rs +++ b/dsl/src/dsl_expr.rs @@ -63,7 +63,7 @@ dsl_type!(DslExpr { ).into()) }; )* - Ok(Some($body as $Type)) + Ok(Some($body/* as $Type*/)) }; ($name, get) }),* )? ]; @@ -74,12 +74,14 @@ pub trait DslNsExprs<'a, T: 'a>: 'a { const EXPRS: DslExprs<'a, Self, T> = &[]; /// Resolve an expression if known. fn from_expr (&'a self, dsl: impl DslExpr + 'a) -> Perhaps { - let head = dsl.head()?; - for (key, get) in Self::EXPRS.iter() { - if Some(*key) == head { - return get(self, dsl.tail()?.unwrap_or("")) + if let Some(dsl) = dsl.expr()? { + let head = dsl.head()?; + for (key, get) in Self::EXPRS.iter() { + if Some(*key) == head { + return get(self, dsl.tail()?.unwrap_or("")) + } } } - return Ok(None) + Ok(None) } } diff --git a/dsl/src/dsl_ns.rs b/dsl/src/dsl_ns.rs index f17c283..c8b701f 100644 --- a/dsl/src/dsl_ns.rs +++ b/dsl/src/dsl_ns.rs @@ -16,3 +16,31 @@ pub trait DslNs<'a, T: 'a>: DslNsWords<'a, T> + DslNsExprs<'a, T> { Ok(None) } } + +#[macro_export] macro_rules! dsl_ns { + ($State:ty: $Type:ty { + $(literal = |$dsl:ident| $literal:expr;)? + $(word = |$state_w:ident| { + $($word:literal => $body_w:expr),* $(,)? + };)? + $(expr = |$state_e:ident| { + $($head:literal $args:tt => $body_e:expr),* $(,)? + };)? + }) => { + impl<'a> DslNs<'a, $Type> for $State { + $(fn from_literal (&self, $dsl: impl Dsl) -> Perhaps<$Type> { + $literal + })? + } + impl<'a> DslNsWords<'a, $Type> for $State { + $(dsl_words! { 'a |$state_w| -> $Type { + $($word => $body_w),* + } })? + } + impl<'a> DslNsExprs<'a, $Type> for $State { + $(dsl_exprs! { 'a |$state_e| -> $Type { + $($head $args => $body_e),* + } })? + } + } +} diff --git a/output/Cargo.toml b/output/Cargo.toml index 5172710..ed7f210 100644 --- a/output/Cargo.toml +++ b/output/Cargo.toml @@ -4,12 +4,17 @@ description = "UI metaframework, output layer." version = { workspace = true } edition = { workspace = true } +[lib] +path = "src/output.rs" + [features] -dsl = [ "tengri_dsl" ] +bumpalo = [ "dep:bumpalo" ] +dsl = [ "dep:tengri_dsl" ] [dependencies] tengri_core = { path = "../core" } tengri_dsl = { optional = true, path = "../dsl" } +bumpalo = { optional = true, workspace = true } [dev-dependencies] tengri = { path = "../tengri", features = [ "dsl", "tui" ] } diff --git a/output/README.md b/output/README.md index b827f46..e9669a4 100644 --- a/output/README.md +++ b/output/README.md @@ -4,10 +4,10 @@ it expresses the following notions: * [**space:**](./src/space.rs) `Direction`, `Coordinate`, `Area`, `Size`, `Measure` -* [**output:**](./src/output.rs) `Output`, `Render`, `Content` - * the layout operators are generic over `Render` and/or `Content` - * the traits `Render` and `Content` are generic over `Output` - * implement `Output` to bring a layout to a new backend: +* [**output:**](./src/output.rs) `Out`, `Draw`, `Content` + * the layout operators are generic over `Draw` and/or `Content` + * the traits `Draw` and `Content` are generic over `Out` + * implement `Out` to bring a layout to a new backend: [see `TuiOut` in `tengri_tui`](../tui/src/tui_engine/tui_output.rs) * [**layout:**](./src/layout.rs) diff --git a/output/src/content.rs b/output/src/content.rs new file mode 100644 index 0000000..f797f98 --- /dev/null +++ b/output/src/content.rs @@ -0,0 +1,40 @@ +use crate::*; + +/// Composable renderable with static dispatch. +pub trait Content: Sized { + /// Return opaque [Draw]able. + fn content (&self) -> impl Draw + Layout + '_ { () } +} + +/// The platonic ideal unit of [Content]: +/// total emptiness at dead center (e=1vg^sqrt(-1)) +impl Content for () {} + +impl + Layout> Content for fn()->T { + fn content (&self) -> impl Draw + Layout + '_ { + self() + } +} + +/// Implement composable content for a struct. +#[macro_export] macro_rules! content { + + // Implement for all [Out]s. + (|$self:ident:$Struct:ty| $content:expr) => { + impl Content for $Struct { + fn content (&$self) -> impl Draw + Layout + '_ { Some($content) } + } + }; + + // Implement for specific [Out]. + ($Out:ty:| + $self:ident: + $Struct:ident$(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)? + |$content:expr) => { + impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Content<$Out> + for $Struct $(<$($($L)? $($T)?),+>)? { + fn content (&$self) -> impl Draw<$Out> + Layout<$Out> + '_ { $content } + } + }; + +} diff --git a/output/src/draw.rs b/output/src/draw.rs new file mode 100644 index 0000000..f78574d --- /dev/null +++ b/output/src/draw.rs @@ -0,0 +1,94 @@ +use crate::*; + +/// Drawable with dynamic dispatch. +pub trait Draw { + /// Write data to display. + fn draw (&self, to: &mut O); +} + +impl Draw for fn(&mut O) { + fn draw (&self, to: &mut O) { + self(to) + } +} + +impl Draw for () { fn draw (&self, _: &mut O) {} } + +impl> Draw for &T { + fn draw (&self, to: &mut O) { (*self).draw(to) } +} + +impl<'x, O: Out> Draw for &(dyn Draw + 'x) { + fn draw (&self, to: &mut O) { + (*self).draw(to) + } +} + +impl<'x, O: Out> Draw for &mut (dyn Draw + 'x) { + fn draw (&self, to: &mut O) { + (**self).draw(to) + } +} + +impl> Draw for RwLock { + fn draw (&self, to: &mut O) { self.read().unwrap().draw(to) } +} + +impl> Draw for [T] { + fn draw (&self, to: &mut O) { + for draw in self.iter() { + draw.draw(to) + } + } +} + +//impl> Draw for &mut T { + //fn draw (&self, to: &mut O) { + //(**self).draw(to) + //} +//} + +//impl, U: Draw> Draw for &mut T { + //fn draw (&self, to: &mut O) { + //for draw in *self { + //draw.draw(to) + //} + //} +//} + +/// Implement custom drawing for a struct. +#[macro_export] macro_rules! draw { + + // Implement for all [Out] backends. + (|$self:ident:$Struct:ident $(< + $($L:lifetime),* $($T:ident $(:$Trait:path)?),* + >)?, $to:ident | $draw:expr) => { + impl <$($($L),*)? O: Out, $($($T$(:$Trait)?),*)?> Draw + for $Struct $(<$($L),* $($T),*>)? { + fn draw (&$self, $to: &mut O) { $draw } + } + }; + + // Implement for a specific [Out] backend. + ($O:ty:| + $self:ident: + $Struct:ident $(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)?, $to:ident + |$draw:expr) => { + impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Draw<$O> + for $Struct $(<$($($L)? $($T)?),+>)? { + fn draw (&$self, $to: &mut $O) { $draw } + } + }; +} + +draw!(|self: Arc>, to|(**self).draw(to)); +draw!(|self: Box>, to|(**self).draw(to)); +//draw!(|self: Option>, to|if let Some(draw) = self { draw.draw(to) }); + +impl> Draw for Option { + fn draw (&self, to: &mut O) { + if let Some(draw) = self { + draw.draw(to) + } + } +} diff --git a/output/src/group.rs b/output/src/group.rs new file mode 100644 index 0000000..e043a1b --- /dev/null +++ b/output/src/group.rs @@ -0,0 +1,12 @@ +use crate::*; + +pub struct Group(T); + +impl Group { + pub const fn new () -> Group<()> { + Group(()) + } + pub const fn add (self, value: U) -> Group<(T, U)> { + Group((self.0, value)) + } +} diff --git a/output/src/layout.rs b/output/src/layout.rs new file mode 100644 index 0000000..7bba46a --- /dev/null +++ b/output/src/layout.rs @@ -0,0 +1,98 @@ +use crate::*; + +pub trait Layout { + fn x (&self, area: O::Area) -> O::Unit { + area.x() + } + fn y (&self, area: O::Area) -> O::Unit { + area.y() + } + + fn min_w (&self, _area: O::Area) -> O::Unit { + 0.into() + } + fn max_w (&self, area: O::Area) -> O::Unit { + area.w() + } + + fn min_h (&self, _area: O::Area) -> O::Unit { + 0.into() + } + fn max_h (&self, area: O::Area) -> O::Unit { + area.h() + } + + fn layout (&self, area: O::Area) -> O::Area { + O::Area::from([ + self.x(area), + self.y(area), + area.w().max(self.min_w(area)).min(self.max_w(area)), + area.h().max(self.min_h(area)).min(self.max_h(area)), + ]) + } +} + +#[macro_export] macro_rules! layout { + // Implement for all [Out] backends. + (|$self:ident:$Struct:ident $(< + $($L:lifetime),* $($T:ident $(:$Trait:path)?),* + >)?, $to:ident|$($method:ident = |$area:ident|$body:expr;)*) => { + impl <$($($L),*)? O: Out, $($($T$(:$Trait)?),*)?> Layout + for $Struct $(<$($L),* $($T),*>)? { + $(fn $method (&$self, $area: O::Area) -> O::Area { + $body + })* + } + }; + // Implement for a specific [Out] backend. + ($O:ty:| + $self:ident: + $Struct:ident $(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)?, + $to:ident + |$($method:ident = |$area:ident|$body:expr;)*) => { + impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Layout<$O> + for $Struct $(<$($($L)? $($T)?),+>)? { + $(fn $method (&$self, $area: <$O as Out>::Area) -> <$O as Out>::Area { + $body + })* + } + }; +} + +impl Layout for () {} + +impl> Layout for &L { /*FIXME*/ } + +impl> Layout for RwLock { /*FIXME*/ } + +impl> Layout for Option { /*FIXME*/ } + +//impl Layout for fn(&mut O) {} + +impl> Layout for Arc { + fn layout (&self, to: O::Area) -> O::Area { + (**self).layout(to) + } +} + +impl<'x, O: Out> Layout for &(dyn Draw + 'x) { + fn layout (&self, to: O::Area) -> O::Area { + Fill::xy(self).layout(to) + } +} + +mod layout_align; pub use self::layout_align::*; +mod layout_bsp; pub use self::layout_bsp::*; +mod layout_cond; pub use self::layout_cond::*; +mod layout_expand; pub use self::layout_expand::*; +mod layout_fill; pub use self::layout_fill::*; +mod layout_fixed; pub use self::layout_fixed::*; +mod layout_map; pub use self::layout_map::*; +mod layout_margin; pub use self::layout_margin::*; +mod layout_max; pub use self::layout_max::*; +mod layout_min; pub use self::layout_min::*; +mod layout_padding; pub use self::layout_padding::*; +mod layout_pull; pub use self::layout_pull::*; +mod layout_push; pub use self::layout_push::*; +mod layout_shrink; pub use self::layout_shrink::*; +mod layout_stack; pub use self::layout_stack::*; diff --git a/output/src/layout_align.rs b/output/src/layout/layout_align.rs similarity index 66% rename from output/src/layout_align.rs rename to output/src/layout/layout_align.rs index 76cc8fd..c8f0922 100644 --- a/output/src/layout_align.rs +++ b/output/src/layout/layout_align.rs @@ -1,9 +1,9 @@ //! ``` //! use ::tengri::{output::*, tui::*}; //! let area: [u16;4] = [10, 10, 20, 20]; -//! fn test (area: [u16;4], item: &impl Render, expected: [u16;4]) { +//! fn test (area: [u16;4], item: &impl Draw, expected: [u16;4]) { //! assert_eq!(Content::layout(item, area), expected); -//! assert_eq!(Render::layout(item, area), expected); +//! assert_eq!(Draw::layout(item, area), expected); //! }; //! //! let four = ||Fixed::xy(4, 4, ""); @@ -32,33 +32,35 @@ use crate::*; #[derive(Debug, Copy, Clone, Default)] pub enum Alignment { #[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W } -pub struct Align(Alignment, A); +pub struct Align(Alignment, T); -impl Align { - #[inline] pub const fn c (a: A) -> Self { Self(Alignment::Center, a) } - #[inline] pub const fn x (a: A) -> Self { Self(Alignment::X, a) } - #[inline] pub const fn y (a: A) -> Self { Self(Alignment::Y, a) } - #[inline] pub const fn n (a: A) -> Self { Self(Alignment::N, a) } - #[inline] pub const fn s (a: A) -> Self { Self(Alignment::S, a) } - #[inline] pub const fn e (a: A) -> Self { Self(Alignment::E, a) } - #[inline] pub const fn w (a: A) -> Self { Self(Alignment::W, a) } - #[inline] pub const fn nw (a: A) -> Self { Self(Alignment::NW, a) } - #[inline] pub const fn sw (a: A) -> Self { Self(Alignment::SW, a) } - #[inline] pub const fn ne (a: A) -> Self { Self(Alignment::NE, a) } - #[inline] pub const fn se (a: A) -> Self { Self(Alignment::SE, a) } +impl Align { + #[inline] pub const fn c (a: T) -> Self { Self(Alignment::Center, a) } + #[inline] pub const fn x (a: T) -> Self { Self(Alignment::X, a) } + #[inline] pub const fn y (a: T) -> Self { Self(Alignment::Y, a) } + #[inline] pub const fn n (a: T) -> Self { Self(Alignment::N, a) } + #[inline] pub const fn s (a: T) -> Self { Self(Alignment::S, a) } + #[inline] pub const fn e (a: T) -> Self { Self(Alignment::E, a) } + #[inline] pub const fn w (a: T) -> Self { Self(Alignment::W, a) } + #[inline] pub const fn nw (a: T) -> Self { Self(Alignment::NW, a) } + #[inline] pub const fn sw (a: T) -> Self { Self(Alignment::SW, a) } + #[inline] pub const fn ne (a: T) -> Self { Self(Alignment::NE, a) } + #[inline] pub const fn se (a: T) -> Self { Self(Alignment::SE, a) } } -impl> Render for Align { +impl> Layout for Align { fn layout (&self, on: E::Area) -> E::Area { self.0.align(on, &self.1) } - fn render (&self, to: &mut E) { - to.place(self.layout(to.area()), &self.1) +} +impl + Layout> Draw for Align { + fn draw (&self, to: &mut E) { + to.place_at(self.layout(to.area()), &self.1) } } impl Alignment { - fn align (&self, on: E::Area, content: impl Render) -> E::Area { + fn align (&self, on: E::Area, content: &impl Layout) -> E::Area { use Alignment::*; let it = content.layout(on).xywh(); let cx = on.x()+(on.w().minus(it.w())/2.into()); diff --git a/output/src/layout/layout_bsp.rs b/output/src/layout/layout_bsp.rs new file mode 100644 index 0000000..ff3e4aa --- /dev/null +++ b/output/src/layout/layout_bsp.rs @@ -0,0 +1,105 @@ +use crate::*; +use Direction::*; + +/// A split or layer. +pub struct Bsp( + pub(crate) Direction, + /// First element. + pub(crate) Head, + /// Second element. + pub(crate) Tail, +); + +impl Bsp { + #[inline] pub const fn n (a: Head, b: Tail) -> Self { Self(North, a, b) } + #[inline] pub const fn s (a: Head, b: Tail) -> Self { Self(South, a, b) } + #[inline] pub const fn e (a: Head, b: Tail) -> Self { Self(East, a, b) } + #[inline] pub const fn w (a: Head, b: Tail) -> Self { Self(West, a, b) } + #[inline] pub const fn a (a: Head, b: Tail) -> Self { Self(Above, a, b) } + #[inline] pub const fn b (a: Head, b: Tail) -> Self { Self(Below, a, b) } +} + +impl< + O: Out, + Head: Draw + Layout, + Tail: Draw + Layout +> Draw for Bsp { + fn draw (&self, to: &mut O) { + let [a, b, _] = bsp_areas(to.area(), self.0, &self.1, &self.2); + if self.0 == Below { + to.place_at(a, &self.1); + to.place_at(b, &self.2); + } else { + to.place_at(b, &self.2); + to.place_at(a, &self.1); + } + } +} + +impl, Tail: Layout> Layout for Bsp { + fn layout (&self, area: O::Area) -> O::Area { + bsp_areas(area, self.0, &self.1, &self.2)[2] + } +} + +fn bsp_areas ( + area: O::Area, direction: Direction, a: &impl Layout, b: &impl Layout, +) -> [O::Area;3] { + let [x, y, w, h] = area.xywh(); + let [aw, ah] = a.layout(area).wh(); + let [bw, bh] = b.layout(match direction { + Above | Below => area, + South => [x, y + ah, w, h.minus(ah)].into(), + North => [x, y, w, h.minus(ah)].into(), + East => [x + aw, y, w.minus(aw), h].into(), + West => [x, y, w.minus(aw), h].into(), + }).wh(); + match direction { + Above | Below => { + let [x, y, w, h] = area.center_xy([aw.max(bw), ah.max(bh)]); + let a = [(x + w/2.into()).minus(aw/2.into()), (y + h/2.into()).minus(ah/2.into()), aw, ah]; + let b = [(x + w/2.into()).minus(bw/2.into()), (y + h/2.into()).minus(bh/2.into()), bw, bh]; + [a.into(), b.into(), [x, y, w, h].into()] + }, + South => { + let [x, y, w, h] = area.center_xy([aw.max(bw), ah + bh]); + let a = [(x + w/2.into()).minus(aw/2.into()), y, aw, ah]; + let b = [(x + w/2.into()).minus(bw/2.into()), y + ah, bw, bh]; + [a.into(), b.into(), [x, y, w, h].into()] + }, + North => { + let [x, y, w, h] = area.center_xy([aw.max(bw), ah + bh]); + let a = [(x + (w/2.into())).minus(aw/2.into()), y + bh, aw, ah]; + let b = [(x + (w/2.into())).minus(bw/2.into()), y, bw, bh]; + [a.into(), b.into(), [x, y, w, h].into()] + }, + East => { + let [x, y, w, h] = area.center_xy([aw + bw, ah.max(bh)]); + let a = [x, (y + h/2.into()).minus(ah/2.into()), aw, ah]; + let b = [x + aw, (y + h/2.into()).minus(bh/2.into()), bw, bh]; + [a.into(), b.into(), [x, y, w, h].into()] + }, + West => { + let [x, y, w, h] = area.center_xy([aw + bw, ah.max(bh)]); + let a = [x + bw, (y + h/2.into()).minus(ah/2.into()), aw, ah]; + let b = [x, (y + h/2.into()).minus(bh/2.into()), bw, bh]; + [a.into(), b.into(), [x, y, w, h].into()] + }, + } +} + +/// Stack things on top of each other, +#[macro_export] macro_rules! lay (($($expr:expr),* $(,)?) => + {{ let bsp = (); $(let bsp = Bsp::b(bsp, $expr);)*; bsp }}); + +/// Stack southward. +#[macro_export] macro_rules! col (($($expr:expr),* $(,)?) => + {{ let bsp = (); $(let bsp = Bsp::s(bsp, $expr);)*; bsp }}); + +/// Stack northward. +#[macro_export] macro_rules! col_up (($($expr:expr),* $(,)?) => + {{ let bsp = (); $(let bsp = Bsp::n(bsp, $expr);)*; bsp }}); + +/// Stack eastward. +#[macro_export] macro_rules! row (($($expr:expr),* $(,)?) => + {{ let bsp = (); $(let bsp = Bsp::e(bsp, $expr);)*; bsp }}); diff --git a/output/src/layout_cond.rs b/output/src/layout/layout_cond.rs similarity index 93% rename from output/src/layout_cond.rs rename to output/src/layout/layout_cond.rs index 7a7bdc0..103166f 100644 --- a/output/src/layout_cond.rs +++ b/output/src/layout/layout_cond.rs @@ -6,7 +6,7 @@ impl When { /// Create a binary condition. pub const fn new (c: bool, a: A) -> Self { Self(c, a) } } -impl> Render for When { +impl> Layout for When { fn layout (&self, to: E::Area) -> E::Area { let Self(cond, item) = self; let mut area = E::Area::zero(); @@ -19,26 +19,32 @@ impl> Render for When { } area.into() } - fn render (&self, to: &mut E) { +} +impl> Draw for When { + fn draw (&self, to: &mut E) { let Self(cond, item) = self; - if *cond { item.render(to) } + if *cond { item.draw(to) } } } /// Show one item if a condition is true and another if the condition is false -pub struct Either(pub bool, pub A, pub B); -impl Either { +pub struct Either + Layout, B: Draw + Layout>(pub bool, pub A, pub B, pub PhantomData); +impl + Layout, B: Draw + Layout> Either { /// Create a ternary view condition. - pub const fn new (c: bool, a: A, b: B) -> Self { Self(c, a, b) } + pub const fn new (c: bool, a: A, b: B) -> Self { + Self(c, a, b, PhantomData) + } } -impl, B: Render> Render for Either { +impl + Layout, B: Draw + Layout> Layout for Either { fn layout (&self, to: E::Area) -> E::Area { - let Self(cond, a, b) = self; + let Self(cond, a, b, ..) = self; if *cond { a.layout(to) } else { b.layout(to) } } - fn render (&self, to: &mut E) { - let Self(cond, a, b) = self; - if *cond { a.render(to) } else { b.render(to) } +} +impl + Layout, B: Draw + Layout> Draw for Either { + fn draw (&self, to: &mut E) { + let Self(cond, a, b, ..) = self; + if *cond { a.draw(to) } else { b.draw(to) } } } diff --git a/output/src/layout/layout_expand.rs b/output/src/layout/layout_expand.rs new file mode 100644 index 0000000..8194fe2 --- /dev/null +++ b/output/src/layout/layout_expand.rs @@ -0,0 +1,39 @@ +use crate::*; + +pub enum Expand { X(U, A), Y(U, A), XY(U, U, A), } + +impl Expand { + #[inline] pub const fn x (x: U, item: A) -> Self { Self::X(x, item) } + #[inline] pub const fn y (y: U, item: A) -> Self { Self::Y(y, item) } + #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { Self::XY(x, y, item) } +} + +impl + Layout> Content for Expand { + fn content (&self) -> impl Draw + Layout + '_ { + use Expand::*; + match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } + } +} + +impl + Layout> Draw for Expand { + fn draw (&self, to: &mut O) { + to.place_at(self.layout(to.area()), &self.content()) + } +} + +impl Expand { + #[inline] pub fn dx (&self) -> U { + use Expand::*; + match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } + } + #[inline] pub fn dy (&self) -> U { + use Expand::*; + match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } + } +} + +impl> Layout for Expand { + fn layout (&self, to: O::Area) -> O::Area { + [to.x(), to.y(), to.w().plus(self.dx()), to.h().plus(self.dy())].into() + } +} diff --git a/output/src/layout/layout_fill.rs b/output/src/layout/layout_fill.rs new file mode 100644 index 0000000..6287714 --- /dev/null +++ b/output/src/layout/layout_fill.rs @@ -0,0 +1,55 @@ +use crate::*; + +pub enum Fill { + /// Use maximum width of area. + X(A), + /// Use maximum height of area. + Y(A), + /// Use maximum width and height of area. + XY(A) +} + +impl Fill { + #[inline] pub const fn x (item: T) -> Self { + Self::X(item) + } + #[inline] pub const fn y (item: T) -> Self { + Self::Y(item) + } + #[inline] pub const fn xy (item: T) -> Self { + Self::XY(item) + } + #[inline] pub const fn has_x (&self) -> bool { + matches!(self, Self::X(_) | Self::XY(_)) + } + #[inline] pub const fn has_y (&self) -> bool { + matches!(self, Self::Y(_) | Self::XY(_)) + } + #[inline] pub const fn content (&self) -> &T { + use Fill::*; + match self { X(item) | Y(item) | XY(item) => item } + } +} + +impl> Draw for Fill { + fn draw (&self, to: &mut O) { + to.place_at(self.layout(to.area()), &self.content()) + } +} + +impl> Layout for Fill { + fn min_w (&self, area: O::Area) -> O::Unit { + if self.has_x() { + area.w() + } else { + 0.into() + } + } + fn min_h (&self, area: O::Area) -> O::Unit { + if self.has_y() { + area.h() + } else { + 0.into() + } + } +} diff --git a/output/src/layout/layout_fixed.rs b/output/src/layout/layout_fixed.rs new file mode 100644 index 0000000..1d6eb1d --- /dev/null +++ b/output/src/layout/layout_fixed.rs @@ -0,0 +1,47 @@ +use crate::*; + +pub enum Fixed { X(U, A), Y(U, A), XY(U, U, A), } + +impl Fixed { + #[inline] pub const fn x (x: U, item: A) -> Self { + Self::X(x, item) + } + #[inline] pub const fn y (y: U, item: A) -> Self { + Self::Y(y, item) + } + #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { + Self::XY(x, y, item) + } + #[inline] pub const fn content (&self) -> &A { + use Fixed::*; + match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } + } +} + +impl Fixed { + #[inline] pub fn dx (&self) -> U { + use Fixed::*; + match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } + } + #[inline] pub fn dy (&self) -> U { + use Fixed::*; + match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } + } +} + +impl> Draw for Fixed { + fn draw (&self, to: &mut O) { + let area = Layout::::layout(&self, to.area()); + to.place_at(area, &self.content()) + } +} + +impl Layout for Fixed { + fn layout (&self, area: O::Area) -> O::Area { + [area.x(), area.y(), match self { + Fixed::X(w, _) | Fixed::XY(w, _, _) => *w, _ => area.w() + }, match self { + Fixed::Y(h, _) | Fixed::XY(_, h, _) => *h, _ => area.h() + }].into() + } +} diff --git a/output/src/layout/layout_iter.rs b/output/src/layout/layout_iter.rs new file mode 100644 index 0000000..e69de29 diff --git a/output/src/layout_map.rs b/output/src/layout/layout_map.rs similarity index 66% rename from output/src/layout_map.rs rename to output/src/layout/layout_map.rs index 4c8494c..cb230e2 100644 --- a/output/src/layout_map.rs +++ b/output/src/layout/layout_map.rs @@ -1,19 +1,19 @@ use crate::*; -/// Renders items from an iterator. -pub struct Map +/// Draws items from an iterator. +pub struct Map where I: Iterator + Send + Sync, F: Fn() -> I + Send + Sync, { - __: PhantomData<(E, B)>, + __: PhantomData<(O, B)>, /// Function that returns iterator over stacked components get_iter: F, /// Function that returns each stacked component get_item: G, } -impl<'a, E, A, B, I, F, G> Map where +impl<'a, O, A, B, I, F, G> Map where I: Iterator + Send + Sync + 'a, F: Fn() -> I + Send + Sync + 'a, { @@ -27,30 +27,30 @@ impl<'a, E, A, B, I, F, G> Map where } macro_rules! impl_map_direction (($name:ident, $axis:ident, $align:ident)=>{ - impl<'a, E, A, B, I, F> Map< - E, A, Push>>>, I, F, fn(A, usize)->B + impl<'a, O, A, B, I, F> Map< + O, A, Push>>>, I, F, fn(A, usize)->B > where - E: Output, - B: Render, + O: Out, + B: Draw + Layout, I: Iterator + Send + Sync + 'a, F: Fn() -> I + Send + Sync + 'a { pub const fn $name ( - size: E::Unit, + size: O::Unit, get_iter: F, get_item: impl Fn(A, usize)->B + Send + Sync ) -> Map< - E, A, - Push>>, + O, A, + Push>>, I, F, - impl Fn(A, usize)->Push>> + Send + Sync + impl Fn(A, usize)->Push>> + Send + Sync > { Map { __: PhantomData, get_iter, get_item: move |item: A, index: usize|{ // FIXME: multiply - let mut push: E::Unit = E::Unit::from(0u16); + let mut push: O::Unit = O::Unit::from(0u16); for _ in 0..index { push = push + size; } @@ -66,14 +66,14 @@ impl_map_direction!(south, y, n); impl_map_direction!(west, x, e); impl_map_direction!(north, y, s); -impl<'a, E, A, B, I, F, G> Render for Map where - E: Output, - B: Render, +impl<'a, O, A, B, I, F, G> Layout for Map where + O: Out, + B: Draw + Layout, I: Iterator + Send + Sync + 'a, F: Fn() -> I + Send + Sync + 'a, G: Fn(A, usize)->B + Send + Sync { - fn layout (&self, area: E::Area) -> E::Area { + fn layout (&self, area: O::Area) -> O::Area { let Self { get_iter, get_item, .. } = self; let mut index = 0; let [mut min_x, mut min_y] = area.center(); @@ -91,40 +91,48 @@ impl<'a, E, A, B, I, F, G> Render for Map where //[min_x.into(), min_y.into(), w.into(), h.into()].into() area.center_xy([w.into(), h.into()].into()).into() } - fn render (&self, to: &mut E) { +} +impl<'a, O, A, B, I, F, G> Draw for Map where + O: Out, + B: Draw + Layout, + I: Iterator + Send + Sync + 'a, + F: Fn() -> I + Send + Sync + 'a, + G: Fn(A, usize)->B + Send + Sync +{ + fn draw (&self, to: &mut O) { let Self { get_iter, get_item, .. } = self; let mut index = 0; - let area = Render::layout(self, to.area()); + let area = self.layout(to.area()); for item in get_iter() { let item = get_item(item, index); - //to.place(area.into(), &item); - to.place(item.layout(area), &item); + //to.place_at(area.into(), &item); + to.place_at(item.layout(area), &item); index += 1; } } } -#[inline] pub fn map_south( +#[inline] pub fn map_south( item_offset: O::Unit, item_height: O::Unit, - item: impl Render -) -> impl Render { + item: impl Draw + Layout +) -> impl Draw + Layout { Push::y(item_offset, Fixed::y(item_height, Fill::x(item))) } -#[inline] pub fn map_south_west( +#[inline] pub fn map_south_west( item_offset: O::Unit, item_height: O::Unit, - item: impl Render -) -> impl Render { + item: impl Draw + Layout +) -> impl Draw + Layout { Push::y(item_offset, Align::nw(Fixed::y(item_height, Fill::x(item)))) } -#[inline] pub fn map_east( +#[inline] pub fn map_east( item_offset: O::Unit, item_width: O::Unit, - item: impl Render -) -> impl Render { + item: impl Draw + Layout +) -> impl Draw + Layout { Push::x(item_offset, Align::w(Fixed::x(item_width, Fill::y(item)))) } diff --git a/output/src/layout/layout_margin.rs b/output/src/layout/layout_margin.rs new file mode 100644 index 0000000..cee3800 --- /dev/null +++ b/output/src/layout/layout_margin.rs @@ -0,0 +1,50 @@ +use crate::*; + +pub enum Margin { X(U, A), Y(U, A), XY(U, U, A), } + +impl Margin { + #[inline] pub const fn x (x: U, item: A) -> Self { + Self::X(x, item) + } + #[inline] pub const fn y (y: U, item: A) -> Self { + Self::Y(y, item) + } + #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { + Self::XY(x, y, item) + } + #[inline] pub const fn content (&self) -> &A { + use Margin::*; + match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } + } +} + +impl + Layout> Draw for Margin { + fn draw (&self, to: &mut E) { + to.place_at(self.layout(to.area()), &self.content()) + } +} + +impl Margin { + #[inline] pub fn dx (&self) -> U { + use Margin::*; + match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } + } + #[inline] pub fn dy (&self) -> U { + use Margin::*; + match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } + } +} + +impl> Layout for Margin { + fn layout (&self, area: E::Area) -> E::Area { + let area = self.content().layout(area); + let dx = self.dx(); + let dy = self.dy(); + [ + area.x().minus(dx), + area.y().minus(dy), + area.w().plus(dy.plus(dy)), + area.h().plus(dy.plus(dy)), + ].into() + } +} diff --git a/output/src/layout/layout_max.rs b/output/src/layout/layout_max.rs new file mode 100644 index 0000000..81467ae --- /dev/null +++ b/output/src/layout/layout_max.rs @@ -0,0 +1,47 @@ +use crate::*; + +pub enum Max { X(U, A), Y(U, A), XY(U, U, A), } + +impl Max { + #[inline] pub const fn x (x: U, item: A) -> Self { + Self::X(x, item) + } + #[inline] pub const fn y (y: U, item: A) -> Self { + Self::Y(y, item) + } + #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { + Self::XY(x, y, item) + } + #[inline] pub const fn content (&self) -> &A { + use Max::*; + match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } + } +} + +impl + Layout> Draw for Max { + fn draw (&self, to: &mut E) { + to.place_at(self.layout(to.area()), &self.content()) + } +} + +impl Max { + #[inline] pub fn dx (&self) -> U { + use Max::*; + match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } + } + #[inline] pub fn dy (&self) -> U { + use Max::*; + match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } + } +} + +impl> Layout for Max { + fn layout (&self, area: E::Area) -> E::Area { + let [x, y, w, h] = self.content().layout(area).xywh(); + match self { + Self::X(mw, _) => [x, y, w.min(*mw), h], + Self::Y(mh, _) => [x, y, w, h.min(*mh)], + Self::XY(mw, mh, _) => [x, y, w.min(*mw), h.min(*mh)], + }.into() + } +} diff --git a/output/src/layout/layout_min.rs b/output/src/layout/layout_min.rs new file mode 100644 index 0000000..73f8209 --- /dev/null +++ b/output/src/layout/layout_min.rs @@ -0,0 +1,47 @@ +use crate::*; + +pub enum Min { X(U, A), Y(U, A), XY(U, U, A), } + +impl Min { + #[inline] pub const fn x (x: U, item: A) -> Self { + Self::X(x, item) + } + #[inline] pub const fn y (y: U, item: A) -> Self { + Self::Y(y, item) + } + #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { + Self::XY(x, y, item) + } + #[inline] pub const fn content (&self) -> &A { + use Min::*; + match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } + } +} + +impl + Layout> Draw for Min { + fn draw (&self, to: &mut E) { + to.place_at(self.layout(to.area()), &self.content()) + } +} + +impl Min { + #[inline] pub fn dx (&self) -> U { + use Min::*; + match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } + } + #[inline] pub fn dy (&self) -> U { + use Min::*; + match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } + } +} + +impl> Layout for Min { + fn layout (&self, area: E::Area) -> E::Area { + let [x, y, w, h] = self.content().layout(area).xywh(); + match self { + Self::X(mw, _) => [x, y, w.max(*mw), h], + Self::Y(mh, _) => [x, y, w, h.max(*mh)], + Self::XY(mw, mh, _) => [x, y, w.max(*mw), h.max(*mh)], + }.into() + } +} diff --git a/output/src/layout/layout_padding.rs b/output/src/layout/layout_padding.rs new file mode 100644 index 0000000..5295fdf --- /dev/null +++ b/output/src/layout/layout_padding.rs @@ -0,0 +1,50 @@ +use crate::*; + +pub enum Padding { X(U, A), Y(U, A), XY(U, U, A), } + +impl Padding { + #[inline] pub const fn x (x: U, item: A) -> Self { + Self::X(x, item) + } + #[inline] pub const fn y (y: U, item: A) -> Self { + Self::Y(y, item) + } + #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { + Self::XY(x, y, item) + } + #[inline] pub const fn content (&self) -> &A { + use Padding::*; + match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } + } +} + +impl + Layout> Draw for Padding { + fn draw (&self, to: &mut E) { + to.place_at(self.layout(to.area()), &self.content()) + } +} + +impl Padding { + #[inline] pub fn dx (&self) -> U { + use Padding::*; + match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } + } + #[inline] pub fn dy (&self) -> U { + use Padding::*; + match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } + } +} + +impl> Layout for Padding { + fn layout (&self, area: E::Area) -> E::Area { + let area = self.content().layout(area); + let dx = self.dx(); + let dy = self.dy(); + [ + area.x().plus(dx), + area.y().plus(dy), + area.w().minus(dy.plus(dy)), + area.h().minus(dy.plus(dy)) + ].into() + } +} diff --git a/output/src/layout/layout_pull.rs b/output/src/layout/layout_pull.rs new file mode 100644 index 0000000..9fdda7f --- /dev/null +++ b/output/src/layout/layout_pull.rs @@ -0,0 +1,48 @@ +use crate::*; + +pub enum Pull { X(U, A), Y(U, A), XY(U, U, A), } + +impl Pull { + #[inline] pub const fn x (x: U, item: A) -> Self { + Self::X(x, item) + } + #[inline] pub const fn y (y: U, item: A) -> Self { + Self::Y(y, item) + } + #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { + Self::XY(x, y, item) + } + #[inline] pub const fn content (&self) -> &A { + use Pull::*; + match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } + } +} + +impl + Layout> Draw for Pull { + fn draw (&self, to: &mut E) { + to.place_at(self.layout(to.area()), &self.content()) + } +} + +impl Pull { + #[inline] pub fn dx (&self) -> U { + use Pull::*; + match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } + } + #[inline] pub fn dy (&self) -> U { + use Pull::*; + match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } + } +} + +impl> Layout for Pull { + fn layout (&self, to: E::Area) -> E::Area { + let area = self.content().layout(to); + [ + area.x().minus(self.dx()), + area.y().minus(self.dy()), + area.w(), + area.h() + ].into() + } +} diff --git a/output/src/layout/layout_push.rs b/output/src/layout/layout_push.rs new file mode 100644 index 0000000..4c507b3 --- /dev/null +++ b/output/src/layout/layout_push.rs @@ -0,0 +1,43 @@ +use crate::*; + +pub enum Push { X(U, A), Y(U, A), XY(U, U, A), } + +impl Push { + #[inline] pub const fn x (x: U, item: A) -> Self { + Self::X(x, item) + } + #[inline] pub const fn y (y: U, item: A) -> Self { + Self::Y(y, item) + } + #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { + Self::XY(x, y, item) + } + #[inline] pub const fn content (&self) -> &A { + use Push::*; + match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } + } +} + +impl + Layout> Draw for Push { + fn draw (&self, to: &mut E) { + to.place_at(self.layout(to.area()), &self.content()) + } +} + +impl Push { + #[inline] pub fn dx (&self) -> U { + use Push::*; + match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } + } + #[inline] pub fn dy (&self) -> U { + use Push::*; + match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } + } +} + +impl> Layout for Push { + fn layout (&self, area: E::Area) -> E::Area { + let area = self.content().layout(area); + [area.x().plus(self.dx()), area.y().plus(self.dy()), area.w(), area.h()].into() + } +} diff --git a/output/src/layout/layout_shrink.rs b/output/src/layout/layout_shrink.rs new file mode 100644 index 0000000..6a64425 --- /dev/null +++ b/output/src/layout/layout_shrink.rs @@ -0,0 +1,43 @@ +use crate::*; + +pub enum Shrink { X(U, A), Y(U, A), XY(U, U, A), } + +impl Shrink { + #[inline] pub const fn x (x: U, item: A) -> Self { + Self::X(x, item) + } + #[inline] pub const fn y (y: U, item: A) -> Self { + Self::Y(y, item) + } + #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { + Self::XY(x, y, item) + } + #[inline] pub const fn content (&self) -> &A { + use Shrink::*; + match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, } + } +} + +impl + Layout> Draw for Shrink { + fn draw (&self, to: &mut E) { + to.place_at(self.layout(to.area()), &self.content()) + } +} + +impl Shrink { + #[inline] pub fn dx (&self) -> U { + use Shrink::*; + match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } + } + #[inline] pub fn dy (&self) -> U { + use Shrink::*; + match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } + } +} + +impl> Layout for Shrink { + fn layout (&self, to: E::Area) -> E::Area { + let area = self.content().layout(to); + [area.x(), area.y(), area.w().minus(self.dx()), area.h().minus(self.dy())].into() + } +} diff --git a/output/src/layout/layout_stack.rs b/output/src/layout/layout_stack.rs new file mode 100644 index 0000000..d5a311d --- /dev/null +++ b/output/src/layout/layout_stack.rs @@ -0,0 +1,170 @@ +//use crate::*; +//use Direction::*; + +//pub struct Stack<'x, E, F1> { + //__: PhantomData<&'x (E, F1)>, + //direction: Direction, + //callback: F1 +//} + +//impl<'x, E, F1> Stack<'x, E, F1> { + //pub fn new (direction: Direction, callback: F1) -> Self { + //Self { direction, callback, __: Default::default(), } + //} + //pub fn above (callback: F1) -> Self { + //Self::new(Above, callback) + //} + //pub fn below (callback: F1) -> Self { + //Self::new(Below, callback) + //} + //pub fn north (callback: F1) -> Self { + //Self::new(North, callback) + //} + //pub fn south (callback: F1) -> Self { + //Self::new(South, callback) + //} + //pub fn east (callback: F1) -> Self { + //Self::new(East, callback) + //} + //pub fn west (callback: F1) -> Self { + //Self::new(West, callback) + //} +//} + +//impl<'x, E: Out, F1: Fn(&mut dyn FnMut(&dyn Layout))> Layout for Stack<'x, E, F1> { + //fn layout (&self, to: E::Area) -> E::Area { + //let state = StackLayoutState::::new(self.direction, to); + //(self.callback)(&mut |component: &dyn Layout|{ + //let StackLayoutState { x, y, w_remaining, h_remaining, .. } = *state.borrow(); + //let [_, _, w, h] = component.layout([x, y, w_remaining, h_remaining].into()).xywh(); + //state.borrow_mut().grow(w, h); + //}); + //let StackLayoutState { w_used, h_used, .. } = *state.borrow(); + //match self.direction { + //North | West => { todo!() }, + //South | East => { [to.x(), to.y(), w_used, h_used].into() }, + //_ => unreachable!(), + //} + //} +//} +//impl<'x, E: Out, F1: Fn(&mut dyn FnMut(&dyn Draw))> Draw for Stack<'x, E, F1> { + //fn draw (&self, to: &mut E) { + //let state = StackLayoutState::::new(self.direction, to.area()); + //let to = Rc::new(RefCell::new(to)); + //(self.callback)(&mut |component: &dyn Draw|{ + //let StackLayoutState { x, y, w_remaining, h_remaining, .. } = *state.borrow(); + //let layout = component.layout([x, y, w_remaining, h_remaining].into()); + //state.borrow_mut().grow(layout.w(), layout.h()); + //to.borrow_mut().place_at(layout, component); + //}); + //} +//} + +//#[derive(Copy, Clone)] +//struct StackLayoutState { + //direction: Direction, + //x: E::Unit, + //y: E::Unit, + //w_used: E::Unit, + //h_used: E::Unit, + //w_remaining: E::Unit, + //h_remaining: E::Unit, +//} +//impl StackLayoutState { + //fn new (direction: Direction, area: E::Area) -> std::rc::Rc> { + //let [x, y, w_remaining, h_remaining] = area.xywh(); + //std::rc::Rc::new(std::cell::RefCell::new(Self { + //direction, + //x, y, w_remaining, h_remaining, + //w_used: E::Unit::zero(), h_used: E::Unit::zero() + //})) + //} + //fn grow (&mut self, w: E::Unit, h: E::Unit) -> &mut Self { + //match self.direction { + //South => { self.y = self.y.plus(h); + //self.h_used = self.h_used.plus(h); + //self.h_remaining = self.h_remaining.minus(h); + //self.w_used = self.w_used.max(w); }, + //East => { self.x = self.x.plus(w); + //self.w_used = self.w_used.plus(w); + //self.w_remaining = self.w_remaining.minus(w); + //self.h_used = self.h_used.max(h); }, + //North | West => { todo!() }, + //Above | Below => {}, + //}; + //self + //} + //fn area_remaining (&self) -> E::Area { + //[self.x, self.y, self.w_remaining, self.h_remaining].into() + //} +//} + +////pub struct Stack<'a, E, F1> { + ////__: PhantomData<&'a (E, F1)>, + ////direction: Direction, + ////callback: F1 +////} +////impl<'a, E, F1> Stack<'a, E, F1> where + ////E: Out, F1: Fn(&mut dyn FnMut(&'a dyn Draw)) + Send + Sync, +////{ + ////pub fn north (callback: F1) -> Self { Self::new(North, callback) } + ////pub fn south (callback: F1) -> Self { Self::new(South, callback) } + ////pub fn east (callback: F1) -> Self { Self::new(East, callback) } + ////pub fn west (callback: F1) -> Self { Self::new(West, callback) } + ////pub fn above (callback: F1) -> Self { Self::new(Above, callback) } + ////pub fn below (callback: F1) -> Self { Self::new(Below, callback) } + ////pub fn new (direction: Direction, callback: F1) -> Self { + ////Self { direction, callback, __: Default::default(), } + ////} +////} +////impl<'a, E, F1> Draw for Stack<'a, E, F1> where + ////E: Out, F1: Fn(&mut dyn FnMut(&'a dyn Draw)) + Send + Sync, +////{ + ////fn layout (&self, to: E::Area) -> E::Area { + ////let state = StackLayoutState::::new(self.direction, to); + ////let mut adder = { + ////let state = state.clone(); + ////move|component: &dyn Draw|{ + ////let [w, h] = component.layout(state.borrow().area_remaining()).wh(); + ////state.borrow_mut().grow(w, h); + ////} + ////}; + ////(self.callback)(&mut adder); + ////let StackLayoutState { w_used, h_used, .. } = *state.borrow(); + ////match self.direction { + ////North | West => { todo!() }, + ////South | East => { [to.x(), to.y(), w_used, h_used].into() }, + ////Above | Below => { [to.x(), to.y(), to.w(), to.h()].into() }, + ////} + ////} + ////fn draw (&self, to: &mut E) { + ////let state = StackLayoutState::::new(self.direction, to.area()); + ////let mut adder = { + ////let state = state.clone(); + ////move|component: &dyn Draw|{ + ////let [x, y, w, h] = component.layout(state.borrow().area_remaining()).xywh(); + ////state.borrow_mut().grow(w, h); + ////to.place_at([x, y, w, h].into(), component); + ////} + ////}; + ////(self.callback)(&mut adder); + ////} +////} + +//[>Stack::down(|add|{ + //let mut i = 0; + //for (_, name) in self.dirs.iter() { + //if i >= self.scroll { + //add(&Tui::bold(i == self.index, name.as_str()))?; + //} + //i += 1; + //} + //for (_, name) in self.files.iter() { + //if i >= self.scroll { + //add(&Tui::bold(i == self.index, name.as_str()))?; + //} + //i += 1; + //} + //add(&format!("{}/{i}", self.index))?; + //Ok(()) +//}));*/ diff --git a/output/src/layout_bsp.rs b/output/src/layout_bsp.rs deleted file mode 100644 index 73d6882..0000000 --- a/output/src/layout_bsp.rs +++ /dev/null @@ -1,97 +0,0 @@ -use crate::*; -use Direction::*; - -/// A split or layer. -pub struct Bsp( - pub(crate) Direction, - pub(crate) A, - pub(crate) B, -); -impl Bsp { - #[inline] pub const fn n (a: A, b: B) -> Self { Self(North, a, b) } - #[inline] pub const fn s (a: A, b: B) -> Self { Self(South, a, b) } - #[inline] pub const fn e (a: A, b: B) -> Self { Self(East, a, b) } - #[inline] pub const fn w (a: A, b: B) -> Self { Self(West, a, b) } - #[inline] pub const fn a (a: A, b: B) -> Self { Self(Above, a, b) } - #[inline] pub const fn b (a: A, b: B) -> Self { Self(Below, a, b) } -} -impl, B: Render> Render for Bsp { - fn layout (&self, outer: E::Area) -> E::Area { let [_, _, c] = self.areas(outer); c } - fn render (&self, to: &mut E) { - let [area_a, area_b, _] = self.areas(to.area()); - let (a, b) = self.contents(); - match self.0 { - Below => { to.place(area_a, a); to.place(area_b, b); }, - _ => { to.place(area_b, b); to.place(area_a, a); } - } - } -} -impl, B: Render> BspAreas for Bsp { - fn direction (&self) -> Direction { self.0 } - fn contents (&self) -> (&A, &B) { (&self.1, &self.2) } -} -pub trait BspAreas, B: Render> { - fn direction (&self) -> Direction; - fn contents (&self) -> (&A, &B); - fn areas (&self, outer: E::Area) -> [E::Area;3] { - let direction = self.direction(); - let [x, y, w, h] = outer.xywh(); - let (a, b) = self.contents(); - let [aw, ah] = a.layout(outer).wh(); - let [bw, bh] = b.layout(match direction { - Above | Below => outer, - South => [x, y + ah, w, h.minus(ah)].into(), - North => [x, y, w, h.minus(ah)].into(), - East => [x + aw, y, w.minus(aw), h].into(), - West => [x, y, w.minus(aw), h].into(), - }).wh(); - match direction { - Above | Below => { - let [x, y, w, h] = outer.center_xy([aw.max(bw), ah.max(bh)]); - let a = [(x + w/2.into()).minus(aw/2.into()), (y + h/2.into()).minus(ah/2.into()), aw, ah]; - let b = [(x + w/2.into()).minus(bw/2.into()), (y + h/2.into()).minus(bh/2.into()), bw, bh]; - [a.into(), b.into(), [x, y, w, h].into()] - }, - South => { - let [x, y, w, h] = outer.center_xy([aw.max(bw), ah + bh]); - let a = [(x + w/2.into()).minus(aw/2.into()), y, aw, ah]; - let b = [(x + w/2.into()).minus(bw/2.into()), y + ah, bw, bh]; - [a.into(), b.into(), [x, y, w, h].into()] - }, - North => { - let [x, y, w, h] = outer.center_xy([aw.max(bw), ah + bh]); - let a = [(x + (w/2.into())).minus(aw/2.into()), y + bh, aw, ah]; - let b = [(x + (w/2.into())).minus(bw/2.into()), y, bw, bh]; - [a.into(), b.into(), [x, y, w, h].into()] - }, - East => { - let [x, y, w, h] = outer.center_xy([aw + bw, ah.max(bh)]); - let a = [x, (y + h/2.into()).minus(ah/2.into()), aw, ah]; - let b = [x + aw, (y + h/2.into()).minus(bh/2.into()), bw, bh]; - [a.into(), b.into(), [x, y, w, h].into()] - }, - West => { - let [x, y, w, h] = outer.center_xy([aw + bw, ah.max(bh)]); - let a = [x + bw, (y + h/2.into()).minus(ah/2.into()), aw, ah]; - let b = [x, (y + h/2.into()).minus(bh/2.into()), bw, bh]; - [a.into(), b.into(), [x, y, w, h].into()] - }, - } - } -} - -/// Stack things on top of each other, -#[macro_export] macro_rules! lay (($($expr:expr),* $(,)?) => - {{ let bsp = (); $(let bsp = Bsp::b(bsp, $expr);)*; bsp }}); - -/// Stack southward. -#[macro_export] macro_rules! col (($($expr:expr),* $(,)?) => - {{ let bsp = (); $(let bsp = Bsp::s(bsp, $expr);)*; bsp }}); - -/// Stack northward. -#[macro_export] macro_rules! col_up (($($expr:expr),* $(,)?) => - {{ let bsp = (); $(let bsp = Bsp::n(bsp, $expr);)*; bsp }}); - -/// Stack eastward. -#[macro_export] macro_rules! row (($($expr:expr),* $(,)?) => - {{ let bsp = (); $(let bsp = Bsp::e(bsp, $expr);)*; bsp }}); diff --git a/output/src/layout_stack.rs b/output/src/layout_stack.rs deleted file mode 100644 index c316c39..0000000 --- a/output/src/layout_stack.rs +++ /dev/null @@ -1,168 +0,0 @@ -use crate::*; -use Direction::*; - -pub struct Stack<'x, E, F1> { - __: PhantomData<&'x (E, F1)>, - direction: Direction, - callback: F1 -} - -impl<'x, E, F1> Stack<'x, E, F1> { - pub fn new (direction: Direction, callback: F1) -> Self { - Self { direction, callback, __: Default::default(), } - } - pub fn above (callback: F1) -> Self { - Self::new(Above, callback) - } - pub fn below (callback: F1) -> Self { - Self::new(Below, callback) - } - pub fn north (callback: F1) -> Self { - Self::new(North, callback) - } - pub fn south (callback: F1) -> Self { - Self::new(South, callback) - } - pub fn east (callback: F1) -> Self { - Self::new(East, callback) - } - pub fn west (callback: F1) -> Self { - Self::new(West, callback) - } -} - -impl<'x, E: Output, F1: Fn(&mut dyn FnMut(&dyn Render))> Render for Stack<'x, E, F1> { - fn layout (&self, to: E::Area) -> E::Area { - let state = StackLayoutState::::new(self.direction, to); - (self.callback)(&mut |component: &dyn Render|{ - let StackLayoutState { x, y, w_remaining, h_remaining, .. } = *state.borrow(); - let [_, _, w, h] = component.layout([x, y, w_remaining, h_remaining].into()).xywh(); - state.borrow_mut().grow(w, h); - }); - let StackLayoutState { w_used, h_used, .. } = *state.borrow(); - match self.direction { - North | West => { todo!() }, - South | East => { [to.x(), to.y(), w_used, h_used].into() }, - _ => unreachable!(), - } - } - fn render (&self, to: &mut E) { - let state = StackLayoutState::::new(self.direction, to.area()); - let to = Rc::new(RefCell::new(to)); - (self.callback)(&mut |component: &dyn Render|{ - let StackLayoutState { x, y, w_remaining, h_remaining, .. } = *state.borrow(); - let layout = component.layout([x, y, w_remaining, h_remaining].into()); - state.borrow_mut().grow(layout.w(), layout.h()); - to.borrow_mut().place(layout, component); - }); - } -} - -#[derive(Copy, Clone)] -struct StackLayoutState { - direction: Direction, - x: E::Unit, - y: E::Unit, - w_used: E::Unit, - h_used: E::Unit, - w_remaining: E::Unit, - h_remaining: E::Unit, -} -impl StackLayoutState { - fn new (direction: Direction, area: E::Area) -> std::rc::Rc> { - let [x, y, w_remaining, h_remaining] = area.xywh(); - std::rc::Rc::new(std::cell::RefCell::new(Self { - direction, - x, y, w_remaining, h_remaining, - w_used: E::Unit::zero(), h_used: E::Unit::zero() - })) - } - fn grow (&mut self, w: E::Unit, h: E::Unit) -> &mut Self { - match self.direction { - South => { self.y = self.y.plus(h); - self.h_used = self.h_used.plus(h); - self.h_remaining = self.h_remaining.minus(h); - self.w_used = self.w_used.max(w); }, - East => { self.x = self.x.plus(w); - self.w_used = self.w_used.plus(w); - self.w_remaining = self.w_remaining.minus(w); - self.h_used = self.h_used.max(h); }, - North | West => { todo!() }, - Above | Below => {}, - }; - self - } - fn area_remaining (&self) -> E::Area { - [self.x, self.y, self.w_remaining, self.h_remaining].into() - } -} - -//pub struct Stack<'a, E, F1> { - //__: PhantomData<&'a (E, F1)>, - //direction: Direction, - //callback: F1 -//} -//impl<'a, E, F1> Stack<'a, E, F1> where - //E: Output, F1: Fn(&mut dyn FnMut(&'a dyn Render)) + Send + Sync, -//{ - //pub fn north (callback: F1) -> Self { Self::new(North, callback) } - //pub fn south (callback: F1) -> Self { Self::new(South, callback) } - //pub fn east (callback: F1) -> Self { Self::new(East, callback) } - //pub fn west (callback: F1) -> Self { Self::new(West, callback) } - //pub fn above (callback: F1) -> Self { Self::new(Above, callback) } - //pub fn below (callback: F1) -> Self { Self::new(Below, callback) } - //pub fn new (direction: Direction, callback: F1) -> Self { - //Self { direction, callback, __: Default::default(), } - //} -//} -//impl<'a, E, F1> Render for Stack<'a, E, F1> where - //E: Output, F1: Fn(&mut dyn FnMut(&'a dyn Render)) + Send + Sync, -//{ - //fn layout (&self, to: E::Area) -> E::Area { - //let state = StackLayoutState::::new(self.direction, to); - //let mut adder = { - //let state = state.clone(); - //move|component: &dyn Render|{ - //let [w, h] = component.layout(state.borrow().area_remaining()).wh(); - //state.borrow_mut().grow(w, h); - //} - //}; - //(self.callback)(&mut adder); - //let StackLayoutState { w_used, h_used, .. } = *state.borrow(); - //match self.direction { - //North | West => { todo!() }, - //South | East => { [to.x(), to.y(), w_used, h_used].into() }, - //Above | Below => { [to.x(), to.y(), to.w(), to.h()].into() }, - //} - //} - //fn render (&self, to: &mut E) { - //let state = StackLayoutState::::new(self.direction, to.area()); - //let mut adder = { - //let state = state.clone(); - //move|component: &dyn Render|{ - //let [x, y, w, h] = component.layout(state.borrow().area_remaining()).xywh(); - //state.borrow_mut().grow(w, h); - //to.place([x, y, w, h].into(), component); - //} - //}; - //(self.callback)(&mut adder); - //} -//} - -/*Stack::down(|add|{ - let mut i = 0; - for (_, name) in self.dirs.iter() { - if i >= self.scroll { - add(&Tui::bold(i == self.index, name.as_str()))?; - } - i += 1; - } - for (_, name) in self.files.iter() { - if i >= self.scroll { - add(&Tui::bold(i == self.index, name.as_str()))?; - } - i += 1; - } - add(&format!("{}/{i}", self.index))?; - Ok(()) -}));*/ diff --git a/output/src/layout_xy.rs b/output/src/layout_xy.rs deleted file mode 100644 index 52d99a6..0000000 --- a/output/src/layout_xy.rs +++ /dev/null @@ -1,148 +0,0 @@ -//! Transform: -//! ``` -//! use ::tengri::{output::*, tui::*}; -//! let area: [u16;4] = [10, 10, 20, 20]; -//! fn test (area: [u16;4], item: &impl Render, expected: [u16;4]) { -//! assert_eq!(item.layout(area), expected); -//! }; -//! test(area, &(), [20, 20, 0, 0]); -//! -//! test(area, &Fill::xy(()), area); -//! test(area, &Fill::x(()), [10, 20, 20, 0]); -//! test(area, &Fill::y(()), [20, 10, 0, 20]); -//! -//! //FIXME:test(area, &Fixed::x(4, ()), [18, 20, 4, 0]); -//! //FIXME:test(area, &Fixed::y(4, ()), [20, 18, 0, 4]); -//! //FIXME:test(area, &Fixed::xy(4, 4, unit), [18, 18, 4, 4]); -//! ``` -use crate::*; - -/// Defines an enum that transforms its content -/// along either the X axis, the Y axis, or both. -macro_rules! transform_xy { - ($x:literal $y:literal $xy:literal |$self:ident : $Enum:ident, $to:ident|$area:expr) => { - pub enum $Enum { X(A), Y(A), XY(A) } - impl $Enum { - #[inline] pub const fn x (item: A) -> Self { Self::X(item) } - #[inline] pub const fn y (item: A) -> Self { Self::Y(item) } - #[inline] pub const fn xy (item: A) -> Self { Self::XY(item) } - } - impl> Content for $Enum { - fn content (&self) -> Option + '_> { - use $Enum::*; - Some(match self { X(item) | Y(item) | XY(item) => item, }) - } - } - impl> Render for $Enum { - fn layout (&$self, $to: ::Area) -> ::Area { - use $Enum::*; - $area - } - fn render (&self, output: &mut E) { - output.place(self.layout(output.area()), &self.content()) - } - } - } -} - -/// Defines an enum that parametrically transforms its content -/// along either the X axis, the Y axis, or both. -macro_rules! transform_xy_unit { - ($x:literal $y:literal $xy:literal |$self:ident : $Enum:ident, $to:ident|$layout:expr) => { - pub enum $Enum { X(U, A), Y(U, A), XY(U, U, A), } - impl $Enum { - #[inline] pub const fn x (x: U, item: A) -> Self { Self::X(x, item) } - #[inline] pub const fn y (y: U, item: A) -> Self { Self::Y(y, item) } - #[inline] pub const fn xy (x: U, y: U, item: A) -> Self { Self::XY(x, y, item) } - } - impl> Content for $Enum { - fn content (&self) -> Option + '_> { - use $Enum::*; - Some(match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, }) - } - } - impl> Render for $Enum { - fn layout (&$self, $to: E::Area) -> E::Area { - $layout.into() - } - fn render (&self, output: &mut E) { - output.place(self.layout(output.area()), &self.content()) - } - } - impl $Enum { - #[inline] pub fn dx (&self) -> U { - use $Enum::*; - match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } - } - #[inline] pub fn dy (&self) -> U { - use $Enum::*; - match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } - } - } - } -} - -transform_xy!("fill/x" "fill/y" "fill/xy" |self: Fill, to|{ - let [x0, y0, wmax, hmax] = to.xywh(); - let [x, y, w, h] = self.content().layout(to).xywh(); - match self { - X(_) => [x0, y, wmax, h], - Y(_) => [x, y0, w, hmax], - XY(_) => [x0, y0, wmax, hmax], - }.into() -}); - -transform_xy_unit!("fixed/x" "fixed/y" "fixed/xy"|self: Fixed, area|{ - let [x, y, w, h] = area.xywh(); - let [x, y, w, h] = self.content().layout(match self { - Self::X(fw, _) => [x, y, *fw, h], - Self::Y(fh, _) => [x, y, w, *fh], - Self::XY(fw, fh, _) => [x, y, *fw, *fh], - }.into()).xywh(); - let fixed_area = match self { - Self::X(fw, _) => [x, y, *fw, h], - Self::Y(fh, _) => [x, y, w, *fh], - Self::XY(fw, fh, _) => [x, y, *fw, *fh], - }; - fixed_area -}); - -transform_xy_unit!("min/x" "min/y" "min/xy"|self: Min, area|{ - let [x, y, w, h] = self.content().layout(area).xywh(); - match self { - Self::X(mw, _) => [x, y, w.max(*mw), h], - Self::Y(mh, _) => [x, y, w, h.max(*mh)], - Self::XY(mw, mh, _) => [x, y, w.max(*mw), h.max(*mh)], } }); - -transform_xy_unit!("max/x" "max/y" "max/xy"|self: Max, area|{ - let [x, y, w, h] = area.xywh(); - self.content().layout(match self { - Self::X(fw, _) => [x, y, *fw, h], - Self::Y(fh, _) => [x, y, w, *fh], - Self::XY(fw, fh, _) => [x, y, *fw, *fh], }.into()) }); - -transform_xy_unit!("shrink/x" "shrink/y" "shrink/xy"|self: Shrink, area|self.content().layout( - [area.x(), area.y(), area.w().minus(self.dx()), area.h().minus(self.dy())].into())); - -transform_xy_unit!("expand/x" "expand/y" "expand/xy"|self: Expand, area|self.content().layout( - [area.x(), area.y(), area.w().plus(self.dx()), area.h().plus(self.dy())].into())); - -transform_xy_unit!("push/x" "push/y" "push/xy"|self: Push, area|{ - let area = self.content().layout(area); - [area.x().plus(self.dx()), area.y().plus(self.dy()), area.w(), area.h()] }); - -transform_xy_unit!("pull/x" "pull/y" "pull/xy"|self: Pull, area|{ - let area = self.content().layout(area); - [area.x().minus(self.dx()), area.y().minus(self.dy()), area.w(), area.h()] }); - -transform_xy_unit!("margin/x" "margin/y" "margin/xy"|self: Margin, area|{ - let area = self.content().layout(area); - let dx = self.dx(); - let dy = self.dy(); - [area.x().minus(dx), area.y().minus(dy), area.w().plus(dy.plus(dy)), area.h().plus(dy.plus(dy))] }); - -transform_xy_unit!("padding/x" "padding/y" "padding/xy"|self: Padding, area|{ - let area = self.content().layout(area); - let dx = self.dx(); - let dy = self.dy(); - [area.x().plus(dx), area.y().plus(dy), area.w().minus(dy.plus(dy)), area.h().minus(dy.plus(dy))] }); diff --git a/output/src/lib.rs b/output/src/lib.rs deleted file mode 100644 index df18d69..0000000 --- a/output/src/lib.rs +++ /dev/null @@ -1,35 +0,0 @@ -#![feature(step_trait)] -#![feature(type_alias_impl_trait)] -#![feature(impl_trait_in_assoc_type)] -//#![feature(non_lifetime_binders)] - -pub(crate) use std::cell::RefCell; -pub(crate) use std::fmt::{Debug, Display}; -pub(crate) use std::marker::PhantomData; -pub(crate) use std::ops::{Add, Sub, Mul, Div, Deref}; -pub(crate) use std::rc::Rc; -pub(crate) use std::sync::RwLock; -pub(crate) use std::sync::{Arc, atomic::{AtomicUsize, Ordering::Relaxed}}; -pub(crate) use tengri_core::*; -#[cfg(feature = "dsl")] pub(crate) use ::tengri_dsl::*; - -mod output; pub use self::output::*; -mod output_render; pub use self::output_render::*; -mod output_content; pub use self::output_content::*; -mod output_thunk; pub use self::output_thunk::*; -mod space_area; pub use self::space_area::*; -mod space_coordinate; pub use self::space_coordinate::*; -mod space_direction; pub use self::space_direction::*; -mod space_measure; pub use self::space_measure::*; -mod space_size; pub use self::space_size::*; -mod layout_align; pub use self::layout_align::*; -mod layout_bsp; pub use self::layout_bsp::*; -mod layout_cond; pub use self::layout_cond::*; -mod layout_map; pub use self::layout_map::*; -mod layout_stack; pub use self::layout_stack::*; -mod layout_xy; pub use self::layout_xy::*; - -pub(crate) use self::Direction::*; - -#[cfg(test)] mod test; -#[cfg(test)] pub(crate) use proptest_derive::Arbitrary; diff --git a/output/src/output.rs b/output/src/output.rs index bc9e2b9..474ea55 100644 --- a/output/src/output.rs +++ b/output/src/output.rs @@ -1,7 +1,28 @@ -use crate::*; +#![feature(step_trait)] +#![feature(type_alias_impl_trait)] +#![feature(impl_trait_in_assoc_type)] +#![feature(const_precise_live_drops)] +#![feature(type_changing_struct_update)] +//#![feature(non_lifetime_binders)] -/// Render target. -pub trait Output: Send + Sync + Sized { +mod content; pub use self::content::*; +mod draw; pub use self::draw::*; +mod group; pub use self::group::*; +mod layout; pub use self::layout::*; +mod space; pub use self::space::*; +mod thunk; pub use self::thunk::*; +mod widget; pub use self::widget::*; +pub(crate) use self::Direction::*; +pub(crate) use std::fmt::{Debug, Display}; +pub(crate) use std::marker::PhantomData; +pub(crate) use std::ops::{Add, Sub, Mul, Div, Deref}; +pub(crate) use std::rc::Rc; +pub(crate) use std::sync::{Arc, RwLock, atomic::{AtomicUsize, Ordering::Relaxed}}; +pub(crate) use tengri_core::*; +#[cfg(feature = "dsl")] pub(crate) use ::tengri_dsl::*; + +/// Draw target. +pub trait Out: Send + Sync + Sized { /// Unit of length type Unit: Coordinate; /// Rectangle without offset @@ -12,11 +33,26 @@ pub trait Output: Send + Sync + Sized { fn area (&self) -> Self::Area; /// Mutable pointer to area fn area_mut (&mut self) -> &mut Self::Area; - /// Render widget in area - fn place <'t, T: Render + ?Sized> (&mut self, area: Self::Area, content: &'t T); + /// Draw widget in area + fn place_at <'t, T: Draw + ?Sized> (&mut self, area: Self::Area, content: &'t T); + + fn place <'t, T: Draw + Layout + ?Sized> (&mut self, content: &'t T) { + self.place_at(content.layout(self.area()), content) + } #[inline] fn x (&self) -> Self::Unit { self.area().x() } #[inline] fn y (&self) -> Self::Unit { self.area().y() } #[inline] fn w (&self) -> Self::Unit { self.area().w() } #[inline] fn h (&self) -> Self::Unit { self.area().h() } #[inline] fn wh (&self) -> Self::Size { self.area().wh().into() } } + +#[cfg(test)] mod test; +#[cfg(test)] pub(crate) use proptest_derive::Arbitrary; + +//impl + Layout> Draw for C { // if only + //fn draw (&self, to: &mut E) { + //if let Some(content) = self.content() { + //to.place_at(self.layout(to.area()), &content); + //} + //} +//} diff --git a/output/src/output_content.rs b/output/src/output_content.rs deleted file mode 100644 index cf09897..0000000 --- a/output/src/output_content.rs +++ /dev/null @@ -1,60 +0,0 @@ -use crate::*; - -/// Composable renderable with static dispatch. -pub trait Content: Sized { - /// Return opaque [Render]able. - fn content (&self) -> Option + '_> { Option::<()>::None } -} - -/// The platonic ideal unit of [Content]: total emptiness at dead center (e=1vg^sqrt(-1)) -impl Content for () {} - -impl Content for fn(&mut E) { - fn content (&self) -> Option + '_> { - Some(self) - } -} - -impl> Content for fn()->T { - fn content (&self) -> Option + '_> { - Some(self()) - } -} - -impl> Content for Option { - fn content (&self) -> Option + '_> { - if let Some(content) = self { - content.content() - } else { - None - } - } -} - -/// You can render from an opaque pointer. -impl Content for &dyn Render where Self: Sized { - fn content (&self) -> Option + '_> { - #[allow(suspicious_double_ref_op)] - Some(self.deref()) - } -} - -/// Implement composable content for a struct. -#[macro_export] macro_rules! content { - // Implement for all [Output]s. - (|$self:ident:$Struct:ty| $content:expr) => { - impl Content for $Struct { - fn content (&$self) -> impl Render + '_ { Some($content) } - } - }; - // Implement for specific [Output]. - ($Output:ty:| - $self:ident: - $Struct:ident$(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)? - |$content:expr) => { - impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Content<$Output> - for $Struct $(<$($($L)? $($T)?),+>)? { - fn content (&$self) -> impl Render<$Output> + '_ { $content } - } - }; -} diff --git a/output/src/output_render.rs b/output/src/output_render.rs deleted file mode 100644 index d757597..0000000 --- a/output/src/output_render.rs +++ /dev/null @@ -1,87 +0,0 @@ -use crate::*; - -/// Renderable with dynamic dispatch. -pub trait Render { - /// Write data to display. - fn render (&self, output: &mut E); - /// Compute layout. - fn layout (&self, area: E::Area) -> E::Area { area } - - fn boxed <'a> (self) -> Box + 'a> where Self: Sized + 'a { - Box::new(self) as Box + 'a> - } - fn rc <'a> (self) -> Rc + 'a> where Self: Sized + 'a { - Rc::new(self) as Rc + 'a> - } -} - -impl Render for () { - fn render (&self, _: &mut E) {} -} - -impl Render for fn(&mut E) { - fn render (&self, output: &mut E) { - self(output) - } -} - -impl<'x, E: Output> Render for &(dyn Render + 'x) { - fn render (&self, output: &mut E) { - (*self).render(output) - } -} - -impl> Render for Box { - fn render (&self, output: &mut E) { - (**self).render(output) - } -} - -impl> Render for Option { - fn render (&self, output: &mut E) { - if let Some(render) = self { - render.render(output) - } - } -} - -impl> Render for &R { - fn render (&self, output: &mut E) { - (*self).render(output) - } -} - -impl> Render for &mut R { - fn render (&self, output: &mut E) { - (**self).render(output) - } -} - -impl> Render for [R] { - fn render (&self, output: &mut E) { - for render in self.iter() { - render.render(output) - } - } -} - -/// Implement custom rendering for a struct. -#[macro_export] macro_rules! render { - (|$self:ident:$Struct:ident $(< - $($L:lifetime),* $($T:ident $(:$Trait:path)?),* - >)?, $to:ident | $render:expr) => { - impl <$($($L),*)? E: Output, $($T$(:$Trait)?),*> Render - for $Struct $(<$($L),* $($T),*>>)? { - fn render (&$self, $to: &mut E) { $render } - } - }; - ($Output:ty:| - $self:ident: - $Struct:ident $(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)?, $to:ident - |$render:expr) => { - impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Render<$Output> - for $Struct $(<$($($L)? $($T)?),+>)? { - fn render (&$self, $to: &mut $Output) { $render } - } - }; -} diff --git a/output/src/output_thunk.rs b/output/src/output_thunk.rs deleted file mode 100644 index f505c38..0000000 --- a/output/src/output_thunk.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::*; - -/// Lazily-evaluated [Render]able. -pub struct Thunk, F: Fn()->T>( - PhantomData, - F -); -impl, F: Fn()->T> Thunk { - pub const fn new (thunk: F) -> Self { - Self(PhantomData, thunk) - } -} -impl, F: Fn()->T> Content for Thunk { - fn content (&self) -> Option> { - Some((self.1)()) - } -} - -pub struct ThunkRender(PhantomData, F); -impl ThunkRender { - pub fn new (render: F) -> Self { Self(PhantomData, render) } -} -impl Render for ThunkRender { - fn render (&self, to: &mut E) { (self.1)(to) } -} - -#[derive(Debug, Default)] pub struct Memo { - pub value: T, - pub view: Arc> -} - -impl Memo { - pub fn new (value: T, view: U) -> Self { - Self { value, view: Arc::new(view.into()) } - } - pub fn update ( - &mut self, - newval: T, - render: impl Fn(&mut U, &T, &T)->R - ) -> Option { - if newval != self.value { - let result = render(&mut*self.view.write().unwrap(), &newval, &self.value); - self.value = newval; - return Some(result); - } - None - } -} - -/// Clear a pre-allocated buffer, then write into it. -#[macro_export] macro_rules! rewrite { - ($buf:ident, $($rest:tt)*) => { |$buf,_,_|{ $buf.clear(); write!($buf, $($rest)*) } } -} diff --git a/output/src/space.rs b/output/src/space.rs new file mode 100644 index 0000000..8875438 --- /dev/null +++ b/output/src/space.rs @@ -0,0 +1,5 @@ +mod space_area; pub use self::space_area::*; +mod space_coordinate; pub use self::space_coordinate::*; +mod space_direction; pub use self::space_direction::*; +mod space_measure; pub use self::space_measure::*; +mod space_size; pub use self::space_size::*; diff --git a/output/src/space_area.rs b/output/src/space/space_area.rs similarity index 100% rename from output/src/space_area.rs rename to output/src/space/space_area.rs diff --git a/output/src/space_coordinate.rs b/output/src/space/space_coordinate.rs similarity index 100% rename from output/src/space_coordinate.rs rename to output/src/space/space_coordinate.rs diff --git a/output/src/space_direction.rs b/output/src/space/space_direction.rs similarity index 100% rename from output/src/space_direction.rs rename to output/src/space/space_direction.rs diff --git a/output/src/space_measure.rs b/output/src/space/space_measure.rs similarity index 84% rename from output/src/space_measure.rs rename to output/src/space/space_measure.rs index c2ca936..d1bdb30 100644 --- a/output/src/space_measure.rs +++ b/output/src/space/space_measure.rs @@ -2,13 +2,13 @@ use crate::*; /// A widget that tracks its render width and height #[derive(Default)] -pub struct Measure { +pub struct Measure { _engine: PhantomData, pub x: Arc, pub y: Arc, } -impl PartialEq for Measure { +impl PartialEq for Measure { fn eq (&self, other: &Self) -> bool { self.x.load(Relaxed) == other.x.load(Relaxed) && self.y.load(Relaxed) == other.y.load(Relaxed) @@ -16,14 +16,14 @@ impl PartialEq for Measure { } // TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small -impl Render for Measure { - fn render (&self, to: &mut E) { +impl Draw for Measure { + fn draw (&self, to: &mut E) { self.x.store(to.area().w().into(), Relaxed); self.y.store(to.area().h().into(), Relaxed); } } -impl Clone for Measure { +impl Clone for Measure { fn clone (&self) -> Self { Self { _engine: Default::default(), @@ -33,7 +33,7 @@ impl Clone for Measure { } } -impl std::fmt::Debug for Measure { +impl std::fmt::Debug for Measure { fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { f.debug_struct("Measure") .field("width", &self.x) @@ -42,7 +42,7 @@ impl std::fmt::Debug for Measure { } } -impl Measure { +impl Measure { pub fn new () -> Self { Self { _engine: PhantomData::default(), @@ -75,7 +75,7 @@ impl Measure { pub fn format (&self) -> Arc { format!("{}x{}", self.w(), self.h()).into() } - pub fn of > (&self, item: T) -> Bsp, T> { + pub fn of > (&self, item: T) -> Bsp, T> { Bsp::b(Fill::xy(self), item) } } diff --git a/output/src/space_size.rs b/output/src/space/space_size.rs similarity index 93% rename from output/src/space_size.rs rename to output/src/space/space_size.rs index ff53c25..235f098 100644 --- a/output/src/space_size.rs +++ b/output/src/space/space_size.rs @@ -38,7 +38,7 @@ impl Size for [N;2] { fn y (&self) -> N { self[1] } } -pub trait HasSize { +pub trait HasSize { fn size (&self) -> &Measure; fn width (&self) -> usize { self.size().w() @@ -48,7 +48,7 @@ pub trait HasSize { } } -impl>> HasSize for T { +impl>> HasSize for T { fn size (&self) -> &Measure { self.get() } diff --git a/output/src/test.rs b/output/src/test.rs index 6bd653b..ce859ed 100644 --- a/output/src/test.rs +++ b/output/src/test.rs @@ -90,7 +90,7 @@ macro_rules! test_op_transform { _ => None } { assert_eq!(Content::layout(&op, [x, y, w, h]), - Render::layout(&op, [x, y, w, h])); + Draw::layout(&op, [x, y, w, h])); } } } @@ -124,15 +124,15 @@ proptest! { let bsp = Bsp(d, a, b); assert_eq!( Content::layout(&bsp, [x, y, w, h]), - Render::layout(&bsp, [x, y, w, h]), + Draw::layout(&bsp, [x, y, w, h]), ); } } #[test] fn test_stub_output () -> Usually<()> { use crate::*; - struct TestOutput([u16;4]); - impl Output for TestOutput { + struct TestOut([u16;4]); + impl Out for TestOut { type Unit = u16; type Size = [u16;2]; type Area = [u16;4]; @@ -142,12 +142,12 @@ proptest! { fn area_mut (&mut self) -> &mut [u16;4] { &mut self.0 } - fn place + ?Sized> (&mut self, _: [u16;4], _: &T) { + fn place + ?Sized> (&mut self, _: [u16;4], _: &T) { () } } - impl Render for String { - fn render (&self, to: &mut TestOutput) { + impl Draw for String { + fn draw (&self, to: &mut TestOut) { to.area_mut().set_w(self.len() as u16); } } @@ -161,8 +161,8 @@ proptest! { #[test] fn test_iter_map () { struct Foo; - impl Content for Foo {} - fn _make_map + Send + Sync> (data: &Vec) -> impl Render { + impl Content for Foo {} + fn _make_map + Send + Sync> (data: &Vec) -> impl Draw { Map::new(||data.iter(), |_foo, _index|{}) } let _data = vec![Foo, Foo, Foo]; diff --git a/output/src/thunk.rs b/output/src/thunk.rs new file mode 100644 index 0000000..da2d84e --- /dev/null +++ b/output/src/thunk.rs @@ -0,0 +1,53 @@ +use crate::*; + +/// Lazily-evaluated [Draw]able. +pub struct Lazy(PhantomData<(E, T)>, F); +impl + Layout, F: Fn()->T> Lazy { + pub const fn new (thunk: F) -> Self { + Self(PhantomData, thunk) + } +} +impl + Layout, F: Fn()->T> Content for Lazy { + fn content (&self) -> impl Draw + Layout + '_ { + (self.1)() + } +} + +pub struct Thunk(PhantomData, F); +impl Thunk { + pub const fn new (draw: F) -> Self { Self(PhantomData, draw) } +} +impl Draw for Thunk { + fn draw (&self, to: &mut E) { (self.1)(to) } +} +impl Layout for Thunk { + fn layout (&self, to: E::Area) -> E::Area { to } +} + +#[derive(Debug, Default)] pub struct Memo { + pub value: T, + pub view: Arc> +} + +impl Memo { + pub fn new (value: T, view: U) -> Self { + Self { value, view: Arc::new(view.into()) } + } + pub fn update ( + &mut self, + newval: T, + draw: impl Fn(&mut U, &T, &T)->R + ) -> Option { + if newval != self.value { + let result = draw(&mut*self.view.write().unwrap(), &newval, &self.value); + self.value = newval; + return Some(result); + } + None + } +} + +/// Clear a pre-allocated buffer, then write into it. +#[macro_export] macro_rules! rewrite { + ($buf:ident, $($rest:tt)*) => { |$buf,_,_|{ $buf.clear(); write!($buf, $($rest)*) } } +} diff --git a/output/src/widget.rs b/output/src/widget.rs new file mode 100644 index 0000000..44e3257 --- /dev/null +++ b/output/src/widget.rs @@ -0,0 +1,4 @@ +mod widget_border; pub use self::widget_border::*; +mod widget_field; pub use self::widget_field::*; +mod widget_style; pub use self::widget_style::*; +mod widget_tryptich; pub use self::widget_tryptich::*; diff --git a/output/src/widget/widget_border.rs b/output/src/widget/widget_border.rs new file mode 100644 index 0000000..7f44a17 --- /dev/null +++ b/output/src/widget/widget_border.rs @@ -0,0 +1,10 @@ +use crate::*; + +pub struct Border(pub bool, pub S); +impl> Layout for Border { + fn layout (&self, area: O::Area) -> O::Area { + self.1.layout(area) + } +} + +pub struct Bordered(pub bool, pub S, pub W); diff --git a/output/src/widget/widget_field.rs b/output/src/widget/widget_field.rs new file mode 100644 index 0000000..8de2515 --- /dev/null +++ b/output/src/widget/widget_field.rs @@ -0,0 +1,25 @@ +use crate::*; + +pub struct FieldH(pub Theme, pub Label, pub Value); +impl, V: Draw> Layout for FieldH where Self: Content { + fn layout (&self, to: O::Area) -> O::Area { + self.content().layout(to) + } +} +impl, V: Draw> Draw for FieldH where Self: Content { + fn draw (&self, to: &mut O) { + self.content().draw(to) + } +} + +pub struct FieldV(pub Theme, pub Label, pub Value); +impl, V: Draw> Layout for FieldV where Self: Content { + fn layout (&self, to: O::Area) -> O::Area { + self.content().layout(to) + } +} +impl, V: Draw> Draw for FieldV where Self: Content { + fn draw (&self, to: &mut O) { + self.content().draw(to) + } +} diff --git a/output/src/widget/widget_style.rs b/output/src/widget/widget_style.rs new file mode 100644 index 0000000..b7ba4ba --- /dev/null +++ b/output/src/widget/widget_style.rs @@ -0,0 +1,15 @@ +#[allow(unused)] use crate::*; + +pub struct Foreground(pub Color, pub Item); +impl> Layout for Foreground { + fn layout (&self, to: O::Area) -> O::Area { + self.1.layout(to) + } +} + +pub struct Background(pub Color, pub Item); +impl> Layout for Background { + fn layout (&self, to: O::Area) -> O::Area { + self.1.layout(to) + } +} diff --git a/output/src/widget/widget_tryptich.rs b/output/src/widget/widget_tryptich.rs new file mode 100644 index 0000000..3039f5e --- /dev/null +++ b/output/src/widget/widget_tryptich.rs @@ -0,0 +1,31 @@ +#[allow(unused)] use crate::*; + +/// A three-column layout. +pub struct Tryptich { + pub top: bool, + pub h: u16, + pub left: (u16, A), + pub middle: (u16, B), + pub right: (u16, C), +} + +impl Tryptich<(), (), ()> { + pub fn center (h: u16) -> Self { + Self { h, top: false, left: (0, ()), middle: (0, ()), right: (0, ()) } + } + pub fn top (h: u16) -> Self { + Self { h, top: true, left: (0, ()), middle: (0, ()), right: (0, ()) } + } +} + +impl Tryptich { + pub fn left (self, w: u16, content: D) -> Tryptich { + Tryptich { left: (w, content), ..self } + } + pub fn middle (self, w: u16, content: D) -> Tryptich { + Tryptich { middle: (w, content), ..self } + } + pub fn right (self, w: u16, content: D) -> Tryptich { + Tryptich { right: (w, content), ..self } + } +} diff --git a/proc/src/lib.rs b/proc/src/lib.rs index 9deff9e..fa33152 100644 --- a/proc/src/lib.rs +++ b/proc/src/lib.rs @@ -71,8 +71,8 @@ pub(crate) fn write_quote_to (out: &mut TokenStream2, quote: TokenStream2) { } #[cfg(test)] #[test] fn test_proc_view () { - let x: crate::proc_view::ViewMeta = pq! { SomeOutput }; - let output: Ident = pq! { SomeOutput }; + let x: crate::proc_view::ViewMeta = pq! { SomeOut }; + let output: Ident = pq! { SomeOut }; assert_eq!(x.output, output); // TODO @@ -103,10 +103,10 @@ pub(crate) fn write_quote_to (out: &mut TokenStream2, quote: TokenStream2) { //]); // FIXME //let parsed: ViewDefinition = pq! { - //#[tengri_proc::view(SomeOutput)] + //#[tengri_proc::view(SomeOut)] //impl SomeView { //#[tengri::view(":view-1")] - //fn view_1 (&self) -> impl Render + use<'_> { + //fn view_1 (&self) -> impl Draw + use<'_> { //"view-1" //} //} @@ -114,19 +114,19 @@ pub(crate) fn write_quote_to (out: &mut TokenStream2, quote: TokenStream2) { //let written = quote! { #parsed }; //assert_eq!(format!("{written}"), format!("{}", quote! { //impl SomeView { - //fn view_1 (&self) -> impl Render + use<'_> { + //fn view_1 (&self) -> impl Draw + use<'_> { //"view-1" //} //} ///// Generated by [tengri_proc]. - //impl ::tengri::output::Content for SomeView { - //fn content (&self) -> impl Render { + //impl ::tengri::output::Content for SomeView { + //fn content (&self) -> impl Draw { //self.size.of(::tengri::output::View(self, self.config.view)) //} //} ///// Generated by [tengri_proc]. - //impl<'a> ::tengri::dsl::ViewContext<'a, SomeOutput> for SomeView { - //fn get_content_sym (&'a self, value: &Value<'a>) -> Option> { + //impl<'a> ::tengri::dsl::ViewContext<'a, SomeOut> for SomeView { + //fn get_content_sym (&'a self, value: &Value<'a>) -> Option> { //match value { //::tengri::dsl::Value::Sym(":view-1") => self.view_1().boxed(), //_ => panic!("expected Sym(content), got: {value:?}") diff --git a/proc/src/proc_view.rs b/proc/src/proc_view.rs index 7a70b2e..b9e1017 100644 --- a/proc/src/proc_view.rs +++ b/proc/src/proc_view.rs @@ -77,12 +77,12 @@ impl ViewDef { quote! { /// Generated by [tengri_proc]. /// - /// Makes [#self_ty] able to construct the [Render]able + /// Makes [#self_ty] able to construct the [Draw]able /// which might correspond to a given [TokenStream], /// while taking [#self_ty]'s state into consideration. - impl<'state> ::tengri::dsl::DslInto + 'state>> for #self_ty { + impl<'state> ::tengri::dsl::DslInto + 'state>> for #self_ty { fn dsl_into (&self, dsl: &impl ::tengri::dsl::Dsl) - -> Perhaps + 'state>> + -> Perhaps + 'state>> { #(#builtins)* if let Some(sym) = dsl.sym()? { @@ -128,9 +128,9 @@ fn _builtins_with_holes () -> impl Iterator { } fn _builtins_with_boxes () -> impl Iterator { - builtins_with(quote! { _ }, quote! { Box> }) + builtins_with(quote! { _ }, quote! { Box> }) } fn builtins_with_boxes_output (o: TokenStream2) -> impl Iterator { - builtins_with(quote! { _ }, quote! { Box> }) + builtins_with(quote! { _ }, quote! { Box> }) } diff --git a/tui/Cargo.toml b/tui/Cargo.toml index 284ad75..9dfa085 100644 --- a/tui/Cargo.toml +++ b/tui/Cargo.toml @@ -5,7 +5,8 @@ version = { workspace = true } edition = { workspace = true } [features] -dsl = [ "tengri_dsl", "tengri_output/dsl" ] +dsl = [ "dep:tengri_dsl", "tengri_output/dsl" ] +bumpalo = [ "dep:bumpalo" ] [dependencies] tengri_core = { workspace = true } @@ -13,14 +14,15 @@ tengri_input = { workspace = true } tengri_output = { workspace = true } tengri_dsl = { workspace = true, optional = true } -palette = { workspace = true } -rand = { workspace = true } -crossterm = { workspace = true } -ratatui = { workspace = true } -better-panic = { workspace = true } -konst = { workspace = true } atomic_float = { workspace = true } +better-panic = { workspace = true } +bumpalo = { workspace = true, optional = true } +crossterm = { workspace = true } +konst = { workspace = true } +palette = { workspace = true } quanta = { workspace = true } +rand = { workspace = true } +ratatui = { workspace = true } unicode-width = { workspace = true } [dev-dependencies] diff --git a/tui/examples/edn/edn99.edn b/tui/examples/edn/edn99.edn index cac4726..933db8c 100644 --- a/tui/examples/edn/edn99.edn +++ b/tui/examples/edn/edn99.edn @@ -3,7 +3,7 @@ (bg/behind :bg2 (border/around :border2 (margin/xy 4 2 :label2))) (bg/behind :bg3 (border/around :border3 (margin/xy 6 3 :label3))))))) - fn content (&self) -> dyn Render { + fn content (&self) -> dyn Draw { let border_style = Style::default().fg(Color::Rgb(0,0,0)); Align::Center(Layers::new(move|add|{ diff --git a/tui/examples/tui.rs b/tui/examples/tui.rs index 677f0da..62dbfe8 100644 --- a/tui/examples/tui.rs +++ b/tui/examples/tui.rs @@ -76,25 +76,25 @@ content!(TuiOut: |self: Example|{ //#[tengri_proc::view(TuiOut)] //impl Example { - //pub fn title (&self) -> impl Render + use<'_> { + //pub fn title (&self) -> impl Draw + use<'_> { //Tui::bg(Color::Rgb(60, 10, 10), Push::y(1, Align::n(format!("Example {}/{}:", self.0 + 1, EXAMPLES.len())))).boxed() //} - //pub fn code (&self) -> impl Render + use<'_> { + //pub fn code (&self) -> impl Draw + use<'_> { //Tui::bg(Color::Rgb(10, 60, 10), Push::y(2, Align::n(format!("{}", EXAMPLES[self.0])))).boxed() //} - //pub fn hello (&self) -> impl Render + use<'_> { + //pub fn hello (&self) -> impl Draw + use<'_> { //Tui::bg(Color::Rgb(10, 100, 10), "Hello").boxed() //} - //pub fn world (&self) -> impl Render + use<'_> { + //pub fn world (&self) -> impl Draw + use<'_> { //Tui::bg(Color::Rgb(100, 10, 10), "world").boxed() //} - //pub fn hello_world (&self) -> impl Render + use<'_> { + //pub fn hello_world (&self) -> impl Draw + use<'_> { //"Hello world!".boxed() //} - //pub fn map_e (&self) -> impl Render + use<'_> { + //pub fn map_e (&self) -> impl Draw + use<'_> { //Map::east(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed() //} - //pub fn map_s (&self) -> impl Render + use<'_> { + //pub fn map_s (&self) -> impl Draw + use<'_> { //Map::south(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed() //} //} diff --git a/tui/src/lib.rs b/tui/src/lib.rs index 8ed9006..a2ca42b 100644 --- a/tui/src/lib.rs +++ b/tui/src/lib.rs @@ -27,7 +27,7 @@ pub(crate) use std::io::{stdout, Stdout}; //use std::sync::{Arc, RwLock}; struct TestComponent(String); impl Content for TestComponent { - fn content (&self) -> impl Render { + fn content (&self) -> impl Draw { Some(self.0.as_str()) } } diff --git a/tui/src/tui_content.rs b/tui/src/tui_content.rs index 1eef3cc..34d5697 100644 --- a/tui/src/tui_content.rs +++ b/tui/src/tui_content.rs @@ -1,56 +1,128 @@ -use crate::*; +#[allow(unused)] use crate::*; -macro_rules! impl_content_layout_render { - ($Output:ty: |$self:ident: $Struct:ty, $to:ident| - layout = $layout:expr; - render = $render:expr) => - { - impl Render<$Output> for $Struct { - fn layout (&$self, $to: [u16;4]) -> [u16;4] { $layout } - fn render (&$self, $to: &mut $Output) { $render } +impl Tui { + pub const fn fg (color: Color, w: T) -> TuiForeground { + TuiForeground(Foreground(color, w)) + } + pub const fn bg (color: Color, w: T) -> TuiBackground { + TuiBackground(Background(color, w)) + } + pub const fn fg_bg (fg: Color, bg: Color, w: T) -> TuiBackground> { + TuiBackground(Background(bg, TuiForeground(Foreground(fg, w)))) + } + pub const fn modify (enable: bool, modifier: Modifier, w: T) -> Modify { + Modify(enable, modifier, w) + } + pub const fn bold (enable: bool, w: T) -> Modify { + Self::modify(enable, Modifier::BOLD, w) + } + pub const fn border (enable: bool, style: S, w: T) -> Bordered { + Bordered(enable, style, w) + } +} + +#[macro_export] macro_rules! tui_layout ((|$self:ident:$Self:ty, $to:ident|$expr:expr)=>{ + impl Layout for $Self { + fn layout (&$self, $to: [u16;4]) -> [u16;4] { + $expr } } -} +}); + +#[macro_export] macro_rules! tui_draw ((|$self:ident:$Self:ty, $to:ident|$expr:expr)=>{ + impl Draw for $Self { + fn draw (&$self, $to: &mut TuiOut) { + $expr + } + } +}); + +mod tui_border; pub use self::tui_border::*; +mod tui_button; pub use self::tui_button::*; +mod tui_color; pub use self::tui_color::*; +mod tui_error; pub use self::tui_error::*; +mod tui_field; pub use self::tui_field::*; +mod tui_phat; pub use self::tui_phat::*; +mod tui_repeat; pub use self::tui_repeat::*; +mod tui_scroll; pub use self::tui_scroll::*; +mod tui_string; pub use self::tui_string::*; -mod tui_border; pub use self::tui_border::*; -mod tui_button; //pub use self::tui_button::*; -mod tui_color; pub use self::tui_color::*; -mod tui_error; pub use self::tui_error::*; -mod tui_field; pub use self::tui_field::*; mod tui_number; //pub use self::tui_number::*; -mod tui_phat; pub use self::tui_phat::*; -mod tui_repeat; pub use self::tui_repeat::*; -mod tui_scroll; pub use self::tui_scroll::*; -mod tui_string; pub use self::tui_string::*; -mod tui_style; pub use self::tui_style::*; -mod tui_tryptich; pub use self::tui_tryptich::*; +mod tui_tryptich; //pub use self::tui_tryptich::*; -impl> Render for std::sync::Arc { +pub struct TuiForeground(pub(crate) Foreground); +pub struct TuiBackground(pub(crate) Background); +pub struct Modify(pub bool, pub Modifier, pub T); +pub struct Styled(pub Option