From d37bd3e0c5bf4541c659b14413a6a041e04f3f57 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Tue, 31 Dec 2024 00:38:47 +0100 Subject: [PATCH] the wild Layout trait appears --- engine/src/lib.rs | 12 +- engine/src/output.rs | 611 +++++++++++++++++++++------------------- engine/src/tui.rs | 28 +- layout/Cargo.lock | 254 ++++++++++------- layout/src/logic.rs | 42 +-- layout/src/space.rs | 15 +- layout/src/transform.rs | 209 +++++--------- 7 files changed, 576 insertions(+), 595 deletions(-) diff --git a/engine/src/lib.rs b/engine/src/lib.rs index 7dbd7d44..539a1e6f 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -46,13 +46,13 @@ pub type Perhaps = Result, Box>; fn area_mut (&mut self) -> &mut [u16;4] { &mut self.0 } - fn render_in (&mut self, _: [u16;4], _: &dyn Render) -> Usually<()> { + fn render_in (&mut self, _: [u16;4], _: &impl Render) -> Usually<()> { Ok(()) } } impl Render for String { - fn min_size (&self, _: [u16;2]) -> Perhaps<[u16;2]> { - Ok(Some([self.len() as u16, 1])) + fn render (&self, to: &mut TestOutput) { + to.area_mut().set_w(self.len() as u16); } } Ok(()) @@ -62,9 +62,9 @@ pub type Perhaps = Result, Box>; use crate::tui::*; use std::sync::{Arc, RwLock}; struct TestComponent(String); - impl Content for TestComponent { - fn content (&self) -> Option> { - Some(&self.0) + impl Layout for TestComponent { + fn layout (&self) -> Option> { + Some(self.0.as_str()) } } impl Handle for TestComponent { diff --git a/engine/src/output.rs b/engine/src/output.rs index ff2d4a16..5ba1cd5f 100644 --- a/engine/src/output.rs +++ b/engine/src/output.rs @@ -7,158 +7,212 @@ pub trait Output { fn area (&self) -> E::Area; /// Mutable pointer to area fn area_mut (&mut self) -> &mut E::Area; - /// Render widget in area + ///// Render widget in area fn render_in (&mut self, area: E::Area, widget: &impl Render) -> Usually<()>; + + #[inline] fn x (&self) -> E::Unit { self.area().x() } + #[inline] fn y (&self) -> E::Unit { self.area().y() } + #[inline] fn w (&self) -> E::Unit { self.area().w() } + #[inline] fn h (&self) -> E::Unit { self.area().h() } } -/// Something that can be represented by a renderable component. -pub trait Content: Send + Sync { - fn content (&self) -> Option>; -} -impl> Content for &C { - fn content (&self) -> Option> { - (*self).content() +pub trait Layout: Send + Sync { + fn layout (&self) -> Option> { + None::<()> } } -/// Something that writes to an [Output]. +impl Layout for () {} + +impl> Layout for &L {} + pub trait Render: Send + Sync { - /// Minimum size to use - fn min_size (&self, _: E::Size) -> Perhaps { - Ok(None) - } - /// Draw to output render target - fn render (&self, _: &mut E::Output) -> Usually<()> { - Ok(()) - } + fn render (&self, _: &mut E::Output); } -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 &R { - 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 Option { - fn min_size (&self, to: E::Size) -> Perhaps { - if let Some(content) = self { - content.min_size(to) - } else { - Ok(None) + +impl> Render for L { + fn render (&self, to: &mut E::Output) { + if let Some(content) = self.layout() { + content.render(to) } } - fn render (&self, to: &mut E::Output) -> Usually<()> { - if let Some(content) = self { - content.render(to)?; - } - Ok(()) - } } -/// Define custom content for a struct. -/// -/// This code wires the `Content` and `Render` traits together, -/// since the only way to have the cake and eat it too is by -/// implementing both traits for a given renderable. -#[macro_export] macro_rules! render { +//impl> Layout for &L {} - // Implement from [Content] for all [Engine]s - (|$self:ident:$Struct:ident$(<$($L:lifetime),*E$(,$T:ident$(:$U:path)?)*$(,)?>)?|$cb:expr) => { - impl Content for $Struct $(<$($L,)* E, $($T),*>)? { - fn content (&$self) -> Option> { Some($cb) } - } - impl Render for $Struct $(<$($L,)* E, $($T),*>)? { - fn min_size (&self, to: E::Size) -> Perhaps { - self.content().unwrap().min_size(to) - } - fn render (&self, to: &mut E::Output) -> Usually<()> { - self.content().unwrap().render(to) - } - } - }; +///// Something that can be represented by a renderable component. +//pub trait Content: Send + Sync { + //fn content (&self) -> Option>; +//} +//impl> Content for &C { + //fn content (&self) -> Option> { + //(*self).content() + //} +//} - // Implement from [min_size] and [render] callbacks for all engines - ($Struct:ident$(<$($L:lifetime),*E$(,$T:ident$(:$U:path)?)*$(,)?>)? - |$self1:ident, $to1:ident|$min_size:expr, - |$self2:ident, $to2:ident|$render:expr) => - { - impl Content for $Struct $(<$($L,)* E, $($T),*>)? { - fn content (&self) -> Option> { Some(self) } - } - impl Render for $Struct $(<$($L,)* E, $($T),*>)? { - fn min_size (&$self1, $to1: E::Size) -> Perhaps { $min_size } - fn render (&$self2, $to2: &mut E::Output) -> Usually<()> { $render } - } - }; +///// Something that writes to an [Output]. +//pub trait Render: Send + Sync { + ///// Minimum size to use + //fn min_size (&self, _: E::Size) -> Perhaps { + //Ok(None) + //} + ///// Draw to output render target + //fn render (&self, _: &mut E::Output) -> Usually<()> { + //Ok(()) + //} +//} +//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 &R { + //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 Option { + //fn min_size (&self, to: E::Size) -> Perhaps { + //if let Some(content) = self { + //content.min_size(to) + //} else { + //Ok(None) + //} + //} + //fn render (&self, to: &mut E::Output) -> Usually<()> { + //if let Some(content) = self { + //content.render(to)?; + //} + //Ok(()) + //} +//} - // Implement from [Content] for a particular [Engine] - (<$E:ty>|$self:ident:$Struct:ident$(< - $($($L:lifetime),+)? - $($($T:ident$(:$U:path)?),+)? - >)?|$cb:expr) => { - impl $(< - $($($L),+)? - $($($T$(:$U)?),+)? - >)? Content<$E> for $Struct $(< - $($($L),+)? - $($($T),+)? - >)? { - fn content (&$self) -> Option> { - Some($cb) - } - } - impl $(< - $($($L),+)? - $($($T$(:$U)?),+)? - >)? Render<$E> for $Struct $(< - $($($L),+)? - $($($T),+)? - >)? { - fn min_size (&self, to: <$E as Engine>::Size) -> Perhaps<<$E as Engine>::Size> { - self.content().map(|content|content.min_size(to)).unwrap_or(Ok(None)) - } - fn render (&self, to: &mut <$E as Engine>::Output) -> Usually<()> { - self.content().map(|content|content.render(to)).unwrap_or(Ok(())) - } - } - }; +///// Define custom content for a struct. +///// +///// This code wires the `Content` and `Render` traits together, +///// since the only way to have the cake and eat it too is by +///// implementing both traits for a given renderable. +//#[macro_export] macro_rules! render { - // Implement from [min_size] and [render] callbacks for a particular [Engine] - (<$E:ty>($self:ident:$Struct:ident$(<$($L:lifetime),*$(,$T:ident$(:$U:path)?)*$(,)?>)?) - |$to1:ident|$min_size:expr, |$to2:ident|$render:expr) => - { - impl $(< - $($L),* $($($T$(:$U)?),+)? - >)? Content<$E> for $Struct $(< - $($L),* $($($T),+)? - >)? { - fn content (&self) -> Option> { Some(self) } - } - impl $(< - $($L),* $($($T$(:$U)?),+)? - >)? Render<$E> for $Struct $(< - $($L),* $($($T),+)? - >)? { - fn min_size (&$self, $to1: <$E as Engine>::Size) -> Perhaps<<$E as Engine>::Size> { - $min_size - } - fn render (&$self, $to2: &mut <$E as Engine>::Output) -> Usually<()> { - $render - } - } - } + //// Implement from [Content] for all [Engine]s + //(|$self:ident:$Struct:ident$(<$($L:lifetime),*E$(,$T:ident$(:$U:path)?)*$(,)?>)?|$cb:expr) => { + //impl Content for $Struct $(<$($L,)* E, $($T),*>)? { + //fn content (&$self) -> Option> { Some($cb) } + //} + //impl Render for $Struct $(<$($L,)* E, $($T),*>)? { + //fn min_size (&self, to: E::Size) -> Perhaps { + //self.content().unwrap().min_size(to) + //} + //fn render (&self, to: &mut E::Output) -> Usually<()> { + //self.content().unwrap().render(to) + //} + //} + //}; -} + //// Implement from [min_size] and [render] callbacks for all engines + //($Struct:ident$(<$($L:lifetime),*E$(,$T:ident$(:$U:path)?)*$(,)?>)? + //|$self1:ident, $to1:ident|$min_size:expr, + //|$self2:ident, $to2:ident|$render:expr) => + //{ + //impl Content for $Struct $(<$($L,)* E, $($T),*>)? { + //fn content (&self) -> Option> { Some(self) } + //} + //impl Render for $Struct $(<$($L,)* E, $($T),*>)? { + //fn min_size (&$self1, $to1: E::Size) -> Perhaps { $min_size } + //fn render (&$self2, $to2: &mut E::Output) -> Usually<()> { $render } + //} + //}; -//impl> Content for &mut C {} -//impl> Content for Option {} -//impl> Content for Arc {} -//impl> Content for Mutex {} -//impl> Content for RwLock {} + //// Implement from [Content] for a particular [Engine] + //(<$E:ty>|$self:ident:$Struct:ident$(< + //$($($L:lifetime),+)? + //$($($T:ident$(:$U:path)?),+)? + //>)?|$cb:expr) => { + //impl $(< + //$($($L),+)? + //$($($T$(:$U)?),+)? + //>)? Content<$E> for $Struct $(< + //$($($L),+)? + //$($($T),+)? + //>)? { + //fn content (&$self) -> Option> { + //Some($cb) + //} + //} + //impl $(< + //$($($L),+)? + //$($($T$(:$U)?),+)? + //>)? Render<$E> for $Struct $(< + //$($($L),+)? + //$($($T),+)? + //>)? { + //fn min_size (&self, to: <$E as Engine>::Size) -> Perhaps<<$E as Engine>::Size> { + //self.content().map(|content|content.min_size(to)).unwrap_or(Ok(None)) + //} + //fn render (&self, to: &mut <$E as Engine>::Output) -> Usually<()> { + //self.content().map(|content|content.render(to)).unwrap_or(Ok(())) + //} + //} + //}; -//impl> Render for C {} + //// Implement from [min_size] and [render] callbacks for a particular [Engine] + //(<$E:ty>($self:ident:$Struct:ident$(<$($L:lifetime),*$(,$T:ident$(:$U:path)?)*$(,)?>)?) + //|$to1:ident|$min_size:expr, |$to2:ident|$render:expr) => + //{ + //impl $(< + //$($L),* $($($T$(:$U)?),+)? + //>)? Content<$E> for $Struct $(< + //$($L),* $($($T),+)? + //>)? { + //fn content (&self) -> Option> { Some(self) } + //} + //impl $(< + //$($L),* $($($T$(:$U)?),+)? + //>)? Render<$E> for $Struct $(< + //$($L),* $($($T),+)? + //>)? { + //fn min_size (&$self, $to1: <$E as Engine>::Size) -> Perhaps<<$E as Engine>::Size> { + //$min_size + //} + //fn render (&$self, $to2: &mut <$E as Engine>::Output) -> Usually<()> { + //$render + //} + //} + //} +//} + +////impl> Content for &mut C {} +////impl> Content for Option {} +////impl> Content for Arc {} +////impl> Content for Mutex {} +////impl> Content for RwLock {} + +////impl> Render for C {} + +////impl> Render for C { + /////// Minimum size to use + ////fn min_size (&self, to: E::Size) -> Perhaps { + ////self.content().map(|content|content.min_size(to)) + ////.unwrap_or(Ok(None)) + ////} + /////// Draw to output render target + ////fn render (&self, to: &mut E::Output) -> Usually<()> { + ////self.content().map(|content|content.render(to)) + ////.unwrap_or(Ok(())) + ////} +////} + +//[>*** + + +//impl + Send + Sync> Content for R {} + +////impl> Content for R { + ////fn content (&self) -> Option> { + ////Some(self) + ////} +////} + +///// All implementors of [Content] can be [Render]ed. //impl> Render for C { ///// Minimum size to use //fn min_size (&self, to: E::Size) -> Perhaps { @@ -172,65 +226,20 @@ impl> Render for Option { //} //} -/**** +////impl> Content for &C { + ////fn content (&self) -> Option> { + ////Some(self) + ////} +////} + +////impl> Content for Option { + ////fn content (&self) -> Option> { + ////Some(self) + ////} +////} -impl + Send + Sync> Content for R {} - -//impl> Content for R { - //fn content (&self) -> Option> { - //Some(self) - //} -//} - -/// All implementors of [Content] can be [Render]ed. -impl> Render for C { - /// Minimum size to use - fn min_size (&self, to: E::Size) -> Perhaps { - self.content().map(|content|content.min_size(to)) - .unwrap_or(Ok(None)) - } - /// Draw to output render target - fn render (&self, to: &mut E::Output) -> Usually<()> { - self.content().map(|content|content.render(to)) - .unwrap_or(Ok(())) - } -} - -//impl> Content for &C { - //fn content (&self) -> Option> { - //Some(self) - //} -//} - -//impl> Content for Option { - //fn content (&self) -> Option> { - //Some(self) - //} -//} - - -impl> Render for &R { - 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 Option { - fn min_size (&self, to: E::Size) -> Perhaps { - self.map(|content|content.min_size(to)) - .unwrap_or(Ok(None)) - } - fn render (&self, to: &mut E::Output) -> Usually<()> { - self.map(|content|content.render(to)) - .unwrap_or(Ok(())) - } -} - -//impl Render for &dyn Render { +//impl> Render for &R { //fn min_size (&self, to: E::Size) -> Perhaps { //(*self).min_size(to) //} @@ -239,124 +248,144 @@ impl> Render for Option { //} //} -//impl Render for &mut dyn Render { +//impl> Render for Option { //fn min_size (&self, to: E::Size) -> Perhaps { - //(*self).min_size(to) + //self.map(|content|content.min_size(to)) + //.unwrap_or(Ok(None)) //} //fn render (&self, to: &mut E::Output) -> Usually<()> { - //(*self).render(to) + //self.map(|content|content.render(to)) + //.unwrap_or(Ok(())) //} //} -//impl Render for Box + '_> { +////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 render (&self, to: &mut E::Output) -> Usually<()> { + ////(*self).render(to) + ////} +////} + +////impl Render for Box + '_> { + ////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 Arc { + ////fn min_size (&self, to: E::Size) -> Perhaps { + ////self.as_ref().min_size(to) + ////} + ////fn render (&self, to: &mut E::Output) -> Usually<()> { + ////self.as_ref().render(to) + ////} +////} + +////impl> Render for Mutex { + ////fn min_size (&self, to: E::Size) -> Perhaps { + ////self.lock().unwrap().min_size(to) + ////} + ////fn render (&self, to: &mut E::Output) -> Usually<()> { + ////self.lock().unwrap().render(to) + ////} +////} + +////impl> Render for RwLock { + ////fn min_size (&self, to: E::Size) -> Perhaps { + ////self.read().unwrap().min_size(to) + ////} + ////fn render (&self, to: &mut E::Output) -> Usually<()> { + ////self.read().unwrap().render(to) + ////} +////} + +////impl> Render for Option { + ////fn min_size (&self, to: E::Size) -> Perhaps { + ////Ok(self.as_ref().map(|widget|widget.min_size(to)).transpose()?.flatten()) + ////} + ////fn render (&self, to: &mut E::Output) -> Usually<()> { + ////self.as_ref().map(|widget|widget.render(to)).unwrap_or(Ok(())) + ////} +////} + +///// Cast to dynamic pointer +//pub fn widget (w: &T) -> &dyn Render + //where E: Engine, T: Render +//{ + //w as &dyn Render +//} + +///// Ad-hoc widget with custom rendering. +//pub fn render (render: F) -> impl Render + //where E: Engine, F: Fn(&mut E::Output)->Usually<()>+Send+Sync +//{ + //Widget::new(|_|Ok(Some([0.into(),0.into()].into())), render) +//} + +///// A custom [Render] defined by passing layout and render closures in place. +//pub struct Widget< + //E: Engine, + //L: Send + Sync + Fn(E::Size)->Perhaps, + //R: Send + Sync + Fn(&mut E::Output)->Usually<()> +//>(L, R, PhantomData); + +//impl< + //E: Engine, + //L: Send + Sync + Fn(E::Size)->Perhaps, + //R: Send + Sync + Fn(&mut E::Output)->Usually<()> +//> Widget { + //pub fn new (layout: L, render: R) -> Self { + //Self(layout, render, Default::default()) + //} +//} + +//impl< + //E: Engine, + //L: Send + Sync + Fn(E::Size)->Perhaps, + //R: Send + Sync + Fn(&mut E::Output)->Usually<()> +//> Render for Widget { //fn min_size (&self, to: E::Size) -> Perhaps { - //(**self).min_size(to) + //self.0(to) //} //fn render (&self, to: &mut E::Output) -> Usually<()> { - //(**self).render(to) + //self.1(to) //} //} -//impl> Render for Arc { - //fn min_size (&self, to: E::Size) -> Perhaps { - //self.as_ref().min_size(to) +///// Has static methods for conditional rendering, +///// in unary and binary forms. +//pub struct Cond; + +//impl Cond { + ///// Render `item` when `cond` is true. + //pub fn when > (cond: bool, item: A) -> When { + //When(cond, item, Default::default()) //} - //fn render (&self, to: &mut E::Output) -> Usually<()> { - //self.as_ref().render(to) + ///// Render `item` if `cond` is true, otherwise render `other`. + //pub fn either , B: Render> (cond: bool, item: A, other: B) -> Either { + //Either(cond, item, other, Default::default()) //} //} -//impl> Render for Mutex { - //fn min_size (&self, to: E::Size) -> Perhaps { - //self.lock().unwrap().min_size(to) - //} - //fn render (&self, to: &mut E::Output) -> Usually<()> { - //self.lock().unwrap().render(to) - //} -//} +///// Renders `self.1` when `self.0` is true. +//pub struct When>(bool, A, PhantomData); -//impl> Render for RwLock { - //fn min_size (&self, to: E::Size) -> Perhaps { - //self.read().unwrap().min_size(to) - //} - //fn render (&self, to: &mut E::Output) -> Usually<()> { - //self.read().unwrap().render(to) - //} -//} - -//impl> Render for Option { - //fn min_size (&self, to: E::Size) -> Perhaps { - //Ok(self.as_ref().map(|widget|widget.min_size(to)).transpose()?.flatten()) - //} - //fn render (&self, to: &mut E::Output) -> Usually<()> { - //self.as_ref().map(|widget|widget.render(to)).unwrap_or(Ok(())) - //} -//} - -/// Cast to dynamic pointer -pub fn widget (w: &T) -> &dyn Render - where E: Engine, T: Render -{ - w as &dyn Render -} - -/// Ad-hoc widget with custom rendering. -pub fn render (render: F) -> impl Render - where E: Engine, F: Fn(&mut E::Output)->Usually<()>+Send+Sync -{ - Widget::new(|_|Ok(Some([0.into(),0.into()].into())), render) -} - -/// A custom [Render] defined by passing layout and render closures in place. -pub struct Widget< - E: Engine, - L: Send + Sync + Fn(E::Size)->Perhaps, - R: Send + Sync + Fn(&mut E::Output)->Usually<()> ->(L, R, PhantomData); - -impl< - E: Engine, - L: Send + Sync + Fn(E::Size)->Perhaps, - R: Send + Sync + Fn(&mut E::Output)->Usually<()> -> Widget { - pub fn new (layout: L, render: R) -> Self { - Self(layout, render, Default::default()) - } -} - -impl< - E: Engine, - L: Send + Sync + Fn(E::Size)->Perhaps, - R: Send + Sync + Fn(&mut E::Output)->Usually<()> -> Render for Widget { - fn min_size (&self, to: E::Size) -> Perhaps { - self.0(to) - } - fn render (&self, to: &mut E::Output) -> Usually<()> { - self.1(to) - } -} - -/// Has static methods for conditional rendering, -/// in unary and binary forms. -pub struct Cond; - -impl Cond { - /// Render `item` when `cond` is true. - pub fn when > (cond: bool, item: A) -> When { - When(cond, item, Default::default()) - } - /// Render `item` if `cond` is true, otherwise render `other`. - pub fn either , B: Render> (cond: bool, item: A, other: B) -> Either { - Either(cond, item, other, Default::default()) - } -} - -/// Renders `self.1` when `self.0` is true. -pub struct When>(bool, A, PhantomData); - -/// Renders `self.1` when `self.0` is true, otherwise renders `self.2` -pub struct Either, B: Render>(bool, A, B, PhantomData); +///// Renders `self.1` when `self.0` is true, otherwise renders `self.2` +//pub struct Either, B: Render>(bool, A, B, PhantomData); -**/ +//**/ diff --git a/engine/src/tui.rs b/engine/src/tui.rs index 5fae7615..392cf378 100644 --- a/engine/src/tui.rs +++ b/engine/src/tui.rs @@ -89,7 +89,7 @@ impl Tui { } } -pub trait TuiRun + Handle + Sized + 'static> { +pub trait TuiRun + Handle + Sized + 'static> { /// Run an app in the main loop. fn run (&self, state: &Arc>) -> Usually<()>; /// Spawn the input thread. @@ -98,7 +98,7 @@ pub trait TuiRun + Handle + Sized + 'static> { fn run_output (&self, state: &Arc>, sleep: Duration) -> JoinHandle<()>; } -impl + Handle + Sized + 'static> TuiRun for Arc> { +impl + Handle + Sized + 'static> TuiRun for Arc> { fn run (&self, state: &Arc>) -> Usually<()> { let _input_thread = self.run_input(state, Duration::from_millis(100)); self.write().unwrap().setup()?; @@ -151,7 +151,7 @@ impl + Handle + Sized + 'static> TuiRun for Arc for TuiOutput { #[inline] fn render_in (&mut self, area: [u16;4], widget: &impl Render) -> Usually<()> { let last = self.area(); *self.area_mut() = area; - widget.render(self)?; + widget.render(self); *self.area_mut() = last; Ok(()) } @@ -331,26 +331,14 @@ pub fn half_block (lower: bool, upper: bool) -> Option { //impl> Render for T {} 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)) + fn render (&self, to: &mut TuiOutput) { + to.blit(self, to.area.x(), to.area.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)) + fn render (&self, to: &mut TuiOutput) { + to.blit(self, to.area.x(), to.area.y(), None) } } diff --git a/layout/Cargo.lock b/layout/Cargo.lock index b49cd71f..e5a508f6 100644 --- a/layout/Cargo.lock +++ b/layout/Cargo.lock @@ -41,7 +41,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -83,13 +83,14 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "compact_str" -version = "0.7.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" dependencies = [ "castaway", "cfg-if", "itoa", + "rustversion", "ryu", "static_assertions", ] @@ -108,15 +109,15 @@ dependencies = [ [[package]] name = "crossterm" -version = "0.27.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ "bitflags", "crossterm_winapi", - "libc", "mio", "parking_lot", + "rustix", "signal-hook", "signal-hook-mio", "winapi", @@ -131,6 +132,47 @@ dependencies = [ "winapi", ] +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "either" version = "1.13.0" @@ -149,6 +191,22 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "foldhash" version = "0.1.4" @@ -179,12 +237,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] -name = "itertools" -version = "0.12.1" +name = "ident_case" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indoc" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" + +[[package]] +name = "instability" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "898e106451f7335950c9cc64f8ec67b5f65698679ac67ed00619aeef14e1cf75" dependencies = [ - "either", + "darling", + "indoc", + "pretty_assertions", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -208,6 +283,12 @@ version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "lock_api" version = "0.4.12" @@ -250,14 +331,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.11" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", "log", "wasi", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -295,7 +376,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -304,6 +385,16 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + [[package]] name = "proc-macro2" version = "1.0.92" @@ -324,22 +415,23 @@ dependencies = [ [[package]] name = "ratatui" -version = "0.26.3" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f44c9e68fd46eda15c646fbb85e1040b657a58cdc8c98db1d97a55930d991eef" +checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" dependencies = [ "bitflags", "cassowary", "compact_str", "crossterm", - "itertools 0.12.1", + "indoc", + "instability", + "itertools", "lru", "paste", - "stability", "strum", "unicode-segmentation", "unicode-truncate", - "unicode-width", + "unicode-width 0.2.0", ] [[package]] @@ -357,6 +449,19 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustix" +version = "0.38.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + [[package]] name = "rustversion" version = "1.0.19" @@ -411,22 +516,18 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -[[package]] -name = "stability" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac" -dependencies = [ - "quote", - "syn", -] - [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.26.3" @@ -494,9 +595,9 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" dependencies = [ - "itertools 0.13.0", + "itertools", "unicode-segmentation", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -505,6 +606,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -535,11 +642,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.48.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.48.5", + "windows-targets", ] [[package]] @@ -548,22 +655,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -572,46 +664,28 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -624,50 +698,32 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" diff --git a/layout/src/logic.rs b/layout/src/logic.rs index 235d17e6..ec78a026 100644 --- a/layout/src/logic.rs +++ b/layout/src/logic.rs @@ -4,40 +4,26 @@ use crate::*; pub struct Cond; impl Cond { - /// Render `item` when `cond` is true. - pub fn when > (cond: bool, item: A) -> When { - When(cond, item, Default::default()) + /// Show an item conditionally. + pub fn when (cond: bool, item: Box>) -> When { + When(cond, item) } - /// Render `item` if `cond` is true, otherwise render `other`. - pub fn either , B: Render> (cond: bool, item: A, other: B) -> Either { - Either(cond, item, other, Default::default()) + /// Show either of two items. + pub fn either (cond: bool, a: Box>, b: Box>) -> Either { + Either(cond, a, b) } } -/// Renders `self.1` when `self.0` is true. -pub struct When>(bool, A, PhantomData); - -impl> Render for When { - fn min_size (&self, to: E::Size) -> Perhaps { - let Self(cond, item, ..) = self; - if *cond { item.min_size(to) } else { Ok(Some([0.into(), 0.into()].into())) } - } - fn render (&self, to: &mut E::Output) -> Usually<()> { - let Self(cond, item, ..) = self; - if *cond { item.render(to) } else { Ok(()) } +pub struct When(bool, Box>); +impl Layout for When { + fn layout (self, _: &mut E::Output) -> Option>> { + if self.0 { Some(self.1) } else { None } } } -/// Renders `self.1` when `self.0` is true, otherwise renders `self.2` -pub struct Either, B: Render>(bool, A, B, PhantomData); - -impl, B: Render> Render for Either { - fn min_size (&self, to: E::Size) -> Perhaps { - let Self(cond, item, other, ..) = self; - if *cond { item.min_size(to) } else { other.min_size(to) } - } - fn render (&self, to: &mut E::Output) -> Usually<()> { - let Self(cond, item, other, ..) = self; - if *cond { item.render(to) } else { other.render(to) } +pub struct Either(bool, Box>, Box>); +impl Layout for Either { + fn layout (self, _: &mut E::Output) -> Option>> { + Some(if self.0 { self.1 } else { self.2 }) } } diff --git a/layout/src/space.rs b/layout/src/space.rs index fde0acfe..05550930 100644 --- a/layout/src/space.rs +++ b/layout/src/space.rs @@ -1,6 +1,8 @@ use crate::*; use std::sync::{Arc, atomic::{AtomicUsize, Ordering::Relaxed}}; +// TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small + impl Direction { pub fn is_north (&self) -> bool { matches!(self, Self::North) } pub fn is_south (&self) -> bool { matches!(self, Self::South) } @@ -46,13 +48,12 @@ pub struct Measure { pub y: Arc, } -render!(Measure - |self, layout|Ok(Some(layout)), - |self, render|{ - self.x.store(render.area().w().into(), Relaxed); - self.y.store(render.area().h().into(), Relaxed); - Ok(()) - }); +impl Render for Measure { + fn render (&self, to: &mut E::Output) { + self.x.store(to.area().w().into(), Relaxed); + self.y.store(to.area().h().into(), Relaxed); + } +} impl Clone for Measure { fn clone (&self) -> Self { diff --git a/layout/src/transform.rs b/layout/src/transform.rs index cda8f91a..4a500896 100644 --- a/layout/src/transform.rs +++ b/layout/src/transform.rs @@ -11,7 +11,7 @@ macro_rules! content_enum { pub enum $Enum> { _Unused(PhantomData), $($Variant(T)),+ } - impl> Content for $Enum { + impl> $Enum { fn content (&self) -> Option> { match self { Self::_Unused(_) => None, @@ -25,36 +25,25 @@ macro_rules! content_enum { /// Defines an enum that transforms its content /// along either the X axis, the Y axis, or both. macro_rules! transform_xy { - ($Enum:ident $( - |$self1:ident, $to1:ident|$min_size:expr, - |$self2:ident, $to2:ident|$render:expr - )?) => { + (|$self:ident : $Enum:ident, $to:ident|$render:expr) => { content_enum!($Enum: X, Y, XY); impl> $Enum { pub fn x (item: T) -> Self { Self::X(item) } pub fn y (item: T) -> Self { Self::Y(item) } pub fn xy (item: T) -> Self { Self::XY(item) } } - $( - impl> Render for $Enum { - fn min_size (&$self1, $to1: E::Size) -> Perhaps { - $min_size - } - fn render (&$self2, $to2: &mut E::Output) -> Usually<()> { - $render - } + impl> Render for $Enum { + fn render (&$self, $to: &mut ::Output) { + $render } - )? + } } } /// Defines an enum that transforms its content parametrically /// along either the X axis, the Y axis, or both macro_rules! transform_xy_unit { - ($Enum:ident $( - |$self1:ident, $to1:ident|$min_size:expr, - |$self2:ident, $to2:ident|$render:expr - )?) => { + (|$self:ident : $Enum:ident, $to:ident|$render:expr) => { pub enum $Enum> { X(E::Unit, T), Y(E::Unit, T), XY(E::Unit, E::Unit, T), } @@ -77,151 +66,83 @@ macro_rules! transform_xy_unit { } } } - impl> Content for $Enum { + impl> $Enum { fn content (&self) -> Option> { Some(match self { - Self::X(_, content) => content, - Self::Y(_, content) => content, + Self::X(_, content) => content, + Self::Y(_, content) => content, Self::XY(_, _, content) => content, }) } } - $( - impl> Render for $Enum { - fn min_size (&$self1, $to1: E::Size) -> Perhaps { - $min_size - } - fn render (&$self2, $to2: &mut E::Output) -> Usually<()> { - $render - } + impl> Render for $Enum { + fn render (&$self, $to: &mut E::Output) -> Usually<()> { + $render } - )? + } } } -transform_xy!(Fill - |self, to|{ - let area = self.content().min_size(to.into())?; - if let Some(area) = area { - Ok(Some(match self { - Self::X(_) => [to.w().into(), area.h()], - Self::Y(_) => [area.w(), to.h().into()], - Self::XY(_) => [to.w().into(), to.h().into()], - _ => unreachable!(), - }.into())) - } else { - Ok(None) - } - }, - |self, to|self.content().render(to)); +transform_xy!(|self: Fill, to|todo!()); -transform_xy_unit!(Fixed - |self, to|{ - Ok(match self { - Self::X(w, _) => - if to.w() >= *w { Some([*w, to.h()].into()) } else { None }, - Self::Y(h, _) => - if to.h() >= *h { Some([to.w(), *h].into()) } else { None }, - Self::XY(w, h, _) - => if to.w() >= *w && to.h() >= *h { Some([*w, *h].into()) } else { None }, - }) - }, - |self, to|{ - // 🡘 🡙 ←🡙→ - if let Some(size) = self.min_size(to.area().wh().into())? { - to.render_in(to.area().clip(size).into(), &self.content()) - } else { - Ok(()) - } - }); +transform_xy_unit!(|self: Fixed, to|{ + let [x, y, w, h] = to.area().xywh(); + to.render_in(match self { + Self::X(fw, _) => [x, y, fw, h], + Self::Y(fh, _) => [x, y, w, fh], + Self::XY(fw, fh, _) => [x, y, fw, fh], + }, self.content()) +}); -transform_xy_unit!(Shrink - |self, to|Ok(self.content().min_size(to)? - .map(|wh|wh.wh()) - .map(|[w, h]|[ - w.minus(self.dx()).into(), - h.minus(self.dy()).into() - ].into())), - |self, to|Ok(self.min_size(to.area().wh().into())? - .map(|size|to.render_in(to.area().clip(size).into(), &self.content())) - .transpose()?.unwrap_or(()))); +transform_xy_unit!(|self: Shrink, to|to.render_in( + [to.x(), to.y(), to.w().minus(self.dx()), to.h().minus(self.dy())], + self.content())); -transform_xy_unit!(Expand - |self, to|Ok(self.content().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())), - |self, to|Ok(self.min_size(to.area().wh().into())? - .map(|size|to.render_in(to.area().clip(size).into(), &self.content())) - .transpose()?.unwrap_or(()))); +transform_xy_unit!(|self: Expand, to|to.render_in( + [to.x(), to.y(), to.w() + self.dx(), to.h() + self.dy()], + self.content())); -// TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small +transform_xy_unit!(|self: Min, to|to.render_in(match self { + Self::X(mw, _) => [to.x(), to.y(), to.w().max(mw), to.h()], + Self::Y(mh, _) => [to.x(), to.y(), to.w(), to.h().max(mh)], + Self::XY(mw, mh, _) => [to.x(), to.y(), to.w().max(mw), to.h().max(mh)] +}, self.content())); -transform_xy_unit!(Min - |self, to|Ok(self.content().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())), - |self, to|Ok(self.min_size(to.area().wh().into())? - .map(|size|to.render_in(to.area().clip(size).into(), &self.content())) - .transpose()?.unwrap_or(()))); +transform_xy_unit!(|self: Max, to|to.render_in(match self { + Self::X(mw, _) => [to.x(), to.y(), to.w().min(mw), to.h()], + Self::Y(mh, _) => [to.x(), to.y(), to.w(), to.h().min(mh)], + Self::XY(mw, mh, _) => [to.x(), to.y(), to.w().min(mw), to.h().min(mh)], +}, self.content())); -transform_xy_unit!(Max - |self, to|Ok(self.content().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())), - |self, to|Ok(self.min_size(to.area().wh().into())? - .map(|size|to.render_in(to.area().clip(size).into(), &self.content())) - .transpose()?.unwrap_or(()))); +transform_xy_unit!(|self: Push, to|to.render_in( + [to.x() + self.dx(), to.y() + self.dy(), to.w(), to.h()], + self.content())); -transform_xy_unit!(Push - |self, to|self.content().min_size(to), - |self, to|Ok(if let Some(size) = self.min_size(to.area().wh().into())? { - to.render_in([ - to.area().x() + self.dx(), - to.area().y() + self.dy(), - size.w(), - size.h(), - ].into(), &self.content())?; - })); +transform_xy_unit!(|self: Pull, to|to.render_in( + [to.x().minus(self.dx()), to.y().minus(self.dy()), to.w(), to.h()], + self.content())); -transform_xy_unit!(Pull - |self, to|self.content().min_size(to), - |self, to|Ok(if let Some(size) = self.min_size(to.area().wh().into())? { - to.render_in([ - to.area().x().minus(self.dx()), - to.area().y().minus(self.dy()), - size.w(), - size.h(), - ].into(), &self.content())?; - })); +transform_xy_unit!(|self: Margin, to|{ + let dx = self.dx(); + let dy = self.dy(); + to.render_in([ + to.x().minus(dx), + to.y().minus(dy), + to.w() + dy + dy, + to.h() + dy + dy, + ]) +}); -transform_xy_unit!(Margin - |self, to|match *self { - Self::X(x, ref content) => Expand::x(x + x, content), - Self::Y(y, ref content) => Expand::y(y + y, content), - Self::XY(x, y, ref content) => Expand::xy(x + x, y + y, content), - }.min_size(to), - |self, to|match *self { - Self::X(x, ref content) => Push::x(x, content), - Self::Y(y, ref content) => Push::y(y, content), - Self::XY(x, y, ref content) => Push::xy(x, y, content), - }.render(to)); - -transform_xy_unit!(Padding); -impl> Render for Padding { - fn render (&self, to: &mut E::Output) -> Usually<()> { - match self { - Self::X(x, content) => Push::x(*x, Shrink::x(*x, content)), - Self::Y(y, content) => Push::y(*y, Shrink::y(*y, content)), - Self::XY(x, y, content) => Push::xy(*x, *y, Shrink::xy(*x, *y, content)), - }.render(to) - } -} +transform_xy_unit!(|self: Padding, to|{ + let dx = self.dx(); + let dy = self.dy(); + to.render_in([ + to.x() + dx, + to.y() + dy, + to.w().minus(dy + dy), + to.h().minus(dy + dy), + ]) +}); content_enum!(Align: Center, X, Y, NW, N, NE, E, SE, S, SW, W); impl> Align {