mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 04:06:45 +01:00
wip: big flat pt.4: extract layout crate
This commit is contained in:
parent
cb680ab096
commit
34e731f111
21 changed files with 2125 additions and 83 deletions
372
layout/src/transform.rs
Normal file
372
layout/src/transform.rs
Normal 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())?
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue