diff --git a/layout/README.md b/layout/README.md index 1e76cb54..ce238f74 100644 --- a/layout/README.md +++ b/layout/README.md @@ -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 diff --git a/layout/src/align.rs b/layout/src/align.rs new file mode 100644 index 00000000..403d41e5 --- /dev/null +++ b/layout/src/align.rs @@ -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>(Alignment, T, PhantomData); + +impl> Align { + 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> Content for Align { + 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(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, N: Coordinate, R: Area + From<[N;4]>> (align: &Align, outer: R, content: R) -> Option { + //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!() + //}) + //} +//} diff --git a/layout/src/bsp.rs b/layout/src/bsp.rs new file mode 100644 index 00000000..bf2258a6 --- /dev/null +++ b/layout/src/bsp.rs @@ -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, Y: Content> { + /// X is north of Y + North(Option, Option, Option), + /// X is south of Y + South(Option, Option, Option), + /// X is east of Y + East(Option, Option, Option), + /// X is west of Y + West(Option, Option, Option), + /// X is above Y + Above(Option, Option), + /// X is below Y + Below(Option, Option), + /// Should be avoided. + Null(PhantomData), +} + +impl, Y: Content> Bsp { + 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, Y: Content> Default for Bsp { + fn default () -> Self { + Self::Null(Default::default()) + } +} + +impl, Y: Content> Content for Bsp { + 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(_) => {} + } + } +} diff --git a/layout/src/collection.rs b/layout/src/collection.rs index 5d48b6d3..5559afea 100644 --- a/layout/src/collection.rs +++ b/layout/src/collection.rs @@ -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: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)) {} -impl Layout for E {} +/// Any function or closure that emits renderables for the given engine matches [CollectCallback]. +impl Collector for F +where E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)) {} -pub trait Layout { - /// Content `item` when `cond` is true. - fn when > (cond: bool, item: A) -> When { - When(cond, item, Default::default()) - } - /// Content `item` if `cond` is true, otherwise render `other`. - fn either , B: Content> (cond: bool, a: A, b: B) - -> Either - { - Either(cond, a, b, Default::default()) - } - /// Maps an [Option] through a callback `F` - fn opt R, R: Content> (option: Option, cb: F) - -> Opt - { - Opt(option, cb, Default::default()) - } - fn map (iterator: I, callback: F) -> Map where - E: Engine, - I: Iterator + Send + Sync, - R: Content, - F: Fn(T, usize)->R + Send + Sync - { - Map(Default::default(), RwLock::new(iterator), callback) - } - //pub fn reduce (iterator: I, callback: F) -> Reduce where - //E: Engine, - //I: Iterator + Send + Sync, - //R: Content, - //F: Fn(R, T, usize) -> R + Send + Sync - //{ - //Reduce(Default::default(), iterator, callback) - //} -} - -pub struct OptR, R: Content>(Option, F, PhantomData); - -/// Contents `self.1` when `self.0` is true. -pub struct When(bool, A, PhantomData); - -impl> Content for When { - 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(bool, A, B, PhantomData); - -impl, B: Content> Content for Either { - 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 { +pub trait Render { fn area (&self, to: E::Area) -> E::Area; fn render (&self, to: &mut E::Output); } @@ -95,55 +21,3 @@ impl> Render for C { Content::render(self, to) } } - -/// A function or closure that emits renderables. -pub trait Collector: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)) {} - -/// Any function or closure that emits renderables for the given engine matches [CollectCallback]. -impl Collector for F -where E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)) {} - -/// Rendering of iterable collections, one-to-one and many-to one. -pub struct Coll; - -impl Coll { -} - -pub struct Map(PhantomData, RwLock, F) where - E: Engine, - I: Iterator + Send + Sync, - R: Content, - F: Fn(T, usize)->R + Send + Sync; - -impl Content for Map where - E: Engine, - I: Iterator + Send + Sync, - R: Content, - 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(PhantomData<(E, R)>, I, F) where - E: Engine, - I: Iterator + Send + Sync, - R: Content, - F: Fn(R, T, usize) -> R + Send + Sync; -impl Content for Reduce where - E: Engine, - I: Iterator + Send + Sync, - R: Content, - F: Fn(R, T, usize) -> R + Send + Sync -{ - fn render (&self, to: &mut E::Output) { - todo!() - } -} -*/ diff --git a/layout/src/collection/split.rs b/layout/src/collection/split.rs deleted file mode 100644 index cff769ed..00000000 --- a/layout/src/collection/split.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::*; - -/// A binary split with fixed proportion -pub struct Split(pub bool, pub Direction, pub E::Unit, A, B, PhantomData) -where E: Engine, A: Content, B: Content; - -impl, B: Content> Split { - #[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, B: Content> Content for Split { - 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); - } - } -} diff --git a/layout/src/collection/stack.rs b/layout/src/collection/stack.rs deleted file mode 100644 index c178bc97..00000000 --- a/layout/src/collection/stack.rs +++ /dev/null @@ -1,117 +0,0 @@ -use crate::*; - -pub struct Stack>(pub F, pub Direction, PhantomData); - -impl> Stack { - #[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> Content for Stack { - //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(()) - //} -//} diff --git a/layout/src/direction.rs b/layout/src/direction.rs index 13609213..8c189e3f 100644 --- a/layout/src/direction.rs +++ b/layout/src/direction.rs @@ -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, Y: Content> { - /// X is north of Y - North(Option, Option, Option), - /// X is south of Y - South(Option, Option, Option), - /// X is east of Y - East(Option, Option, Option), - /// X is west of Y - West(Option, Option, Option), - /// X is above Y - Above(Option, Option), - /// X is below Y - Below(Option, Option), - /// Should be avoided. - Null(PhantomData), -} - -impl, Y: Content> Bsp { - 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, Y: Content> Default for Bsp { - fn default () -> Self { - Self::Null(Default::default()) - } -} - -impl, Y: Content> Content for Bsp { - 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(_) => {} - } - } -} diff --git a/layout/src/lib.rs b/layout/src/lib.rs index e8bdb134..bead884a 100644 --- a/layout/src/lib.rs +++ b/layout/src/lib.rs @@ -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::*; diff --git a/layout/src/ops.rs b/layout/src/ops.rs new file mode 100644 index 00000000..baadfab9 --- /dev/null +++ b/layout/src/ops.rs @@ -0,0 +1,117 @@ +use crate::*; + +use std::sync::RwLock; + +impl Layout for E {} + +pub trait Layout { + /// Content `item` when `cond` is true. + fn when > (cond: bool, item: A) -> When { + When(cond, item, Default::default()) + } + /// Content `item` if `cond` is true, otherwise render `other`. + fn either , B: Content> (cond: bool, a: A, b: B) + -> Either + { + Either(cond, a, b, Default::default()) + } + /// Maps an [Option] through a callback `F` + fn opt R, R: Content> (option: Option, cb: F) + -> Opt + { + Opt(option, cb, Default::default()) + } + fn map (iterator: I, callback: F) -> Map where + E: Engine, + I: Iterator + Send + Sync, + R: Content, + F: Fn(T, usize)->R + Send + Sync + { + Map(Default::default(), RwLock::new(iterator), callback) + } + //pub fn reduce (iterator: I, callback: F) -> Reduce where + //E: Engine, + //I: Iterator + Send + Sync, + //R: Content, + //F: Fn(R, T, usize) -> R + Send + Sync + //{ + //Reduce(Default::default(), iterator, callback) + //} +} + +pub struct OptR, R: Content>(Option, F, PhantomData); + +/// Contents `self.1` when `self.0` is true. +pub struct When(bool, A, PhantomData); + +impl> Content for When { + 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(bool, A, B, PhantomData); + +impl, B: Content> Content for Either { + 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(PhantomData, RwLock, F) where + E: Engine, + I: Iterator + Send + Sync, + R: Content, + F: Fn(T, usize)->R + Send + Sync; + +impl Content for Map where + E: Engine, + I: Iterator + Send + Sync, + R: Content, + 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(PhantomData<(E, R)>, I, F) where + E: Engine, + I: Iterator + Send + Sync, + R: Content, + F: Fn(R, T, usize) -> R + Send + Sync; +impl Content for Reduce where + E: Engine, + I: Iterator + Send + Sync, + R: Content, + F: Fn(R, T, usize) -> R + Send + Sync +{ + fn render (&self, to: &mut E::Output) { + todo!() + } +} +*/ diff --git a/layout/src/transform.rs b/layout/src/transform.rs deleted file mode 100644 index c21345e2..00000000 --- a/layout/src/transform.rs +++ /dev/null @@ -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> { - _Unused(PhantomData), $($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> $Enum { - 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> Content for $Enum { - fn content (&self) -> impl Content { - match self { - Self::X(item) => item, - Self::Y(item) => item, - Self::XY(item) => item, - _ => unreachable!() - } - } - fn area (&$self, $to: ::Area) -> ::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> { - X(E::Unit, T), Y(E::Unit, T), XY(E::Unit, E::Unit, T), - } - impl> $Enum { - 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> Content for $Enum { - fn content (&self) -> impl Content { - 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>(Alignment, T, PhantomData); - -impl> Align { - 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> Content for Align { - 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(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, N: Coordinate, R: Area + From<[N;4]>> (align: &Align, outer: R, content: R) -> Option { - //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!() - //}) - //} -//} diff --git a/layout/src/transform_xy.rs b/layout/src/transform_xy.rs new file mode 100644 index 00000000..7ede48d8 --- /dev/null +++ b/layout/src/transform_xy.rs @@ -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> { _Unused(PhantomData), X(T), Y(T), XY(T) } + impl> $Enum { + 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> Content for $Enum { + fn content (&self) -> impl Content { + match self { + Self::X(item) => item, + Self::Y(item) => item, + Self::XY(item) => item, + _ => unreachable!() + } + } + fn area (&$self, $to: ::Area) -> ::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() +}); diff --git a/layout/src/transform_xy_unit.rs b/layout/src/transform_xy_unit.rs new file mode 100644 index 00000000..6f7984a1 --- /dev/null +++ b/layout/src/transform_xy_unit.rs @@ -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> { + X(E::Unit, T), Y(E::Unit, T), XY(E::Unit, E::Unit, T), + } + impl> $Enum { + 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> Content for $Enum { + fn content (&self) -> impl Content { + 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), ] +});