diff --git a/engine/src/engine.rs b/engine/src/engine.rs index f1e8d6a7..37909526 100644 --- a/engine/src/engine.rs +++ b/engine/src/engine.rs @@ -102,6 +102,33 @@ pub trait Area: Copy { #[inline] fn clip (&self, wh: impl Size) -> [N;4] { [self.x(), self.y(), wh.w(), wh.h()] } + #[inline] fn set_w (&self, w: N) -> [N;4] { + [self.x(), self.y(), w, self.h()] + } + #[inline] fn set_h (&self, h: N) -> [N;4] { + [self.x(), self.y(), self.w(), h] + } + fn x2 (&self) -> N { + self.x() + self.w() + } + fn y2 (&self) -> N { + self.y() + self.h() + } + #[inline] fn lrtb (&self) -> [N;4] { + [self.x(), self.x2(), self.y(), self.y2()] + } + #[inline] fn push_x (&self, x: N) -> [N;4] { + [self.x() + x, self.y(), self.w(), self.h()] + } + #[inline] fn push_y (&self, y: N) -> [N;4] { + [self.x(), self.y() + y, self.w(), self.h()] + } + #[inline] fn shrink_x (&self, x: N) -> [N;4] { + [self.x(), self.y(), self.w().minus(x), self.h()] + } + #[inline] fn shrink_y (&self, y: N) -> [N;4] { + [self.x(), self.y(), self.w(), self.h().minus(y)] + } } impl Area for (N, N, N, N) { diff --git a/engine/src/output.rs b/engine/src/output.rs index 42412e75..cfb4fa31 100644 --- a/engine/src/output.rs +++ b/engine/src/output.rs @@ -1,6 +1,16 @@ use crate::*; use std::sync::{Arc, Mutex, RwLock}; +/// Rendering target +pub trait Output { + /// Current output area + fn area (&self) -> E::Area; + /// Mutable pointer to area + fn area_mut (&mut self) -> &mut E::Area; + /// Render widget in area + fn render_in (&mut self, area: E::Area, widget: &impl Render) -> Usually<()>; +} + /// Define custom content for a struct. #[macro_export] macro_rules! render { @@ -45,11 +55,37 @@ pub trait Render: Send + Sync { } } +impl Render for &dyn Render {} +//impl Render for &mut dyn Render {} +//impl Render for Box> {} +impl> Render for &R {} +//impl> Render for &mut R {} +impl> Render for Option {} +//impl> Render for Arc {} +//impl> Render for Mutex {} +//impl> Render for RwLock {} + /// Something that can be represented by a renderable component. pub trait Content: Send + Sync { fn content (&self) -> Option>; } +//impl Content for &dyn Render {} +//impl Content for &mut dyn Render {} +//impl Content for Box> {} +impl> Content for &C { + fn content (&self) -> Option> { + (*self).content() + } +} +//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 { @@ -63,38 +99,6 @@ pub trait Content: Send + Sync { //} //} -/// Rendering target -pub trait Output { - /// Current output area - fn area (&self) -> E::Area; - /// Mutable pointer to area - fn area_mut (&mut self) -> &mut E::Area; - /// Render widget in area - fn render_in (&mut self, area: E::Area, widget: &dyn Render) -> Usually<()>; -} - -//impl Render for &dyn Render {} -//impl Render for &mut dyn Render {} -//impl Render for Box> {} -impl> Render for &R {} -//impl> Render for &mut R {} -impl> Render for Option {} -//impl> Render for Arc {} -//impl> Render for Mutex {} -//impl> Render for RwLock {} - -//impl Content for &dyn Render {} -//impl Content for &mut dyn Render {} -//impl Content for Box> {} -//impl> Content for &C {} -//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 {} - /**** diff --git a/engine/src/tui.rs b/engine/src/tui.rs index 2f1d2925..c2224a63 100644 --- a/engine/src/tui.rs +++ b/engine/src/tui.rs @@ -261,7 +261,7 @@ pub struct TuiOutput { impl Output for TuiOutput { #[inline] fn area (&self) -> [u16;4] { self.area } #[inline] fn area_mut (&mut self) -> &mut [u16;4] { &mut self.area } - #[inline] fn render_in (&mut self, area: [u16;4], widget: &dyn Render) -> Usually<()> { + #[inline] fn render_in (&mut self, area: [u16;4], widget: &impl Render) -> Usually<()> { let last = self.area(); *self.area_mut() = area; widget.render(self)?; diff --git a/layout/src/collection/layers.rs b/layout/src/collection/layers.rs index 5173dd2a..5349ef0b 100644 --- a/layout/src/collection/layers.rs +++ b/layout/src/collection/layers.rs @@ -46,7 +46,7 @@ where } fn render (&self, to: &mut E::Output) -> Usually<()> { if let Some(size) = self.min_size(to.area().wh().into())? { - (self.0)(&mut |layer|to.render_in(to.area().clip(size).into(), layer)) + (self.0)(&mut |layer|to.render_in(to.area().clip(size).into(), &layer)) } else { Ok(()) } diff --git a/layout/src/collection/split.rs b/layout/src/collection/split.rs index 8672bbb5..959d675a 100644 --- a/layout/src/collection/split.rs +++ b/layout/src/collection/split.rs @@ -27,7 +27,7 @@ impl, B: Render> Render for Split { Ok(Some(to)) } fn render (&self, to: &mut E::Output) -> Usually<()> { - let (a, b) = self.1.split_fixed(self.2, self.3); + let (a, b) = self.1.split_fixed(to.area(), self.2); Ok(if self.0 { to.render_in(a.into(), &self.4)?; to.render_in(b.into(), &self.3)?; diff --git a/layout/src/scroll.rs b/layout/src/scroll.rs new file mode 100644 index 00000000..c7b7e813 --- /dev/null +++ b/layout/src/scroll.rs @@ -0,0 +1 @@ +use crate::*; diff --git a/layout/src/space.rs b/layout/src/space.rs index 7807d5e9..9d4ec207 100644 --- a/layout/src/space.rs +++ b/layout/src/space.rs @@ -26,41 +26,6 @@ impl Direction { } } -trait AreaMod: Area { - fn x2 (&self) -> N { - self.x() + self.w() - } - fn y2 (&self) -> N { - self.y() + self.h() - } - #[inline] fn xywh (&self) -> [N;4] { - [self.x(), self.y(), self.w(), self.h()] - } - #[inline] fn lrtb (&self) -> [N;4] { - [self.x(), self.x2(), self.y(), self.y2()] - } - #[inline] fn push_x (&self, x: N) -> [N;4] { - [self.x() + x, self.y(), self.w(), self.h()] - } - #[inline] fn push_y (&self, y: N) -> [N;4] { - [self.x(), self.y() + y, self.w(), self.h()] - } - #[inline] fn shrink_x (&self, x: N) -> [N;4] { - [self.x(), self.y(), self.w().minus(x), self.h()] - } - #[inline] fn shrink_y (&self, y: N) -> [N;4] { - [self.x(), self.y(), self.w(), self.h().minus(y)] - } - #[inline] fn set_w (&self, w: N) -> [N;4] { - [self.x(), self.y(), w, self.h()] - } - #[inline] fn set_h (&self, h: N) -> [N;4] { - [self.x(), self.y(), self.w(), h] - } -} - -impl> AreaMod for A {} - pub trait HasSize { fn size (&self) -> &Measure; } @@ -81,197 +46,6 @@ pub struct Measure { pub y: Arc, } -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(())) - } -} - -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) - } -} - -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> 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(())) - } -} - impl Clone for Measure { fn clone (&self) -> Self { Self { @@ -308,6 +82,12 @@ impl Measure { } } +/// A scrollable area. +pub struct Scroll(pub F, pub Direction, pub u64, PhantomData) +where + E: Engine, + F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)->Usually<()>)->Usually<()>; + //pub trait LayoutDebug { //fn debug > (other: W) -> DebugOverlay { //DebugOverlay(Default::default(), other) diff --git a/layout/src/transform.rs b/layout/src/transform.rs index 135699d0..77a3ae93 100644 --- a/layout/src/transform.rs +++ b/layout/src/transform.rs @@ -1,308 +1,151 @@ use crate::*; -macro_rules! by_axis { - (!$Enum:ident) => { +/// Defines an enum that wraps the same renderable item +/// but discriminates on the variant which wraps it. +/// +/// It also has a special `_Unused` variant which wraps +/// the engine type using `PhantomData` to permit the +/// double generic. +macro_rules! content_enum { + ($Enum:ident: $($Variant:ident),+ $(,)?) => { + pub enum $Enum> { + _Unused(PhantomData), $($Variant(T)),+ + } + impl> Content for $Enum { + fn content (&self) -> Option> { + match self { + Self::_Unused(_) => None, + $(Self::$Variant(content) => Some(content)),+ + } + } + } + } +} + +macro_rules! transform_xy { + ($Enum:ident) => { + 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) - } + 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) } + } + } +} + +macro_rules! transform_xy_unit { + ($Enum:ident $( + |$self1:ident, $to1:ident|$min_size:expr, + |$self2:ident, $to2:ident|$render:expr + )?) => { + pub enum $Enum> { + X(E::Unit, T), Y(E::Unit, T), XY(E::Unit, E::Unit, T), } - }; - (+$Enum:ident) => { impl> $Enum { - pub fn x (x: E::Unit, item: T) -> Self { - Self::X(x, item) + pub fn x (x: E::Unit, item: T) -> Self { Self::X(x, item) } + pub fn y (y: E::Unit, item: T) -> Self { Self::Y(y, item) } + pub fn xy (x: E::Unit, y: E::Unit, item: T) -> Self { Self::XY(x, y, item) } + pub fn dx (&self) -> E::Unit { + match self { + Self::X(x, _) => *x, + Self::Y(_, _) => E::Unit::zero(), + Self::XY(x, _, _) => *x, + } } - pub fn y (y: E::Unit, item: T) -> Self { - Self::Y(y, item) - } - pub fn xy (x: E::Unit, y: E::Unit, item: T) -> Self { - Self::XY(x, y, item) + pub fn dy (&self) -> E::Unit { + match self { + Self::X(_, _) => E::Unit::zero(), + Self::Y(y, _) => *y, + Self::XY(_, y, _) => *y, + } } } - }; -} - -/// 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), -} - -by_axis!(+Shrink); - -/// 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) -} - -by_axis!(+Grow); - -pub enum Fill> { - _Unused(PhantomData), - /// Maximize width - X(W), - /// Maximize height - Y(W), - /// Maximize width and height - XY(W), -} - -by_axis!(!Fill); - -/// Enforce fixed size of drawing area -pub enum Fixed> { - /// Set width - X(E::Unit, T), - /// Set height - Y(E::Unit, T), - /// Set width and height - XY(E::Unit, E::Unit, T), -} - -by_axis!(+Fixed); - -/// 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), -} - -by_axis!(+Min); - -/// 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), -} - -by_axis!(+Max); - -/// Increment origin point of drawing area -pub enum Push> { - /// Move origin to the right - X(E::Unit, T), - /// Move origin downwards - Y(E::Unit, T), - /// Move origin to the right and downwards - XY(E::Unit, E::Unit, T), -} - -by_axis!(+Push); - -/// Decrement origin point of drawing area -pub enum Pull> { - /// Move origin to the right - X(E::Unit, T), - /// Move origin downwards - Y(E::Unit, T), - /// Move origin to the right and downwards - XY(E::Unit, E::Unit, T), -} - -by_axis!(+Pull); - -/// Shrink from each side -pub enum Padding { - /// Decrease width - X(E::Unit, T), - /// Decrease height - Y(E::Unit, T), - /// Decrease width and height - XY(E::Unit, E::Unit, T), -} - -by_axis!(+Padding); - -/// Grow on each side -pub enum Margin> { - /// Increase width - X(E::Unit, T), - /// Increase height - Y(E::Unit, T), - /// Increase width and height - XY(E::Unit, E::Unit, T), -} - -by_axis!(+Margin); - -/// A scrollable area. -pub struct Scroll(pub F, pub Direction, pub u64, PhantomData) -where - E: Engine, - F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)->Usually<()>)->Usually<()>; - -/// Override X and Y coordinates, aligning to corner, side, or center of area -pub enum Align> { - _Unused(PhantomData), - /// Draw at center of container - Center(T), - /// Draw at center of X axis - X(T), - /// Draw at center of Y axis - Y(T), - /// Draw at upper left corner of contaier - NW(T), - /// Draw at center of upper edge of container - N(T), - /// Draw at right left corner of contaier - NE(T), - /// Draw at center of left edge of container - W(T), - /// Draw at center of right edge of container - E(T), - /// Draw at lower left corner of container - SW(T), - /// Draw at center of lower edge of container - S(T), - /// Draw at lower right edge of container - SE(T) -} - -impl> Push { - pub fn inner (&self) -> &T { - use Push::*; - match self { X(_, i) => i, Y(_, i) => i, XY(_, _, i) => i, } - } - pub fn dx (&self) -> E::Unit { - use Push::*; - match self { X(x, _) => *x, Y(_, _) => E::Unit::default(), XY(x, _, _) => *x, } - } - pub fn dy (&self) -> E::Unit { - use Push::*; - match self { X(_, _) => E::Unit::default(), Y(y, _) => *y, XY(_, y, _) => *y, } - } -} - -impl> Render for Push { - fn min_size (&self, to: E::Size) -> Perhaps { - self.inner().min_size(to) - } - fn render (&self, to: &mut E::Output) -> Usually<()> { - let area = to.area(); - Ok(self.min_size(area.wh().into())? - .map(|size|to.render_in(match *self { - Self::X(x, _) => [area.x() + x, area.y(), size.w(), size.h()], - Self::Y(y, _) => [area.x(), area.y() + y, size.w(), size.h()], - Self::XY(x, y, _) => [area.x() + x, area.y() + y, size.w(), size.h()], - }.into(), self.inner())).transpose()?.unwrap_or(())) - } -} - -impl> Pull { - pub fn inner (&self) -> &T { - match self { - Self::X(_, i) => i, - Self::Y(_, i) => i, - Self::XY(_, _, i) => i, - _ => unreachable!(), - } - } - pub fn dx (&self) -> E::Unit { - match self { - Self::X(x, _) => *x, - Self::Y(_, _) => E::Unit::default(), - Self::XY(x, _, _) => *x, - _ => unreachable!(), - } - } - pub fn dy (&self) -> E::Unit { - match self { - Self::X(_, _) => E::Unit::default(), - Self::Y(y, _) => *y, - Self::XY(_, y, _) => *y, - _ => unreachable!(), + impl> Content for $Enum { + fn content (&self) -> Option> { + Some(match self { + 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 Pull { - fn min_size (&self, to: E::Size) -> Perhaps { - self.inner().min_size(to) - } - fn render (&self, to: &mut E::Output) -> Usually<()> { - let area = to.area(); - Ok(self.min_size(area.wh().into())? - .map(|size|to.render_in(match *self { - Self::X(x, _) => [area.x().minus(x), area.y(), size.w(), size.h()], - Self::Y(y, _) => [area.x(), area.y().minus(y), size.w(), size.h()], - Self::XY(x, y, _) => [area.x().minus(x), area.y().minus(y), size.w(), size.h()], - _ => unreachable!(), - }.into(), self.inner())).transpose()?.unwrap_or(())) - } -} +transform_xy!(Fill); -impl> Padding { - pub fn inner (&self) -> &T { - match self { - Self::X(_, i) => i, - Self::Y(_, i) => i, - Self::XY(_, _, i) => i, - } - } -} +transform_xy_unit!(Fixed); +transform_xy_unit!(Shrink); + +transform_xy_unit!(Expand); + +transform_xy_unit!(Min); + +transform_xy_unit!(Max); + +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!(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!(Padding); impl> Render for Padding { fn render (&self, to: &mut E::Output) -> Usually<()> { match self { - Self::X(x, inner) => Push::x(*x, Shrink::x(*x, inner)), - Self::Y(y, inner) => Push::y(*y, Shrink::y(*y, inner)), - Self::XY(x, y, inner) => Push::xy(*x, *y, Shrink::xy(*x, *y, inner)), + 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) } } - -impl> Margin { - pub fn inner (&self) -> &T { - match self { - Self::X(_, i) => i, - Self::Y(_, i) => i, - Self::XY(_, _, i) => i, - } - } -} - +transform_xy_unit!(Margin); impl> Render for Margin { fn min_size (&self, to: E::Size) -> Perhaps { match *self { - Self::X(x, ref inner) => Grow::x(x + x, inner), - Self::Y(y, ref inner) => Grow::y(y + y, inner), - Self::XY(x, y, ref inner) => Grow::xy(x + x, y + y, inner), + 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) } fn render (&self, to: &mut E::Output) -> Usually<()> { match *self { - Self::X(x, ref inner) => Push::x(x, inner), - Self::Y(y, ref inner) => Push::y(y, inner), - Self::XY(x, y, ref inner) => Push::xy(x, y, inner), + 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) } } +content_enum!(Align: Center, X, Y, NW, N, NE, E, SE, S, SW, W); impl> Align { pub fn c (w: T) -> Self { Self::Center(w) } pub fn x (w: T) -> Self { Self::X(w) } @@ -315,30 +158,14 @@ impl> Align { pub fn sw (w: T) -> Self { Self::SW(w) } pub fn ne (w: T) -> Self { Self::NE(w) } pub fn se (w: T) -> Self { Self::SE(w) } - pub fn inner (&self) -> &T { - match self { - Self::Center(inner) => inner, - Self::X(inner) => inner, - Self::Y(inner) => inner, - Self::NW(inner) => inner, - Self::N(inner) => inner, - Self::NE(inner) => inner, - Self::W(inner) => inner, - Self::E(inner) => inner, - Self::SW(inner) => inner, - Self::S(inner) => inner, - Self::SE(inner) => inner, - _ => unreachable!(), - } - } } -fn align, N: Coordinate, R: Area + From<[N;4]>> (align: &Align, outer: R, inner: R) -> Option { - if outer.w() < inner.w() || outer.h() < inner.h() { +fn align, N: Coordinate, R: Area + From<[N;4]>> (align: &Align, outer: R, content: R) -> Option { + if outer.w() < content.w() || outer.h() < content.h() { None } else { let [ox, oy, ow, oh] = outer.xywh(); - let [ix, iy, iw, ih] = inner.xywh(); + let [ix, iy, iw, ih] = content.xywh(); Some(match align { Align::Center(_) => [ox + (ow - iw) / 2.into(), oy + (oh - ih) / 2.into(), iw, ih,].into(), Align::X(_) => [ox + (ow - iw) / 2.into(), iy, iw, ih,].into(), @@ -358,15 +185,205 @@ fn align, N: Coordinate, R: Area + From<[N;4]>> (alig impl> Render for Align { fn min_size (&self, outer_area: E::Size) -> Perhaps { - self.inner().min_size(outer_area) + self.content().min_size(outer_area) } fn render (&self, to: &mut E::Output) -> Usually<()> { let outer_area = to.area(); - Ok(if let Some(inner_size) = self.min_size(outer_area.wh().into())? { - let inner_area = outer_area.clip(inner_size); - if let Some(aligned) = align(&self, outer_area.into(), inner_area.into()) { - to.render_in(aligned, self.inner())? + Ok(if let Some(content_size) = self.min_size(outer_area.wh().into())? { + let content_area = outer_area.clip(content_size); + if let Some(aligned) = align(&self, outer_area.into(), content_area.into()) { + to.render_in(aligned, &self.content())? } }) } } + +impl> Shrink { + fn inner (&self) -> &T { + match self { + Self::X(_, i) => i, + Self::Y(_, i) => i, + Self::XY(_, _, i) => i, + } + } +} + +impl> Expand { + 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 Expand { + 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(())) + } +} + +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) + } +} + +impl> Fixed { + pub fn inner (&self) -> &T { + match self { + Self::X(_, i) => i, + Self::Y(_, i) => i, + Self::XY(_, _, i) => i, + } + } + 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> 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(())) + } +}