wip: extract tek_layout

This commit is contained in:
🪞👃🪞 2024-12-09 12:19:55 +01:00
parent ddb3c28c01
commit 2265d951d1
36 changed files with 2010 additions and 1780 deletions

View file

@ -32,21 +32,21 @@ impl<'a, E: Engine, const N: usize> From<ArrayCollection<'a, E, N>> for Collect<
}
type CallbackCollection<'a, E> =
&'a dyn Fn(&'a mut dyn FnMut(&dyn Render<Engine = E>)->Usually<()>);
&'a dyn Fn(&'a mut dyn FnMut(&dyn Render<E>)->Usually<()>);
//type IteratorCollection<'a, E> =
//&'a mut dyn Iterator<Item = dyn Render<Engine = E>>;
//&'a mut dyn Iterator<Item = dyn Render<E>>;
type SliceCollection<'a, E> =
&'a [&'a dyn Render<Engine = E>];
&'a [&'a dyn Render<E>];
type ArrayCollection<'a, E, const N: usize> =
[&'a dyn Render<Engine = E>; N];
[&'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<Engine = E>;
type Item = &'a dyn Render<E>;
fn next (&mut self) -> Option<Self::Item> {
match self.1 {
Collect::Callback(callback) => {

View file

@ -1,6 +1,10 @@
use crate::*;
/// Entry point for main loop
pub trait App<T: Engine> { fn run (self, context: T) -> Usually<T>; }
pub trait App<T: Engine> {
fn run (self, context: T) -> Usually<T>;
}
/// Platform backend.
pub trait Engine: Send + Sync + Sized {
/// Input event type
@ -22,213 +26,13 @@ pub trait Engine: Send + Sync + Sized {
/// Clean up after run
fn teardown (&mut self) -> Usually<()> { Ok(()) }
}
/// Current input state
pub trait Input<E: Engine> {
/// Type of input event
type Event;
/// Currently handled event
fn event (&self) -> &Self::Event;
/// Whether component should exit
fn is_done (&self) -> bool;
/// Mark component as done
fn done (&self);
}
/// Rendering target
pub trait Output<E: Engine> {
/// Current output area
fn area (&self) -> E::Area;
/// Mutable pointer to area
fn area_mut (&mut self) -> &mut E::Area;
/// Render widget in area
fn render_in (&mut self, area: E::Area, widget: &dyn Render<Engine = E>) -> Usually<()>;
}
/// Cast to dynamic pointer
pub fn widget <E: Engine, T: Render<Engine = E>> (w: &T) -> &dyn Render<Engine = E> {
w as &dyn Render<Engine = E>
}
/// A renderable component
pub trait Render: Send + Sync {
/// Engine for which this component is implemented
type Engine: Engine;
/// Minimum size to use
fn min_size (&self, to: <Self::Engine as Engine>::Size)
-> Perhaps<<Self::Engine as Engine>::Size>
{
Ok(Some(to))
}
/// Draw to output render target
fn render (&self,to: &mut <Self::Engine as Engine>::Output) -> Usually<()>;
}
impl<E: Engine> Render for &dyn Render<Engine = E> {
type Engine = E;
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
(*self).min_size(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
(*self).render(to)
}
}
impl<E: Engine> Render for &mut dyn Render<Engine = E> {
type Engine = E;
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
(**self).min_size(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
(**self).render(to)
}
}
impl<'a, E: Engine> Render for Box<dyn Render<Engine = E> + 'a> {
type Engine = E;
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
(**self).min_size(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
(**self).render(to)
}
}
impl<E: Engine, W: Render<Engine = E>> Render for Arc<W> {
type Engine = E;
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
self.as_ref().min_size(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
self.as_ref().render(to)
}
}
impl<E: Engine, W: Render<Engine = E>> Render for Mutex<W> {
type Engine = E;
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
self.lock().unwrap().min_size(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
self.lock().unwrap().render(to)
}
}
impl<E: Engine, W: Render<Engine = E>> Render for RwLock<W> {
type Engine = E;
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
self.read().unwrap().min_size(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
self.read().unwrap().render(to)
}
}
impl<E: Engine, W: Render<Engine = E>> Render for Option<W> {
type Engine = E;
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
Ok(self.as_ref().map(|widget|widget.min_size(to)).transpose()?.flatten())
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
self.as_ref().map(|widget|widget.render(to)).unwrap_or(Ok(()))
}
}
/// Render either of two widgets depending on predicate
pub struct Either<E: Engine, A: Render<Engine = E>, B: Render<Engine = E>>(
pub bool,
pub A,
pub B,
);
impl<E: Engine, A: Render<Engine = E>, B: Render<Engine = E>> Render for Either<E, A, B> {
type Engine = E;
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
if self.0 { self.1.min_size(to) } else { self.2.min_size(to) }
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
if self.0 { self.1.render(to) } else { self.2.render(to) }
}
}
/// A custom [Render] defined by passing layout and render closures in place.
pub struct Widget<
E: Engine,
L: Send + Sync + Fn(E::Size)->Perhaps<E::Size>,
R: Send + Sync + Fn(&mut E::Output)->Usually<()>
>(L, R, PhantomData<E>);
impl<
E: Engine,
L: Send + Sync + Fn(E::Size)->Perhaps<E::Size>,
R: Send + Sync + Fn(&mut E::Output)->Usually<()>
> Widget<E, L, R> {
pub fn new (layout: L, render: R) -> Self {
Self(layout, render, Default::default())
}
}
impl<
E: Engine,
L: Send + Sync + Fn(E::Size)->Perhaps<E::Size>,
R: Send + Sync + Fn(&mut E::Output)->Usually<()>
> Render for Widget<E, L, R> {
type Engine = E;
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
self.0(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
self.1(to)
}
}
/// A [Render] that contains other [Render]s
pub trait Content: Send + Sync {
type Engine: Engine;
fn content (&self) -> impl Render<Engine = <Self as Content>::Engine>;
}
/// Every struct that has [Content] is a renderable [Render].
impl<E: Engine, W: Content<Engine = E>> Render for W {
type Engine = E;
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
self.content().min_size(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
match self.min_size(to.area().wh().into())? {
Some(wh) => to.render_in(to.area().clip(wh).into(), &self.content()),
None => Ok(())
}
}
}
/// Handle input
pub trait Handle<E: Engine>: Send + Sync {
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled>;
}
impl<E: Engine, H: Handle<E>> Handle<E> for &mut H {
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
(*self).handle(context)
}
}
impl<E: Engine, H: Handle<E>> Handle<E> for Option<H> {
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
if let Some(ref mut handle) = self {
handle.handle(context)
} else {
Ok(None)
}
}
}
impl<H, E: Engine> Handle<E> for Mutex<H> where H: Handle<E> {
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
self.lock().unwrap().handle(context)
}
}
impl<H, E: Engine> Handle<E> for Arc<Mutex<H>> where H: Handle<E> {
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
self.lock().unwrap().handle(context)
}
}
impl<H, E: Engine> Handle<E> for RwLock<H> where H: Handle<E> {
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
self.write().unwrap().handle(context)
}
}
impl<H, E: Engine> Handle<E> for Arc<RwLock<H>> where H: Handle<E> {
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
self.write().unwrap().handle(context)
}
}
/// A UI component that can render itself as a [Render], and [Handle] input.
pub trait Component<E: Engine>: Render<Engine = E> + Handle<E> {}
pub trait Component<E: Engine>: Render<E> + Handle<E> {}
/// Everything that implements [Render] and [Handle] is a [Component].
impl<E: Engine, C: Render<Engine = E> + Handle<E>> Component<E> for C {}
/// A UI component that has [Content] and can [Handle] input.
pub trait ContentComponent<E: Engine>: Render<Engine = E> + Handle<E> {}
/// Everything that implements [Content] and [Handle] is a [Component].
impl<E: Engine, C: Content<Engine = E> + Handle<E>> ContentComponent<E> for C {}
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;
@ -237,6 +41,7 @@ pub trait Exit: Send {
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.
@ -244,5 +49,6 @@ pub trait ExitableComponent<E>: Exit + Component<E> where E: Engine {
Box::new(self)
}
}
/// All [Components]s that implement [Exit] implement [ExitableComponent].
impl<E: Engine, C: Component<E> + Exit> ExitableComponent<E> for C {}

View file

@ -0,0 +1,58 @@
use crate::*;
/// Current input state
pub trait Input<E: Engine> {
/// Type of input event
type Event;
/// Currently handled event
fn event (&self) -> &Self::Event;
/// Whether component should exit
fn is_done (&self) -> bool;
/// Mark component as done
fn done (&self);
}
/// Handle input
pub trait Handle<E: Engine>: Send + Sync {
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled>;
}
impl<E: Engine, H: Handle<E>> Handle<E> for &mut H {
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
(*self).handle(context)
}
}
impl<E: Engine, H: Handle<E>> Handle<E> for Option<H> {
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
if let Some(ref mut handle) = self {
handle.handle(context)
} else {
Ok(None)
}
}
}
impl<H, E: Engine> Handle<E> for Mutex<H> where H: Handle<E> {
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
self.lock().unwrap().handle(context)
}
}
impl<H, E: Engine> Handle<E> for Arc<Mutex<H>> where H: Handle<E> {
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
self.lock().unwrap().handle(context)
}
}
impl<H, E: Engine> Handle<E> for RwLock<H> where H: Handle<E> {
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
self.write().unwrap().handle(context)
}
}
impl<H, E: Engine> Handle<E> for Arc<RwLock<H>> where H: Handle<E> {
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
self.write().unwrap().handle(context)
}
}

View file

@ -32,17 +32,19 @@ use std::fmt::{Debug, Display};
}
submod! {
//tui
audio
color
collect
command
edn
engine
focus
input
output
pitch
space
time
//tui
layout
}
testmod! {

View file

@ -0,0 +1,137 @@
use crate::*;
/// Rendering target
pub trait Output<E: Engine> {
/// Current output area
fn area (&self) -> E::Area;
/// Mutable pointer to area
fn area_mut (&mut self) -> &mut E::Area;
/// Render widget in area
fn render_in (&mut self, area: E::Area, widget: &dyn Render<E>) -> Usually<()>;
}
/// Cast to dynamic pointer
pub fn widget <E: Engine, T: Render<E>> (w: &T) -> &dyn Render<E> {
w as &dyn Render<E>
}
/// A renderable component
pub trait Render<E: Engine>: Send + Sync {
/// Minimum size to use
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
Ok(Some(to))
}
/// Draw to output render target
fn render (&self, to: &mut E::Output) -> Usually<()>;
}
impl<E: Engine> Render<E> for &dyn Render<E> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
(*self).min_size(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
(*self).render(to)
}
}
impl<E: Engine> Render<E> for &mut dyn Render<E> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
(**self).min_size(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
(**self).render(to)
}
}
impl<'a, E: Engine> Render<E> for Box<dyn Render<E> + 'a> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
(**self).min_size(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
(**self).render(to)
}
}
impl<E: Engine, W: Render<E>> Render<E> for Arc<W> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
self.as_ref().min_size(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
self.as_ref().render(to)
}
}
impl<E: Engine, W: Render<E>> Render<E> for Mutex<W> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
self.lock().unwrap().min_size(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
self.lock().unwrap().render(to)
}
}
impl<E: Engine, W: Render<E>> Render<E> for RwLock<W> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
self.read().unwrap().min_size(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
self.read().unwrap().render(to)
}
}
impl<E: Engine, W: Render<E>> Render<E> for Option<W> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
Ok(self.as_ref().map(|widget|widget.min_size(to)).transpose()?.flatten())
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
self.as_ref().map(|widget|widget.render(to)).unwrap_or(Ok(()))
}
}
/// A [Render] that contains other [Render]s
pub trait Content<E: Engine>: Send + Sync {
fn content (&self) -> impl Render<E>;
}
/// Every struct that has [Content] is a renderable [Render].
impl<E: Engine, W: Content<E>> Render<E> for &W {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
self.content().min_size(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
match self.min_size(to.area().wh().into())? {
Some(wh) => to.render_in(to.area().clip(wh).into(), &self.content()),
None => Ok(())
}
}
}
/// A custom [Render] defined by passing layout and render closures in place.
pub struct Widget<
E: Engine,
L: Send + Sync + Fn(E::Size)->Perhaps<E::Size>,
R: Send + Sync + Fn(&mut E::Output)->Usually<()>
>(L, R, PhantomData<E>);
impl<
E: Engine,
L: Send + Sync + Fn(E::Size)->Perhaps<E::Size>,
R: Send + Sync + Fn(&mut E::Output)->Usually<()>
> Widget<E, L, R> {
pub fn new (layout: L, render: R) -> Self {
Self(layout, render, Default::default())
}
}
impl<
E: Engine,
L: Send + Sync + Fn(E::Size)->Perhaps<E::Size>,
R: Send + Sync + Fn(&mut E::Output)->Usually<()>
> Render<E> for Widget<E, L, R> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
self.0(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
self.1(to)
}
}

View file

@ -129,133 +129,6 @@ impl<N: Coordinate> Area<N> for [N;4] {
#[inline] fn h (&self) -> N { self[3] }
}
pub trait Layout<E: Engine>: Render<Engine = E> + Sized {
fn align_center (self) -> Align<Self> { Align::Center(self) }
fn align_n (self) -> Align<Self> { Align::N(self) }
fn align_s (self) -> Align<Self> { Align::S(self) }
fn align_e (self) -> Align<Self> { Align::E(self) }
fn align_w (self) -> Align<Self> { Align::W(self) }
fn align_nw (self) -> Align<Self> { Align::NW(self) }
fn align_sw (self) -> Align<Self> { Align::SW(self) }
fn align_ne (self) -> Align<Self> { Align::NE(self) }
fn align_se (self) -> Align<Self> { Align::SE(self) }
fn align_x (self) -> Align<Self> { Align::X(self) }
fn align_y (self) -> Align<Self> { Align::Y(self) }
fn fixed_x (self, x: E::Unit) -> Fixed<E::Unit, Self> { Fixed::X(x, self) }
fn fixed_y (self, y: E::Unit) -> Fixed<E::Unit, Self> { Fixed::Y(y, self) }
fn fixed_xy (self, x: E::Unit, y: E::Unit) -> Fixed<E::Unit, Self> { Fixed::XY(x, y, self) }
fn min_x (self, x: E::Unit) -> Min<E::Unit, Self> { Min::X(x, self) }
fn min_y (self, y: E::Unit) -> Min<E::Unit, Self> { Min::Y(y, self) }
fn min_xy (self, x: E::Unit, y: E::Unit) -> Min<E::Unit, Self> { Min::XY(x, y, self) }
fn max_x (self, x: E::Unit) -> Max<E::Unit, Self> { Max::X(x, self) }
fn max_y (self, y: E::Unit) -> Max<E::Unit, Self> { Max::Y(y, self) }
fn max_xy (self, x: E::Unit, y: E::Unit) -> Max<E::Unit, Self> { Max::XY(x, y, self) }
fn push_x (self, x: E::Unit) -> Push<E::Unit, Self> { Push::X(x, self) }
fn push_y (self, y: E::Unit) -> Push<E::Unit, Self> { Push::Y(y, self) }
fn push_xy (self, x: E::Unit, y: E::Unit) -> Push<E::Unit, Self> { Push::XY(x, y, self) }
fn pull_x (self, x: E::Unit) -> Pull<E::Unit, Self> { Pull::X(x, self) }
fn pull_y (self, y: E::Unit) -> Pull<E::Unit, Self> { Pull::Y(y, self) }
fn pull_xy (self, x: E::Unit, y: E::Unit) -> Pull<E::Unit, Self> { Pull::XY(x, y, self) }
fn grow_x (self, x: E::Unit) -> Grow<E::Unit, Self> { Grow::X(x, self) }
fn grow_y (self, y: E::Unit) -> Grow<E::Unit, Self> { Grow::Y(y, self) }
fn grow_xy (self, x: E::Unit, y: E::Unit) -> Grow<E::Unit, Self> { Grow::XY(x, y, self) }
fn shrink_x (self, x: E::Unit) -> Shrink<E::Unit, Self> { Shrink::X(x, self) }
fn shrink_y (self, y: E::Unit) -> Shrink<E::Unit, Self> { Shrink::Y(y, self) }
fn shrink_xy (self, x: E::Unit, y: E::Unit) -> Shrink<E::Unit, Self> { Shrink::XY(x, y, self) }
fn inset_x (self, x: E::Unit) -> Inset<E::Unit, Self> { Inset::X(x, self) }
fn inset_y (self, y: E::Unit) -> Inset<E::Unit, Self> { Inset::Y(y, self) }
fn inset_xy (self, x: E::Unit, y: E::Unit) -> Inset<E::Unit, Self> { Inset::XY(x, y, self) }
fn outset_x (self, x: E::Unit) -> Outset<E::Unit, Self> { Outset::X(x, self) }
fn outset_y (self, y: E::Unit) -> Outset<E::Unit, Self> { Outset::Y(y, self) }
fn outset_xy (self, x: E::Unit, y: E::Unit) -> Outset<E::Unit, Self> { Outset::XY(x, y, self) }
fn fill_x (self) -> Fill<E, Self> { Fill::X(self) }
fn fill_y (self) -> Fill<E, Self> { Fill::Y(self) }
fn fill_xy (self) -> Fill<E, Self> { Fill::XY(self) }
fn debug (self) -> DebugOverlay<E, Self> { DebugOverlay(self) }
fn split <W: Render<Engine = E>> (
self, direction: Direction, amount: E::Unit, other: W
) -> Split<E, Self, W> { Split::new(direction, amount, self, other) }
fn split_flip <W: Render<Engine = E>> (
self, direction: Direction, amount: E::Unit, other: W
) -> Split<E, W, Self> { Split::new(direction, amount, other, self) }
}
impl<E: Engine, W: Render<Engine = E>> Layout<E> for W {}
pub struct DebugOverlay<E: Engine, W: Render<Engine = E>>(pub W);
pub enum Fill<E: Engine, W: Render<Engine = E>> { X(W), Y(W), XY(W) }
impl<E: Engine, W: Render<Engine = E>> Fill<E, W> {
fn inner (&self) -> &W {
match self {
Self::X(inner) => &inner,
Self::Y(inner) => &inner,
Self::XY(inner) => &inner,
}
}
}
impl<E: Engine, W: Render<Engine = E>> Render for Fill<E, W> {
type Engine = E;
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
let area = self.inner().min_size(to.into())?;
if let Some(area) = area {
Ok(Some(match self {
Self::X(_) => [to.w().into(), area.h()],
Self::Y(_) => [area.w(), to.h().into()],
Self::XY(_) => [to.w().into(), to.h().into()],
}.into()))
} else {
Ok(None)
}
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
self.inner().render(to)
}
}
pub struct Layers<
E: Engine,
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<Engine = E>)->Usually<()>)->Usually<()>
>(pub F, PhantomData<E>);
impl<
E: Engine,
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<Engine = E>)->Usually<()>)->Usually<()>
> Layers<E, F> {
#[inline]
pub fn new (build: F) -> Self {
Self(build, Default::default())
}
}
impl<E: Engine, F> Render for Layers<E, F>
where
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<Engine = E>)->Usually<()>)->Usually<()>
{
type Engine = E;
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(())
}
}
}
#[derive(Copy, Clone, PartialEq)]
pub enum Direction { Up, Down, Left, Right, }
impl Direction {
@ -282,676 +155,3 @@ impl Direction {
}
}
}
/// Override X and Y coordinates, aligning to corner, side, or center of area
pub enum Align<L> {
/// Draw at center of container
Center(L),
/// Draw at center of X axis
X(L),
/// Draw at center of Y axis
Y(L),
/// Draw at upper left corner of contaier
NW(L),
/// Draw at center of upper edge of container
N(L),
/// Draw at right left corner of contaier
NE(L),
/// Draw at center of left edge of container
W(L),
/// Draw at center of right edge of container
E(L),
/// Draw at lower left corner of container
SW(L),
/// Draw at center of lower edge of container
S(L),
/// Draw at lower right edge of container
SE(L)
}
impl<T> Align<T> {
pub fn inner (&self) -> &T {
match self {
Self::Center(inner) => inner,
Self::X(inner) => inner,
Self::Y(inner) => inner,
Self::NW(inner) => inner,
Self::N(inner) => inner,
Self::NE(inner) => inner,
Self::W(inner) => inner,
Self::E(inner) => inner,
Self::SW(inner) => inner,
Self::S(inner) => inner,
Self::SE(inner) => inner,
}
}
}
fn align<T, N: Coordinate, R: Area<N> + From<[N;4]>> (align: &Align<T>, outer: R, inner: R) -> Option<R> {
if outer.w() < inner.w() || outer.h() < inner.h() {
None
} else {
let [ox, oy, ow, oh] = outer.xywh();
let [ix, iy, iw, ih] = inner.xywh();
Some(match align {
Align::Center(_) => [ox + (ow - iw) / 2.into(), oy + (oh - ih) / 2.into(), iw, ih,].into(),
Align::X(_) => [ox + (ow - iw) / 2.into(), iy, iw, ih,].into(),
Align::Y(_) => [ix, oy + (oh - ih) / 2.into(), iw, ih,].into(),
Align::NW(_) => [ox, oy, iw, ih,].into(),
Align::N(_) => [ox + (ow - iw) / 2.into(), oy, iw, ih,].into(),
Align::NE(_) => [ox + ow - iw, oy, iw, ih,].into(),
Align::W(_) => [ox, oy + (oh - ih) / 2.into(), iw, ih,].into(),
Align::E(_) => [ox + ow - iw, oy + (oh - ih) / 2.into(), iw, ih,].into(),
Align::SW(_) => [ox, oy + oh - ih, iw, ih,].into(),
Align::S(_) => [ox + (ow - iw) / 2.into(), oy + oh - ih, iw, ih,].into(),
Align::SE(_) => [ox + ow - iw, oy + oh - ih, iw, ih,].into(),
})
}
}
impl<E: Engine, T: Render<Engine = E>> Render for Align<T> {
type Engine = E;
fn min_size (&self, outer_area: E::Size) -> Perhaps<E::Size> {
self.inner().min_size(outer_area)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
let outer_area = to.area();
Ok(if let Some(inner_size) = self.min_size(outer_area.wh().into())? {
let inner_area = outer_area.clip(inner_size);
if let Some(aligned) = align(&self, outer_area.into(), inner_area.into()) {
to.render_in(aligned, self.inner())?
} else {
()
}
} else {
()
})
}
}
/// Enforce fixed size of drawing area
pub enum Fixed<U: Coordinate, T> {
/// Enforce fixed width
X(U, T),
/// Enforce fixed height
Y(U, T),
/// Enforce fixed width and height
XY(U, U, T),
}
impl<N: Coordinate, T> Fixed<N, T> {
pub fn inner (&self) -> &T {
match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, }
}
}
impl<E: Engine, T: Render<Engine = E>> Render for Fixed<E::Unit, T> {
type Engine = E;
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
Ok(match self {
Self::X(w, _) =>
if to.w() >= *w { Some([*w, to.h()].into()) } else { None },
Self::Y(h, _) =>
if to.h() >= *h { Some([to.w(), *h].into()) } else { None },
Self::XY(w, h, _)
=> if to.w() >= *w && to.h() >= *h { Some([*w, *h].into()) } else { None },
})
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
// 🡘 🡙 ←🡙→
if let Some(size) = self.min_size(to.area().wh().into())? {
to.render_in(to.area().clip(size).into(), self.inner())
} else {
Ok(())
}
}
}
/// Enforce minimum size of drawing area
pub enum Min<U: Coordinate, T> {
/// Enforce minimum width
X(U, T),
/// Enforce minimum height
Y(U, T),
/// Enforce minimum width and height
XY(U, U, T),
}
impl<N: Coordinate, T> Min<N, T> {
pub fn inner (&self) -> &T {
match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, }
}
}
impl<E: Engine, T: Render<Engine = E>> Render for Min<E::Unit, T> {
type Engine = E;
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
Ok(self.inner().min_size(to)?.map(|to|match *self {
Self::X(w, _) => [to.w().max(w), to.h()],
Self::Y(h, _) => [to.w(), to.h().max(h)],
Self::XY(w, h, _) => [to.w().max(w), to.h().max(h)],
}.into()))
}
// TODO: 🡘 🡙 ←🡙→
fn render (&self, to: &mut E::Output) -> Usually<()> {
Ok(self.min_size(to.area().wh().into())?
.map(|size|to.render_in(to.area().clip(size).into(), self.inner()))
.transpose()?.unwrap_or(()))
}
}
/// Enforce maximum size of drawing area
pub enum Max<U: Coordinate, T> {
/// Enforce maximum width
X(U, T),
/// Enforce maximum height
Y(U, T),
/// Enforce maximum width and height
XY(U, U, T),
}
impl<N: Coordinate, T> Max<N, T> {
fn inner (&self) -> &T {
match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, }
}
}
impl<E: Engine, T: Render<Engine = E>> Render for Max<E:: Unit, T> {
type Engine = E;
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
Ok(self.inner().min_size(to)?.map(|to|match *self {
Self::X(w, _) => [to.w().min(w), to.h()],
Self::Y(h, _) => [to.w(), to.h().min(h)],
Self::XY(w, h, _) => [to.w().min(w), to.h().min(h)],
}.into()))
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
Ok(self.min_size(to.area().wh().into())?
.map(|size|to.render_in(to.area().clip(size).into(), self.inner()))
.transpose()?.unwrap_or(()))
}
}
/// Expand drawing area
pub enum Grow<N: Coordinate, T> {
/// Increase width
X(N, T),
/// Increase height
Y(N, T),
/// Increase width and height
XY(N, N, T)
}
impl<N: Coordinate, T> Grow<N, T> {
fn inner (&self) -> &T {
match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, }
}
}
impl<E: Engine, T: Render<Engine = E>> Render for Grow<E::Unit, T> {
type Engine = E;
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
Ok(self.inner().min_size(to)?.map(|to|match *self {
Self::X(w, _) => [to.w() + w, to.h()],
Self::Y(h, _) => [to.w(), to.h() + h],
Self::XY(w, h, _) => [to.w() + w, to.h() + h],
}.into()))
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
Ok(self.min_size(to.area().wh().into())?
.map(|size|to.render_in(to.area().clip(size).into(), self.inner()))
.transpose()?.unwrap_or(()))
}
}
/// Shrink drawing area
pub enum Shrink<N: Coordinate, T> {
/// Decrease width
X(N, T),
/// Decrease height
Y(N, T),
/// Decrease width and height
XY(N, N, T),
}
impl<N: Coordinate, T: Render> Shrink<N, T> {
fn inner (&self) -> &T {
match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, }
}
}
impl<E: Engine, T: Render<Engine = E>> Render for Shrink<E::Unit, T> {
type Engine = E;
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
Ok(self.inner().min_size(to)?.map(|to|match *self {
Self::X(w, _) => [
if to.w() > w { to.w() - w } else { 0.into() },
to.h()
],
Self::Y(h, _) => [
to.w(),
if to.h() > h { to.h() - h } else { 0.into() }
],
Self::XY(w, h, _) => [
if to.w() > w { to.w() - w } else { 0.into() },
if to.h() > h { to.h() - h } else { 0.into() }
]
}.into()))
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
Ok(self.min_size(to.area().wh().into())?
.map(|size|to.render_in(to.area().clip(size).into(), self.inner()))
.transpose()?.unwrap_or(()))
}
}
/// Shrink from each side
pub enum Inset<N: Coordinate, T> {
/// Decrease width
X(N, T),
/// Decrease height
Y(N, T),
/// Decrease width and height
XY(N, N, T),
}
impl<N: Coordinate, T: Render> Inset<N, T> {
pub fn inner (&self) -> &T {
match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, }
}
}
/// Grow on each side
pub enum Outset<N: Coordinate, T> {
/// Increase width
X(N, T),
/// Increase height
Y(N, T),
/// Increase width and height
XY(N, N, T),
}
impl<N: Coordinate, T: Render> Outset<N, T> {
pub fn inner (&self) -> &T {
match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, }
}
}
impl<E: Engine, T: Render<Engine = E>> Render for Inset<E::Unit, T> {
type Engine = E;
fn render (&self, to: &mut E::Output) -> Usually<()> {
match *self {
Self::X(x, ref inner) =>
(inner as &dyn Render<Engine = E>).shrink_x(x).push_x(x),
Self::Y(y, ref inner) =>
(inner as &dyn Render<Engine = E>).shrink_y(y).push_y(y),
Self::XY(x, y, ref inner) =>
(inner as &dyn Render<Engine = E>).shrink_xy(x, y).push_xy(x, y)
}.render(to)
}
}
impl<E: Engine, T: Render<Engine = E>> Render for Outset<E::Unit, T> {
type Engine = E;
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
match *self {
Self::X(x, ref inner) =>
(inner as &dyn Render<Engine = E>).grow_x(x + x),
Self::Y(y, ref inner) =>
(inner as &dyn Render<Engine = E>).grow_y(y + y),
Self::XY(x, y, ref inner) =>
(inner as &dyn Render<Engine = E>).grow_xy(x + x, y + y),
}.min_size(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
match *self {
Self::X(x, ref inner) =>
(inner as &dyn Render<Engine = E>).push_x(x),
Self::Y(y, ref inner) =>
(inner as &dyn Render<Engine = E>).push_y(y),
Self::XY(x, y, ref inner) =>
(inner as &dyn Render<Engine = E>).push_xy(x, y),
}.render(to)
}
}
/// Move origin point of drawing area
pub enum Push<N: Coordinate, T: Render> {
/// Move origin to the right
X(N, T),
/// Move origin downwards
Y(N, T),
/// Move origin to the right and downwards
XY(N, N, T),
}
impl<N: Coordinate, T: Render> Push<N, T> {
pub fn inner (&self) -> &T {
match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, }
}
pub fn x (&self) -> N {
match self { Self::X(x, _) => *x, Self::Y(_, _) => N::default(), Self::XY(x, _, _) => *x }
}
pub fn y (&self) -> N {
match self { Self::X(_, _) => N::default(), Self::Y(y, _) => *y, Self::XY(_, y, _) => *y }
}
}
impl<E: Engine, T: Render<Engine = E>> Render for Push<E::Unit, T> {
type Engine = E;
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
self.inner().min_size(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
let area = to.area();
Ok(self.min_size(area.wh().into())?
.map(|size|to.render_in(match *self {
Self::X(x, _) => [area.x() + x, area.y(), size.w(), size.h()],
Self::Y(y, _) => [area.x(), area.y() + y, size.w(), size.h()],
Self::XY(x, y, _) => [area.x() + x, area.y() + y, size.w(), size.h()],
}.into(), self.inner())).transpose()?.unwrap_or(()))
}
}
/// Move origin point of drawing area
pub enum Pull<N: Coordinate, T: Render> {
/// Move origin to the right
X(N, T),
/// Move origin downwards
Y(N, T),
/// Move origin to the right and downwards
XY(N, N, T),
}
impl<N: Coordinate, T: Render> Pull<N, T> {
pub fn inner (&self) -> &T {
match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, }
}
pub fn x (&self) -> N {
match self { Self::X(x, _) => *x, Self::Y(_, _) => N::default(), Self::XY(x, _, _) => *x }
}
pub fn y (&self) -> N {
match self { Self::X(_, _) => N::default(), Self::Y(y, _) => *y, Self::XY(_, y, _) => *y }
}
}
impl<E: Engine, T: Render<Engine = E>> Render for Pull<E::Unit, T> {
type Engine = E;
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
self.inner().min_size(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
let area = to.area();
Ok(self.min_size(area.wh().into())?
.map(|size|to.render_in(match *self {
Self::X(x, _) => [area.x().minus(x), area.y(), size.w(), size.h()],
Self::Y(y, _) => [area.x(), area.y().minus(y), size.w(), size.h()],
Self::XY(x, y, _) => [area.x().minus(x), area.y().minus(y), size.w(), size.h()],
}.into(), self.inner())).transpose()?.unwrap_or(()))
}
}
pub struct Stack<
E: Engine,
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<Engine = E>)->Usually<()>)->Usually<()>
>(pub F, pub Direction, PhantomData<E>);
impl<
E: Engine,
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<Engine = E>)->Usually<()>)->Usually<()>
> Stack<E, F> {
#[inline] pub fn new (direction: Direction, build: F) -> Self {
Self(build, direction, Default::default())
}
#[inline] pub fn right (build: F) -> Self {
Self::new(Direction::Right, build)
}
#[inline] pub fn down (build: F) -> Self {
Self::new(Direction::Down, build)
}
#[inline] pub fn up (build: F) -> Self {
Self::new(Direction::Up, build)
}
}
impl<E: Engine, F> Render for Stack<E, F>
where
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<Engine = E>)->Usually<()>)->Usually<()>
{
type Engine = E;
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
match self.1 {
Direction::Down => {
let mut w: E::Unit = 0.into();
let mut h: E::Unit = 0.into();
(self.0)(&mut |component: &dyn Render<Engine = E>| {
let max = to.h().minus(h);
if max > E::Unit::ZERO() {
let item = component.push_y(h).max_y(max);
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()))
},
Direction::Right => {
let mut w: E::Unit = 0.into();
let mut h: E::Unit = 0.into();
(self.0)(&mut |component: &dyn Render<Engine = E>| {
let max = to.w().minus(w);
if max > E::Unit::ZERO() {
let item = component.push_x(w).max_x(max);
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()))
},
Direction::Up => {
let mut w: E::Unit = 0.into();
let mut h: E::Unit = 0.into();
(self.0)(&mut |component: &dyn Render<Engine = E>| {
let max = to.h().minus(h);
if max > E::Unit::ZERO() {
let item = component.max_y(to.h() - h);
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()))
},
Direction::Left => {
let mut w: E::Unit = 0.into();
let mut h: E::Unit = 0.into();
(self.0)(&mut |component: &dyn Render<Engine = 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 {
Direction::Down => {
(self.0)(&mut |item| {
if h < area.h() {
let item = item.push_y(h).max_y(area.h() - h);
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(())
})?;
},
Direction::Right => {
(self.0)(&mut |item| {
if w < area.w() {
let item = item.push_x(w).max_x(area.w() - w);
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(())
})?;
},
Direction::Up => {
(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 {
item.push_y(area.h() - height).shrink_y(height).render(to)?;
h = h + height;
if width > w { w = width }
};
}
Ok(())
})?;
},
_ => todo!()
};
Ok(())
}
}
#[macro_export] macro_rules! lay {
($($expr:expr),* $(,)?) => { Layers::new(move|add|{ $(add(&$expr)?;)* Ok(()) }) }
}
#[macro_export] macro_rules! col {
($($expr:expr),* $(,)?) => { Stack::down(move|add|{ $(add(&$expr)?;)* Ok(()) }) };
($pat:pat in $collection:expr => $item:expr) => {
Stack::down(move |add|{
for $pat in $collection { add(&$item)?; }
Ok(())
})
}
}
#[macro_export] macro_rules! col_up {
($($expr:expr),* $(,)?) => { Stack::down(move|add|{ $(add(&$expr)?;)* Ok(()) }) };
($pat:pat in $collection:expr => $item:expr) => {
Stack::up(move |add|{
for $pat in $collection { add(&$item)?; }
Ok(())
})
}
}
#[macro_export] macro_rules! row {
($($expr:expr),* $(,)?) => { Stack::right(move|add|{ $(add(&$expr)?;)* Ok(()) }) };
($pat:pat in $collection:expr => $item:expr) => {
Stack::right(move |add|{
for $pat in $collection { add(&$item)?; }
Ok(())
})
}
}
/// A binary split with fixed proportion
pub struct Split<E: Engine, A: Render<Engine = E>, B: Render<Engine = E>>(
pub Direction, pub E::Unit, A, B, PhantomData<E>
);
impl<E: Engine, A: Render<Engine = E>, B: Render<Engine = E>> Split<E, A, B> {
pub fn new (direction: Direction, proportion: E::Unit, a: A, b: B) -> Self {
Self(direction, proportion, a, b, Default::default())
}
pub fn up (proportion: E::Unit, a: A, b: B) -> Self {
Self(Direction::Up, proportion, a, b, Default::default())
}
pub fn down (proportion: E::Unit, a: A, b: B) -> Self {
Self(Direction::Down, proportion, a, b, Default::default())
}
pub fn left (proportion: E::Unit, a: A, b: B) -> Self {
Self(Direction::Left, proportion, a, b, Default::default())
}
pub fn right (proportion: E::Unit, a: A, b: B) -> Self {
Self(Direction::Right, proportion, a, b, Default::default())
}
}
impl<E: Engine, A: Render<Engine = E>, B: Render<Engine = E>> Render for Split<E, A, B> {
type Engine = E;
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
Ok(Some(to))
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
let (a, b) = to.area().split_fixed(self.0, self.1);
to.render_in(a.into(), &self.2)?;
to.render_in(b.into(), &self.3)?;
Ok(())
}
}
/// A widget that tracks its render width and height
pub struct Measure<E: Engine>(PhantomData<E>, AtomicUsize, AtomicUsize);
impl<E: Engine> Clone for Measure<E> {
fn clone (&self) -> Self {
Self(
Default::default(),
AtomicUsize::from(self.1.load(Ordering::Relaxed)),
AtomicUsize::from(self.2.load(Ordering::Relaxed)),
)
}
}
impl<E: Engine> std::fmt::Debug for Measure<E> {
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
f.debug_struct("Measure")
.field("width", &self.0)
.field("height", &self.1)
.finish()
}
}
impl<E: Engine> Measure<E> {
pub fn w (&self) -> usize { self.1.load(Ordering::Relaxed) }
pub fn h (&self) -> usize { self.2.load(Ordering::Relaxed) }
pub fn wh (&self) -> [usize;2] { [self.w(), self.h()] }
pub fn set_w (&self, w: impl Into<usize>) { self.1.store(w.into(), Ordering::Relaxed) }
pub fn set_h (&self, h: impl Into<usize>) { self.2.store(h.into(), Ordering::Relaxed) }
pub fn set_wh (&self, w: impl Into<usize>, h: impl Into<usize>) { self.set_w(w); self.set_h(h); }
pub fn new () -> Self { Self(PhantomData::default(), 0.into(), 0.into()) }
pub fn format (&self) -> String { format!("{}x{}", self.w(), self.h()) }
}
impl<E: Engine> Render for Measure<E> {
type Engine = E;
fn min_size (&self, _: E::Size) -> Perhaps<E::Size> {
Ok(Some([0u16.into(), 0u16.into()].into()))
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
self.set_w(to.area().w());
self.set_h(to.area().h());
Ok(())
}
}
/// A scrollable area.
pub struct Scroll<
E: Engine,
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<Engine = E>)->Usually<()>)->Usually<()>
>(pub F, pub Direction, pub u64, PhantomData<E>);