mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-08 20:56:43 +01:00
still dark; refactor and document layout crate
This commit is contained in:
parent
ed72ab1635
commit
675d376100
12 changed files with 510 additions and 659 deletions
|
|
@ -2,3 +2,19 @@
|
||||||
|
|
||||||
this crate exposes several layout operators
|
this crate exposes several layout operators
|
||||||
which are generic over `tek_engine::Engine`.
|
which are generic over `tek_engine::Engine`.
|
||||||
|
|
||||||
|
* `Fill` makes the content's dimension equal to the container's.
|
||||||
|
* `Fixed` assigns a fixed dimension to its content.
|
||||||
|
* `Shrink` reduces the dimension of the content
|
||||||
|
* `Expand` increases the dimension of the content
|
||||||
|
* `Min` enforces minimum dimension for the content
|
||||||
|
* `Max` enforces maximum dimension for the content
|
||||||
|
* `Push` moves the content in the positive direction
|
||||||
|
* `Pull` moves the content in the negative direction
|
||||||
|
* `Margin` grows each dimension from both ends
|
||||||
|
* `Padding` shrinks each dimension from both ends
|
||||||
|
* `Align` pins the content along an axis of the container
|
||||||
|
* `When` renders a content conditionally
|
||||||
|
* `Either` alternates between two contents
|
||||||
|
* `Map` transforms each content
|
||||||
|
* `Reduce` transforms all contents into one
|
||||||
|
|
|
||||||
76
layout/src/align.rs
Normal file
76
layout/src/align.rs
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Copy, Clone)]
|
||||||
|
pub enum Alignment { #[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W }
|
||||||
|
|
||||||
|
pub struct Align<E: Engine, T: Content<E>>(Alignment, T, PhantomData<E>);
|
||||||
|
|
||||||
|
impl<E: Engine, T: Content<E>> Align<E, T> {
|
||||||
|
pub fn c (w: T) -> Self { Self(Alignment::Center, w, Default::default()) }
|
||||||
|
pub fn x (w: T) -> Self { Self(Alignment::X, w, Default::default()) }
|
||||||
|
pub fn y (w: T) -> Self { Self(Alignment::Y, w, Default::default()) }
|
||||||
|
pub fn n (w: T) -> Self { Self(Alignment::N, w, Default::default()) }
|
||||||
|
pub fn s (w: T) -> Self { Self(Alignment::S, w, Default::default()) }
|
||||||
|
pub fn e (w: T) -> Self { Self(Alignment::E, w, Default::default()) }
|
||||||
|
pub fn w (w: T) -> Self { Self(Alignment::W, w, Default::default()) }
|
||||||
|
pub fn nw (w: T) -> Self { Self(Alignment::NW, w, Default::default()) }
|
||||||
|
pub fn sw (w: T) -> Self { Self(Alignment::SW, w, Default::default()) }
|
||||||
|
pub fn ne (w: T) -> Self { Self(Alignment::NE, w, Default::default()) }
|
||||||
|
pub fn se (w: T) -> Self { Self(Alignment::SE, w, Default::default()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine, T: Content<E>> Content<E> for Align<E, T> {
|
||||||
|
fn area (&self, outer: E::Area) -> E::Area {
|
||||||
|
align_areas(self.0, outer.xywh(), Content::area(&self.content(), outer).xywh()).into()
|
||||||
|
}
|
||||||
|
fn render (&self, render: &mut E::Output) {
|
||||||
|
render.place(self.area(render.area()), &self.content())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn align_areas<N: Coordinate>(alignment: Alignment, on: [N;4], it: [N;4]) -> [N;4] {
|
||||||
|
let [cfx, cfy] = on.center();
|
||||||
|
let [cmx, cmy] = it.center();
|
||||||
|
let center = |cf, cm, m: N|if cf >= cm { m + (cf - cm) } else { m.minus(cm - cf) };
|
||||||
|
let center_x = center(cfx, cmx, it.x());
|
||||||
|
let center_y = center(cfy, cmy, it.y());
|
||||||
|
let east_x = on.x() + on.w().minus(it.w());
|
||||||
|
let south_y = on.y() + on.h().minus(it.h());
|
||||||
|
match alignment {
|
||||||
|
Alignment::Center => [center_x, center_y, it.w(), it.h()],
|
||||||
|
Alignment::X => [center_x, it.y(), it.w(), it.h()],
|
||||||
|
Alignment::Y => [it.x(), center_y, it.w(), it.h()],
|
||||||
|
|
||||||
|
Alignment::NW => [on.x(), on.y(), it.w(), it.h()],
|
||||||
|
Alignment::N => [center_x, on.y(), it.w(), it.h()],
|
||||||
|
Alignment::NE => [east_x, on.y(), it.w(), it.h()],
|
||||||
|
Alignment::E => [east_x, center_y, it.w(), it.h()],
|
||||||
|
Alignment::SE => [east_x, south_y, it.w(), it.h()],
|
||||||
|
Alignment::S => [center_x, south_y, it.w(), it.h()],
|
||||||
|
Alignment::SW => [on.x(), south_y, it.w(), it.h()],
|
||||||
|
Alignment::W => [on.x(), center_y, it.w(), it.h()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//fn align<E: Engine, T: Content<E>, N: Coordinate, R: Area<N> + From<[N;4]>> (align: &Align<E, T>, outer: R, content: R) -> Option<R> {
|
||||||
|
//if outer.w() < content.w() || outer.h() < content.h() {
|
||||||
|
//None
|
||||||
|
//} else {
|
||||||
|
//let [ox, oy, ow, oh] = outer.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(),
|
||||||
|
//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!()
|
||||||
|
//})
|
||||||
|
//}
|
||||||
|
//}
|
||||||
144
layout/src/bsp.rs
Normal file
144
layout/src/bsp.rs
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
/// Renders multiple things on top of each other,
|
||||||
|
#[macro_export] macro_rules! lay {
|
||||||
|
($($expr:expr),* $(,)?) => {{
|
||||||
|
let bsp = ();
|
||||||
|
$(let bsp = Bsp::b(bsp, $expr);)*;
|
||||||
|
bsp
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stack southward.
|
||||||
|
#[macro_export] macro_rules! col {
|
||||||
|
($($expr:expr),* $(,)?) => {{
|
||||||
|
let bsp = ();
|
||||||
|
$(let bsp = Bsp::s(bsp, $expr);)*;
|
||||||
|
bsp
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stack northward.
|
||||||
|
#[macro_export] macro_rules! col_up {
|
||||||
|
($($expr:expr),* $(,)?) => {{
|
||||||
|
let bsp = ();
|
||||||
|
$(let bsp = Bsp::n(bsp, $expr);)*;
|
||||||
|
bsp
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stack eastward.
|
||||||
|
#[macro_export] macro_rules! row {
|
||||||
|
($($expr:expr),* $(,)?) => {{
|
||||||
|
let bsp = ();
|
||||||
|
$(let bsp = Bsp::e(bsp, $expr);)*;
|
||||||
|
bsp
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Bsp<E: Engine, X: Content<E>, Y: Content<E>> {
|
||||||
|
/// X is north of Y
|
||||||
|
North(Option<f64>, Option<X>, Option<Y>),
|
||||||
|
/// X is south of Y
|
||||||
|
South(Option<f64>, Option<X>, Option<Y>),
|
||||||
|
/// X is east of Y
|
||||||
|
East(Option<f64>, Option<X>, Option<Y>),
|
||||||
|
/// X is west of Y
|
||||||
|
West(Option<f64>, Option<X>, Option<Y>),
|
||||||
|
/// X is above Y
|
||||||
|
Above(Option<X>, Option<Y>),
|
||||||
|
/// X is below Y
|
||||||
|
Below(Option<X>, Option<Y>),
|
||||||
|
/// Should be avoided.
|
||||||
|
Null(PhantomData<E>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine, X: Content<E>, Y: Content<E>> Bsp<E, X, Y> {
|
||||||
|
pub fn n (x: X, y: Y) -> Self { Self::North(None, Some(x), Some(y)) }
|
||||||
|
pub fn s (x: X, y: Y) -> Self { Self::South(None, Some(x), Some(y)) }
|
||||||
|
pub fn e (x: X, y: Y) -> Self { Self::East(None, Some(x), Some(y)) }
|
||||||
|
pub fn w (x: X, y: Y) -> Self { Self::West(None, Some(x), Some(y)) }
|
||||||
|
pub fn a (x: X, y: Y) -> Self { Self::Above(Some(x), Some(y)) }
|
||||||
|
pub fn b (x: X, y: Y) -> Self { Self::Below(Some(x), Some(y)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine, X: Content<E>, Y: Content<E>> Default for Bsp<E, X, Y> {
|
||||||
|
fn default () -> Self {
|
||||||
|
Self::Null(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Engine, X: Content<E>, Y: Content<E>> Content<E> for Bsp<E, X, Y> {
|
||||||
|
fn area (&self, outer: E::Area) -> E::Area {
|
||||||
|
match self {
|
||||||
|
Self::Null(_) => [0.into(), 0.into(), 0.into(), 0.into()].into(),
|
||||||
|
Self::North(_, a, b) => {
|
||||||
|
let a = a.area(outer);
|
||||||
|
let b = b.area(North.split_fixed(outer, a.y() + a.h()).1.into());
|
||||||
|
[a.x().min(b.x()), a.y().min(b.y()), a.w().max(b.w()), a.h() + b.h()].into()
|
||||||
|
}
|
||||||
|
Self::South(_, a, b) => {
|
||||||
|
let a = a.area(outer);
|
||||||
|
let b = b.area(South.split_fixed(outer, a.y() + a.h()).1.into());
|
||||||
|
[a.x().min(b.x()), a.y().min(b.y()), a.w().max(b.w()), a.h() + b.h()].into()
|
||||||
|
},
|
||||||
|
Self::East(_, a, b) => {
|
||||||
|
let a = a.area(outer);
|
||||||
|
let b = b.area(East.split_fixed(outer, a.x() + a.w()).1.into());
|
||||||
|
[a.x().min(b.x()), a.y().min(b.y()), a.w() + b.w(), a.h().max(b.h())].into()
|
||||||
|
},
|
||||||
|
Self::West(_, a, b) => {
|
||||||
|
let a = a.area(outer);
|
||||||
|
let b = b.area(West.split_fixed(outer, a.x() + a.w()).1.into());
|
||||||
|
[a.x().min(b.x()), a.y().min(b.y()), a.w() + b.w(), a.h().max(b.h())].into()
|
||||||
|
},
|
||||||
|
Self::Above(a, b) | Self::Below(a, b) => {
|
||||||
|
let a = a.area(outer);
|
||||||
|
let b = b.area(outer);
|
||||||
|
[a.x().min(b.x()), a.y().min(b.y()), a.w().max(b.w()), a.h().max(b.h())].into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn render (&self, to: &mut E::Output) {
|
||||||
|
let area = to.area().clone();
|
||||||
|
match self {
|
||||||
|
Self::North(_, a, b) => {
|
||||||
|
let area_a = a.area(area);
|
||||||
|
let area_b = b.area(North.split_fixed(area, area_a.y() + area_a.h()).1.into());
|
||||||
|
to.place(area_a, a);
|
||||||
|
to.place(area_b, b);
|
||||||
|
},
|
||||||
|
Self::South(_, a, b) => {
|
||||||
|
let area_a = a.area(area).clone();
|
||||||
|
let area_b = b.area(South.split_fixed(area, area_a.y() + area_a.h()).1.into()).clone();
|
||||||
|
to.place(area_a, a);
|
||||||
|
to.place(area_b, b);
|
||||||
|
},
|
||||||
|
Self::East(_, a, b) => {
|
||||||
|
let area_a = a.area(area);
|
||||||
|
let area_b = b.area(East.split_fixed(area, area_a.x() + area_a.w()).1.into());
|
||||||
|
to.place(area_a, a);
|
||||||
|
to.place(area_b, b);
|
||||||
|
},
|
||||||
|
Self::West(_, a, b) => {
|
||||||
|
let area_a = a.area(area);
|
||||||
|
let area_b = b.area(West.split_fixed(area, area_a.x() + area_a.w()).1.into());
|
||||||
|
to.place(area_a, a);
|
||||||
|
to.place(area_b, b);
|
||||||
|
},
|
||||||
|
Self::Above(a, b) => {
|
||||||
|
let area_a = a.area(area);
|
||||||
|
let area_b = b.area(area);
|
||||||
|
to.place(area_b, b);
|
||||||
|
to.place(area_a, a);
|
||||||
|
},
|
||||||
|
Self::Below(a, b) => {
|
||||||
|
let area_a = a.area(area);
|
||||||
|
let area_b = b.area(area);
|
||||||
|
to.place(area_a, a);
|
||||||
|
to.place(area_b, b);
|
||||||
|
},
|
||||||
|
Self::Null(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,88 +1,14 @@
|
||||||
//! Groupings of elements.
|
//! Groupings of elements.
|
||||||
|
|
||||||
//mod split; pub use self::split::*;
|
|
||||||
//mod stack; pub use self::stack::*;
|
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
use std::sync::RwLock;
|
/// A function or closure that emits renderables.
|
||||||
|
pub trait Collector<E: Engine>: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)) {}
|
||||||
|
|
||||||
impl<E: Engine> Layout<E> for E {}
|
/// Any function or closure that emits renderables for the given engine matches [CollectCallback].
|
||||||
|
impl<E, F> Collector<E> for F
|
||||||
|
where E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)) {}
|
||||||
|
|
||||||
pub trait Layout<E: Engine> {
|
pub trait Render<E: Engine> {
|
||||||
/// Content `item` when `cond` is true.
|
|
||||||
fn when <A: Content<E>> (cond: bool, item: A) -> When<E, A> {
|
|
||||||
When(cond, item, Default::default())
|
|
||||||
}
|
|
||||||
/// Content `item` if `cond` is true, otherwise render `other`.
|
|
||||||
fn either <A: Content<E>, B: Content<E>> (cond: bool, a: A, b: B)
|
|
||||||
-> Either<E, A, B>
|
|
||||||
{
|
|
||||||
Either(cond, a, b, Default::default())
|
|
||||||
}
|
|
||||||
/// Maps an [Option<T>] through a callback `F`
|
|
||||||
fn opt <A, F: Fn(A)->R, R: Content<E>> (option: Option<A>, cb: F)
|
|
||||||
-> Opt<E, A, F, R>
|
|
||||||
{
|
|
||||||
Opt(option, cb, Default::default())
|
|
||||||
}
|
|
||||||
fn map <T, I, R, F>(iterator: I, callback: F) -> Map<E, T, I, R, F> where
|
|
||||||
E: Engine,
|
|
||||||
I: Iterator<Item = T> + Send + Sync,
|
|
||||||
R: Content<E>,
|
|
||||||
F: Fn(T, usize)->R + Send + Sync
|
|
||||||
{
|
|
||||||
Map(Default::default(), RwLock::new(iterator), callback)
|
|
||||||
}
|
|
||||||
//pub fn reduce <E, T, I, R, F>(iterator: I, callback: F) -> Reduce<E, T, I, R, F> where
|
|
||||||
//E: Engine,
|
|
||||||
//I: Iterator<Item = T> + Send + Sync,
|
|
||||||
//R: Content<E>,
|
|
||||||
//F: Fn(R, T, usize) -> R + Send + Sync
|
|
||||||
//{
|
|
||||||
//Reduce(Default::default(), iterator, callback)
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Opt<E: Engine, A, F: Fn(A)->R, R: Content<E>>(Option<A>, F, PhantomData<E>);
|
|
||||||
|
|
||||||
/// Contents `self.1` when `self.0` is true.
|
|
||||||
pub struct When<E: Engine, A>(bool, A, PhantomData<E>);
|
|
||||||
|
|
||||||
impl<E: Engine, A: Content<E>> Content<E> for When<E, A> {
|
|
||||||
fn area (&self, to: E::Area) -> E::Area {
|
|
||||||
let Self(cond, item, ..) = self;
|
|
||||||
let mut area = E::Area::zero();
|
|
||||||
if *cond {
|
|
||||||
let item_area = item.area(to);
|
|
||||||
area[0] = item_area.x();
|
|
||||||
area[1] = item_area.y();
|
|
||||||
area[2] = item_area.w();
|
|
||||||
area[3] = item_area.h();
|
|
||||||
}
|
|
||||||
area.into()
|
|
||||||
}
|
|
||||||
fn render (&self, to: &mut E::Output) {
|
|
||||||
let Self(cond, item, ..) = self;
|
|
||||||
if *cond { item.render(to) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Contents `self.1` when `self.0` is true, otherwise renders `self.2`
|
|
||||||
pub struct Either<E: Engine, A, B>(bool, A, B, PhantomData<E>);
|
|
||||||
|
|
||||||
impl<E: Engine, A: Content<E>, B: Content<E>> Content<E> for Either<E, A, B> {
|
|
||||||
fn area (&self, to: E::Area) -> E::Area {
|
|
||||||
let Self(cond, a, b, ..) = self;
|
|
||||||
if *cond { a.area(to) } else { b.area(to) }
|
|
||||||
}
|
|
||||||
fn render (&self, to: &mut E::Output) {
|
|
||||||
let Self(cond, a, b, ..) = self;
|
|
||||||
if *cond { a.render(to) } else { b.render(to) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trait Render<E: Engine> {
|
|
||||||
fn area (&self, to: E::Area) -> E::Area;
|
fn area (&self, to: E::Area) -> E::Area;
|
||||||
fn render (&self, to: &mut E::Output);
|
fn render (&self, to: &mut E::Output);
|
||||||
}
|
}
|
||||||
|
|
@ -95,55 +21,3 @@ impl<E: Engine, C: Content<E>> Render<E> for C {
|
||||||
Content::render(self, to)
|
Content::render(self, to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A function or closure that emits renderables.
|
|
||||||
pub trait Collector<E: Engine>: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)) {}
|
|
||||||
|
|
||||||
/// Any function or closure that emits renderables for the given engine matches [CollectCallback].
|
|
||||||
impl<E, F> Collector<E> for F
|
|
||||||
where E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)) {}
|
|
||||||
|
|
||||||
/// Rendering of iterable collections, one-to-one and many-to one.
|
|
||||||
pub struct Coll;
|
|
||||||
|
|
||||||
impl Coll {
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Map<E, T, I, R, F>(PhantomData<E>, RwLock<I>, F) where
|
|
||||||
E: Engine,
|
|
||||||
I: Iterator<Item = T> + Send + Sync,
|
|
||||||
R: Content<E>,
|
|
||||||
F: Fn(T, usize)->R + Send + Sync;
|
|
||||||
|
|
||||||
impl<E, T, I, R, F> Content<E> for Map<E, T, I, R, F> where
|
|
||||||
E: Engine,
|
|
||||||
I: Iterator<Item = T> + Send + Sync,
|
|
||||||
R: Content<E>,
|
|
||||||
F: Fn(T, usize)->R + Send + Sync
|
|
||||||
{
|
|
||||||
fn render (&self, to: &mut E::Output) {
|
|
||||||
let mut index = 0;
|
|
||||||
for item in &mut*self.1.write().unwrap() {
|
|
||||||
(self.2)(item, index).render(to);
|
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
pub struct Reduce<E, T, I, R, F>(PhantomData<(E, R)>, I, F) where
|
|
||||||
E: Engine,
|
|
||||||
I: Iterator<Item = T> + Send + Sync,
|
|
||||||
R: Content<E>,
|
|
||||||
F: Fn(R, T, usize) -> R + Send + Sync;
|
|
||||||
impl<E, T, I, R, F> Content<E> for Reduce<E, T, I, R, F> where
|
|
||||||
E: Engine,
|
|
||||||
I: Iterator<Item = T> + Send + Sync,
|
|
||||||
R: Content<E>,
|
|
||||||
F: Fn(R, T, usize) -> R + Send + Sync
|
|
||||||
{
|
|
||||||
fn render (&self, to: &mut E::Output) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
/// A binary split with fixed proportion
|
|
||||||
pub struct Split<E, A, B>(pub bool, pub Direction, pub E::Unit, A, B, PhantomData<E>)
|
|
||||||
where E: Engine, A: Content<E>, B: Content<E>;
|
|
||||||
|
|
||||||
impl<E: Engine, A: Content<E>, B: Content<E>> Split<E, A, B> {
|
|
||||||
#[inline] pub fn new (flip: bool, direction: Direction, proportion: E::Unit, a: A, b: B) -> Self {
|
|
||||||
Self(flip, direction, proportion, a, b, Default::default())
|
|
||||||
}
|
|
||||||
#[inline] pub fn n (flip: bool, proportion: E::Unit, a: A, b: B) -> Self {
|
|
||||||
Self::new(flip, North, proportion, a, b)
|
|
||||||
}
|
|
||||||
#[inline] pub fn s (flip: bool, proportion: E::Unit, a: A, b: B) -> Self {
|
|
||||||
Self::new(flip, South, proportion, a, b)
|
|
||||||
}
|
|
||||||
#[inline] pub fn e (flip: bool, proportion: E::Unit, a: A, b: B) -> Self {
|
|
||||||
Self::new(flip, West, proportion, a, b)
|
|
||||||
}
|
|
||||||
#[inline] pub fn w (flip: bool, proportion: E::Unit, a: A, b: B) -> Self {
|
|
||||||
Self::new(flip, East, proportion, a, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Engine, A: Content<E>, B: Content<E>> Content<E> for Split<E, A, B> {
|
|
||||||
fn render (&self, to: &mut E::Output) {
|
|
||||||
let (a, b) = self.1.split_fixed(to.area(), self.2);
|
|
||||||
if self.0 {
|
|
||||||
to.place(a.into(), &self.4);
|
|
||||||
to.place(b.into(), &self.3);
|
|
||||||
} else {
|
|
||||||
to.place(a.into(), &self.3);
|
|
||||||
to.place(b.into(), &self.4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,117 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
pub struct Stack<E: Engine, F: Collector<E>>(pub F, pub Direction, PhantomData<E>);
|
|
||||||
|
|
||||||
impl<E: Engine, F: Collector<E>> Stack<E, F> {
|
|
||||||
#[inline] pub fn new (direction: Direction, build: F) -> Self {
|
|
||||||
Self(build, direction, Default::default())
|
|
||||||
}
|
|
||||||
#[inline] pub fn right (build: F) -> Self {
|
|
||||||
Self::new(East, build)
|
|
||||||
}
|
|
||||||
#[inline] pub fn down (build: F) -> Self {
|
|
||||||
Self::new(South, build)
|
|
||||||
}
|
|
||||||
#[inline] pub fn up (build: F) -> Self {
|
|
||||||
Self::new(North, build)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//#[macro_export] macro_rules! col {
|
|
||||||
//([$($expr:expr),* $(,)?]) => {
|
|
||||||
//Stack::down(move|add|{ $(add(&$expr)?;)* Ok(()) })
|
|
||||||
//};
|
|
||||||
//(![$($expr:expr),* $(,)?]) => {
|
|
||||||
//Stack::down(|add|{ $(add(&$expr)?;)* Ok(()) })
|
|
||||||
//};
|
|
||||||
//($expr:expr) => {
|
|
||||||
//Stack::down($expr)
|
|
||||||
//};
|
|
||||||
//($pat:pat in $collection:expr => $item:expr) => {
|
|
||||||
//Stack::down(move|add|{ for $pat in $collection { add(&$item)?; } Ok(()) })
|
|
||||||
//};
|
|
||||||
//}
|
|
||||||
|
|
||||||
//#[macro_export] macro_rules! col_up {
|
|
||||||
//([$($expr:expr),* $(,)?]) => {
|
|
||||||
//Stack::up(move|add|{ $(add(&$expr)?;)* Ok(()) })
|
|
||||||
//};
|
|
||||||
//(![$($expr:expr),* $(,)?]) => {
|
|
||||||
//Stack::up(|add|{ $(add(&$expr)?;)* Ok(()) })
|
|
||||||
//};
|
|
||||||
//($expr:expr) => {
|
|
||||||
//Stack::up(expr)
|
|
||||||
//};
|
|
||||||
//($pat:pat in $collection:expr => $item:expr) => {
|
|
||||||
//Stack::up(move |add|{ for $pat in $collection { add(&$item)?; } Ok(()) })
|
|
||||||
//};
|
|
||||||
//}
|
|
||||||
|
|
||||||
//#[macro_export] macro_rules! row {
|
|
||||||
//([$($expr:expr),* $(,)?]) => {
|
|
||||||
//Stack::right(move|add|{ $(add(&$expr)?;)* Ok(()) })
|
|
||||||
//};
|
|
||||||
//(![$($expr:expr),* $(,)?]) => {
|
|
||||||
//Stack::right(|add|{ $(add(&$expr)?;)* Ok(()) })
|
|
||||||
//};
|
|
||||||
//($expr:expr) => {
|
|
||||||
//Stack::right($expr)
|
|
||||||
//};
|
|
||||||
//($pat:pat in $collection:expr => $item:expr) => {
|
|
||||||
//Stack::right(move|add|{ for $pat in $collection { add(&$item)?; } Ok(()) })
|
|
||||||
//};
|
|
||||||
//}
|
|
||||||
|
|
||||||
//impl<E: Engine, F: Collector<E>> Content<E> for Stack<E, F> {
|
|
||||||
//fn render (&self, to: &mut E::Output) {
|
|
||||||
//let area = to.area();
|
|
||||||
//let mut w = 0.into();
|
|
||||||
//let mut h = 0.into();
|
|
||||||
//match self.1 {
|
|
||||||
//South => {
|
|
||||||
//(self.0)(&mut |item| {
|
|
||||||
//if h < area.h() {
|
|
||||||
//let item = Max::y(area.h() - h, Push::y(h, item));
|
|
||||||
//let show = item.min_size(area.wh().into())?.map(|s|s.wh());
|
|
||||||
//if let Some([width, height]) = show {
|
|
||||||
//item.render(to)?;
|
|
||||||
//h = h + height;
|
|
||||||
//if width > w { w = width }
|
|
||||||
//};
|
|
||||||
//}
|
|
||||||
//Ok(())
|
|
||||||
//})?;
|
|
||||||
//},
|
|
||||||
//East => {
|
|
||||||
//(self.0)(&mut |item| {
|
|
||||||
//if w < area.w() {
|
|
||||||
//let item = Max::x(area.w() - w, Push::x(w, item));
|
|
||||||
//let show = item.min_size(area.wh().into())?.map(|s|s.wh());
|
|
||||||
//if let Some([width, height]) = show {
|
|
||||||
//item.render(to)?;
|
|
||||||
//w = width + w;
|
|
||||||
//if height > h { h = height }
|
|
||||||
//};
|
|
||||||
//}
|
|
||||||
//Ok(())
|
|
||||||
//})?;
|
|
||||||
//},
|
|
||||||
//North => {
|
|
||||||
//(self.0)(&mut |item| {
|
|
||||||
//if h < area.h() {
|
|
||||||
//let show = item.min_size([area.w(), area.h().minus(h)].into())?.map(|s|s.wh());
|
|
||||||
//if let Some([width, height]) = show {
|
|
||||||
//Shrink::y(height, Push::y(area.h() - height, item))
|
|
||||||
//.render(to)?;
|
|
||||||
//h = h + height;
|
|
||||||
//if width > w { w = width }
|
|
||||||
//};
|
|
||||||
//}
|
|
||||||
//Ok(())
|
|
||||||
//})?;
|
|
||||||
//},
|
|
||||||
//_ => todo!()
|
|
||||||
//};
|
|
||||||
//Ok(())
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
|
|
@ -50,146 +50,3 @@ impl Direction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders multiple things on top of each other,
|
|
||||||
#[macro_export] macro_rules! lay {
|
|
||||||
($($expr:expr),* $(,)?) => {{
|
|
||||||
let bsp = ();
|
|
||||||
$(let bsp = Bsp::b(bsp, $expr);)*;
|
|
||||||
bsp
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stack southward.
|
|
||||||
#[macro_export] macro_rules! col {
|
|
||||||
($($expr:expr),* $(,)?) => {{
|
|
||||||
let bsp = ();
|
|
||||||
$(let bsp = Bsp::s(bsp, $expr);)*;
|
|
||||||
bsp
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stack northward.
|
|
||||||
#[macro_export] macro_rules! col_up {
|
|
||||||
($($expr:expr),* $(,)?) => {{
|
|
||||||
let bsp = ();
|
|
||||||
$(let bsp = Bsp::n(bsp, $expr);)*;
|
|
||||||
bsp
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stack eastward.
|
|
||||||
#[macro_export] macro_rules! row {
|
|
||||||
($($expr:expr),* $(,)?) => {{
|
|
||||||
let bsp = ();
|
|
||||||
$(let bsp = Bsp::e(bsp, $expr);)*;
|
|
||||||
bsp
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Bsp<E: Engine, X: Content<E>, Y: Content<E>> {
|
|
||||||
/// X is north of Y
|
|
||||||
North(Option<f64>, Option<X>, Option<Y>),
|
|
||||||
/// X is south of Y
|
|
||||||
South(Option<f64>, Option<X>, Option<Y>),
|
|
||||||
/// X is east of Y
|
|
||||||
East(Option<f64>, Option<X>, Option<Y>),
|
|
||||||
/// X is west of Y
|
|
||||||
West(Option<f64>, Option<X>, Option<Y>),
|
|
||||||
/// X is above Y
|
|
||||||
Above(Option<X>, Option<Y>),
|
|
||||||
/// X is below Y
|
|
||||||
Below(Option<X>, Option<Y>),
|
|
||||||
/// Should be avoided.
|
|
||||||
Null(PhantomData<E>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Engine, X: Content<E>, Y: Content<E>> Bsp<E, X, Y> {
|
|
||||||
pub fn n (x: X, y: Y) -> Self { Self::North(None, Some(x), Some(y)) }
|
|
||||||
pub fn s (x: X, y: Y) -> Self { Self::South(None, Some(x), Some(y)) }
|
|
||||||
pub fn e (x: X, y: Y) -> Self { Self::East(None, Some(x), Some(y)) }
|
|
||||||
pub fn w (x: X, y: Y) -> Self { Self::West(None, Some(x), Some(y)) }
|
|
||||||
pub fn a (x: X, y: Y) -> Self { Self::Above(Some(x), Some(y)) }
|
|
||||||
pub fn b (x: X, y: Y) -> Self { Self::Below(Some(x), Some(y)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Engine, X: Content<E>, Y: Content<E>> Default for Bsp<E, X, Y> {
|
|
||||||
fn default () -> Self {
|
|
||||||
Self::Null(Default::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Engine, X: Content<E>, Y: Content<E>> Content<E> for Bsp<E, X, Y> {
|
|
||||||
fn area (&self, outer: E::Area) -> E::Area {
|
|
||||||
match self {
|
|
||||||
Self::Null(_) => [0.into(), 0.into(), 0.into(), 0.into()].into(),
|
|
||||||
Self::North(_, a, b) => {
|
|
||||||
let a = a.area(outer);
|
|
||||||
let b = b.area(North.split_fixed(outer, a.y() + a.h()).1.into());
|
|
||||||
[a.x().min(b.x()), a.y().min(b.y()), a.w().max(b.w()), a.h() + b.h()].into()
|
|
||||||
}
|
|
||||||
Self::South(_, a, b) => {
|
|
||||||
let a = a.area(outer);
|
|
||||||
let b = b.area(South.split_fixed(outer, a.y() + a.h()).1.into());
|
|
||||||
[a.x().min(b.x()), a.y().min(b.y()), a.w().max(b.w()), a.h() + b.h()].into()
|
|
||||||
},
|
|
||||||
Self::East(_, a, b) => {
|
|
||||||
let a = a.area(outer);
|
|
||||||
let b = b.area(East.split_fixed(outer, a.x() + a.w()).1.into());
|
|
||||||
[a.x().min(b.x()), a.y().min(b.y()), a.w() + b.w(), a.h().max(b.h())].into()
|
|
||||||
},
|
|
||||||
Self::West(_, a, b) => {
|
|
||||||
let a = a.area(outer);
|
|
||||||
let b = b.area(West.split_fixed(outer, a.x() + a.w()).1.into());
|
|
||||||
[a.x().min(b.x()), a.y().min(b.y()), a.w() + b.w(), a.h().max(b.h())].into()
|
|
||||||
},
|
|
||||||
Self::Above(a, b) | Self::Below(a, b) => {
|
|
||||||
let a = a.area(outer);
|
|
||||||
let b = b.area(outer);
|
|
||||||
[a.x().min(b.x()), a.y().min(b.y()), a.w().max(b.w()), a.h().max(b.h())].into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn render (&self, to: &mut E::Output) {
|
|
||||||
let area = to.area().clone();
|
|
||||||
match self {
|
|
||||||
Self::North(_, a, b) => {
|
|
||||||
let area_a = a.area(area);
|
|
||||||
let area_b = b.area(North.split_fixed(area, area_a.y() + area_a.h()).1.into());
|
|
||||||
to.place(area_a, a);
|
|
||||||
to.place(area_b, b);
|
|
||||||
},
|
|
||||||
Self::South(_, a, b) => {
|
|
||||||
let area_a = a.area(area).clone();
|
|
||||||
let area_b = b.area(South.split_fixed(area, area_a.y() + area_a.h()).1.into()).clone();
|
|
||||||
to.place(area_a, a);
|
|
||||||
to.place(area_b, b);
|
|
||||||
},
|
|
||||||
Self::East(_, a, b) => {
|
|
||||||
let area_a = a.area(area);
|
|
||||||
let area_b = b.area(East.split_fixed(area, area_a.x() + area_a.w()).1.into());
|
|
||||||
to.place(area_a, a);
|
|
||||||
to.place(area_b, b);
|
|
||||||
},
|
|
||||||
Self::West(_, a, b) => {
|
|
||||||
let area_a = a.area(area);
|
|
||||||
let area_b = b.area(West.split_fixed(area, area_a.x() + area_a.w()).1.into());
|
|
||||||
to.place(area_a, a);
|
|
||||||
to.place(area_b, b);
|
|
||||||
},
|
|
||||||
Self::Above(a, b) => {
|
|
||||||
let area_a = a.area(area);
|
|
||||||
let area_b = b.area(area);
|
|
||||||
to.place(area_b, b);
|
|
||||||
to.place(area_a, a);
|
|
||||||
},
|
|
||||||
Self::Below(a, b) => {
|
|
||||||
let area_a = a.area(area);
|
|
||||||
let area_b = b.area(area);
|
|
||||||
to.place(area_a, a);
|
|
||||||
to.place(area_b, b);
|
|
||||||
},
|
|
||||||
Self::Null(_) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,12 @@
|
||||||
mod collection; pub use self::collection::*;
|
//mod collection; pub use self::collection::*;
|
||||||
|
|
||||||
|
mod align; pub use self::align::*;
|
||||||
|
mod bsp; pub use self::bsp::*;
|
||||||
mod direction; pub use self::direction::*;
|
mod direction; pub use self::direction::*;
|
||||||
mod measure; pub use self::measure::*;
|
mod measure; pub use self::measure::*;
|
||||||
mod transform; pub use self::transform::*;
|
mod ops; pub use self::ops::*;
|
||||||
|
mod transform_xy; pub use self::transform_xy::*;
|
||||||
|
mod transform_xy_unit; pub use self::transform_xy_unit::*;
|
||||||
|
|
||||||
pub use ::tek_engine;
|
pub use ::tek_engine;
|
||||||
pub(crate) use ::tek_engine::*;
|
pub(crate) use ::tek_engine::*;
|
||||||
|
|
|
||||||
117
layout/src/ops.rs
Normal file
117
layout/src/ops.rs
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
use std::sync::RwLock;
|
||||||
|
|
||||||
|
impl<E: Engine> Layout<E> for E {}
|
||||||
|
|
||||||
|
pub trait Layout<E: Engine> {
|
||||||
|
/// Content `item` when `cond` is true.
|
||||||
|
fn when <A: Content<E>> (cond: bool, item: A) -> When<E, A> {
|
||||||
|
When(cond, item, Default::default())
|
||||||
|
}
|
||||||
|
/// Content `item` if `cond` is true, otherwise render `other`.
|
||||||
|
fn either <A: Content<E>, B: Content<E>> (cond: bool, a: A, b: B)
|
||||||
|
-> Either<E, A, B>
|
||||||
|
{
|
||||||
|
Either(cond, a, b, Default::default())
|
||||||
|
}
|
||||||
|
/// Maps an [Option<T>] through a callback `F`
|
||||||
|
fn opt <A, F: Fn(A)->R, R: Content<E>> (option: Option<A>, cb: F)
|
||||||
|
-> Opt<E, A, F, R>
|
||||||
|
{
|
||||||
|
Opt(option, cb, Default::default())
|
||||||
|
}
|
||||||
|
fn map <T, I, R, F>(iterator: I, callback: F) -> Map<E, T, I, R, F> where
|
||||||
|
E: Engine,
|
||||||
|
I: Iterator<Item = T> + Send + Sync,
|
||||||
|
R: Content<E>,
|
||||||
|
F: Fn(T, usize)->R + Send + Sync
|
||||||
|
{
|
||||||
|
Map(Default::default(), RwLock::new(iterator), callback)
|
||||||
|
}
|
||||||
|
//pub fn reduce <E, T, I, R, F>(iterator: I, callback: F) -> Reduce<E, T, I, R, F> where
|
||||||
|
//E: Engine,
|
||||||
|
//I: Iterator<Item = T> + Send + Sync,
|
||||||
|
//R: Content<E>,
|
||||||
|
//F: Fn(R, T, usize) -> R + Send + Sync
|
||||||
|
//{
|
||||||
|
//Reduce(Default::default(), iterator, callback)
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Opt<E: Engine, A, F: Fn(A)->R, R: Content<E>>(Option<A>, F, PhantomData<E>);
|
||||||
|
|
||||||
|
/// Contents `self.1` when `self.0` is true.
|
||||||
|
pub struct When<E: Engine, A>(bool, A, PhantomData<E>);
|
||||||
|
|
||||||
|
impl<E: Engine, A: Content<E>> Content<E> for When<E, A> {
|
||||||
|
fn area (&self, to: E::Area) -> E::Area {
|
||||||
|
let Self(cond, item, ..) = self;
|
||||||
|
let mut area = E::Area::zero();
|
||||||
|
if *cond {
|
||||||
|
let item_area = item.area(to);
|
||||||
|
area[0] = item_area.x();
|
||||||
|
area[1] = item_area.y();
|
||||||
|
area[2] = item_area.w();
|
||||||
|
area[3] = item_area.h();
|
||||||
|
}
|
||||||
|
area.into()
|
||||||
|
}
|
||||||
|
fn render (&self, to: &mut E::Output) {
|
||||||
|
let Self(cond, item, ..) = self;
|
||||||
|
if *cond { item.render(to) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Contents `self.1` when `self.0` is true, otherwise renders `self.2`
|
||||||
|
pub struct Either<E: Engine, A, B>(bool, A, B, PhantomData<E>);
|
||||||
|
|
||||||
|
impl<E: Engine, A: Content<E>, B: Content<E>> Content<E> for Either<E, A, B> {
|
||||||
|
fn area (&self, to: E::Area) -> E::Area {
|
||||||
|
let Self(cond, a, b, ..) = self;
|
||||||
|
if *cond { a.area(to) } else { b.area(to) }
|
||||||
|
}
|
||||||
|
fn render (&self, to: &mut E::Output) {
|
||||||
|
let Self(cond, a, b, ..) = self;
|
||||||
|
if *cond { a.render(to) } else { b.render(to) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Map<E, T, I, R, F>(PhantomData<E>, RwLock<I>, F) where
|
||||||
|
E: Engine,
|
||||||
|
I: Iterator<Item = T> + Send + Sync,
|
||||||
|
R: Content<E>,
|
||||||
|
F: Fn(T, usize)->R + Send + Sync;
|
||||||
|
|
||||||
|
impl<E, T, I, R, F> Content<E> for Map<E, T, I, R, F> where
|
||||||
|
E: Engine,
|
||||||
|
I: Iterator<Item = T> + Send + Sync,
|
||||||
|
R: Content<E>,
|
||||||
|
F: Fn(T, usize)->R + Send + Sync
|
||||||
|
{
|
||||||
|
fn render (&self, to: &mut E::Output) {
|
||||||
|
let mut index = 0;
|
||||||
|
for item in &mut*self.1.write().unwrap() {
|
||||||
|
(self.2)(item, index).render(to);
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
pub struct Reduce<E, T, I, R, F>(PhantomData<(E, R)>, I, F) where
|
||||||
|
E: Engine,
|
||||||
|
I: Iterator<Item = T> + Send + Sync,
|
||||||
|
R: Content<E>,
|
||||||
|
F: Fn(R, T, usize) -> R + Send + Sync;
|
||||||
|
impl<E, T, I, R, F> Content<E> for Reduce<E, T, I, R, F> where
|
||||||
|
E: Engine,
|
||||||
|
I: Iterator<Item = T> + Send + Sync,
|
||||||
|
R: Content<E>,
|
||||||
|
F: Fn(R, T, usize) -> R + Send + Sync
|
||||||
|
{
|
||||||
|
fn render (&self, to: &mut E::Output) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
@ -1,227 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
/// 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<E: Engine, T: Content<E>> {
|
|
||||||
_Unused(PhantomData<E>), $($Variant(T)),+
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defines an enum that transforms its content
|
|
||||||
/// along either the X axis, the Y axis, or both.
|
|
||||||
macro_rules! transform_xy {
|
|
||||||
($self:ident : $Enum:ident |$to:ident|$area:expr) => {
|
|
||||||
content_enum!($Enum: X, Y, XY);
|
|
||||||
impl<E: Engine, T: Content<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: Content<E>> Content<E> for $Enum<E, T> {
|
|
||||||
fn content (&self) -> impl Content<E> {
|
|
||||||
match self {
|
|
||||||
Self::X(item) => item,
|
|
||||||
Self::Y(item) => item,
|
|
||||||
Self::XY(item) => item,
|
|
||||||
_ => unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn area (&$self, $to: <E as Engine>::Area) -> <E as Engine>::Area {
|
|
||||||
$area
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
transform_xy!(self: Fill |to|{
|
|
||||||
let [x0, y0, wmax, hmax] = to.xywh();
|
|
||||||
let [x, y, w, h] = Content::area(&self.content(), to).xywh();
|
|
||||||
return match self {
|
|
||||||
Self::X(_) => [x0, y, wmax, h],
|
|
||||||
Self::Y(_) => [x, y0, w, hmax],
|
|
||||||
Self::XY(_) => [x0, y0, wmax, hmax],
|
|
||||||
_ => unreachable!()
|
|
||||||
}.into()
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Defines an enum that transforms its content parametrically
|
|
||||||
/// along either the X axis, the Y axis, or both
|
|
||||||
macro_rules! transform_xy_unit {
|
|
||||||
(|$self:ident : $Enum:ident, $to:ident|$area:expr) => {
|
|
||||||
pub enum $Enum<E: Engine, T: Content<E>> {
|
|
||||||
X(E::Unit, T), Y(E::Unit, T), XY(E::Unit, E::Unit, T),
|
|
||||||
}
|
|
||||||
impl<E: Engine, T: Content<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 {
|
|
||||||
match self {
|
|
||||||
Self::X(x, _) => *x,
|
|
||||||
Self::Y(_, _) => E::Unit::zero(),
|
|
||||||
Self::XY(x, _, _) => *x,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn dy (&self) -> E::Unit {
|
|
||||||
match self {
|
|
||||||
Self::X(_, _) => E::Unit::zero(),
|
|
||||||
Self::Y(y, _) => *y,
|
|
||||||
Self::XY(_, y, _) => *y,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<E: Engine, T: Content<E>> Content<E> for $Enum<E, T> {
|
|
||||||
fn content (&self) -> impl Content<E> {
|
|
||||||
Some(match self {
|
|
||||||
Self::X(_, content) => content,
|
|
||||||
Self::Y(_, content) => content,
|
|
||||||
Self::XY(_, _, content) => content,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fn area (&$self, $to: E::Area) -> E::Area {
|
|
||||||
$area.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
transform_xy_unit!(|self: Fixed, area|{
|
|
||||||
let area = self.content().area(area);
|
|
||||||
match self {
|
|
||||||
Self::X(fw, _) => [area.x(), area.y(), *fw, area.h()],
|
|
||||||
Self::Y(fh, _) => [area.x(), area.y(), area.w(), *fh],
|
|
||||||
Self::XY(fw, fh, _) => [area.x(), area.y(), *fw, *fh], // tagn
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
transform_xy_unit!(|self: Shrink, area|{
|
|
||||||
let area = self.content().area(area);
|
|
||||||
[area.x(), area.y(), area.w().minus(self.dx()), area.h().minus(self.dy())]
|
|
||||||
});
|
|
||||||
|
|
||||||
transform_xy_unit!(|self: Expand, area|{
|
|
||||||
let area = self.content().area(area);
|
|
||||||
[area.x(), area.y(), area.w() + self.dx(), area.h() + self.dy()]
|
|
||||||
});
|
|
||||||
|
|
||||||
transform_xy_unit!(|self: Min, area|{
|
|
||||||
let area = self.content().area(area);
|
|
||||||
match self {
|
|
||||||
Self::X(mw, _) => [area.x(), area.y(), area.w().max(*mw), area.h()],
|
|
||||||
Self::Y(mh, _) => [area.x(), area.y(), area.w(), area.h().max(*mh)],
|
|
||||||
Self::XY(mw, mh, _) => [area.x(), area.y(), area.w().max(*mw), area.h().max(*mh)]
|
|
||||||
}});
|
|
||||||
|
|
||||||
transform_xy_unit!(|self: Max, area|{
|
|
||||||
let area = self.content().area(area);
|
|
||||||
match self {
|
|
||||||
Self::X(mw, _) => [area.x(), area.y(), area.w().min(*mw), area.h()],
|
|
||||||
Self::Y(mh, _) => [area.x(), area.y(), area.w(), area.h().min(*mh)],
|
|
||||||
Self::XY(mw, mh, _) => [area.x(), area.y(), area.w().min(*mw), area.h().min(*mh)],
|
|
||||||
}});
|
|
||||||
|
|
||||||
transform_xy_unit!(|self: Push, area|{
|
|
||||||
let area = self.content().area(area);
|
|
||||||
[area.x() + self.dx(), area.y() + self.dy(), area.w(), area.h()]
|
|
||||||
});
|
|
||||||
|
|
||||||
transform_xy_unit!(|self: Pull, area|{
|
|
||||||
let area = self.content().area(area);
|
|
||||||
[area.x().minus(self.dx()), area.y().minus(self.dy()), area.w(), area.h()]
|
|
||||||
});
|
|
||||||
|
|
||||||
transform_xy_unit!(|self: Margin, area|{
|
|
||||||
let area = self.content().area(area);
|
|
||||||
let dx = self.dx();
|
|
||||||
let dy = self.dy();
|
|
||||||
[area.x().minus(dx), area.y().minus(dy), area.w() + dy + dy, area.h() + dy + dy]
|
|
||||||
});
|
|
||||||
|
|
||||||
transform_xy_unit!(|self: Padding, area|{
|
|
||||||
let area = self.content().area(area);
|
|
||||||
let dx = self.dx();
|
|
||||||
let dy = self.dy();
|
|
||||||
[area.x() + dx, area.y() + dy, area.w().minus(dy + dy), area.h().minus(dy + dy), ]
|
|
||||||
});
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Copy, Clone)]
|
|
||||||
pub enum Alignment { #[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W }
|
|
||||||
|
|
||||||
pub struct Align<E: Engine, T: Content<E>>(Alignment, T, PhantomData<E>);
|
|
||||||
|
|
||||||
impl<E: Engine, T: Content<E>> Align<E, T> {
|
|
||||||
pub fn c (w: T) -> Self { Self(Alignment::Center, w, Default::default()) }
|
|
||||||
pub fn x (w: T) -> Self { Self(Alignment::X, w, Default::default()) }
|
|
||||||
pub fn y (w: T) -> Self { Self(Alignment::Y, w, Default::default()) }
|
|
||||||
pub fn n (w: T) -> Self { Self(Alignment::N, w, Default::default()) }
|
|
||||||
pub fn s (w: T) -> Self { Self(Alignment::S, w, Default::default()) }
|
|
||||||
pub fn e (w: T) -> Self { Self(Alignment::E, w, Default::default()) }
|
|
||||||
pub fn w (w: T) -> Self { Self(Alignment::W, w, Default::default()) }
|
|
||||||
pub fn nw (w: T) -> Self { Self(Alignment::NW, w, Default::default()) }
|
|
||||||
pub fn sw (w: T) -> Self { Self(Alignment::SW, w, Default::default()) }
|
|
||||||
pub fn ne (w: T) -> Self { Self(Alignment::NE, w, Default::default()) }
|
|
||||||
pub fn se (w: T) -> Self { Self(Alignment::SE, w, Default::default()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Engine, T: Content<E>> Content<E> for Align<E, T> {
|
|
||||||
fn area (&self, outer: E::Area) -> E::Area {
|
|
||||||
align_areas(self.0, outer.xywh(), Content::area(&self.content(), outer).xywh()).into()
|
|
||||||
}
|
|
||||||
fn render (&self, render: &mut E::Output) {
|
|
||||||
render.place(self.area(render.area()), &self.content())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn align_areas<N: Coordinate>(alignment: Alignment, on: [N;4], it: [N;4]) -> [N;4] {
|
|
||||||
let [cfx, cfy] = on.center();
|
|
||||||
let [cmx, cmy] = it.center();
|
|
||||||
let center = |cf, cm, m: N|if cf >= cm { m + (cf - cm) } else { m.minus(cm - cf) };
|
|
||||||
let center_x = center(cfx, cmx, it.x());
|
|
||||||
let center_y = center(cfy, cmy, it.y());
|
|
||||||
let east_x = on.x() + on.w().minus(it.w());
|
|
||||||
let south_y = on.y() + on.h().minus(it.h());
|
|
||||||
match alignment {
|
|
||||||
Alignment::Center => [center_x, center_y, it.w(), it.h()],
|
|
||||||
Alignment::X => [center_x, it.y(), it.w(), it.h()],
|
|
||||||
Alignment::Y => [it.x(), center_y, it.w(), it.h()],
|
|
||||||
|
|
||||||
Alignment::NW => [on.x(), on.y(), it.w(), it.h()],
|
|
||||||
Alignment::N => [center_x, on.y(), it.w(), it.h()],
|
|
||||||
Alignment::NE => [east_x, on.y(), it.w(), it.h()],
|
|
||||||
Alignment::E => [east_x, center_y, it.w(), it.h()],
|
|
||||||
Alignment::SE => [east_x, south_y, it.w(), it.h()],
|
|
||||||
Alignment::S => [center_x, south_y, it.w(), it.h()],
|
|
||||||
Alignment::SW => [on.x(), south_y, it.w(), it.h()],
|
|
||||||
Alignment::W => [on.x(), center_y, it.w(), it.h()],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//fn align<E: Engine, T: Content<E>, N: Coordinate, R: Area<N> + From<[N;4]>> (align: &Align<E, T>, outer: R, content: R) -> Option<R> {
|
|
||||||
//if outer.w() < content.w() || outer.h() < content.h() {
|
|
||||||
//None
|
|
||||||
//} else {
|
|
||||||
//let [ox, oy, ow, oh] = outer.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(),
|
|
||||||
//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!()
|
|
||||||
//})
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
41
layout/src/transform_xy.rs
Normal file
41
layout/src/transform_xy.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
/// Defines an enum that transforms its content
|
||||||
|
/// along either the X axis, the Y axis, or both.
|
||||||
|
///
|
||||||
|
/// The `_Unused` variant wraps the `Engine` type
|
||||||
|
/// using `PhantomData` to permit the double generic.
|
||||||
|
macro_rules! transform_xy {
|
||||||
|
($self:ident : $Enum:ident |$to:ident|$area:expr) => {
|
||||||
|
pub enum $Enum<E: Engine, T: Content<E>> { _Unused(PhantomData<E>), X(T), Y(T), XY(T) }
|
||||||
|
impl<E: Engine, T: Content<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: Content<E>> Content<E> for $Enum<E, T> {
|
||||||
|
fn content (&self) -> impl Content<E> {
|
||||||
|
match self {
|
||||||
|
Self::X(item) => item,
|
||||||
|
Self::Y(item) => item,
|
||||||
|
Self::XY(item) => item,
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn area (&$self, $to: <E as Engine>::Area) -> <E as Engine>::Area {
|
||||||
|
$area
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transform_xy!(self: Fill |to|{
|
||||||
|
let [x0, y0, wmax, hmax] = to.xywh();
|
||||||
|
let [x, y, w, h] = Content::area(&self.content(), to).xywh();
|
||||||
|
return match self {
|
||||||
|
Self::X(_) => [x0, y, wmax, h],
|
||||||
|
Self::Y(_) => [x, y0, w, hmax],
|
||||||
|
Self::XY(_) => [x0, y0, wmax, hmax],
|
||||||
|
_ => unreachable!()
|
||||||
|
}.into()
|
||||||
|
});
|
||||||
101
layout/src/transform_xy_unit.rs
Normal file
101
layout/src/transform_xy_unit.rs
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
/// Defines an enum that parametrically transforms its content
|
||||||
|
/// along either the X axis, the Y axis, or both.
|
||||||
|
macro_rules! transform_xy_unit {
|
||||||
|
(|$self:ident : $Enum:ident, $to:ident|$area:expr) => {
|
||||||
|
pub enum $Enum<E: Engine, T: Content<E>> {
|
||||||
|
X(E::Unit, T), Y(E::Unit, T), XY(E::Unit, E::Unit, T),
|
||||||
|
}
|
||||||
|
impl<E: Engine, T: Content<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 {
|
||||||
|
match self {
|
||||||
|
Self::X(x, _) => *x,
|
||||||
|
Self::Y(_, _) => E::Unit::zero(),
|
||||||
|
Self::XY(x, _, _) => *x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn dy (&self) -> E::Unit {
|
||||||
|
match self {
|
||||||
|
Self::X(_, _) => E::Unit::zero(),
|
||||||
|
Self::Y(y, _) => *y,
|
||||||
|
Self::XY(_, y, _) => *y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<E: Engine, T: Content<E>> Content<E> for $Enum<E, T> {
|
||||||
|
fn content (&self) -> impl Content<E> {
|
||||||
|
Some(match self {
|
||||||
|
Self::X(_, content) => content,
|
||||||
|
Self::Y(_, content) => content,
|
||||||
|
Self::XY(_, _, content) => content,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn area (&$self, $to: E::Area) -> E::Area {
|
||||||
|
$area.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transform_xy_unit!(|self: Fixed, area|{
|
||||||
|
let area = self.content().area(area);
|
||||||
|
match self {
|
||||||
|
Self::X(fw, _) => [area.x(), area.y(), *fw, area.h()],
|
||||||
|
Self::Y(fh, _) => [area.x(), area.y(), area.w(), *fh],
|
||||||
|
Self::XY(fw, fh, _) => [area.x(), area.y(), *fw, *fh], // tagn
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
transform_xy_unit!(|self: Shrink, area|{
|
||||||
|
let area = self.content().area(area);
|
||||||
|
[area.x(), area.y(), area.w().minus(self.dx()), area.h().minus(self.dy())]
|
||||||
|
});
|
||||||
|
|
||||||
|
transform_xy_unit!(|self: Expand, area|{
|
||||||
|
let area = self.content().area(area);
|
||||||
|
[area.x(), area.y(), area.w() + self.dx(), area.h() + self.dy()]
|
||||||
|
});
|
||||||
|
|
||||||
|
transform_xy_unit!(|self: Min, area|{
|
||||||
|
let area = self.content().area(area);
|
||||||
|
match self {
|
||||||
|
Self::X(mw, _) => [area.x(), area.y(), area.w().max(*mw), area.h()],
|
||||||
|
Self::Y(mh, _) => [area.x(), area.y(), area.w(), area.h().max(*mh)],
|
||||||
|
Self::XY(mw, mh, _) => [area.x(), area.y(), area.w().max(*mw), area.h().max(*mh)]
|
||||||
|
}});
|
||||||
|
|
||||||
|
transform_xy_unit!(|self: Max, area|{
|
||||||
|
let area = self.content().area(area);
|
||||||
|
match self {
|
||||||
|
Self::X(mw, _) => [area.x(), area.y(), area.w().min(*mw), area.h()],
|
||||||
|
Self::Y(mh, _) => [area.x(), area.y(), area.w(), area.h().min(*mh)],
|
||||||
|
Self::XY(mw, mh, _) => [area.x(), area.y(), area.w().min(*mw), area.h().min(*mh)],
|
||||||
|
}});
|
||||||
|
|
||||||
|
transform_xy_unit!(|self: Push, area|{
|
||||||
|
let area = self.content().area(area);
|
||||||
|
[area.x() + self.dx(), area.y() + self.dy(), area.w(), area.h()]
|
||||||
|
});
|
||||||
|
|
||||||
|
transform_xy_unit!(|self: Pull, area|{
|
||||||
|
let area = self.content().area(area);
|
||||||
|
[area.x().minus(self.dx()), area.y().minus(self.dy()), area.w(), area.h()]
|
||||||
|
});
|
||||||
|
|
||||||
|
transform_xy_unit!(|self: Margin, area|{
|
||||||
|
let area = self.content().area(area);
|
||||||
|
let dx = self.dx();
|
||||||
|
let dy = self.dy();
|
||||||
|
[area.x().minus(dx), area.y().minus(dy), area.w() + dy + dy, area.h() + dy + dy]
|
||||||
|
});
|
||||||
|
|
||||||
|
transform_xy_unit!(|self: Padding, area|{
|
||||||
|
let area = self.content().area(area);
|
||||||
|
let dx = self.dx();
|
||||||
|
let dy = self.dy();
|
||||||
|
[area.x() + dx, area.y() + dy, area.w().minus(dy + dy), area.h().minus(dy + dy), ]
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue