From c9b09b7dead4eae79d8a14495ec5726e285b37f7 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Tue, 31 Dec 2024 02:03:16 +0100 Subject: [PATCH] wip: and sweeps right through the codebase --- engine/src/component.rs | 27 ----- engine/src/engine.rs | 7 ++ engine/src/lib.rs | 8 +- engine/src/output.rs | 49 ++++++--- engine/src/tui.rs | 13 ++- layout/src/collection.rs | 161 +++++++++------------------- layout/src/collection/bsp.rs | 139 +++++++++++------------- layout/src/collection/layers.rs | 56 ---------- layout/src/collection/split.rs | 151 ++------------------------ layout/src/collection/stack.rs | 142 ++++++++++++++++-------- layout/src/direction.rs | 24 ++++- layout/src/lib.rs | 7 +- layout/src/logic.rs | 29 ----- layout/src/{space.rs => measure.rs} | 35 +----- layout/src/scroll.rs | 1 - layout/src/transform.rs | 146 ++++++++++++------------- 16 files changed, 370 insertions(+), 625 deletions(-) delete mode 100644 engine/src/component.rs delete mode 100644 layout/src/collection/layers.rs delete mode 100644 layout/src/logic.rs rename layout/src/{space.rs => measure.rs} (67%) delete mode 100644 layout/src/scroll.rs diff --git a/engine/src/component.rs b/engine/src/component.rs deleted file mode 100644 index 3a9cba2b..00000000 --- a/engine/src/component.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::*; - -/// A UI component that can render itself as a [Render], and [Handle] input. -pub trait Component: Render + Handle {} - -/// Everything that implements [Render] and [Handle] is a [Component]. -impl + Handle> Component for C {} - -/// A component that can exit. -pub trait Exit: Send { - //fn exited (&self) -> bool; - //fn exit (&mut self); - //fn boxed (self) -> Box where Self: Sized + 'static { - //Box::new(self) - //} -} - -/// Marker trait for [Component]s that can [Exit]. -pub trait ExitableComponent: Exit + Component where E: Engine { - ///// Perform type erasure for collecting heterogeneous components. - //fn boxed (self) -> Box> where Self: Sized + 'static { - //Box::new(self) - //} -} - -/// All [Components]s that implement [Exit] implement [ExitableComponent]. -impl + Exit> ExitableComponent for C {} diff --git a/engine/src/engine.rs b/engine/src/engine.rs index 37909526..497876fd 100644 --- a/engine/src/engine.rs +++ b/engine/src/engine.rs @@ -63,6 +63,9 @@ pub trait Size { Ok(self) } } + #[inline] fn zero () -> [N;2] { + [N::zero(), N::zero()] + } } impl Size for (N, N) { @@ -129,6 +132,10 @@ pub trait Area: Copy { #[inline] fn shrink_y (&self, y: N) -> [N;4] { [self.x(), self.y(), self.w(), self.h().minus(y)] } + + fn zero () -> [N;4] { + [N::zero(), N::zero(), N::zero(), N::zero()] + } } impl Area for (N, N, N, N) { diff --git a/engine/src/lib.rs b/engine/src/lib.rs index 539a1e6f..dd47d059 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -46,11 +46,11 @@ pub type Perhaps = Result, Box>; fn area_mut (&mut self) -> &mut [u16;4] { &mut self.0 } - fn render_in (&mut self, _: [u16;4], _: &impl Render) -> Usually<()> { - Ok(()) + fn place (&mut self, _: [u16;4], _: &impl Layout) { + () } } - impl Render for String { + impl Layout for String { fn render (&self, to: &mut TestOutput) { to.area_mut().set_w(self.len() as u16); } @@ -63,7 +63,7 @@ pub type Perhaps = Result, Box>; use std::sync::{Arc, RwLock}; struct TestComponent(String); impl Layout for TestComponent { - fn layout (&self) -> Option> { + fn layout (&self) -> Option> { Some(self.0.as_str()) } } diff --git a/engine/src/output.rs b/engine/src/output.rs index 5ba1cd5f..4f361ce6 100644 --- a/engine/src/output.rs +++ b/engine/src/output.rs @@ -8,36 +8,51 @@ pub trait Output { /// Mutable pointer to area fn area_mut (&mut self) -> &mut E::Area; ///// Render widget in area - fn render_in (&mut self, area: E::Area, widget: &impl Render) -> Usually<()>; + fn place (&mut self, area: E::Area, content: &impl Content); #[inline] fn x (&self) -> E::Unit { self.area().x() } #[inline] fn y (&self) -> E::Unit { self.area().y() } #[inline] fn w (&self) -> E::Unit { self.area().w() } #[inline] fn h (&self) -> E::Unit { self.area().h() } + #[inline] fn wh (&self) -> E::Size { self.area().wh().into() } } -pub trait Layout: Send + Sync { - fn layout (&self) -> Option> { +pub trait Content: Send + Sync { + fn content (&self) -> Option> { None::<()> } -} - -impl Layout for () {} - -impl> Layout for &L {} - -pub trait Render: Send + Sync { - fn render (&self, _: &mut E::Output); -} - -impl> Render for L { - fn render (&self, to: &mut E::Output) { - if let Some(content) = self.layout() { - content.render(to) + fn area (&self, area: E::Area) -> E::Area { + if let Some(content) = self.content() { + content.area(area) + } else { + [0.into(), 0.into(), 0.into(), 0.into()].into() + } + } + fn render (&self, output: &mut E::Output) { + if let Some(content) = self.content() { + output.place(self.area(output.area()), &content) } } } +impl Content for () {} + +impl> Content for &T {} + +//impl> Render for &R {} + +//pub trait Render: Send + Sync { + //fn render (&self, _: &mut E::Output); +//} + +//impl> Render for L { + //fn render (&self, to: &mut E::Output) { + //if let Some(content) = self.layout() { + //content.render(to) + //} + //} +//} + //impl> Layout for &L {} ///// Something that can be represented by a renderable component. diff --git a/engine/src/tui.rs b/engine/src/tui.rs index 392cf378..ae3e89bb 100644 --- a/engine/src/tui.rs +++ b/engine/src/tui.rs @@ -89,7 +89,7 @@ impl Tui { } } -pub trait TuiRun + Handle + Sized + 'static> { +pub trait TuiRun + Handle + Sized + 'static> { /// Run an app in the main loop. fn run (&self, state: &Arc>) -> Usually<()>; /// Spawn the input thread. @@ -98,7 +98,7 @@ pub trait TuiRun + Handle + Sized + 'static> { fn run_output (&self, state: &Arc>, sleep: Duration) -> JoinHandle<()>; } -impl + Handle + Sized + 'static> TuiRun for Arc> { +impl + Handle + Sized + 'static> TuiRun for Arc> { fn run (&self, state: &Arc>) -> Usually<()> { let _input_thread = self.run_input(state, Duration::from_millis(100)); self.write().unwrap().setup()?; @@ -254,12 +254,11 @@ pub struct TuiOutput { impl Output for TuiOutput { #[inline] fn area (&self) -> [u16;4] { self.area } #[inline] fn area_mut (&mut self) -> &mut [u16;4] { &mut self.area } - #[inline] fn render_in (&mut self, area: [u16;4], widget: &impl Render) -> Usually<()> { + #[inline] fn place (&mut self, area: [u16;4], content: &impl Content) { let last = self.area(); *self.area_mut() = area; - widget.render(self); + content.render(self); *self.area_mut() = last; - Ok(()) } } @@ -330,13 +329,13 @@ pub fn half_block (lower: bool, upper: bool) -> Option { //impl> Render for T {} -impl Render for &str { +impl Content for &str { fn render (&self, to: &mut TuiOutput) { to.blit(self, to.area.x(), to.area.y(), None) } } -impl Render for String { +impl Content for String { fn render (&self, to: &mut TuiOutput) { to.blit(self, to.area.x(), to.area.y(), None) } diff --git a/layout/src/collection.rs b/layout/src/collection.rs index 2d750f5f..ef390198 100644 --- a/layout/src/collection.rs +++ b/layout/src/collection.rs @@ -1,139 +1,76 @@ -use crate::*; - -//////////////////////////////////////////////////////////////////////////////////////////////////// +//! Groupings of elements. mod bsp; pub use self::bsp::*; -mod layers; pub use self::layers::*; mod split; pub use self::split::*; mod stack; pub use self::stack::*; -//////////////////////////////////////////////////////////////////////////////////////////////////// +use crate::*; /// A function or closure that emits renderables. -pub trait CollectCallback: Send - + Sync - + Fn(&mut dyn FnMut(&dyn Render)->Usually<()>)->Usually<()> -{} +pub trait Collector: Send + Sync + Fn(&mut dyn FnMut(&dyn Content)) {} /// Any function or closure that emits renderables for the given engine matches [CollectCallback]. -impl CollectCallback for F -where - E: Engine, - F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)->Usually<()>)->Usually<()> -{} +impl Collector for F +where E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Content)) {} -//////////////////////////////////////////////////////////////////////////////////////////////////// +pub struct Map(PhantomData, I, F) + where E: Engine, I: Iterator, R: Content, F: Fn(T)->R; -pub enum Collect<'a, E: Engine, const N: usize> { - Callback(CallbackCollection<'a, E>), - //Iterator(IteratorCollection<'a, E>), - Array(ArrayCollection<'a, E, N>), - Slice(SliceCollection<'a, E>), -} +pub struct Reduce(PhantomData<(E, R)>, I, F) + where E: Engine, I: Iterator, R: Content, F: Fn(&dyn Content, T)->R; -impl<'a, E: Engine, const N: usize> Collect<'a, E, N> { - pub fn iter (&'a self) -> CollectIterator<'a, E, N> { - CollectIterator(0, &self) +impl+Send+Sync, R: Content, F: Fn(&dyn Content, T)->R+Send+Sync> Content for Reduce { + fn render (&self, _to: &mut E::Output) { + todo!() } } -impl<'a, E: Engine, const N: usize> From> for Collect<'a, E, N> { - fn from (callback: CallbackCollection<'a, E>) -> Self { - Self::Callback(callback) +/// Conditional rendering, in unary and binary forms. +pub struct Cond; + +impl Cond { + /// Content `item` when `cond` is true. + pub fn when > (cond: bool, item: A) -> When { + When(cond, item, Default::default()) + } + /// Content `item` if `cond` is true, otherwise render `other`. + pub fn either , B: Content> (cond: bool, a: A, b: B) -> Either { + Either(cond, a, b, Default::default()) } } -impl<'a, E: Engine, const N: usize> From> for Collect<'a, E, N> { - fn from (slice: SliceCollection<'a, E>) -> Self { - Self::Slice(slice) - } -} +/// Contents `self.1` when `self.0` is true. +pub struct When(bool, A, PhantomData); -impl<'a, E: Engine, const N: usize> From> for Collect<'a, E, N>{ - fn from (array: ArrayCollection<'a, E, N>) -> Self { - Self::Array(array) - } -} - -type CallbackCollection<'a, E> = - &'a dyn Fn(&'a mut dyn FnMut(&dyn Render)->Usually<()>); - -//type IteratorCollection<'a, E> = - //&'a mut dyn Iterator>; - -type SliceCollection<'a, E> = - &'a [&'a dyn Render]; - -type ArrayCollection<'a, E, const N: usize> = - [&'a dyn Render; N]; - -pub struct CollectIterator<'a, E: Engine, const N: usize>(usize, &'a Collect<'a, E, N>); - -impl<'a, E: Engine, const N: usize> Iterator for CollectIterator<'a, E, N> { - type Item = &'a dyn Render; - fn next (&mut self) -> Option { - match self.1 { - Collect::Callback(_callback) => { - todo!() - }, - //Collection::Iterator(iterator) => { - //iterator.next() - //}, - Collect::Array(array) => { - if let Some(_item) = array.get(self.0) { - self.0 += 1; - //Some(item) - None - } else { - None - } - } - Collect::Slice(slice) => { - if let Some(_item) = slice.get(self.0) { - self.0 += 1; - //Some(item) - None - } else { - None - } - } +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); -pub struct Map, R: Render, F: Fn(T)->R>( - PhantomData, - I, - F -); - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -pub struct Reduce, R: Render, F: Fn(&dyn Render, T)->R>( - PhantomData<(E, R)>, - I, - F -); - -impl+Send+Sync, R: Render, F: Fn(&dyn Render, T)->R+Send+Sync> Render for Reduce { - fn min_size (&self, _to: E::Size) -> Perhaps { - todo!() +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) -> Usually<()> { - todo!() + fn render (&self, to: &mut E::Output) { + let Self(cond, a, b, ..) = self; + if *cond { a.render(to) } else { b.render(to) } } } - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -#[cfg(test)] #[test] fn test_bsp () { - // TODO -} diff --git a/layout/src/collection/bsp.rs b/layout/src/collection/bsp.rs index 181f7fbd..ae5084fa 100644 --- a/layout/src/collection/bsp.rs +++ b/layout/src/collection/bsp.rs @@ -1,14 +1,32 @@ use crate::*; -pub enum Bsp, Y: Render> { +/// Renders multiple things on top of each other, +macro_rules! lay { + ($($expr:expr),*) => {{ + let layers = (); + $(let layers = Bsp::b(layers, $expr);)*; + layers + }} +} + +/// Renders multiple things on top of each other, +macro_rules! col { + ($($expr:expr),*) => {{ + let layers = (); + $(let layers = Bsp::s(layers, $expr);)*; + layers + }} +} + +pub enum Bsp, Y: Content> { /// X is north of Y - N(Option, Option), + N(f64, Option, Option), /// X is south of Y - S(Option, Option), + S(f64, Option, Option), /// X is east of Y - E(Option, Option), + E(f64, Option, Option), /// X is west of Y - W(Option, Option), + W(f64, Option, Option), /// X is above Y A(Option, Option), /// X is below Y @@ -17,83 +35,56 @@ pub enum Bsp, Y: Render> { Null(PhantomData), } -impl, Y: Render> Bsp { - pub fn new (x: X) -> Self { Self::A(Some(x), None) } - pub fn n (x: X, y: Y) -> Self { Self::N(Some(x), Some(y)) } - pub fn s (x: X, y: Y) -> Self { Self::S(Some(x), Some(y)) } - pub fn e (x: X, y: Y) -> Self { Self::E(Some(x), Some(y)) } - pub fn w (x: X, y: Y) -> Self { Self::W(Some(x), Some(y)) } - pub fn a (x: X, y: Y) -> Self { Self::A(Some(x), Some(y)) } - pub fn b (x: X, y: Y) -> Self { Self::B(Some(x), Some(y)) } +impl, Y: Content> Bsp { + pub fn n (p: f64, x: X, y: Y) -> Self { Self::N(p, Some(x), Some(y)) } + pub fn s (p: f64, x: X, y: Y) -> Self { Self::S(p, Some(x), Some(y)) } + pub fn e (p: f64, x: X, y: Y) -> Self { Self::E(p, Some(x), Some(y)) } + pub fn w (p: f64, x: X, y: Y) -> Self { Self::W(p, Some(x), Some(y)) } + pub fn a (x: X, y: Y) -> Self { Self::A(Some(x), Some(y)) } + pub fn b (x: X, y: Y) -> Self { Self::B(Some(x), Some(y)) } } -impl, Y: Render> Default for Bsp { +impl, Y: Content> Default for Bsp { fn default () -> Self { Self::Null(Default::default()) } } -impl, Y: Render> Render for Bsp { - fn min_size (&self, to: E::Size) -> Perhaps { - Ok(Some(match self { - Self::Null(_) => [0.into(), 0.into()].into(), - Self::S(a, b) => { - let a = a.min_size(to)?.unwrap_or([0.into(), 0.into()].into()); - let b = b.min_size(to)?.unwrap_or([0.into(), 0.into()].into()); - [a.w().max(b.w()), a.h() + b.h()].into() - }, - Self::E(a, b) => { - let a = a.min_size(to)?.unwrap_or([0.into(), 0.into()].into()); - let b = b.min_size(to)?.unwrap_or([0.into(), 0.into()].into()); - [a.w() + b.w(), a.h().max(b.h())].into() - }, - Self::W(a, b) => { - let a = a.min_size(to)?.unwrap_or([0.into(), 0.into()].into()); - let b = b.min_size(to)?.unwrap_or([0.into(), 0.into()].into()); - [a.w() + b.w(), a.h().max(b.h())].into() - }, - Self::N(a, b) => { - let a = a.min_size(to)?.unwrap_or([0.into(), 0.into()].into()); - let b = b.min_size(to)?.unwrap_or([0.into(), 0.into()].into()); - [a.w().max(b.w()), a.h() + b.h()].into() - }, - _ => todo!() - })) - } - fn render (&self, to: &mut E::Output) -> Usually<()> { - let n = [0.into(), 0.into()].into(); - let s = to.area().wh().into(); - Ok(match self { +impl, Y: Content> Content for Bsp { + fn render (&self, to: &mut E::Output) { + let n = E::Size::zero(); + let s = to.wh(); + match self { Self::Null(_) => {}, - Self::S(a, b) => { - let s_a = a.min_size(s)?.unwrap_or(n); - let _ = b.min_size(s)?.unwrap_or(n); - let h = s_a.h().into(); - to.render_in(to.area().clip_h(h).into(), a)?; - to.render_in(to.area().shrink_y(h).push_y(h).into(), b)?; - }, - Self::E(a, b) => { - let s_a = a.min_size(s)?.unwrap_or(n); - let _ = b.min_size(s)?.unwrap_or(n); - let w = s_a.w().into(); - to.render_in(to.area().clip_w(w).into(), a)?; - to.render_in(to.area().push_x(w).shrink_x(w).into(), b)?; - }, - Self::W(a, b) => { - let s_a = a.min_size(s)?.unwrap_or(n); - let _ = b.min_size(s)?.unwrap_or(n); - let w = (to.area().w() - s_a.w()).into(); - to.render_in(to.area().push_x(w).into(), a)?; - to.render_in(to.area().shrink_x(w).into(), b)?; - }, - Self::N(a, b) => { - let s_a = a.min_size(s)?.unwrap_or(n); - let _ = b.min_size(s)?.unwrap_or(n); - let h = to.area().h() - s_a.h(); - to.render_in(to.area().push_y(h).into(), a)?; - to.render_in(to.area().shrink_y(h).into(), b)?; - }, + //Self::S(p, a, b) => { + //let s_a = a.min_size(s)?.unwrap_or(n); + //let _ = b.min_size(s)?.unwrap_or(n); + //let h = s_a.h().into(); + //to.render_in(to.area().clip_h(h).into(), a); + //to.render_in(to.area().shrink_y(h).push_y(h).into(), b); + //}, + //Self::E(p, a, b) => { + //let s_a = a.min_size(s)?.unwrap_or(n); + //let _ = b.min_size(s)?.unwrap_or(n); + //let w = s_a.w().into(); + //to.render_in(to.area().clip_w(w).into(), a); + //to.render_in(to.area().push_x(w).shrink_x(w).into(), b); + //}, + //Self::W(p, a, b) => { + //let s_a = a.min_size(s)?.unwrap_or(n); + //let _ = b.min_size(s)?.unwrap_or(n); + //let w = (to.area().w() - s_a.w()).into(); + //to.render_in(to.area().push_x(w).into(), a); + //to.render_in(to.area().shrink_x(w).into(), b); + //}, + //Self::N(p, a, b) => { + //let s_a = a.min_size(s)?.unwrap_or(n); + //let _ = b.min_size(s)?.unwrap_or(n); + //let h = to.area().h() - s_a.h(); + //to.render_in(to.area().push_y(h).into(), a); + //to.render_in(to.area().shrink_y(h).into(), b); + //}, _ => todo!() - }) + } } } diff --git a/layout/src/collection/layers.rs b/layout/src/collection/layers.rs deleted file mode 100644 index 5349ef0b..00000000 --- a/layout/src/collection/layers.rs +++ /dev/null @@ -1,56 +0,0 @@ -use crate::*; - -/// Renders multiple things on top of each other, -/// in the order they are provided by the callback. -/// Total size is largest width x largest height. -pub struct Layers>(pub F, PhantomData); - -/// Shorthand for defining an instance of [Layers]. -#[macro_export] macro_rules! lay { - ([$($expr:expr),* $(,)?]) => { - Layers::new(move|add|{ $(add(&$expr)?;)* Ok(()) }) - }; - (![$($expr:expr),* $(,)?]) => { - Layers::new(|add|{ $(add(&$expr)?;)* Ok(()) }) - }; - ($expr:expr) => { - Layers::new($expr) - }; -} - -impl< - E: Engine, - F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)->Usually<()>)->Usually<()> -> Layers { - #[inline] - pub fn new (build: F) -> Self { - Self(build, Default::default()) - } -} - -impl Render for Layers -where - F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)->Usually<()>)->Usually<()> -{ - fn min_size (&self, area: E::Size) -> Perhaps { - let mut w: E::Unit = 0.into(); - let mut h: E::Unit = 0.into(); - (self.0)(&mut |layer| { - if let Some(layer_area) = layer.min_size(area)? { - w = w.max(layer_area.w()); - h = h.max(layer_area.h()); - } - Ok(()) - })?; - Ok(Some([w, h].into())) - } - fn render (&self, to: &mut E::Output) -> Usually<()> { - if let Some(size) = self.min_size(to.area().wh().into())? { - (self.0)(&mut |layer|to.render_in(to.area().clip(size).into(), &layer)) - } else { - Ok(()) - } - } -} - - diff --git a/layout/src/collection/split.rs b/layout/src/collection/split.rs index 1c813218..cff769ed 100644 --- a/layout/src/collection/split.rs +++ b/layout/src/collection/split.rs @@ -2,9 +2,9 @@ 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: Render, B: Render; +where E: Engine, A: Content, B: Content; -impl, B: Render> Split { +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()) } @@ -22,148 +22,15 @@ impl, B: Render> Split { } } -impl, B: Render> Render for Split { - fn min_size (&self, to: E::Size) -> Perhaps { - Ok(Some(to)) - } - fn render (&self, to: &mut E::Output) -> Usually<()> { +impl, B: Content> Content for Split { + fn render (&self, to: &mut E::Output) { let (a, b) = self.1.split_fixed(to.area(), self.2); - Ok(if self.0 { - to.render_in(a.into(), &self.4)?; - to.render_in(b.into(), &self.3)?; + if self.0 { + to.place(a.into(), &self.4); + to.place(b.into(), &self.3); } else { - to.render_in(a.into(), &self.3)?; - to.render_in(b.into(), &self.4)?; - }) - } -} - -impl Render for Stack -where - F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)->Usually<()>)->Usually<()> -{ - fn min_size (&self, to: E::Size) -> Perhaps { - match self.1 { - - South => { - let mut w: E::Unit = 0.into(); - let mut h: E::Unit = 0.into(); - (self.0)(&mut |component: &dyn Render| { - let max = to.h().minus(h); - if max > E::Unit::zero() { - let item = Max::y(max, Push::y(h, component)); - let size = item.min_size(to)?.map(|size|size.wh()); - if let Some([width, height]) = size { - h = h + height.into(); - w = w.max(width); - } - } - Ok(()) - })?; - Ok(Some([w, h].into())) - }, - - East => { - let mut w: E::Unit = 0.into(); - let mut h: E::Unit = 0.into(); - (self.0)(&mut |component: &dyn Render| { - let max = to.w().minus(w); - if max > E::Unit::zero() { - let item = Max::x(max, Push::x(h, component)); - let size = item.min_size(to)?.map(|size|size.wh()); - if let Some([width, height]) = size { - w = w + width.into(); - h = h.max(height); - } - } - Ok(()) - })?; - Ok(Some([w, h].into())) - }, - - North => { - let mut w: E::Unit = 0.into(); - let mut h: E::Unit = 0.into(); - (self.0)(&mut |component: &dyn Render| { - let max = to.h().minus(h); - if max > E::Unit::zero() { - let item = Max::y(to.h() - h, component); - let size = item.min_size(to)?.map(|size|size.wh()); - if let Some([width, height]) = size { - h = h + height.into(); - w = w.max(width); - } - } - Ok(()) - })?; - Ok(Some([w, h].into())) - }, - - West => { - let w: E::Unit = 0.into(); - let h: E::Unit = 0.into(); - (self.0)(&mut |_component: &dyn Render| { - if w < to.w() { - todo!(); - } - Ok(()) - })?; - Ok(Some([w, h].into())) - }, + to.place(a.into(), &self.3); + to.place(b.into(), &self.4); } } - - fn render (&self, to: &mut E::Output) -> Usually<()> { - 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/collection/stack.rs b/layout/src/collection/stack.rs index a5594a0e..c178bc97 100644 --- a/layout/src/collection/stack.rs +++ b/layout/src/collection/stack.rs @@ -1,8 +1,8 @@ use crate::*; -pub struct Stack>(pub F, pub Direction, PhantomData); +pub struct Stack>(pub F, pub Direction, PhantomData); -impl> Stack { +impl> Stack { #[inline] pub fn new (direction: Direction, build: F) -> Self { Self(build, direction, Default::default()) } @@ -17,49 +17,101 @@ impl> Stack { } } -#[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 { + //([$($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(()) }) - }; -} +//#[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 4c379515..8c189e3f 100644 --- a/layout/src/direction.rs +++ b/layout/src/direction.rs @@ -3,10 +3,32 @@ use crate::*; /// A cardinal direction. #[derive(Copy, Clone, PartialEq)] pub enum Direction { North, South, West, East, } + pub use self::Direction::*; impl Direction { - #[inline] + pub fn is_north (&self) -> bool { matches!(self, Self::North) } + pub fn is_south (&self) -> bool { matches!(self, Self::South) } + pub fn is_east (&self) -> bool { matches!(self, Self::West) } + pub fn is_west (&self) -> bool { matches!(self, Self::East) } + /// Return next direction clockwise + pub fn cw (&self) -> Self { + match self { + Self::North => Self::East, + Self::South => Self::West, + Self::West => Self::North, + Self::East => Self::South, + } + } + /// Return next direction counterclockwise + pub fn ccw (&self) -> Self { + match self { + Self::North => Self::West, + Self::South => Self::East, + Self::West => Self::South, + Self::East => Self::North, + } + } pub fn split_fixed (self, area: impl Area, a: N) -> ([N;4],[N;4]) { match self { North => ( diff --git a/layout/src/lib.rs b/layout/src/lib.rs index 93cb5665..e8bdb134 100644 --- a/layout/src/lib.rs +++ b/layout/src/lib.rs @@ -1,7 +1,6 @@ mod collection; pub use self::collection::*; mod direction; pub use self::direction::*; -mod logic; pub use self::logic::*; -mod space; pub use self::space::*; +mod measure; pub use self::measure::*; mod transform; pub use self::transform::*; pub use ::tek_engine; @@ -11,3 +10,7 @@ pub(crate) use std::marker::PhantomData; #[cfg(test)] #[test] fn test_layout () -> Usually<()> { Ok(()) } + +#[cfg(test)] #[test] fn test_bsp () { + // TODO +} diff --git a/layout/src/logic.rs b/layout/src/logic.rs deleted file mode 100644 index ec78a026..00000000 --- a/layout/src/logic.rs +++ /dev/null @@ -1,29 +0,0 @@ -use crate::*; - -/// Conditional rendering, in unary and binary forms. -pub struct Cond; - -impl Cond { - /// Show an item conditionally. - pub fn when (cond: bool, item: Box>) -> When { - When(cond, item) - } - /// Show either of two items. - pub fn either (cond: bool, a: Box>, b: Box>) -> Either { - Either(cond, a, b) - } -} - -pub struct When(bool, Box>); -impl Layout for When { - fn layout (self, _: &mut E::Output) -> Option>> { - if self.0 { Some(self.1) } else { None } - } -} - -pub struct Either(bool, Box>, Box>); -impl Layout for Either { - fn layout (self, _: &mut E::Output) -> Option>> { - Some(if self.0 { self.1 } else { self.2 }) - } -} diff --git a/layout/src/space.rs b/layout/src/measure.rs similarity index 67% rename from layout/src/space.rs rename to layout/src/measure.rs index 05550930..93cdaca2 100644 --- a/layout/src/space.rs +++ b/layout/src/measure.rs @@ -3,31 +3,6 @@ use std::sync::{Arc, atomic::{AtomicUsize, Ordering::Relaxed}}; // TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small -impl Direction { - pub fn is_north (&self) -> bool { matches!(self, Self::North) } - pub fn is_south (&self) -> bool { matches!(self, Self::South) } - pub fn is_east (&self) -> bool { matches!(self, Self::West) } - pub fn is_west (&self) -> bool { matches!(self, Self::East) } - /// Return next direction clockwise - pub fn cw (&self) -> Self { - match self { - Self::North => Self::East, - Self::South => Self::West, - Self::West => Self::North, - Self::East => Self::South, - } - } - /// Return next direction counterclockwise - pub fn ccw (&self) -> Self { - match self { - Self::North => Self::West, - Self::South => Self::East, - Self::West => Self::South, - Self::East => Self::North, - } - } -} - pub trait HasSize { fn size (&self) -> &Measure; } @@ -48,7 +23,7 @@ pub struct Measure { pub y: Arc, } -impl Render for Measure { +impl Content for Measure { fn render (&self, to: &mut E::Output) { self.x.store(to.area().w().into(), Relaxed); self.y.store(to.area().h().into(), Relaxed); @@ -95,12 +70,12 @@ impl Measure { pub struct Scroll(pub F, pub Direction, pub u64, PhantomData) where E: Engine, - F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render)->Usually<()>)->Usually<()>; + F: Send + Sync + Fn(&mut dyn FnMut(&dyn Content)->Usually<()>)->Usually<()>; -//pub trait LayoutDebug { - //fn debug > (other: W) -> DebugOverlay { +//pub trait ContentDebug { + //fn debug > (other: W) -> DebugOverlay { //DebugOverlay(Default::default(), other) //} //} -//impl LayoutDebug for E {} +//impl ContentDebug for E {} diff --git a/layout/src/scroll.rs b/layout/src/scroll.rs deleted file mode 100644 index c7b7e813..00000000 --- a/layout/src/scroll.rs +++ /dev/null @@ -1 +0,0 @@ -use crate::*; diff --git a/layout/src/transform.rs b/layout/src/transform.rs index 4a500896..446f24ae 100644 --- a/layout/src/transform.rs +++ b/layout/src/transform.rs @@ -8,46 +8,60 @@ use crate::*; /// double generic. macro_rules! content_enum { ($Enum:ident: $($Variant:ident),+ $(,)?) => { - pub enum $Enum> { + pub enum $Enum> { _Unused(PhantomData), $($Variant(T)),+ } - impl> $Enum { - fn content (&self) -> Option> { - match self { - Self::_Unused(_) => None, - $(Self::$Variant(content) => Some(content)),+ - } - } - } } } /// 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|$render:expr) => { + ($self:ident : $Enum:ident |$to:ident|$area:expr) => { content_enum!($Enum: X, Y, XY); - impl> $Enum { + 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> Render for $Enum { - fn render (&$self, $to: &mut ::Output) { - $render + impl> Content for $Enum { + fn content (&self) -> Option> { + match self { + Self::_Unused(_) => None, + Self::X(item) => Some(item), + Self::Y(item) => Some(item), + Self::XY(item) => Some(item), + } + } + fn area (&$self, $to: ::Area) -> ::Area { + $area } } } } +transform_xy!(self: Fill |to|{ + let [x0, y0, wmax, hmax] = to.xywh(); + if let Some(content) = self.content() { + let [x, y, w, h] = content.area(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() + } + return [0.into(), 0.into(), 0.into(), 0.into(),].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|$render:expr) => { - pub enum $Enum> { + (|$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 { + 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) } @@ -66,86 +80,65 @@ macro_rules! transform_xy_unit { } } } - impl> $Enum { - fn content (&self) -> Option> { + impl> Content for $Enum { + fn content (&self) -> Option> { Some(match self { Self::X(_, content) => content, Self::Y(_, content) => content, Self::XY(_, _, content) => content, }) } - } - impl> Render for $Enum { - fn render (&$self, $to: &mut E::Output) -> Usually<()> { - $render + fn area (&$self, $to: E::Area) -> E::Area { + $area.into() } } } } -transform_xy!(|self: Fill, to|todo!()); - -transform_xy_unit!(|self: Fixed, to|{ - let [x, y, w, h] = to.area().xywh(); - to.render_in(match self { - Self::X(fw, _) => [x, y, fw, h], - Self::Y(fh, _) => [x, y, w, fh], - Self::XY(fw, fh, _) => [x, y, fw, fh], - }, self.content()) +transform_xy_unit!(|self: Fixed, to|match self { + Self::X(fw, _) => [to.x(), to.y(), *fw, to.h()], + Self::Y(fh, _) => [to.x(), to.y(), to.w(), *fh], + Self::XY(fw, fh, _) => [to.x(), to.y(), *fw, *fh], // tagn }); -transform_xy_unit!(|self: Shrink, to|to.render_in( - [to.x(), to.y(), to.w().minus(self.dx()), to.h().minus(self.dy())], - self.content())); +transform_xy_unit!(|self: Shrink, to| + [to.x(), to.y(), to.w().minus(self.dx()), to.h().minus(self.dy())]); -transform_xy_unit!(|self: Expand, to|to.render_in( - [to.x(), to.y(), to.w() + self.dx(), to.h() + self.dy()], - self.content())); +transform_xy_unit!(|self: Expand, to| + [to.x(), to.y(), to.w() + self.dx(), to.h() + self.dy()]); -transform_xy_unit!(|self: Min, to|to.render_in(match self { - Self::X(mw, _) => [to.x(), to.y(), to.w().max(mw), to.h()], - Self::Y(mh, _) => [to.x(), to.y(), to.w(), to.h().max(mh)], - Self::XY(mw, mh, _) => [to.x(), to.y(), to.w().max(mw), to.h().max(mh)] -}, self.content())); +transform_xy_unit!(|self: Min, to|match self { + Self::X(mw, _) => [to.x(), to.y(), to.w().max(*mw), to.h()], + Self::Y(mh, _) => [to.x(), to.y(), to.w(), to.h().max(*mh)], + Self::XY(mw, mh, _) => [to.x(), to.y(), to.w().max(*mw), to.h().max(*mh)] +}); -transform_xy_unit!(|self: Max, to|to.render_in(match self { - Self::X(mw, _) => [to.x(), to.y(), to.w().min(mw), to.h()], - Self::Y(mh, _) => [to.x(), to.y(), to.w(), to.h().min(mh)], - Self::XY(mw, mh, _) => [to.x(), to.y(), to.w().min(mw), to.h().min(mh)], -}, self.content())); +transform_xy_unit!(|self: Max, to|match self { + Self::X(mw, _) => [to.x(), to.y(), to.w().min(*mw), to.h()], + Self::Y(mh, _) => [to.x(), to.y(), to.w(), to.h().min(*mh)], + Self::XY(mw, mh, _) => [to.x(), to.y(), to.w().min(*mw), to.h().min(*mh)], +}); -transform_xy_unit!(|self: Push, to|to.render_in( - [to.x() + self.dx(), to.y() + self.dy(), to.w(), to.h()], - self.content())); +transform_xy_unit!(|self: Push, to| + [to.x() + self.dx(), to.y() + self.dy(), to.w(), to.h()]); -transform_xy_unit!(|self: Pull, to|to.render_in( - [to.x().minus(self.dx()), to.y().minus(self.dy()), to.w(), to.h()], - self.content())); +transform_xy_unit!(|self: Pull, to| + [to.x().minus(self.dx()), to.y().minus(self.dy()), to.w(), to.h()]); transform_xy_unit!(|self: Margin, to|{ let dx = self.dx(); let dy = self.dy(); - to.render_in([ - to.x().minus(dx), - to.y().minus(dy), - to.w() + dy + dy, - to.h() + dy + dy, - ]) + [to.x().minus(dx), to.y().minus(dy), to.w() + dy + dy, to.h() + dy + dy] }); transform_xy_unit!(|self: Padding, to|{ let dx = self.dx(); let dy = self.dy(); - to.render_in([ - to.x() + dx, - to.y() + dy, - to.w().minus(dy + dy), - to.h().minus(dy + dy), - ]) + [to.x() + dx, to.y() + dy, to.w().minus(dy + dy), to.h().minus(dy + dy), ] }); content_enum!(Align: Center, X, Y, NW, N, NE, E, SE, S, SW, W); -impl> Align { +impl> Align { pub fn c (w: T) -> Self { Self::Center(w) } pub fn x (w: T) -> Self { Self::X(w) } pub fn y (w: T) -> Self { Self::Y(w) } @@ -159,7 +152,7 @@ impl> Align { pub fn se (w: T) -> Self { Self::SE(w) } } -fn align, N: Coordinate, R: Area + From<[N;4]>> (align: &Align, outer: R, content: R) -> Option { +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 { @@ -182,17 +175,14 @@ fn align, N: Coordinate, R: Area + From<[N;4]>> (alig } } -impl> Render for Align { - fn min_size (&self, outer_area: E::Size) -> Perhaps { - self.content().min_size(outer_area) - } - fn render (&self, to: &mut E::Output) -> Usually<()> { +impl> Content for Align { + fn render (&self, to: &mut E::Output) { let outer_area = to.area(); - Ok(if let Some(content_size) = self.min_size(outer_area.wh().into())? { - let content_area = outer_area.clip(content_size); - if let Some(aligned) = align(&self, outer_area.into(), content_area.into()) { - to.render_in(aligned, &self.content())? + if let Some(content) = self.content() { + let inner_area = content.area(outer_area); + if let Some(aligned) = align(&self, outer_area.into(), inner_area.into()) { + to.place(aligned, &content) } - }) + } } }