From 36280ce9b7908aa7d562e2554cf74aea3641fa11 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Mon, 9 Dec 2024 16:34:45 +0100 Subject: [PATCH] wip3 (25e): woohoohoo --- crates/tek_core/src/collect.rs | 6 +- crates/tek_core/src/output.rs | 68 ++- crates/tek_layout/README.md | 1 + crates/tek_layout/src/align.rs | 26 +- crates/tek_layout/src/bsp.rs | 90 ++-- crates/tek_layout/src/cond.rs | 67 +++ crates/tek_layout/src/fill.rs | 16 +- crates/tek_layout/src/grow.rs | 46 -- crates/tek_layout/src/inset.rs | 48 -- crates/tek_layout/src/inset_outset.rs | 99 ++++ crates/tek_layout/src/layers.rs | 2 +- crates/tek_layout/src/lib.rs | 44 +- crates/tek_layout/src/map_reduce.rs | 34 ++ crates/tek_layout/src/max.rs | 40 -- crates/tek_layout/src/min.rs | 39 -- crates/tek_layout/src/min_max.rs | 85 +++ crates/tek_layout/src/outset.rs | 57 -- crates/tek_layout/src/pull.rs | 69 --- crates/tek_layout/src/push.rs | 68 --- crates/tek_layout/src/push_pull.rs | 132 +++++ crates/tek_layout/src/shrink.rs | 62 --- crates/tek_layout/src/shrink_grow.rs | 102 ++++ crates/tek_layout/src/stack.rs | 32 +- crates/tek_tui/Cargo.toml | 3 + crates/tek_tui/src/lib.rs | 214 -------- crates/tek_tui/src/{tui_engine.rs => tui.rs} | 86 ++- crates/tek_tui/src/tui_app_arranger.rs | 166 +++--- crates/tek_tui/src/tui_app_transport.rs | 18 +- crates/tek_tui/src/tui_engine_focus.rs | 66 +++ crates/tek_tui/src/tui_engine_layout.rs | 1 + crates/tek_tui/src/tui_engine_output.rs | 84 +-- crates/tek_tui/src/tui_engine_style.rs | 127 +++-- crates/tek_tui/src/tui_view_arranger.rs | 489 +++++++++--------- crates/tek_tui/src/tui_view_phrase_editor.rs | 32 +- crates/tek_tui/src/tui_view_phrase_length.rs | 75 +-- crates/tek_tui/src/tui_view_phrase_list.rs | 98 ++-- .../tek_tui/src/tui_view_phrase_selector.rs | 2 +- crates/tek_tui/src/tui_view_sequencer.rs | 132 +++-- crates/tek_tui/src/tui_view_status_bar.rs | 25 + crates/tek_tui/src/tui_view_transport.rs | 168 +++--- 40 files changed, 1607 insertions(+), 1412 deletions(-) create mode 100644 crates/tek_layout/README.md create mode 100644 crates/tek_layout/src/cond.rs delete mode 100644 crates/tek_layout/src/grow.rs delete mode 100644 crates/tek_layout/src/inset.rs create mode 100644 crates/tek_layout/src/inset_outset.rs create mode 100644 crates/tek_layout/src/map_reduce.rs delete mode 100644 crates/tek_layout/src/max.rs delete mode 100644 crates/tek_layout/src/min.rs create mode 100644 crates/tek_layout/src/min_max.rs delete mode 100644 crates/tek_layout/src/outset.rs delete mode 100644 crates/tek_layout/src/pull.rs delete mode 100644 crates/tek_layout/src/push.rs create mode 100644 crates/tek_layout/src/push_pull.rs delete mode 100644 crates/tek_layout/src/shrink.rs create mode 100644 crates/tek_layout/src/shrink_grow.rs delete mode 100644 crates/tek_tui/src/lib.rs rename crates/tek_tui/src/{tui_engine.rs => tui.rs} (70%) create mode 100644 crates/tek_tui/src/tui_engine_focus.rs create mode 100644 crates/tek_tui/src/tui_engine_layout.rs create mode 100644 crates/tek_tui/src/tui_view_status_bar.rs diff --git a/crates/tek_core/src/collect.rs b/crates/tek_core/src/collect.rs index 09595e74..540b9f29 100644 --- a/crates/tek_core/src/collect.rs +++ b/crates/tek_core/src/collect.rs @@ -58,7 +58,8 @@ impl<'a, E: Engine, const N: usize> Iterator for CollectIterator<'a, E, N> { Collect::Array(array) => { if let Some(item) = array.get(self.0) { self.0 += 1; - Some(item) + //Some(item) + None } else { None } @@ -66,7 +67,8 @@ impl<'a, E: Engine, const N: usize> Iterator for CollectIterator<'a, E, N> { Collect::Slice(slice) => { if let Some(item) = slice.get(self.0) { self.0 += 1; - Some(item) + //Some(item) + None } else { None } diff --git a/crates/tek_core/src/output.rs b/crates/tek_core/src/output.rs index a2fd5d6f..93dcee04 100644 --- a/crates/tek_core/src/output.rs +++ b/crates/tek_core/src/output.rs @@ -15,6 +15,39 @@ pub fn widget > (w: &T) -> &dyn Render { w as &dyn Render } +/// A [Render] that contains other [Render]s +pub trait Content: Send + Sync { + fn content (&self) -> impl Render; +} + +//impl> Render for &C { + //fn min_size (&self, to: E::Size) -> Perhaps { + //self.content().min_size(to) + //} + //fn render (&self, to: &mut E::Output) -> Usually<()> { + //match self.min_size(to.area().wh().into())? { + //Some(wh) => to.render_in(to.area().clip(wh).into(), &self.content()), + //None => Ok(()) + //} + //} +//} + +/* + +/// Every struct that has [Content] is a renderable [Render]. +impl> Render for C { + fn min_size (&self, to: E::Size) -> Perhaps { + self.content().min_size(to) + } + fn render (&self, to: &mut E::Output) -> Usually<()> { + match self.min_size(to.area().wh().into())? { + Some(wh) => to.render_in(to.area().clip(wh).into(), &self.content()), + None => Ok(()) + } + } +} +*/ + /// A renderable component pub trait Render: Send + Sync { /// Minimum size to use @@ -25,7 +58,7 @@ pub trait Render: Send + Sync { fn render (&self, to: &mut E::Output) -> Usually<()>; } -impl Render for &dyn Render { +impl> Render for &R { fn min_size (&self, to: E::Size) -> Perhaps { (*self).min_size(to) } @@ -34,12 +67,21 @@ impl Render for &dyn Render { } } +//impl Render for &dyn Render { + //fn min_size (&self, to: E::Size) -> Perhaps { + //(*self).min_size(to) + //} + //fn render (&self, to: &mut E::Output) -> Usually<()> { + //(*self).render(to) + //} +//} + impl Render for &mut dyn Render { - fn min_size (&self, to: E::Size) -> Perhaps { - (**self).min_size(to) + fn min_size (&self, to: E::Size) -> Perhaps { + (*self).min_size(to) } fn render (&self, to: &mut E::Output) -> Usually<()> { - (**self).render(to) + (*self).render(to) } } @@ -88,24 +130,6 @@ impl> Render for Option { } } -/// A [Render] that contains other [Render]s -pub trait Content: Send + Sync { - fn content (&self) -> impl Render; -} - -/// Every struct that has [Content] is a renderable [Render]. -impl> Render for &W { - fn min_size (&self, to: E::Size) -> Perhaps { - self.content().min_size(to) - } - fn render (&self, to: &mut E::Output) -> Usually<()> { - match self.min_size(to.area().wh().into())? { - Some(wh) => to.render_in(to.area().clip(wh).into(), &self.content()), - None => Ok(()) - } - } -} - /// A custom [Render] defined by passing layout and render closures in place. pub struct Widget< E: Engine, diff --git a/crates/tek_layout/README.md b/crates/tek_layout/README.md new file mode 100644 index 00000000..6d622181 --- /dev/null +++ b/crates/tek_layout/README.md @@ -0,0 +1 @@ +manja s grozde i ikebana s chiaroscuro diff --git a/crates/tek_layout/src/align.rs b/crates/tek_layout/src/align.rs index 1a89e3db..48c64874 100644 --- a/crates/tek_layout/src/align.rs +++ b/crates/tek_layout/src/align.rs @@ -1,19 +1,19 @@ use crate::*; -impl> LayoutAlign for W {} +impl LayoutAlign for E {} -pub trait LayoutAlign: Render + Sized { - fn align_x (self) -> Align { Align::X(self) } - fn align_y (self) -> Align { Align::Y(self) } - fn align_center (self) -> Align { Align::Center(self) } - fn align_n (self) -> Align { Align::N(self) } - fn align_s (self) -> Align { Align::S(self) } - fn align_e (self) -> Align { Align::E(self) } - fn align_w (self) -> Align { Align::W(self) } - fn align_nw (self) -> Align { Align::NW(self) } - fn align_sw (self) -> Align { Align::SW(self) } - fn align_ne (self) -> Align { Align::NE(self) } - fn align_se (self) -> Align { Align::SE(self) } +pub trait LayoutAlign { + fn center_x > (w: W) -> Align { Align::X(w) } + fn center_y > (w: W) -> Align { Align::Y(w) } + fn center > (w: W) -> Align { Align::Center(w) } + fn at_n > (w: W) -> Align { Align::N(w) } + fn at_s > (w: W) -> Align { Align::S(w) } + fn at_e > (w: W) -> Align { Align::E(w) } + fn at_w > (w: W) -> Align { Align::W(w) } + fn at_nw > (w: W) -> Align { Align::NW(w) } + fn at_sw > (w: W) -> Align { Align::SW(w) } + fn at_ne > (w: W) -> Align { Align::NE(w) } + fn at_se > (w: W) -> Align { Align::SE(w) } } /// Override X and Y coordinates, aligning to corner, side, or center of area diff --git a/crates/tek_layout/src/bsp.rs b/crates/tek_layout/src/bsp.rs index d5f904d4..d916ae33 100644 --- a/crates/tek_layout/src/bsp.rs +++ b/crates/tek_layout/src/bsp.rs @@ -1,54 +1,78 @@ use crate::*; -impl> LayoutBsp for R {} +impl LayoutBspStatic for E {} -pub trait LayoutBsp: Render + Sized { - fn when (self, cond: bool) -> If { - If(Default::default(), cond, self) +pub trait LayoutBspStatic: { + fn over , B: Render> (a: A, b: B) -> Over { + Over(Default::default(), a, b) } - fn or > (self, cond: bool, other: B) -> Either { - Either(Default::default(), cond, self, other) + fn under , B: Render> (a: A, b: B) -> Under { + Under(Default::default(), a, b) } - fn over > (self, other: B) -> Over { - Over(Default::default(), self, other) + fn to_north , B: Render> (a: A, b: B) -> ToNorth { + ToNorth(None, a, b) } - fn under > (self, other: B) -> Under { - Under(Default::default(), self, other) + fn to_south , B: Render> (a: A, b: B) -> ToSouth { + ToSouth(None, a, b) } - fn north_of > (self, other: B) -> North { - North(Default::default(), self, other) + fn to_east , B: Render> (a: A, b: B) -> ToEast { + ToEast(None, a, b) } - fn south_of > (self, other: B) -> South { - South(Default::default(), self, other) - } - fn east_of > (self, other: B) -> East { - East(Default::default(), self, other) - } - fn west_of > (self, other: B) -> West { - West(Default::default(), self, other) + fn to_west , B: Render> (a: A, b: B) -> ToWest { + ToWest(None, a, b) } } -/// Render widget if predicate is true -pub struct If>(PhantomData, bool, A); - -impl> Content for If { - fn content (&self) -> impl Render { - if self.1 { Some(widget(&self.2)) } else { None } +pub trait LayoutBspFixedStatic: { + fn to_north , B: Render> (n: E::Unit, a: A, b: B) -> ToNorth { + ToNorth(Some(n), a, b) + } + fn to_south , B: Render> (n: E::Unit, a: A, b: B) -> ToSouth { + ToSouth(Some(n), a, b) + } + fn to_east , B: Render> (n: E::Unit, a: A, b: B) -> ToEast { + ToEast(Some(n), a, b) + } + fn to_west , B: Render> (n: E::Unit, a: A, b: B) -> ToWest { + ToWest(Some(n), a, b) } } -/// Render widget A if predicate is true, otherwise widget B -pub struct Either, B: Render>(PhantomData, bool, A, B); - pub struct Over, B: Render>(PhantomData, A, B); pub struct Under, B: Render>(PhantomData, A, B); -pub struct North, B: Render>(PhantomData, A, B); +pub struct ToNorth, B: Render>(Option, A, B); -pub struct South, B: Render>(PhantomData, A, B); +pub struct ToSouth, B: Render>(Option, A, B); -pub struct East, B: Render>(PhantomData, A, B); +pub struct ToEast, B: Render>(Option, A, B); -pub struct West, B: Render>(PhantomData, A, B); +pub struct ToWest, B: Render>(Option, A, B); + +impl, B: Render> Render for ToNorth { + fn min_size (&self, _: E::Size) -> Perhaps { + todo!(); + } + fn render (&self, _: &mut E::Output) -> Usually<()> { + Ok(()) + } +} + +impl, B: Render> Render for ToSouth { + fn min_size (&self, _: E::Size) -> Perhaps { + todo!(); + } + fn render (&self, _: &mut E::Output) -> Usually<()> { + Ok(()) + } +} + +impl, B: Render> Render for ToEast { + fn min_size (&self, _: E::Size) -> Perhaps { + todo!(); + } + fn render (&self, _: &mut E::Output) -> Usually<()> { + Ok(()) + } +} diff --git a/crates/tek_layout/src/cond.rs b/crates/tek_layout/src/cond.rs new file mode 100644 index 00000000..c4a6314d --- /dev/null +++ b/crates/tek_layout/src/cond.rs @@ -0,0 +1,67 @@ +use crate::*; + +impl> LayoutCond for R {} + +pub trait LayoutCond: Render + Sized { + fn when (self, cond: bool) -> If { + If(Default::default(), cond, self) + } + fn or > (self, cond: bool, other: B) -> Either { + Either(Default::default(), cond, self, other) + } +} + +impl LayoutCondStatic for E {} + +pub trait LayoutCondStatic { + fn either , B: Render> ( + condition: bool, + a: A, + b: B, + ) -> Either { + Either(Default::default(), condition, a, b) + } +} + +/// Render widget if predicate is true +pub struct If>(PhantomData, bool, A); + +impl> Render for If { + fn min_size (&self, to: E::Size) -> Perhaps { + if self.1 { + return self.2.min_size(to) + } + Ok(None) + } + fn render (&self, to: &mut E::Output) -> Usually<()> { + if self.1 { + return self.2.render(to) + } + Ok(()) + } +} + +/// Render widget A if predicate is true, otherwise widget B +pub struct Either, B: Render>( + PhantomData, + bool, + A, + B, +); + +impl, B: Render> Render for Either { + fn min_size (&self, to: E::Size) -> Perhaps { + if self.1 { + return self.2.min_size(to) + } else { + return self.3.min_size(to) + } + } + fn render (&self, to: &mut E::Output) -> Usually<()> { + if self.1 { + return self.2.render(to) + } else { + return self.3.render(to) + } + } +} diff --git a/crates/tek_layout/src/fill.rs b/crates/tek_layout/src/fill.rs index b9269f7b..28ba7066 100644 --- a/crates/tek_layout/src/fill.rs +++ b/crates/tek_layout/src/fill.rs @@ -1,16 +1,16 @@ use crate::*; -impl> LayoutFill for W {} +impl LayoutFill for E {} -pub trait LayoutFill: Render + Sized { - fn fill_x (self) -> Fill { - Fill::X(self) +pub trait LayoutFill { + fn fill_x > (fill: W) -> Fill { + Fill::X(fill) } - fn fill_y (self) -> Fill { - Fill::Y(self) + fn fill_y > (fill: W) -> Fill { + Fill::Y(fill) } - fn fill_xy (self) -> Fill { - Fill::XY(self) + fn fill_xy > (fill: W) -> Fill { + Fill::XY(fill) } } diff --git a/crates/tek_layout/src/grow.rs b/crates/tek_layout/src/grow.rs deleted file mode 100644 index 572ef9e4..00000000 --- a/crates/tek_layout/src/grow.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::*; - -impl> LayoutGrow for W {} - -pub trait LayoutGrow: Render + Sized { - fn grow_x (self, x: E::Unit) -> Grow { - Grow::X(x, self) - } - fn grow_y (self, y: E::Unit) -> Grow { - Grow::Y(y, self) - } - fn grow_xy (self, x: E::Unit, y: E::Unit) -> Grow { - Grow::XY(x, y, self) - } -} - -/// Expand drawing area -pub enum Grow { - /// Increase width - X(N, T), - /// Increase height - Y(N, T), - /// Increase width and height - XY(N, N, T) -} - -impl Grow { - fn inner (&self) -> &T { - match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, } - } -} - -impl> Render for Grow { - fn min_size (&self, to: E::Size) -> Perhaps { - Ok(self.inner().min_size(to)?.map(|to|match *self { - Self::X(w, _) => [to.w() + w, to.h()], - Self::Y(h, _) => [to.w(), to.h() + h], - Self::XY(w, h, _) => [to.w() + w, to.h() + h], - }.into())) - } - fn render (&self, to: &mut E::Output) -> Usually<()> { - Ok(self.min_size(to.area().wh().into())? - .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) - .transpose()?.unwrap_or(())) - } -} diff --git a/crates/tek_layout/src/inset.rs b/crates/tek_layout/src/inset.rs deleted file mode 100644 index 10442993..00000000 --- a/crates/tek_layout/src/inset.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::*; - -impl> LayoutInset for W {} - -pub trait LayoutInset: Render + Sized { - fn inset_x (self, x: E::Unit) -> Inset { - Inset::X(x, self) - } - fn inset_y (self, y: E::Unit) -> Inset { - Inset::Y(y, self) - } - fn inset_xy (self, x: E::Unit, y: E::Unit) -> Inset { - Inset::XY(x, y, self) - } -} - -/// Shrink from each side -pub enum Inset { - _Unused(PhantomData), - /// Decrease width - X(E::Unit, T), - /// Decrease height - Y(E::Unit, T), - /// Decrease width and height - XY(E::Unit, E::Unit, T), -} - -impl> Inset { - pub fn inner (&self) -> &T { - match self { - Self::X(_, i) => i, - Self::Y(_, i) => i, - Self::XY(_, _, i) => i, - _ => unreachable!(), - } - } -} - -impl> Render for Inset { - fn render (&self, to: &mut E::Output) -> Usually<()> { - match *self { - Self::X(x, ref inner) => (inner as &dyn Render).shrink_x(x).push_x(x), - Self::Y(y, ref inner) => (inner as &dyn Render).shrink_y(y).push_y(y), - Self::XY(x, y, ref inner) => (inner as &dyn Render).shrink_xy(x, y).push_xy(x, y), - _ => unreachable!(), - }.render(to) - } -} diff --git a/crates/tek_layout/src/inset_outset.rs b/crates/tek_layout/src/inset_outset.rs new file mode 100644 index 00000000..55c359a1 --- /dev/null +++ b/crates/tek_layout/src/inset_outset.rs @@ -0,0 +1,99 @@ +use crate::*; + +impl + LayoutShrinkGrow> LayoutInsetOutset for E {} + +pub trait LayoutInsetOutset: LayoutPushPull + LayoutShrinkGrow { + fn inset_x > (x: E::Unit, w: W) -> Inset { + Inset::X(x, w) + } + fn inset_y > (y: E::Unit, w: W) -> Inset { + Inset::Y(y, w) + } + fn inset_xy > (x: E::Unit, y: E::Unit, w: W) -> Inset { + Inset::XY(x, y, w) + } + fn outset_x > (x: E::Unit, w: W) -> Outset { + Outset::X(x, w) + } + fn outset_y > (y: E::Unit, w: W) -> Outset { + Outset::Y(y, w) + } + fn outset_xy > (x: E::Unit, y: E::Unit, w: W) -> Outset { + Outset::XY(x, y, w) + } +} + +/// Shrink from each side +pub enum Inset { + _Unused(PhantomData), + /// Decrease width + X(E::Unit, T), + /// Decrease height + Y(E::Unit, T), + /// Decrease width and height + XY(E::Unit, E::Unit, T), +} + +impl> Inset { + pub fn inner (&self) -> &T { + match self { + Self::X(_, i) => i, + Self::Y(_, i) => i, + Self::XY(_, _, i) => i, + _ => unreachable!(), + } + } +} + +impl> Render for Inset { + fn render (&self, to: &mut E::Output) -> Usually<()> { + match self { + Self::X(x, inner) => E::push_x(*x, E::shrink_x(*x, inner)), + Self::Y(y, inner) => E::push_y(*y, E::shrink_y(*y, inner)), + Self::XY(x, y, inner) => E::push_xy(*x, *y, E::shrink_xy(*x, *y, inner)), + _ => unreachable!(), + }.render(to) + } +} + +/// Grow on each side +pub enum Outset> { + _Unused(PhantomData), + /// Increase width + X(E::Unit, T), + /// Increase height + Y(E::Unit, T), + /// Increase width and height + XY(E::Unit, E::Unit, T), +} + + +impl> Outset { + pub fn inner (&self) -> &T { + match self { + Self::X(_, i) => i, + Self::Y(_, i) => i, + Self::XY(_, _, i) => i, + _ => unreachable!(), + } + } +} + +impl> Render for Outset { + fn min_size (&self, to: E::Size) -> Perhaps { + match *self { + Self::X(x, ref inner) => E::grow_x(x + x, inner), + Self::Y(y, ref inner) => E::grow_y(y + y, inner), + Self::XY(x, y, ref inner) => E::grow_xy(x + x, y + y, inner), + _ => unreachable!(), + }.min_size(to) + } + fn render (&self, to: &mut E::Output) -> Usually<()> { + match *self { + Self::X(x, ref inner) => E::push_x(x, inner), + Self::Y(y, ref inner) => E::push_y(y, inner), + Self::XY(x, y, ref inner) => E::push_xy(x, y, inner), + _ => unreachable!(), + }.render(to) + } +} diff --git a/crates/tek_layout/src/layers.rs b/crates/tek_layout/src/layers.rs index ddf2382c..9fe07ecd 100644 --- a/crates/tek_layout/src/layers.rs +++ b/crates/tek_layout/src/layers.rs @@ -33,7 +33,7 @@ where } fn render (&self, to: &mut E::Output) -> Usually<()> { if let Some(size) = self.min_size(to.area().wh().into())? { - (self.0)(&mut |layer|to.render_in(to.area().clip(size).into(), &layer)) + (self.0)(&mut |layer|to.render_in(to.area().clip(size).into(), layer)) } else { Ok(()) } diff --git a/crates/tek_layout/src/lib.rs b/crates/tek_layout/src/lib.rs index 2e7806a4..d9563df5 100644 --- a/crates/tek_layout/src/lib.rs +++ b/crates/tek_layout/src/lib.rs @@ -3,52 +3,22 @@ pub(crate) use tek_core::*; submod! { align bsp + cond debug fill fixed - grow - inset + inset_outset layers - max + map_reduce measure - min - outset - pull - push + min_max + push_pull scroll - shrink + shrink_grow split - stack + //stack } #[macro_export] macro_rules! lay { ($($expr:expr),* $(,)?) => { Layers::new(move|add|{ $(add(&$expr)?;)* Ok(()) }) } } - -#[macro_export] macro_rules! col { - ($($expr:expr),* $(,)?) => { Stack::down(move|add|{ $(add(&$expr)?;)* Ok(()) }) }; - ($pat:pat in $collection:expr => $item:expr) => { - Stack::down(move |add|{ - for $pat in $collection { add(&$item)?; } - Ok(()) - }) - } -} -#[macro_export] macro_rules! col_up { - ($($expr:expr),* $(,)?) => { Stack::down(move|add|{ $(add(&$expr)?;)* Ok(()) }) }; - ($pat:pat in $collection:expr => $item:expr) => { - Stack::up(move |add|{ - for $pat in $collection { add(&$item)?; } - Ok(()) - }) - } -} -#[macro_export] macro_rules! row { - ($($expr:expr),* $(,)?) => { Stack::right(move|add|{ $(add(&$expr)?;)* Ok(()) }) }; - ($pat:pat in $collection:expr => $item:expr) => { - Stack::right(move |add|{ - for $pat in $collection { add(&$item)?; } - Ok(()) - }) - } -} diff --git a/crates/tek_layout/src/map_reduce.rs b/crates/tek_layout/src/map_reduce.rs new file mode 100644 index 00000000..e407603f --- /dev/null +++ b/crates/tek_layout/src/map_reduce.rs @@ -0,0 +1,34 @@ +use crate::*; + +impl LayoutMapReduce for E {} + +pub trait LayoutMapReduce { + fn map (iterator: I, callback: F) -> Map + where + I: Iterator, + R: Render, + F: Fn(T)->R + { + Map(Default::default(), iterator, callback) + } + fn reduce (iterator: I, callback: F) -> Reduce + where + I: Iterator, + R: Render, + F: Fn(R, T)->R + { + Reduce(Default::default(), iterator, callback) + } +} + +pub struct Map, R: Render, F: Fn(T)->R>( + PhantomData, + I, + F +); + +pub struct Reduce, R: Render, F: Fn(R, T)->R>( + PhantomData<(E, R)>, + I, + F +); diff --git a/crates/tek_layout/src/max.rs b/crates/tek_layout/src/max.rs deleted file mode 100644 index 7939264c..00000000 --- a/crates/tek_layout/src/max.rs +++ /dev/null @@ -1,40 +0,0 @@ -use crate::*; - -impl> LayoutMax for W {} - -pub trait LayoutMax: Render + Sized { - fn max_x (self, x: E::Unit) -> Max { Max::X(x, self) } - fn max_y (self, y: E::Unit) -> Max { Max::Y(y, self) } - fn max_xy (self, x: E::Unit, y: E::Unit) -> Max { Max::XY(x, y, self) } -} - -/// Enforce maximum size of drawing area -pub enum Max { - /// Enforce maximum width - X(U, T), - /// Enforce maximum height - Y(U, T), - /// Enforce maximum width and height - XY(U, U, T), -} - -impl Max { - fn inner (&self) -> &T { - match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, } - } -} - -impl> Render for Max { - fn min_size (&self, to: E::Size) -> Perhaps { - Ok(self.inner().min_size(to)?.map(|to|match *self { - Self::X(w, _) => [to.w().min(w), to.h()], - Self::Y(h, _) => [to.w(), to.h().min(h)], - Self::XY(w, h, _) => [to.w().min(w), to.h().min(h)], - }.into())) - } - fn render (&self, to: &mut E::Output) -> Usually<()> { - Ok(self.min_size(to.area().wh().into())? - .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) - .transpose()?.unwrap_or(())) - } -} diff --git a/crates/tek_layout/src/min.rs b/crates/tek_layout/src/min.rs deleted file mode 100644 index 2cba806c..00000000 --- a/crates/tek_layout/src/min.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crate::*; - -impl> LayoutMin for W {} - -pub trait LayoutMin: Render + Sized { - fn min_x (self, x: E::Unit) -> Min { Min::X(x, self) } - fn min_y (self, y: E::Unit) -> Min { Min::Y(y, self) } - fn min_xy (self, x: E::Unit, y: E::Unit) -> Min { Min::XY(x, y, self) } -} - -/// Enforce minimum size of drawing area -pub enum Min { - /// Enforce minimum width - X(U, T), - /// Enforce minimum height - Y(U, T), - /// Enforce minimum width and height - XY(U, U, T), -} -impl Min { - pub fn inner (&self) -> &T { - match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, } - } -} -impl> Render for Min { - fn min_size (&self, to: E::Size) -> Perhaps { - Ok(self.inner().min_size(to)?.map(|to|match *self { - Self::X(w, _) => [to.w().max(w), to.h()], - Self::Y(h, _) => [to.w(), to.h().max(h)], - Self::XY(w, h, _) => [to.w().max(w), to.h().max(h)], - }.into())) - } - // TODO: 👘 πŸ‘™ β†πŸ‘™β†’ - fn render (&self, to: &mut E::Output) -> Usually<()> { - Ok(self.min_size(to.area().wh().into())? - .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) - .transpose()?.unwrap_or(())) - } -} diff --git a/crates/tek_layout/src/min_max.rs b/crates/tek_layout/src/min_max.rs new file mode 100644 index 00000000..042f7421 --- /dev/null +++ b/crates/tek_layout/src/min_max.rs @@ -0,0 +1,85 @@ +use crate::*; + +impl LayoutMinMax for E {} + +pub trait LayoutMinMax { + fn min_x > (x: E::Unit, w: W) -> Min { + Min::X(x, w) + } + fn min_y > (y: E::Unit, w: W) -> Min { + Min::Y(y, w) + } + fn min_xy > (x: E::Unit, y: E::Unit, w: W) -> Min { + Min::XY(x, y, w) + } + fn max_x > (x: E::Unit, w: W) -> Max { + Max::X(x, w) + } + fn max_y > (y: E::Unit, w: W) -> Max { + Max::Y(y, w) + } + fn max_xy > (x: E::Unit, y: E::Unit, w: W) -> Max { + Max::XY(x, y, w) + } +} + +/// Enforce minimum size of drawing area +pub enum Min { + /// Enforce minimum width + X(U, T), + /// Enforce minimum height + Y(U, T), + /// Enforce minimum width and height + XY(U, U, T), +} +impl Min { + pub fn inner (&self) -> &T { + match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, } + } +} +impl> Render for Min { + fn min_size (&self, to: E::Size) -> Perhaps { + Ok(self.inner().min_size(to)?.map(|to|match *self { + Self::X(w, _) => [to.w().max(w), to.h()], + Self::Y(h, _) => [to.w(), to.h().max(h)], + Self::XY(w, h, _) => [to.w().max(w), to.h().max(h)], + }.into())) + } + // TODO: 👘 πŸ‘™ β†πŸ‘™β†’ + fn render (&self, to: &mut E::Output) -> Usually<()> { + Ok(self.min_size(to.area().wh().into())? + .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) + .transpose()?.unwrap_or(())) + } +} + +/// Enforce maximum size of drawing area +pub enum Max { + /// Enforce maximum width + X(U, T), + /// Enforce maximum height + Y(U, T), + /// Enforce maximum width and height + XY(U, U, T), +} + +impl Max { + fn inner (&self) -> &T { + match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, } + } +} + +impl> Render for Max { + fn min_size (&self, to: E::Size) -> Perhaps { + Ok(self.inner().min_size(to)?.map(|to|match *self { + Self::X(w, _) => [to.w().min(w), to.h()], + Self::Y(h, _) => [to.w(), to.h().min(h)], + Self::XY(w, h, _) => [to.w().min(w), to.h().min(h)], + }.into())) + } + fn render (&self, to: &mut E::Output) -> Usually<()> { + Ok(self.min_size(to.area().wh().into())? + .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) + .transpose()?.unwrap_or(())) + } +} diff --git a/crates/tek_layout/src/outset.rs b/crates/tek_layout/src/outset.rs deleted file mode 100644 index 13851b68..00000000 --- a/crates/tek_layout/src/outset.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::*; - -impl> LayoutOutset for W {} - -pub trait LayoutOutset: Render + Sized { - fn outset_x (self, x: E::Unit) -> Outset { - Outset::X(x, self) - } - fn outset_y (self, y: E::Unit) -> Outset { - Outset::Y(y, self) - } - fn outset_xy (self, x: E::Unit, y: E::Unit) -> Outset { - Outset::XY(x, y, self) - } -} - -/// Grow on each side -pub enum Outset> { - _Unused(PhantomData), - /// Increase width - X(E::Unit, T), - /// Increase height - Y(E::Unit, T), - /// Increase width and height - XY(E::Unit, E::Unit, T), -} - - -impl> Outset { - pub fn inner (&self) -> &T { - match self { - Self::X(_, i) => i, - Self::Y(_, i) => i, - Self::XY(_, _, i) => i, - _ => unreachable!(), - } - } -} - -impl> Render for Outset { - fn min_size (&self, to: E::Size) -> Perhaps { - match *self { - Self::X(x, ref inner) => (inner as &dyn Render).grow_x(x + x), - Self::Y(y, ref inner) => (inner as &dyn Render).grow_y(y + y), - Self::XY(x, y, ref inner) => (inner as &dyn Render).grow_xy(x + x, y + y), - _ => unreachable!(), - }.min_size(to) - } - fn render (&self, to: &mut E::Output) -> Usually<()> { - match *self { - Self::X(x, ref inner) => (inner as &dyn Render).push_x(x), - Self::Y(y, ref inner) => (inner as &dyn Render).push_y(y), - Self::XY(x, y, ref inner) => (inner as &dyn Render).push_xy(x, y), - _ => unreachable!(), - }.render(to) - } -} diff --git a/crates/tek_layout/src/pull.rs b/crates/tek_layout/src/pull.rs deleted file mode 100644 index 918c870c..00000000 --- a/crates/tek_layout/src/pull.rs +++ /dev/null @@ -1,69 +0,0 @@ -use crate::*; - -impl> LayoutPull for W {} - -pub trait LayoutPull: Render + Sized { - fn pull_x (self, x: E::Unit) -> Pull { - Pull::X(x, self) - } - fn pull_y (self, y: E::Unit) -> Pull { - Pull::Y(y, self) - } - fn pull_xy (self, x: E::Unit, y: E::Unit) -> Pull { - Pull::XY(x, y, self) - } -} - -/// Move origin point of drawing area -pub enum Pull> { - _Unused(PhantomData), - /// Move origin to the right - X(E::Unit, T), - /// Move origin downwards - Y(E::Unit, T), - /// Move origin to the right and downwards - XY(E::Unit, E::Unit, T), -} - -impl> Pull { - pub fn inner (&self) -> &T { - match self { - Self::X(_, i) => i, - Self::Y(_, i) => i, - Self::XY(_, _, i) => i, - _ => unreachable!(), - } - } - pub fn x (&self) -> E::Unit { - match self { - Self::X(x, _) => *x, - Self::Y(_, _) => E::Unit::default(), - Self::XY(x, _, _) => *x, - _ => unreachable!(), - } - } - pub fn y (&self) -> E::Unit { - match self { - Self::X(_, _) => E::Unit::default(), - Self::Y(y, _) => *y, - Self::XY(_, y, _) => *y, - _ => unreachable!(), - } - } -} - -impl> Render for Pull { - fn min_size (&self, to: E::Size) -> Perhaps { - self.inner().min_size(to) - } - fn render (&self, to: &mut E::Output) -> Usually<()> { - let area = to.area(); - Ok(self.min_size(area.wh().into())? - .map(|size|to.render_in(match *self { - Self::X(x, _) => [area.x().minus(x), area.y(), size.w(), size.h()], - Self::Y(y, _) => [area.x(), area.y().minus(y), size.w(), size.h()], - Self::XY(x, y, _) => [area.x().minus(x), area.y().minus(y), size.w(), size.h()], - _ => unreachable!(), - }.into(), self.inner())).transpose()?.unwrap_or(())) - } -} diff --git a/crates/tek_layout/src/push.rs b/crates/tek_layout/src/push.rs deleted file mode 100644 index ff41c553..00000000 --- a/crates/tek_layout/src/push.rs +++ /dev/null @@ -1,68 +0,0 @@ -use crate::*; - -impl> LayoutPush for W {} - -pub trait LayoutPush: Render + Sized { - fn push_x (self, x: E::Unit) -> Push { - Push::X(x, self) - } - fn push_y (self, y: E::Unit) -> Push { - Push::Y(y, self) - } - fn push_xy (self, x: E::Unit, y: E::Unit) -> Push { - Push::XY(x, y, self) - } -} - -/// Move origin point of drawing area -pub enum Push> { - /// Move origin to the right - X(E::Unit, T), - /// Move origin downwards - Y(E::Unit, T), - /// Move origin to the right and downwards - XY(E::Unit, E::Unit, T), -} - -impl> Push { - pub fn inner (&self) -> &T { - match self { - Self::X(_, i) => i, - Self::Y(_, i) => i, - Self::XY(_, _, i) => i, - _ => unreachable!(), - } - } - pub fn x (&self) -> E::Unit { - match self { - Self::X(x, _) => *x, - Self::Y(_, _) => E::Unit::default(), - Self::XY(x, _, _) => *x, - _ => unreachable!(), - } - } - pub fn y (&self) -> E::Unit { - match self { - Self::X(_, _) => E::Unit::default(), - Self::Y(y, _) => *y, - Self::XY(_, y, _) => *y, - _ => unreachable!(), - } - } -} - -impl> Render for Push { - fn min_size (&self, to: E::Size) -> Perhaps { - self.inner().min_size(to) - } - fn render (&self, to: &mut E::Output) -> Usually<()> { - let area = to.area(); - Ok(self.min_size(area.wh().into())? - .map(|size|to.render_in(match *self { - Self::X(x, _) => [area.x() + x, area.y(), size.w(), size.h()], - Self::Y(y, _) => [area.x(), area.y() + y, size.w(), size.h()], - Self::XY(x, y, _) => [area.x() + x, area.y() + y, size.w(), size.h()], - _ => unreachable!(), - }.into(), self.inner())).transpose()?.unwrap_or(())) - } -} diff --git a/crates/tek_layout/src/push_pull.rs b/crates/tek_layout/src/push_pull.rs new file mode 100644 index 00000000..f06c9e22 --- /dev/null +++ b/crates/tek_layout/src/push_pull.rs @@ -0,0 +1,132 @@ +use crate::*; + +impl LayoutPushPull for E {} + +pub trait LayoutPushPull { + fn push_x > (x: E::Unit, w: W) -> Push { + Push::X(x, w) + } + fn push_y > (y: E::Unit, w: W) -> Push { + Push::Y(y, w) + } + fn push_xy > (x: E::Unit, y: E::Unit, w: W) -> Push { + Push::XY(x, y, w) + } + fn pull_x > (x: E::Unit, w: W) -> Pull { + Pull::X(x, w) + } + fn pull_y > (y: E::Unit, w: W) -> Pull { + Pull::Y(y, w) + } + fn pull_xy > (x: E::Unit, y: E::Unit, w: W) -> Pull { + Pull::XY(x, y, w) + } +} + +/// Increment origin point of drawing area +pub enum Push> { + /// Move origin to the right + X(E::Unit, T), + /// Move origin downwards + Y(E::Unit, T), + /// Move origin to the right and downwards + XY(E::Unit, E::Unit, T), +} + +impl> Push { + pub fn inner (&self) -> &T { + match self { + Self::X(_, i) => i, + Self::Y(_, i) => i, + Self::XY(_, _, i) => i, + _ => unreachable!(), + } + } + pub fn x (&self) -> E::Unit { + match self { + Self::X(x, _) => *x, + Self::Y(_, _) => E::Unit::default(), + Self::XY(x, _, _) => *x, + _ => unreachable!(), + } + } + pub fn y (&self) -> E::Unit { + match self { + Self::X(_, _) => E::Unit::default(), + Self::Y(y, _) => *y, + Self::XY(_, y, _) => *y, + _ => unreachable!(), + } + } +} + +impl> Render for Push { + fn min_size (&self, to: E::Size) -> Perhaps { + self.inner().min_size(to) + } + fn render (&self, to: &mut E::Output) -> Usually<()> { + let area = to.area(); + Ok(self.min_size(area.wh().into())? + .map(|size|to.render_in(match *self { + Self::X(x, _) => [area.x() + x, area.y(), size.w(), size.h()], + Self::Y(y, _) => [area.x(), area.y() + y, size.w(), size.h()], + Self::XY(x, y, _) => [area.x() + x, area.y() + y, size.w(), size.h()], + _ => unreachable!(), + }.into(), self.inner())).transpose()?.unwrap_or(())) + } +} + +/// Decrement origin point of drawing area +pub enum Pull> { + _Unused(PhantomData), + /// Move origin to the right + X(E::Unit, T), + /// Move origin downwards + Y(E::Unit, T), + /// Move origin to the right and downwards + XY(E::Unit, E::Unit, T), +} + +impl> Pull { + pub fn inner (&self) -> &T { + match self { + Self::X(_, i) => i, + Self::Y(_, i) => i, + Self::XY(_, _, i) => i, + _ => unreachable!(), + } + } + pub fn x (&self) -> E::Unit { + match self { + Self::X(x, _) => *x, + Self::Y(_, _) => E::Unit::default(), + Self::XY(x, _, _) => *x, + _ => unreachable!(), + } + } + pub fn y (&self) -> E::Unit { + match self { + Self::X(_, _) => E::Unit::default(), + Self::Y(y, _) => *y, + Self::XY(_, y, _) => *y, + _ => unreachable!(), + } + } +} + +impl> Render for Pull { + fn min_size (&self, to: E::Size) -> Perhaps { + self.inner().min_size(to) + } + fn render (&self, to: &mut E::Output) -> Usually<()> { + let area = to.area(); + Ok(self.min_size(area.wh().into())? + .map(|size|to.render_in(match *self { + Self::X(x, _) => [area.x().minus(x), area.y(), size.w(), size.h()], + Self::Y(y, _) => [area.x(), area.y().minus(y), size.w(), size.h()], + Self::XY(x, y, _) => [area.x().minus(x), area.y().minus(y), size.w(), size.h()], + _ => unreachable!(), + }.into(), self.inner())).transpose()?.unwrap_or(())) + } +} + diff --git a/crates/tek_layout/src/shrink.rs b/crates/tek_layout/src/shrink.rs deleted file mode 100644 index 706973dc..00000000 --- a/crates/tek_layout/src/shrink.rs +++ /dev/null @@ -1,62 +0,0 @@ -use crate::*; - -impl> LayoutShrink for W {} - -pub trait LayoutShrink: Render + Sized { - fn shrink_x (self, x: E::Unit) -> Shrink { - Shrink::X(x, self) - } - fn shrink_y (self, y: E::Unit) -> Shrink { - Shrink::Y(y, self) - } - fn shrink_xy (self, x: E::Unit, y: E::Unit) -> Shrink { - Shrink::XY(x, y, self) - } -} - -/// Shrink drawing area -pub enum Shrink { - _Unused(PhantomData), - /// Decrease width - X(E::Unit, T), - /// Decrease height - Y(E::Unit, T), - /// Decrease width and height - XY(E::Unit, E::Unit, T), -} - -impl> Shrink { - fn inner (&self) -> &T { - match self { - Self::X(_, i) => i, - Self::Y(_, i) => i, - Self::XY(_, _, i) => i, - _ => unreachable!(), - } - } -} - -impl> Render for Shrink { - fn min_size (&self, to: E::Size) -> Perhaps { - Ok(self.inner().min_size(to)?.map(|to|match *self { - Self::X(w, _) => [ - if to.w() > w { to.w() - w } else { 0.into() }, - to.h() - ], - Self::Y(h, _) => [ - to.w(), - if to.h() > h { to.h() - h } else { 0.into() } - ], - Self::XY(w, h, _) => [ - if to.w() > w { to.w() - w } else { 0.into() }, - if to.h() > h { to.h() - h } else { 0.into() } - ], - _ => unreachable!(), - }.into())) - } - fn render (&self, to: &mut E::Output) -> Usually<()> { - Ok(self.min_size(to.area().wh().into())? - .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) - .transpose()?.unwrap_or(())) - } -} diff --git a/crates/tek_layout/src/shrink_grow.rs b/crates/tek_layout/src/shrink_grow.rs new file mode 100644 index 00000000..9186f724 --- /dev/null +++ b/crates/tek_layout/src/shrink_grow.rs @@ -0,0 +1,102 @@ +use crate::*; + +impl LayoutShrinkGrow for E {} + +pub trait LayoutShrinkGrow { + fn shrink_x > (x: E::Unit, w: W) -> Shrink { + Shrink::X(x, w) + } + fn shrink_y > (y: E::Unit, w: W) -> Shrink { + Shrink::Y(y, w) + } + fn shrink_xy > (x: E::Unit, y: E::Unit, w: W) -> Shrink { + Shrink::XY(x, y, w) + } + fn grow_x > (x: E::Unit, w: W) -> Grow { + Grow::X(x, w) + } + fn grow_y > (y: E::Unit, w: W) -> Grow { + Grow::Y(y, w) + } + fn grow_xy > (x: E::Unit, y: E::Unit, w: W) -> Grow { + Grow::XY(x, y, w) + } +} + +/// Shrink drawing area +pub enum Shrink { + _Unused(PhantomData), + /// Decrease width + X(E::Unit, T), + /// Decrease height + Y(E::Unit, T), + /// Decrease width and height + XY(E::Unit, E::Unit, T), +} + +impl> Shrink { + fn inner (&self) -> &T { + match self { + Self::X(_, i) => i, + Self::Y(_, i) => i, + Self::XY(_, _, i) => i, + _ => unreachable!(), + } + } +} + +impl> Render for Shrink { + fn min_size (&self, to: E::Size) -> Perhaps { + Ok(self.inner().min_size(to)?.map(|to|match *self { + Self::X(w, _) => [ + if to.w() > w { to.w() - w } else { 0.into() }, + to.h() + ], + Self::Y(h, _) => [ + to.w(), + if to.h() > h { to.h() - h } else { 0.into() } + ], + Self::XY(w, h, _) => [ + if to.w() > w { to.w() - w } else { 0.into() }, + if to.h() > h { to.h() - h } else { 0.into() } + ], + _ => unreachable!(), + }.into())) + } + fn render (&self, to: &mut E::Output) -> Usually<()> { + Ok(self.min_size(to.area().wh().into())? + .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) + .transpose()?.unwrap_or(())) + } +} + +/// Expand drawing area +pub enum Grow { + /// Increase width + X(N, T), + /// Increase height + Y(N, T), + /// Increase width and height + XY(N, N, T) +} + +impl Grow { + fn inner (&self) -> &T { + match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, } + } +} + +impl> Render for Grow { + fn min_size (&self, to: E::Size) -> Perhaps { + Ok(self.inner().min_size(to)?.map(|to|match *self { + Self::X(w, _) => [to.w() + w, to.h()], + Self::Y(h, _) => [to.w(), to.h() + h], + Self::XY(w, h, _) => [to.w() + w, to.h() + h], + }.into())) + } + fn render (&self, to: &mut E::Output) -> Usually<()> { + Ok(self.min_size(to.area().wh().into())? + .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) + .transpose()?.unwrap_or(())) + } +} diff --git a/crates/tek_layout/src/stack.rs b/crates/tek_layout/src/stack.rs index d2b6f285..ac383989 100644 --- a/crates/tek_layout/src/stack.rs +++ b/crates/tek_layout/src/stack.rs @@ -1,5 +1,33 @@ use crate::*; +#[macro_export] macro_rules! col { + ($($expr:expr),* $(,)?) => { Stack::down(move|add|{ $(add(&$expr)?;)* Ok(()) }) }; + ($pat:pat in $collection:expr => $item:expr) => { + Stack::down(move |add|{ + for $pat in $collection { add(&$item)?; } + Ok(()) + }) + } +} +#[macro_export] macro_rules! col_up { + ($($expr:expr),* $(,)?) => { Stack::down(move|add|{ $(add(&$expr)?;)* Ok(()) }) }; + ($pat:pat in $collection:expr => $item:expr) => { + Stack::up(move |add|{ + for $pat in $collection { add(&$item)?; } + Ok(()) + }) + } +} +#[macro_export] macro_rules! row { + ($($expr:expr),* $(,)?) => { Stack::right(move|add|{ $(add(&$expr)?;)* Ok(()) }) }; + ($pat:pat in $collection:expr => $item:expr) => { + Stack::right(move |add|{ + for $pat in $collection { add(&$item)?; } + Ok(()) + }) + } +} + pub struct Stack< E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)->Usually<()>)->Usually<()> @@ -36,7 +64,7 @@ where (self.0)(&mut |component: &dyn Render| { let max = to.h().minus(h); if max > E::Unit::ZERO() { - let item = component.push_y(h).max_y(max); + let item = E::max_y(max, E::push_y(h, component)); let size = item.min_size(to)?.map(|size|size.wh()); if let Some([width, height]) = size { h = h + height.into(); @@ -54,7 +82,7 @@ where (self.0)(&mut |component: &dyn Render| { let max = to.w().minus(w); if max > E::Unit::ZERO() { - let item = component.push_x(w).max_x(max); + let item = E::max_x(max, E::push_x(h, component)); let size = item.min_size(to)?.map(|size|size.wh()); if let Some([width, height]) = size { w = w + width.into(); diff --git a/crates/tek_tui/Cargo.toml b/crates/tek_tui/Cargo.toml index c50b78fe..2d7b3a90 100644 --- a/crates/tek_tui/Cargo.toml +++ b/crates/tek_tui/Cargo.toml @@ -3,6 +3,9 @@ name = "tek_tui" edition = "2021" version = "0.1.0" +[lib] +path = "src/tui.rs" + [dependencies] tek_core = { path = "../tek_core" } tek_layout = { path = "../tek_layout" } diff --git a/crates/tek_tui/src/lib.rs b/crates/tek_tui/src/lib.rs deleted file mode 100644 index 7ef99545..00000000 --- a/crates/tek_tui/src/lib.rs +++ /dev/null @@ -1,214 +0,0 @@ -pub(crate) use crossterm::event::{KeyCode, KeyModifiers}; -pub(crate) use tek_core::midly::{num::u7, live::LiveEvent, MidiMessage}; -pub(crate) use tek_core::{*, jack::*}; -pub(crate) use tek_layout::*; -pub(crate) use tek_api::*; - -pub(crate) use std::collections::BTreeMap; -pub(crate) use std::sync::{Arc, Mutex, RwLock}; -pub(crate) use std::path::PathBuf; -pub(crate) use std::ffi::OsString; -pub(crate) use std::fs::read_dir; -pub(crate) use better_panic::{Settings, Verbosity}; - -submod! { - tui_engine - tui_engine_input - tui_engine_output - tui_engine_style - - tui_app_arranger - tui_app_sequencer - tui_app_transport - - tui_jack_transport - tui_jack_sequencer - tui_jack_arranger - - tui_control_arranger - tui_control_file_browser - tui_control_phrase_editor - tui_control_phrase_length - tui_control_phrase_list - tui_control_phrase_rename - tui_control_sequencer - tui_control_transport - - tui_model_arranger - tui_model_clock - tui_model_file_browser - tui_model_phrase_editor - tui_model_phrase_length - tui_model_phrase_list - tui_model_phrase_player - - tui_view_arranger - tui_view_file_browser - tui_view_phrase_editor - tui_view_phrase_length - tui_view_phrase_list - tui_view_phrase_selector - tui_view_sequencer - tui_view_transport -} - -pub struct TuiTheme; - -impl TuiTheme { - pub fn border_bg () -> Color { - Color::Rgb(40, 50, 30) - } - pub fn border_fg (focused: bool) -> Color { - if focused { Color::Rgb(100, 110, 40) } else { Color::Rgb(70, 80, 50) } - } - pub fn title_fg (focused: bool) -> Color { - if focused { Color::Rgb(150, 160, 90) } else { Color::Rgb(120, 130, 100) } - } - pub fn separator_fg (_: bool) -> Color { - Color::Rgb(0, 0, 0) - } - pub const fn hotkey_fg () -> Color { - Color::Rgb(255, 255, 0) - } - pub fn mode_bg () -> Color { - Color::Rgb(150, 160, 90) - } - pub fn mode_fg () -> Color { - Color::Rgb(255, 255, 255) - } - pub fn status_bar_bg () -> Color { - Color::Rgb(28, 35, 25) - } -} - -pub trait FocusWrap { - fn wrap <'a, W: Render> (self, focus: T, content: &'a W) - -> impl Render + 'a; -} - -pub fn to_focus_command (input: &TuiInput) -> Option { - use KeyCode::{Tab, BackTab, Up, Down, Left, Right, Enter, Esc}; - Some(match input.event() { - key!(Tab) => FocusCommand::Next, - key!(Shift-Tab) => FocusCommand::Prev, - key!(BackTab) => FocusCommand::Prev, - key!(Shift-BackTab) => FocusCommand::Prev, - key!(Up) => FocusCommand::Up, - key!(Down) => FocusCommand::Down, - key!(Left) => FocusCommand::Left, - key!(Right) => FocusCommand::Right, - key!(Enter) => FocusCommand::Enter, - key!(Esc) => FocusCommand::Exit, - _ => return None - }) -} - -#[macro_export] macro_rules! impl_focus { - ($Struct:ident $Focus:ident $Grid:expr $(=> [$self:ident : $update_focus:expr])?) => { - impl HasFocus for $Struct { - type Item = $Focus; - /// Get the currently focused item. - fn focused (&self) -> Self::Item { - self.focus.inner() - } - /// Get the currently focused item. - fn set_focused (&mut self, to: Self::Item) { - self.focus.set_inner(to) - } - $(fn focus_updated (&mut $self) { $update_focus })? - } - impl HasEnter for $Struct { - /// Get the currently focused item. - fn entered (&self) -> bool { - self.focus.is_entered() - } - /// Get the currently focused item. - fn set_entered (&mut self, entered: bool) { - if entered { - self.focus.to_entered() - } else { - self.focus.to_focused() - } - } - } - impl FocusGrid for $Struct { - fn focus_cursor (&self) -> (usize, usize) { - self.cursor - } - fn focus_cursor_mut (&mut self) -> &mut (usize, usize) { - &mut self.cursor - } - fn focus_layout (&self) -> &[&[$Focus]] { - use $Focus::*; - &$Grid - } - } - } -} - -pub trait StatusBar: Render { - type State: Send + Sync; - fn hotkey_fg () -> Color where Self: Sized; - fn update (&mut self, state: &Self::State) where Self: Sized; - fn command (commands: &[[impl Render;3]]) - -> impl Render + '_ - where - Self: Sized - { - let hotkey_fg = Self::hotkey_fg(); - Stack::right(move |add|{ - Ok(for [a, b, c] in commands.iter() { - add(&row!( - " ", - widget(a), - widget(b).bold(true).fg(hotkey_fg), - widget(c), - ))?; - }) - }) - } - - fn with <'a> (state: &'a Self::State, content: impl Render) -> impl Render - where Self: Sized, &'a Self::State: Into - { - Stack::up(move |add|{ - add(&state.into())?; - add(&content) - }) - } -} - -fn content_with_menu_and_status <'a, A, S, C> ( - content: &'a A, - menu_bar: &'a Option>, - status_bar: &'a Option -) -> impl Render + 'a -where - A: Render, - S: Send + Sync + 'a, - C: Command -{ - let menus = menu_bar.as_ref().map_or_else( - ||&[] as &[Menu<_, _, _>], - |m|m.menus.as_slice() - ); - Either( - menu_bar.is_none(), - Either( - status_bar.is_none(), - widget(content), - Split::up( - 1, - widget(status_bar.as_ref().unwrap()), - widget(content) - ), - ), - Split::down( - 1, - row!(menu in menus.iter() => { - row!(" ", menu.title.as_str(), " ") - }), - widget(content) - ) - ) -} diff --git a/crates/tek_tui/src/tui_engine.rs b/crates/tek_tui/src/tui.rs similarity index 70% rename from crates/tek_tui/src/tui_engine.rs rename to crates/tek_tui/src/tui.rs index c47c3b9f..f629109f 100644 --- a/crates/tek_tui/src/tui_engine.rs +++ b/crates/tek_tui/src/tui.rs @@ -1,18 +1,76 @@ -use crate::*; -pub(crate) use std::io::{stdout}; +pub(crate) use crossterm::{ExecutableCommand}; +pub(crate) use crossterm::terminal::{EnterAlternateScreen, LeaveAlternateScreen, enable_raw_mode, disable_raw_mode}; +pub(crate) use crossterm::event::{KeyCode, KeyModifiers, KeyEvent, KeyEventKind, KeyEventState}; +pub(crate) use tek_core::midly::{num::u7, MidiMessage}; +pub(crate) use tek_core::{*, jack::*}; +pub(crate) use tek_layout::*; +pub(crate) use tek_api::*; +pub(crate) use std::sync::{Arc, RwLock}; +pub(crate) use std::path::PathBuf; +pub(crate) use std::ffi::OsString; +pub(crate) use better_panic::{Settings, Verbosity}; pub(crate) use std::thread::{spawn, JoinHandle}; pub(crate) use std::time::Duration; -pub(crate) use ratatui::buffer::Cell; -pub(crate) use crossterm::{ExecutableCommand}; -pub use crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState}; -pub use ratatui::prelude::{Rect, Style, Color, Buffer}; -pub use ratatui::style::{Stylize, Modifier}; -use ratatui::backend::{Backend, CrosstermBackend, ClearType}; -use std::io::Stdout; -use crossterm::terminal::{ - EnterAlternateScreen, LeaveAlternateScreen, - enable_raw_mode, disable_raw_mode -}; +pub(crate) use ratatui::prelude::{Style, Color, Buffer}; +pub(crate) use ratatui::style::{Stylize, Modifier}; +pub(crate) use ratatui::backend::{Backend, CrosstermBackend, ClearType}; +pub(crate) use std::io::{Stdout, stdout}; + +submod! { + tui_engine_focus + tui_engine_input + tui_engine_layout + tui_engine_output + tui_engine_style + + tui_app_arranger + tui_app_sequencer + tui_app_transport + + tui_jack_transport + tui_jack_sequencer + tui_jack_arranger + + tui_control_arranger + tui_control_file_browser + tui_control_phrase_editor + tui_control_phrase_length + tui_control_phrase_list + tui_control_phrase_rename + tui_control_sequencer + tui_control_transport + + tui_model_arranger + tui_model_clock + tui_model_file_browser + tui_model_phrase_editor + tui_model_phrase_length + tui_model_phrase_list + tui_model_phrase_player + + tui_view_arranger + tui_view_file_browser + tui_view_phrase_editor + tui_view_phrase_length + tui_view_phrase_list + tui_view_phrase_selector + tui_view_sequencer + tui_view_status_bar + tui_view_transport +} + +#[macro_export] macro_rules! render { + (|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => { + impl $(<$($L),*$($T $(: $U)?),*>)? Render for $Struct $(<$($L),*$($T),*>)? { + fn min_size (&$self, to: [u16;2]) -> Perhaps<[u16;2]> { + $cb.min_size(to) + } + fn render (&$self, to: &mut TuiOutput) -> Usually<()> { + $cb.render(to) + } + } + } +} pub struct Tui { pub exited: Arc, @@ -20,6 +78,7 @@ pub struct Tui { pub backend: CrosstermBackend, pub area: [u16;4], // FIXME auto resize } + impl Engine for Tui { type Unit = u16; type Size = [Self::Unit;2]; @@ -48,6 +107,7 @@ impl Engine for Tui { disable_raw_mode().map_err(Into::into) } } + impl Tui { /// Run the main loop. pub fn run + Sized + 'static> ( diff --git a/crates/tek_tui/src/tui_app_arranger.rs b/crates/tek_tui/src/tui_app_arranger.rs index 9155f9cf..2487d85b 100644 --- a/crates/tek_tui/src/tui_app_arranger.rs +++ b/crates/tek_tui/src/tui_app_arranger.rs @@ -152,84 +152,88 @@ impl StatusBar for ArrangerStatus { } } -impl Content for ArrangerStatus { - fn content (&self) -> impl Render { - let label = match self { - Self::Transport => "TRANSPORT", - Self::ArrangerMix => "PROJECT", - Self::ArrangerTrack => "TRACK", - Self::ArrangerScene => "SCENE", - Self::ArrangerClip => "CLIP", - Self::PhrasePool => "SEQ LIST", - Self::PhraseView => "VIEW SEQ", - Self::PhraseEdit => "EDIT SEQ", - }; - let status_bar_bg = TuiTheme::status_bar_bg(); - let mode_bg = TuiTheme::mode_bg(); - let mode_fg = TuiTheme::mode_fg(); - let mode = TuiStyle::bold(format!(" {label} "), true).bg(mode_bg).fg(mode_fg); - let commands = match self { - Self::ArrangerMix => Self::command(&[ - ["", "c", "olor"], - ["", "<>", "resize"], - ["", "+-", "zoom"], - ["", "n", "ame/number"], - ["", "Enter", " stop all"], - ]), - Self::ArrangerClip => Self::command(&[ - ["", "g", "et"], - ["", "s", "et"], - ["", "a", "dd"], - ["", "i", "ns"], - ["", "d", "up"], - ["", "e", "dit"], - ["", "c", "olor"], - ["re", "n", "ame"], - ["", ",.", "select"], - ["", "Enter", " launch"], - ]), - Self::ArrangerTrack => Self::command(&[ - ["re", "n", "ame"], - ["", ",.", "resize"], - ["", "<>", "move"], - ["", "i", "nput"], - ["", "o", "utput"], - ["", "m", "ute"], - ["", "s", "olo"], - ["", "Del", "ete"], - ["", "Enter", " stop"], - ]), - Self::ArrangerScene => Self::command(&[ - ["re", "n", "ame"], - ["", "Del", "ete"], - ["", "Enter", " launch"], - ]), - Self::PhrasePool => Self::command(&[ - ["", "a", "ppend"], - ["", "i", "nsert"], - ["", "d", "uplicate"], - ["", "Del", "ete"], - ["", "c", "olor"], - ["re", "n", "ame"], - ["leng", "t", "h"], - ["", ",.", "move"], - ["", "+-", "resize view"], - ]), - Self::PhraseView => Self::command(&[ - ["", "enter", " edit"], - ["", "arrows/pgup/pgdn", " scroll"], - ["", "+=", "zoom"], - ]), - Self::PhraseEdit => Self::command(&[ - ["", "esc", " exit"], - ["", "a", "ppend"], - ["", "s", "et"], - ["", "][", "length"], - ["", "+-", "zoom"], - ]), - _ => Self::command(&[]) - }; - //let commands = commands.iter().reduce(String::new(), |s, (a, b, c)| format!("{s} {a}{b}{c}")); - row!(mode, commands).fill_x().bg(status_bar_bg) - } -} +render!(|self: ArrangerStatus|{ + + let label = match self { + Self::Transport => "TRANSPORT", + Self::ArrangerMix => "PROJECT", + Self::ArrangerTrack => "TRACK", + Self::ArrangerScene => "SCENE", + Self::ArrangerClip => "CLIP", + Self::PhrasePool => "SEQ LIST", + Self::PhraseView => "VIEW SEQ", + Self::PhraseEdit => "EDIT SEQ", + }; + + let status_bar_bg = TuiTheme::status_bar_bg(); + + let mode_bg = TuiTheme::mode_bg(); + let mode_fg = TuiTheme::mode_fg(); + let mode = TuiStyle::bold(format!(" {label} "), true).bg(mode_bg).fg(mode_fg); + + let commands = match self { + Self::ArrangerMix => Self::command(&[ + ["", "c", "olor"], + ["", "<>", "resize"], + ["", "+-", "zoom"], + ["", "n", "ame/number"], + ["", "Enter", " stop all"], + ]), + Self::ArrangerClip => Self::command(&[ + ["", "g", "et"], + ["", "s", "et"], + ["", "a", "dd"], + ["", "i", "ns"], + ["", "d", "up"], + ["", "e", "dit"], + ["", "c", "olor"], + ["re", "n", "ame"], + ["", ",.", "select"], + ["", "Enter", " launch"], + ]), + Self::ArrangerTrack => Self::command(&[ + ["re", "n", "ame"], + ["", ",.", "resize"], + ["", "<>", "move"], + ["", "i", "nput"], + ["", "o", "utput"], + ["", "m", "ute"], + ["", "s", "olo"], + ["", "Del", "ete"], + ["", "Enter", " stop"], + ]), + Self::ArrangerScene => Self::command(&[ + ["re", "n", "ame"], + ["", "Del", "ete"], + ["", "Enter", " launch"], + ]), + Self::PhrasePool => Self::command(&[ + ["", "a", "ppend"], + ["", "i", "nsert"], + ["", "d", "uplicate"], + ["", "Del", "ete"], + ["", "c", "olor"], + ["re", "n", "ame"], + ["leng", "t", "h"], + ["", ",.", "move"], + ["", "+-", "resize view"], + ]), + Self::PhraseView => Self::command(&[ + ["", "enter", " edit"], + ["", "arrows/pgup/pgdn", " scroll"], + ["", "+=", "zoom"], + ]), + Self::PhraseEdit => Self::command(&[ + ["", "esc", " exit"], + ["", "a", "ppend"], + ["", "s", "et"], + ["", "][", "length"], + ["", "+-", "zoom"], + ]), + _ => Self::command(&[]) + }; + + //let commands = commands.iter().reduce(String::new(), |s, (a, b, c)| format!("{s} {a}{b}{c}")); + Tui::bg(status_bar_bg, Tui::fill_x(Tui::to_east(mode, commands))) + +}); diff --git a/crates/tek_tui/src/tui_app_transport.rs b/crates/tek_tui/src/tui_app_transport.rs index 3c689839..bb566084 100644 --- a/crates/tek_tui/src/tui_app_transport.rs +++ b/crates/tek_tui/src/tui_app_transport.rs @@ -55,8 +55,8 @@ impl FocusWrap for TransportFocus { { let focused = focus == self; let corners = focused.then_some(CORNERS); - let highlight = focused.then_some(Background(Color::Rgb(60, 70, 50))); - lay!(corners, highlight, *content) + //let highlight = focused.then_some(Tui::bg(Color::Rgb(60, 70, 50))); + lay!(corners, /*highlight,*/ *content) } } @@ -66,8 +66,8 @@ impl FocusWrap for Option { { let focused = Some(focus) == self; let corners = focused.then_some(CORNERS); - let highlight = focused.then_some(Background(Color::Rgb(60, 70, 50))); - lay!(corners, highlight, *content) + //let highlight = focused.then_some(Background(Color::Rgb(60, 70, 50))); + lay!(corners, /*highlight,*/ *content) } } @@ -95,9 +95,7 @@ impl StatusBar for TransportStatusBar { } } -impl Content for TransportStatusBar { - fn content (&self) -> impl Render { - todo!(); - "" - } -} +render!(|self: TransportStatusBar|{ + todo!(); + "" +}); diff --git a/crates/tek_tui/src/tui_engine_focus.rs b/crates/tek_tui/src/tui_engine_focus.rs new file mode 100644 index 00000000..4b0c417a --- /dev/null +++ b/crates/tek_tui/src/tui_engine_focus.rs @@ -0,0 +1,66 @@ +use crate::*; + +pub trait FocusWrap { + fn wrap <'a, W: Render> (self, focus: T, content: &'a W) + -> impl Render + 'a; +} + +pub fn to_focus_command (input: &TuiInput) -> Option { + use KeyCode::{Tab, BackTab, Up, Down, Left, Right, Enter, Esc}; + Some(match input.event() { + key!(Tab) => FocusCommand::Next, + key!(Shift-Tab) => FocusCommand::Prev, + key!(BackTab) => FocusCommand::Prev, + key!(Shift-BackTab) => FocusCommand::Prev, + key!(Up) => FocusCommand::Up, + key!(Down) => FocusCommand::Down, + key!(Left) => FocusCommand::Left, + key!(Right) => FocusCommand::Right, + key!(Enter) => FocusCommand::Enter, + key!(Esc) => FocusCommand::Exit, + _ => return None + }) +} + +#[macro_export] macro_rules! impl_focus { + ($Struct:ident $Focus:ident $Grid:expr $(=> [$self:ident : $update_focus:expr])?) => { + impl HasFocus for $Struct { + type Item = $Focus; + /// Get the currently focused item. + fn focused (&self) -> Self::Item { + self.focus.inner() + } + /// Get the currently focused item. + fn set_focused (&mut self, to: Self::Item) { + self.focus.set_inner(to) + } + $(fn focus_updated (&mut $self) { $update_focus })? + } + impl HasEnter for $Struct { + /// Get the currently focused item. + fn entered (&self) -> bool { + self.focus.is_entered() + } + /// Get the currently focused item. + fn set_entered (&mut self, entered: bool) { + if entered { + self.focus.to_entered() + } else { + self.focus.to_focused() + } + } + } + impl FocusGrid for $Struct { + fn focus_cursor (&self) -> (usize, usize) { + self.cursor + } + fn focus_cursor_mut (&mut self) -> &mut (usize, usize) { + &mut self.cursor + } + fn focus_layout (&self) -> &[&[$Focus]] { + use $Focus::*; + &$Grid + } + } + } +} diff --git a/crates/tek_tui/src/tui_engine_layout.rs b/crates/tek_tui/src/tui_engine_layout.rs new file mode 100644 index 00000000..c7b7e813 --- /dev/null +++ b/crates/tek_tui/src/tui_engine_layout.rs @@ -0,0 +1 @@ +use crate::*; diff --git a/crates/tek_tui/src/tui_engine_output.rs b/crates/tek_tui/src/tui_engine_output.rs index 70021358..025c3bfb 100644 --- a/crates/tek_tui/src/tui_engine_output.rs +++ b/crates/tek_tui/src/tui_engine_output.rs @@ -1,6 +1,19 @@ use crate::*; use ratatui::buffer::Cell; +/// Every struct that has [Content]<[Tui]> is a renderable [Render]<[Tui]>. +//impl> Render for C { + //fn min_size (&self, to: [u16;2]) -> Perhaps { + //self.content().min_size(to) + //} + //fn render (&self, to: &mut TuiOutput) -> Usually<()> { + //match self.min_size(to.area().wh().into())? { + //Some(wh) => to.render_in(to.area().clip(wh).into(), &self.content()), + //None => Ok(()) + //} + //} +//} + pub struct TuiOutput { pub buffer: Buffer, pub area: [u16;4] @@ -124,38 +137,39 @@ pub fn buffer_update (buf: &mut Buffer, area: [u16;4], callback: &impl Fn(&mut C } } } -//impl Render for &str { - //type Tui; - //fn min_size (&self, _: [u16;2]) -> Perhaps<[u16;2]> { - //// TODO: line breaks - //Ok(Some([self.chars().count() as u16, 1])) - //} - //fn render (&self, to: &mut TuiOutput) -> Usually<()> { - //let [x, y, ..] = to.area(); - ////let [w, h] = self.min_size(to.area().wh())?.unwrap(); - //Ok(to.blit(&self, x, y, None)) - //} -//} -//impl Render for String { - //type Tui; - //fn min_size (&self, _: [u16;2]) -> Perhaps<[u16;2]> { - //// TODO: line breaks - //Ok(Some([self.chars().count() as u16, 1])) - //} - //fn render (&self, to: &mut TuiOutput) -> Usually<()> { - //let [x, y, ..] = to.area(); - ////let [w, h] = self.min_size(to.area().wh())?.unwrap(); - //Ok(to.blit(&self, x, y, None)) - //} -//} -//impl> Render for DebugOverlay { - //type Tui; - //fn min_size (&self, to: [u16;2]) -> Perhaps<[u16;2]> { - //self.0.min_size(to) - //} - //fn render (&self, to: &mut TuiOutput) -> Usually<()> { - //let [x, y, w, h] = to.area(); - //self.0.render(to)?; - //Ok(to.blit(&format!("{w}x{h}+{x}+{y}"), x, y, Some(Style::default().green()))) - //} -//} + +impl Render for &str { + fn min_size (&self, _: [u16;2]) -> Perhaps<[u16;2]> { + // TODO: line breaks + Ok(Some([self.chars().count() as u16, 1])) + } + fn render (&self, to: &mut TuiOutput) -> Usually<()> { + let [x, y, ..] = to.area(); + //let [w, h] = self.min_size(to.area().wh())?.unwrap(); + Ok(to.blit(&self, x, y, None)) + } +} + +impl Render for String { + fn min_size (&self, _: [u16;2]) -> Perhaps<[u16;2]> { + // TODO: line breaks + Ok(Some([self.chars().count() as u16, 1])) + } + fn render (&self, to: &mut TuiOutput) -> Usually<()> { + let [x, y, ..] = to.area(); + //let [w, h] = self.min_size(to.area().wh())?.unwrap(); + Ok(to.blit(&self, x, y, None)) + } +} + +impl> Render for DebugOverlay { + fn min_size (&self, to: [u16;2]) -> Perhaps<[u16;2]> { + self.1.min_size(to) + } + fn render (&self, to: &mut TuiOutput) -> Usually<()> { + let [x, y, w, h] = to.area(); + self.1.render(to)?; + Ok(to.blit(&format!("{w}x{h}+{x}+{y}"), x, y, Some(Style::default().green()))) + } +} + diff --git a/crates/tek_tui/src/tui_engine_style.rs b/crates/tek_tui/src/tui_engine_style.rs index bd287daf..e1c1c61e 100644 --- a/crates/tek_tui/src/tui_engine_style.rs +++ b/crates/tek_tui/src/tui_engine_style.rs @@ -1,57 +1,47 @@ use crate::*; -pub struct Styled>(pub Option