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

@ -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<()> {
$render
}
impl<E: Engine, T: Render<E>> Render<E> for $Enum<E, T> {
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,151 +66,83 @@ 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,
Self::Y(_, content) => content,
Self::X(_, content) => content,
Self::Y(_, content) => content,
Self::XY(_, _, content) => content,
})
}
}
$(
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<()> {
$render
}
impl<E: Engine, T: Render<E>> Render<E> for $Enum<E, T> {
fn render (&$self, $to: &mut E::Output) -> Usually<()> {
$render
}
)?
}
}
}
transform_xy!(Fill
|self, to|{
let area = self.content().min_size(to.into())?;
if let Some(area) = area {
Ok(Some(match self {
Self::X(_) => [to.w().into(), area.h()],
Self::Y(_) => [area.w(), to.h().into()],
Self::XY(_) => [to.w().into(), to.h().into()],
_ => unreachable!(),
}.into()))
} else {
Ok(None)
}
},
|self, to|self.content().render(to));
transform_xy!(|self: Fill, to|todo!());
transform_xy_unit!(Fixed
|self, to|{
Ok(match self {
Self::X(w, _) =>
if to.w() >= *w { Some([*w, to.h()].into()) } else { None },
Self::Y(h, _) =>
if to.h() >= *h { Some([to.w(), *h].into()) } else { None },
Self::XY(w, h, _)
=> if to.w() >= *w && to.h() >= *h { Some([*w, *h].into()) } else { None },
})
},
|self, to|{
// 🡘 🡙 ←🡙→
if let Some(size) = self.min_size(to.area().wh().into())? {
to.render_in(to.area().clip(size).into(), &self.content())
} else {
Ok(())
}
});
transform_xy_unit!(|self: Fixed, to|{
let [x, y, w, h] = to.area().xywh();
to.render_in(match self {
Self::X(fw, _) => [x, y, fw, h],
Self::Y(fh, _) => [x, y, w, fh],
Self::XY(fw, fh, _) => [x, y, fw, fh],
}, self.content())
});
transform_xy_unit!(Shrink
|self, to|Ok(self.content().min_size(to)?
.map(|wh|wh.wh())
.map(|[w, h]|[
w.minus(self.dx()).into(),
h.minus(self.dy()).into()
].into())),
|self, to|Ok(self.min_size(to.area().wh().into())?
.map(|size|to.render_in(to.area().clip(size).into(), &self.content()))
.transpose()?.unwrap_or(())));
transform_xy_unit!(|self: Shrink, to|to.render_in(
[to.x(), to.y(), to.w().minus(self.dx()), to.h().minus(self.dy())],
self.content()));
transform_xy_unit!(Expand
|self, to|Ok(self.content().min_size(to)?.map(|to|match *self {
Self::X(w, _) => [to.w() + w, to.h()],
Self::Y(h, _) => [to.w(), to.h() + h],
Self::XY(w, h, _) => [to.w() + w, to.h() + h],
}.into())),
|self, to|Ok(self.min_size(to.area().wh().into())?
.map(|size|to.render_in(to.area().clip(size).into(), &self.content()))
.transpose()?.unwrap_or(())));
transform_xy_unit!(|self: Expand, to|to.render_in(
[to.x(), to.y(), to.w() + self.dx(), to.h() + self.dy()],
self.content()));
// TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small
transform_xy_unit!(|self: Min, to|to.render_in(match self {
Self::X(mw, _) => [to.x(), to.y(), to.w().max(mw), to.h()],
Self::Y(mh, _) => [to.x(), to.y(), to.w(), to.h().max(mh)],
Self::XY(mw, mh, _) => [to.x(), to.y(), to.w().max(mw), to.h().max(mh)]
}, self.content()));
transform_xy_unit!(Min
|self, to|Ok(self.content().min_size(to)?.map(|to|match *self {
Self::X(w, _) => [to.w().max(w), to.h()],
Self::Y(h, _) => [to.w(), to.h().max(h)],
Self::XY(w, h, _) => [to.w().max(w), to.h().max(h)],
}.into())),
|self, to|Ok(self.min_size(to.area().wh().into())?
.map(|size|to.render_in(to.area().clip(size).into(), &self.content()))
.transpose()?.unwrap_or(())));
transform_xy_unit!(|self: Max, to|to.render_in(match self {
Self::X(mw, _) => [to.x(), to.y(), to.w().min(mw), to.h()],
Self::Y(mh, _) => [to.x(), to.y(), to.w(), to.h().min(mh)],
Self::XY(mw, mh, _) => [to.x(), to.y(), to.w().min(mw), to.h().min(mh)],
}, self.content()));
transform_xy_unit!(Max
|self, to|Ok(self.content().min_size(to)?.map(|to|match *self {
Self::X(w, _) => [to.w().min(w), to.h()],
Self::Y(h, _) => [to.w(), to.h().min(h)],
Self::XY(w, h, _) => [to.w().min(w), to.h().min(h)],
}.into())),
|self, to|Ok(self.min_size(to.area().wh().into())?
.map(|size|to.render_in(to.area().clip(size).into(), &self.content()))
.transpose()?.unwrap_or(())));
transform_xy_unit!(|self: Push, to|to.render_in(
[to.x() + self.dx(), to.y() + self.dy(), to.w(), to.h()],
self.content()));
transform_xy_unit!(Push
|self, to|self.content().min_size(to),
|self, to|Ok(if let Some(size) = self.min_size(to.area().wh().into())? {
to.render_in([
to.area().x() + self.dx(),
to.area().y() + self.dy(),
size.w(),
size.h(),
].into(), &self.content())?;
}));
transform_xy_unit!(|self: Pull, to|to.render_in(
[to.x().minus(self.dx()), to.y().minus(self.dy()), to.w(), to.h()],
self.content()));
transform_xy_unit!(Pull
|self, to|self.content().min_size(to),
|self, to|Ok(if let Some(size) = self.min_size(to.area().wh().into())? {
to.render_in([
to.area().x().minus(self.dx()),
to.area().y().minus(self.dy()),
size.w(),
size.h(),
].into(), &self.content())?;
}));
transform_xy_unit!(|self: Margin, to|{
let dx = self.dx();
let dy = self.dy();
to.render_in([
to.x().minus(dx),
to.y().minus(dy),
to.w() + dy + dy,
to.h() + dy + dy,
])
});
transform_xy_unit!(Margin
|self, to|match *self {
Self::X(x, ref content) => Expand::x(x + x, content),
Self::Y(y, ref content) => Expand::y(y + y, content),
Self::XY(x, y, ref content) => Expand::xy(x + x, y + y, content),
}.min_size(to),
|self, to|match *self {
Self::X(x, ref content) => Push::x(x, content),
Self::Y(y, ref content) => Push::y(y, content),
Self::XY(x, y, ref content) => Push::xy(x, y, content),
}.render(to));
transform_xy_unit!(Padding);
impl<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)
}
}
transform_xy_unit!(|self: Padding, to|{
let dx = self.dx();
let dy = self.dy();
to.render_in([
to.x() + dx,
to.y() + dy,
to.w().minus(dy + dy),
to.h().minus(dy + dy),
])
});
content_enum!(Align: Center, X, Y, NW, N, NE, E, SE, S, SW, W);
impl<E: Engine, T: Render<E>> Align<E, T> {