the wild Layout trait appears

This commit is contained in:
🪞👃🪞 2024-12-31 00:38:47 +01:00
parent 7c652135ad
commit d37bd3e0c5
7 changed files with 576 additions and 595 deletions

View file

@ -46,13 +46,13 @@ pub type Perhaps<T> = Result<Option<T>, Box<dyn Error>>;
fn area_mut (&mut self) -> &mut [u16;4] {
&mut self.0
}
fn render_in (&mut self, _: [u16;4], _: &dyn Render<TestEngine>) -> Usually<()> {
fn render_in (&mut self, _: [u16;4], _: &impl Render<TestEngine>) -> Usually<()> {
Ok(())
}
}
impl Render<TestEngine> 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<T> = Result<Option<T>, Box<dyn Error>>;
use crate::tui::*;
use std::sync::{Arc, RwLock};
struct TestComponent(String);
impl Content<Tui> for TestComponent {
fn content (&self) -> Option<impl Render<Tui>> {
Some(&self.0)
impl Layout<Tui> for TestComponent {
fn layout (&self) -> Option<impl Render<Tui>> {
Some(self.0.as_str())
}
}
impl Handle<Tui> for TestComponent {

View file

@ -7,158 +7,212 @@ pub trait Output<E: Engine> {
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<E>) -> 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<E: Engine>: Send + Sync {
fn content (&self) -> Option<impl Render<E>>;
}
impl<E: Engine, C: Content<E>> Content<E> for &C {
fn content (&self) -> Option<impl Render<E>> {
(*self).content()
pub trait Layout<E: Engine>: Send + Sync {
fn layout (&self) -> Option<impl Render<E>> {
None::<()>
}
}
/// Something that writes to an [Output].
impl<E: Engine> Layout<E> for () {}
impl<E: Engine, L: Layout<E>> Layout<E> for &L {}
pub trait Render<E: Engine>: Send + Sync {
/// Minimum size to use
fn min_size (&self, _: E::Size) -> Perhaps<E::Size> {
Ok(None)
}
/// Draw to output render target
fn render (&self, _: &mut E::Output) -> Usually<()> {
Ok(())
}
fn render (&self, _: &mut E::Output);
}
impl<E: Engine> Render<E> for &dyn Render<E> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> { (*self).min_size(to) }
fn render (&self, to: &mut E::Output) -> Usually<()> { (*self).render(to) }
}
impl<E: Engine, R: Render<E>> Render<E> for &R {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> { (*self).min_size(to) }
fn render (&self, to: &mut E::Output) -> Usually<()> { (*self).render(to) }
}
impl<E: Engine, R: Render<E>> Render<E> for Option<R> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
if let Some(content) = self {
content.min_size(to)
} else {
Ok(None)
impl<E: Engine, L: Layout<E>> Render<E> 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<E: Engine, L: Layout<E>> Layout<E> for &L {}
// Implement from [Content] for all [Engine]s
(|$self:ident:$Struct:ident$(<$($L:lifetime),*E$(,$T:ident$(:$U:path)?)*$(,)?>)?|$cb:expr) => {
impl<E: Engine, $($($L),*$($T $(: $U)?),*)?> Content<E> for $Struct $(<$($L,)* E, $($T),*>)? {
fn content (&$self) -> Option<impl Render<$E>> { Some($cb) }
}
impl<E: Engine, $($($L),*$($T $(: $U)?),*)?> Render<E> for $Struct $(<$($L,)* E, $($T),*>)? {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
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<E: Engine>: Send + Sync {
//fn content (&self) -> Option<impl Render<E>>;
//}
//impl<E: Engine, C: Content<E>> Content<E> for &C {
//fn content (&self) -> Option<impl Render<E>> {
//(*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<E: Engine, $($($L),*$($T $(: $U)?),*)?> Content<E> for $Struct $(<$($L,)* E, $($T),*>)? {
fn content (&self) -> Option<impl Render<E>> { Some(self) }
}
impl<E: Engine, $($($L),*$($T $(: $U)?),*)?> Render<E> for $Struct $(<$($L,)* E, $($T),*>)? {
fn min_size (&$self1, $to1: E::Size) -> Perhaps<E::Size> { $min_size }
fn render (&$self2, $to2: &mut E::Output) -> Usually<()> { $render }
}
};
///// Something that writes to an [Output].
//pub trait Render<E: Engine>: Send + Sync {
///// Minimum size to use
//fn min_size (&self, _: E::Size) -> Perhaps<E::Size> {
//Ok(None)
//}
///// Draw to output render target
//fn render (&self, _: &mut E::Output) -> Usually<()> {
//Ok(())
//}
//}
//impl<E: Engine> Render<E> for &dyn Render<E> {
//fn min_size (&self, to: E::Size) -> Perhaps<E::Size> { (*self).min_size(to) }
//fn render (&self, to: &mut E::Output) -> Usually<()> { (*self).render(to) }
//}
//impl<E: Engine, R: Render<E>> Render<E> for &R {
//fn min_size (&self, to: E::Size) -> Perhaps<E::Size> { (*self).min_size(to) }
//fn render (&self, to: &mut E::Output) -> Usually<()> { (*self).render(to) }
//}
//impl<E: Engine, R: Render<E>> Render<E> for Option<R> {
//fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
//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<impl Render<$E>> {
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<impl Render<$E>> { 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<E: Engine, $($($L),*$($T $(: $U)?),*)?> Content<E> for $Struct $(<$($L,)* E, $($T),*>)? {
//fn content (&$self) -> Option<impl Render<$E>> { Some($cb) }
//}
//impl<E: Engine, $($($L),*$($T $(: $U)?),*)?> Render<E> for $Struct $(<$($L,)* E, $($T),*>)? {
//fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
//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<E: Engine, $($($L),*$($T $(: $U)?),*)?> Content<E> for $Struct $(<$($L,)* E, $($T),*>)? {
//fn content (&self) -> Option<impl Render<E>> { Some(self) }
//}
//impl<E: Engine, $($($L),*$($T $(: $U)?),*)?> Render<E> for $Struct $(<$($L,)* E, $($T),*>)? {
//fn min_size (&$self1, $to1: E::Size) -> Perhaps<E::Size> { $min_size }
//fn render (&$self2, $to2: &mut E::Output) -> Usually<()> { $render }
//}
//};
//impl<E: Engine, C: Content<E>> Content<E> for &mut C {}
//impl<E: Engine, C: Content<E>> Content<E> for Option<C> {}
//impl<E: Engine, C: Content<E>> Content<E> for Arc<C> {}
//impl<E: Engine, C: Content<E>> Content<E> for Mutex<C> {}
//impl<E: Engine, C: Content<E>> Content<E> for RwLock<C> {}
//// 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<impl Render<$E>> {
//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<E: Engine, C: Content<E>> Render<E> 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<impl Render<$E>> { 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<E: Engine, C: Content<E>> Content<E> for &mut C {}
////impl<E: Engine, C: Content<E>> Content<E> for Option<C> {}
////impl<E: Engine, C: Content<E>> Content<E> for Arc<C> {}
////impl<E: Engine, C: Content<E>> Content<E> for Mutex<C> {}
////impl<E: Engine, C: Content<E>> Content<E> for RwLock<C> {}
////impl<E: Engine, C: Content<E>> Render<E> for C {}
////impl<E: Engine, C: Content<E>> Render<E> for C {
/////// Minimum size to use
////fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
////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<E: Engine, R: Render<E> + Send + Sync> Content<E> for R {}
////impl<E: Engine, R: Render<E>> Content<E> for R {
////fn content (&self) -> Option<impl Render<E>> {
////Some(self)
////}
////}
///// All implementors of [Content] can be [Render]ed.
//impl<E: Engine, C: Content<E>> Render<E> for C {
///// Minimum size to use
//fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
@ -172,65 +226,20 @@ impl<E: Engine, R: Render<E>> Render<E> for Option<R> {
//}
//}
/****
////impl<E: Engine, C: Content<E>> Content<E> for &C {
////fn content (&self) -> Option<impl Render<E>> {
////Some(self)
////}
////}
////impl<E: Engine, C: Content<E>> Content<E> for Option<C> {
////fn content (&self) -> Option<impl Render<E>> {
////Some(self)
////}
////}
impl<E: Engine, R: Render<E> + Send + Sync> Content<E> for R {}
//impl<E: Engine, R: Render<E>> Content<E> for R {
//fn content (&self) -> Option<impl Render<E>> {
//Some(self)
//}
//}
/// All implementors of [Content] can be [Render]ed.
impl<E: Engine, C: Content<E>> Render<E> for C {
/// Minimum size to use
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
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<E: Engine, C: Content<E>> Content<E> for &C {
//fn content (&self) -> Option<impl Render<E>> {
//Some(self)
//}
//}
//impl<E: Engine, C: Content<E>> Content<E> for Option<C> {
//fn content (&self) -> Option<impl Render<E>> {
//Some(self)
//}
//}
impl<E: Engine, R: Render<E>> Render<E> for &R {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
(*self).min_size(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
(*self).render(to)
}
}
impl<E: Engine, R: Render<E>> Render<E> for Option<R> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
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<E: Engine> Render<E> for &dyn Render<E> {
//impl<E: Engine, R: Render<E>> Render<E> for &R {
//fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
//(*self).min_size(to)
//}
@ -239,124 +248,144 @@ impl<E: Engine, R: Render<E>> Render<E> for Option<R> {
//}
//}
//impl<E: Engine> Render<E> for &mut dyn Render<E> {
//impl<E: Engine, R: Render<E>> Render<E> for Option<R> {
//fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
//(*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<E: Engine> Render<E> for Box<dyn Render<E> + '_> {
//fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
//(**self).min_size(to)
//}
//fn render (&self, to: &mut E::Output) -> Usually<()> {
//(**self).render(to)
////impl<E: Engine> Render<E> for &dyn Render<E> {
////fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
////(*self).min_size(to)
////}
////fn render (&self, to: &mut E::Output) -> Usually<()> {
////(*self).render(to)
////}
////}
////impl<E: Engine> Render<E> for &mut dyn Render<E> {
////fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
////(*self).min_size(to)
////}
////fn render (&self, to: &mut E::Output) -> Usually<()> {
////(*self).render(to)
////}
////}
////impl<E: Engine> Render<E> for Box<dyn Render<E> + '_> {
////fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
////(**self).min_size(to)
////}
////fn render (&self, to: &mut E::Output) -> Usually<()> {
////(**self).render(to)
////}
////}
////impl<E: Engine, W: Render<E>> Render<E> for Arc<W> {
////fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
////self.as_ref().min_size(to)
////}
////fn render (&self, to: &mut E::Output) -> Usually<()> {
////self.as_ref().render(to)
////}
////}
////impl<E: Engine, W: Render<E>> Render<E> for Mutex<W> {
////fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
////self.lock().unwrap().min_size(to)
////}
////fn render (&self, to: &mut E::Output) -> Usually<()> {
////self.lock().unwrap().render(to)
////}
////}
////impl<E: Engine, W: Render<E>> Render<E> for RwLock<W> {
////fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
////self.read().unwrap().min_size(to)
////}
////fn render (&self, to: &mut E::Output) -> Usually<()> {
////self.read().unwrap().render(to)
////}
////}
////impl<E: Engine, W: Render<E>> Render<E> for Option<W> {
////fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
////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 <E, T> (w: &T) -> &dyn Render<E>
//where E: Engine, T: Render<E>
//{
//w as &dyn Render<E>
//}
///// Ad-hoc widget with custom rendering.
//pub fn render <E, F> (render: F) -> impl Render<E>
//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<E::Size>,
//R: Send + Sync + Fn(&mut E::Output)->Usually<()>
//>(L, R, PhantomData<E>);
//impl<
//E: Engine,
//L: Send + Sync + Fn(E::Size)->Perhaps<E::Size>,
//R: Send + Sync + Fn(&mut E::Output)->Usually<()>
//> Widget<E, L, R> {
//pub fn new (layout: L, render: R) -> Self {
//Self(layout, render, Default::default())
//}
//}
//impl<E: Engine, W: Render<E>> Render<E> for Arc<W> {
//impl<
//E: Engine,
//L: Send + Sync + Fn(E::Size)->Perhaps<E::Size>,
//R: Send + Sync + Fn(&mut E::Output)->Usually<()>
//> Render<E> for Widget<E, L, R> {
//fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
//self.as_ref().min_size(to)
//self.0(to)
//}
//fn render (&self, to: &mut E::Output) -> Usually<()> {
//self.as_ref().render(to)
//self.1(to)
//}
//}
//impl<E: Engine, W: Render<E>> Render<E> for Mutex<W> {
//fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
//self.lock().unwrap().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 <E: Engine, A: Render<E>> (cond: bool, item: A) -> When<E, A> {
//When(cond, item, Default::default())
//}
//fn render (&self, to: &mut E::Output) -> Usually<()> {
//self.lock().unwrap().render(to)
///// Render `item` if `cond` is true, otherwise render `other`.
//pub fn either <E: Engine, A: Render<E>, B: Render<E>> (cond: bool, item: A, other: B) -> Either<E, A, B> {
//Either(cond, item, other, Default::default())
//}
//}
//impl<E: Engine, W: Render<E>> Render<E> for RwLock<W> {
//fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
//self.read().unwrap().min_size(to)
//}
//fn render (&self, to: &mut E::Output) -> Usually<()> {
//self.read().unwrap().render(to)
//}
//}
///// Renders `self.1` when `self.0` is true.
//pub struct When<E: Engine, A: Render<E>>(bool, A, PhantomData<E>);
//impl<E: Engine, W: Render<E>> Render<E> for Option<W> {
//fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
//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 <E, T> (w: &T) -> &dyn Render<E>
where E: Engine, T: Render<E>
{
w as &dyn Render<E>
}
/// Ad-hoc widget with custom rendering.
pub fn render <E, F> (render: F) -> impl Render<E>
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<E::Size>,
R: Send + Sync + Fn(&mut E::Output)->Usually<()>
>(L, R, PhantomData<E>);
impl<
E: Engine,
L: Send + Sync + Fn(E::Size)->Perhaps<E::Size>,
R: Send + Sync + Fn(&mut E::Output)->Usually<()>
> Widget<E, L, R> {
pub fn new (layout: L, render: R) -> Self {
Self(layout, render, Default::default())
}
}
impl<
E: Engine,
L: Send + Sync + Fn(E::Size)->Perhaps<E::Size>,
R: Send + Sync + Fn(&mut E::Output)->Usually<()>
> Render<E> for Widget<E, L, R> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
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 <E: Engine, A: Render<E>> (cond: bool, item: A) -> When<E, A> {
When(cond, item, Default::default())
}
/// Render `item` if `cond` is true, otherwise render `other`.
pub fn either <E: Engine, A: Render<E>, B: Render<E>> (cond: bool, item: A, other: B) -> Either<E, A, B> {
Either(cond, item, other, Default::default())
}
}
/// Renders `self.1` when `self.0` is true.
pub struct When<E: Engine, A: Render<E>>(bool, A, PhantomData<E>);
/// Renders `self.1` when `self.0` is true, otherwise renders `self.2`
pub struct Either<E: Engine, A: Render<E>, B: Render<E>>(bool, A, B, PhantomData<E>);
///// Renders `self.1` when `self.0` is true, otherwise renders `self.2`
//pub struct Either<E: Engine, A: Render<E>, B: Render<E>>(bool, A, B, PhantomData<E>);
**/
//**/

View file

@ -89,7 +89,7 @@ impl Tui {
}
}
pub trait TuiRun<R: Render<Tui> + Handle<Tui> + Sized + 'static> {
pub trait TuiRun<R: Layout<Tui> + Handle<Tui> + Sized + 'static> {
/// Run an app in the main loop.
fn run (&self, state: &Arc<RwLock<R>>) -> Usually<()>;
/// Spawn the input thread.
@ -98,7 +98,7 @@ pub trait TuiRun<R: Render<Tui> + Handle<Tui> + Sized + 'static> {
fn run_output (&self, state: &Arc<RwLock<R>>, sleep: Duration) -> JoinHandle<()>;
}
impl<T: Render<Tui> + Handle<Tui> + Sized + 'static> TuiRun<T> for Arc<RwLock<Tui>> {
impl<T: Layout<Tui> + Handle<Tui> + Sized + 'static> TuiRun<T> for Arc<RwLock<Tui>> {
fn run (&self, state: &Arc<RwLock<T>>) -> Usually<()> {
let _input_thread = self.run_input(state, Duration::from_millis(100));
self.write().unwrap().setup()?;
@ -151,7 +151,7 @@ impl<T: Render<Tui> + Handle<Tui> + Sized + 'static> TuiRun<T> for Arc<RwLock<Tu
buffer.reset();
}
let mut output = TuiOutput { buffer, area: [0, 0, width, height] };
state.render(&mut output).expect("render failed");
state.render(&mut output);
buffer = engine.write().unwrap().flip(output.buffer, size);
}
std::thread::sleep(sleep);
@ -257,7 +257,7 @@ impl Output<Tui> for TuiOutput {
#[inline] fn render_in (&mut self, area: [u16;4], widget: &impl Render<Tui>) -> 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<char> {
//impl<T: Content<Tui>> Render<Tui> for T {}
impl Render<Tui> 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<Tui> 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)
}
}

254
layout/Cargo.lock generated
View file

@ -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"

View file

@ -4,40 +4,26 @@ use crate::*;
pub struct Cond;
impl Cond {
/// Render `item` when `cond` is true.
pub fn when <E: Engine, A: Render<E>> (cond: bool, item: A) -> When<E, A> {
When(cond, item, Default::default())
/// Show an item conditionally.
pub fn when <E: Engine> (cond: bool, item: Box<dyn Layout<E>>) -> When<E> {
When(cond, item)
}
/// Render `item` if `cond` is true, otherwise render `other`.
pub fn either <E: Engine, A: Render<E>, B: Render<E>> (cond: bool, item: A, other: B) -> Either<E, A, B> {
Either(cond, item, other, Default::default())
/// Show either of two items.
pub fn either <E: Engine> (cond: bool, a: Box<dyn Layout<E>>, b: Box<dyn Layout<E>>) -> Either<E> {
Either(cond, a, b)
}
}
/// Renders `self.1` when `self.0` is true.
pub struct When<E: Engine, A: Render<E>>(bool, A, PhantomData<E>);
impl<E: Engine, A: Render<E>> Render<E> for When<E, A> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
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<E>(bool, Box<dyn Layout<E>>);
impl<E: Engine> Layout<E> for When<E> {
fn layout (self, _: &mut E::Output) -> Option<Box<dyn Layout<E>>> {
if self.0 { Some(self.1) } else { None }
}
}
/// Renders `self.1` when `self.0` is true, otherwise renders `self.2`
pub struct Either<E: Engine, A: Render<E>, B: Render<E>>(bool, A, B, PhantomData<E>);
impl<E: Engine, A: Render<E>, B: Render<E>> Render<E> for Either<E, A, B> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
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<E: Engine>(bool, Box<dyn Layout<E>>, Box<dyn Layout<E>>);
impl<E: Engine> Layout<E> for Either<E> {
fn layout (self, _: &mut E::Output) -> Option<Box<dyn Layout<E>>> {
Some(if self.0 { self.1 } else { self.2 })
}
}

View file

@ -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<E: Engine> {
pub y: Arc<AtomicUsize>,
}
render!(Measure<E>
|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<E: Engine> Render<E> for Measure<E> {
fn render (&self, to: &mut E::Output) {
self.x.store(to.area().w().into(), Relaxed);
self.y.store(to.area().h().into(), Relaxed);
}
}
impl<E: Engine> Clone for Measure<E> {
fn clone (&self) -> Self {

View file

@ -11,7 +11,7 @@ macro_rules! content_enum {
pub enum $Enum<E: Engine, T: Render<E>> {
_Unused(PhantomData<E>), $($Variant(T)),+
}
impl<E: Engine, T: Render<E>> Content<E> for $Enum<E, T> {
impl<E: Engine, T: Render<E>> $Enum<E, T> {
fn content (&self) -> Option<impl Render<E>> {
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<E: Engine, T: Render<E>> $Enum<E, T> {
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<E: Engine, T: Render<E>> Render<E> for $Enum<E, T> {
fn min_size (&$self1, $to1: E::Size) -> Perhaps<E::Size> {
$min_size
}
fn render (&$self2, $to2: &mut E::Output) -> Usually<()> {
fn render (&$self, $to: &mut <E as Engine>::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<E: Engine, T: Render<E>> {
X(E::Unit, T), Y(E::Unit, T), XY(E::Unit, E::Unit, T),
}
@ -77,7 +66,7 @@ macro_rules! transform_xy_unit {
}
}
}
impl<E: Engine, T: Render<E>> Content<E> for $Enum<E, T> {
impl<E: Engine, T: Render<E>> $Enum<E, T> {
fn content (&self) -> Option<impl Render<E>> {
Some(match self {
Self::X(_, content) => content,
@ -86,142 +75,74 @@ macro_rules! transform_xy_unit {
})
}
}
$(
impl<E: Engine, T: Render<E>> Render<E> for $Enum<E, T> {
fn min_size (&$self1, $to1: E::Size) -> Perhaps<E::Size> {
$min_size
}
fn render (&$self2, $to2: &mut E::Output) -> Usually<()> {
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())? {
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!(|self: Margin, to|{
let dx = self.dx();
let dy = self.dy();
to.render_in([
to.area().x() + self.dx(),
to.area().y() + self.dy(),
size.w(),
size.h(),
].into(), &self.content())?;
}));
to.x().minus(dx),
to.y().minus(dy),
to.w() + dy + dy,
to.h() + dy + dy,
])
});
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())? {
transform_xy_unit!(|self: Padding, to|{
let dx = self.dx();
let dy = self.dy();
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!(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<E: Engine, T: Render<E>> Render<E> for Padding<E, T> {
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)
}
}
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<E: Engine, T: Render<E>> Align<E, T> {