diff --git a/output/src/layout/layout_cond.rs b/output/.scratch.rs similarity index 59% rename from output/src/layout/layout_cond.rs rename to output/.scratch.rs index 485d5b3..0e5db23 100644 --- a/output/src/layout/layout_cond.rs +++ b/output/.scratch.rs @@ -1,45 +1,4 @@ -use crate::*; -/// Show an item only when a condition is true. -pub struct When(bool, T, PhantomData); -impl> When { - /// Create a binary condition. - pub const fn new (c: bool, a: T) -> Self { Self(c, a, PhantomData) } -} -impl> Layout for When { - fn layout (&self, to: O::Area) -> O::Area { - let Self(cond, item, ..) = self; - if *cond { item.layout(to) } else { O::Area::zero().into() } - } -} -impl> Draw for When { - fn draw (&self, to: &mut O) { - let Self(cond, item, ..) = self; - if *cond { Bounded(self.layout(to.area()), item).draw(to) } - } -} - -/// Show one item if a condition is true and another if the condition is false -pub struct Either(pub bool, pub A, pub B, pub PhantomData); -impl, B: Content> Either { - /// Create a ternary view condition. - pub const fn new (c: bool, a: A, b: B) -> Self { - Self(c, a, b, PhantomData) - } -} -impl, B: Layout> Layout for Either { - fn layout (&self, to: E::Area) -> E::Area { - let Self(cond, a, b, ..) = self; - if *cond { a.layout(to) } else { b.layout(to) } - } -} -impl, B: Content> Draw for Either { - fn draw (&self, to: &mut E) { - let Self(cond, a, b, ..) = self; - let area = self.layout(to.area()); - if *cond { Bounded(area, a).draw(to) } else { Bounded(area, b).draw(to) } - } -} /////////////////////////////////////////////////////////////////////////////// @@ -55,7 +14,7 @@ impl, B: Content> Draw for Either { //)))) //} //} - ///// The syntagm `(either :condition :content1 :content2)` corresponds to an [Either] layout element. + ///// The syntagm `(either :condition :content1 :content2)` corresponds to an [Either] layout element. //impl FromDsl for Either where S: Eval + Eval + Eval { //fn try_provide (state: &S, source: &DslVal) -> Perhaps { //source.exp_match("either", |_, tail|Ok(Some(Self( @@ -209,3 +168,184 @@ impl, B: Content> Draw for Either { //)), //_ => None //}) +//use crate::*; +//use Direction::*; + +//pub struct Stack<'x, E, F1> { + //__: PhantomData<&'x (E, F1)>, + //direction: Direction, + //callback: F1 +//} + +//impl<'x, E, F1> Stack<'x, E, F1> { + //pub fn new (direction: Direction, callback: F1) -> Self { + //Self { direction, callback, __: Default::default(), } + //} + //pub fn above (callback: F1) -> Self { + //Self::new(Above, callback) + //} + //pub fn below (callback: F1) -> Self { + //Self::new(Below, callback) + //} + //pub fn north (callback: F1) -> Self { + //Self::new(North, callback) + //} + //pub fn south (callback: F1) -> Self { + //Self::new(South, callback) + //} + //pub fn east (callback: F1) -> Self { + //Self::new(East, callback) + //} + //pub fn west (callback: F1) -> Self { + //Self::new(West, callback) + //} +//} + +//impl<'x, E: Out, F1: Fn(&mut dyn FnMut(&dyn Layout))> Layout for Stack<'x, E, F1> { + //fn layout (&self, to: E::Area) -> E::Area { + //let state = StackLayoutState::::new(self.direction, to); + //(self.callback)(&mut |component: &dyn Layout|{ + //let StackLayoutState { x, y, w_remaining, h_remaining, .. } = *state.borrow(); + //let [_, _, w, h] = component.layout([x, y, w_remaining, h_remaining].into()).xywh(); + //state.borrow_mut().grow(w, h); + //}); + //let StackLayoutState { w_used, h_used, .. } = *state.borrow(); + //match self.direction { + //North | West => { todo!() }, + //South | East => { [to.x(), to.y(), w_used, h_used].into() }, + //_ => unreachable!(), + //} + //} +//} +//impl<'x, E: Out, F1: Fn(&mut dyn FnMut(&dyn Draw))> Draw for Stack<'x, E, F1> { + //fn draw (&self, to: &mut E) { + //let state = StackLayoutState::::new(self.direction, to.area()); + //let to = Rc::new(RefCell::new(to)); + //(self.callback)(&mut |component: &dyn Draw|{ + //let StackLayoutState { x, y, w_remaining, h_remaining, .. } = *state.borrow(); + //let layout = component.layout([x, y, w_remaining, h_remaining].into()); + //state.borrow_mut().grow(layout.w(), layout.h()); + //to.borrow_mut().place_at(layout, component); + //}); + //} +//} + +//#[derive(Copy, Clone)] +//struct StackLayoutState { + //direction: Direction, + //x: E::Unit, + //y: E::Unit, + //w_used: E::Unit, + //h_used: E::Unit, + //w_remaining: E::Unit, + //h_remaining: E::Unit, +//} +//impl StackLayoutState { + //fn new (direction: Direction, area: E::Area) -> std::rc::Rc> { + //let [x, y, w_remaining, h_remaining] = area.xywh(); + //std::rc::Rc::new(std::cell::RefCell::new(Self { + //direction, + //x, y, w_remaining, h_remaining, + //w_used: E::Unit::zero(), h_used: E::Unit::zero() + //})) + //} + //fn grow (&mut self, w: E::Unit, h: E::Unit) -> &mut Self { + //match self.direction { + //South => { self.y = self.y.plus(h); + //self.h_used = self.h_used.plus(h); + //self.h_remaining = self.h_remaining.minus(h); + //self.w_used = self.w_used.max(w); }, + //East => { self.x = self.x.plus(w); + //self.w_used = self.w_used.plus(w); + //self.w_remaining = self.w_remaining.minus(w); + //self.h_used = self.h_used.max(h); }, + //North | West => { todo!() }, + //Above | Below => {}, + //}; + //self + //} + //fn area_remaining (&self) -> E::Area { + //[self.x, self.y, self.w_remaining, self.h_remaining].into() + //} +//} + +////pub struct Stack<'a, E, F1> { + ////__: PhantomData<&'a (E, F1)>, + ////direction: Direction, + ////callback: F1 +////} +////impl<'a, E, F1> Stack<'a, E, F1> where + ////E: Out, F1: Fn(&mut dyn FnMut(&'a dyn Draw)) + Send + Sync, +////{ + ////pub fn north (callback: F1) -> Self { Self::new(North, callback) } + ////pub fn south (callback: F1) -> Self { Self::new(South, callback) } + ////pub fn east (callback: F1) -> Self { Self::new(East, callback) } + ////pub fn west (callback: F1) -> Self { Self::new(West, callback) } + ////pub fn above (callback: F1) -> Self { Self::new(Above, callback) } + ////pub fn below (callback: F1) -> Self { Self::new(Below, callback) } + ////pub fn new (direction: Direction, callback: F1) -> Self { + ////Self { direction, callback, __: Default::default(), } + ////} +////} +////impl<'a, E, F1> Draw for Stack<'a, E, F1> where + ////E: Out, F1: Fn(&mut dyn FnMut(&'a dyn Draw)) + Send + Sync, +////{ + ////fn layout (&self, to: E::Area) -> E::Area { + ////let state = StackLayoutState::::new(self.direction, to); + ////let mut adder = { + ////let state = state.clone(); + ////move|component: &dyn Draw|{ + ////let [w, h] = component.layout(state.borrow().area_remaining()).wh(); + ////state.borrow_mut().grow(w, h); + ////} + ////}; + ////(self.callback)(&mut adder); + ////let StackLayoutState { w_used, h_used, .. } = *state.borrow(); + ////match self.direction { + ////North | West => { todo!() }, + ////South | East => { [to.x(), to.y(), w_used, h_used].into() }, + ////Above | Below => { [to.x(), to.y(), to.w(), to.h()].into() }, + ////} + ////} + ////fn draw (&self, to: &mut E) { + ////let state = StackLayoutState::::new(self.direction, to.area()); + ////let mut adder = { + ////let state = state.clone(); + ////move|component: &dyn Draw|{ + ////let [x, y, w, h] = component.layout(state.borrow().area_remaining()).xywh(); + ////state.borrow_mut().grow(w, h); + ////to.place_at([x, y, w, h].into(), component); + ////} + ////}; + ////(self.callback)(&mut adder); + ////} +////} + +//[>Stack::down(|add|{ + //let mut i = 0; + //for (_, name) in self.dirs.iter() { + //if i >= self.scroll { + //add(&Tui::bold(i == self.index, name.as_str()))?; + //} + //i += 1; + //} + //for (_, name) in self.files.iter() { + //if i >= self.scroll { + //add(&Tui::bold(i == self.index, name.as_str()))?; + //} + //i += 1; + //} + //add(&format!("{}/{i}", self.index))?; + //Ok(()) +//}));*/ + + +//#[test] fn test_iter_map () { + //struct Foo; + //impl Content for Foo {} + //fn _make_map + Send + Sync> (data: &Vec) -> impl Draw { + //Map::new(||data.iter(), |_foo, _index|{}) + //} + //let _data = vec![Foo, Foo, Foo]; + ////let map = make_map(&data); +//} diff --git a/output/src/content.rs b/output/src/content.rs deleted file mode 100644 index 2925259..0000000 --- a/output/src/content.rs +++ /dev/null @@ -1,26 +0,0 @@ -use crate::*; - -pub trait Content: Draw + Layout {} - -impl + Layout> Content for T {} - -impl<'a, O: Out> AsRef + 'a> for dyn Content + 'a { - fn as_ref (&self) -> &(dyn Draw + 'a) { self } -} - -impl<'a, O: Out> AsRef + 'a> for dyn Content + 'a { - fn as_ref (&self) -> &(dyn Layout + 'a) { self } -} - -pub trait HasContent { - fn content (&self) -> impl Content; -} - -//impl> Draw for T { - //fn draw (&self, to: &mut O) { - //let area = to.area(); - //*to.area_mut() = self.0; - //self.content().draw(to); - //*to.area_mut() = area; - //} -//} diff --git a/output/src/draw.rs b/output/src/draw.rs deleted file mode 100644 index 12ef066..0000000 --- a/output/src/draw.rs +++ /dev/null @@ -1,30 +0,0 @@ -use crate::*; - -/// Drawable with dynamic dispatch. -pub trait Draw { - fn draw (&self, to: &mut O); -} -impl Draw for () { - fn draw (&self, _: &mut O) {} -} -impl Draw for fn(&mut O) { - fn draw (&self, to: &mut O) { (*self)(to) } -} -impl Draw for Box> { - fn draw (&self, to: &mut O) { (**self).draw(to) } -} -impl> Draw for &D { - fn draw (&self, to: &mut O) { (*self).draw(to) } -} -impl> Draw for &mut D { - fn draw (&self, to: &mut O) { (**self).draw(to) } -} -impl> Draw for Arc { - fn draw (&self, to: &mut O) { (**self).draw(to) } -} -impl> Draw for RwLock { - fn draw (&self, to: &mut O) { self.read().unwrap().draw(to) } -} -impl> Draw for Option { - fn draw (&self, to: &mut O) { if let Some(draw) = self { draw.draw(to) } } -} diff --git a/output/src/group.rs b/output/src/group.rs deleted file mode 100644 index 4c949c7..0000000 --- a/output/src/group.rs +++ /dev/null @@ -1,12 +0,0 @@ -#[allow(unused)] use crate::*; - -pub struct Group(T); - -impl Group { - pub const fn new () -> Group<()> { - Group(()) - } - pub const fn add (self, value: U) -> Group<(T, U)> { - Group((self.0, value)) - } -} diff --git a/output/src/layout.rs b/output/src/layout.rs deleted file mode 100644 index 50ddec8..0000000 --- a/output/src/layout.rs +++ /dev/null @@ -1,199 +0,0 @@ -mod layout_cond; pub use self::layout_cond::*; -mod layout_map; pub use self::layout_map::*; -mod layout_pad; pub use self::layout_pad::*; -mod layout_move; pub use self::layout_move::*; -mod layout_size; pub use self::layout_size::*; -mod layout_stack; //pub use self::layout_stack::*; - -/// Stack 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 use self::align::*; mod align { - use crate::*; - use Alignment::*; - - /// 9th of area to place. - #[derive(Debug, Copy, Clone, Default)] - pub enum Alignment { #[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W } - - /// Align position of inner area to middle, side, or corner of outer area. - /// - /// - /// ``` - /// use ::tengri::{output::*, tui::*}; - /// let area: [u16;4] = [10, 10, 20, 20]; - /// fn test (area: [u16;4], item: &impl Draw, expected: [u16;4]) { - /// assert_eq!(Content::layout(item, area), expected); - /// assert_eq!(Draw::layout(item, area), expected); - /// }; - /// - /// let four = ||Fixed::XY(4, 4, ""); - /// test(area, &Align::nw(four()), [10, 10, 4, 4]); - /// test(area, &Align::n(four()), [18, 10, 4, 4]); - /// test(area, &Align::ne(four()), [26, 10, 4, 4]); - /// test(area, &Align::e(four()), [26, 18, 4, 4]); - /// test(area, &Align::se(four()), [26, 26, 4, 4]); - /// test(area, &Align::s(four()), [18, 26, 4, 4]); - /// test(area, &Align::sw(four()), [10, 26, 4, 4]); - /// test(area, &Align::w(four()), [10, 18, 4, 4]); - /// - /// let two_by_four = ||Fixed::XY(4, 2, ""); - /// test(area, &Align::nw(two_by_four()), [10, 10, 4, 2]); - /// test(area, &Align::n(two_by_four()), [18, 10, 4, 2]); - /// test(area, &Align::ne(two_by_four()), [26, 10, 4, 2]); - /// test(area, &Align::e(two_by_four()), [26, 19, 4, 2]); - /// test(area, &Align::se(two_by_four()), [26, 28, 4, 2]); - /// test(area, &Align::s(two_by_four()), [18, 28, 4, 2]); - /// test(area, &Align::sw(two_by_four()), [10, 28, 4, 2]); - /// test(area, &Align::w(two_by_four()), [10, 19, 4, 2]); - /// ``` - pub struct Align(Alignment, T); - impl Align { - #[inline] pub const fn c (a: T) -> Self { Self(Alignment::Center, a) } - #[inline] pub const fn x (a: T) -> Self { Self(Alignment::X, a) } - #[inline] pub const fn y (a: T) -> Self { Self(Alignment::Y, a) } - #[inline] pub const fn n (a: T) -> Self { Self(Alignment::N, a) } - #[inline] pub const fn s (a: T) -> Self { Self(Alignment::S, a) } - #[inline] pub const fn e (a: T) -> Self { Self(Alignment::E, a) } - #[inline] pub const fn w (a: T) -> Self { Self(Alignment::W, a) } - #[inline] pub const fn nw (a: T) -> Self { Self(Alignment::NW, a) } - #[inline] pub const fn sw (a: T) -> Self { Self(Alignment::SW, a) } - #[inline] pub const fn ne (a: T) -> Self { Self(Alignment::NE, a) } - #[inline] pub const fn se (a: T) -> Self { Self(Alignment::SE, a) } - } - - impl> Draw for Align { - fn draw (&self, to: &mut O) { Bounded(self.layout(to.area()), &self.1).draw(to) } - } - - impl> Layout for Align { - fn x (&self, to: O::Area) -> O::Unit { - match self.0 { - NW | W | SW => to.x(), - N | Center | S => to.x().plus(to.w() / 2.into()).minus(self.1.w(to) / 2.into()), - NE | E | SE => to.x().plus(to.w()).minus(self.1.w(to)), - _ => todo!(), - } - } - fn y (&self, to: O::Area) -> O::Unit { - match self.0 { - NW | N | NE => to.y(), - W | Center | E => to.y().plus(to.h() / 2.into()).minus(self.1.h(to) / 2.into()), - SW | S | SE => to.y().plus(to.h()).minus(self.1.h(to)), - _ => todo!(), - } - } - } - -} - -pub use self::bsp::*; mod bsp { - use crate::*; - - /// A binary split or layer. - pub struct Bsp( - pub(crate) Direction, - /// First element. - pub(crate) Head, - /// Second element. - pub(crate) Tail, - ); - - impl Bsp { - #[inline] pub const fn n (a: Head, b: Tail) -> Self { Self(North, a, b) } - #[inline] pub const fn s (a: Head, b: Tail) -> Self { Self(South, a, b) } - #[inline] pub const fn e (a: Head, b: Tail) -> Self { Self(East, a, b) } - #[inline] pub const fn w (a: Head, b: Tail) -> Self { Self(West, a, b) } - #[inline] pub const fn a (a: Head, b: Tail) -> Self { Self(Above, a, b) } - #[inline] pub const fn b (a: Head, b: Tail) -> Self { Self(Below, a, b) } - } - - impl, Tail: Content> Draw for Bsp { - fn draw (&self, to: &mut O) { - match self.0 { - South => { - //panic!("{}", self.1.h(to.area())); - let area_1 = self.1.layout(to.area()); - let area_2 = self.2.layout([ - to.area().x(), - to.area().y().plus(area_1.h()), - to.area().w(), - to.area().h().minus(area_1.h()) - ].into()); - //panic!("{area_1:?} {area_2:?}"); - to.place_at(area_1, &self.1); - to.place_at(area_2, &self.2); - }, - _ => todo!("{:?}", self.0) - } - //let [a, b, _] = bsp_areas(to.area(), self.0, &self.1, &self.2); - //panic!("{a:?} {b:?}"); - //if self.0 == Below { - //to.place_at(a, &self.1); - //to.place_at(b, &self.2); - //} else { - //to.place_at(b, &self.2); - //to.place_at(a, &self.1); - //} - } - } - impl, Tail: Layout> Layout for Bsp { - fn w (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | North | South => self.1.w(area).max(self.2.w(area)), East | West => self.1.min_w(area).plus(self.2.w(area)), } } - fn min_w (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | North | South => self.1.min_w(area).max(self.2.min_w(area)), East | West => self.1.min_w(area).plus(self.2.min_w(area)), } } - fn max_w (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | North | South => self.1.max_w(area).max(self.2.max_w(area)), East | West => self.1.max_w(area).plus(self.2.max_w(area)), } } - fn h (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | East | West => self.1.h(area).max(self.2.h(area)), North | South => self.1.h(area).plus(self.2.h(area)), } } - fn min_h (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | East | West => self.1.min_h(area).max(self.2.min_h(area)), North | South => self.1.min_h(area).plus(self.2.min_h(area)), } } - fn max_h (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | North | South => self.1.max_h(area).max(self.2.max_h(area)), East | West => self.1.max_h(area).plus(self.2.max_h(area)), } } - fn layout (&self, area: O::Area) -> O::Area { bsp_areas(area, self.0, &self.1, &self.2)[2] } - } - - fn bsp_areas , B: Layout> (area: O::Area, direction: Direction, a: &A, b: &B,) -> [O::Area;3] { - let [x, y, w, h] = area.xywh(); - let [aw, ah] = a.layout(area).wh(); - let [bw, bh] = b.layout(match direction { - Above | Below => area, - South => [x, y + ah, w, h.minus(ah)].into(), - North => [x, y, w, h.minus(ah)].into(), - East => [x + aw, y, w.minus(aw), h].into(), - West => [x, y, w.minus(aw), h].into(), - }).wh(); - match direction { - Above | Below => { - let [x, y, w, h] = area.center_xy([aw.max(bw), ah.max(bh)]); - let a = [(x + w/2.into()).minus(aw/2.into()), (y + h/2.into()).minus(ah/2.into()), aw, ah]; - let b = [(x + w/2.into()).minus(bw/2.into()), (y + h/2.into()).minus(bh/2.into()), bw, bh]; - [a.into(), b.into(), [x, y, w, h].into()] - }, - South => { - let [x, y, w, h] = area.center_xy([aw.max(bw), ah + bh]); - let a = [(x + w/2.into()).minus(aw/2.into()), y, aw, ah]; - let b = [(x + w/2.into()).minus(bw/2.into()), y + ah, bw, bh]; - [a.into(), b.into(), [x, y, w, h].into()] - }, - North => { - let [x, y, w, h] = area.center_xy([aw.max(bw), ah + bh]); - let a = [(x + (w/2.into())).minus(aw/2.into()), y + bh, aw, ah]; - let b = [(x + (w/2.into())).minus(bw/2.into()), y, bw, bh]; - [a.into(), b.into(), [x, y, w, h].into()] - }, - East => { - let [x, y, w, h] = area.center_xy([aw + bw, ah.max(bh)]); - let a = [x, (y + h/2.into()).minus(ah/2.into()), aw, ah]; - let b = [x + aw, (y + h/2.into()).minus(bh/2.into()), bw, bh]; - [a.into(), b.into(), [x, y, w, h].into()] - }, - West => { - let [x, y, w, h] = area.center_xy([aw + bw, ah.max(bh)]); - let a = [x + bw, (y + h/2.into()).minus(ah/2.into()), aw, ah]; - let b = [x, (y + h/2.into()).minus(bh/2.into()), bw, bh]; - [a.into(), b.into(), [x, y, w, h].into()] - }, - } - } -} diff --git a/output/src/layout/layout_iter.rs b/output/src/layout/layout_iter.rs deleted file mode 100644 index e69de29..0000000 diff --git a/output/src/layout/layout_map.rs b/output/src/layout/layout_map.rs deleted file mode 100644 index ffd7005..0000000 --- a/output/src/layout/layout_map.rs +++ /dev/null @@ -1,139 +0,0 @@ -use crate::*; - -/// Draws items from an iterator. -pub struct Map -where - I: Iterator + Send + Sync, - F: Fn() -> I + Send + Sync, -{ - __: PhantomData<(O, B)>, - /// Function that returns iterator over stacked components - get_iter: F, - /// Function that returns each stacked component - get_item: G, -} - -impl<'a, O, A, B, I, F, G> Map where - I: Iterator + Send + Sync + 'a, - F: Fn() -> I + Send + Sync + 'a, -{ - pub const fn new (get_iter: F, get_item: G) -> Self { - Self { - __: PhantomData, - get_iter, - get_item - } - } -} - -macro_rules! impl_map_direction (($name:ident, $axis:ident, $align:ident)=>{ - impl<'a, O, A, B, I, F> Map< - O, A, Push>>>, I, F, fn(A, usize)->B - > where - O: Out, - B: Draw, - I: Iterator + Send + Sync + 'a, - F: Fn() -> I + Send + Sync + 'a - { - pub const fn $name ( - size: O::Unit, - get_iter: F, - get_item: impl Fn(A, usize)->B + Send + Sync - ) -> Map< - O, A, - Push>>, - I, F, - impl Fn(A, usize)->Push>> + Send + Sync - > { - Map { - __: PhantomData, - get_iter, - get_item: move |item: A, index: usize|{ - // FIXME: multiply - let mut push: O::Unit = O::Unit::from(0u16); - for _ in 0..index { - push = push + size; - } - Push::$axis(push, Align::$align(Fixed::$axis(size, get_item(item, index)))) - } - } - } - } -}); - -impl_map_direction!(east, X, w); -impl_map_direction!(south, Y, n); -impl_map_direction!(west, X, e); -impl_map_direction!(north, Y, s); - -impl<'a, O, A, B, I, F, G> Layout for Map where - O: Out, - B: Layout, - I: Iterator + Send + Sync + 'a, - F: Fn() -> I + Send + Sync + 'a, - G: Fn(A, usize)->B + Send + Sync -{ - fn layout (&self, area: O::Area) -> O::Area { - let Self { get_iter, get_item, .. } = self; - let mut index = 0; - let [mut min_x, mut min_y] = area.center(); - let [mut max_x, mut max_y] = area.center(); - for item in get_iter() { - let [x,y,w,h] = get_item(item, index).layout(area).xywh(); - min_x = min_x.min(x); - min_y = min_y.min(y); - max_x = max_x.max(x + w); - max_y = max_y.max(y + h); - index += 1; - } - let w = max_x - min_x; - let h = max_y - min_y; - //[min_x.into(), min_y.into(), w.into(), h.into()].into() - area.center_xy([w.into(), h.into()]).into() - } -} -impl<'a, O, A, B, I, F, G> Draw for Map where - O: Out, - B: Content, - I: Iterator + Send + Sync + 'a, - F: Fn() -> I + Send + Sync + 'a, - G: Fn(A, usize)->B + Send + Sync -{ - fn draw (&self, to: &mut O) { - let Self { get_iter, get_item, .. } = self; - let mut index = 0; - let area = self.layout(to.area()); - for item in get_iter() { - let item = get_item(item, index); - //to.place_at(area.into(), &item); - to.place_at(item.layout(area), &item); - index += 1; - } - } -} - -#[inline] pub fn map_south( - item_offset: O::Unit, - item_height: O::Unit, - item: impl Content -) -> impl Content { - Push::Y(item_offset, Fixed::Y(item_height, Fill::X(item))) -} - -#[inline] pub fn map_south_west( - item_offset: O::Unit, - item_height: O::Unit, - item: impl Content -) -> impl Content { - Push::Y(item_offset, Align::nw(Fixed::Y(item_height, Fill::X(item)))) -} - -#[inline] pub fn map_east( - item_offset: O::Unit, - item_width: O::Unit, - item: impl Content -) -> impl Content { - Push::X(item_offset, Align::w(Fixed::X(item_width, Fill::Y(item)))) -} - - diff --git a/output/src/layout/layout_move.rs b/output/src/layout/layout_move.rs deleted file mode 100644 index c634cc3..0000000 --- a/output/src/layout/layout_move.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::*; -/// Increment X and/or Y coordinate. -pub enum Push { X(U, A), Y(U, A), XY(U, U, A), } -impl Push { - #[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c, } } -} -impl Push { - #[inline] pub fn dx (&self) -> U { match self { Self::X(x, ..) | Self::XY(x, _, _) => *x, Self::Y(_, _) => 0.into() } } - #[inline] pub fn dy (&self) -> U { match self { Self::Y(y, ..) | Self::XY(_, y, _) => *y, Self::X(_, _) => 0.into() } } -} -impl> Draw for Push { - fn draw (&self, to: &mut O) { Bounded(self.layout(to.area()), self.inner()).draw(to) } -} -impl> Layout for Push { - fn x (&self, area: O::Area) -> O::Unit { area.x().plus(self.dx()) } - fn y (&self, area: O::Area) -> O::Unit { area.y().plus(self.dy()) } -} -/// Decrement X and/or Y coordinate. -pub enum Pull { X(U, A), Y(U, A), XY(U, U, A), } -impl Pull { - #[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c, } } -} -impl Pull { - #[inline] pub fn dx (&self) -> U { match self { Self::X(x, ..) | Self::XY(x, _, _) => *x, Self::Y(_, _) => 0.into() } } - #[inline] pub fn dy (&self) -> U { match self { Self::Y(y, ..) | Self::XY(_, y, _) => *y, Self::X(_, _) => 0.into() } } -} -impl> Draw for Pull { - fn draw (&self, to: &mut O) { Bounded(self.layout(to.area()), self.inner()).draw(to) } -} -impl> Layout for Pull { - fn x (&self, area: O::Area) -> O::Unit { area.x().minus(self.dx()) } - fn y (&self, area: O::Area) -> O::Unit { area.y().minus(self.dy()) } -} diff --git a/output/src/layout/layout_pad.rs b/output/src/layout/layout_pad.rs deleted file mode 100644 index b625ef8..0000000 --- a/output/src/layout/layout_pad.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::*; -use Pad::*; -pub enum Pad { X(U, A), Y(U, A), XY(U, U, A), } -impl Pad { - #[inline] pub const fn inner (&self) -> &A { match self { X(_, c) | Y(_, c) | XY(_, _, c) => c, } } -} -impl Pad { - #[inline] pub fn dx (&self) -> U { match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } } - #[inline] pub fn dy (&self) -> U { match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } } -} -impl> Draw for Pad { - fn draw (&self, to: &mut O) { Bounded(self.layout(to.area()), self.inner()).draw(to) } -} -impl> Layout for Pad { - fn x (&self, area: O::Area) -> O::Unit { - area.x().plus(self.dx()) - } - fn y (&self, area: O::Area) -> O::Unit { - area.x().plus(self.dx()) - } - fn w (&self, area: O::Area) -> O::Unit { - area.w().minus(self.dx() * 2.into()) - } - fn h (&self, area: O::Area) -> O::Unit { - area.h().minus(self.dy() * 2.into()) - } -} diff --git a/output/src/layout/layout_size.rs b/output/src/layout/layout_size.rs deleted file mode 100644 index 6ef5f7c..0000000 --- a/output/src/layout/layout_size.rs +++ /dev/null @@ -1,121 +0,0 @@ -use crate::*; - -pub enum Fill { X(A), Y(A), XY(A) } -impl Fill { - #[inline] pub const fn inner (&self) -> &A { match self { Self::X(c) | Self::Y(c) | Self::XY(c) => c } } - #[inline] pub const fn dx (&self) -> bool { match self { Self::X(_) | Self::XY(_) => true, _ => false } } - #[inline] pub const fn dy (&self) -> bool { match self { Self::Y(_) | Self::XY(_) => true, _ => false } } -} -impl> Draw for Fill { - fn draw (&self, to: &mut O) { Bounded(self.layout(to.area()), self.inner()).draw(to) } -} -impl> Layout for Fill { - fn x (&self, area: O::Area) -> O::Unit { if self.dx() { area.x() } else { self.inner().x(area) } } - fn y (&self, area: O::Area) -> O::Unit { if self.dy() { area.y() } else { self.inner().y(area) } } - fn w (&self, area: O::Area) -> O::Unit { if self.dx() { area.w() } else { self.inner().w(area) } } - fn min_w (&self, area: O::Area) -> O::Unit { if self.dx() { area.w() } else { self.inner().min_w(area) } } - fn max_w (&self, area: O::Area) -> O::Unit { if self.dx() { area.w() } else { self.inner().max_w(area) } } - fn h (&self, area: O::Area) -> O::Unit { if self.dy() { area.h() } else { self.inner().h(area) } } - fn min_h (&self, area: O::Area) -> O::Unit { if self.dy() { area.h() } else { self.inner().min_h(area) } } - fn max_h (&self, area: O::Area) -> O::Unit { if self.dy() { area.h() } else { self.inner().max_h(area) } } -} -/// Set fixed size for content. -pub enum Fixed { X(U, A), Y(U, A), XY(U, U, A), } -impl Fixed { - #[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c } } -} -impl Fixed { - #[inline] pub const fn dx (&self) -> Option { match self { Self::X(x, _) | Self::XY(x, ..) => Some(*x), _ => None } } - #[inline] pub const fn dy (&self) -> Option { match self { Self::Y(y, _) | Self::XY(y, ..) => Some(*y), _ => None } } -} -impl> Draw for Fixed { - fn draw (&self, to: &mut O) { Bounded(self.layout(to.area()), self.inner()).draw(to) } -} -impl> Layout for Fixed { - fn w (&self, area: O::Area) -> O::Unit { self.dx().unwrap_or(self.inner().w(area)) } - fn min_w (&self, area: O::Area) -> O::Unit { self.dx().unwrap_or(self.inner().min_w(area)) } - fn max_w (&self, area: O::Area) -> O::Unit { self.dx().unwrap_or(self.inner().max_w(area)) } - fn h (&self, area: O::Area) -> O::Unit { self.dy().unwrap_or(self.inner().h(area)) } - fn min_h (&self, area: O::Area) -> O::Unit { self.dy().unwrap_or(self.inner().min_h(area)) } - fn max_h (&self, area: O::Area) -> O::Unit { self.dy().unwrap_or(self.inner().max_h(area)) } -} - -pub enum Max { X(U, A), Y(U, A), XY(U, U, A), } -impl Max { - #[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c } } -} -impl Max { - #[inline] pub const fn dx (&self) -> Option { match self { Self::X(x, _) | Self::XY(x, ..) => Some(*x), _ => None } } - #[inline] pub const fn dy (&self) -> Option { match self { Self::Y(y, _) | Self::XY(y, ..) => Some(*y), _ => None } } -} -impl> Draw for Max { - fn draw (&self, to: &mut O) { Bounded(self.layout(to.area()), self.inner()).draw(to) } -} -impl> Layout for Max { - fn layout (&self, area: E::Area) -> E::Area { - let [x, y, w, h] = self.inner().layout(area).xywh(); - match self { - Self::X(mw, _) => [x, y, w.min(*mw), h], - Self::Y(mh, _) => [x, y, w, h.min(*mh)], - Self::XY(mw, mh, _) => [x, y, w.min(*mw), h.min(*mh)], - }.into() - } -} - -pub enum Min { X(U, A), Y(U, A), XY(U, U, A), } -impl Min { - #[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c } } -} -impl Min { - #[inline] pub const fn dx (&self) -> Option { match self { Self::X(x, _) | Self::XY(x, ..) => Some(*x), _ => None } } - #[inline] pub const fn dy (&self) -> Option { match self { Self::Y(y, _) | Self::XY(y, ..) => Some(*y), _ => None } } -} -impl> Draw for Min { - fn draw (&self, to: &mut O) { Bounded(self.layout(to.area()), self.inner()).draw(to) } -} -impl> Layout for Min { - fn layout (&self, area: E::Area) -> E::Area { - let [x, y, w, h] = self.inner().layout(area).xywh(); - match self { - Self::X(mw, _) => [x, y, w.max(*mw), h], - Self::Y(mh, _) => [x, y, w, h.max(*mh)], - Self::XY(mw, mh, _) => [x, y, w.max(*mw), h.max(*mh)], - }.into() - } -} - -pub enum Expand { X(U, A), Y(U, A), XY(U, U, A), } -impl Expand { - #[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c } } -} -impl Expand { - #[inline] pub const fn dx (&self) -> Option { match self { Self::X(x, _) | Self::XY(x, ..) => Some(*x), _ => None } } - #[inline] pub const fn dy (&self) -> Option { match self { Self::Y(y, _) | Self::XY(y, ..) => Some(*y), _ => None } } -} -impl> Draw for Expand { - fn draw (&self, to: &mut O) { Bounded(self.layout(to.area()), self.inner()).draw(to) } -} -impl> Layout for Expand { - fn w (&self, to: O::Area) -> O::Unit { self.inner().w(to).plus(self.dx().unwrap_or_default()) } - fn h (&self, to: O::Area) -> O::Unit { self.inner().w(to).plus(self.dy().unwrap_or_default()) } -} - -pub enum Shrink { X(U, A), Y(U, A), XY(U, U, A), } -impl Shrink { - #[inline] pub const fn inner (&self) -> &A { match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c } } -} -impl Shrink { - #[inline] pub const fn dx (&self) -> Option { match self { Self::X(x, _) | Self::XY(x, ..) => Some(*x), _ => None } } - #[inline] pub const fn dy (&self) -> Option { match self { Self::Y(y, _) | Self::XY(y, ..) => Some(*y), _ => None } } -} -impl> Draw for Shrink { - fn draw (&self, to: &mut O) { Bounded(self.layout(to.area()), self.inner()).draw(to) } -} -impl> Layout for Shrink { - fn layout (&self, to: E::Area) -> E::Area { - let area = self.inner().layout(to); - let dx = self.dx().unwrap_or_default(); - let dy = self.dy().unwrap_or_default(); - [area.x(), area.y(), area.w().minus(dx), area.h().minus(dy)].into() - } -} diff --git a/output/src/layout/layout_stack.rs b/output/src/layout/layout_stack.rs deleted file mode 100644 index d5a311d..0000000 --- a/output/src/layout/layout_stack.rs +++ /dev/null @@ -1,170 +0,0 @@ -//use crate::*; -//use Direction::*; - -//pub struct Stack<'x, E, F1> { - //__: PhantomData<&'x (E, F1)>, - //direction: Direction, - //callback: F1 -//} - -//impl<'x, E, F1> Stack<'x, E, F1> { - //pub fn new (direction: Direction, callback: F1) -> Self { - //Self { direction, callback, __: Default::default(), } - //} - //pub fn above (callback: F1) -> Self { - //Self::new(Above, callback) - //} - //pub fn below (callback: F1) -> Self { - //Self::new(Below, callback) - //} - //pub fn north (callback: F1) -> Self { - //Self::new(North, callback) - //} - //pub fn south (callback: F1) -> Self { - //Self::new(South, callback) - //} - //pub fn east (callback: F1) -> Self { - //Self::new(East, callback) - //} - //pub fn west (callback: F1) -> Self { - //Self::new(West, callback) - //} -//} - -//impl<'x, E: Out, F1: Fn(&mut dyn FnMut(&dyn Layout))> Layout for Stack<'x, E, F1> { - //fn layout (&self, to: E::Area) -> E::Area { - //let state = StackLayoutState::::new(self.direction, to); - //(self.callback)(&mut |component: &dyn Layout|{ - //let StackLayoutState { x, y, w_remaining, h_remaining, .. } = *state.borrow(); - //let [_, _, w, h] = component.layout([x, y, w_remaining, h_remaining].into()).xywh(); - //state.borrow_mut().grow(w, h); - //}); - //let StackLayoutState { w_used, h_used, .. } = *state.borrow(); - //match self.direction { - //North | West => { todo!() }, - //South | East => { [to.x(), to.y(), w_used, h_used].into() }, - //_ => unreachable!(), - //} - //} -//} -//impl<'x, E: Out, F1: Fn(&mut dyn FnMut(&dyn Draw))> Draw for Stack<'x, E, F1> { - //fn draw (&self, to: &mut E) { - //let state = StackLayoutState::::new(self.direction, to.area()); - //let to = Rc::new(RefCell::new(to)); - //(self.callback)(&mut |component: &dyn Draw|{ - //let StackLayoutState { x, y, w_remaining, h_remaining, .. } = *state.borrow(); - //let layout = component.layout([x, y, w_remaining, h_remaining].into()); - //state.borrow_mut().grow(layout.w(), layout.h()); - //to.borrow_mut().place_at(layout, component); - //}); - //} -//} - -//#[derive(Copy, Clone)] -//struct StackLayoutState { - //direction: Direction, - //x: E::Unit, - //y: E::Unit, - //w_used: E::Unit, - //h_used: E::Unit, - //w_remaining: E::Unit, - //h_remaining: E::Unit, -//} -//impl StackLayoutState { - //fn new (direction: Direction, area: E::Area) -> std::rc::Rc> { - //let [x, y, w_remaining, h_remaining] = area.xywh(); - //std::rc::Rc::new(std::cell::RefCell::new(Self { - //direction, - //x, y, w_remaining, h_remaining, - //w_used: E::Unit::zero(), h_used: E::Unit::zero() - //})) - //} - //fn grow (&mut self, w: E::Unit, h: E::Unit) -> &mut Self { - //match self.direction { - //South => { self.y = self.y.plus(h); - //self.h_used = self.h_used.plus(h); - //self.h_remaining = self.h_remaining.minus(h); - //self.w_used = self.w_used.max(w); }, - //East => { self.x = self.x.plus(w); - //self.w_used = self.w_used.plus(w); - //self.w_remaining = self.w_remaining.minus(w); - //self.h_used = self.h_used.max(h); }, - //North | West => { todo!() }, - //Above | Below => {}, - //}; - //self - //} - //fn area_remaining (&self) -> E::Area { - //[self.x, self.y, self.w_remaining, self.h_remaining].into() - //} -//} - -////pub struct Stack<'a, E, F1> { - ////__: PhantomData<&'a (E, F1)>, - ////direction: Direction, - ////callback: F1 -////} -////impl<'a, E, F1> Stack<'a, E, F1> where - ////E: Out, F1: Fn(&mut dyn FnMut(&'a dyn Draw)) + Send + Sync, -////{ - ////pub fn north (callback: F1) -> Self { Self::new(North, callback) } - ////pub fn south (callback: F1) -> Self { Self::new(South, callback) } - ////pub fn east (callback: F1) -> Self { Self::new(East, callback) } - ////pub fn west (callback: F1) -> Self { Self::new(West, callback) } - ////pub fn above (callback: F1) -> Self { Self::new(Above, callback) } - ////pub fn below (callback: F1) -> Self { Self::new(Below, callback) } - ////pub fn new (direction: Direction, callback: F1) -> Self { - ////Self { direction, callback, __: Default::default(), } - ////} -////} -////impl<'a, E, F1> Draw for Stack<'a, E, F1> where - ////E: Out, F1: Fn(&mut dyn FnMut(&'a dyn Draw)) + Send + Sync, -////{ - ////fn layout (&self, to: E::Area) -> E::Area { - ////let state = StackLayoutState::::new(self.direction, to); - ////let mut adder = { - ////let state = state.clone(); - ////move|component: &dyn Draw|{ - ////let [w, h] = component.layout(state.borrow().area_remaining()).wh(); - ////state.borrow_mut().grow(w, h); - ////} - ////}; - ////(self.callback)(&mut adder); - ////let StackLayoutState { w_used, h_used, .. } = *state.borrow(); - ////match self.direction { - ////North | West => { todo!() }, - ////South | East => { [to.x(), to.y(), w_used, h_used].into() }, - ////Above | Below => { [to.x(), to.y(), to.w(), to.h()].into() }, - ////} - ////} - ////fn draw (&self, to: &mut E) { - ////let state = StackLayoutState::::new(self.direction, to.area()); - ////let mut adder = { - ////let state = state.clone(); - ////move|component: &dyn Draw|{ - ////let [x, y, w, h] = component.layout(state.borrow().area_remaining()).xywh(); - ////state.borrow_mut().grow(w, h); - ////to.place_at([x, y, w, h].into(), component); - ////} - ////}; - ////(self.callback)(&mut adder); - ////} -////} - -//[>Stack::down(|add|{ - //let mut i = 0; - //for (_, name) in self.dirs.iter() { - //if i >= self.scroll { - //add(&Tui::bold(i == self.index, name.as_str()))?; - //} - //i += 1; - //} - //for (_, name) in self.files.iter() { - //if i >= self.scroll { - //add(&Tui::bold(i == self.index, name.as_str()))?; - //} - //i += 1; - //} - //add(&format!("{}/{i}", self.index))?; - //Ok(()) -//}));*/ diff --git a/output/src/lib.rs b/output/src/lib.rs index 7148692..10348d7 100644 --- a/output/src/lib.rs +++ b/output/src/lib.rs @@ -11,88 +11,145 @@ //#![feature(non_lifetime_binders)] pub(crate) use self::Direction::*; pub(crate) use std::fmt::{Debug, Display}; -pub(crate) use std::marker::PhantomData; pub(crate) use std::ops::{Add, Sub, Mul, Div}; pub(crate) use std::sync::{Arc, RwLock, atomic::{AtomicUsize, Ordering::Relaxed}}; +pub(crate) use std::marker::PhantomData; pub(crate) use dizzle::*; -mod output; pub use self::output::*; -mod content; pub use self::content::*; -mod draw; pub use self::draw::*; -mod group; pub use self::group::*; -mod layout; pub use self::layout::*; -mod space; pub use self::space::*; -mod thunk; pub use self::thunk::*; -mod widget; pub use self::widget::*; -#[cfg(feature = "dsl")] mod view; -#[cfg(feature = "dsl")] pub use self::view::*; -#[cfg(test)] pub(crate) use proptest_derive::Arbitrary; -#[cfg(test)] mod test { - use crate::{*, Direction::*}; - //use proptest_derive::Arbitrary; - use proptest::{prelude::*, option::of}; - macro_rules! test_op_transform { - ($fn:ident, $Op:ident) => { - proptest! { - #[test] fn $fn ( - op_x in of(u16::MIN..u16::MAX), - op_y in of(u16::MIN..u16::MAX), - content in "\\PC*", - x in u16::MIN..u16::MAX, - y in u16::MIN..u16::MAX, - w in u16::MIN..u16::MAX, - h in u16::MIN..u16::MAX, - ) { - if let Some(op) = match (op_x, op_y) { - (Some(x), Some(y)) => Some($Op::XY(x, y, content)), - (Some(x), None) => Some($Op::X(x, content)), - (None, Some(y)) => Some($Op::Y(y, content)), - _ => None - } { - //assert_eq!(Content::layout(&op, [x, y, w, h]), - //Draw::layout(&op, [x, y, w, h])); - } - } + +mod out_macros; // Must be defined first +mod out_traits; pub use self::out_traits::*; +mod out_structs; pub use self::out_structs::*; +mod out_impls; pub use self::out_impls::*; + +mod widget; pub use self::widget::*; +mod layout; pub use self::layout::*; + +#[cfg(test)] mod out_tests; + +#[cfg(feature = "dsl")] +pub fn evaluate_output_expression <'a, O: Out + 'a, S> ( + state: &S, output: &mut O, expr: &'a impl Expression +) -> Usually where + S: View + + for<'b>Namespace<'b, bool> + + for<'b>Namespace<'b, O::Unit> +{ + // First element of expression is used for dispatch. + // Dispatch is proto-namespaced using separator character + let head = expr.head()?; + let mut frags = head.src()?.unwrap_or_default().split("/"); + // The rest of the tokens in the expr are arguments. + // Their meanings depend on the dispatched operation + let args = expr.tail(); + let arg0 = args.head(); + let tail0 = args.tail(); + let arg1 = tail0.head(); + let tail1 = tail0.tail(); + let arg2 = tail1.head(); + // And we also have to do the above binding dance + // so that the Perhapss remain in scope. + match frags.next() { + + Some("when") => output.place(&When::new( + state.resolve(arg0?)?.unwrap(), + Thunk::new(move|output: &mut O|state.view(output, &arg1).unwrap()) + )), + + Some("either") => output.place(&Either::new( + state.resolve(arg0?)?.unwrap(), + Thunk::new(move|output: &mut O|state.view(output, &arg1).unwrap()), + Thunk::new(move|output: &mut O|state.view(output, &arg2).unwrap()) + )), + + Some("bsp") => output.place(&{ + let a = Thunk::new(move|output: &mut O|state.view(output, &arg0).unwrap()); + let b = Thunk::new(move|output: &mut O|state.view(output, &arg1).unwrap()); + match frags.next() { + Some("n") => Bsp::n(a, b), + Some("s") => Bsp::s(a, b), + Some("e") => Bsp::e(a, b), + Some("w") => Bsp::w(a, b), + Some("a") => Bsp::a(a, b), + Some("b") => Bsp::b(a, b), + frag => unimplemented!("bsp/{frag:?}") } - } - } - test_op_transform!(proptest_op_fixed, Fixed); - test_op_transform!(proptest_op_min, Min); - test_op_transform!(proptest_op_max, Max); - test_op_transform!(proptest_op_push, Push); - test_op_transform!(proptest_op_pull, Pull); - test_op_transform!(proptest_op_shrink, Shrink); - test_op_transform!(proptest_op_expand, Expand); - test_op_transform!(proptest_op_padding, Pad); + }), - proptest! { - #[test] fn proptest_op_bsp ( - d in prop_oneof![ - Just(North), Just(South), - Just(East), Just(West), - Just(Above), Just(Below) - ], - a in "\\PC*", - b in "\\PC*", - x in u16::MIN..u16::MAX, - y in u16::MIN..u16::MAX, - w in u16::MIN..u16::MAX, - h in u16::MIN..u16::MAX, - ) { - let bsp = Bsp(d, a, b); - //assert_eq!( - //Content::layout(&bsp, [x, y, w, h]), - //Draw::layout(&bsp, [x, y, w, h]), - //); - } - } + Some("align") => output.place(&{ + let a = Thunk::new(move|output: &mut O|state.view(output, &arg0).unwrap()); + match frags.next() { + Some("n") => Align::n(a), + Some("s") => Align::s(a), + Some("e") => Align::e(a), + Some("w") => Align::w(a), + Some("x") => Align::x(a), + Some("y") => Align::y(a), + Some("c") => Align::c(a), + frag => unimplemented!("align/{frag:?}") + } + }), - //#[test] fn test_iter_map () { - //struct Foo; - //impl Content for Foo {} - //fn _make_map + Send + Sync> (data: &Vec) -> impl Draw { - //Map::new(||data.iter(), |_foo, _index|{}) - //} - //let _data = vec![Foo, Foo, Foo]; - ////let map = make_map(&data); - //} + Some("fill") => output.place(&{ + let a = Thunk::new(move|output: &mut O|state.view(output, &arg0).unwrap()); + match frags.next() { + Some("xy") | None => Fill::XY(a), + Some("x") => Fill::X(a), + Some("y") => Fill::Y(a), + frag => unimplemented!("fill/{frag:?}") + } + }), + + Some("fixed") => output.place(&{ + let axis = frags.next(); + let arg = match axis { Some("x") | Some("y") => arg1, Some("xy") | None => arg2, _ => panic!("fixed: unsupported axis {axis:?}") }; + let cb = Thunk::new(move|output: &mut O|state.view(output, &arg).unwrap()); + match axis { + Some("xy") | None => Fixed::XY(state.resolve(arg0?)?.unwrap(), state.resolve(arg1?)?.unwrap(), cb), + Some("x") => Fixed::X(state.resolve(arg0?)?.unwrap(), cb), + Some("y") => Fixed::Y(state.resolve(arg0?)?.unwrap(), cb), + frag => unimplemented!("fixed/{frag:?} ({expr:?}) ({head:?}) ({:?})", + head.src()?.unwrap_or_default().split("/").next()) + } + }), + + Some("min") => output.place(&{ + let axis = frags.next(); + let arg = match axis { Some("x") | Some("y") => arg1, Some("xy") | None => arg2, _ => panic!("fixed: unsupported axis {axis:?}") }; + let cb = Thunk::new(move|output: &mut O|state.view(output, &arg).unwrap()); + match axis { + Some("xy") | None => Min::XY(state.resolve(arg0?)?.unwrap(), state.resolve(arg1?)?.unwrap(), cb), + Some("x") => Min::X(state.resolve(arg0?)?.unwrap(), cb), + Some("y") => Min::Y(state.resolve(arg0?)?.unwrap(), cb), + frag => unimplemented!("min/{frag:?}") + } + }), + + Some("max") => output.place(&{ + let axis = frags.next(); + let arg = match axis { Some("x") | Some("y") => arg1, Some("xy") | None => arg2, _ => panic!("fixed: unsupported axis {axis:?}") }; + let cb = Thunk::new(move|output: &mut O|state.view(output, &arg).unwrap()); + match axis { + Some("xy") | None => Max::XY(state.resolve(arg0?)?.unwrap(), state.resolve(arg1?)?.unwrap(), cb), + Some("x") => Max::X(state.resolve(arg0?)?.unwrap(), cb), + Some("y") => Max::Y(state.resolve(arg0?)?.unwrap(), cb), + frag => unimplemented!("max/{frag:?}") + } + }), + + Some("push") => output.place(&{ + let axis = frags.next(); + let arg = match axis { Some("x") | Some("y") => arg1, Some("xy") | None => arg2, _ => panic!("fixed: unsupported axis {axis:?}") }; + let cb = Thunk::new(move|output: &mut O|state.view(output, &arg).unwrap()); + match axis { + Some("xy") | None => Push::XY(state.resolve(arg0?)?.unwrap(), state.resolve(arg1?)?.unwrap(), cb), + Some("x") => Push::X(state.resolve(arg0?)?.unwrap(), cb), + Some("y") => Push::Y(state.resolve(arg0?)?.unwrap(), cb), + frag => unimplemented!("push/{frag:?}") + } + }), + + _ => return Ok(false) + + }; + Ok(true) } diff --git a/output/src/out_impls.rs b/output/src/out_impls.rs new file mode 100644 index 0000000..8dcab55 --- /dev/null +++ b/output/src/out_impls.rs @@ -0,0 +1,790 @@ +use crate::*; + +impl Coord for u16 { + fn plus (self, other: Self) -> Self { self.saturating_add(other) } +} + +impl HasXY for XY { + fn x (&self) -> N { self.0 } + fn y (&self) -> N { self.1 } +} + +impl HasWH for WH { + fn w (&self) -> N { self.0 } + fn h (&self) -> N { self.1 } +} + +impl WH { + fn clip_w (&self, w: N) -> [N;2] { [self.w().min(w), self.h()] } + fn clip_h (&self, h: N) -> [N;2] { [self.w(), self.h().min(h)] } + fn to_area_pos (&self) -> [N;4] { let [x, y] = self.wh(); [x, y, 0.into(), 0.into()] } + fn to_area_size (&self) -> [N;4] { let [w, h] = self.wh(); [0.into(), 0.into(), w, h] } + fn expect_min (&self, w: N, h: N) -> Usually<&Self> { + if self.w() < w || self.h() < h { return Err(format!("min {w}x{h}").into()) } + Ok(self) + } +} + +impl XYWH { + fn from_position (pos: impl Size) -> Self { + let [x, y] = pos.wh(); + [x, y, 0.into(), 0.into()] + } + fn from_size (size: impl Size) -> Self { + let [w, h] = size.wh(); + [0.into(), 0.into(), w, h] + } + fn lrtb (&self) -> [N;4] { + [self.x(), self.x2(), self.y(), self.y2()] + } + fn center (&self) -> XY { + [self.x().plus(self.w()/2.into()), self.y().plus(self.h()/2.into())] + } + fn centered (&self) -> XY { + [self.x().minus(self.w()/2.into()), self.y().minus(self.h()/2.into())] + } + fn centered_x (&self, n: N) -> XYWH { + let [x, y, w, h] = self.xywh(); + [(x.plus(w / 2.into())).minus(n / 2.into()), y.plus(h / 2.into()), n, 1.into()] + } + fn centered_y (&self, n: N) -> XYWH { + let [x, y, w, h] = self.xywh(); + [x.plus(w / 2.into()), (y.plus(h / 2.into())).minus(n / 2.into()), 1.into(), n] + } + fn centered_xy (&self, [n, m]: [N;2]) -> XYWH { + let [x, y, w, h] = self.xywh(); + [(x.plus(w / 2.into())).minus(n / 2.into()), (y.plus(h / 2.into())).minus(m / 2.into()), n, m] + } + fn iter_x (&self) -> impl Iterator where N: std::iter::Step { + self.x()..(self.x()+self.w()) + } + fn iter_y (&self) -> impl Iterator where N: std::iter::Step { + self.y()..(self.y()+self.h()) + } + fn clipped (&self, wh: impl Size) -> XYWH { + [self.x(), self.y(), wh.w(), wh.h()] + } + fn clipped_h (&self, h: N) -> XYWH { + [self.x(), self.y(), self.w(), self.h().min(h)] + } + fn clipped_w (&self, w: N) -> XYWH { + [self.x(), self.y(), self.w().min(w), self.h()] + } + fn with_w (&self, w: N) -> XYWH { + [self.x(), self.y(), w, self.h()] + } + fn with_h (&self, h: N) -> XYWH { + [self.x(), self.y(), self.w(), h] + } +} + +impl HasXY for XYWH { + fn x (&self) -> N { self.0 } + fn y (&self) -> N { self.1 } +} + +impl HasWH for XYWH { + fn w (&self) -> N { self.2 } + fn h (&self) -> N { self.3 } +} + +impl super::ng::HasXY for O { + // X coordinate of output area + #[inline] fn x (&self) -> O::Unit { self.area().x() } + // Y coordinate of output area + #[inline] fn y (&self) -> O::Unit { self.area().y() } +} + +impl super::ng::HasWH for O { + // Width of output area + #[inline] fn w (&self) -> O::Unit { self.area().w() } + // Height of output area + #[inline] fn h (&self) -> O::Unit { self.area().h() } +} + +impl + Layout> Content for T {} + +impl<'a, O: Out> AsRef + 'a> for dyn Content + 'a { + fn as_ref (&self) -> &(dyn Draw + 'a) { self } +} + +impl<'a, O: Out> AsRef + 'a> for dyn Content + 'a { + fn as_ref (&self) -> &(dyn Layout + 'a) { self } +} + +pub trait HasContent { + fn content (&self) -> impl Content; +} + +impl Draw for () { + fn draw (&self, _: &mut O) {} +} + +impl Draw for fn(&mut O) { + fn draw (&self, to: &mut O) { (*self)(to) } +} + +impl Draw for Box> { + fn draw (&self, to: &mut O) { (**self).draw(to) } +} + +impl> Draw for &D { + fn draw (&self, to: &mut O) { (*self).draw(to) } +} + +impl> Draw for &mut D { + fn draw (&self, to: &mut O) { (**self).draw(to) } +} + +impl> Draw for Arc { + fn draw (&self, to: &mut O) { (**self).draw(to) } +} + +impl> Draw for RwLock { + fn draw (&self, to: &mut O) { self.read().unwrap().draw(to) } +} + +impl> Draw for Option { + fn draw (&self, to: &mut O) { if let Some(draw) = self { draw.draw(to) } } +} + +//impl> Draw for T { + //fn draw (&self, to: &mut O) { + //let area = to.area(); + //*to.area_mut() = self.0; + //self.content().draw(to); + //*to.area_mut() = area; + //} +//} + +impl, F: Fn()->T> Lazy { + pub const fn new (thunk: F) -> Self { + Self(thunk, PhantomData) + } +} + +impl Thunk { + pub const fn new (draw: F) -> Self { + Self(PhantomData, draw) + } +} + +impl Layout for Thunk {} + +impl Draw for Thunk { + fn draw (&self, to: &mut O) { + (self.1)(to) + } +} + +impl Memo { + pub fn new (value: T, view: U) -> Self { Self { value, view: Arc::new(view.into()) } } + pub fn update (&mut self, newval: T, draw: impl Fn(&mut U, &T, &T)->R) -> Option { + if newval != self.value { + let result = draw(&mut*self.view.write().unwrap(), &newval, &self.value); + self.value = newval; + return Some(result); + } + None + } +} + +impl Direction { + pub fn split_fixed (self, area: impl Area, a: N) -> ([N;4],[N;4]) { + let [x, y, w, h] = area.xywh(); + match self { + North => ([x, y.plus(h).minus(a), w, a], [x, y, w, h.minus(a)]), + South => ([x, y, w, a], [x, y.plus(a), w, h.minus(a)]), + East => ([x, y, a, h], [x.plus(a), y, w.minus(a), h]), + West => ([x.plus(w).minus(a), y, a, h], [x, y, w.minus(a), h]), + Above | Below => (area.xywh(), area.xywh()) + } + } +} + +impl Size for (N, N) { + fn x (&self) -> N { self.0 } + fn y (&self) -> N { self.1 } +} + +impl Size for [N;2] { + fn x (&self) -> N { self[0] } + fn y (&self) -> N { self[1] } +} + +impl>> HasSize for T { + fn size (&self) -> &Measure { + self.get() + } +} + +impl Clone for Measure { + fn clone (&self) -> Self { + Self { __: Default::default(), x: self.x.clone(), y: self.y.clone(), } + } +} + +impl Layout for Measure {} + +impl PartialEq for Measure { + fn eq (&self, other: &Self) -> bool { + self.x.load(Relaxed) == other.x.load(Relaxed) && + self.y.load(Relaxed) == other.y.load(Relaxed) + } +} + +// TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small +impl Draw for Measure { + fn draw (&self, to: &mut O) { + self.x.store(to.area().w().into(), Relaxed); + self.y.store(to.area().h().into(), Relaxed); + } +} + +impl Debug for Measure { + fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + f.debug_struct("Measure").field("width", &self.x).field("height", &self.y).finish() + } +} + +impl Measure { + pub fn set_w (&self, w: impl Into) -> &Self { self.x.store(w.into(), Relaxed); self } + pub fn set_h (&self, h: impl Into) -> &Self { self.y.store(h.into(), Relaxed); self } + pub fn set_wh (&self, w: impl Into, h: impl Into) -> &Self { self.set_w(w); self.set_h(h); self } + pub fn format (&self) -> Arc { format!("{}x{}", self.w(), self.h()).into() } + pub fn of > (&self, item: T) -> Bsp, T> { Bsp::b(Fill::XY(self), item) } + pub fn new (x: O::Unit, y: O::Unit) -> Self { + Self { __: PhantomData::default(), x: Arc::new(x.into()), y: Arc::new(y.into()), } + } +} + +impl From<[O::Unit; 2]> for Measure { + fn from ([x, y]: [O::Unit; 2]) -> Self { Self::new(x, y) } +} + +impl Layout for () { + fn x (&self, a: O::Area) -> O::Unit { a.x() } + fn y (&self, a: O::Area) -> O::Unit { a.y() } + fn w (&self, _: O::Area) -> O::Unit { 0.into() } + fn w_min (&self, _: O::Area) -> O::Unit { 0.into() } + fn w_max (&self, _: O::Area) -> O::Unit { 0.into() } + fn h (&self, _: O::Area) -> O::Unit { 0.into() } + fn h_min (&self, _: O::Area) -> O::Unit { 0.into() } + fn h_max (&self, _: O::Area) -> O::Unit { 0.into() } + fn layout (&self, a: O::Area) -> O::Area { [a.x(), a.y(), 0.into(), 0.into()].into() } +} + +impl> Layout for &L { + fn x (&self, a: O::Area) -> O::Unit { (*self).x(a) } + fn y (&self, a: O::Area) -> O::Unit { (*self).y(a) } + fn w (&self, a: O::Area) -> O::Unit { (*self).w(a) } + fn w_min (&self, a: O::Area) -> O::Unit { (*self).w_min(a) } + fn w_max (&self, a: O::Area) -> O::Unit { (*self).w_max(a) } + fn h (&self, a: O::Area) -> O::Unit { (*self).h(a) } + fn h_min (&self, a: O::Area) -> O::Unit { (*self).h_min(a) } + fn h_max (&self, a: O::Area) -> O::Unit { (*self).h_max(a) } + fn layout (&self, a: O::Area) -> O::Area { (*self).layout(a) } +} + +impl> Layout for &mut L { + fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) } + fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) } + fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) } + fn w_min (&self, a: O::Area) -> O::Unit { (**self).w_min(a) } + fn w_max (&self, a: O::Area) -> O::Unit { (**self).w_max(a) } + fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) } + fn h_min (&self, a: O::Area) -> O::Unit { (**self).h_min(a) } + fn h_max (&self, a: O::Area) -> O::Unit { (**self).h_max(a) } + fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) } +} + +impl> Layout for Arc { + fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) } + fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) } + fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) } + fn w_min (&self, a: O::Area) -> O::Unit { (**self).w_min(a) } + fn w_max (&self, a: O::Area) -> O::Unit { (**self).w_max(a) } + fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) } + fn h_min (&self, a: O::Area) -> O::Unit { (**self).h_min(a) } + fn h_max (&self, a: O::Area) -> O::Unit { (**self).h_max(a) } + fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) } +} + +impl Layout for Box> { + fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) } + fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) } + fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) } + fn w_min (&self, a: O::Area) -> O::Unit { (**self).w_min(a) } + fn w_max (&self, a: O::Area) -> O::Unit { (**self).w_max(a) } + fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) } + fn h_min (&self, a: O::Area) -> O::Unit { (**self).h_min(a) } + fn h_max (&self, a: O::Area) -> O::Unit { (**self).h_max(a) } + fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) } +} + +impl> Layout for RwLock { + fn x (&self, a: O::Area) -> O::Unit { self.read().unwrap().x(a) } + fn y (&self, a: O::Area) -> O::Unit { self.read().unwrap().y(a) } + fn w (&self, a: O::Area) -> O::Unit { self.read().unwrap().w(a) } + fn w_min (&self, a: O::Area) -> O::Unit { self.read().unwrap().w_min(a) } + fn w_max (&self, a: O::Area) -> O::Unit { self.read().unwrap().w_max(a) } + fn h (&self, a: O::Area) -> O::Unit { self.read().unwrap().h(a) } + fn h_min (&self, a: O::Area) -> O::Unit { self.read().unwrap().h_min(a) } + fn h_max (&self, a: O::Area) -> O::Unit { self.read().unwrap().h_max(a) } + fn layout (&self, a: O::Area) -> O::Area { self.read().unwrap().layout(a) } +} + +impl> Layout for Option { + fn x (&self, to: O::Area) -> O::Unit { self.as_ref().map(|c|c.x(to)).unwrap_or(to.x()) } + fn y (&self, to: O::Area) -> O::Unit { self.as_ref().map(|c|c.y(to)).unwrap_or(to.y()) } + fn w_min (&self, to: O::Area) -> O::Unit { self.as_ref().map(|c|c.w_min(to)).unwrap_or(0.into()) } + fn w_max (&self, to: O::Area) -> O::Unit { self.as_ref().map(|c|c.w_max(to)).unwrap_or(0.into()) } + fn w (&self, to: O::Area) -> O::Unit { self.as_ref().map(|c|c.w(to)).unwrap_or(0.into()) } + fn h_min (&self, to: O::Area) -> O::Unit { self.as_ref().map(|c|c.h_min(to)).unwrap_or(0.into()) } + fn h_max (&self, to: O::Area) -> O::Unit { self.as_ref().map(|c|c.h_max(to)).unwrap_or(0.into()) } + fn h (&self, to: O::Area) -> O::Unit { self.as_ref().map(|c|c.h(to)).unwrap_or(0.into()) } + fn layout (&self, to: O::Area) -> O::Area { self.as_ref().map(|c|c.layout([self.x(to), self.y(to), self.w(to), self.h(to)].into())) + .unwrap_or([to.x(), to.y(), 0.into(), 0.into()].into()) } +} + +impl> HasContent for Bounded { + fn content (&self) -> impl Content { + &self.1 + } +} + +impl> Draw for Bounded { + fn draw (&self, to: &mut O) { + let area = to.area(); + *to.area_mut() = self.0; + self.1.draw(to); + *to.area_mut() = area; + } +} + +impl> When { + /// Create a binary condition. + pub const fn new (c: bool, a: T) -> Self { Self(c, a, PhantomData) } +} + +impl> Layout for When { + fn layout (&self, to: O::Area) -> O::Area { + let Self(cond, item, ..) = self; + if *cond { item.layout(to) } else { O::Area::zero().into() } + } +} + +impl> Draw for When { + fn draw (&self, to: &mut O) { + let Self(cond, item, ..) = self; + if *cond { Bounded(self.layout(to.area()), item).draw(to) } + } +} + +impl, B: Content> Either { + /// Create a ternary view condition. + pub const fn new (c: bool, a: A, b: B) -> Self { + Self(c, a, b, PhantomData) + } +} + +impl, B: Layout> Layout for Either { + fn layout (&self, to: E::Area) -> E::Area { + let Self(cond, a, b, ..) = self; + if *cond { a.layout(to) } else { b.layout(to) } + } +} + +impl, B: Content> Draw for Either { + fn draw (&self, to: &mut E) { + let Self(cond, a, b, ..) = self; + let area = self.layout(to.area()); + if *cond { Bounded(area, a).draw(to) } else { Bounded(area, b).draw(to) } + } +} + + push_pull!(Push: plus); + + push_pull!(Pull: minus); + + layout_op_xy!(0: Fill); + impl> Layout for Fill { + fn x (&self, area: O::Area) -> O::Unit { if self.dx() { area.x() } else { self.inner().x(area) } } + fn y (&self, area: O::Area) -> O::Unit { if self.dy() { area.y() } else { self.inner().y(area) } } + fn w (&self, area: O::Area) -> O::Unit { if self.dx() { area.w() } else { self.inner().w(area) } } + fn w_min (&self, area: O::Area) -> O::Unit { if self.dx() { area.w() } else { self.inner().w_min(area) } } + fn w_max (&self, area: O::Area) -> O::Unit { if self.dx() { area.w() } else { self.inner().w_max(area) } } + fn h (&self, area: O::Area) -> O::Unit { if self.dy() { area.h() } else { self.inner().h(area) } } + fn h_min (&self, area: O::Area) -> O::Unit { if self.dy() { area.h() } else { self.inner().h_min(area) } } + fn h_max (&self, area: O::Area) -> O::Unit { if self.dy() { area.h() } else { self.inner().h_max(area) } } + } + impl Fill { + #[inline] pub const fn dx (&self) -> bool { matches!(self, Self::X(_) | Self::XY(_)) } + #[inline] pub const fn dy (&self) -> bool { matches!(self, Self::Y(_) | Self::XY(_)) } + } + + layout_op_xy!(1 opt: Fixed); + impl> Layout for Fixed { + fn w (&self, area: O::Area) -> O::Unit { self.dx().unwrap_or(self.inner().w(area)) } + fn w_min (&self, area: O::Area) -> O::Unit { self.dx().unwrap_or(self.inner().w_min(area)) } + fn w_max (&self, area: O::Area) -> O::Unit { self.dx().unwrap_or(self.inner().w_max(area)) } + fn h (&self, area: O::Area) -> O::Unit { self.dy().unwrap_or(self.inner().h(area)) } + fn h_min (&self, area: O::Area) -> O::Unit { self.dy().unwrap_or(self.inner().h_min(area)) } + fn h_max (&self, area: O::Area) -> O::Unit { self.dy().unwrap_or(self.inner().h_max(area)) } + } + + layout_op_xy!(1 opt: Max); + impl> Layout for Max { + fn layout (&self, area: E::Area) -> E::Area { + let [x, y, w, h] = self.inner().layout(area).xywh(); + match self { + Self::X(mw, _) => [x, y, w.min(*mw), h ], + Self::Y(mh, _) => [x, y, w, h.min(*mh)], + Self::XY(mw, mh, _) => [x, y, w.min(*mw), h.min(*mh)], + }.into() + } + } + + layout_op_xy!(1 opt: Min); + impl> Layout for Min { + fn layout (&self, area: E::Area) -> E::Area { + let [x, y, w, h] = self.inner().layout(area).xywh(); + match self { + Self::X(mw, _) => [x, y, w.max(*mw), h], + Self::Y(mh, _) => [x, y, w, h.max(*mh)], + Self::XY(mw, mh, _) => [x, y, w.max(*mw), h.max(*mh)], + }.into() + } + } + + layout_op_xy!(1 opt: Expand); + impl> Layout for Expand { + fn w (&self, to: O::Area) -> O::Unit { self.inner().w(to).plus(self.dx().unwrap_or_default()) } + fn h (&self, to: O::Area) -> O::Unit { self.inner().w(to).plus(self.dy().unwrap_or_default()) } + } + + // FIXME: why they differ? + + layout_op_xy!(1 opt: Shrink); + impl> Layout for Shrink { + fn layout (&self, to: E::Area) -> E::Area { + let area = self.inner().layout(to); + let dx = self.dx().unwrap_or_default(); + let dy = self.dy().unwrap_or_default(); + [area.x(), area.y(), area.w().minus(dx), area.h().minus(dy)].into() + } + } + +impl Align { + #[inline] pub const fn c (a: T) -> Self { Self(Alignment::Center, a) } + #[inline] pub const fn x (a: T) -> Self { Self(Alignment::X, a) } + #[inline] pub const fn y (a: T) -> Self { Self(Alignment::Y, a) } + #[inline] pub const fn n (a: T) -> Self { Self(Alignment::N, a) } + #[inline] pub const fn s (a: T) -> Self { Self(Alignment::S, a) } + #[inline] pub const fn e (a: T) -> Self { Self(Alignment::E, a) } + #[inline] pub const fn w (a: T) -> Self { Self(Alignment::W, a) } + #[inline] pub const fn nw (a: T) -> Self { Self(Alignment::NW, a) } + #[inline] pub const fn sw (a: T) -> Self { Self(Alignment::SW, a) } + #[inline] pub const fn ne (a: T) -> Self { Self(Alignment::NE, a) } + #[inline] pub const fn se (a: T) -> Self { Self(Alignment::SE, a) } +} + +impl> Draw for Align { + fn draw (&self, to: &mut O) { Bounded(self.layout(to.area()), &self.1).draw(to) } +} + +impl> Layout for Align { + fn x (&self, to: O::Area) -> O::Unit { + match self.0 { + NW | W | SW => to.x(), + N | Center | S => to.x().plus(to.w() / 2.into()).minus(self.1.w(to) / 2.into()), + NE | E | SE => to.x().plus(to.w()).minus(self.1.w(to)), + _ => todo!(), + } + } + fn y (&self, to: O::Area) -> O::Unit { + match self.0 { + NW | N | NE => to.y(), + W | Center | E => to.y().plus(to.h() / 2.into()).minus(self.1.h(to) / 2.into()), + SW | S | SE => to.y().plus(to.h()).minus(self.1.h(to)), + _ => todo!(), + } + } +} + +impl Pad { + #[inline] pub const fn inner (&self) -> &A { match self { X(_, c) | Y(_, c) | XY(_, _, c) => c, } } +} +impl Pad { + #[inline] pub fn dx (&self) -> U { match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, } } + #[inline] pub fn dy (&self) -> U { match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } } +} +impl> Draw for Pad { + fn draw (&self, to: &mut O) { Bounded(self.layout(to.area()), self.inner()).draw(to) } +} +impl> Layout for Pad { + fn x (&self, area: O::Area) -> O::Unit { area.x().plus(self.dx()) } + fn y (&self, area: O::Area) -> O::Unit { area.x().plus(self.dx()) } + fn w (&self, area: O::Area) -> O::Unit { area.w().minus(self.dx() * 2.into()) } + fn h (&self, area: O::Area) -> O::Unit { area.h().minus(self.dy() * 2.into()) } +} +impl Bsp { + #[inline] pub const fn n (a: Head, b: Tail) -> Self { Self(North, a, b) } + #[inline] pub const fn s (a: Head, b: Tail) -> Self { Self(South, a, b) } + #[inline] pub const fn e (a: Head, b: Tail) -> Self { Self(East, a, b) } + #[inline] pub const fn w (a: Head, b: Tail) -> Self { Self(West, a, b) } + #[inline] pub const fn a (a: Head, b: Tail) -> Self { Self(Above, a, b) } + #[inline] pub const fn b (a: Head, b: Tail) -> Self { Self(Below, a, b) } +} + +impl, Tail: Content> Draw for Bsp { + fn draw (&self, to: &mut O) { + match self.0 { + South => { + //panic!("{}", self.1.h(to.area())); + let area_1 = self.1.layout(to.area()); + let area_2 = self.2.layout([ + to.area().x(), + to.area().y().plus(area_1.h()), + to.area().w(), + to.area().h().minus(area_1.h()) + ].into()); + //panic!("{area_1:?} {area_2:?}"); + to.place_at(area_1, &self.1); + to.place_at(area_2, &self.2); + }, + _ => todo!("{:?}", self.0) + } + //let [a, b, _] = bsp_areas(to.area(), self.0, &self.1, &self.2); + //panic!("{a:?} {b:?}"); + //if self.0 == Below { + //to.place_at(a, &self.1); + //to.place_at(b, &self.2); + //} else { + //to.place_at(b, &self.2); + //to.place_at(a, &self.1); + //} + } +} +impl, Tail: Layout> Layout for Bsp { + fn w (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | North | South => self.1.w(area).max(self.2.w(area)), East | West => self.1.w_min(area).plus(self.2.w(area)), } } + fn w_min (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | North | South => self.1.w_min(area).max(self.2.w_min(area)), East | West => self.1.w_min(area).plus(self.2.w_min(area)), } } + fn w_max (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | North | South => self.1.w_max(area).max(self.2.w_max(area)), East | West => self.1.w_max(area).plus(self.2.w_max(area)), } } + fn h (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | East | West => self.1.h(area).max(self.2.h(area)), North | South => self.1.h(area).plus(self.2.h(area)), } } + fn h_min (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | East | West => self.1.h_min(area).max(self.2.h_min(area)), North | South => self.1.h_min(area).plus(self.2.h_min(area)), } } + fn h_max (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | North | South => self.1.h_max(area).max(self.2.h_max(area)), East | West => self.1.h_max(area).plus(self.2.h_max(area)), } } + fn layout (&self, area: O::Area) -> O::Area { bsp_areas(area, self.0, &self.1, &self.2)[2] } +} + +fn bsp_areas , B: Layout> (area: O::Area, direction: Direction, a: &A, b: &B,) -> [O::Area;3] { + let [x, y, w, h] = area.xywh(); + let [aw, ah] = a.layout(area).wh(); + let [bw, bh] = b.layout(match direction { + Above | Below => area, + South => [x, y + ah, w, h.minus(ah)].into(), + North => [x, y, w, h.minus(ah)].into(), + East => [x + aw, y, w.minus(aw), h].into(), + West => [x, y, w.minus(aw), h].into(), + }).wh(); + match direction { + Above | Below => { + let [x, y, w, h] = area.center_xy([aw.max(bw), ah.max(bh)]); + let a = [(x + w/2.into()).minus(aw/2.into()), (y + h/2.into()).minus(ah/2.into()), aw, ah]; + let b = [(x + w/2.into()).minus(bw/2.into()), (y + h/2.into()).minus(bh/2.into()), bw, bh]; + [a.into(), b.into(), [x, y, w, h].into()] + }, + South => { + let [x, y, w, h] = area.center_xy([aw.max(bw), ah + bh]); + let a = [(x + w/2.into()).minus(aw/2.into()), y, aw, ah]; + let b = [(x + w/2.into()).minus(bw/2.into()), y + ah, bw, bh]; + [a.into(), b.into(), [x, y, w, h].into()] + }, + North => { + let [x, y, w, h] = area.center_xy([aw.max(bw), ah + bh]); + let a = [(x + (w/2.into())).minus(aw/2.into()), y + bh, aw, ah]; + let b = [(x + (w/2.into())).minus(bw/2.into()), y, bw, bh]; + [a.into(), b.into(), [x, y, w, h].into()] + }, + East => { + let [x, y, w, h] = area.center_xy([aw + bw, ah.max(bh)]); + let a = [x, (y + h/2.into()).minus(ah/2.into()), aw, ah]; + let b = [x + aw, (y + h/2.into()).minus(bh/2.into()), bw, bh]; + [a.into(), b.into(), [x, y, w, h].into()] + }, + West => { + let [x, y, w, h] = area.center_xy([aw + bw, ah.max(bh)]); + let a = [x + bw, (y + h/2.into()).minus(ah/2.into()), aw, ah]; + let b = [x, (y + h/2.into()).minus(bh/2.into()), bw, bh]; + [a.into(), b.into(), [x, y, w, h].into()] + }, + } +} + +impl<'a, O, A, B, I, F, G> Map where + I: Iterator + Send + Sync + 'a, + F: Fn() -> I + Send + Sync + 'a, +{ + pub const fn new (get_iter: F, get_item: G) -> Self { + Self { + __: PhantomData, + get_iter, + get_item + } + } +} + +impl_map_direction!(east, X, w); +impl_map_direction!(south, Y, n); +impl_map_direction!(west, X, e); +impl_map_direction!(north, Y, s); + +impl<'a, O, A, B, I, F, G> Layout for Map where + O: Out, + B: Layout, + I: Iterator + Send + Sync + 'a, + F: Fn() -> I + Send + Sync + 'a, + G: Fn(A, usize)->B + Send + Sync +{ + fn layout (&self, area: O::Area) -> O::Area { + let Self { get_iter, get_item, .. } = self; + let mut index = 0; + let [mut min_x, mut min_y] = area.center(); + let [mut max_x, mut max_y] = area.center(); + for item in get_iter() { + let [x,y,w,h] = get_item(item, index).layout(area).xywh(); + min_x = min_x.min(x); + min_y = min_y.min(y); + max_x = max_x.max(x + w); + max_y = max_y.max(y + h); + index += 1; + } + let w = max_x - min_x; + let h = max_y - min_y; + //[min_x.into(), min_y.into(), w.into(), h.into()].into() + area.center_xy([w.into(), h.into()]).into() + } +} +impl<'a, O, A, B, I, F, G> Draw for Map where + O: Out, + B: Content, + I: Iterator + Send + Sync + 'a, + F: Fn() -> I + Send + Sync + 'a, + G: Fn(A, usize)->B + Send + Sync +{ + fn draw (&self, to: &mut O) { + let Self { get_iter, get_item, .. } = self; + let mut index = 0; + let area = self.layout(to.area()); + for item in get_iter() { + let item = get_item(item, index); + //to.place_at(area.into(), &item); + to.place_at(item.layout(area), &item); + index += 1; + } + } +} + +#[inline] pub fn map_south( + item_offset: O::Unit, + item_height: O::Unit, + item: impl Content +) -> impl Content { + Push::Y(item_offset, Fixed::Y(item_height, Fill::X(item))) +} + +#[inline] pub fn map_south_west( + item_offset: O::Unit, + item_height: O::Unit, + item: impl Content +) -> impl Content { + Push::Y(item_offset, Align::nw(Fixed::Y(item_height, Fill::X(item)))) +} + +#[inline] pub fn map_east( + item_offset: O::Unit, + item_width: O::Unit, + item: impl Content +) -> impl Content { + Push::X(item_offset, Align::w(Fixed::X(item_width, Fill::Y(item)))) +} + +impl Tryptich<(), (), ()> { + pub fn center (h: u16) -> Self { + Self { h, top: false, left: (0, ()), middle: (0, ()), right: (0, ()) } + } + pub fn top (h: u16) -> Self { + Self { h, top: true, left: (0, ()), middle: (0, ()), right: (0, ()) } + } +} + +impl Tryptich { + pub fn left (self, w: u16, content: D) -> Tryptich { + Tryptich { left: (w, content), ..self } + } + pub fn middle (self, w: u16, content: D) -> Tryptich { + Tryptich { middle: (w, content), ..self } + } + pub fn right (self, w: u16, content: D) -> Tryptich { + Tryptich { right: (w, content), ..self } + } +} + +impl> Layout for Foreground { + fn layout (&self, to: O::Area) -> O::Area { + self.1.layout(to) + } +} + +impl> Layout for Background { + fn layout (&self, to: O::Area) -> O::Area { + self.1.layout(to) + } +} + +impl, V: Content> HasContent for FieldH { + fn content (&self) -> impl Content { Bsp::e(&self.1, &self.2) } +} + +impl, V: Content> Layout for FieldH { + fn layout (&self, to: O::Area) -> O::Area { self.content().layout(to) } +} + +impl, V: Content> Draw for FieldH { + fn draw (&self, to: &mut O) { self.content().draw(to) } +} + +impl, V: Content> HasContent for FieldV { + fn content (&self) -> impl Content { Bsp::s(&self.1, &self.2) } +} + +impl, V: Content> Layout for FieldV { + fn layout (&self, to: O::Area) -> O::Area { self.content().layout(to) } +} + +impl, V: Content> Draw for FieldV { + fn draw (&self, to: &mut O) { self.content().draw(to) } +} +impl> Layout for Border { + fn layout (&self, area: O::Area) -> O::Area { + self.1.layout(area) + } +} +impl Field { + pub fn new (direction: Direction) -> Field { + Field:: { + direction, + label: None, label_fg: None, label_bg: None, label_align: None, + value: None, value_fg: None, value_bg: None, value_align: None, + } + } + pub fn label ( + self, label: Option, align: Option, fg: Option, bg: Option + ) -> Field { + Field:: { label, label_fg: fg, label_bg: bg, label_align: align, ..self } + } + pub fn value ( + self, value: Option, align: Option, fg: Option, bg: Option + ) -> Field { + Field:: { value, value_fg: fg, value_bg: bg, value_align: align, ..self } + } +} diff --git a/output/src/out_macros.rs b/output/src/out_macros.rs new file mode 100644 index 0000000..2b7a8c6 --- /dev/null +++ b/output/src/out_macros.rs @@ -0,0 +1,138 @@ +/// Clear a pre-allocated buffer, then write into it. +#[macro_export] macro_rules! rewrite { + ($buf:ident, $($rest:tt)*) => { |$buf,_,_|{ $buf.clear(); write!($buf, $($rest)*) } } +} + +/// FIXME: This macro should be some variant of `eval`, too. +/// But taking into account the different signatures (resolving them into 1?) +#[cfg(feature = "dsl")] #[macro_export] macro_rules! draw { + ($State:ident: $Output:ident: $layers:expr) => { + impl Draw<$Output> for $State { + fn draw (&self, to: &mut $Output) { + for layer in $layers { layer(self, to) } + } + } + } +} + +/// FIXME: This is generic: should be called `eval` and be part of [dizzle]. +#[cfg(feature = "dsl")] #[macro_export] macro_rules! view { + ($State:ident: $Output:ident: $namespaces:expr) => { + impl View<$Output, ()> for $State { + fn view_expr <'a> (&'a self, to: &mut $Output, expr: &'a impl Expression) -> Usually<()> { + for namespace in $namespaces { if namespace(self, to, expr)? { return Ok(()) } } + Err(format!("{}::<{}, ()>::view_expr: unexpected: {expr:?}", + stringify! { $State }, + stringify! { $Output }).into()) + } + } + } +} + +/// Stack 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 }}); + +/// Define layout operation. +macro_rules! layout_op_xy ( + // Variant for layout ops that take no coordinates + (0: $T: ident) => { + impl $T { + #[inline] pub const fn inner (&self) -> &A { + match self { Self::X(c) | Self::Y(c) | Self::XY(c) => c } + } + } + impl> Draw for $T { + fn draw (&self, to: &mut O) { Bounded(self.layout(to.area()), self.inner()).draw(to) } + } + }; + // Variant for layout ops that take one coordinate + (1: $T: ident) => { + impl $T { + #[inline] pub const fn inner (&self) -> &A { + match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c, } + } + } + impl> Draw for $T { + fn draw (&self, to: &mut O) { Bounded(self.layout(to.area()), self.inner()).draw(to) } + } + impl $T { + #[inline] pub fn dx (&self) -> U { + match self { Self::X(x, _) | Self::XY(x, ..) => *x, _ => 0.into() } + } + #[inline] pub fn dy (&self) -> U { + match self { Self::Y(y, _) | Self::XY(y, ..) => *y, _ => 0.into() } + } + } + }; + (1 opt: $T: ident) => { + impl $T { + #[inline] pub const fn inner (&self) -> &A { + match self { Self::X(_, c) | Self::Y(_, c) | Self::XY(_, _, c) => c, } + } + } + impl> Draw for $T { + fn draw (&self, to: &mut O) { Bounded(self.layout(to.area()), self.inner()).draw(to) } + } + impl $T { + #[inline] pub const fn dx (&self) -> Option { + match self { Self::X(x, _) | Self::XY(x, ..) => Some(*x), _ => None } + } + #[inline] pub const fn dy (&self) -> Option { + match self { Self::Y(y, _) | Self::XY(y, ..) => Some(*y), _ => None } + } + } + }; +); + +// Implement layout op that increments X and/or Y by fixed amount. +macro_rules! push_pull(($T:ident: $method: ident)=>{ + layout_op_xy!(1: $T); + impl> Layout for $T { + fn x (&self, area: O::Area) -> O::Unit { area.x().$method(self.dx()) } + fn y (&self, area: O::Area) -> O::Unit { area.y().$method(self.dy()) } + } +}); + +macro_rules! impl_map_direction (($name:ident, $axis:ident, $align:ident)=>{ + impl<'a, O, A, B, I, F> Map< + O, A, Push>>>, I, F, fn(A, usize)->B + > where + O: Out, + B: Draw, + I: Iterator + Send + Sync + 'a, + F: Fn() -> I + Send + Sync + 'a + { + pub const fn $name ( + size: O::Unit, + get_iter: F, + get_item: impl Fn(A, usize)->B + Send + Sync + ) -> Map< + O, A, + Push>>, + I, F, + impl Fn(A, usize)->Push>> + Send + Sync + > { + Map { + __: PhantomData, + get_iter, + get_item: move |item: A, index: usize|{ + // FIXME: multiply + let mut push: O::Unit = O::Unit::from(0u16); + for _ in 0..index { + push = push + size; + } + Push::$axis(push, Align::$align(Fixed::$axis(size, get_item(item, index)))) + } + } + } + } +}); diff --git a/output/src/out_structs.rs b/output/src/out_structs.rs new file mode 100644 index 0000000..69278b1 --- /dev/null +++ b/output/src/out_structs.rs @@ -0,0 +1,176 @@ +use crate::*; + +/// A point (X, Y). +pub struct XY(N, N); + +/// A size (Width, Height). +pub struct WH(N, N); + +/// Point with size. +/// +/// TODO: anchor field (determines at which corner/side is X0 Y0) +pub struct XYWH(N, N, N, N); + +/// A cardinal direction. +#[derive(Copy, Clone, PartialEq, Debug)] #[cfg_attr(test, derive(Arbitrary))] +pub enum Direction { North, South, East, West, Above, Below } + +/// 9th of area to place. +#[derive(Debug, Copy, Clone, Default)] +pub enum Alignment { #[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W } + +/// A widget that tracks its rendered width and height +#[derive(Default)] pub struct Measure { + __: PhantomData, + pub x: Arc, + pub y: Arc, +} + +// TODO DOCUMENTME +pub struct Bounded(pub O::Area, pub D); + +/// Draws items from an iterator. +pub struct Map +where + I: Iterator + Send + Sync, + F: Fn() -> I + Send + Sync, +{ + __: PhantomData<(O, B)>, + /// Function that returns iterator over stacked components + get_iter: F, + /// Function that returns each stacked component + get_item: G, +} + +// TODO DOCUMENTME +pub struct Lazy( + F, + PhantomData<(O, T)> +); + +// TODO DOCUMENTME +pub struct Thunk( + PhantomData, + F +); + +// TODO DOCUMENTME +#[derive(Debug, Default)] pub struct Memo { + pub value: T, + pub view: Arc> +} + +/// Show an item only when a condition is true. +pub struct When(bool, T, PhantomData); + +/// Show one item if a condition is true and another if the condition is false +pub struct Either(pub bool, pub A, pub B, pub PhantomData); + +/// Increment X and/or Y coordinate. +pub enum Push { X(U, A), Y(U, A), XY(U, U, A), } + +/// Decrement X and/or Y coordinate. +pub enum Pull { X(U, A), Y(U, A), XY(U, U, A), } + +/// Set the content to fill the container. +pub enum Fill { X(A), Y(A), XY(A) } + +/// Set fixed size for content. +pub enum Fixed { X(U, A), Y(U, A), XY(U, U, A), } + +/// Set the maximum width and/or height of the content. +pub enum Max { X(U, A), Y(U, A), XY(U, U, A), } + +/// Set the minimum width and/or height of the content. +pub enum Min { X(U, A), Y(U, A), XY(U, U, A), } + +/// Decrease the width and/or height of the content. +pub enum Shrink { X(U, A), Y(U, A), XY(U, U, A), } + +/// Increaase the width and/or height of the content. +pub enum Expand { X(U, A), Y(U, A), XY(U, U, A), } + +/// Align position of inner area to middle, side, or corner of outer area. +/// +/// +/// ``` +/// use ::tengri::{output::*, tui::*}; +/// let area: [u16;4] = [10, 10, 20, 20]; +/// fn test (area: [u16;4], item: &impl Draw, expected: [u16;4]) { +/// assert_eq!(Content::layout(item, area), expected); +/// assert_eq!(Draw::layout(item, area), expected); +/// }; +/// +/// let four = ||Fixed::XY(4, 4, ""); +/// test(area, &Align::nw(four()), [10, 10, 4, 4]); +/// test(area, &Align::n(four()), [18, 10, 4, 4]); +/// test(area, &Align::ne(four()), [26, 10, 4, 4]); +/// test(area, &Align::e(four()), [26, 18, 4, 4]); +/// test(area, &Align::se(four()), [26, 26, 4, 4]); +/// test(area, &Align::s(four()), [18, 26, 4, 4]); +/// test(area, &Align::sw(four()), [10, 26, 4, 4]); +/// test(area, &Align::w(four()), [10, 18, 4, 4]); +/// +/// let two_by_four = ||Fixed::XY(4, 2, ""); +/// test(area, &Align::nw(two_by_four()), [10, 10, 4, 2]); +/// test(area, &Align::n(two_by_four()), [18, 10, 4, 2]); +/// test(area, &Align::ne(two_by_four()), [26, 10, 4, 2]); +/// test(area, &Align::e(two_by_four()), [26, 19, 4, 2]); +/// test(area, &Align::se(two_by_four()), [26, 28, 4, 2]); +/// test(area, &Align::s(two_by_four()), [18, 28, 4, 2]); +/// test(area, &Align::sw(two_by_four()), [10, 28, 4, 2]); +/// test(area, &Align::w(two_by_four()), [10, 19, 4, 2]); +/// ``` +pub struct Align(Alignment, T); + +// TODO DOCUMENTME +pub enum Pad { X(U, A), Y(U, A), XY(U, U, A), } + +/// A binary split or layer. +pub struct Bsp( + pub(crate) Direction, + /// First element. + pub(crate) Head, + /// Second element. + pub(crate) Tail, +); + +// TODO DOCUMENTME +pub struct Bordered(pub bool, pub S, pub W); + +// TODO DOCUMENTME +pub struct Border(pub bool, pub S); + +// TODO DOCUMENTME +pub struct Foreground(pub Color, pub Item); + +// TODO DOCUMENTME +pub struct Background(pub Color, pub Item); + +// TODO DOCUMENTME +pub struct FieldH(pub Theme, pub Label, pub Value); + +// TODO DOCUMENTME +pub struct FieldV(pub Theme, pub Label, pub Value); + +/// A three-column layout. +pub struct Tryptich { + pub top: bool, + pub h: u16, + pub left: (u16, A), + pub middle: (u16, B), + pub right: (u16, C), +} + +// TODO: +pub struct Field { + pub direction: Direction, + pub label: Option, + pub label_fg: Option, + pub label_bg: Option, + pub label_align: Option, + pub value: Option, + pub value_fg: Option, + pub value_bg: Option, + pub value_align: Option, +} diff --git a/output/src/out_tests.rs b/output/src/out_tests.rs new file mode 100644 index 0000000..784f0a1 --- /dev/null +++ b/output/src/out_tests.rs @@ -0,0 +1,132 @@ +use proptest::{prelude::*, option::of}; +use proptest_derive::Arbitrary; +use crate::*; + +#[test] fn test_area () { + assert_eq!(Area::center(&[10u16, 10, 20, 20]), [20, 20]); +} + +proptest! { + #[test] fn proptest_direction ( + d in prop_oneof![ + Just(North), Just(South), + Just(East), Just(West), + Just(Above), Just(Below) + ], + x in u16::MIN..u16::MAX, + y in u16::MIN..u16::MAX, + w in u16::MIN..u16::MAX, + h in u16::MIN..u16::MAX, + a in u16::MIN..u16::MAX, + ) { + let _ = d.split_fixed([x, y, w, h], a); + } +} + +proptest! { + #[test] fn proptest_area ( + x in u16::MIN..u16::MAX, + y in u16::MIN..u16::MAX, + w in u16::MIN..u16::MAX, + h in u16::MIN..u16::MAX, + a in u16::MIN..u16::MAX, + b in u16::MIN..u16::MAX, + ) { + let _: [u16;4] = <[u16;4] as Area>::zero(); + let _: [u16;4] = <[u16;4] as Area>::from_position([a, b]); + let _: [u16;4] = <[u16;4] as Area>::from_size([a, b]); + let area: [u16;4] = [x, y, w, h]; + let _ = area.expect_min(a, b); + let _ = area.xy(); + let _ = area.wh(); + let _ = area.xywh(); + let _ = area.clip_h(a); + let _ = area.clip_w(b); + let _ = area.clip([a, b]); + let _ = area.set_w(a); + let _ = area.set_h(b); + let _ = area.x2(); + let _ = area.y2(); + let _ = area.lrtb(); + let _ = area.center(); + let _ = area.center_x(a); + let _ = area.center_y(b); + let _ = area.center_xy([a, b]); + let _ = area.centered(); + } +} + +proptest! { + #[test] fn proptest_size ( + x in u16::MIN..u16::MAX, + y in u16::MIN..u16::MAX, + a in u16::MIN..u16::MAX, + b in u16::MIN..u16::MAX, + ) { + let size = [x, y]; + let _ = size.w(); + let _ = size.h(); + let _ = size.wh(); + let _ = size.clip_w(a); + let _ = size.clip_h(b); + let _ = size.expect_min(a, b); + let _ = size.to_area_pos(); + let _ = size.to_area_size(); + } +} + +macro_rules! test_op_transform { + ($fn:ident, $Op:ident) => { + proptest! { + #[test] fn $fn ( + op_x in of(u16::MIN..u16::MAX), + op_y in of(u16::MIN..u16::MAX), + content in "\\PC*", + x in u16::MIN..u16::MAX, + y in u16::MIN..u16::MAX, + w in u16::MIN..u16::MAX, + h in u16::MIN..u16::MAX, + ) { + if let Some(op) = match (op_x, op_y) { + (Some(x), Some(y)) => Some($Op::XY(x, y, content)), + (Some(x), None) => Some($Op::X(x, content)), + (None, Some(y)) => Some($Op::Y(y, content)), + _ => None + } { + //assert_eq!(Content::layout(&op, [x, y, w, h]), + //Draw::layout(&op, [x, y, w, h])); + } + } + } + } +} +test_op_transform!(proptest_op_fixed, Fixed); +test_op_transform!(proptest_op_min, Min); +test_op_transform!(proptest_op_max, Max); +test_op_transform!(proptest_op_push, Push); +test_op_transform!(proptest_op_pull, Pull); +test_op_transform!(proptest_op_shrink, Shrink); +test_op_transform!(proptest_op_expand, Expand); +test_op_transform!(proptest_op_padding, Pad); + +proptest! { + #[test] fn proptest_op_bsp ( + d in prop_oneof![ + Just(North), Just(South), + Just(East), Just(West), + Just(Above), Just(Below) + ], + a in "\\PC*", + b in "\\PC*", + x in u16::MIN..u16::MAX, + y in u16::MIN..u16::MAX, + w in u16::MIN..u16::MAX, + h in u16::MIN..u16::MAX, + ) { + let bsp = Bsp(d, a, b); + //assert_eq!( + //Content::layout(&bsp, [x, y, w, h]), + //Draw::layout(&bsp, [x, y, w, h]), + //); + } +} diff --git a/output/src/out_traits.rs b/output/src/out_traits.rs new file mode 100644 index 0000000..7dea104 --- /dev/null +++ b/output/src/out_traits.rs @@ -0,0 +1,148 @@ +use crate::*; + +/// Drawing target. +/// +/// ``` +/// use crate::*; +/// struct TestOut([u16;4]); +/// impl Out for TestOut { +/// type Unit = u16; +/// type Size = [u16;2]; +/// type Area = [u16;4]; +/// fn area (&self) -> [u16;4] { +/// self.0 +/// } +/// fn area_mut (&mut self) -> &mut [u16;4] { +/// &mut self.0 +/// } +/// fn place_at + ?Sized> (&mut self, area: [u16;4], _: &T) { +/// println!("place_at: {area:?}"); +/// () +/// } +/// } +/// impl Draw for String { +/// fn draw (&self, to: &mut TestOut) { +/// to.area_mut().set_w(self.len() as u16); +/// } +/// } +/// ``` +pub trait Out: Send + Sync + Sized { + /// Unit of length + type Unit: Coord; + /// Rectangle without offset + type Size: Size; + /// Rectangle with offset + type Area: Area; + /// Current output area + fn area (&self) -> Self::Area; + /// Mutable pointer to area. + fn area_mut (&mut self) -> &mut Self::Area; + /// Render drawable in area specified by `area` + fn place_at <'t, T: Draw + ?Sized> (&mut self, area: Self::Area, content: &'t T); + /// Render drawable in area specified by `T::layout(self.area())` + #[inline] fn place <'t, T: Content + ?Sized> ( + &mut self, content: &'t T + ) { + self.place_at(content.layout(self.area()), content) + } +} + +/// Drawable with dynamic dispatch. +pub trait Draw { + fn draw (&self, to: &mut O); +} + +/// FIXME: This is a general implementation: should be called `Eval` and be part of [dizzle]. +/// Matches [Language] expressions to renderings for a given [Output] target. +pub trait View { + fn view_expr <'a> (&'a self, _output: &mut O, expr: &'a impl Expression) -> Usually { + Err(format!("View::view_expr: no exprs defined: {expr:?}").into()) + } + fn view_word <'a> (&'a self, _output: &mut O, word: &'a impl Symbol) -> Usually { + Err(format!("View::view_word: no words defined: {word:?}").into()) + } + fn view <'a> (&'a self, output: &mut O, dsl: &'a impl Language) -> Usually { + let is_expr = dsl.expr(); + let is_word = dsl.word(); + match (dsl.expr(), dsl.word()) { + (Ok(Some(e)), _ ) => self.view_expr(output, &e), + (_, Ok(Some(w))) => self.view_word(output, &w), + (Err(e), _ ) => Err(format!("invalid view expr:\n{dsl:?}\n{e}").into()), + (_, Err(w) ) => Err(format!("invalid view word:\n{dsl:?}\n{w}").into()), + (Ok(None), Ok(None) ) => Err(format!("empty view:\n{dsl:?}").into()), + } + } +} + +/// Outputs combinator. +pub trait Lay: Sized {} + +/// Drawable area of display. +pub trait Layout { + fn x (&self, to: O::Area) -> O::Unit { to.x() } + fn y (&self, to: O::Area) -> O::Unit { to.y() } + fn w_min (&self, _t: O::Area) -> O::Unit { 0.into() } + fn w_max (&self, to: O::Area) -> O::Unit { to.w() } + fn w (&self, to: O::Area) -> O::Unit { to.w().max(self.w_min(to)).min(self.w_max(to)) } + fn h_min (&self, _t: O::Area) -> O::Unit { 0.into() } + fn h_max (&self, to: O::Area) -> O::Unit { to.h() } + fn h (&self, to: O::Area) -> O::Unit { to.h().max(self.h_min(to)).min(self.h_max(to)) } + fn layout (&self, to: O::Area) -> O::Area { [self.x(to), self.y(to), self.w(to), self.h(to)].into() } +} + +// TODO DOCUMENTME +pub trait Content: Draw + Layout {} + +/// A numeric type that can be used as coordinate. +/// +/// FIXME: Replace this ad-hoc trait with `num` crate. +pub trait Coord: Send + Sync + Copy + + Add + + Sub + + Mul + + Div + + Ord + PartialEq + Eq + + Debug + Display + Default + + From + Into + + Into + + Into +{ + fn zero () -> Self { 0.into() } + fn plus (self, other: Self) -> Self; + fn minus (self, other: Self) -> Self { if self >= other { self - other } else { 0.into() } } +} + +// Something that has an origin point (X, Y). +pub trait HasXY { + fn x (&self) -> N; + fn y (&self) -> N; + fn xy (&self) -> XY { XY(self.x(), self.y()) } +} + +// Something that has a size (W, H). +pub trait HasWH { + fn w (&self) -> N; + fn h (&self) -> N; + fn wh (&self) -> WH { WH(self.w(), self.h()) } +} + +// Something that has a 2D bounding box (X, Y, W, H). +pub trait HasXYWH: HasXY + HasWH { + fn x2 (&self) -> N { self.x().plus(self.w()) } + fn y2 (&self) -> N { self.y().plus(self.h()) } + fn xywh (&self) -> XYWH { XYWH(self.x(), self.y(), self.w(), self.h()) } + fn expect_min (&self, w: N, h: N) -> Usually<&Self> { + if self.w() < w || self.h() < h { + Err(format!("min {w}x{h}").into()) + } else { + Ok(self) + } + } +} + +// TODO DOCUMENTME +pub trait HasSize { + fn size (&self) -> &Measure; + fn width (&self) -> usize { self.size().w() } + fn height (&self) -> usize { self.size().h() } +} diff --git a/output/src/output.rs b/output/src/output.rs deleted file mode 100644 index b006d38..0000000 --- a/output/src/output.rs +++ /dev/null @@ -1,60 +0,0 @@ -use crate::*; - -/// Drawing target. -pub trait Out: Send + Sync + Sized { - /// Unit of length - type Unit: Coordinate; - - /// Rectangle without offset - type Size: Size; - - /// Rectangle with offset - type Area: Area; - - /// Render drawable in area specified by `T::layout(self.area())` - #[inline] fn place <'t, T: Content + ?Sized> ( - &mut self, content: &'t T - ) { - self.place_at(content.layout(self.area()), content) - } - - /// Render drawable in area specified by `area` - fn place_at <'t, T: Draw + ?Sized> (&mut self, area: Self::Area, content: &'t T); - - /// Current output area - fn area (&self) -> Self::Area; - #[inline] fn x (&self) -> Self::Unit { self.area().x() } - #[inline] fn y (&self) -> Self::Unit { self.area().y() } - #[inline] fn w (&self) -> Self::Unit { self.area().w() } - #[inline] fn h (&self) -> Self::Unit { self.area().h() } - #[inline] fn wh (&self) -> Self::Size { self.area().wh().into() } - - /// Mutable pointer to area. - fn area_mut (&mut self) -> &mut Self::Area; -} - -#[cfg(test)] #[test] fn test_stub_output () -> Usually<()> { - use crate::*; - struct TestOut([u16;4]); - impl Out for TestOut { - type Unit = u16; - type Size = [u16;2]; - type Area = [u16;4]; - fn area (&self) -> [u16;4] { - self.0 - } - fn area_mut (&mut self) -> &mut [u16;4] { - &mut self.0 - } - fn place_at + ?Sized> (&mut self, area: [u16;4], _: &T) { - println!("place_at: {area:?}"); - () - } - } - impl Draw for String { - fn draw (&self, to: &mut TestOut) { - to.area_mut().set_w(self.len() as u16); - } - } - Ok(()) -} diff --git a/output/src/space.rs b/output/src/space.rs deleted file mode 100644 index 94f6cf8..0000000 --- a/output/src/space.rs +++ /dev/null @@ -1,441 +0,0 @@ -pub(self) use crate::*; -#[cfg(test)] use proptest::{prelude::*, option::of}; - -pub use self::direction::*; mod direction { - use super::*; - /// A cardinal direction. - #[derive(Copy, Clone, PartialEq, Debug)] - #[cfg_attr(test, derive(Arbitrary))] - pub enum Direction { North, South, East, West, Above, Below } - impl Direction { - pub fn split_fixed (self, area: impl Area, a: N) -> ([N;4],[N;4]) { - let [x, y, w, h] = area.xywh(); - match self { - North => ([x, y.plus(h).minus(a), w, a], [x, y, w, h.minus(a)]), - South => ([x, y, w, a], [x, y.plus(a), w, h.minus(a)]), - East => ([x, y, a, h], [x.plus(a), y, w.minus(a), h]), - West => ([x.plus(w).minus(a), y, a, h], [x, y, w.minus(a), h]), - Above | Below => (area.xywh(), area.xywh()) - } - } - } - #[cfg(test)] proptest! { - #[test] fn proptest_direction ( - d in prop_oneof![ - Just(North), Just(South), - Just(East), Just(West), - Just(Above), Just(Below) - ], - x in u16::MIN..u16::MAX, - y in u16::MIN..u16::MAX, - w in u16::MIN..u16::MAX, - h in u16::MIN..u16::MAX, - a in u16::MIN..u16::MAX, - ) { - let _ = d.split_fixed([x, y, w, h], a); - } - } -} - -pub use self::coordinate::*; mod coordinate { - use super::*; - /// A linear coordinate. - pub trait Coordinate: Send + Sync + Copy - + Add - + Sub - + Mul - + Div - + Ord + PartialEq + Eq - + Debug + Display + Default - + From + Into - + Into - + Into - { - fn zero () -> Self { 0.into() } - fn plus (self, other: Self) -> Self; - fn minus (self, other: Self) -> Self { - if self >= other { - self - other - } else { - 0.into() - } - } - } - impl Coordinate for u16 { fn plus (self, other: Self) -> Self { self.saturating_add(other) } } -} - -pub use self::area::*; mod area { - use super::*; - - pub trait Area: From<[N;4]> + Debug + Copy { - fn x (&self) -> N; - fn y (&self) -> N; - fn w (&self) -> N; - fn h (&self) -> N; - fn zero () -> [N;4] { - [N::zero(), N::zero(), N::zero(), N::zero()] - } - fn from_position (pos: impl Size) -> [N;4] { - let [x, y] = pos.wh(); - [x, y, 0.into(), 0.into()] - } - fn from_size (size: impl Size) -> [N;4] { - let [w, h] = size.wh(); - [0.into(), 0.into(), w, h] - } - fn expect_min (&self, w: N, h: N) -> Usually<&Self> { - if self.w() < w || self.h() < h { - Err(format!("min {w}x{h}").into()) - } else { - Ok(self) - } - } - fn xy (&self) -> [N;2] { - [self.x(), self.y()] - } - fn wh (&self) -> [N;2] { - [self.w(), self.h()] - } - fn xywh (&self) -> [N;4] { - [self.x(), self.y(), self.w(), self.h()] - } - fn clip_h (&self, h: N) -> [N;4] { - [self.x(), self.y(), self.w(), self.h().min(h)] - } - fn clip_w (&self, w: N) -> [N;4] { - [self.x(), self.y(), self.w().min(w), self.h()] - } - fn clip (&self, wh: impl Size) -> [N;4] { - [self.x(), self.y(), wh.w(), wh.h()] - } - fn set_w (&self, w: N) -> [N;4] { - [self.x(), self.y(), w, self.h()] - } - fn set_h (&self, h: N) -> [N;4] { - [self.x(), self.y(), self.w(), h] - } - fn x2 (&self) -> N { - self.x().plus(self.w()) - } - fn y2 (&self) -> N { - self.y().plus(self.h()) - } - fn lrtb (&self) -> [N;4] { - [self.x(), self.x2(), self.y(), self.y2()] - } - fn center (&self) -> [N;2] { - [self.x().plus(self.w()/2.into()), self.y().plus(self.h()/2.into())] - } - fn center_x (&self, n: N) -> [N;4] { - let [x, y, w, h] = self.xywh(); - [(x.plus(w / 2.into())).minus(n / 2.into()), y.plus(h / 2.into()), n, 1.into()] - } - fn center_y (&self, n: N) -> [N;4] { - let [x, y, w, h] = self.xywh(); - [x.plus(w / 2.into()), (y.plus(h / 2.into())).minus(n / 2.into()), 1.into(), n] - } - fn center_xy (&self, [n, m]: [N;2]) -> [N;4] { - let [x, y, w, h] = self.xywh(); - [(x.plus(w / 2.into())).minus(n / 2.into()), (y.plus(h / 2.into())).minus(m / 2.into()), n, m] - } - fn centered (&self) -> [N;2] { - [self.x().minus(self.w()/2.into()), self.y().minus(self.h()/2.into())] - } - fn iter_x (&self) -> impl Iterator where N: std::iter::Step { - self.x()..(self.x()+self.w()) - } - fn iter_y (&self) -> impl Iterator where N: std::iter::Step { - self.y()..(self.y()+self.h()) - } - } - - impl Area for (N, N, N, N) { - fn x (&self) -> N { self.0 } - fn y (&self) -> N { self.1 } - fn w (&self) -> N { self.2 } - fn h (&self) -> N { self.3 } - } - - impl Area for [N;4] { - fn x (&self) -> N { self[0] } - fn y (&self) -> N { self[1] } - fn w (&self) -> N { self[2] } - fn h (&self) -> N { self[3] } - } - - #[cfg(test)] #[test] fn test_area () { assert_eq!(Area::center(&[10u16, 10, 20, 20]), [20, 20]); } - #[cfg(test)] proptest! { - #[test] fn proptest_area ( - x in u16::MIN..u16::MAX, - y in u16::MIN..u16::MAX, - w in u16::MIN..u16::MAX, - h in u16::MIN..u16::MAX, - a in u16::MIN..u16::MAX, - b in u16::MIN..u16::MAX, - ) { - let _: [u16;4] = <[u16;4] as Area>::zero(); - let _: [u16;4] = <[u16;4] as Area>::from_position([a, b]); - let _: [u16;4] = <[u16;4] as Area>::from_size([a, b]); - let area: [u16;4] = [x, y, w, h]; - let _ = area.expect_min(a, b); - let _ = area.xy(); - let _ = area.wh(); - let _ = area.xywh(); - let _ = area.clip_h(a); - let _ = area.clip_w(b); - let _ = area.clip([a, b]); - let _ = area.set_w(a); - let _ = area.set_h(b); - let _ = area.x2(); - let _ = area.y2(); - let _ = area.lrtb(); - let _ = area.center(); - let _ = area.center_x(a); - let _ = area.center_y(b); - let _ = area.center_xy([a, b]); - let _ = area.centered(); - } - } -} - -pub use self::size::*; mod size { - use super::*; - /// The size of something. - pub trait Size: From<[N;2]> + Debug/* + Copy*/ { - fn zero () -> [N;2] { [N::zero(), N::zero()] } - fn x (&self) -> N; - fn y (&self) -> N; - fn w (&self) -> N { self.x() } - fn h (&self) -> N { self.y() } - fn wh (&self) -> [N;2] { [self.x(), self.y()] } - fn clip_w (&self, w: N) -> [N;2] { [self.w().min(w), self.h()] } - fn clip_h (&self, h: N) -> [N;2] { [self.w(), self.h().min(h)] } - fn to_area_pos (&self) -> [N;4] { let [x, y] = self.wh(); [x, y, 0.into(), 0.into()] } - fn to_area_size (&self) -> [N;4] { let [w, h] = self.wh(); [0.into(), 0.into(), w, h] } - fn expect_min (&self, w: N, h: N) -> Usually<&Self> { - if self.w() < w || self.h() < h { return Err(format!("min {w}x{h}").into()) } - Ok(self) - } - } - impl Size for (N, N) { fn x (&self) -> N { self.0 } fn y (&self) -> N { self.1 } } - impl Size for [N;2] { fn x (&self) -> N { self[0] } fn y (&self) -> N { self[1] } } - #[cfg(test)] proptest! { - #[test] fn proptest_size ( - x in u16::MIN..u16::MAX, - y in u16::MIN..u16::MAX, - a in u16::MIN..u16::MAX, - b in u16::MIN..u16::MAX, - ) { - let size = [x, y]; - let _ = size.w(); - let _ = size.h(); - let _ = size.wh(); - let _ = size.clip_w(a); - let _ = size.clip_h(b); - let _ = size.expect_min(a, b); - let _ = size.to_area_pos(); - let _ = size.to_area_size(); - } - } - pub trait HasSize { - fn size (&self) -> &Measure; - fn width (&self) -> usize { self.size().w() } - fn height (&self) -> usize { self.size().h() } - } - impl>> HasSize for T { fn size (&self) -> &Measure { self.get() } } - - /// A widget that tracks its rendered width and height - #[derive(Default)] pub struct Measure { __: PhantomData, pub x: Arc, pub y: Arc, } - impl Clone for Measure { fn clone (&self) -> Self { Self { __: Default::default(), x: self.x.clone(), y: self.y.clone(), } } } - impl Layout for Measure {} - impl PartialEq for Measure { - fn eq (&self, other: &Self) -> bool { - self.x.load(Relaxed) == other.x.load(Relaxed) && - self.y.load(Relaxed) == other.y.load(Relaxed) - } - } - // TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small - impl Draw for Measure { - fn draw (&self, to: &mut O) { - self.x.store(to.area().w().into(), Relaxed); - self.y.store(to.area().h().into(), Relaxed); - } - } - impl Debug for Measure { - fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { - f.debug_struct("Measure").field("width", &self.x).field("height", &self.y).finish() - } - } - impl Measure { - pub fn set_w (&self, w: impl Into) -> &Self { self.x.store(w.into(), Relaxed); self } - pub fn set_h (&self, h: impl Into) -> &Self { self.y.store(h.into(), Relaxed); self } - pub fn set_wh (&self, w: impl Into, h: impl Into) -> &Self { self.set_w(w); self.set_h(h); self } - pub fn format (&self) -> Arc { format!("{}x{}", self.w(), self.h()).into() } - pub fn of > (&self, item: T) -> Bsp, T> { Bsp::b(Fill::XY(self), item) } - pub fn new (x: O::Unit, y: O::Unit) -> Self { - Self { __: PhantomData::default(), x: Arc::new(x.into()), y: Arc::new(y.into()), } - } - } - impl From<[O::Unit; 2]> for Measure { - fn from ([x, y]: [O::Unit; 2]) -> Self { Self::new(x, y) } - } - //impl Size for Measure { - //fn x (&self) -> O::Unit { self.x.load(Relaxed).into() } - //fn y (&self) -> O::Unit { self.y.load(Relaxed).into() } - //} -} - -pub use self::layout::*; mod layout { - use super::*; - - /// Drawable area of display. - pub trait Layout { - fn x (&self, to: O::Area) -> O::Unit { - to.x() - } - fn y (&self, to: O::Area) -> O::Unit { - to.y() - } - fn min_w (&self, _to: O::Area) -> O::Unit { - 0.into() - } - fn max_w (&self, to: O::Area) -> O::Unit { - to.w() - } - fn w (&self, to: O::Area) -> O::Unit { - to.w().max(self.min_w(to)).min(self.max_w(to)) - } - fn min_h (&self, _to: O::Area) -> O::Unit { - 0.into() - } - fn max_h (&self, to: O::Area) -> O::Unit { - to.h() - } - fn h (&self, to: O::Area) -> O::Unit { - to.h().max(self.min_h(to)).min(self.max_h(to)) - } - fn layout (&self, to: O::Area) -> O::Area { - [self.x(to), self.y(to), self.w(to), self.h(to)].into() - } - } - - impl Layout for () { - fn x (&self, a: O::Area) -> O::Unit { a.x() } - fn y (&self, a: O::Area) -> O::Unit { a.y() } - fn w (&self, _: O::Area) -> O::Unit { 0.into() } - fn min_w (&self, _: O::Area) -> O::Unit { 0.into() } - fn max_w (&self, _: O::Area) -> O::Unit { 0.into() } - fn h (&self, _: O::Area) -> O::Unit { 0.into() } - fn min_h (&self, _: O::Area) -> O::Unit { 0.into() } - fn max_h (&self, _: O::Area) -> O::Unit { 0.into() } - fn layout (&self, a: O::Area) -> O::Area { [a.x(), a.y(), 0.into(), 0.into()].into() } - } - - impl> Layout for &L { - fn x (&self, a: O::Area) -> O::Unit { (*self).x(a) } - fn y (&self, a: O::Area) -> O::Unit { (*self).y(a) } - fn w (&self, a: O::Area) -> O::Unit { (*self).w(a) } - fn min_w (&self, a: O::Area) -> O::Unit { (*self).min_w(a) } - fn max_w (&self, a: O::Area) -> O::Unit { (*self).max_w(a) } - fn h (&self, a: O::Area) -> O::Unit { (*self).h(a) } - fn min_h (&self, a: O::Area) -> O::Unit { (*self).min_h(a) } - fn max_h (&self, a: O::Area) -> O::Unit { (*self).max_h(a) } - fn layout (&self, a: O::Area) -> O::Area { (*self).layout(a) } - } - - impl> Layout for &mut L { - fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) } - fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) } - fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) } - fn min_w (&self, a: O::Area) -> O::Unit { (**self).min_w(a) } - fn max_w (&self, a: O::Area) -> O::Unit { (**self).max_w(a) } - fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) } - fn min_h (&self, a: O::Area) -> O::Unit { (**self).min_h(a) } - fn max_h (&self, a: O::Area) -> O::Unit { (**self).max_h(a) } - fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) } - } - - impl> Layout for Arc { - fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) } - fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) } - fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) } - fn min_w (&self, a: O::Area) -> O::Unit { (**self).min_w(a) } - fn max_w (&self, a: O::Area) -> O::Unit { (**self).max_w(a) } - fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) } - fn min_h (&self, a: O::Area) -> O::Unit { (**self).min_h(a) } - fn max_h (&self, a: O::Area) -> O::Unit { (**self).max_h(a) } - fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) } - } - - impl Layout for Box> { - fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) } - fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) } - fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) } - fn min_w (&self, a: O::Area) -> O::Unit { (**self).min_w(a) } - fn max_w (&self, a: O::Area) -> O::Unit { (**self).max_w(a) } - fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) } - fn min_h (&self, a: O::Area) -> O::Unit { (**self).min_h(a) } - fn max_h (&self, a: O::Area) -> O::Unit { (**self).max_h(a) } - fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) } - } - - impl> Layout for RwLock { - fn x (&self, a: O::Area) -> O::Unit { self.read().unwrap().x(a) } - fn y (&self, a: O::Area) -> O::Unit { self.read().unwrap().y(a) } - fn w (&self, a: O::Area) -> O::Unit { self.read().unwrap().w(a) } - fn min_w (&self, a: O::Area) -> O::Unit { self.read().unwrap().min_w(a) } - fn max_w (&self, a: O::Area) -> O::Unit { self.read().unwrap().max_w(a) } - fn h (&self, a: O::Area) -> O::Unit { self.read().unwrap().h(a) } - fn min_h (&self, a: O::Area) -> O::Unit { self.read().unwrap().min_h(a) } - fn max_h (&self, a: O::Area) -> O::Unit { self.read().unwrap().max_h(a) } - fn layout (&self, a: O::Area) -> O::Area { self.read().unwrap().layout(a) } - } - - impl> Layout for Option { - fn x (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.x(to)).unwrap_or(to.x()) - } - fn y (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.y(to)).unwrap_or(to.y()) - } - fn min_w (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.min_w(to)).unwrap_or(0.into()) - } - fn max_w (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.max_w(to)).unwrap_or(0.into()) - } - fn w (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.w(to)).unwrap_or(0.into()) - } - fn min_h (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.min_h(to)).unwrap_or(0.into()) - } - fn max_h (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.max_h(to)).unwrap_or(0.into()) - } - fn h (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.h(to)).unwrap_or(0.into()) - } - fn layout (&self, to: O::Area) -> O::Area { - self.as_ref().map(|c|c.layout([self.x(to), self.y(to), self.w(to), self.h(to)].into())) - .unwrap_or([to.x(), to.y(), 0.into(), 0.into()].into()) - } - } - - pub struct Bounded(pub O::Area, pub D); - - impl> HasContent for Bounded { - fn content (&self) -> impl Content { - &self.1 - } - } - - impl> Draw for Bounded { - fn draw (&self, to: &mut O) { - let area = to.area(); - *to.area_mut() = self.0; - self.1.draw(to); - *to.area_mut() = area; - } - } -} diff --git a/output/src/thunk.rs b/output/src/thunk.rs deleted file mode 100644 index 29b7321..0000000 --- a/output/src/thunk.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::*; - -pub struct Lazy(F, PhantomData<(O, T)>); -impl, F: Fn()->T> Lazy { pub const fn new (thunk: F) -> Self { Self(thunk, PhantomData) } } - -pub struct Thunk(PhantomData, F); -impl Thunk { pub const fn new (draw: F) -> Self { Self(PhantomData, draw) } } -impl Layout for Thunk {} -impl Draw for Thunk { - fn draw (&self, to: &mut O) { (self.1)(to) } -} - -#[derive(Debug, Default)] pub struct Memo { pub value: T, pub view: Arc> } -impl Memo { - pub fn new (value: T, view: U) -> Self { Self { value, view: Arc::new(view.into()) } } - pub fn update (&mut self, newval: T, draw: impl Fn(&mut U, &T, &T)->R) -> Option { - if newval != self.value { - let result = draw(&mut*self.view.write().unwrap(), &newval, &self.value); - self.value = newval; - return Some(result); - } - None - } -} - -/// Clear a pre-allocated buffer, then write into it. -#[macro_export] macro_rules! rewrite { ($buf:ident, $($rest:tt)*) => { |$buf,_,_|{ $buf.clear(); write!($buf, $($rest)*) } } } diff --git a/output/src/view.rs b/output/src/view.rs deleted file mode 100644 index 1630a6f..0000000 --- a/output/src/view.rs +++ /dev/null @@ -1,177 +0,0 @@ -use crate::*; -use ::dizzle::{Language, Expression, Symbol, Namespace}; - -/// FIXME: This macro should be some variant of `eval`, too. -/// But taking into account the different signatures (resolving them into 1?) -#[macro_export] macro_rules! draw { - ($State:ident: $Output:ident: $layers:expr) => { - impl Draw<$Output> for $State { - fn draw (&self, to: &mut $Output) { - for layer in $layers { layer(self, to) } - } - } - } -} - -/// FIXME: This is generic: should be called `eval` and be part of [dizzle]. -#[macro_export] macro_rules! view { - ($State:ident: $Output:ident: $namespaces:expr) => { - impl View<$Output, ()> for $State { - fn view_expr <'a> (&'a self, to: &mut $Output, expr: &'a impl Expression) -> Usually<()> { - for namespace in $namespaces { if namespace(self, to, expr)? { return Ok(()) } } - Err(format!("{}::<{}, ()>::view_expr: unexpected: {expr:?}", - stringify! { $State }, - stringify! { $Output }).into()) - } - } - } -} - -/// FIXME: This is generic: should be called `Eval` and be part of [dizzle]. -/// Matches [Language] expressions to renderings for a given [Output] target. -pub trait View { - fn view_expr <'a> (&'a self, _output: &mut O, expr: &'a impl Expression) -> Usually { - Err(format!("View::view_expr: no exprs defined: {expr:?}").into()) - } - fn view_word <'a> (&'a self, _output: &mut O, word: &'a impl Symbol) -> Usually { - Err(format!("View::view_word: no words defined: {word:?}").into()) - } - fn view <'a> (&'a self, output: &mut O, dsl: &'a impl Language) -> Usually { - let is_expr = dsl.expr(); - let is_word = dsl.word(); - match (dsl.expr(), dsl.word()) { - (Ok(Some(e)), _ ) => self.view_expr(output, &e), - (_, Ok(Some(w))) => self.view_word(output, &w), - (Err(e), _ ) => Err(format!("invalid view expr:\n{dsl:?}\n{e}").into()), - (_, Err(w) ) => Err(format!("invalid view word:\n{dsl:?}\n{w}").into()), - (Ok(None), Ok(None) ) => Err(format!("empty view:\n{dsl:?}").into()), - } - } -} - -pub fn evaluate_output_expression <'a, O: Out + 'a, S> ( - state: &S, output: &mut O, expr: &'a impl Expression -) -> Usually where - S: View - + for<'b>Namespace<'b, bool> - + for<'b>Namespace<'b, O::Unit> -{ - // First element of expression is used for dispatch. - // Dispatch is proto-namespaced using separator character - let head = expr.head()?; - let mut frags = head.src()?.unwrap_or_default().split("/"); - // The rest of the tokens in the expr are arguments. - // Their meanings depend on the dispatched operation - let args = expr.tail(); - let arg0 = args.head(); - let tail0 = args.tail(); - let arg1 = tail0.head(); - let tail1 = tail0.tail(); - let arg2 = tail1.head(); - // And we also have to do the above binding dance - // so that the Perhapss remain in scope. - match frags.next() { - - Some("when") => output.place(&When::new( - state.resolve(arg0?)?.unwrap(), - Thunk::new(move|output: &mut O|state.view(output, &arg1).unwrap()) - )), - - Some("either") => output.place(&Either::new( - state.resolve(arg0?)?.unwrap(), - Thunk::new(move|output: &mut O|state.view(output, &arg1).unwrap()), - Thunk::new(move|output: &mut O|state.view(output, &arg2).unwrap()) - )), - - Some("bsp") => output.place(&{ - let a = Thunk::new(move|output: &mut O|state.view(output, &arg0).unwrap()); - let b = Thunk::new(move|output: &mut O|state.view(output, &arg1).unwrap()); - match frags.next() { - Some("n") => Bsp::n(a, b), - Some("s") => Bsp::s(a, b), - Some("e") => Bsp::e(a, b), - Some("w") => Bsp::w(a, b), - Some("a") => Bsp::a(a, b), - Some("b") => Bsp::b(a, b), - frag => unimplemented!("bsp/{frag:?}") - } - }), - - Some("align") => output.place(&{ - let a = Thunk::new(move|output: &mut O|state.view(output, &arg0).unwrap()); - match frags.next() { - Some("n") => Align::n(a), - Some("s") => Align::s(a), - Some("e") => Align::e(a), - Some("w") => Align::w(a), - Some("x") => Align::x(a), - Some("y") => Align::y(a), - Some("c") => Align::c(a), - frag => unimplemented!("align/{frag:?}") - } - }), - - Some("fill") => output.place(&{ - let a = Thunk::new(move|output: &mut O|state.view(output, &arg0).unwrap()); - match frags.next() { - Some("xy") | None => Fill::XY(a), - Some("x") => Fill::X(a), - Some("y") => Fill::Y(a), - frag => unimplemented!("fill/{frag:?}") - } - }), - - Some("fixed") => output.place(&{ - let axis = frags.next(); - let arg = match axis { Some("x") | Some("y") => arg1, Some("xy") | None => arg2, _ => panic!("fixed: unsupported axis {axis:?}") }; - let cb = Thunk::new(move|output: &mut O|state.view(output, &arg).unwrap()); - match axis { - Some("xy") | None => Fixed::XY(state.resolve(arg0?)?.unwrap(), state.resolve(arg1?)?.unwrap(), cb), - Some("x") => Fixed::X(state.resolve(arg0?)?.unwrap(), cb), - Some("y") => Fixed::Y(state.resolve(arg0?)?.unwrap(), cb), - frag => unimplemented!("fixed/{frag:?} ({expr:?}) ({head:?}) ({:?})", - head.src()?.unwrap_or_default().split("/").next()) - } - }), - - Some("min") => output.place(&{ - let axis = frags.next(); - let arg = match axis { Some("x") | Some("y") => arg1, Some("xy") | None => arg2, _ => panic!("fixed: unsupported axis {axis:?}") }; - let cb = Thunk::new(move|output: &mut O|state.view(output, &arg).unwrap()); - match axis { - Some("xy") | None => Min::XY(state.resolve(arg0?)?.unwrap(), state.resolve(arg1?)?.unwrap(), cb), - Some("x") => Min::X(state.resolve(arg0?)?.unwrap(), cb), - Some("y") => Min::Y(state.resolve(arg0?)?.unwrap(), cb), - frag => unimplemented!("min/{frag:?}") - } - }), - - Some("max") => output.place(&{ - let axis = frags.next(); - let arg = match axis { Some("x") | Some("y") => arg1, Some("xy") | None => arg2, _ => panic!("fixed: unsupported axis {axis:?}") }; - let cb = Thunk::new(move|output: &mut O|state.view(output, &arg).unwrap()); - match axis { - Some("xy") | None => Max::XY(state.resolve(arg0?)?.unwrap(), state.resolve(arg1?)?.unwrap(), cb), - Some("x") => Max::X(state.resolve(arg0?)?.unwrap(), cb), - Some("y") => Max::Y(state.resolve(arg0?)?.unwrap(), cb), - frag => unimplemented!("max/{frag:?}") - } - }), - - Some("push") => output.place(&{ - let axis = frags.next(); - let arg = match axis { Some("x") | Some("y") => arg1, Some("xy") | None => arg2, _ => panic!("fixed: unsupported axis {axis:?}") }; - let cb = Thunk::new(move|output: &mut O|state.view(output, &arg).unwrap()); - match axis { - Some("xy") | None => Push::XY(state.resolve(arg0?)?.unwrap(), state.resolve(arg1?)?.unwrap(), cb), - Some("x") => Push::X(state.resolve(arg0?)?.unwrap(), cb), - Some("y") => Push::Y(state.resolve(arg0?)?.unwrap(), cb), - frag => unimplemented!("push/{frag:?}") - } - }), - - _ => return Ok(false) - - }; - Ok(true) -} diff --git a/output/src/widget.rs b/output/src/widget.rs deleted file mode 100644 index e8fc4c4..0000000 --- a/output/src/widget.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod widget_border; pub use self::widget_border::*; -mod widget_form; pub use self::widget_form::*; -mod widget_style; pub use self::widget_style::*; -mod widget_tryptich; pub use self::widget_tryptich::*; diff --git a/output/src/widget/widget_border.rs b/output/src/widget/widget_border.rs deleted file mode 100644 index 7f44a17..0000000 --- a/output/src/widget/widget_border.rs +++ /dev/null @@ -1,10 +0,0 @@ -use crate::*; - -pub struct Border(pub bool, pub S); -impl> Layout for Border { - fn layout (&self, area: O::Area) -> O::Area { - self.1.layout(area) - } -} - -pub struct Bordered(pub bool, pub S, pub W); diff --git a/output/src/widget/widget_form.rs b/output/src/widget/widget_form.rs deleted file mode 100644 index 960c77d..0000000 --- a/output/src/widget/widget_form.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::*; - -pub struct FieldH(pub Theme, pub Label, pub Value); -impl, V: Content> HasContent for FieldH { - fn content (&self) -> impl Content { Bsp::e(&self.1, &self.2) } -} -impl, V: Content> Layout for FieldH { - fn layout (&self, to: O::Area) -> O::Area { self.content().layout(to) } -} -impl, V: Content> Draw for FieldH { - fn draw (&self, to: &mut O) { self.content().draw(to) } -} - -pub struct FieldV(pub Theme, pub Label, pub Value); -impl, V: Content> HasContent for FieldV { - fn content (&self) -> impl Content { Bsp::s(&self.1, &self.2) } -} -impl, V: Content> Layout for FieldV { - fn layout (&self, to: O::Area) -> O::Area { self.content().layout(to) } -} -impl, V: Content> Draw for FieldV { - fn draw (&self, to: &mut O) { self.content().draw(to) } -} - -// TODO: -pub struct Field { - pub direction: Direction, - pub label: Option, - pub label_fg: Option, - pub label_bg: Option, - pub label_align: Option, - pub value: Option, - pub value_fg: Option, - pub value_bg: Option, - pub value_align: Option, -} -impl Field { - pub fn new (direction: Direction) -> Field { - Field:: { - direction, - label: None, label_fg: None, label_bg: None, label_align: None, - value: None, value_fg: None, value_bg: None, value_align: None, - } - } - pub fn label ( - self, label: Option, align: Option, fg: Option, bg: Option - ) -> Field { - Field:: { label, label_fg: fg, label_bg: bg, label_align: align, ..self } - } - pub fn value ( - self, value: Option, align: Option, fg: Option, bg: Option - ) -> Field { - Field:: { value, value_fg: fg, value_bg: bg, value_align: align, ..self } - } -} diff --git a/output/src/widget/widget_style.rs b/output/src/widget/widget_style.rs deleted file mode 100644 index b7ba4ba..0000000 --- a/output/src/widget/widget_style.rs +++ /dev/null @@ -1,15 +0,0 @@ -#[allow(unused)] use crate::*; - -pub struct Foreground(pub Color, pub Item); -impl> Layout for Foreground { - fn layout (&self, to: O::Area) -> O::Area { - self.1.layout(to) - } -} - -pub struct Background(pub Color, pub Item); -impl> Layout for Background { - fn layout (&self, to: O::Area) -> O::Area { - self.1.layout(to) - } -} diff --git a/output/src/widget/widget_tryptich.rs b/output/src/widget/widget_tryptich.rs deleted file mode 100644 index 3039f5e..0000000 --- a/output/src/widget/widget_tryptich.rs +++ /dev/null @@ -1,31 +0,0 @@ -#[allow(unused)] use crate::*; - -/// A three-column layout. -pub struct Tryptich { - pub top: bool, - pub h: u16, - pub left: (u16, A), - pub middle: (u16, B), - pub right: (u16, C), -} - -impl Tryptich<(), (), ()> { - pub fn center (h: u16) -> Self { - Self { h, top: false, left: (0, ()), middle: (0, ()), right: (0, ()) } - } - pub fn top (h: u16) -> Self { - Self { h, top: true, left: (0, ()), middle: (0, ()), right: (0, ()) } - } -} - -impl Tryptich { - pub fn left (self, w: u16, content: D) -> Tryptich { - Tryptich { left: (w, content), ..self } - } - pub fn middle (self, w: u16, content: D) -> Tryptich { - Tryptich { middle: (w, content), ..self } - } - pub fn right (self, w: u16, content: D) -> Tryptich { - Tryptich { right: (w, content), ..self } - } -}