diff --git a/output/src/layout_align.rs b/output/src/layout_align.rs index 59eda80..9991d1d 100644 --- a/output/src/layout_align.rs +++ b/output/src/layout_align.rs @@ -48,33 +48,36 @@ impl Align { #[inline] pub const fn se (a: A) -> Self { Self(Alignment::SE, a) } } -impl> Content for Align { - fn content (&self) -> impl Render + '_ { - &self.1 - } +impl> Render 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 Alignment { + fn align (&self, on: E::Area, content: impl Render) -> E::Area { use Alignment::*; - let it = Render::layout(&self.content(), on).xywh(); + let it = content.layout(on).xywh(); let cx = on.x()+(on.w().minus(it.w())/2.into()); let cy = on.y()+(on.h().minus(it.h())/2.into()); let fx = (on.x()+on.w()).minus(it.w()); let fy = (on.y()+on.h()).minus(it.h()); - let [x, y] = match self.0 { - Center => [cx, cy], - X => [cx, it.y()], - Y => [it.x(), cy], - NW => [on.x(), on.y()], - N => [cx, on.y()], - NE => [fx, on.y()], - W => [on.x(), cy], - E => [fx, cy], - SW => [on.x(), fy], - S => [cx, fy], - SE => [fx, fy], - }.into(); + let [x, y] = match self { + Center => [cx, cy], + X => [cx, it.y()], + Y => [it.x(), cy], + NW => [on.x(), on.y()], + N => [cx, on.y()], + NE => [fx, on.y()], + W => [on.x(), cy], + E => [fx, cy], + SW => [on.x(), fy], + S => [cx, fy], + SE => [fx, fy], + }; [x, y, it.w(), it.h()].into() } - fn render (&self, to: &mut E) { - to.place(Content::layout(self, to.area()), &self.content()) - } } diff --git a/output/src/layout_bsp.rs b/output/src/layout_bsp.rs index 0272d17..73d6882 100644 --- a/output/src/layout_bsp.rs +++ b/output/src/layout_bsp.rs @@ -15,7 +15,7 @@ impl Bsp { #[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: Content> Content for Bsp { +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()); @@ -26,11 +26,11 @@ impl, B: Content> Content for Bsp { } } } -impl, B: Content> BspAreas for Bsp { +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: Content> { +pub trait BspAreas, B: Render> { fn direction (&self) -> Direction; fn contents (&self) -> (&A, &B); fn areas (&self, outer: E::Area) -> [E::Area;3] { diff --git a/output/src/layout_cond.rs b/output/src/layout_cond.rs index 1da4925..7a7bdc0 100644 --- a/output/src/layout_cond.rs +++ b/output/src/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> Content for When { +impl> Render for When { fn layout (&self, to: E::Area) -> E::Area { let Self(cond, item) = self; let mut area = E::Area::zero(); @@ -31,7 +31,7 @@ impl Either { /// Create a ternary view condition. pub const fn new (c: bool, a: A, b: B) -> Self { Self(c, a, b) } } -impl, B: Render> Content for Either { +impl, B: Render> Render for Either { fn layout (&self, to: E::Area) -> E::Area { let Self(cond, a, b) = self; if *cond { a.layout(to) } else { b.layout(to) } diff --git a/output/src/layout_map.rs b/output/src/layout_map.rs index a194c06..4c8494c 100644 --- a/output/src/layout_map.rs +++ b/output/src/layout_map.rs @@ -66,7 +66,7 @@ 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> Content for Map where +impl<'a, E, A, B, I, F, G> Render for Map where E: Output, B: Render, I: Iterator + Send + Sync + 'a, @@ -94,7 +94,7 @@ impl<'a, E, A, B, I, F, G> Content for Map where fn render (&self, to: &mut E) { let Self { get_iter, get_item, .. } = self; let mut index = 0; - let area = Content::layout(self, to.area()); + let area = Render::layout(self, to.area()); for item in get_iter() { let item = get_item(item, index); //to.place(area.into(), &item); @@ -107,24 +107,24 @@ impl<'a, E, A, B, I, F, G> Content for Map where #[inline] pub fn map_south( item_offset: O::Unit, item_height: O::Unit, - item: impl Content -) -> impl Content { + item: impl Render +) -> impl Render { Push::y(item_offset, Fixed::y(item_height, Fill::x(item))) } #[inline] pub fn map_south_west( item_offset: O::Unit, item_height: O::Unit, - item: impl Content -) -> impl Content { + item: impl Render +) -> impl Render { Push::y(item_offset, Align::nw(Fixed::y(item_height, Fill::x(item)))) } #[inline] pub fn map_east( item_offset: O::Unit, item_width: O::Unit, - item: impl Content -) -> impl Content { + item: impl Render +) -> impl Render { Push::x(item_offset, Align::w(Fixed::x(item_width, Fill::y(item)))) } diff --git a/output/src/layout_stack.rs b/output/src/layout_stack.rs index 95f8c82..c316c39 100644 --- a/output/src/layout_stack.rs +++ b/output/src/layout_stack.rs @@ -1,43 +1,44 @@ use crate::*; use Direction::*; -pub struct Stack<'x, E, F> { - __: PhantomData<&'x E>, +pub struct Stack<'x, E, F1> { + __: PhantomData<&'x (E, F1)>, direction: Direction, - callback: F + callback: F1 } -impl<'x, E, F: Fn(&mut dyn FnMut(&dyn Render)) + 'x> Stack<'x, E, F> { - pub fn new (direction: Direction, callback: F) -> Self { + +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: F) -> Self { + pub fn above (callback: F1) -> Self { Self::new(Above, callback) } - pub fn below (callback: F) -> Self { + pub fn below (callback: F1) -> Self { Self::new(Below, callback) } - pub fn north (callback: F) -> Self { + pub fn north (callback: F1) -> Self { Self::new(North, callback) } - pub fn south (callback: F) -> Self { + pub fn south (callback: F1) -> Self { Self::new(South, callback) } - pub fn east (callback: F) -> Self { + pub fn east (callback: F1) -> Self { Self::new(East, callback) } - pub fn west (callback: F) -> Self { + pub fn west (callback: F1) -> Self { Self::new(West, callback) } } -impl<'x, E: Output, F: Fn(&mut dyn FnMut(&dyn Render)) + 'x> Content for Stack<'x, E, F> { + +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); - let mut adder = |component: &dyn Render|{ + (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); - }; - (self.callback)(&mut adder); + }); let StackLayoutState { w_used, h_used, .. } = *state.borrow(); match self.direction { North | West => { todo!() }, @@ -48,13 +49,12 @@ impl<'x, E: Output, F: Fn(&mut dyn FnMut(&dyn Render)) + 'x> Content for S fn render (&self, to: &mut E) { let state = StackLayoutState::::new(self.direction, to.area()); let to = Rc::new(RefCell::new(to)); - let mut adder = |component: &dyn Render|{ + (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); - }; - (self.callback)(&mut adder); + }); } } @@ -115,7 +115,7 @@ impl StackLayoutState { //Self { direction, callback, __: Default::default(), } //} //} -//impl<'a, E, F1> Content for Stack<'a, E, F1> where +//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 { diff --git a/output/src/layout_xy.rs b/output/src/layout_xy.rs index 93d2eea..52d99a6 100644 --- a/output/src/layout_xy.rs +++ b/output/src/layout_xy.rs @@ -2,9 +2,8 @@ //! ``` //! use ::tengri::{output::*, tui::*}; //! let area: [u16;4] = [10, 10, 20, 20]; -//! fn test (area: [u16;4], item: &impl Content, expected: [u16;4]) { -//! assert_eq!(Content::layout(item, area), expected); -//! assert_eq!(Render::layout(item, area), expected); +//! fn test (area: [u16;4], item: &impl Render, expected: [u16;4]) { +//! assert_eq!(item.layout(area), expected); //! }; //! test(area, &(), [20, 20, 0, 0]); //! @@ -28,18 +27,20 @@ macro_rules! transform_xy { #[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) -> impl Render + '_ { - match self { - Self::X(item) => item, - Self::Y(item) => item, - Self::XY(item) => 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()) + } } } } @@ -54,13 +55,18 @@ macro_rules! transform_xy_unit { #[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 { + 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 content (&self) -> impl Render + '_ { - use $Enum::*; - Some(match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, }) + fn render (&self, output: &mut E) { + output.place(self.layout(output.area()), &self.content()) } } impl $Enum { @@ -88,12 +94,11 @@ transform_xy!("fill/x" "fill/y" "fill/xy" |self: Fill, to|{ transform_xy_unit!("fixed/x" "fixed/y" "fixed/xy"|self: Fixed, area|{ let [x, y, w, h] = area.xywh(); - let fixed_area = match self { + 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], - }; - let [x, y, w, h] = Render::layout(&self.content(), fixed_area.into()).xywh(); + }.into()).xywh(); let fixed_area = match self { Self::X(fw, _) => [x, y, *fw, h], Self::Y(fh, _) => [x, y, w, *fh], @@ -103,51 +108,41 @@ transform_xy_unit!("fixed/x" "fixed/y" "fixed/xy"|self: Fixed, area|{ }); transform_xy_unit!("min/x" "min/y" "min/xy"|self: Min, area|{ - let area = Render::layout(&self.content(), area); + let [x, y, w, h] = self.content().layout(area).xywh(); match self { - Self::X(mw, _) => [area.x(), area.y(), area.w().max(*mw), area.h()], - Self::Y(mh, _) => [area.x(), area.y(), area.w(), area.h().max(*mh)], - Self::XY(mw, mh, _) => [area.x(), area.y(), area.w().max(*mw), area.h().max(*mh)], - } -}); + 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(); - Render::layout(&self.content(), match self { + 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()) -}); + Self::XY(fw, fh, _) => [x, y, *fw, *fh], }.into()) }); -transform_xy_unit!("shrink/x" "shrink/y" "shrink/xy"|self: Shrink, area|Render::layout( - &self.content(), +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|Render::layout( - &self.content(), +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 = Render::layout(&self.content(), area); - [area.x().plus(self.dx()), area.y().plus(self.dy()), area.w(), area.h()] -}); + 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 = Render::layout(&self.content(), area); - [area.x().minus(self.dx()), area.y().minus(self.dy()), area.w(), area.h()] -}); + 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 = Render::layout(&self.content(), 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))] -}); + [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 = Render::layout(&self.content(), 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))] -}); + [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/output.rs b/output/src/output.rs index 4eb1036..bc9e2b9 100644 --- a/output/src/output.rs +++ b/output/src/output.rs @@ -1,5 +1,4 @@ use crate::*; -use std::ops::Deref; /// Render target. pub trait Output: Send + Sync + Sized { diff --git a/output/src/output_content.rs b/output/src/output_content.rs index ef199db..392f49c 100644 --- a/output/src/output_content.rs +++ b/output/src/output_content.rs @@ -1,88 +1,67 @@ use crate::*; /// Composable renderable with static dispatch. -pub trait Content { - /// Return a [Render]able of a specific type. - fn content (&self) -> impl Render + '_ { - () - } - /// Perform layout. By default, delegates to [Self::content]. - fn layout (&self, area: E::Area) -> E::Area { - self.content().layout(area) - } - /// Draw to output. By default, delegates to [Self::content]. - fn render (&self, output: &mut E) { - self.content().render(output) - } -} - -/// Every pointer to [Content] is a [Content]. -impl> Content for &C { - fn content (&self) -> impl Render + '_ { (*self).content() } - fn layout (&self, area: E::Area) -> E::Area { (*self).layout(area) } - fn render (&self, output: &mut E) { (*self).render(output) } +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 () { - fn layout (&self, area: E::Area) -> E::Area { area.center().to_area_pos().into() } - fn render (&self, _: &mut E) {} +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) -> impl Render + '_ { - self.as_ref() - } - fn layout (&self, area: E::Area) -> E::Area { - self.as_ref() - .map(|content|content.layout(area)) - .unwrap_or([0.into(), 0.into(), 0.into(), 0.into(),].into()) - } - fn render (&self, output: &mut E) { - self.as_ref() - .map(|content|content.render(output)); + fn content (&self) -> Option + '_> { + if let Some(content) = self { + content.content() + } else { + None + } } } /// You can render from a box. impl Content for RenderBox { - fn content (&self) -> impl Render + '_ { self.deref() } - //fn boxed <'b> (self) -> RenderBox<'b, E> where Self: Sized + 'b { self } + fn content (&self) -> Option + '_> { + Some(self.deref()) + } } /// You can render from an opaque pointer. impl Content for &dyn Render where Self: Sized { - fn content (&self) -> impl Render + '_ { + fn content (&self) -> Option + '_> { #[allow(suspicious_double_ref_op)] - self.deref() - } - fn layout (&self, area: E::Area) -> E::Area { - #[allow(suspicious_double_ref_op)] - Render::layout(self.deref(), area) - } - fn render (&self, output: &mut E) { - #[allow(suspicious_double_ref_op)] - Render::render(self.deref(), output) + Some(self.deref()) } } -/// Implement [Content] with 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)?),*> Content - for $Struct $(<$($L),* $($T),*>>)? { - fn render (&$self, $to: &mut E) { $render } +/// 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)?),+>)?, $to:ident - |$render:expr) => { + $Struct:ident$(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)? + |$content:expr) => { impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Content<$Output> for $Struct $(<$($($L)? $($T)?),+>)? { - fn render (&$self, $to: &mut $Output) { $render } + fn content (&$self) -> impl Render<$Output> + '_ { $content } } }; } diff --git a/output/src/output_render.rs b/output/src/output_render.rs index 3027b0c..d1f97a9 100644 --- a/output/src/output_render.rs +++ b/output/src/output_render.rs @@ -2,46 +2,92 @@ use crate::*; /// Renderable with dynamic dispatch. pub trait Render { - /// Compute layout. - fn layout (&self, area: E::Area) -> E::Area; /// Write data to display. fn render (&self, output: &mut E); + /// Compute layout. + fn layout (&self, area: E::Area) -> E::Area { area } /// Perform type erasure, turning `self` into an opaque [RenderBox]. fn boxed <'a> (self) -> Box + 'a> where Self: Sized + 'a { Box::new(self) as Box + 'a> } + /// Perform type erasure, turning `self` into an opaque [Rc]. + fn rc <'a> (self) -> Rc + 'a> where Self: Sized + 'a { + Rc::new(self) as Rc + 'a> + } } -/// Every [Content] is also a [Render]. -/// However, the converse does not hold true. -/// Instead, the [Content::content] method returns an -/// opaque [Render] pointer. -impl> Render for C { - fn layout (&self, area: E::Area) -> E::Area { Content::layout(self, area) } - fn render (&self, output: &mut E) { Content::render(self, output) } +impl Render for () { + fn render (&self, _: &mut E) {} } -/// Opaque pointer to a renderable living on the heap. +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) + } + } +} + +/// Opaque pointer to a renderable that lives on the heap. /// /// Return this from [Content::content] to use dynamic dispatch. pub type RenderBox = Box>; -/// Implement [Content] with 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 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)?),*> Content + for $Struct $(<$($L),* $($T),*>>)? { + fn render (&$self, $to: &mut E) { $render } } }; - // Implement for specific [Output]. ($Output:ty:| $self:ident: - $Struct:ident$(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)? - |$content:expr) => { + $Struct:ident $(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)?, $to:ident + |$render:expr) => { impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Content<$Output> for $Struct $(<$($($L)? $($T)?),+>)? { - fn content (&$self) -> impl Render<$Output> + '_ { $content } + fn render (&$self, $to: &mut $Output) { $render } } }; } diff --git a/output/src/output_thunk.rs b/output/src/output_thunk.rs index 5a2cb5c..f505c38 100644 --- a/output/src/output_thunk.rs +++ b/output/src/output_thunk.rs @@ -11,65 +11,19 @@ impl, F: Fn()->T> Thunk { } } impl, F: Fn()->T> Content for Thunk { - fn content (&self) -> impl Render { (self.1)() } -} - -/// Lazily-evaluated [Render]able with -/// mandatory stack allocation and dynamic dispatch. -pub struct ThunkBox( - PhantomData, - BoxBox>>, -); -impl ThunkBox { - pub const fn new (thunk: BoxBox>>) -> Self { - Self(PhantomData, thunk) + fn content (&self) -> Option> { + Some((self.1)()) } } -impl Content for ThunkBox { - fn content (&self) -> impl Render { (&self.1)() } -} -impl FromBox>>> for ThunkBox { - fn from (f: BoxBox>>) -> Self { - Self(PhantomData, f) - } -} - -//impl<'a, E: Output, F: Fn()->Box + 'a> + 'a> From for ThunkBox<'a, E> { - //fn from (f: F) -> Self { - //Self(Default::default(), Box::new(f)) - //} -//} pub struct ThunkRender(PhantomData, F); impl ThunkRender { pub fn new (render: F) -> Self { Self(PhantomData, render) } } -impl Content for ThunkRender { +impl Render for ThunkRender { fn render (&self, to: &mut E) { (self.1)(to) } } -pub struct ThunkLayout< - E: Output, - F1: Fn(E::Area)->E::Area, - F2: Fn(&mut E) ->( - PhantomData, - F1, - F2 -); -implE::Area, F2: Fn(&mut E)> ThunkLayout { - pub fn new (layout: F1, render: F2) -> Self { Self(PhantomData, layout, render) } -} -impl Content for ThunkLayout -where - E: Output, - F1: Fn(E::Area)->E::Area, - F2: Fn(&mut E) -{ - fn layout (&self, to: E::Area) -> E::Area { (self.1)(to) } - fn render (&self, to: &mut E) { (self.2)(to) } -} - #[derive(Debug, Default)] pub struct Memo { pub value: T, pub view: Arc> diff --git a/output/src/space.rs b/output/src/space.rs deleted file mode 100644 index 3e850fd..0000000 --- a/output/src/space.rs +++ /dev/null @@ -1,281 +0,0 @@ -use crate::*; -use Direction::*; - -/// A cardinal direction. -#[derive(Copy, Clone, PartialEq, Debug)] -#[cfg_attr(test, derive(Arbitrary))] -pub enum Direction { - North, South, East, West, Above, Below -} - -impl Direction { - pub fn split_fixed (self, area: impl Area, a: N) -> ([N;4],[N;4]) { - let [x, y, w, h] = area.xywh(); - match self { - North => ([x, y.plus(h).minus(a), w, a], [x, y, w, h.minus(a)]), - South => ([x, y, w, a], [x, y.plus(a), w, h.minus(a)]), - East => ([x, y, a, h], [x.plus(a), y, w.minus(a), h]), - West => ([x.plus(w).minus(a), y, a, h], [x, y, w.minus(a), h]), - Above | Below => (area.xywh(), area.xywh()) - } - } -} - -/// A linear coordinate. -pub trait Coordinate: Send + Sync + Copy - + Add - + Sub - + Mul - + Div - + Ord + PartialEq + Eq - + Debug + Display + Default - + From + Into - + Into - + Into -{ - fn zero () -> Self { 0.into() } - fn plus (self, other: Self) -> Self; - fn minus (self, other: Self) -> Self { - if self >= other { - self - other - } else { - 0.into() - } - } -} - -impl Coordinate for u16 { - fn plus (self, other: Self) -> Self { - self.saturating_add(other) - } -} - -pub trait Area: From<[N;4]> + Debug + Copy { - fn x (&self) -> N; - fn y (&self) -> N; - fn w (&self) -> N; - fn h (&self) -> N; - fn zero () -> [N;4] { - [N::zero(), N::zero(), N::zero(), N::zero()] - } - fn from_position (pos: impl Size) -> [N;4] { - let [x, y] = pos.wh(); - [x, y, 0.into(), 0.into()] - } - fn from_size (size: impl Size) -> [N;4] { - let [w, h] = size.wh(); - [0.into(), 0.into(), w, h] - } - fn expect_min (&self, w: N, h: N) -> Usually<&Self> { - if self.w() < w || self.h() < h { - Err(format!("min {w}x{h}").into()) - } else { - Ok(self) - } - } - fn xy (&self) -> [N;2] { - [self.x(), self.y()] - } - fn wh (&self) -> [N;2] { - [self.w(), self.h()] - } - fn xywh (&self) -> [N;4] { - [self.x(), self.y(), self.w(), self.h()] - } - fn clip_h (&self, h: N) -> [N;4] { - [self.x(), self.y(), self.w(), self.h().min(h)] - } - fn clip_w (&self, w: N) -> [N;4] { - [self.x(), self.y(), self.w().min(w), self.h()] - } - fn clip (&self, wh: impl Size) -> [N;4] { - [self.x(), self.y(), wh.w(), wh.h()] - } - fn set_w (&self, w: N) -> [N;4] { - [self.x(), self.y(), w, self.h()] - } - fn set_h (&self, h: N) -> [N;4] { - [self.x(), self.y(), self.w(), h] - } - fn x2 (&self) -> N { - self.x().plus(self.w()) - } - fn y2 (&self) -> N { - self.y().plus(self.h()) - } - fn lrtb (&self) -> [N;4] { - [self.x(), self.x2(), self.y(), self.y2()] - } - fn center (&self) -> [N;2] { - [self.x().plus(self.w()/2.into()), self.y().plus(self.h()/2.into())] - } - fn center_x (&self, n: N) -> [N;4] { - let [x, y, w, h] = self.xywh(); - [(x.plus(w / 2.into())).minus(n / 2.into()), y.plus(h / 2.into()), n, 1.into()] - } - fn center_y (&self, n: N) -> [N;4] { - let [x, y, w, h] = self.xywh(); - [x.plus(w / 2.into()), (y.plus(h / 2.into())).minus(n / 2.into()), 1.into(), n] - } - fn center_xy (&self, [n, m]: [N;2]) -> [N;4] { - let [x, y, w, h] = self.xywh(); - [(x.plus(w / 2.into())).minus(n / 2.into()), (y.plus(h / 2.into())).minus(m / 2.into()), n, m] - } - fn centered (&self) -> [N;2] { - [self.x().minus(self.w()/2.into()), self.y().minus(self.h()/2.into())] - } - fn iter_x (&self) -> impl Iterator where N: std::iter::Step { - self.x()..(self.x()+self.w()) - } - fn iter_y (&self) -> impl Iterator where N: std::iter::Step { - self.y()..(self.y()+self.h()) - } -} - -impl Area for (N, N, N, N) { - fn x (&self) -> N { self.0 } - fn y (&self) -> N { self.1 } - fn w (&self) -> N { self.2 } - fn h (&self) -> N { self.3 } -} - -impl Area for [N;4] { - fn x (&self) -> N { self[0] } - fn y (&self) -> N { self[1] } - fn w (&self) -> N { self[2] } - fn h (&self) -> N { self[3] } -} - -pub trait Size: From<[N;2]> + Debug + Copy { - fn x (&self) -> N; - fn y (&self) -> N; - fn w (&self) -> N { self.x() } - fn h (&self) -> N { self.y() } - fn wh (&self) -> [N;2] { [self.x(), self.y()] } - fn clip_w (&self, w: N) -> [N;2] { [self.w().min(w), self.h()] } - fn clip_h (&self, h: N) -> [N;2] { [self.w(), self.h().min(h)] } - fn expect_min (&self, w: N, h: N) -> Usually<&Self> { - if self.w() < w || self.h() < h { - Err(format!("min {w}x{h}").into()) - } else { - Ok(self) - } - } - fn zero () -> [N;2] { - [N::zero(), N::zero()] - } - fn to_area_pos (&self) -> [N;4] { - let [x, y] = self.wh(); - [x, y, 0.into(), 0.into()] - } - fn to_area_size (&self) -> [N;4] { - let [w, h] = self.wh(); - [0.into(), 0.into(), w, h] - } -} - -impl Size for (N, N) { - fn x (&self) -> N { self.0 } - fn y (&self) -> N { self.1 } -} - -impl Size for [N;2] { - fn x (&self) -> N { self[0] } - fn y (&self) -> N { self[1] } -} - -pub trait HasSize { - fn size (&self) -> &Measure; - fn width (&self) -> usize { - self.size().w() - } - fn height (&self) -> usize { - self.size().h() - } -} - -impl>> HasSize for T { - fn size (&self) -> &Measure { - self.get() - } -} - -/// A widget that tracks its render width and height -#[derive(Default)] -pub struct Measure { - _engine: PhantomData, - pub x: Arc, - pub y: Arc, -} - -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) - } -} - -// TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small -impl Content for Measure { - fn render (&self, to: &mut E) { - self.x.store(to.area().w().into(), Relaxed); - self.y.store(to.area().h().into(), Relaxed); - } -} - -impl Clone for Measure { - fn clone (&self) -> Self { - Self { - _engine: Default::default(), - x: self.x.clone(), - y: self.y.clone(), - } - } -} - -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) - .field("height", &self.y) - .finish() - } -} - -impl Measure { - pub fn new () -> Self { - Self { - _engine: PhantomData::default(), - x: Arc::new(0.into()), - y: Arc::new(0.into()), - } - } - pub fn set_w (&self, w: impl Into) -> &Self { - self.x.store(w.into(), Relaxed); - self - } - pub fn set_h (&self, h: impl Into) -> &Self { - self.y.store(h.into(), Relaxed); - self - } - pub fn set_wh (&self, w: impl Into, h: impl Into) -> &Self { - self.set_w(w); - self.set_h(h); - self - } - pub fn w (&self) -> usize { - self.x.load(Relaxed) - } - pub fn h (&self) -> usize { - self.y.load(Relaxed) - } - pub fn wh (&self) -> [usize;2] { - [self.w(), self.h()] - } - pub fn format (&self) -> Arc { - format!("{}x{}", self.w(), self.h()).into() - } - pub fn of > (&self, item: T) -> Bsp, T> { - Bsp::b(Fill::xy(self), item) - } -} diff --git a/output/src/space_measure.rs b/output/src/space_measure.rs index 8279c9d..c2ca936 100644 --- a/output/src/space_measure.rs +++ b/output/src/space_measure.rs @@ -16,7 +16,7 @@ impl PartialEq for Measure { } // TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small -impl Content for Measure { +impl Render for Measure { fn render (&self, to: &mut E) { self.x.store(to.area().w().into(), Relaxed); self.y.store(to.area().h().into(), Relaxed); diff --git a/tui/src/tui_content.rs b/tui/src/tui_content.rs index 9fc0c13..1eef3cc 100644 --- a/tui/src/tui_content.rs +++ b/tui/src/tui_content.rs @@ -5,7 +5,7 @@ macro_rules! impl_content_layout_render { layout = $layout:expr; render = $render:expr) => { - impl Content<$Output> for $Struct { + impl Render<$Output> for $Struct { fn layout (&$self, $to: [u16;4]) -> [u16;4] { $layout } fn render (&$self, $to: &mut $Output) { $render } } @@ -25,19 +25,19 @@ mod tui_string; pub use self::tui_string::*; mod tui_style; pub use self::tui_style::*; mod tui_tryptich; pub use self::tui_tryptich::*; -impl> Content for std::sync::Arc { +impl> Render for std::sync::Arc { fn layout (&self, to: [u16;4]) -> [u16;4] { - Content::::layout(&**self, to) + Render::::layout(&**self, to) } fn render (&self, to: &mut TuiOut) { - Content::::render(&**self, to) + Render::::render(&**self, to) } } -impl> Content for Result> { - fn content (&self) -> impl Render { - Bsp::a(self.as_ref().ok(), self.as_ref().err() - .map(|e|Tui::fg_bg(Color::Rgb(255,255,255), Color::Rgb(32,32,32), e.to_string()))) +impl> Content for Result> { + fn content (&self) -> Option + '_> { + Some(Bsp::a(self.as_ref().ok(), self.as_ref().err() + .map(|e|Tui::fg_bg(Color::Rgb(255,255,255), Color::Rgb(32,32,32), e.to_string())))) } } diff --git a/tui/src/tui_content/tui_border.rs b/tui/src/tui_content/tui_border.rs index 124b7cb..62f2252 100644 --- a/tui/src/tui_content/tui_border.rs +++ b/tui/src/tui_content/tui_border.rs @@ -1,40 +1,49 @@ use crate::*; -pub struct Bordered>(pub bool, pub S, pub W); -content!(TuiOut: |self: Bordered>|Fill::xy( - lay!(When::new(self.0, Border(self.0, self.1)), Padding::xy(1, 1, &self.2)) -)); +pub struct Bordered(pub bool, pub S, pub W); +impl> Content for Bordered { + fn content (&self) -> Option + '_> { + Some(Fill::xy( + lay!(When::new(self.0, Border(self.0, self.1)), Padding::xy(1, 1, &self.2)) + )) + } +} pub struct Border(pub bool, pub S); -render!(TuiOut: |self: Border, to| { - if self.0 { - let area = to.area(); - if area.w() > 0 && area.y() > 0 { - to.blit(&self.1.nw(), area.x(), area.y(), self.1.style()); - to.blit(&self.1.ne(), area.x() + area.w() - 1, area.y(), self.1.style()); - to.blit(&self.1.sw(), area.x(), area.y() + area.h() - 1, self.1.style()); - to.blit(&self.1.se(), area.x() + area.w() - 1, area.y() + area.h() - 1, self.1.style()); - for x in area.x()+1..area.x()+area.w()-1 { - to.blit(&self.1.n(), x, area.y(), self.1.style()); - to.blit(&self.1.s(), x, area.y() + area.h() - 1, self.1.style()); - } - for y in area.y()+1..area.y()+area.h()-1 { - to.blit(&self.1.w(), area.x(), y, self.1.style()); - to.blit(&self.1.e(), area.x() + area.w() - 1, y, self.1.style()); +impl Render for Border { + fn layout (&self, area: [u16;4]) -> [u16;4] { + self.1.layout(area) + } + fn render (&self, to: &mut TuiOut) { + if self.0 { + let area = to.area(); + if area.w() > 0 && area.y() > 0 { + to.blit(&self.1.nw(), area.x(), area.y(), self.1.style()); + to.blit(&self.1.ne(), area.x() + area.w() - 1, area.y(), self.1.style()); + to.blit(&self.1.sw(), area.x(), area.y() + area.h() - 1, self.1.style()); + to.blit(&self.1.se(), area.x() + area.w() - 1, area.y() + area.h() - 1, self.1.style()); + for x in area.x()+1..area.x()+area.w()-1 { + to.blit(&self.1.n(), x, area.y(), self.1.style()); + to.blit(&self.1.s(), x, area.y() + area.h() - 1, self.1.style()); + } + for y in area.y()+1..area.y()+area.h()-1 { + to.blit(&self.1.w(), area.x(), y, self.1.style()); + to.blit(&self.1.e(), area.x() + area.w() - 1, y, self.1.style()); + } } } } -}); +} -pub trait BorderStyle: Send + Sync + Copy { +pub trait BorderStyle: Render + Copy { fn enabled (&self) -> bool; - fn enclose > (self, w: W) -> impl Content { + fn enclose > (self, w: W) -> impl Render { Bsp::b(Fill::xy(Border(self.enabled(), self)), w) } - fn enclose2 > (self, w: W) -> impl Content { + fn enclose2 > (self, w: W) -> impl Render { Bsp::b(Margin::xy(1, 1, Fill::xy(Border(self.enabled(), self))), w) } - fn enclose_bg > (self, w: W) -> impl Content { + fn enclose_bg > (self, w: W) -> impl Render { Tui::bg(self.style().unwrap().bg.unwrap_or(Color::Reset), Bsp::b(Fill::xy(Border(self.enabled(), self)), w)) } @@ -138,7 +147,7 @@ macro_rules! border { fn enabled (&self) -> bool { self.0 } } #[derive(Copy, Clone)] pub struct $T(pub bool, pub Style); - impl Content for $T { + impl Render for $T { fn render (&self, to: &mut TuiOut) { if self.enabled() { let _ = self.draw(to); } } diff --git a/tui/src/tui_content/tui_error.rs b/tui/src/tui_content/tui_error.rs index e48f203..9ec7773 100644 --- a/tui/src/tui_content/tui_error.rs +++ b/tui/src/tui_content/tui_error.rs @@ -2,23 +2,26 @@ use crate::*; use ratatui::style::Stylize; // Thunks can be natural error boundaries! -pub struct ErrorBoundary>(std::marker::PhantomData, Perhaps); +pub struct ErrorBoundary>( + std::marker::PhantomData, Perhaps +); -impl> ErrorBoundary { +impl> ErrorBoundary { pub fn new (content: Perhaps) -> Self { Self(Default::default(), content) } } -impl> Content for ErrorBoundary { - fn content (&self) -> impl Render + '_ { - ThunkRender::new(|to|match self.1.as_ref() { +impl> Render for ErrorBoundary { + fn render (&self, to: &mut TuiOut) { + match self.1.as_ref() { Ok(Some(content)) => content.render(to), Ok(None) => to.blit(&"empty?", 0, 0, Some(Style::default().yellow())), - Err(e) => Content::render(&Tui::fg_bg( + Err(e) => Tui::fg_bg( Color::Rgb(255,224,244), Color::Rgb(96,24,24), Bsp::s( Bsp::e(Tui::bold(true, "oops. "), "rendering failed."), - Bsp::e("\"why?\" ", Tui::bold(true, &format!("{e}"))))), to) - }) + Bsp::e("\"why?\" ", Tui::bold(true, &format!("{e}")))) + ).render(to) + } } } diff --git a/tui/src/tui_content/tui_field.rs b/tui/src/tui_content/tui_field.rs index 3e47e9f..e0161fd 100644 --- a/tui/src/tui_content/tui_field.rs +++ b/tui/src/tui_content/tui_field.rs @@ -1,30 +1,30 @@ use crate::*; pub struct FieldH(pub ItemTheme, pub T, pub U); -impl, U: Content> Content for FieldH { - fn content (&self) -> impl Render { +impl, U: Render> Content for FieldH { + fn content (&self) -> Option + '_> { let Self(ItemTheme { darkest, dark, lightest, .. }, title, value) = self; - row!( + Some(row!( Tui::fg_bg(dark.rgb, darkest.rgb, "▐"), Tui::fg_bg(lightest.rgb, dark.rgb, title), Tui::fg_bg(dark.rgb, darkest.rgb, "▌"), Tui::fg_bg(lightest.rgb, darkest.rgb, Tui::bold(true, value)), - ) + )) } } pub struct FieldV(pub ItemTheme, pub T, pub U); -impl, U: Content> Content for FieldV { - fn content (&self) -> impl Render { +impl, U: Render> Content for FieldV { + fn content (&self) -> Option + '_> { let Self(ItemTheme { darkest, dark, lightest, .. }, title, value) = self; - Bsp::n( + Some(Bsp::n( Align::w(Tui::bg(darkest.rgb, Tui::fg(lightest.rgb, Tui::bold(true, value)))), Fill::x(Align::w(row!( Tui::bg(darkest.rgb, Tui::fg(dark.rgb, "▐")), Tui::bg(dark.rgb, Tui::fg(lightest.rgb, title)), Tui::bg(darkest.rgb, Tui::fg(dark.rgb, "▌")), ))) - ) + )) } } @@ -40,9 +40,9 @@ pub struct Field { pub value_bg: Option, pub value_align: Option, } -impl, U: Content> Content for Field { - fn content (&self) -> impl Render { - "TODO" +impl, U: Render> Content for Field { + fn content (&self) -> Option + '_> { + Some("TODO") } } diff --git a/tui/src/tui_content/tui_number.rs b/tui/src/tui_content/tui_number.rs index 46c4ef1..9e8eea1 100644 --- a/tui/src/tui_content/tui_number.rs +++ b/tui/src/tui_content/tui_number.rs @@ -1,5 +1,13 @@ use crate::*; -render!(TuiOut: |self: u64, _to|todo!()); +impl Render for u64 { + fn render (&self, _to: &mut TuiOut) { + todo!() + } +} -render!(TuiOut: |self: f64, _to|todo!()); +impl Render for f64 { + fn render (&self, _to: &mut TuiOut) { + todo!() + } +} diff --git a/tui/src/tui_content/tui_phat.rs b/tui/src/tui_content/tui_phat.rs index 01a852f..1117636 100644 --- a/tui/src/tui_content/tui_phat.rs +++ b/tui/src/tui_content/tui_phat.rs @@ -12,24 +12,24 @@ impl Phat { pub const LO: &'static str = "▄"; pub const HI: &'static str = "▀"; /// A phat line - pub fn lo (fg: Color, bg: Color) -> impl Content { + pub fn lo (fg: Color, bg: Color) -> impl Render { Fixed::y(1, Tui::fg_bg(fg, bg, RepeatH(Self::LO))) } /// A phat line - pub fn hi (fg: Color, bg: Color) -> impl Content { + pub fn hi (fg: Color, bg: Color) -> impl Render { Fixed::y(1, Tui::fg_bg(fg, bg, RepeatH(Self::HI))) } } -impl> Content for Phat { - fn content (&self) -> impl Render { +impl> Content for Phat { + fn content (&self) -> Option + '_> { let [fg, bg, hi, lo] = self.colors; let top = Fixed::y(1, Self::lo(bg, hi)); let low = Fixed::y(1, Self::hi(bg, lo)); let content = Tui::fg_bg(fg, bg, &self.content); - Min::xy( + Some(Min::xy( self.width, self.height, Bsp::s(top, Bsp::n(low, Fill::xy(content))) - ) + )) } } diff --git a/tui/src/tui_content/tui_repeat.rs b/tui/src/tui_content/tui_repeat.rs index ecd2d40..03e2209 100644 --- a/tui/src/tui_content/tui_repeat.rs +++ b/tui/src/tui_content/tui_repeat.rs @@ -2,8 +2,10 @@ use crate::*; use ratatui::prelude::Position; pub struct Repeat<'a>(pub &'a str); -impl Content for Repeat<'_> { - fn layout (&self, to: [u16;4]) -> [u16;4] { to } +impl Render for Repeat<'_> { + fn layout (&self, to: [u16;4]) -> [u16;4] { + to + } fn render (&self, to: &mut TuiOut) { let [x, y, w, h] = to.area().xywh(); let a = self.0.len(); @@ -19,8 +21,10 @@ impl Content for Repeat<'_> { } pub struct RepeatV<'a>(pub &'a str); -impl Content for RepeatV<'_> { - fn layout (&self, to: [u16;4]) -> [u16;4] { to } +impl Render for RepeatV<'_> { + fn layout (&self, to: [u16;4]) -> [u16;4] { + to + } fn render (&self, to: &mut TuiOut) { let [x, y, _w, h] = to.area().xywh(); for y in y..y+h { @@ -32,8 +36,10 @@ impl Content for RepeatV<'_> { } pub struct RepeatH<'a>(pub &'a str); -impl Content for RepeatH<'_> { - fn layout (&self, to: [u16;4]) -> [u16;4] { to } +impl Render for RepeatH<'_> { + fn layout (&self, to: [u16;4]) -> [u16;4] { + to + } fn render (&self, to: &mut TuiOut) { let [x, y, w, _h] = to.area().xywh(); for x in x..x+w { diff --git a/tui/src/tui_content/tui_scroll.rs b/tui/src/tui_content/tui_scroll.rs index fe8bf3a..5f78c1d 100644 --- a/tui/src/tui_content/tui_scroll.rs +++ b/tui/src/tui_content/tui_scroll.rs @@ -23,7 +23,7 @@ impl ScrollbarH { const ICON_INC: &[char] = &[' ', '🞂', ' ']; } -impl Content for ScrollbarV { +impl Render for ScrollbarV { fn render (&self, to: &mut TuiOut) { let [x, y1, _w, h] = to.area().xywh(); let y2 = y1 + h; @@ -51,7 +51,7 @@ impl Content for ScrollbarV { } } -impl Content for ScrollbarH { +impl Render for ScrollbarH { fn render (&self, to: &mut TuiOut) { let [x1, y, w, _h] = to.area().xywh(); let x2 = x1 + w; diff --git a/tui/src/tui_content/tui_string.rs b/tui/src/tui_content/tui_string.rs index 57ae7f4..0163dc2 100644 --- a/tui/src/tui_content/tui_string.rs +++ b/tui/src/tui_content/tui_string.rs @@ -4,24 +4,24 @@ use unicode_width::{UnicodeWidthStr, UnicodeWidthChar}; impl_content_layout_render!(TuiOut: |self: &str, to| layout = to.center_xy([width_chars_max(to.w(), self), 1]); - render = {let [x, y, w, ..] = Content::layout(self, to.area()); + render = {let [x, y, w, ..] = Render::layout(self, to.area()); to.text(self, x, y, w)}); impl_content_layout_render!(TuiOut: |self: String, to| - layout = Content::::layout(&self.as_str(), to); - render = Content::::render(&self.as_str(), to)); + layout = Render::::layout(&self.as_str(), to); + render = Render::::render(&self.as_str(), to)); impl_content_layout_render!(TuiOut: |self: Arc, to| - layout = Content::::layout(&self.as_ref(), to); - render = Content::::render(&self.as_ref(), to)); + layout = Render::::layout(&self.as_ref(), to); + render = Render::::render(&self.as_ref(), to)); impl_content_layout_render!(TuiOut: |self: std::sync::RwLock, to| - layout = Content::::layout(&self.read().unwrap(), to); - render = Content::::render(&self.read().unwrap(), to)); + layout = Render::::layout(&self.read().unwrap(), to); + render = Render::::render(&self.read().unwrap(), to)); impl_content_layout_render!(TuiOut: |self: std::sync::RwLockReadGuard<'_, String>, to| - layout = Content::::layout(&**self, to); - render = Content::::render(&**self, to)); + layout = Render::::layout(&**self, to); + render = Render::::render(&**self, to)); fn width_chars_max (max: u16, text: impl AsRef) -> u16 { let mut width: u16 = 0; @@ -61,12 +61,12 @@ impl<'a, T: AsRef> TrimString { TrimStringRef(self.0, &self.1) } } -impl<'a, T: AsRef> Content for TrimString { +impl<'a, T: AsRef> Render for TrimString { fn layout (&self, to: [u16; 4]) -> [u16;4] { - Content::layout(&self.as_ref(), to) + Render::layout(&self.as_ref(), to) } fn render (&self, to: &mut TuiOut) { - Content::render(&self.as_ref(), to) + Render::render(&self.as_ref(), to) } } @@ -75,7 +75,7 @@ impl<'a, T: AsRef> Content for TrimString { /// Width is computed using [unicode_width]. pub struct TrimStringRef<'a, T: AsRef>(pub u16, pub &'a T); -impl> Content for TrimStringRef<'_, T> { +impl> Render for TrimStringRef<'_, T> { fn layout (&self, to: [u16; 4]) -> [u16;4] { [to.x(), to.y(), to.w().min(self.0).min(self.1.as_ref().width() as u16), to.h()] } diff --git a/tui/src/tui_content/tui_style.rs b/tui/src/tui_content/tui_style.rs index 10d79d4..3673f53 100644 --- a/tui/src/tui_content/tui_style.rs +++ b/tui/src/tui_content/tui_style.rs @@ -1,60 +1,70 @@ use crate::*; pub trait TuiStyle { - fn fg > (color: Color, w: R) -> Foreground { + fn fg > (color: Color, w: R) -> Foreground { Foreground(color, w) } - fn bg > (color: Color, w: R) -> Background { + fn bg > (color: Color, w: R) -> Background { Background(color, w) } - fn fg_bg > (fg: Color, bg: Color, w: R) -> Background> { + fn fg_bg > (fg: Color, bg: Color, w: R) -> Background> { Background(bg, Foreground(fg, w)) } - fn modify > (enable: bool, modifier: Modifier, w: R) -> Modify { + fn modify > (enable: bool, modifier: Modifier, w: R) -> Modify { Modify(enable, modifier, w) } - fn bold > (enable: bool, w: R) -> Modify { + fn bold > (enable: bool, w: R) -> Modify { Self::modify(enable, Modifier::BOLD, w) } - fn border , S: BorderStyle> (enable: bool, style: S, w: R) -> Bordered { + fn border , S: BorderStyle> (enable: bool, style: S, w: R) -> Bordered { Bordered(enable, style, w) } } impl TuiStyle for Tui {} -pub struct Foreground>(pub Color, pub R); -impl> Content for Foreground { - fn content (&self) -> impl Render { &self.1 } +pub struct Foreground(pub Color, pub R); +impl> Render for Foreground { + fn layout (&self, to: [u16;4]) -> [u16;4] { + self.1.layout(to) + } fn render (&self, to: &mut TuiOut) { - to.fill_fg(to.area(), self.0); - self.1.render(to) + let area = self.layout(to.area()); + to.fill_fg(area, self.0); + to.place(area, &self.1); } } -pub struct Background>(pub Color, pub R); -impl> Content for Background { - fn content (&self) -> impl Render { &self.1 } +pub struct Background(pub Color, pub R); +impl> Render for Background { + fn layout (&self, to: [u16;4]) -> [u16;4] { + self.1.layout(to) + } fn render (&self, to: &mut TuiOut) { - to.fill_bg(to.area(), self.0); - self.1.render(to) + let area = self.layout(to.area()); + to.fill_bg(area, self.0); + to.place(area, &self.1); } } -pub struct Modify>(pub bool, pub Modifier, pub R); -impl> Content for Modify { - fn content (&self) -> impl Render { &self.2 } +pub struct Modify>(pub bool, pub Modifier, pub R); +impl> Render for Modify { + fn layout (&self, to: [u16;4]) -> [u16;4] { + self.2.layout(to) + } fn render (&self, to: &mut TuiOut) { to.fill_mod(to.area(), self.0, self.1); self.2.render(to) } } -pub struct Styled>(pub Option