wip: and sweeps right through the codebase

This commit is contained in:
🪞👃🪞 2024-12-31 02:03:16 +01:00
parent d37bd3e0c5
commit c9b09b7dea
16 changed files with 370 additions and 625 deletions

View file

@ -1,27 +0,0 @@
use crate::*;
/// A UI component that can render itself as a [Render], and [Handle] input.
pub trait Component<E: Engine>: Render<E> + Handle<E> {}
/// Everything that implements [Render] and [Handle] is a [Component].
impl<E: Engine, C: Render<E> + Handle<E>> Component<E> for C {}
/// A component that can exit.
pub trait Exit: Send {
//fn exited (&self) -> bool;
//fn exit (&mut self);
//fn boxed (self) -> Box<dyn Exit> where Self: Sized + 'static {
//Box::new(self)
//}
}
/// Marker trait for [Component]s that can [Exit].
pub trait ExitableComponent<E>: Exit + Component<E> where E: Engine {
///// Perform type erasure for collecting heterogeneous components.
//fn boxed (self) -> Box<dyn ExitableComponent<E>> where Self: Sized + 'static {
//Box::new(self)
//}
}
/// All [Components]s that implement [Exit] implement [ExitableComponent].
impl<E: Engine, C: Component<E> + Exit> ExitableComponent<E> for C {}

View file

@ -63,6 +63,9 @@ pub trait Size<N: Coordinate> {
Ok(self) Ok(self)
} }
} }
#[inline] fn zero () -> [N;2] {
[N::zero(), N::zero()]
}
} }
impl<N: Coordinate> Size<N> for (N, N) { impl<N: Coordinate> Size<N> for (N, N) {
@ -129,6 +132,10 @@ pub trait Area<N: Coordinate>: Copy {
#[inline] fn shrink_y (&self, y: N) -> [N;4] { #[inline] fn shrink_y (&self, y: N) -> [N;4] {
[self.x(), self.y(), self.w(), self.h().minus(y)] [self.x(), self.y(), self.w(), self.h().minus(y)]
} }
fn zero () -> [N;4] {
[N::zero(), N::zero(), N::zero(), N::zero()]
}
} }
impl<N: Coordinate> Area<N> for (N, N, N, N) { impl<N: Coordinate> Area<N> for (N, N, N, N) {

View file

@ -46,11 +46,11 @@ pub type Perhaps<T> = Result<Option<T>, Box<dyn Error>>;
fn area_mut (&mut self) -> &mut [u16;4] { fn area_mut (&mut self) -> &mut [u16;4] {
&mut self.0 &mut self.0
} }
fn render_in (&mut self, _: [u16;4], _: &impl Render<TestEngine>) -> Usually<()> { fn place (&mut self, _: [u16;4], _: &impl Layout<TestEngine>) {
Ok(()) ()
} }
} }
impl Render<TestEngine> for String { impl Layout<TestEngine> for String {
fn render (&self, to: &mut TestOutput) { fn render (&self, to: &mut TestOutput) {
to.area_mut().set_w(self.len() as u16); to.area_mut().set_w(self.len() as u16);
} }
@ -63,7 +63,7 @@ pub type Perhaps<T> = Result<Option<T>, Box<dyn Error>>;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
struct TestComponent(String); struct TestComponent(String);
impl Layout<Tui> for TestComponent { impl Layout<Tui> for TestComponent {
fn layout (&self) -> Option<impl Render<Tui>> { fn layout (&self) -> Option<impl Layout<Tui>> {
Some(self.0.as_str()) Some(self.0.as_str())
} }
} }

View file

@ -8,35 +8,50 @@ pub trait Output<E: Engine> {
/// Mutable pointer to area /// Mutable pointer to area
fn area_mut (&mut self) -> &mut E::Area; fn area_mut (&mut self) -> &mut E::Area;
///// Render widget in area ///// Render widget in area
fn render_in (&mut self, area: E::Area, widget: &impl Render<E>) -> Usually<()>; fn place (&mut self, area: E::Area, content: &impl Content<E>);
#[inline] fn x (&self) -> E::Unit { self.area().x() } #[inline] fn x (&self) -> E::Unit { self.area().x() }
#[inline] fn y (&self) -> E::Unit { self.area().y() } #[inline] fn y (&self) -> E::Unit { self.area().y() }
#[inline] fn w (&self) -> E::Unit { self.area().w() } #[inline] fn w (&self) -> E::Unit { self.area().w() }
#[inline] fn h (&self) -> E::Unit { self.area().h() } #[inline] fn h (&self) -> E::Unit { self.area().h() }
#[inline] fn wh (&self) -> E::Size { self.area().wh().into() }
} }
pub trait Layout<E: Engine>: Send + Sync { pub trait Content<E: Engine>: Send + Sync {
fn layout (&self) -> Option<impl Render<E>> { fn content (&self) -> Option<impl Content<E>> {
None::<()> None::<()>
} }
fn area (&self, area: E::Area) -> E::Area {
if let Some(content) = self.content() {
content.area(area)
} else {
[0.into(), 0.into(), 0.into(), 0.into()].into()
}
}
fn render (&self, output: &mut E::Output) {
if let Some(content) = self.content() {
output.place(self.area(output.area()), &content)
}
}
} }
impl<E: Engine> Layout<E> for () {} impl<E: Engine> Content<E> for () {}
impl<E: Engine, L: Layout<E>> Layout<E> for &L {} impl<E: Engine, T: Content<E>> Content<E> for &T {}
pub trait Render<E: Engine>: Send + Sync { //impl<E: Engine, R: Render<E>> Render<E> for &R {}
fn render (&self, _: &mut E::Output);
}
impl<E: Engine, L: Layout<E>> Render<E> for L { //pub trait Render<E: Engine>: Send + Sync {
fn render (&self, to: &mut E::Output) { //fn render (&self, _: &mut E::Output);
if let Some(content) = self.layout() { //}
content.render(to)
} //impl<E: Engine, L: Layout<E>> Render<E> for L {
} //fn render (&self, to: &mut E::Output) {
} //if let Some(content) = self.layout() {
//content.render(to)
//}
//}
//}
//impl<E: Engine, L: Layout<E>> Layout<E> for &L {} //impl<E: Engine, L: Layout<E>> Layout<E> for &L {}

View file

@ -89,7 +89,7 @@ impl Tui {
} }
} }
pub trait TuiRun<R: Layout<Tui> + Handle<Tui> + Sized + 'static> { pub trait TuiRun<R: Content<Tui> + Handle<Tui> + Sized + 'static> {
/// Run an app in the main loop. /// Run an app in the main loop.
fn run (&self, state: &Arc<RwLock<R>>) -> Usually<()>; fn run (&self, state: &Arc<RwLock<R>>) -> Usually<()>;
/// Spawn the input thread. /// Spawn the input thread.
@ -98,7 +98,7 @@ pub trait TuiRun<R: Layout<Tui> + Handle<Tui> + Sized + 'static> {
fn run_output (&self, state: &Arc<RwLock<R>>, sleep: Duration) -> JoinHandle<()>; fn run_output (&self, state: &Arc<RwLock<R>>, sleep: Duration) -> JoinHandle<()>;
} }
impl<T: Layout<Tui> + Handle<Tui> + Sized + 'static> TuiRun<T> for Arc<RwLock<Tui>> { impl<T: Content<Tui> + Handle<Tui> + Sized + 'static> TuiRun<T> for Arc<RwLock<Tui>> {
fn run (&self, state: &Arc<RwLock<T>>) -> Usually<()> { fn run (&self, state: &Arc<RwLock<T>>) -> Usually<()> {
let _input_thread = self.run_input(state, Duration::from_millis(100)); let _input_thread = self.run_input(state, Duration::from_millis(100));
self.write().unwrap().setup()?; self.write().unwrap().setup()?;
@ -254,12 +254,11 @@ pub struct TuiOutput {
impl Output<Tui> for TuiOutput { impl Output<Tui> for TuiOutput {
#[inline] fn area (&self) -> [u16;4] { self.area } #[inline] fn area (&self) -> [u16;4] { self.area }
#[inline] fn area_mut (&mut self) -> &mut [u16;4] { &mut self.area } #[inline] fn area_mut (&mut self) -> &mut [u16;4] { &mut self.area }
#[inline] fn render_in (&mut self, area: [u16;4], widget: &impl Render<Tui>) -> Usually<()> { #[inline] fn place (&mut self, area: [u16;4], content: &impl Content<Tui>) {
let last = self.area(); let last = self.area();
*self.area_mut() = area; *self.area_mut() = area;
widget.render(self); content.render(self);
*self.area_mut() = last; *self.area_mut() = last;
Ok(())
} }
} }
@ -330,13 +329,13 @@ pub fn half_block (lower: bool, upper: bool) -> Option<char> {
//impl<T: Content<Tui>> Render<Tui> for T {} //impl<T: Content<Tui>> Render<Tui> for T {}
impl Render<Tui> for &str { impl Content<Tui> for &str {
fn render (&self, to: &mut TuiOutput) { fn render (&self, to: &mut TuiOutput) {
to.blit(self, to.area.x(), to.area.y(), None) to.blit(self, to.area.x(), to.area.y(), None)
} }
} }
impl Render<Tui> for String { impl Content<Tui> for String {
fn render (&self, to: &mut TuiOutput) { fn render (&self, to: &mut TuiOutput) {
to.blit(self, to.area.x(), to.area.y(), None) to.blit(self, to.area.x(), to.area.y(), None)
} }

View file

@ -1,139 +1,76 @@
use crate::*; //! Groupings of elements.
////////////////////////////////////////////////////////////////////////////////////////////////////
mod bsp; pub use self::bsp::*; mod bsp; pub use self::bsp::*;
mod layers; pub use self::layers::*;
mod split; pub use self::split::*; mod split; pub use self::split::*;
mod stack; pub use self::stack::*; mod stack; pub use self::stack::*;
//////////////////////////////////////////////////////////////////////////////////////////////////// use crate::*;
/// A function or closure that emits renderables. /// A function or closure that emits renderables.
pub trait CollectCallback<E: Engine>: Send pub trait Collector<E: Engine>: Send + Sync + Fn(&mut dyn FnMut(&dyn Content<E>)) {}
+ Sync
+ Fn(&mut dyn FnMut(&dyn Render<E>)->Usually<()>)->Usually<()>
{}
/// Any function or closure that emits renderables for the given engine matches [CollectCallback]. /// Any function or closure that emits renderables for the given engine matches [CollectCallback].
impl<E, F> CollectCallback<E> for F impl<E, F> Collector<E> for F
where where E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Content<E>)) {}
E: Engine,
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)->Usually<()>)->Usually<()>
{}
//////////////////////////////////////////////////////////////////////////////////////////////////// pub struct Map<E, T, I, R, F>(PhantomData<E>, I, F)
where E: Engine, I: Iterator<Item = T>, R: Content<E>, F: Fn(T)->R;
pub enum Collect<'a, E: Engine, const N: usize> { pub struct Reduce<E, T, I, R, F>(PhantomData<(E, R)>, I, F)
Callback(CallbackCollection<'a, E>), where E: Engine, I: Iterator<Item = T>, R: Content<E>, F: Fn(&dyn Content<E>, T)->R;
//Iterator(IteratorCollection<'a, E>),
Array(ArrayCollection<'a, E, N>),
Slice(SliceCollection<'a, E>),
}
impl<'a, E: Engine, const N: usize> Collect<'a, E, N> { impl<E: Engine, T, I: Iterator<Item=T>+Send+Sync, R: Content<E>, F: Fn(&dyn Content<E>, T)->R+Send+Sync> Content<E> for Reduce<E, T, I, R, F> {
pub fn iter (&'a self) -> CollectIterator<'a, E, N> { fn render (&self, _to: &mut E::Output) {
CollectIterator(0, &self)
}
}
impl<'a, E: Engine, const N: usize> From<CallbackCollection<'a, E>> for Collect<'a, E, N> {
fn from (callback: CallbackCollection<'a, E>) -> Self {
Self::Callback(callback)
}
}
impl<'a, E: Engine, const N: usize> From<SliceCollection<'a, E>> for Collect<'a, E, N> {
fn from (slice: SliceCollection<'a, E>) -> Self {
Self::Slice(slice)
}
}
impl<'a, E: Engine, const N: usize> From<ArrayCollection<'a, E, N>> for Collect<'a, E, N>{
fn from (array: ArrayCollection<'a, E, N>) -> Self {
Self::Array(array)
}
}
type CallbackCollection<'a, E> =
&'a dyn Fn(&'a mut dyn FnMut(&dyn Render<E>)->Usually<()>);
//type IteratorCollection<'a, E> =
//&'a mut dyn Iterator<Item = dyn Render<E>>;
type SliceCollection<'a, E> =
&'a [&'a dyn Render<E>];
type ArrayCollection<'a, E, const N: usize> =
[&'a dyn Render<E>; N];
pub struct CollectIterator<'a, E: Engine, const N: usize>(usize, &'a Collect<'a, E, N>);
impl<'a, E: Engine, const N: usize> Iterator for CollectIterator<'a, E, N> {
type Item = &'a dyn Render<E>;
fn next (&mut self) -> Option<Self::Item> {
match self.1 {
Collect::Callback(_callback) => {
todo!()
},
//Collection::Iterator(iterator) => {
//iterator.next()
//},
Collect::Array(array) => {
if let Some(_item) = array.get(self.0) {
self.0 += 1;
//Some(item)
None
} else {
None
}
}
Collect::Slice(slice) => {
if let Some(_item) = slice.get(self.0) {
self.0 += 1;
//Some(item)
None
} else {
None
}
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
pub struct Map<E: Engine, T, I: Iterator<Item=T>, R: Render<E>, F: Fn(T)->R>(
PhantomData<E>,
I,
F
);
////////////////////////////////////////////////////////////////////////////////////////////////////
pub struct Reduce<E: Engine, T, I: Iterator<Item=T>, R: Render<E>, F: Fn(&dyn Render<E>, T)->R>(
PhantomData<(E, R)>,
I,
F
);
impl<E: Engine, T, I: Iterator<Item=T>+Send+Sync, R: Render<E>, F: Fn(&dyn Render<E>, T)->R+Send+Sync> Render<E> for Reduce<E, T, I, R, F> {
fn min_size (&self, _to: E::Size) -> Perhaps<E::Size> {
todo!()
}
fn render (&self, _to: &mut E::Output) -> Usually<()> {
todo!() todo!()
} }
} }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// Conditional rendering, in unary and binary forms.
pub struct Cond;
/////////////////////////////////////////////////////////////////////////////////////////////////// impl Cond {
/// Content `item` when `cond` is true.
/////////////////////////////////////////////////////////////////////////////////////////////////// pub fn when <E: Engine, A: Content<E>> (cond: bool, item: A) -> When<E, A> {
When(cond, item, Default::default())
/////////////////////////////////////////////////////////////////////////////////////////////////// }
/// Content `item` if `cond` is true, otherwise render `other`.
#[cfg(test)] #[test] fn test_bsp () { pub fn either <E: Engine, A: Content<E>, B: Content<E>> (cond: bool, a: A, b: B) -> Either<E, A, B> {
// TODO Either(cond, a, b, Default::default())
}
}
/// Contents `self.1` when `self.0` is true.
pub struct When<E: Engine, A>(bool, A, PhantomData<E>);
impl<E: Engine, A: Content<E>> Content<E> for When<E, A> {
fn area (&self, to: E::Area) -> E::Area {
let Self(cond, item, ..) = self;
let mut area = E::Area::zero();
if *cond {
let item_area = item.area(to);
area[0] = item_area.x();
area[1] = item_area.y();
area[2] = item_area.w();
area[3] = item_area.h();
}
area.into()
}
fn render (&self, to: &mut E::Output) {
let Self(cond, item, ..) = self;
if *cond { item.render(to) }
}
}
/// Contents `self.1` when `self.0` is true, otherwise renders `self.2`
pub struct Either<E: Engine, A, B>(bool, A, B, PhantomData<E>);
impl<E: Engine, A: Content<E>, B: Content<E>> Content<E> for Either<E, A, B> {
fn area (&self, to: E::Area) -> E::Area {
let Self(cond, a, b, ..) = self;
if *cond { a.area(to) } else { b.area(to) }
}
fn render (&self, to: &mut E::Output) {
let Self(cond, a, b, ..) = self;
if *cond { a.render(to) } else { b.render(to) }
}
} }

View file

@ -1,14 +1,32 @@
use crate::*; use crate::*;
pub enum Bsp<E: Engine, X: Render<E>, Y: Render<E>> { /// Renders multiple things on top of each other,
macro_rules! lay {
($($expr:expr),*) => {{
let layers = ();
$(let layers = Bsp::b(layers, $expr);)*;
layers
}}
}
/// Renders multiple things on top of each other,
macro_rules! col {
($($expr:expr),*) => {{
let layers = ();
$(let layers = Bsp::s(layers, $expr);)*;
layers
}}
}
pub enum Bsp<E: Engine, X: Content<E>, Y: Content<E>> {
/// X is north of Y /// X is north of Y
N(Option<X>, Option<Y>), N(f64, Option<X>, Option<Y>),
/// X is south of Y /// X is south of Y
S(Option<X>, Option<Y>), S(f64, Option<X>, Option<Y>),
/// X is east of Y /// X is east of Y
E(Option<X>, Option<Y>), E(f64, Option<X>, Option<Y>),
/// X is west of Y /// X is west of Y
W(Option<X>, Option<Y>), W(f64, Option<X>, Option<Y>),
/// X is above Y /// X is above Y
A(Option<X>, Option<Y>), A(Option<X>, Option<Y>),
/// X is below Y /// X is below Y
@ -17,83 +35,56 @@ pub enum Bsp<E: Engine, X: Render<E>, Y: Render<E>> {
Null(PhantomData<E>), Null(PhantomData<E>),
} }
impl<E: Engine, X: Render<E>, Y: Render<E>> Bsp<E, X, Y> { impl<E: Engine, X: Content<E>, Y: Content<E>> Bsp<E, X, Y> {
pub fn new (x: X) -> Self { Self::A(Some(x), None) } pub fn n (p: f64, x: X, y: Y) -> Self { Self::N(p, Some(x), Some(y)) }
pub fn n (x: X, y: Y) -> Self { Self::N(Some(x), Some(y)) } pub fn s (p: f64, x: X, y: Y) -> Self { Self::S(p, Some(x), Some(y)) }
pub fn s (x: X, y: Y) -> Self { Self::S(Some(x), Some(y)) } pub fn e (p: f64, x: X, y: Y) -> Self { Self::E(p, Some(x), Some(y)) }
pub fn e (x: X, y: Y) -> Self { Self::E(Some(x), Some(y)) } pub fn w (p: f64, x: X, y: Y) -> Self { Self::W(p, Some(x), Some(y)) }
pub fn w (x: X, y: Y) -> Self { Self::W(Some(x), Some(y)) }
pub fn a (x: X, y: Y) -> Self { Self::A(Some(x), Some(y)) } pub fn a (x: X, y: Y) -> Self { Self::A(Some(x), Some(y)) }
pub fn b (x: X, y: Y) -> Self { Self::B(Some(x), Some(y)) } pub fn b (x: X, y: Y) -> Self { Self::B(Some(x), Some(y)) }
} }
impl<E: Engine, X: Render<E>, Y: Render<E>> Default for Bsp<E, X, Y> { impl<E: Engine, X: Content<E>, Y: Content<E>> Default for Bsp<E, X, Y> {
fn default () -> Self { fn default () -> Self {
Self::Null(Default::default()) Self::Null(Default::default())
} }
} }
impl<E: Engine, X: Render<E>, Y: Render<E>> Render<E> for Bsp<E, X, Y> { impl<E: Engine, X: Content<E>, Y: Content<E>> Content<E> for Bsp<E, X, Y> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> { fn render (&self, to: &mut E::Output) {
Ok(Some(match self { let n = E::Size::zero();
Self::Null(_) => [0.into(), 0.into()].into(), let s = to.wh();
Self::S(a, b) => { match self {
let a = a.min_size(to)?.unwrap_or([0.into(), 0.into()].into());
let b = b.min_size(to)?.unwrap_or([0.into(), 0.into()].into());
[a.w().max(b.w()), a.h() + b.h()].into()
},
Self::E(a, b) => {
let a = a.min_size(to)?.unwrap_or([0.into(), 0.into()].into());
let b = b.min_size(to)?.unwrap_or([0.into(), 0.into()].into());
[a.w() + b.w(), a.h().max(b.h())].into()
},
Self::W(a, b) => {
let a = a.min_size(to)?.unwrap_or([0.into(), 0.into()].into());
let b = b.min_size(to)?.unwrap_or([0.into(), 0.into()].into());
[a.w() + b.w(), a.h().max(b.h())].into()
},
Self::N(a, b) => {
let a = a.min_size(to)?.unwrap_or([0.into(), 0.into()].into());
let b = b.min_size(to)?.unwrap_or([0.into(), 0.into()].into());
[a.w().max(b.w()), a.h() + b.h()].into()
},
_ => todo!()
}))
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
let n = [0.into(), 0.into()].into();
let s = to.area().wh().into();
Ok(match self {
Self::Null(_) => {}, Self::Null(_) => {},
Self::S(a, b) => { //Self::S(p, a, b) => {
let s_a = a.min_size(s)?.unwrap_or(n); //let s_a = a.min_size(s)?.unwrap_or(n);
let _ = b.min_size(s)?.unwrap_or(n); //let _ = b.min_size(s)?.unwrap_or(n);
let h = s_a.h().into(); //let h = s_a.h().into();
to.render_in(to.area().clip_h(h).into(), a)?; //to.render_in(to.area().clip_h(h).into(), a);
to.render_in(to.area().shrink_y(h).push_y(h).into(), b)?; //to.render_in(to.area().shrink_y(h).push_y(h).into(), b);
}, //},
Self::E(a, b) => { //Self::E(p, a, b) => {
let s_a = a.min_size(s)?.unwrap_or(n); //let s_a = a.min_size(s)?.unwrap_or(n);
let _ = b.min_size(s)?.unwrap_or(n); //let _ = b.min_size(s)?.unwrap_or(n);
let w = s_a.w().into(); //let w = s_a.w().into();
to.render_in(to.area().clip_w(w).into(), a)?; //to.render_in(to.area().clip_w(w).into(), a);
to.render_in(to.area().push_x(w).shrink_x(w).into(), b)?; //to.render_in(to.area().push_x(w).shrink_x(w).into(), b);
}, //},
Self::W(a, b) => { //Self::W(p, a, b) => {
let s_a = a.min_size(s)?.unwrap_or(n); //let s_a = a.min_size(s)?.unwrap_or(n);
let _ = b.min_size(s)?.unwrap_or(n); //let _ = b.min_size(s)?.unwrap_or(n);
let w = (to.area().w() - s_a.w()).into(); //let w = (to.area().w() - s_a.w()).into();
to.render_in(to.area().push_x(w).into(), a)?; //to.render_in(to.area().push_x(w).into(), a);
to.render_in(to.area().shrink_x(w).into(), b)?; //to.render_in(to.area().shrink_x(w).into(), b);
}, //},
Self::N(a, b) => { //Self::N(p, a, b) => {
let s_a = a.min_size(s)?.unwrap_or(n); //let s_a = a.min_size(s)?.unwrap_or(n);
let _ = b.min_size(s)?.unwrap_or(n); //let _ = b.min_size(s)?.unwrap_or(n);
let h = to.area().h() - s_a.h(); //let h = to.area().h() - s_a.h();
to.render_in(to.area().push_y(h).into(), a)?; //to.render_in(to.area().push_y(h).into(), a);
to.render_in(to.area().shrink_y(h).into(), b)?; //to.render_in(to.area().shrink_y(h).into(), b);
}, //},
_ => todo!() _ => todo!()
}) }
} }
} }

View file

@ -1,56 +0,0 @@
use crate::*;
/// Renders multiple things on top of each other,
/// in the order they are provided by the callback.
/// Total size is largest width x largest height.
pub struct Layers<E: Engine, F: CollectCallback<E>>(pub F, PhantomData<E>);
/// Shorthand for defining an instance of [Layers].
#[macro_export] macro_rules! lay {
([$($expr:expr),* $(,)?]) => {
Layers::new(move|add|{ $(add(&$expr)?;)* Ok(()) })
};
(![$($expr:expr),* $(,)?]) => {
Layers::new(|add|{ $(add(&$expr)?;)* Ok(()) })
};
($expr:expr) => {
Layers::new($expr)
};
}
impl<
E: Engine,
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)->Usually<()>)->Usually<()>
> Layers<E, F> {
#[inline]
pub fn new (build: F) -> Self {
Self(build, Default::default())
}
}
impl<E: Engine, F> Render<E> for Layers<E, F>
where
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)->Usually<()>)->Usually<()>
{
fn min_size (&self, area: E::Size) -> Perhaps<E::Size> {
let mut w: E::Unit = 0.into();
let mut h: E::Unit = 0.into();
(self.0)(&mut |layer| {
if let Some(layer_area) = layer.min_size(area)? {
w = w.max(layer_area.w());
h = h.max(layer_area.h());
}
Ok(())
})?;
Ok(Some([w, h].into()))
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
if let Some(size) = self.min_size(to.area().wh().into())? {
(self.0)(&mut |layer|to.render_in(to.area().clip(size).into(), &layer))
} else {
Ok(())
}
}
}

View file

@ -2,9 +2,9 @@ use crate::*;
/// A binary split with fixed proportion /// A binary split with fixed proportion
pub struct Split<E, A, B>(pub bool, pub Direction, pub E::Unit, A, B, PhantomData<E>) pub struct Split<E, A, B>(pub bool, pub Direction, pub E::Unit, A, B, PhantomData<E>)
where E: Engine, A: Render<E>, B: Render<E>; where E: Engine, A: Content<E>, B: Content<E>;
impl<E: Engine, A: Render<E>, B: Render<E>> Split<E, A, B> { impl<E: Engine, A: Content<E>, B: Content<E>> Split<E, A, B> {
#[inline] pub fn new (flip: bool, direction: Direction, proportion: E::Unit, a: A, b: B) -> Self { #[inline] pub fn new (flip: bool, direction: Direction, proportion: E::Unit, a: A, b: B) -> Self {
Self(flip, direction, proportion, a, b, Default::default()) Self(flip, direction, proportion, a, b, Default::default())
} }
@ -22,148 +22,15 @@ impl<E: Engine, A: Render<E>, B: Render<E>> Split<E, A, B> {
} }
} }
impl<E: Engine, A: Render<E>, B: Render<E>> Render<E> for Split<E, A, B> { impl<E: Engine, A: Content<E>, B: Content<E>> Content<E> for Split<E, A, B> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> { fn render (&self, to: &mut E::Output) {
Ok(Some(to))
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
let (a, b) = self.1.split_fixed(to.area(), self.2); let (a, b) = self.1.split_fixed(to.area(), self.2);
Ok(if self.0 { if self.0 {
to.render_in(a.into(), &self.4)?; to.place(a.into(), &self.4);
to.render_in(b.into(), &self.3)?; to.place(b.into(), &self.3);
} else { } else {
to.render_in(a.into(), &self.3)?; to.place(a.into(), &self.3);
to.render_in(b.into(), &self.4)?; to.place(b.into(), &self.4);
})
} }
} }
impl<E: Engine, F> Render<E> for Stack<E, F>
where
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)->Usually<()>)->Usually<()>
{
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
match self.1 {
South => {
let mut w: E::Unit = 0.into();
let mut h: E::Unit = 0.into();
(self.0)(&mut |component: &dyn Render<E>| {
let max = to.h().minus(h);
if max > E::Unit::zero() {
let item = Max::y(max, Push::y(h, component));
let size = item.min_size(to)?.map(|size|size.wh());
if let Some([width, height]) = size {
h = h + height.into();
w = w.max(width);
} }
}
Ok(())
})?;
Ok(Some([w, h].into()))
},
East => {
let mut w: E::Unit = 0.into();
let mut h: E::Unit = 0.into();
(self.0)(&mut |component: &dyn Render<E>| {
let max = to.w().minus(w);
if max > E::Unit::zero() {
let item = Max::x(max, Push::x(h, component));
let size = item.min_size(to)?.map(|size|size.wh());
if let Some([width, height]) = size {
w = w + width.into();
h = h.max(height);
}
}
Ok(())
})?;
Ok(Some([w, h].into()))
},
North => {
let mut w: E::Unit = 0.into();
let mut h: E::Unit = 0.into();
(self.0)(&mut |component: &dyn Render<E>| {
let max = to.h().minus(h);
if max > E::Unit::zero() {
let item = Max::y(to.h() - h, component);
let size = item.min_size(to)?.map(|size|size.wh());
if let Some([width, height]) = size {
h = h + height.into();
w = w.max(width);
}
}
Ok(())
})?;
Ok(Some([w, h].into()))
},
West => {
let w: E::Unit = 0.into();
let h: E::Unit = 0.into();
(self.0)(&mut |_component: &dyn Render<E>| {
if w < to.w() {
todo!();
}
Ok(())
})?;
Ok(Some([w, h].into()))
},
}
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
let area = to.area();
let mut w = 0.into();
let mut h = 0.into();
match self.1 {
South => {
(self.0)(&mut |item| {
if h < area.h() {
let item = Max::y(area.h() - h, Push::y(h, item));
let show = item.min_size(area.wh().into())?.map(|s|s.wh());
if let Some([width, height]) = show {
item.render(to)?;
h = h + height;
if width > w { w = width }
};
}
Ok(())
})?;
},
East => {
(self.0)(&mut |item| {
if w < area.w() {
let item = Max::x(area.w() - w, Push::x(w, item));
let show = item.min_size(area.wh().into())?.map(|s|s.wh());
if let Some([width, height]) = show {
item.render(to)?;
w = width + w;
if height > h { h = height }
};
}
Ok(())
})?;
},
North => {
(self.0)(&mut |item| {
if h < area.h() {
let show = item.min_size([area.w(), area.h().minus(h)].into())?.map(|s|s.wh());
if let Some([width, height]) = show {
Shrink::y(height, Push::y(area.h() - height, item))
.render(to)?;
h = h + height;
if width > w { w = width }
};
}
Ok(())
})?;
},
_ => todo!()
};
Ok(())
}
}

View file

@ -1,8 +1,8 @@
use crate::*; use crate::*;
pub struct Stack<E: Engine, F: CollectCallback<E>>(pub F, pub Direction, PhantomData<E>); pub struct Stack<E: Engine, F: Collector<E>>(pub F, pub Direction, PhantomData<E>);
impl<E: Engine, F: CollectCallback<E>> Stack<E, F> { impl<E: Engine, F: Collector<E>> Stack<E, F> {
#[inline] pub fn new (direction: Direction, build: F) -> Self { #[inline] pub fn new (direction: Direction, build: F) -> Self {
Self(build, direction, Default::default()) Self(build, direction, Default::default())
} }
@ -17,49 +17,101 @@ impl<E: Engine, F: CollectCallback<E>> Stack<E, F> {
} }
} }
#[macro_export] macro_rules! col { //#[macro_export] macro_rules! col {
([$($expr:expr),* $(,)?]) => { //([$($expr:expr),* $(,)?]) => {
Stack::down(move|add|{ $(add(&$expr)?;)* Ok(()) }) //Stack::down(move|add|{ $(add(&$expr)?;)* Ok(()) })
}; //};
(![$($expr:expr),* $(,)?]) => { //(![$($expr:expr),* $(,)?]) => {
Stack::down(|add|{ $(add(&$expr)?;)* Ok(()) }) //Stack::down(|add|{ $(add(&$expr)?;)* Ok(()) })
}; //};
($expr:expr) => { //($expr:expr) => {
Stack::down($expr) //Stack::down($expr)
}; //};
($pat:pat in $collection:expr => $item:expr) => { //($pat:pat in $collection:expr => $item:expr) => {
Stack::down(move|add|{ for $pat in $collection { add(&$item)?; } Ok(()) }) //Stack::down(move|add|{ for $pat in $collection { add(&$item)?; } Ok(()) })
}; //};
} //}
#[macro_export] macro_rules! col_up { //#[macro_export] macro_rules! col_up {
([$($expr:expr),* $(,)?]) => { //([$($expr:expr),* $(,)?]) => {
Stack::up(move|add|{ $(add(&$expr)?;)* Ok(()) }) //Stack::up(move|add|{ $(add(&$expr)?;)* Ok(()) })
}; //};
(![$($expr:expr),* $(,)?]) => { //(![$($expr:expr),* $(,)?]) => {
Stack::up(|add|{ $(add(&$expr)?;)* Ok(()) }) //Stack::up(|add|{ $(add(&$expr)?;)* Ok(()) })
}; //};
($expr:expr) => { //($expr:expr) => {
Stack::up(expr) //Stack::up(expr)
}; //};
($pat:pat in $collection:expr => $item:expr) => { //($pat:pat in $collection:expr => $item:expr) => {
Stack::up(move |add|{ for $pat in $collection { add(&$item)?; } Ok(()) }) //Stack::up(move |add|{ for $pat in $collection { add(&$item)?; } Ok(()) })
}; //};
} //}
#[macro_export] macro_rules! row {
([$($expr:expr),* $(,)?]) => {
Stack::right(move|add|{ $(add(&$expr)?;)* Ok(()) })
};
(![$($expr:expr),* $(,)?]) => {
Stack::right(|add|{ $(add(&$expr)?;)* Ok(()) })
};
($expr:expr) => {
Stack::right($expr)
};
($pat:pat in $collection:expr => $item:expr) => {
Stack::right(move|add|{ for $pat in $collection { add(&$item)?; } Ok(()) })
};
}
//#[macro_export] macro_rules! row {
//([$($expr:expr),* $(,)?]) => {
//Stack::right(move|add|{ $(add(&$expr)?;)* Ok(()) })
//};
//(![$($expr:expr),* $(,)?]) => {
//Stack::right(|add|{ $(add(&$expr)?;)* Ok(()) })
//};
//($expr:expr) => {
//Stack::right($expr)
//};
//($pat:pat in $collection:expr => $item:expr) => {
//Stack::right(move|add|{ for $pat in $collection { add(&$item)?; } Ok(()) })
//};
//}
//impl<E: Engine, F: Collector<E>> Content<E> for Stack<E, F> {
//fn render (&self, to: &mut E::Output) {
//let area = to.area();
//let mut w = 0.into();
//let mut h = 0.into();
//match self.1 {
//South => {
//(self.0)(&mut |item| {
//if h < area.h() {
//let item = Max::y(area.h() - h, Push::y(h, item));
//let show = item.min_size(area.wh().into())?.map(|s|s.wh());
//if let Some([width, height]) = show {
//item.render(to)?;
//h = h + height;
//if width > w { w = width }
//};
//}
//Ok(())
//})?;
//},
//East => {
//(self.0)(&mut |item| {
//if w < area.w() {
//let item = Max::x(area.w() - w, Push::x(w, item));
//let show = item.min_size(area.wh().into())?.map(|s|s.wh());
//if let Some([width, height]) = show {
//item.render(to)?;
//w = width + w;
//if height > h { h = height }
//};
//}
//Ok(())
//})?;
//},
//North => {
//(self.0)(&mut |item| {
//if h < area.h() {
//let show = item.min_size([area.w(), area.h().minus(h)].into())?.map(|s|s.wh());
//if let Some([width, height]) = show {
//Shrink::y(height, Push::y(area.h() - height, item))
//.render(to)?;
//h = h + height;
//if width > w { w = width }
//};
//}
//Ok(())
//})?;
//},
//_ => todo!()
//};
//Ok(())
//}
//}

View file

@ -3,10 +3,32 @@ use crate::*;
/// A cardinal direction. /// A cardinal direction.
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub enum Direction { North, South, West, East, } pub enum Direction { North, South, West, East, }
pub use self::Direction::*; pub use self::Direction::*;
impl Direction { impl Direction {
#[inline] pub fn is_north (&self) -> bool { matches!(self, Self::North) }
pub fn is_south (&self) -> bool { matches!(self, Self::South) }
pub fn is_east (&self) -> bool { matches!(self, Self::West) }
pub fn is_west (&self) -> bool { matches!(self, Self::East) }
/// Return next direction clockwise
pub fn cw (&self) -> Self {
match self {
Self::North => Self::East,
Self::South => Self::West,
Self::West => Self::North,
Self::East => Self::South,
}
}
/// Return next direction counterclockwise
pub fn ccw (&self) -> Self {
match self {
Self::North => Self::West,
Self::South => Self::East,
Self::West => Self::South,
Self::East => Self::North,
}
}
pub fn split_fixed <N: Coordinate> (self, area: impl Area<N>, a: N) -> ([N;4],[N;4]) { pub fn split_fixed <N: Coordinate> (self, area: impl Area<N>, a: N) -> ([N;4],[N;4]) {
match self { match self {
North => ( North => (

View file

@ -1,7 +1,6 @@
mod collection; pub use self::collection::*; mod collection; pub use self::collection::*;
mod direction; pub use self::direction::*; mod direction; pub use self::direction::*;
mod logic; pub use self::logic::*; mod measure; pub use self::measure::*;
mod space; pub use self::space::*;
mod transform; pub use self::transform::*; mod transform; pub use self::transform::*;
pub use ::tek_engine; pub use ::tek_engine;
@ -11,3 +10,7 @@ pub(crate) use std::marker::PhantomData;
#[cfg(test)] #[test] fn test_layout () -> Usually<()> { #[cfg(test)] #[test] fn test_layout () -> Usually<()> {
Ok(()) Ok(())
} }
#[cfg(test)] #[test] fn test_bsp () {
// TODO
}

View file

@ -1,29 +0,0 @@
use crate::*;
/// Conditional rendering, in unary and binary forms.
pub struct Cond;
impl Cond {
/// Show an item conditionally.
pub fn when <E: Engine> (cond: bool, item: Box<dyn Layout<E>>) -> When<E> {
When(cond, item)
}
/// Show either of two items.
pub fn either <E: Engine> (cond: bool, a: Box<dyn Layout<E>>, b: Box<dyn Layout<E>>) -> Either<E> {
Either(cond, a, b)
}
}
pub struct When<E>(bool, Box<dyn Layout<E>>);
impl<E: Engine> Layout<E> for When<E> {
fn layout (self, _: &mut E::Output) -> Option<Box<dyn Layout<E>>> {
if self.0 { Some(self.1) } else { None }
}
}
pub struct Either<E: Engine>(bool, Box<dyn Layout<E>>, Box<dyn Layout<E>>);
impl<E: Engine> Layout<E> for Either<E> {
fn layout (self, _: &mut E::Output) -> Option<Box<dyn Layout<E>>> {
Some(if self.0 { self.1 } else { self.2 })
}
}

View file

@ -3,31 +3,6 @@ use std::sync::{Arc, atomic::{AtomicUsize, Ordering::Relaxed}};
// TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small // TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small
impl Direction {
pub fn is_north (&self) -> bool { matches!(self, Self::North) }
pub fn is_south (&self) -> bool { matches!(self, Self::South) }
pub fn is_east (&self) -> bool { matches!(self, Self::West) }
pub fn is_west (&self) -> bool { matches!(self, Self::East) }
/// Return next direction clockwise
pub fn cw (&self) -> Self {
match self {
Self::North => Self::East,
Self::South => Self::West,
Self::West => Self::North,
Self::East => Self::South,
}
}
/// Return next direction counterclockwise
pub fn ccw (&self) -> Self {
match self {
Self::North => Self::West,
Self::South => Self::East,
Self::West => Self::South,
Self::East => Self::North,
}
}
}
pub trait HasSize<E: Engine> { pub trait HasSize<E: Engine> {
fn size (&self) -> &Measure<E>; fn size (&self) -> &Measure<E>;
} }
@ -48,7 +23,7 @@ pub struct Measure<E: Engine> {
pub y: Arc<AtomicUsize>, pub y: Arc<AtomicUsize>,
} }
impl<E: Engine> Render<E> for Measure<E> { impl<E: Engine> Content<E> for Measure<E> {
fn render (&self, to: &mut E::Output) { fn render (&self, to: &mut E::Output) {
self.x.store(to.area().w().into(), Relaxed); self.x.store(to.area().w().into(), Relaxed);
self.y.store(to.area().h().into(), Relaxed); self.y.store(to.area().h().into(), Relaxed);
@ -95,12 +70,12 @@ impl<E: Engine> Measure<E> {
pub struct Scroll<E, F>(pub F, pub Direction, pub u64, PhantomData<E>) pub struct Scroll<E, F>(pub F, pub Direction, pub u64, PhantomData<E>)
where where
E: Engine, E: Engine,
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)->Usually<()>)->Usually<()>; F: Send + Sync + Fn(&mut dyn FnMut(&dyn Content<E>)->Usually<()>)->Usually<()>;
//pub trait LayoutDebug<E: Engine> { //pub trait ContentDebug<E: Engine> {
//fn debug <W: Render<E>> (other: W) -> DebugOverlay<E, W> { //fn debug <W: Content<E>> (other: W) -> DebugOverlay<E, W> {
//DebugOverlay(Default::default(), other) //DebugOverlay(Default::default(), other)
//} //}
//} //}
//impl<E: Engine> LayoutDebug<E> for E {} //impl<E: Engine> ContentDebug<E> for E {}

View file

@ -1 +0,0 @@
use crate::*;

View file

@ -8,46 +8,60 @@ use crate::*;
/// double generic. /// double generic.
macro_rules! content_enum { macro_rules! content_enum {
($Enum:ident: $($Variant:ident),+ $(,)?) => { ($Enum:ident: $($Variant:ident),+ $(,)?) => {
pub enum $Enum<E: Engine, T: Render<E>> { pub enum $Enum<E: Engine, T: Content<E>> {
_Unused(PhantomData<E>), $($Variant(T)),+ _Unused(PhantomData<E>), $($Variant(T)),+
} }
impl<E: Engine, T: Render<E>> $Enum<E, T> {
fn content (&self) -> Option<impl Render<E>> {
match self {
Self::_Unused(_) => None,
$(Self::$Variant(content) => Some(content)),+
}
}
}
} }
} }
/// Defines an enum that transforms its content /// Defines an enum that transforms its content
/// along either the X axis, the Y axis, or both. /// along either the X axis, the Y axis, or both.
macro_rules! transform_xy { macro_rules! transform_xy {
(|$self:ident : $Enum:ident, $to:ident|$render:expr) => { ($self:ident : $Enum:ident |$to:ident|$area:expr) => {
content_enum!($Enum: X, Y, XY); content_enum!($Enum: X, Y, XY);
impl<E: Engine, T: Render<E>> $Enum<E, T> { impl<E: Engine, T: Content<E>> $Enum<E, T> {
pub fn x (item: T) -> Self { Self::X(item) } pub fn x (item: T) -> Self { Self::X(item) }
pub fn y (item: T) -> Self { Self::Y(item) } pub fn y (item: T) -> Self { Self::Y(item) }
pub fn xy (item: T) -> Self { Self::XY(item) } pub fn xy (item: T) -> Self { Self::XY(item) }
} }
impl<E: Engine, T: Render<E>> Render<E> for $Enum<E, T> { impl<E: Engine, T: Content<E>> Content<E> for $Enum<E, T> {
fn render (&$self, $to: &mut <E as Engine>::Output) { fn content (&self) -> Option<impl Content<E>> {
$render match self {
Self::_Unused(_) => None,
Self::X(item) => Some(item),
Self::Y(item) => Some(item),
Self::XY(item) => Some(item),
}
}
fn area (&$self, $to: <E as Engine>::Area) -> <E as Engine>::Area {
$area
} }
} }
} }
} }
transform_xy!(self: Fill |to|{
let [x0, y0, wmax, hmax] = to.xywh();
if let Some(content) = self.content() {
let [x, y, w, h] = content.area(to).xywh();
return match self {
Self::X(_) => [x0, y, wmax, h],
Self::Y(_) => [x, y0, w, hmax],
Self::XY(_) => [x0, y0, wmax, hmax],
_ => unreachable!()
}.into()
}
return [0.into(), 0.into(), 0.into(), 0.into(),].into()
});
/// Defines an enum that transforms its content parametrically /// Defines an enum that transforms its content parametrically
/// along either the X axis, the Y axis, or both /// along either the X axis, the Y axis, or both
macro_rules! transform_xy_unit { macro_rules! transform_xy_unit {
(|$self:ident : $Enum:ident, $to:ident|$render:expr) => { (|$self:ident : $Enum:ident, $to:ident|$area:expr) => {
pub enum $Enum<E: Engine, T: Render<E>> { pub enum $Enum<E: Engine, T: Content<E>> {
X(E::Unit, T), Y(E::Unit, T), XY(E::Unit, E::Unit, T), X(E::Unit, T), Y(E::Unit, T), XY(E::Unit, E::Unit, T),
} }
impl<E: Engine, T: Render<E>> $Enum<E, T> { impl<E: Engine, T: Content<E>> $Enum<E, T> {
pub fn x (x: E::Unit, item: T) -> Self { Self::X(x, item) } pub fn x (x: E::Unit, item: T) -> Self { Self::X(x, item) }
pub fn y (y: E::Unit, item: T) -> Self { Self::Y(y, item) } pub fn y (y: E::Unit, item: T) -> Self { Self::Y(y, item) }
pub fn xy (x: E::Unit, y: E::Unit, item: T) -> Self { Self::XY(x, y, item) } pub fn xy (x: E::Unit, y: E::Unit, item: T) -> Self { Self::XY(x, y, item) }
@ -66,86 +80,65 @@ macro_rules! transform_xy_unit {
} }
} }
} }
impl<E: Engine, T: Render<E>> $Enum<E, T> { impl<E: Engine, T: Content<E>> Content<E> for $Enum<E, T> {
fn content (&self) -> Option<impl Render<E>> { fn content (&self) -> Option<impl Content<E>> {
Some(match self { Some(match self {
Self::X(_, content) => content, Self::X(_, content) => content,
Self::Y(_, content) => content, Self::Y(_, content) => content,
Self::XY(_, _, content) => content, Self::XY(_, _, content) => content,
}) })
} }
} fn area (&$self, $to: E::Area) -> E::Area {
impl<E: Engine, T: Render<E>> Render<E> for $Enum<E, T> { $area.into()
fn render (&$self, $to: &mut E::Output) -> Usually<()> {
$render
} }
} }
} }
} }
transform_xy!(|self: Fill, to|todo!()); transform_xy_unit!(|self: Fixed, to|match self {
Self::X(fw, _) => [to.x(), to.y(), *fw, to.h()],
transform_xy_unit!(|self: Fixed, to|{ Self::Y(fh, _) => [to.x(), to.y(), to.w(), *fh],
let [x, y, w, h] = to.area().xywh(); Self::XY(fw, fh, _) => [to.x(), to.y(), *fw, *fh], // tagn
to.render_in(match self {
Self::X(fw, _) => [x, y, fw, h],
Self::Y(fh, _) => [x, y, w, fh],
Self::XY(fw, fh, _) => [x, y, fw, fh],
}, self.content())
}); });
transform_xy_unit!(|self: Shrink, to|to.render_in( transform_xy_unit!(|self: Shrink, to|
[to.x(), to.y(), to.w().minus(self.dx()), to.h().minus(self.dy())], [to.x(), to.y(), to.w().minus(self.dx()), to.h().minus(self.dy())]);
self.content()));
transform_xy_unit!(|self: Expand, to|to.render_in( transform_xy_unit!(|self: Expand, to|
[to.x(), to.y(), to.w() + self.dx(), to.h() + self.dy()], [to.x(), to.y(), to.w() + self.dx(), to.h() + self.dy()]);
self.content()));
transform_xy_unit!(|self: Min, to|to.render_in(match self { transform_xy_unit!(|self: Min, to|match self {
Self::X(mw, _) => [to.x(), to.y(), to.w().max(mw), to.h()], Self::X(mw, _) => [to.x(), to.y(), to.w().max(*mw), to.h()],
Self::Y(mh, _) => [to.x(), to.y(), to.w(), to.h().max(mh)], Self::Y(mh, _) => [to.x(), to.y(), to.w(), to.h().max(*mh)],
Self::XY(mw, mh, _) => [to.x(), to.y(), to.w().max(mw), to.h().max(mh)] Self::XY(mw, mh, _) => [to.x(), to.y(), to.w().max(*mw), to.h().max(*mh)]
}, self.content())); });
transform_xy_unit!(|self: Max, to|to.render_in(match self { transform_xy_unit!(|self: Max, to|match self {
Self::X(mw, _) => [to.x(), to.y(), to.w().min(mw), to.h()], Self::X(mw, _) => [to.x(), to.y(), to.w().min(*mw), to.h()],
Self::Y(mh, _) => [to.x(), to.y(), to.w(), to.h().min(mh)], Self::Y(mh, _) => [to.x(), to.y(), to.w(), to.h().min(*mh)],
Self::XY(mw, mh, _) => [to.x(), to.y(), to.w().min(mw), to.h().min(mh)], Self::XY(mw, mh, _) => [to.x(), to.y(), to.w().min(*mw), to.h().min(*mh)],
}, self.content())); });
transform_xy_unit!(|self: Push, to|to.render_in( transform_xy_unit!(|self: Push, to|
[to.x() + self.dx(), to.y() + self.dy(), to.w(), to.h()], [to.x() + self.dx(), to.y() + self.dy(), to.w(), to.h()]);
self.content()));
transform_xy_unit!(|self: Pull, to|to.render_in( transform_xy_unit!(|self: Pull, to|
[to.x().minus(self.dx()), to.y().minus(self.dy()), to.w(), to.h()], [to.x().minus(self.dx()), to.y().minus(self.dy()), to.w(), to.h()]);
self.content()));
transform_xy_unit!(|self: Margin, to|{ transform_xy_unit!(|self: Margin, to|{
let dx = self.dx(); let dx = self.dx();
let dy = self.dy(); let dy = self.dy();
to.render_in([ [to.x().minus(dx), to.y().minus(dy), to.w() + dy + dy, to.h() + dy + dy]
to.x().minus(dx),
to.y().minus(dy),
to.w() + dy + dy,
to.h() + dy + dy,
])
}); });
transform_xy_unit!(|self: Padding, to|{ transform_xy_unit!(|self: Padding, to|{
let dx = self.dx(); let dx = self.dx();
let dy = self.dy(); let dy = self.dy();
to.render_in([ [to.x() + dx, to.y() + dy, to.w().minus(dy + dy), to.h().minus(dy + dy), ]
to.x() + dx,
to.y() + dy,
to.w().minus(dy + dy),
to.h().minus(dy + dy),
])
}); });
content_enum!(Align: Center, X, Y, NW, N, NE, E, SE, S, SW, W); content_enum!(Align: Center, X, Y, NW, N, NE, E, SE, S, SW, W);
impl<E: Engine, T: Render<E>> Align<E, T> { impl<E: Engine, T: Content<E>> Align<E, T> {
pub fn c (w: T) -> Self { Self::Center(w) } pub fn c (w: T) -> Self { Self::Center(w) }
pub fn x (w: T) -> Self { Self::X(w) } pub fn x (w: T) -> Self { Self::X(w) }
pub fn y (w: T) -> Self { Self::Y(w) } pub fn y (w: T) -> Self { Self::Y(w) }
@ -159,7 +152,7 @@ impl<E: Engine, T: Render<E>> Align<E, T> {
pub fn se (w: T) -> Self { Self::SE(w) } pub fn se (w: T) -> Self { Self::SE(w) }
} }
fn align<E: Engine, T: Render<E>, N: Coordinate, R: Area<N> + From<[N;4]>> (align: &Align<E, T>, outer: R, content: R) -> Option<R> { fn align<E: Engine, T: Content<E>, N: Coordinate, R: Area<N> + From<[N;4]>> (align: &Align<E, T>, outer: R, content: R) -> Option<R> {
if outer.w() < content.w() || outer.h() < content.h() { if outer.w() < content.w() || outer.h() < content.h() {
None None
} else { } else {
@ -182,17 +175,14 @@ fn align<E: Engine, T: Render<E>, N: Coordinate, R: Area<N> + From<[N;4]>> (alig
} }
} }
impl<E: Engine, T: Render<E>> Render<E> for Align<E, T> { impl<E: Engine, T: Content<E>> Content<E> for Align<E, T> {
fn min_size (&self, outer_area: E::Size) -> Perhaps<E::Size> { fn render (&self, to: &mut E::Output) {
self.content().min_size(outer_area)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
let outer_area = to.area(); let outer_area = to.area();
Ok(if let Some(content_size) = self.min_size(outer_area.wh().into())? { if let Some(content) = self.content() {
let content_area = outer_area.clip(content_size); let inner_area = content.area(outer_area);
if let Some(aligned) = align(&self, outer_area.into(), content_area.into()) { if let Some(aligned) = align(&self, outer_area.into(), inner_area.into()) {
to.render_in(aligned, &self.content())? to.place(aligned, &content)
} }
}) }
} }
} }