wip: big flat pt.4: extract layout crate

This commit is contained in:
🪞👃🪞 2024-12-30 19:07:43 +01:00
parent cb680ab096
commit 34e731f111
21 changed files with 2125 additions and 83 deletions

372
layout/src/transform.rs Normal file
View file

@ -0,0 +1,372 @@
use crate::*;
macro_rules! by_axis {
(!$Enum:ident) => {
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)
}
}
};
(+$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 {
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<E: Engine, T: Render<E>> Render<E> for Pull<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().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(()))
}
}
impl<E: Engine, T: Render<E>> Padding<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 Padding<E, T> {
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)),
}.render(to)
}
}
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> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
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),
}.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),
}.render(to)
}
}
impl<E: Engine, T: Render<E>> Align<E, T> {
pub fn c (w: T) -> Self { Self::Center(w) }
pub fn x (w: T) -> Self { Self::X(w) }
pub fn y (w: T) -> Self { Self::Y(w) }
pub fn n (w: T) -> Self { Self::N(w) }
pub fn s (w: T) -> Self { Self::S(w) }
pub fn e (w: T) -> Self { Self::E(w) }
pub fn w (w: T) -> Self { Self::W(w) }
pub fn nw (w: T) -> Self { Self::NW(w) }
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<E: Engine, T: Render<E>, N: Coordinate, R: Area<N> + From<[N;4]>> (align: &Align<E, T>, outer: R, inner: R) -> Option<R> {
if outer.w() < inner.w() || outer.h() < inner.h() {
None
} else {
let [ox, oy, ow, oh] = outer.xywh();
let [ix, iy, iw, ih] = inner.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(),
Align::Y(_) => [ix, oy + (oh - ih) / 2.into(), iw, ih,].into(),
Align::NW(_) => [ox, oy, iw, ih,].into(),
Align::N(_) => [ox + (ow - iw) / 2.into(), oy, iw, ih,].into(),
Align::NE(_) => [ox + ow - iw, oy, iw, ih,].into(),
Align::W(_) => [ox, oy + (oh - ih) / 2.into(), iw, ih,].into(),
Align::E(_) => [ox + ow - iw, oy + (oh - ih) / 2.into(), iw, ih,].into(),
Align::SW(_) => [ox, oy + oh - ih, iw, ih,].into(),
Align::S(_) => [ox + (ow - iw) / 2.into(), oy + oh - ih, iw, ih,].into(),
Align::SE(_) => [ox + ow - iw, oy + oh - ih, iw, ih,].into(),
_ => unreachable!()
})
}
}
impl<E: Engine, T: Render<E>> Render<E> for Align<E, T> {
fn min_size (&self, outer_area: E::Size) -> Perhaps<E::Size> {
self.inner().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())?
}
})
}
}