diff --git a/src/lib.rs b/src/lib.rs index f274eed6..01258fc5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,20 +70,23 @@ pub(crate) use crossterm::{ExecutableCommand}; pub(crate) use crossterm::terminal::{EnterAlternateScreen, LeaveAlternateScreen, enable_raw_mode, disable_raw_mode}; pub(crate) use crossterm::event::{KeyCode, KeyModifiers, KeyEvent, KeyEventKind, KeyEventState}; -pub use ::ratatui; pub(crate) use ratatui::{ +pub use ::ratatui; +pub(crate) use ratatui::{ prelude::{Style, Buffer}, style::{Stylize, Modifier}, backend::{Backend, CrosstermBackend, ClearType} }; -pub use ::midly::{self, num::u7}; pub(crate) use ::midly::{ +pub use ::midly::{self, num::u7}; +pub(crate) use ::midly::{ Smf, MidiMessage, TrackEventKind, live::LiveEvent, }; -pub use ::palette; pub(crate) use ::palette::{ +pub use ::palette; +pub(crate) use ::palette::{ *, convert::*, okhsl::* diff --git a/src/sequencer.rs b/src/sequencer.rs index f41cb6a6..47ac79f6 100644 --- a/src/sequencer.rs +++ b/src/sequencer.rs @@ -51,17 +51,21 @@ render!(|self: SequencerTui|{ let with_editbar = |x|Split::n(false, 1, MidiEditStatus(&self.editor), x); let with_size = |x|lay!([self.size, x]); let editor = with_editbar(with_pool(Fill::wh(&self.editor))); - let color = self.player.play_phrase().as_ref().map(|(_,p)| - p.as_ref().map(|p|p.read().unwrap().color) - ).flatten().clone(); - let toolbar = row!([ + + let color = self.player.play_phrase().as_ref().map(|(_,p)| + p.as_ref().map(|p|p.read().unwrap().color) + ).flatten().clone(); + + let toolbar = Cond::when(self.transport, row!([ Fixed::wh(5, 2, PlayPause(self.clock.is_rolling())), Fixed::h(2, TransportView::from((self, color, true))), - ]).when(self.transport); - let play_queue = row!([ + ])); + + let play_queue = Cond::when(self.selectors, row!([ PhraseSelector::play_phrase(&self.player), PhraseSelector::next_phrase(&self.player), - ]).when(self.selectors);; + ])); + Tui::min_y(15, with_size(with_status(col!([ toolbar, play_queue, diff --git a/src/space.rs b/src/space.rs index 0eed3bfd..83c6b7d8 100644 --- a/src/space.rs +++ b/src/space.rs @@ -8,23 +8,20 @@ use std::fmt::{Display, Debug}; ////////////////////////////////////////////////////// pub(crate) mod align; -pub(crate) mod cond; pub(crate) use cond::*; -pub(crate) mod fill; -pub(crate) mod fixed; pub(crate) use fixed::*; -pub(crate) mod inset_outset; pub(crate) use inset_outset::*; -pub(crate) mod layers; pub(crate) use layers::*; -pub(crate) mod measure; pub(crate) use measure::*; -pub use self::measure::Measure; -pub(crate) mod min_max; pub(crate) use min_max::*; -pub(crate) mod push_pull; pub(crate) use push_pull::*; +pub(crate) mod cond; pub(crate) use cond::*; +pub(crate) mod layers; pub(crate) use layers::*; +pub(crate) mod measure; pub(crate) use measure::*; +pub(crate) mod position; pub(crate) use position::*; pub(crate) mod scroll; -pub(crate) mod shrink_grow; pub(crate) use shrink_grow::*; -pub(crate) mod split; pub(crate) use split::*; -pub(crate) mod stack; pub(crate) use stack::*; +pub(crate) mod size; pub(crate) use size::*; +pub(crate) mod split; pub(crate) use split::*; pub use self::{ align::*, - fill::*, + cond::*, + measure::*, + position::*, + size::*, split::*, }; @@ -234,3 +231,15 @@ impl Area for [N;4] { Stack::right(move|add|{ for $pat in $collection { add(&$item)?; } Ok(()) }) }; } + +#[macro_export] macro_rules! lay { + ([$($expr:expr),* $(,)?]) => { + Layers::new(move|add|{ $(add(&$expr)?;)* Ok(()) }) + }; + (![$($expr:expr),* $(,)?]) => { + Layers::new(|add|{ $(add(&$expr)?;)* Ok(()) }) + }; + ($expr:expr) => { + Layers::new($expr) + }; +} diff --git a/src/space/cond.rs b/src/space/cond.rs index 08fd57da..2f8a6006 100644 --- a/src/space/cond.rs +++ b/src/space/cond.rs @@ -1,65 +1,43 @@ use crate::*; -pub enum Cond, B: Render> { - _Unused(E), - When(bool, A), - Either(bool, A, B) -} +/// Conditional rendering, in unary and binary forms. +pub struct Cond; -impl> LayoutCond for R {} - -pub trait LayoutCond: Render + Sized { - fn when (self, cond: bool) -> If { - If(Default::default(), cond, self) +impl Cond { + /// Render `item` when `cond` is true. + pub fn when > (cond: bool, item: A) -> When { + When(cond, item, Default::default()) } - fn or > (self, cond: bool, other: B) -> Either { - Either(Default::default(), cond, self, other) + /// 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 LayoutCondStatic for E {} +/// Renders `self.1` when `self.0` is true. +pub struct When>(bool, A, PhantomData); -pub trait LayoutCondStatic { - fn either , B: Render> ( - condition: bool, - a: A, - b: B, - ) -> Either { - Either(Default::default(), condition, a, b) - } -} +/// Renders `self.1` when `self.0` is true, otherwise renders `self.2` +pub struct Either, B: Render>(bool, A, B, PhantomData); -/// Render widget if predicate is true -pub struct If>(PhantomData, bool, A); - -impl> Render for If { +impl> Render for When { fn min_size (&self, to: E::Size) -> Perhaps { - if self.1 { - return self.2.min_size(to) - } - Ok(None) + 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<()> { - if self.1 { - return self.2.render(to) - } - Ok(()) + let Self(cond, item, ..) = self; + if *cond { item.render(to) } else { Ok(()) } } } -/// Render widget A if predicate is true, otherwise widget B -pub struct Either, B: Render>( - PhantomData, - bool, - A, - B, -); - impl, B: Render> Render for Either { fn min_size (&self, to: E::Size) -> Perhaps { - if self.1 { self.2.min_size(to) } else { self.3.min_size(to) } + 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<()> { - if self.1 { self.2.render(to) } else { self.3.render(to) } + let Self(cond, item, other, ..) = self; + if *cond { item.render(to) } else { other.render(to) } } } diff --git a/src/space/fill.rs b/src/space/fill.rs deleted file mode 100644 index 044d751a..00000000 --- a/src/space/fill.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::*; - -pub enum Fill> { - X(W), - Y(W), - XY(W), - _Unused(PhantomData) -} - -impl> Fill { - fn inner (&self) -> &W { - match self { - Self::X(inner) => &inner, - Self::Y(inner) => &inner, - Self::XY(inner) => &inner, - _ => unreachable!(), - } - } - pub fn w (fill: W) -> Self { - Self::X(fill) - } - pub fn h (fill: W) -> Self { - Self::Y(fill) - } - pub fn wh (fill: W) -> Self { - Self::XY(fill) - } -} - -impl> Render for Fill { - fn min_size (&self, to: E::Size) -> Perhaps { - let area = self.inner().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) - } - } - fn render (&self, to: &mut E::Output) -> Usually<()> { - self.inner().render(to) - } -} diff --git a/src/space/fixed.rs b/src/space/fixed.rs deleted file mode 100644 index 63db372b..00000000 --- a/src/space/fixed.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::*; - -/// Enforce fixed size of drawing area -pub enum Fixed> { - _Unused(PhantomData), - /// Enforce fixed width - X(E::Unit, T), - /// Enforce fixed height - Y(E::Unit, T), - /// Enforce fixed width and height - XY(E::Unit, E::Unit, T), -} - -impl> Fixed { - pub fn inner (&self) -> &T { - match self { - Self::X(_, i) => i, - Self::Y(_, i) => i, - Self::XY(_, _, i) => i, - _ => unreachable!(), - } - } - pub fn w (x: E::Unit, w: T) -> Self { - Self::X(x, w) - } - pub fn h (y: E::Unit, w: T) -> Self { - Self::Y(y, w) - } - pub fn wh (x: E::Unit, y: E::Unit, w: T) -> Self { - Self::XY(x, y, w) - } -} - -impl> Render for Fixed { - fn min_size (&self, to: E::Size) -> Perhaps { - 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 }, - _ => unreachable!(), - }) - } - fn render (&self, to: &mut E::Output) -> Usually<()> { - // 🡘 🡙 ←🡙→ - if let Some(size) = self.min_size(to.area().wh().into())? { - to.render_in(to.area().clip(size).into(), self.inner()) - } else { - Ok(()) - } - } -} diff --git a/src/space/inset_outset.rs b/src/space/inset_outset.rs deleted file mode 100644 index 2985aecf..00000000 --- a/src/space/inset_outset.rs +++ /dev/null @@ -1,92 +0,0 @@ -use crate::*; - -impl + LayoutShrinkGrow> LayoutInsetOutset for E {} - -pub trait LayoutInsetOutset: LayoutPushPull + LayoutShrinkGrow { - fn inset_x > (x: E::Unit, w: W) -> Inset { - Inset::X(x, w) - } - fn inset_y > (y: E::Unit, w: W) -> Inset { - Inset::Y(y, w) - } - fn inset_xy > (x: E::Unit, y: E::Unit, w: W) -> Inset { - Inset::XY(x, y, w) - } - fn outset_x > (x: E::Unit, w: W) -> Outset { - Outset::X(x, w) - } - fn outset_y > (y: E::Unit, w: W) -> Outset { - Outset::Y(y, w) - } - fn outset_xy > (x: E::Unit, y: E::Unit, w: W) -> Outset { - Outset::XY(x, y, w) - } -} - -/// Shrink from each side -pub enum Inset { - /// Decrease width - X(E::Unit, T), - /// Decrease height - Y(E::Unit, T), - /// Decrease width and height - XY(E::Unit, E::Unit, T), -} - -impl> Inset { - pub fn inner (&self) -> &T { - match self { - Self::X(_, i) => i, - Self::Y(_, i) => i, - Self::XY(_, _, i) => i, - } - } -} - -impl> Render for Inset { - fn render (&self, to: &mut E::Output) -> Usually<()> { - match self { - Self::X(x, inner) => E::push_x(*x, E::shrink_x(*x, inner)), - Self::Y(y, inner) => E::push_y(*y, E::shrink_y(*y, inner)), - Self::XY(x, y, inner) => E::push_xy(*x, *y, E::shrink_xy(*x, *y, inner)), - }.render(to) - } -} - -/// Grow on each side -pub enum Outset> { - /// Increase width - X(E::Unit, T), - /// Increase height - Y(E::Unit, T), - /// Increase width and height - XY(E::Unit, E::Unit, T), -} - - -impl> Outset { - pub fn inner (&self) -> &T { - match self { - Self::X(_, i) => i, - Self::Y(_, i) => i, - Self::XY(_, _, i) => i, - } - } -} - -impl> Render for Outset { - fn min_size (&self, to: E::Size) -> Perhaps { - match *self { - Self::X(x, ref inner) => E::grow_x(x + x, inner), - Self::Y(y, ref inner) => E::grow_y(y + y, inner), - Self::XY(x, y, ref inner) => E::grow_xy(x + x, y + y, inner), - }.min_size(to) - } - fn render (&self, to: &mut E::Output) -> Usually<()> { - match *self { - Self::X(x, ref inner) => E::push_x(x, inner), - Self::Y(y, ref inner) => E::push_y(y, inner), - Self::XY(x, y, ref inner) => E::push_xy(x, y, inner), - }.render(to) - } -} diff --git a/src/space/layers.rs b/src/space/layers.rs index 353198ab..9fe07ecd 100644 --- a/src/space/layers.rs +++ b/src/space/layers.rs @@ -1,17 +1,5 @@ use crate::*; -#[macro_export] macro_rules! lay { - ([$($expr:expr),* $(,)?]) => { - Layers::new(move|add|{ $(add(&$expr)?;)* Ok(()) }) - }; - (![$($expr:expr),* $(,)?]) => { - Layers::new(|add|{ $(add(&$expr)?;)* Ok(()) }) - }; - ($expr:expr) => { - Layers::new($expr) - }; -} - pub struct Layers< E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)->Usually<()>)->Usually<()> diff --git a/src/space/min_max.rs b/src/space/min_max.rs deleted file mode 100644 index f0b5f26f..00000000 --- a/src/space/min_max.rs +++ /dev/null @@ -1,95 +0,0 @@ -use crate::*; - -impl LayoutMinMax for E {} - -pub trait LayoutMinMax { - fn min_x > (x: E::Unit, w: W) -> Min { - Min::X(x, w) - } - fn min_y > (y: E::Unit, w: W) -> Min { - Min::Y(y, w) - } - fn min_xy > (x: E::Unit, y: E::Unit, w: W) -> Min { - Min::XY(x, y, w) - } - fn max_x > (x: E::Unit, w: W) -> Max { - Max::X(x, w) - } - fn max_y > (y: E::Unit, w: W) -> Max { - Max::Y(y, w) - } - fn max_xy > (x: E::Unit, y: E::Unit, w: W) -> Max { - Max::XY(x, y, w) - } -} - -/// Enforce minimum size of drawing area -pub enum Min> { - /// Enforce minimum width - X(E::Unit, T), - /// Enforce minimum height - Y(E::Unit, T), - /// Enforce minimum width and height - XY(E::Unit, E::Unit, T), -} - -/// Enforce maximum size of drawing area -pub enum Max> { - /// Enforce maximum width - X(E::Unit, T), - /// Enforce maximum height - Y(E::Unit, T), - /// Enforce maximum width and height - XY(E::Unit, E::Unit, T), -} - -impl> Min { - pub fn inner (&self) -> &T { - match self { - Self::X(_, i) => i, - Self::Y(_, i) => i, - Self::XY(_, _, i) => i, - } - } -} - -impl> Render for Min { - fn min_size (&self, to: E::Size) -> Perhaps { - Ok(self.inner().min_size(to)?.map(|to|match *self { - Self::X(w, _) => [to.w().max(w), to.h()], - Self::Y(h, _) => [to.w(), to.h().max(h)], - Self::XY(w, h, _) => [to.w().max(w), to.h().max(h)], - }.into())) - } - // TODO: 🡘 🡙 ←🡙→ - fn render (&self, to: &mut E::Output) -> Usually<()> { - Ok(self.min_size(to.area().wh().into())? - .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) - .transpose()?.unwrap_or(())) - } -} - -impl> Max { - fn inner (&self) -> &T { - match self { - Self::X(_, i) => i, - Self::Y(_, i) => i, - Self::XY(_, _, i) => i, - } - } -} - -impl> Render for Max { - fn min_size (&self, to: E::Size) -> Perhaps { - Ok(self.inner().min_size(to)?.map(|to|match *self { - Self::X(w, _) => [to.w().min(w), to.h()], - Self::Y(h, _) => [to.w(), to.h().min(h)], - Self::XY(w, h, _) => [to.w().min(w), to.h().min(h)], - }.into())) - } - fn render (&self, to: &mut E::Output) -> Usually<()> { - Ok(self.min_size(to.area().wh().into())? - .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) - .transpose()?.unwrap_or(())) - } -} diff --git a/src/space/push_pull.rs b/src/space/position.rs similarity index 59% rename from src/space/push_pull.rs rename to src/space/position.rs index 0cd5ef67..459329ee 100644 --- a/src/space/push_pull.rs +++ b/src/space/position.rs @@ -126,3 +126,94 @@ impl> Render for Pull { } } + +impl + LayoutShrinkGrow> LayoutInsetOutset for E {} + +pub trait LayoutInsetOutset: LayoutPushPull + LayoutShrinkGrow { + fn inset_x > (x: E::Unit, w: W) -> Inset { + Inset::X(x, w) + } + fn inset_y > (y: E::Unit, w: W) -> Inset { + Inset::Y(y, w) + } + fn inset_xy > (x: E::Unit, y: E::Unit, w: W) -> Inset { + Inset::XY(x, y, w) + } + fn outset_x > (x: E::Unit, w: W) -> Outset { + Outset::X(x, w) + } + fn outset_y > (y: E::Unit, w: W) -> Outset { + Outset::Y(y, w) + } + fn outset_xy > (x: E::Unit, y: E::Unit, w: W) -> Outset { + Outset::XY(x, y, w) + } +} + +/// Shrink from each side +pub enum Inset { + /// Decrease width + X(E::Unit, T), + /// Decrease height + Y(E::Unit, T), + /// Decrease width and height + XY(E::Unit, E::Unit, T), +} + +impl> Inset { + pub fn inner (&self) -> &T { + match self { + Self::X(_, i) => i, + Self::Y(_, i) => i, + Self::XY(_, _, i) => i, + } + } +} + +impl> Render for Inset { + fn render (&self, to: &mut E::Output) -> Usually<()> { + match self { + Self::X(x, inner) => E::push_x(*x, E::shrink_x(*x, inner)), + Self::Y(y, inner) => E::push_y(*y, E::shrink_y(*y, inner)), + Self::XY(x, y, inner) => E::push_xy(*x, *y, E::shrink_xy(*x, *y, inner)), + }.render(to) + } +} + +/// Grow on each side +pub enum Outset> { + /// Increase width + X(E::Unit, T), + /// Increase height + Y(E::Unit, T), + /// Increase width and height + XY(E::Unit, E::Unit, T), +} + + +impl> Outset { + pub fn inner (&self) -> &T { + match self { + Self::X(_, i) => i, + Self::Y(_, i) => i, + Self::XY(_, _, i) => i, + } + } +} + +impl> Render for Outset { + fn min_size (&self, to: E::Size) -> Perhaps { + match *self { + Self::X(x, ref inner) => E::grow_x(x + x, inner), + Self::Y(y, ref inner) => E::grow_y(y + y, inner), + Self::XY(x, y, ref inner) => E::grow_xy(x + x, y + y, inner), + }.min_size(to) + } + fn render (&self, to: &mut E::Output) -> Usually<()> { + match *self { + Self::X(x, ref inner) => E::push_x(x, inner), + Self::Y(y, ref inner) => E::push_y(y, inner), + Self::XY(x, y, ref inner) => E::push_xy(x, y, inner), + }.render(to) + } +} diff --git a/src/space/shrink_grow.rs b/src/space/shrink_grow.rs deleted file mode 100644 index 9d900843..00000000 --- a/src/space/shrink_grow.rs +++ /dev/null @@ -1,103 +0,0 @@ -use crate::*; - -impl LayoutShrinkGrow for E {} - -pub trait LayoutShrinkGrow { - fn shrink_x > (x: E::Unit, w: W) -> Shrink { - Shrink::X(x, w) - } - fn shrink_y > (y: E::Unit, w: W) -> Shrink { - Shrink::Y(y, w) - } - fn shrink_xy > (x: E::Unit, y: E::Unit, w: W) -> Shrink { - Shrink::XY(x, y, w) - } - fn grow_x > (x: E::Unit, w: W) -> Grow { - Grow::X(x, w) - } - fn grow_y > (y: E::Unit, w: W) -> Grow { - Grow::Y(y, w) - } - fn grow_xy > (x: E::Unit, y: E::Unit, w: W) -> Grow { - Grow::XY(x, y, w) - } -} - -/// Shrink drawing area -pub enum Shrink> { - /// Decrease width - X(E::Unit, T), - /// Decrease height - Y(E::Unit, T), - /// Decrease width and height - XY(E::Unit, E::Unit, T), -} - -/// Expand drawing area -pub enum Grow> { - /// Increase width - X(E::Unit, T), - /// Increase height - Y(E::Unit, T), - /// Increase width and height - XY(E::Unit, E::Unit, T) -} - -impl> Shrink { - fn inner (&self) -> &T { - match self { - Self::X(_, i) => i, - Self::Y(_, i) => i, - Self::XY(_, _, i) => i, - } - } -} - -impl> Grow { - fn inner (&self) -> &T { - match self { - Self::X(_, i) => i, - Self::Y(_, i) => i, - Self::XY(_, _, i) => i, - } - } -} - -impl> Render for Shrink { - fn min_size (&self, to: E::Size) -> Perhaps { - Ok(self.inner().min_size(to)?.map(|to|match *self { - Self::X(w, _) => [ - if to.w() > w { to.w() - w } else { 0.into() }, - to.h() - ], - Self::Y(h, _) => [ - to.w(), - if to.h() > h { to.h() - h } else { 0.into() } - ], - Self::XY(w, h, _) => [ - if to.w() > w { to.w() - w } else { 0.into() }, - if to.h() > h { to.h() - h } else { 0.into() } - ], - }.into())) - } - fn render (&self, to: &mut E::Output) -> Usually<()> { - Ok(self.min_size(to.area().wh().into())? - .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) - .transpose()?.unwrap_or(())) - } -} - -impl> Render for Grow { - fn min_size (&self, to: E::Size) -> Perhaps { - Ok(self.inner().min_size(to)?.map(|to|match *self { - Self::X(w, _) => [to.w() + w, to.h()], - Self::Y(h, _) => [to.w(), to.h() + h], - Self::XY(w, h, _) => [to.w() + w, to.h() + h], - }.into())) - } - fn render (&self, to: &mut E::Output) -> Usually<()> { - Ok(self.min_size(to.area().wh().into())? - .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) - .transpose()?.unwrap_or(())) - } -} diff --git a/src/space/size.rs b/src/space/size.rs new file mode 100644 index 00000000..4200bdf4 --- /dev/null +++ b/src/space/size.rs @@ -0,0 +1,296 @@ +use crate::*; + +impl LayoutShrinkGrow for E {} + +pub trait LayoutShrinkGrow { + fn shrink_x > (x: E::Unit, w: W) -> Shrink { + Shrink::X(x, w) + } + fn shrink_y > (y: E::Unit, w: W) -> Shrink { + Shrink::Y(y, w) + } + fn shrink_xy > (x: E::Unit, y: E::Unit, w: W) -> Shrink { + Shrink::XY(x, y, w) + } + fn grow_x > (x: E::Unit, w: W) -> Grow { + Grow::X(x, w) + } + fn grow_y > (y: E::Unit, w: W) -> Grow { + Grow::Y(y, w) + } + fn grow_xy > (x: E::Unit, y: E::Unit, w: W) -> Grow { + Grow::XY(x, y, w) + } +} + +/// Shrink drawing area +pub enum Shrink> { + /// Decrease width + X(E::Unit, T), + /// Decrease height + Y(E::Unit, T), + /// Decrease width and height + XY(E::Unit, E::Unit, T), +} + +/// Expand drawing area +pub enum Grow> { + /// Increase width + X(E::Unit, T), + /// Increase height + Y(E::Unit, T), + /// Increase width and height + XY(E::Unit, E::Unit, T) +} + +impl> Shrink { + fn inner (&self) -> &T { + match self { + Self::X(_, i) => i, + Self::Y(_, i) => i, + Self::XY(_, _, i) => i, + } + } +} + +impl> Grow { + fn inner (&self) -> &T { + match self { + Self::X(_, i) => i, + Self::Y(_, i) => i, + Self::XY(_, _, i) => i, + } + } +} + +impl> Render for Shrink { + fn min_size (&self, to: E::Size) -> Perhaps { + Ok(self.inner().min_size(to)?.map(|to|match *self { + Self::X(w, _) => [ + if to.w() > w { to.w() - w } else { 0.into() }, + to.h() + ], + Self::Y(h, _) => [ + to.w(), + if to.h() > h { to.h() - h } else { 0.into() } + ], + Self::XY(w, h, _) => [ + if to.w() > w { to.w() - w } else { 0.into() }, + if to.h() > h { to.h() - h } else { 0.into() } + ], + }.into())) + } + fn render (&self, to: &mut E::Output) -> Usually<()> { + Ok(self.min_size(to.area().wh().into())? + .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) + .transpose()?.unwrap_or(())) + } +} + +impl> Render for Grow { + fn min_size (&self, to: E::Size) -> Perhaps { + Ok(self.inner().min_size(to)?.map(|to|match *self { + Self::X(w, _) => [to.w() + w, to.h()], + Self::Y(h, _) => [to.w(), to.h() + h], + Self::XY(w, h, _) => [to.w() + w, to.h() + h], + }.into())) + } + fn render (&self, to: &mut E::Output) -> Usually<()> { + Ok(self.min_size(to.area().wh().into())? + .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) + .transpose()?.unwrap_or(())) + } +} + +pub enum Fill> { + _Unused(PhantomData), + X(W), + Y(W), + XY(W), +} + +impl> Fill { + fn inner (&self) -> &W { + match self { + Self::X(inner) => &inner, + Self::Y(inner) => &inner, + Self::XY(inner) => &inner, + _ => unreachable!(), + } + } + pub fn w (fill: W) -> Self { + Self::X(fill) + } + pub fn h (fill: W) -> Self { + Self::Y(fill) + } + pub fn wh (fill: W) -> Self { + Self::XY(fill) + } +} + +impl> Render for Fill { + fn min_size (&self, to: E::Size) -> Perhaps { + let area = self.inner().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) + } + } + fn render (&self, to: &mut E::Output) -> Usually<()> { + self.inner().render(to) + } +} + +/// Enforce fixed size of drawing area +pub enum Fixed> { + _Unused(PhantomData), + /// Enforce fixed width + X(E::Unit, T), + /// Enforce fixed height + Y(E::Unit, T), + /// Enforce fixed width and height + XY(E::Unit, E::Unit, T), +} + +impl> Fixed { + pub fn inner (&self) -> &T { + match self { + Self::X(_, i) => i, + Self::Y(_, i) => i, + Self::XY(_, _, i) => i, + _ => unreachable!(), + } + } + pub fn w (x: E::Unit, w: T) -> Self { + Self::X(x, w) + } + pub fn h (y: E::Unit, w: T) -> Self { + Self::Y(y, w) + } + pub fn wh (x: E::Unit, y: E::Unit, w: T) -> Self { + Self::XY(x, y, w) + } +} + +impl> Render for Fixed { + fn min_size (&self, to: E::Size) -> Perhaps { + 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 }, + _ => unreachable!(), + }) + } + fn render (&self, to: &mut E::Output) -> Usually<()> { + // 🡘 🡙 ←🡙→ + if let Some(size) = self.min_size(to.area().wh().into())? { + to.render_in(to.area().clip(size).into(), self.inner()) + } else { + Ok(()) + } + } +} + +impl LayoutMinMax for E {} + +pub trait LayoutMinMax { + fn min_x > (x: E::Unit, w: W) -> Min { + Min::X(x, w) + } + fn min_y > (y: E::Unit, w: W) -> Min { + Min::Y(y, w) + } + fn min_xy > (x: E::Unit, y: E::Unit, w: W) -> Min { + Min::XY(x, y, w) + } + fn max_x > (x: E::Unit, w: W) -> Max { + Max::X(x, w) + } + fn max_y > (y: E::Unit, w: W) -> Max { + Max::Y(y, w) + } + fn max_xy > (x: E::Unit, y: E::Unit, w: W) -> Max { + Max::XY(x, y, w) + } +} + +/// Enforce minimum size of drawing area +pub enum Min> { + /// Enforce minimum width + X(E::Unit, T), + /// Enforce minimum height + Y(E::Unit, T), + /// Enforce minimum width and height + XY(E::Unit, E::Unit, T), +} + +/// Enforce maximum size of drawing area +pub enum Max> { + /// Enforce maximum width + X(E::Unit, T), + /// Enforce maximum height + Y(E::Unit, T), + /// Enforce maximum width and height + XY(E::Unit, E::Unit, T), +} + +impl> Min { + pub fn inner (&self) -> &T { + match self { + Self::X(_, i) => i, + Self::Y(_, i) => i, + Self::XY(_, _, i) => i, + } + } +} + +impl> Render for Min { + fn min_size (&self, to: E::Size) -> Perhaps { + Ok(self.inner().min_size(to)?.map(|to|match *self { + Self::X(w, _) => [to.w().max(w), to.h()], + Self::Y(h, _) => [to.w(), to.h().max(h)], + Self::XY(w, h, _) => [to.w().max(w), to.h().max(h)], + }.into())) + } + // TODO: 🡘 🡙 ←🡙→ + fn render (&self, to: &mut E::Output) -> Usually<()> { + Ok(self.min_size(to.area().wh().into())? + .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) + .transpose()?.unwrap_or(())) + } +} + +impl> Max { + fn inner (&self) -> &T { + match self { + Self::X(_, i) => i, + Self::Y(_, i) => i, + Self::XY(_, _, i) => i, + } + } +} + +impl> Render for Max { + fn min_size (&self, to: E::Size) -> Perhaps { + Ok(self.inner().min_size(to)?.map(|to|match *self { + Self::X(w, _) => [to.w().min(w), to.h()], + Self::Y(h, _) => [to.w(), to.h().min(h)], + Self::XY(w, h, _) => [to.w().min(w), to.h().min(h)], + }.into())) + } + fn render (&self, to: &mut E::Output) -> Usually<()> { + Ok(self.min_size(to.area().wh().into())? + .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) + .transpose()?.unwrap_or(())) + } +} diff --git a/src/space/split.rs b/src/space/split.rs index 73f9261d..264b49f2 100644 --- a/src/space/split.rs +++ b/src/space/split.rs @@ -138,6 +138,157 @@ impl, Y: Render> Render for Bsp { } } +pub struct Stack< + E: Engine, + F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)->Usually<()>)->Usually<()> +>(pub F, pub Direction, PhantomData); + +impl< + E: Engine, + F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)->Usually<()>)->Usually<()> +> Stack { + #[inline] pub fn new (direction: Direction, build: F) -> Self { + Self(build, direction, Default::default()) + } + #[inline] pub fn right (build: F) -> Self { + Self::new(East, build) + } + #[inline] pub fn down (build: F) -> Self { + Self::new(South, build) + } + #[inline] pub fn up (build: F) -> Self { + Self::new(North, build) + } +} + +impl Render for Stack +where + F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)->Usually<()>)->Usually<()> +{ + fn min_size (&self, to: E::Size) -> Perhaps { + match self.1 { + + South => { + let mut w: E::Unit = 0.into(); + let mut h: E::Unit = 0.into(); + (self.0)(&mut |component: &dyn Render| { + let max = to.h().minus(h); + if max > E::Unit::zero() { + let item = E::max_y(max, E::push_y(h, component)); + let size = item.min_size(to)?.map(|size|size.wh()); + if let Some([width, height]) = size { + h = h + height.into(); + w = w.max(width); + } + } + Ok(()) + })?; + Ok(Some([w, h].into())) + }, + + East => { + let mut w: E::Unit = 0.into(); + let mut h: E::Unit = 0.into(); + (self.0)(&mut |component: &dyn Render| { + let max = to.w().minus(w); + if max > E::Unit::zero() { + let item = E::max_x(max, E::push_x(h, component)); + let size = item.min_size(to)?.map(|size|size.wh()); + if let Some([width, height]) = size { + w = w + width.into(); + h = h.max(height); + } + } + Ok(()) + })?; + Ok(Some([w, h].into())) + }, + + North => { + let mut w: E::Unit = 0.into(); + let mut h: E::Unit = 0.into(); + (self.0)(&mut |component: &dyn Render| { + let max = to.h().minus(h); + if max > E::Unit::zero() { + let item = E::max_y(to.h() - h, component); + let size = item.min_size(to)?.map(|size|size.wh()); + if let Some([width, height]) = size { + h = h + height.into(); + w = w.max(width); + } + } + Ok(()) + })?; + Ok(Some([w, h].into())) + }, + + West => { + let w: E::Unit = 0.into(); + let h: E::Unit = 0.into(); + (self.0)(&mut |component: &dyn Render| { + if w < to.w() { + todo!(); + } + Ok(()) + })?; + Ok(Some([w, h].into())) + }, + } + } + + fn render (&self, to: &mut E::Output) -> Usually<()> { + let area = to.area(); + let mut w = 0.into(); + let mut h = 0.into(); + match self.1 { + South => { + (self.0)(&mut |item| { + if h < area.h() { + let item = E::max_y(area.h() - h, E::push_y(h, item)); + let show = item.min_size(area.wh().into())?.map(|s|s.wh()); + if let Some([width, height]) = show { + item.render(to)?; + h = h + height; + if width > w { w = width } + }; + } + Ok(()) + })?; + }, + East => { + (self.0)(&mut |item| { + if w < area.w() { + let item = E::max_x(area.w() - w, E::push_x(w, item)); + let show = item.min_size(area.wh().into())?.map(|s|s.wh()); + if let Some([width, height]) = show { + item.render(to)?; + w = width + w; + if height > h { h = height } + }; + } + Ok(()) + })?; + }, + North => { + (self.0)(&mut |item| { + if h < area.h() { + let show = item.min_size([area.w(), area.h().minus(h)].into())?.map(|s|s.wh()); + if let Some([width, height]) = show { + E::shrink_y(height, E::push_y(area.h() - height, item)) + .render(to)?; + h = h + height; + if width > w { w = width } + }; + } + Ok(()) + })?; + }, + _ => todo!() + }; + Ok(()) + } +} + #[cfg(test)] #[test] fn test_bsp () { // TODO } diff --git a/src/space/stack.rs b/src/space/stack.rs deleted file mode 100644 index 40a5142c..00000000 --- a/src/space/stack.rs +++ /dev/null @@ -1,153 +0,0 @@ -use crate::*; -use Direction::*; - -pub struct Stack< - E: Engine, - F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)->Usually<()>)->Usually<()> ->(pub F, pub Direction, PhantomData); - -impl< - E: Engine, - F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)->Usually<()>)->Usually<()> -> Stack { - #[inline] pub fn new (direction: Direction, build: F) -> Self { - Self(build, direction, Default::default()) - } - #[inline] pub fn right (build: F) -> Self { - Self::new(East, build) - } - #[inline] pub fn down (build: F) -> Self { - Self::new(South, build) - } - #[inline] pub fn up (build: F) -> Self { - Self::new(North, build) - } -} - -impl Render for Stack -where - F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)->Usually<()>)->Usually<()> -{ - fn min_size (&self, to: E::Size) -> Perhaps { - match self.1 { - - South => { - let mut w: E::Unit = 0.into(); - let mut h: E::Unit = 0.into(); - (self.0)(&mut |component: &dyn Render| { - let max = to.h().minus(h); - if max > E::Unit::zero() { - let item = E::max_y(max, E::push_y(h, component)); - let size = item.min_size(to)?.map(|size|size.wh()); - if let Some([width, height]) = size { - h = h + height.into(); - w = w.max(width); - } - } - Ok(()) - })?; - Ok(Some([w, h].into())) - }, - - East => { - let mut w: E::Unit = 0.into(); - let mut h: E::Unit = 0.into(); - (self.0)(&mut |component: &dyn Render| { - let max = to.w().minus(w); - if max > E::Unit::zero() { - let item = E::max_x(max, E::push_x(h, component)); - let size = item.min_size(to)?.map(|size|size.wh()); - if let Some([width, height]) = size { - w = w + width.into(); - h = h.max(height); - } - } - Ok(()) - })?; - Ok(Some([w, h].into())) - }, - - North => { - let mut w: E::Unit = 0.into(); - let mut h: E::Unit = 0.into(); - (self.0)(&mut |component: &dyn Render| { - let max = to.h().minus(h); - if max > E::Unit::zero() { - let item = E::max_y(to.h() - h, component); - let size = item.min_size(to)?.map(|size|size.wh()); - if let Some([width, height]) = size { - h = h + height.into(); - w = w.max(width); - } - } - Ok(()) - })?; - Ok(Some([w, h].into())) - }, - - West => { - let w: E::Unit = 0.into(); - let h: E::Unit = 0.into(); - (self.0)(&mut |component: &dyn Render| { - if w < to.w() { - todo!(); - } - Ok(()) - })?; - Ok(Some([w, h].into())) - }, - } - } - - fn render (&self, to: &mut E::Output) -> Usually<()> { - let area = to.area(); - let mut w = 0.into(); - let mut h = 0.into(); - match self.1 { - South => { - (self.0)(&mut |item| { - if h < area.h() { - let item = E::max_y(area.h() - h, E::push_y(h, item)); - let show = item.min_size(area.wh().into())?.map(|s|s.wh()); - if let Some([width, height]) = show { - item.render(to)?; - h = h + height; - if width > w { w = width } - }; - } - Ok(()) - })?; - }, - East => { - (self.0)(&mut |item| { - if w < area.w() { - let item = E::max_x(area.w() - w, E::push_x(w, item)); - let show = item.min_size(area.wh().into())?.map(|s|s.wh()); - if let Some([width, height]) = show { - item.render(to)?; - w = width + w; - if height > h { h = height } - }; - } - Ok(()) - })?; - }, - North => { - (self.0)(&mut |item| { - if h < area.h() { - let show = item.min_size([area.w(), area.h().minus(h)].into())?.map(|s|s.wh()); - if let Some([width, height]) = show { - E::shrink_y(height, E::push_y(area.h() - height, item)) - .render(to)?; - h = h + height; - if width > w { w = width } - }; - } - Ok(()) - })?; - }, - _ => todo!() - }; - Ok(()) - } -}