wip: big flat pt.5: implement transforms with macro

This commit is contained in:
🪞👃🪞 2024-12-30 19:52:56 +01:00
parent 34e731f111
commit 18b2d8c48b
8 changed files with 387 additions and 558 deletions

View file

@ -102,6 +102,33 @@ pub trait Area<N: Coordinate>: Copy {
#[inline] fn clip (&self, wh: impl Size<N>) -> [N;4] { #[inline] fn clip (&self, wh: impl Size<N>) -> [N;4] {
[self.x(), self.y(), wh.w(), wh.h()] [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<N: Coordinate> Area<N> for (N, N, N, N) { impl<N: Coordinate> Area<N> for (N, N, N, N) {

View file

@ -1,6 +1,16 @@
use crate::*; use crate::*;
use std::sync::{Arc, Mutex, RwLock}; use std::sync::{Arc, Mutex, RwLock};
/// Rendering target
pub trait Output<E: Engine> {
/// 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<E>) -> Usually<()>;
}
/// Define custom content for a struct. /// Define custom content for a struct.
#[macro_export] macro_rules! render { #[macro_export] macro_rules! render {
@ -45,11 +55,37 @@ pub trait Render<E: Engine>: Send + Sync {
} }
} }
impl<E: Engine> Render<E> for &dyn Render<E> {}
//impl<E: Engine> Render<E> for &mut dyn Render<E> {}
//impl<E: Engine> Render<E> for Box<dyn Render<E>> {}
impl<E: Engine, R: Render<E>> Render<E> for &R {}
//impl<E: Engine, R: Render<E>> Render<E> for &mut R {}
impl<E: Engine, R: Render<E>> Render<E> for Option<R> {}
//impl<E: Engine, R: Render<E>> Render<E> for Arc<R> {}
//impl<E: Engine, R: Render<E>> Render<E> for Mutex<R> {}
//impl<E: Engine, R: Render<E>> Render<E> for RwLock<R> {}
/// Something that can be represented by a renderable component. /// Something that can be represented by a renderable component.
pub trait Content<E: Engine>: Send + Sync { pub trait Content<E: Engine>: Send + Sync {
fn content (&self) -> Option<impl Render<E>>; fn content (&self) -> Option<impl Render<E>>;
} }
//impl<E: Engine> Content<E> for &dyn Render<E> {}
//impl<E: Engine> Content<E> for &mut dyn Render<E> {}
//impl<E: Engine> Content<E> for Box<dyn Render<E>> {}
impl<E: Engine, C: Content<E>> Content<E> for &C {
fn content (&self) -> Option<impl Render<E>> {
(*self).content()
}
}
//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 { //impl<E: Engine, C: Content<E>> Render<E> for C {
///// Minimum size to use ///// Minimum size to use
//fn min_size (&self, to: E::Size) -> Perhaps<E::Size> { //fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
@ -63,38 +99,6 @@ pub trait Content<E: Engine>: Send + Sync {
//} //}
//} //}
/// Rendering target
pub trait Output<E: Engine> {
/// 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<E>) -> Usually<()>;
}
//impl<E: Engine> Render<E> for &dyn Render<E> {}
//impl<E: Engine> Render<E> for &mut dyn Render<E> {}
//impl<E: Engine> Render<E> for Box<dyn Render<E>> {}
impl<E: Engine, R: Render<E>> Render<E> for &R {}
//impl<E: Engine, R: Render<E>> Render<E> for &mut R {}
impl<E: Engine, R: Render<E>> Render<E> for Option<R> {}
//impl<E: Engine, R: Render<E>> Render<E> for Arc<R> {}
//impl<E: Engine, R: Render<E>> Render<E> for Mutex<R> {}
//impl<E: Engine, R: Render<E>> Render<E> for RwLock<R> {}
//impl<E: Engine> Content<E> for &dyn Render<E> {}
//impl<E: Engine> Content<E> for &mut dyn Render<E> {}
//impl<E: Engine> Content<E> for Box<dyn Render<E>> {}
//impl<E: Engine, C: Content<E>> Content<E> for &C {}
//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 {}
/**** /****

View file

@ -261,7 +261,7 @@ pub struct TuiOutput {
impl Output<Tui> for TuiOutput { impl Output<Tui> for TuiOutput {
#[inline] fn area (&self) -> [u16;4] { self.area } #[inline] fn area (&self) -> [u16;4] { self.area }
#[inline] fn area_mut (&mut self) -> &mut [u16;4] { &mut 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<Tui>) -> Usually<()> { #[inline] fn render_in (&mut self, area: [u16;4], widget: &impl Render<Tui>) -> Usually<()> {
let last = self.area(); let last = self.area();
*self.area_mut() = area; *self.area_mut() = area;
widget.render(self)?; widget.render(self)?;

View file

@ -46,7 +46,7 @@ where
} }
fn render (&self, to: &mut E::Output) -> Usually<()> { fn render (&self, to: &mut E::Output) -> Usually<()> {
if let Some(size) = self.min_size(to.area().wh().into())? { 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 { } else {
Ok(()) Ok(())
} }

View file

@ -27,7 +27,7 @@ impl<E: Engine, A: Render<E>, B: Render<E>> Render<E> for Split<E, A, B> {
Ok(Some(to)) Ok(Some(to))
} }
fn render (&self, to: &mut E::Output) -> Usually<()> { 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 { Ok(if self.0 {
to.render_in(a.into(), &self.4)?; to.render_in(a.into(), &self.4)?;
to.render_in(b.into(), &self.3)?; to.render_in(b.into(), &self.3)?;

1
layout/src/scroll.rs Normal file
View file

@ -0,0 +1 @@
use crate::*;

View file

@ -26,41 +26,6 @@ impl Direction {
} }
} }
trait AreaMod<N: Coordinate>: Area<N> {
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<N: Coordinate, A: Area<N>> AreaMod<N> for A {}
pub trait HasSize<E: Engine> { pub trait HasSize<E: Engine> {
fn size (&self) -> &Measure<E>; fn size (&self) -> &Measure<E>;
} }
@ -81,197 +46,6 @@ pub struct Measure<E: Engine> {
pub y: Arc<AtomicUsize>, pub y: Arc<AtomicUsize>,
} }
impl<E: Engine, T: Render<E>> Shrink<E, T> {
fn inner (&self) -> &T {
match self {
Self::X(_, i) => i,
Self::Y(_, i) => i,
Self::XY(_, _, i) => i,
}
}
}
impl<E: Engine, T: Render<E>> Grow<E, T> {
fn inner (&self) -> &T {
match self {
Self::X(_, i) => i,
Self::Y(_, i) => i,
Self::XY(_, _, i) => i,
}
}
}
impl<E: Engine, T: Render<E>> Render<E> for Shrink<E, T> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
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<E: Engine, T: Render<E>> Render<E> for Grow<E, T> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
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<E: Engine, W: Render<E>> Fill<E, W> {
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<E: Engine, W: Render<E>> Render<E> for Fill<E, W> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
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<E: Engine, T: Render<E>> Fixed<E, T> {
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<E: Engine, T: Render<E>> Render<E> for Fixed<E, T> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
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<E: Engine, T: Render<E>> Min<E, T> {
pub fn inner (&self) -> &T {
match self {
Self::X(_, i) => i,
Self::Y(_, i) => i,
Self::XY(_, _, i) => i,
}
}
}
impl<E: Engine, T: Render<E>> Render<E> for Min<E, T> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
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<E: Engine, T: Render<E>> Max<E, T> {
fn inner (&self) -> &T {
match self {
Self::X(_, i) => i,
Self::Y(_, i) => i,
Self::XY(_, _, i) => i,
}
}
}
impl<E: Engine, T: Render<E>> Render<E> for Max<E, T> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
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<E: Engine> Clone for Measure<E> { impl<E: Engine> Clone for Measure<E> {
fn clone (&self) -> Self { fn clone (&self) -> Self {
Self { Self {
@ -308,6 +82,12 @@ impl<E: Engine> Measure<E> {
} }
} }
/// A scrollable area.
pub struct Scroll<E, F>(pub F, pub Direction, pub u64, PhantomData<E>)
where
E: Engine,
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)->Usually<()>)->Usually<()>;
//pub trait LayoutDebug<E: Engine> { //pub trait LayoutDebug<E: Engine> {
//fn debug <W: Render<E>> (other: W) -> DebugOverlay<E, W> { //fn debug <W: Render<E>> (other: W) -> DebugOverlay<E, W> {
//DebugOverlay(Default::default(), other) //DebugOverlay(Default::default(), other)

View file

@ -1,308 +1,151 @@
use crate::*; use crate::*;
macro_rules! by_axis { /// Defines an enum that wraps the same renderable item
(!$Enum:ident) => { /// but discriminates on the variant which wraps it.
impl<E: Engine, T: Render<E>> $Enum<E, T> { ///
pub fn x (item: T) -> Self { /// It also has a special `_Unused` variant which wraps
Self::X(item) /// the engine type using `PhantomData` to permit the
/// double generic.
macro_rules! content_enum {
($Enum:ident: $($Variant:ident),+ $(,)?) => {
pub enum $Enum<E: Engine, T: Render<E>> {
_Unused(PhantomData<E>), $($Variant(T)),+
} }
pub fn y (item: T) -> Self { impl<E: Engine, T: Render<E>> Content<E> for $Enum<E, T> {
Self::Y(item) fn content (&self) -> Option<impl Render<E>> {
}
pub fn xy (item: T) -> Self {
Self::XY(item)
}
}
};
(+$Enum:ident) => {
impl<E: Engine, T: Render<E>> $Enum<E, T> {
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)
}
}
};
}
/// Shrink drawing area
pub enum Shrink<E: Engine, T: Render<E>> {
/// 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<E: Engine, T: Render<E>> {
/// 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<E: Engine, W: Render<E>> {
_Unused(PhantomData<E>),
/// 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<E: Engine, T: Render<E>> {
/// 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<E: Engine, T: Render<E>> {
/// 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<E: Engine, T: Render<E>> {
/// 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<E: Engine, T: Render<E>> {
/// 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<E: Engine, T: Render<E>> {
/// 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<E: Engine, T> {
/// 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<E: Engine, T: Render<E>> {
/// 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<E, F>(pub F, pub Direction, pub u64, PhantomData<E>)
where
E: Engine,
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)->Usually<()>)->Usually<()>;
/// Override X and Y coordinates, aligning to corner, side, or center of area
pub enum Align<E: Engine, T: Render<E>> {
_Unused(PhantomData<E>),
/// 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<E: Engine, T: Render<E>> Push<E, T> {
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<E: Engine, T: Render<E>> Render<E> for Push<E, T> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
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<E: Engine, T: Render<E>> Pull<E, T> {
pub fn inner (&self) -> &T {
match self { match self {
Self::X(_, i) => i, Self::_Unused(_) => None,
Self::Y(_, i) => i, $(Self::$Variant(content) => Some(content)),+
Self::XY(_, _, i) => i,
_ => unreachable!(),
} }
} }
}
}
}
macro_rules! transform_xy {
($Enum:ident) => {
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) }
}
}
}
macro_rules! transform_xy_unit {
($Enum:ident $(
|$self1:ident, $to1:ident|$min_size:expr,
|$self2:ident, $to2: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),
}
impl<E: Engine, T: Render<E>> $Enum<E, T> {
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 { pub fn dx (&self) -> E::Unit {
match self { match self {
Self::X(x, _) => *x, Self::X(x, _) => *x,
Self::Y(_, _) => E::Unit::default(), Self::Y(_, _) => E::Unit::zero(),
Self::XY(x, _, _) => *x, Self::XY(x, _, _) => *x,
_ => unreachable!(),
} }
} }
pub fn dy (&self) -> E::Unit { pub fn dy (&self) -> E::Unit {
match self { match self {
Self::X(_, _) => E::Unit::default(), Self::X(_, _) => E::Unit::zero(),
Self::Y(y, _) => *y, Self::Y(y, _) => *y,
Self::XY(_, y, _) => *y, Self::XY(_, y, _) => *y,
_ => unreachable!(),
} }
} }
} }
impl<E: Engine, T: Render<E>> Content<E> for $Enum<E, T> {
impl<E: Engine, T: Render<E>> Render<E> for Pull<E, T> { fn content (&self) -> Option<impl Render<E>> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> { Some(match self {
self.inner().min_size(to) Self::X(_, content) => content,
Self::Y(_, content) => content,
Self::XY(_, _, content) => content,
})
} }
fn render (&self, to: &mut E::Output) -> Usually<()> { }
let area = to.area(); $(
Ok(self.min_size(area.wh().into())? impl<E: Engine, T: Render<E>> Render<E> for $Enum<E, T> {
.map(|size|to.render_in(match *self { fn min_size (&$self1, $to1: E::Size) -> Perhaps<E::Size> {
Self::X(x, _) => [area.x().minus(x), area.y(), size.w(), size.h()], $min_size
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()], fn render (&$self2, $to2: &mut E::Output) -> Usually<()> {
_ => unreachable!(), $render
}.into(), self.inner())).transpose()?.unwrap_or(())) }
}
)?
} }
} }
impl<E: Engine, T: Render<E>> Padding<E, T> { transform_xy!(Fill);
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<E: Engine, T: Render<E>> Render<E> for Padding<E, T> { impl<E: Engine, T: Render<E>> Render<E> for Padding<E, T> {
fn render (&self, to: &mut E::Output) -> Usually<()> { fn render (&self, to: &mut E::Output) -> Usually<()> {
match self { match self {
Self::X(x, inner) => Push::x(*x, Shrink::x(*x, inner)), Self::X(x, content) => Push::x(*x, Shrink::x(*x, content)),
Self::Y(y, inner) => Push::y(*y, Shrink::y(*y, inner)), Self::Y(y, content) => Push::y(*y, Shrink::y(*y, content)),
Self::XY(x, y, inner) => Push::xy(*x, *y, Shrink::xy(*x, *y, inner)), Self::XY(x, y, content) => Push::xy(*x, *y, Shrink::xy(*x, *y, content)),
}.render(to) }.render(to)
} }
} }
transform_xy_unit!(Margin);
impl<E: Engine, T: Render<E>> Margin<E, T> {
pub fn inner (&self) -> &T {
match self {
Self::X(_, i) => i,
Self::Y(_, i) => i,
Self::XY(_, _, i) => i,
}
}
}
impl<E: Engine, T: Render<E>> Render<E> for Margin<E, T> { impl<E: Engine, T: Render<E>> Render<E> for Margin<E, T> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> { fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
match *self { match *self {
Self::X(x, ref inner) => Grow::x(x + x, inner), Self::X(x, ref content) => Expand::x(x + x, content),
Self::Y(y, ref inner) => Grow::y(y + y, inner), Self::Y(y, ref content) => Expand::y(y + y, content),
Self::XY(x, y, ref inner) => Grow::xy(x + x, y + y, inner), Self::XY(x, y, ref content) => Expand::xy(x + x, y + y, content),
}.min_size(to) }.min_size(to)
} }
fn render (&self, to: &mut E::Output) -> Usually<()> { fn render (&self, to: &mut E::Output) -> Usually<()> {
match *self { match *self {
Self::X(x, ref inner) => Push::x(x, inner), Self::X(x, ref content) => Push::x(x, content),
Self::Y(y, ref inner) => Push::y(y, inner), Self::Y(y, ref content) => Push::y(y, content),
Self::XY(x, y, ref inner) => Push::xy(x, y, inner), Self::XY(x, y, ref content) => Push::xy(x, y, content),
}.render(to) }.render(to)
} }
} }
content_enum!(Align: Center, X, Y, NW, N, NE, E, SE, S, SW, W);
impl<E: Engine, T: Render<E>> Align<E, T> { impl<E: Engine, T: Render<E>> Align<E, T> {
pub fn c (w: T) -> Self { Self::Center(w) } pub fn c (w: T) -> Self { Self::Center(w) }
pub fn x (w: T) -> Self { Self::X(w) } pub fn x (w: T) -> Self { Self::X(w) }
@ -315,30 +158,14 @@ impl<E: Engine, T: Render<E>> Align<E, T> {
pub fn sw (w: T) -> Self { Self::SW(w) } pub fn sw (w: T) -> Self { Self::SW(w) }
pub fn ne (w: T) -> Self { Self::NE(w) } pub fn ne (w: T) -> Self { Self::NE(w) }
pub fn se (w: T) -> Self { Self::SE(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<E: Engine, T: Render<E>, N: Coordinate, R: Area<N> + From<[N;4]>> (align: &Align<E, T>, outer: R, inner: R) -> Option<R> { fn align<E: Engine, T: Render<E>, N: Coordinate, R: Area<N> + From<[N;4]>> (align: &Align<E, T>, outer: R, content: R) -> Option<R> {
if outer.w() < inner.w() || outer.h() < inner.h() { if outer.w() < content.w() || outer.h() < content.h() {
None None
} else { } else {
let [ox, oy, ow, oh] = outer.xywh(); let [ox, oy, ow, oh] = outer.xywh();
let [ix, iy, iw, ih] = inner.xywh(); let [ix, iy, iw, ih] = content.xywh();
Some(match align { Some(match align {
Align::Center(_) => [ox + (ow - iw) / 2.into(), oy + (oh - ih) / 2.into(), iw, ih,].into(), 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(), Align::X(_) => [ox + (ow - iw) / 2.into(), iy, iw, ih,].into(),
@ -358,15 +185,205 @@ fn align<E: Engine, T: Render<E>, N: Coordinate, R: Area<N> + From<[N;4]>> (alig
impl<E: Engine, T: Render<E>> Render<E> for Align<E, T> { impl<E: Engine, T: Render<E>> Render<E> for Align<E, T> {
fn min_size (&self, outer_area: E::Size) -> Perhaps<E::Size> { fn min_size (&self, outer_area: E::Size) -> Perhaps<E::Size> {
self.inner().min_size(outer_area) self.content().min_size(outer_area)
} }
fn render (&self, to: &mut E::Output) -> Usually<()> { fn render (&self, to: &mut E::Output) -> Usually<()> {
let outer_area = to.area(); let outer_area = to.area();
Ok(if let Some(inner_size) = self.min_size(outer_area.wh().into())? { Ok(if let Some(content_size) = self.min_size(outer_area.wh().into())? {
let inner_area = outer_area.clip(inner_size); let content_area = outer_area.clip(content_size);
if let Some(aligned) = align(&self, outer_area.into(), inner_area.into()) { if let Some(aligned) = align(&self, outer_area.into(), content_area.into()) {
to.render_in(aligned, self.inner())? to.render_in(aligned, &self.content())?
} }
}) })
} }
} }
impl<E: Engine, T: Render<E>> Shrink<E, T> {
fn inner (&self) -> &T {
match self {
Self::X(_, i) => i,
Self::Y(_, i) => i,
Self::XY(_, _, i) => i,
}
}
}
impl<E: Engine, T: Render<E>> Expand<E, T> {
fn inner (&self) -> &T {
match self {
Self::X(_, i) => i,
Self::Y(_, i) => i,
Self::XY(_, _, i) => i,
}
}
}
impl<E: Engine, T: Render<E>> Render<E> for Shrink<E, T> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
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<E: Engine, T: Render<E>> Render<E> for Expand<E, T> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
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<E: Engine, W: Render<E>> Fill<E, W> {
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<E: Engine, W: Render<E>> Render<E> for Fill<E, W> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
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<E: Engine, T: Render<E>> Fixed<E, T> {
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<E: Engine, T: Render<E>> Render<E> for Fixed<E, T> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
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<E: Engine, T: Render<E>> Min<E, T> {
pub fn inner (&self) -> &T {
match self {
Self::X(_, i) => i,
Self::Y(_, i) => i,
Self::XY(_, _, i) => i,
}
}
}
impl<E: Engine, T: Render<E>> Render<E> for Min<E, T> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
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<E: Engine, T: Render<E>> Max<E, T> {
fn inner (&self) -> &T {
match self {
Self::X(_, i) => i,
Self::Y(_, i) => i,
Self::XY(_, _, i) => i,
}
}
}
impl<E: Engine, T: Render<E>> Render<E> for Max<E, T> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
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(()))
}
}