mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +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
|
||||
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.
|
||||
|
||||
//mod split; pub use self::split::*;
|
||||
//mod stack; pub use self::stack::*;
|
||||
|
||||
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> {
|
||||
/// 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> {
|
||||
pub trait Render<E: Engine> {
|
||||
fn area (&self, to: E::Area) -> E::Area;
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 direction; pub use self::direction::*;
|
||||
mod measure; pub use self::measure::*;
|
||||
mod transform; pub use self::transform::*;
|
||||
//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 measure; pub use self::measure::*;
|
||||
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(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