diff --git a/output/src/layout.rs b/output/src/layout.rs index 14f1cef..57eb080 100644 --- a/output/src/layout.rs +++ b/output/src/layout.rs @@ -507,6 +507,303 @@ transform_xy_unit!("padding/x" "padding/y" "padding/xy"|self: Padding, area|{ "y" => Self::y(tail(0)?, tail(1)?), "xy" => Self::xy(tail(0)?, tail(1)?, tail(2)?), _ => return Err("invalid max variant".into()) }); +} + +/// Lazily-evaluated [Render]able. +pub struct Thunk, F: Fn()->T>( + PhantomData, + F +); +impl, F: Fn()->T> Thunk { + pub const fn new (thunk: F) -> Self { + Self(PhantomData, thunk) + } +} +impl, F: Fn()->T> Content for Thunk { + fn content (&self) -> impl Render { (self.1)() } +} + +pub struct ThunkBox( + PhantomData, + BoxBox>>, +); +impl ThunkBox { + pub const fn new (thunk: BoxBox>>) -> Self { + Self(PhantomData, thunk) + } +} +impl Content for ThunkBox { + fn content (&self) -> impl Render { (&self.1)() } +} +impl FromBox>>> for ThunkBox { + fn from (f: BoxBox>>) -> Self { + Self(PhantomData, f) + } +} +//impl<'a, E: Output, F: Fn()->Box + 'a> + 'a> From for ThunkBox<'a, E> { + //fn from (f: F) -> Self { + //Self(Default::default(), Box::new(f)) + //} +//} + +pub struct ThunkRender(PhantomData, F); +impl ThunkRender { + pub fn new (render: F) -> Self { Self(PhantomData, render) } +} +impl Content for ThunkRender { + fn render (&self, to: &mut E) { (self.1)(to) } +} + +pub struct ThunkLayout< + E: Output, + F1: Fn(E::Area)->E::Area, + F2: Fn(&mut E) +>( + PhantomData, + F1, + F2 +); +implE::Area, F2: Fn(&mut E)> ThunkLayout { + pub fn new (layout: F1, render: F2) -> Self { Self(PhantomData, layout, render) } +} +impl Content for ThunkLayout +where + E: Output, + F1: Fn(E::Area)->E::Area, + F2: Fn(&mut E) +{ + fn layout (&self, to: E::Area) -> E::Area { (self.1)(to) } + fn render (&self, to: &mut E) { (self.2)(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, + render: impl Fn(&mut U, &T, &T)->R + ) -> Option { + if newval != self.value { + let result = render(&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)*) } } +} + +pub struct Stack<'t, E, F1> { + __: PhantomData<&'t (E, F1)>, + direction: Direction, + callback: F1 +} +impl<'t, 'u: 't, E, F1> Stack<'t, E, F1> where + Self: 't, E: Output, F1: Fn(&mut dyn FnMut(&dyn Render)) + 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<'t, 'u, E, F1> Content for Stack<'t, E, F1> where + Self: 't, E: Output, F1: Fn(&mut dyn FnMut(&dyn Render)) + Send + Sync, +{ + fn layout (&self, to: E::Area) -> E::Area { + let Self { direction, callback, .. } = self; + let (mut x, mut y) = (to.x(), to.y()); + let (mut w_used, mut w_remaining) = (E::Unit::zero(), to.w()); + let (mut h_used, mut h_remaining) = (E::Unit::zero(), to.h()); + callback(&mut move|component: &dyn Render|{ + let [_, _, w, h] = component.layout([x, y, w_remaining, h_remaining].into()).xywh(); + match direction { + South => { y = y.plus(h); + h_used = h_used.plus(h); + h_remaining = h_remaining.minus(h); + w_used = w_used.max(w); }, + East => { x = x.plus(w); + w_used = w_used.plus(w); + w_remaining = w_remaining.minus(w); + h_used = h_used.max(h); }, + North | West => { todo!() }, + Above | Below => {}, + } + }); + match direction { + North | West => { todo!() }, + South | East => { [to.x(), to.y(), w_used.into(), h_used.into()].into() }, + Above | Below => { [to.x(), to.y(), to.w(), to.h()].into() }, + } + } + fn render (&self, to: &mut E) { + let Self { direction, callback, .. } = self; + let (mut x, mut y) = (to.x(), to.y()); + let (mut w_used, mut w_remaining) = (E::Unit::zero(), to.w()); + let (mut h_used, mut h_remaining) = (E::Unit::zero(), to.h()); + callback(&mut move|component: &dyn Render|{ + let layout = component.layout([x, y, w_remaining, h_remaining].into()); + match direction { + South => { + y = y.plus(layout.h()); + h_remaining = h_remaining.minus(layout.h()); + h_used = h_used.plus(layout.h()) }, + East => { + x = x.plus(layout.w()); + w_remaining = w_remaining.minus(layout.w()); + w_used = w_used.plus(layout.h()) }, + North | West => { todo!() }, + Above | Below => {} + }; + to.place(layout, component); + }); + } +} + +/// Renders items from an iterator. +pub struct Map +where + I: Iterator + Send + Sync, + F: Fn() -> I + Send + Sync, +{ + __: PhantomData<(E, B)>, + /// Function that returns iterator over stacked components + get_iter: F, + /// Function that returns each stacked component + get_item: G, +} + +impl<'a, E, 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, E, A, B, I, F> Map< + E, A, Push>>>, I, F, fn(A, usize)->B + > where + E: Output, + B: Render, + I: Iterator + Send + Sync + 'a, + F: Fn() -> I + Send + Sync + 'a + { + pub const fn $name ( + size: E::Unit, + get_iter: F, + get_item: impl Fn(A, usize)->B + Send + Sync + ) -> Map< + E, 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: E::Unit = E::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, E, A, B, I, F, G> Content for Map where + E: Output, + B: Render, + I: Iterator + Send + Sync + 'a, + F: Fn() -> I + Send + Sync + 'a, + G: Fn(A, usize)->B + Send + Sync +{ + fn layout (&self, area: E::Area) -> E::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.into()); + min_y = min_y.min(y.into()); + max_x = max_x.max((x + w).into()); + max_y = max_y.max((y + h).into()); + 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()).into() + } + fn render (&self, to: &mut E) { + let Self { get_iter, get_item, .. } = self; + let mut index = 0; + let area = Content::layout(self, to.area()); + for item in get_iter() { + let item = get_item(item, index); + //to.place(area.into(), &item); + to.place(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)))) +} + +/////////////////////////////////////////////////////////////////////////////// ///// The syntagm `(when :condition :content)` corresponds to a [When] layout element. @@ -674,176 +971,6 @@ transform_xy_unit!("padding/x" "padding/y" "padding/xy"|self: Padding, area|{ //)), //_ => None //}) -} - -/// Lazily-evaluated [Render]able. -pub struct Thunk, F: Fn()->T>( - PhantomData, - F -); -impl, F: Fn()->T> Thunk { - pub const fn new (thunk: F) -> Self { - Self(PhantomData, thunk) - } -} -impl, F: Fn()->T> Content for Thunk { - fn content (&self) -> impl Render { (self.1)() } -} - -pub struct ThunkBox( - PhantomData, - BoxBox>>, -); -impl ThunkBox { - pub const fn new (thunk: BoxBox>>) -> Self { - Self(PhantomData, thunk) - } -} -impl Content for ThunkBox { - fn content (&self) -> impl Render { (&self.1)() } -} -impl FromBox>>> for ThunkBox { - fn from (f: BoxBox>>) -> Self { - Self(PhantomData, f) - } -} -//impl<'a, E: Output, F: Fn()->Box + 'a> + 'a> From for ThunkBox<'a, E> { - //fn from (f: F) -> Self { - //Self(Default::default(), Box::new(f)) - //} -//} - -pub struct ThunkRender(PhantomData, F); -impl ThunkRender { - pub fn new (render: F) -> Self { Self(PhantomData, render) } -} -impl Content for ThunkRender { - fn render (&self, to: &mut E) { (self.1)(to) } -} - -pub struct ThunkLayout< - E: Output, - F1: Fn(E::Area)->E::Area, - F2: Fn(&mut E) ->( - PhantomData, - F1, - F2 -); -implE::Area, F2: Fn(&mut E)> ThunkLayout { - pub fn new (layout: F1, render: F2) -> Self { Self(PhantomData, layout, render) } -} -impl Content for ThunkLayout -where - E: Output, - F1: Fn(E::Area)->E::Area, - F2: Fn(&mut E) -{ - fn layout (&self, to: E::Area) -> E::Area { (self.1)(to) } - fn render (&self, to: &mut E) { (self.2)(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, - render: impl Fn(&mut U, &T, &T)->R - ) -> Option { - if newval != self.value { - let result = render(&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)*) } } -} - -pub struct Stack<'t, E, F1> { - __: PhantomData<&'t (E, F1)>, - direction: Direction, - callback: F1 -} -impl<'t, E, F1> Stack<'t, E, F1> where - E: Output, - F1: Fn(&mut dyn FnMut(&(dyn Render + 't))) + 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<'t, E, F1> Content for Stack<'t, E, F1> where - Self: 't, - E: Output, - F1: Fn(&mut dyn FnMut(&(dyn Render + 't))) + Send + Sync, -{ - fn layout (&self, to: E::Area) -> E::Area { - let Self { direction, callback, .. } = self; - let (mut x, mut y) = (to.x(), to.y()); - let (mut w_used, mut w_remaining) = (E::Unit::zero(), to.w()); - let (mut h_used, mut h_remaining) = (E::Unit::zero(), to.h()); - callback(&mut move|component|{ - let [_, _, w, h] = component.layout([x, y, w_remaining, h_remaining].into()).xywh(); - match direction { - South => { y = y.plus(h); - h_used = h_used.plus(h); - h_remaining = h_remaining.minus(h); - w_used = w_used.max(w); }, - East => { x = x.plus(w); - w_used = w_used.plus(w); - w_remaining = w_remaining.minus(w); - h_used = h_used.max(h); }, - North | West => { todo!() }, - Above | Below => {}, - } - }); - match direction { - North | West => { todo!() }, - South | East => { [to.x(), to.y(), w_used.into(), h_used.into()].into() }, - Above | Below => { [to.x(), to.y(), to.w(), to.h()].into() }, - } - } - fn render (&self, to: &mut E) { - let Self { direction, callback, .. } = self; - let (mut x, mut y) = (to.x(), to.y()); - let (mut w_used, mut w_remaining) = (E::Unit::zero(), to.w()); - let (mut h_used, mut h_remaining) = (E::Unit::zero(), to.h()); - callback(&mut move|component|{ - let layout = component.layout([x, y, w_remaining, h_remaining].into()); - match direction { - South => { - y = y.plus(layout.h()); - h_remaining = h_remaining.minus(layout.h()); - h_used = h_used.plus(layout.h()) }, - East => { - x = x.plus(layout.w()); - w_remaining = w_remaining.minus(layout.w()); - w_used = w_used.plus(layout.h()) }, - North | West => { todo!() }, - Above | Below => {} - }; - to.place(layout, component); - }); - } -} /*Stack::down(|add|{ let mut i = 0; @@ -862,131 +989,3 @@ impl<'t, E, F1> Content for Stack<'t, E, F1> where add(&format!("{}/{i}", self.index))?; Ok(()) }));*/ - -/// Renders items from an iterator. -pub struct Map -where - I: Iterator + Send + Sync, - F: Fn() -> I + Send + Sync, -{ - __: PhantomData<(E, B)>, - /// Function that returns iterator over stacked components - get_iter: F, - /// Function that returns each stacked component - get_item: G, -} - -impl<'a, E, 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, E, A, B, I, F> Map< - E, A, Push>>>, I, F, fn(A, usize)->B - > where - E: Output, - B: Render, - I: Iterator + Send + Sync + 'a, - F: Fn() -> I + Send + Sync + 'a - { - pub const fn $name ( - size: E::Unit, - get_iter: F, - get_item: impl Fn(A, usize)->B + Send + Sync - ) -> Map< - E, 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: E::Unit = E::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, E, A, B, I, F, G> Content for Map where - E: Output, - B: Render, - I: Iterator + Send + Sync + 'a, - F: Fn() -> I + Send + Sync + 'a, - G: Fn(A, usize)->B + Send + Sync -{ - fn layout (&self, area: E::Area) -> E::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.into()); - min_y = min_y.min(y.into()); - max_x = max_x.max((x + w).into()); - max_y = max_y.max((y + h).into()); - 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()).into() - } - fn render (&self, to: &mut E) { - let Self { get_iter, get_item, .. } = self; - let mut index = 0; - let area = Content::layout(self, to.area()); - for item in get_iter() { - let item = get_item(item, index); - //to.place(area.into(), &item); - to.place(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)))) -}