move containers to space 🪐

This commit is contained in:
🪞👃🪞 2024-09-15 19:47:12 +03:00
parent 2073bb541d
commit 35b37e3e3a
3 changed files with 514 additions and 520 deletions

View file

@ -288,448 +288,6 @@ pub fn handle_keymap <T> (
} }
} }
pub struct Split<
E: Engine,
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Widget<Engine = E>)->Usually<()>)->Usually<()>
>(pub F, pub Direction, PhantomData<E>);
impl<
E: Engine,
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Widget<Engine = E>)->Usually<()>)->Usually<()>
> Split<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)
}
}
pub struct Layers<
E: Engine,
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Widget<Engine = E>)->Usually<()>)->Usually<()>
>(pub F, PhantomData<E>);
impl<
E: Engine,
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Widget<Engine = E>)->Usually<()>)->Usually<()>
> Layers<E, F> {
#[inline]
pub fn new (build: F) -> Self {
Self(build, Default::default())
}
}
//pub fn collect <'a, E: Engine, const N: usize> (
//items: &'a [&'a dyn Widget<Engine = E>;N]
//) -> impl Send + Sync + Fn(&'a mut dyn FnMut(&'a dyn Widget<Engine = E>)->Usually<()>)->Usually<()> + '_ {
//|add: &'a mut dyn FnMut(&'a dyn Widget<Engine = E>)->Usually<()>| {
//for item in items.iter() {
//add(item)?;
//}
//Ok(())
//}
//}
//`Layers<_, impl (Fn(&mut dyn FnMut(&dyn Widget<Engine = _>) -> Result<(), Box<...>>) -> ... ) + Send + Sync>`
//`Layers<Tui, impl (Fn(&mut dyn FnMut(&dyn Widget<Engine = Tui>) -> Result<(), Box<(dyn std::error::Error + 'static)>>) -> Result<(), Box<(dyn std::error::Error + 'static)>>) + Send + Sync + '_>`
#[derive(Copy, Clone)]
pub enum Direction {
Up,
Down,
Left,
Right,
}
impl Direction {
pub fn is_down (&self) -> bool {
match self { Self::Down => true, _ => false }
}
pub fn is_right (&self) -> bool {
match self { Self::Right => true, _ => false }
}
}
/// 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: Number, 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: Widget<Engine = E>> Widget for Align<T> {
type Engine = E;
fn layout (&self, outer_area: E::Size) -> Perhaps<E::Size> {
self.inner().layout(outer_area)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
let outer_area = to.area();
Ok(if let Some(inner_size) = self.layout(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: Number, T> {
/// Enforce fixed width
X(U, T),
/// Enforce fixed height
Y(U, T),
/// Enforce fixed width and height
XY(U, U, T),
}
impl<N: Number, T> Fixed<N, T> {
pub fn inner (&self) -> &T {
match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, }
}
}
/// Enforce minimum size of drawing area
pub enum Min<U: Number, T> {
/// Enforce minimum width
X(U, T),
/// Enforce minimum height
Y(U, T),
/// Enforce minimum width and height
XY(U, U, T),
}
impl<N: Number, 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: Widget<Engine = E>> Widget for Min<E::Unit, T> {
type Engine = E;
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
Ok(self.inner().layout(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.layout(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: Number, T> {
/// Enforce maximum width
X(U, T),
/// Enforce maximum height
Y(U, T),
/// Enforce maximum width and height
XY(U, U, T),
}
impl<N: Number, 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: Widget<Engine = E>> Widget for Max<E:: Unit, T> {
type Engine = E;
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
Ok(self.inner().layout(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.layout(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: Number, T> {
/// Increase width
X(N, T),
/// Increase height
Y(N, T),
/// Increase width and height
XY(N, N, T)
}
impl<N: Number, 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: Widget<Engine = E>> Widget for Grow<E::Unit, T> {
type Engine = E;
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
Ok(self.inner().layout(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.layout(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: Number, T> {
/// Decrease width
X(N, T),
/// Decrease height
Y(N, T),
/// Decrease width and height
XY(N, N, T),
}
impl<N: Number, T: Widget> 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: Widget<Engine = E>> Widget for Shrink<E::Unit, T> {
type Engine = E;
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
Ok(self.inner().layout(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.layout(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: Number, T> {
/// Decrease width
X(N, T),
/// Decrease height
Y(N, T),
/// Decrease width and height
XY(N, N, T),
}
impl<N: Number, T: Widget> 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: Number, T> {
/// Increase width
X(N, T),
/// Increase height
Y(N, T),
/// Increase width and height
XY(N, N, T),
}
impl<N: Number, T: Widget> 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: Widget<Engine = E>> Widget for Inset<E::Unit, T> {
type Engine = E;
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
match *self {
Self::X(x, ref inner) => Shrink::X(x + x, inner as &dyn Widget<Engine = E>),
Self::Y(y, ref inner) => Shrink::Y(y + y, inner as &dyn Widget<Engine = E>),
Self::XY(x, y, ref inner) => Shrink::XY(x + x, y + y, inner as &dyn Widget<Engine = E>),
}.layout(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
match *self {
Self::X(x, ref inner) => Plus::X(x, inner as &dyn Widget<Engine = E>),
Self::Y(y, ref inner) => Plus::Y(y, inner as &dyn Widget<Engine = E>),
Self::XY(x, y, ref inner) => Plus::XY(x, y, inner as &dyn Widget<Engine = E>),
}.render(to)
}
}
impl<E: Engine, T: Widget<Engine = E>> Widget for Outset<E::Unit, T> {
type Engine = E;
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
match *self {
Self::X(x, ref inner) => Grow::X(x + x, inner as &dyn Widget<Engine = E>),
Self::Y(y, ref inner) => Grow::Y(y + y, inner as &dyn Widget<Engine = E>),
Self::XY(x, y, ref inner) => Grow::XY(x + x, y + y, inner as &dyn Widget<Engine = E>),
}.layout(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
match *self {
Self::X(x, ref inner) => Plus::X(x, inner as &dyn Widget<Engine = E>),
Self::Y(y, ref inner) => Plus::Y(y, inner as &dyn Widget<Engine = E>),
Self::XY(x, y, ref inner) => Plus::XY(x, y, inner as &dyn Widget<Engine = E>),
}.render(to)
}
}
/// Move origin point of drawing area
pub enum Plus<N: Number, T: Widget> {
/// 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: Number, T: Widget> Plus<N, T> {
fn inner (&self) -> &T {
match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, }
}
fn x (&self) -> N {
match self { Self::X(x, _) => *x, Self::Y(_, _) => N::default(), Self::XY(x, _, _) => *x }
}
fn y (&self) -> N {
match self { Self::X(_, _) => N::default(), Self::Y(y, _) => *y, Self::XY(_, y, _) => *y }
}
}
impl<E: Engine, T: Widget<Engine = E>> Widget for Plus<E::Unit, T> {
type Engine = E;
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
self.inner().layout(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
let area = to.area();
Ok(self.layout(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 Minus<N: Number, T: Widget> {
/// 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: Number, T: Widget> Minus<N, T> {
fn inner (&self) -> &T {
match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, }
}
fn x (&self) -> N {
match self { Self::X(x, _) => *x, Self::Y(_, _) => N::default(), Self::XY(x, _, _) => *x }
}
fn y (&self) -> N {
match self { Self::X(_, _) => N::default(), Self::Y(y, _) => *y, Self::XY(_, y, _) => *y }
}
}
impl<E: Engine, T: Widget<Engine = E>> Widget for Minus<E::Unit, T> {
type Engine = E;
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
self.inner().layout(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
let area = to.area();
Ok(self.layout(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(()))
}
}
/// A component that may contain [Focusable] components. /// A component that may contain [Focusable] components.
pub trait Focus <const N: usize, E: Engine>: Widget<Engine = E> + Handle<E> { pub trait Focus <const N: usize, E: Engine>: Widget<Engine = E> + Handle<E> {
fn focus (&self) -> usize; fn focus (&self) -> usize;

View file

@ -1,5 +1,43 @@
use crate::*; use crate::*;
macro_rules! impl_axis_common { ($A:ident $T:ty) => {
impl $A<$T> {
pub fn start_inc (&mut self) -> $T {
self.start += 1;
self.start
}
pub fn start_dec (&mut self) -> $T {
self.start = self.start.saturating_sub(1);
self.start
}
pub fn point_inc (&mut self) -> Option<$T> {
self.point = self.point.map(|p|p + 1);
self.point
}
pub fn point_dec (&mut self) -> Option<$T> {
self.point = self.point.map(|p|p.saturating_sub(1));
self.point
}
}
} }
pub struct FixedAxis<T> {
pub start: T,
pub point: Option<T>
}
impl_axis_common!(FixedAxis u16);
impl_axis_common!(FixedAxis usize);
pub struct ScaledAxis<T> {
pub start: T,
pub scale: T,
pub point: Option<T>
}
impl_axis_common!(ScaledAxis u16);
impl_axis_common!(ScaledAxis usize);
// TODO: return impl Point and impl Size instead of [N;x] // TODO: return impl Point and impl Size instead of [N;x]
// to disambiguate between usage of 2-"tuple"s // to disambiguate between usage of 2-"tuple"s
@ -69,46 +107,444 @@ impl<N: Number> Area<N> for [N;4] {
#[inline] fn h (&self) -> N { self[3] } #[inline] fn h (&self) -> N { self[3] }
} }
macro_rules! impl_axis_common { ($A:ident $T:ty) => { pub struct Split<
impl $A<$T> { E: Engine,
pub fn start_inc (&mut self) -> $T { F: Send + Sync + Fn(&mut dyn FnMut(&dyn Widget<Engine = E>)->Usually<()>)->Usually<()>
self.start += 1; >(pub F, pub Direction, PhantomData<E>);
self.start
} impl<
pub fn start_dec (&mut self) -> $T { E: Engine,
self.start = self.start.saturating_sub(1); F: Send + Sync + Fn(&mut dyn FnMut(&dyn Widget<Engine = E>)->Usually<()>)->Usually<()>
self.start > Split<E, F> {
} #[inline]
pub fn point_inc (&mut self) -> Option<$T> { pub fn new (direction: Direction, build: F) -> Self {
self.point = self.point.map(|p|p + 1); Self(build, direction, Default::default())
self.point
}
pub fn point_dec (&mut self) -> Option<$T> {
self.point = self.point.map(|p|p.saturating_sub(1));
self.point
}
} }
} } #[inline]
pub fn right (build: F) -> Self {
pub struct FixedAxis<T> { Self::new(Direction::Right, build)
pub start: T, }
pub point: Option<T> #[inline]
} pub fn down (build: F) -> Self {
Self::new(Direction::Down, build)
impl_axis_common!(FixedAxis u16); }
impl_axis_common!(FixedAxis usize); }
pub struct ScaledAxis<T> { pub struct Layers<
pub start: T, E: Engine,
pub scale: T, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Widget<Engine = E>)->Usually<()>)->Usually<()>
pub point: Option<T> >(pub F, PhantomData<E>);
}
impl<
impl_axis_common!(ScaledAxis u16); E: Engine,
impl_axis_common!(ScaledAxis usize); F: Send + Sync + Fn(&mut dyn FnMut(&dyn Widget<Engine = E>)->Usually<()>)->Usually<()>
> Layers<E, F> {
impl<T: Copy> ScaledAxis<T> { #[inline]
pub fn scale_mut (&mut self, cb: &impl Fn(T)->T) { pub fn new (build: F) -> Self {
self.scale = cb(self.scale) Self(build, Default::default())
}
}
//pub fn collect <'a, E: Engine, const N: usize> (
//items: &'a [&'a dyn Widget<Engine = E>;N]
//) -> impl Send + Sync + Fn(&'a mut dyn FnMut(&'a dyn Widget<Engine = E>)->Usually<()>)->Usually<()> + '_ {
//|add: &'a mut dyn FnMut(&'a dyn Widget<Engine = E>)->Usually<()>| {
//for item in items.iter() {
//add(item)?;
//}
//Ok(())
//}
//}
//`Layers<_, impl (Fn(&mut dyn FnMut(&dyn Widget<Engine = _>) -> Result<(), Box<...>>) -> ... ) + Send + Sync>`
//`Layers<Tui, impl (Fn(&mut dyn FnMut(&dyn Widget<Engine = Tui>) -> Result<(), Box<(dyn std::error::Error + 'static)>>) -> Result<(), Box<(dyn std::error::Error + 'static)>>) + Send + Sync + '_>`
#[derive(Copy, Clone)]
pub enum Direction {
Up,
Down,
Left,
Right,
}
impl Direction {
pub fn is_down (&self) -> bool {
match self { Self::Down => true, _ => false }
}
pub fn is_right (&self) -> bool {
match self { Self::Right => true, _ => false }
}
}
/// 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: Number, 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: Widget<Engine = E>> Widget for Align<T> {
type Engine = E;
fn layout (&self, outer_area: E::Size) -> Perhaps<E::Size> {
self.inner().layout(outer_area)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
let outer_area = to.area();
Ok(if let Some(inner_size) = self.layout(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: Number, T> {
/// Enforce fixed width
X(U, T),
/// Enforce fixed height
Y(U, T),
/// Enforce fixed width and height
XY(U, U, T),
}
impl<N: Number, T> Fixed<N, T> {
pub fn inner (&self) -> &T {
match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, }
}
}
/// Enforce minimum size of drawing area
pub enum Min<U: Number, T> {
/// Enforce minimum width
X(U, T),
/// Enforce minimum height
Y(U, T),
/// Enforce minimum width and height
XY(U, U, T),
}
impl<N: Number, 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: Widget<Engine = E>> Widget for Min<E::Unit, T> {
type Engine = E;
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
Ok(self.inner().layout(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.layout(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: Number, T> {
/// Enforce maximum width
X(U, T),
/// Enforce maximum height
Y(U, T),
/// Enforce maximum width and height
XY(U, U, T),
}
impl<N: Number, 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: Widget<Engine = E>> Widget for Max<E:: Unit, T> {
type Engine = E;
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
Ok(self.inner().layout(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.layout(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: Number, T> {
/// Increase width
X(N, T),
/// Increase height
Y(N, T),
/// Increase width and height
XY(N, N, T)
}
impl<N: Number, 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: Widget<Engine = E>> Widget for Grow<E::Unit, T> {
type Engine = E;
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
Ok(self.inner().layout(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.layout(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: Number, T> {
/// Decrease width
X(N, T),
/// Decrease height
Y(N, T),
/// Decrease width and height
XY(N, N, T),
}
impl<N: Number, T: Widget> 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: Widget<Engine = E>> Widget for Shrink<E::Unit, T> {
type Engine = E;
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
Ok(self.inner().layout(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.layout(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: Number, T> {
/// Decrease width
X(N, T),
/// Decrease height
Y(N, T),
/// Decrease width and height
XY(N, N, T),
}
impl<N: Number, T: Widget> 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: Number, T> {
/// Increase width
X(N, T),
/// Increase height
Y(N, T),
/// Increase width and height
XY(N, N, T),
}
impl<N: Number, T: Widget> 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: Widget<Engine = E>> Widget for Inset<E::Unit, T> {
type Engine = E;
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
match *self {
Self::X(x, ref inner) => Shrink::X(x + x, inner as &dyn Widget<Engine = E>),
Self::Y(y, ref inner) => Shrink::Y(y + y, inner as &dyn Widget<Engine = E>),
Self::XY(x, y, ref inner) => Shrink::XY(x + x, y + y, inner as &dyn Widget<Engine = E>),
}.layout(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
match *self {
Self::X(x, ref inner) => Plus::X(x, inner as &dyn Widget<Engine = E>),
Self::Y(y, ref inner) => Plus::Y(y, inner as &dyn Widget<Engine = E>),
Self::XY(x, y, ref inner) => Plus::XY(x, y, inner as &dyn Widget<Engine = E>),
}.render(to)
}
}
impl<E: Engine, T: Widget<Engine = E>> Widget for Outset<E::Unit, T> {
type Engine = E;
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
match *self {
Self::X(x, ref inner) => Grow::X(x + x, inner as &dyn Widget<Engine = E>),
Self::Y(y, ref inner) => Grow::Y(y + y, inner as &dyn Widget<Engine = E>),
Self::XY(x, y, ref inner) => Grow::XY(x + x, y + y, inner as &dyn Widget<Engine = E>),
}.layout(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
match *self {
Self::X(x, ref inner) => Plus::X(x, inner as &dyn Widget<Engine = E>),
Self::Y(y, ref inner) => Plus::Y(y, inner as &dyn Widget<Engine = E>),
Self::XY(x, y, ref inner) => Plus::XY(x, y, inner as &dyn Widget<Engine = E>),
}.render(to)
}
}
/// Move origin point of drawing area
pub enum Plus<N: Number, T: Widget> {
/// 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: Number, T: Widget> Plus<N, T> {
fn inner (&self) -> &T {
match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, }
}
fn x (&self) -> N {
match self { Self::X(x, _) => *x, Self::Y(_, _) => N::default(), Self::XY(x, _, _) => *x }
}
fn y (&self) -> N {
match self { Self::X(_, _) => N::default(), Self::Y(y, _) => *y, Self::XY(_, y, _) => *y }
}
}
impl<E: Engine, T: Widget<Engine = E>> Widget for Plus<E::Unit, T> {
type Engine = E;
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
self.inner().layout(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
let area = to.area();
Ok(self.layout(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 Minus<N: Number, T: Widget> {
/// 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: Number, T: Widget> Minus<N, T> {
fn inner (&self) -> &T {
match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, }
}
fn x (&self) -> N {
match self { Self::X(x, _) => *x, Self::Y(_, _) => N::default(), Self::XY(x, _, _) => *x }
}
fn y (&self) -> N {
match self { Self::X(_, _) => N::default(), Self::Y(y, _) => *y, Self::XY(_, y, _) => *y }
}
}
impl<E: Engine, T: Widget<Engine = E>> Widget for Minus<E::Unit, T> {
type Engine = E;
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
self.inner().layout(to)
}
fn render (&self, to: &mut E::Output) -> Usually<()> {
let area = to.area();
Ok(self.layout(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(()))
} }
} }

View file

@ -88,7 +88,7 @@ impl<E: Engine> TransportToolbar<E> {
} }
} }
pub fn toggle_play (&mut self) -> Usually<()> { pub fn toggle_play (&mut self) -> Usually<()> {
self.playing.toggle(); self.playing.toggle()?;
Ok(()) Ok(())
} }
pub fn update (&mut self, scope: &ProcessScope) -> (bool, usize, usize, usize, usize, f64) { pub fn update (&mut self, scope: &ProcessScope) -> (bool, usize, usize, usize, usize, f64) {
@ -146,40 +146,6 @@ impl<E: Engine> TransportToolbar<E> {
self.sync.value self.sync.value
} }
} }
impl Focus<5, Tui> for TransportToolbar<Tui> {
fn focus (&self) -> usize {
self.focus
}
fn focus_mut (&mut self) -> &mut usize {
&mut self.focus
}
fn focusable (&self) -> [&dyn Focusable<Tui>;5] {
[
&self.playing as &dyn Focusable<Tui>,
&self.bpm as &dyn Focusable<Tui>,
&self.quant as &dyn Focusable<Tui>,
&self.sync as &dyn Focusable<Tui>,
&self.clock as &dyn Focusable<Tui>,
]
}
fn focusable_mut (&mut self) -> [&mut dyn Focusable<Tui>;5] {
[
&mut self.playing as &mut dyn Focusable<Tui>,
&mut self.bpm as &mut dyn Focusable<Tui>,
&mut self.quant as &mut dyn Focusable<Tui>,
&mut self.sync as &mut dyn Focusable<Tui>,
&mut self.clock as &mut dyn Focusable<Tui>,
]
}
}
impl Focusable<Tui> for TransportToolbar<Tui> {
fn is_focused (&self) -> bool {
self.focused
}
fn set_focused (&mut self, focused: bool) {
self.focused = focused
}
}
impl<E: Engine> Audio for TransportToolbar<E> { impl<E: Engine> Audio for TransportToolbar<E> {
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
self.update(&scope); self.update(&scope);
@ -214,6 +180,40 @@ impl Content for TransportToolbar<Tui> {
}) })
} }
} }
impl Focus<5, Tui> for TransportToolbar<Tui> {
fn focus (&self) -> usize {
self.focus
}
fn focus_mut (&mut self) -> &mut usize {
&mut self.focus
}
fn focusable (&self) -> [&dyn Focusable<Tui>;5] {
[
&self.playing as &dyn Focusable<Tui>,
&self.bpm as &dyn Focusable<Tui>,
&self.quant as &dyn Focusable<Tui>,
&self.sync as &dyn Focusable<Tui>,
&self.clock as &dyn Focusable<Tui>,
]
}
fn focusable_mut (&mut self) -> [&mut dyn Focusable<Tui>;5] {
[
&mut self.playing as &mut dyn Focusable<Tui>,
&mut self.bpm as &mut dyn Focusable<Tui>,
&mut self.quant as &mut dyn Focusable<Tui>,
&mut self.sync as &mut dyn Focusable<Tui>,
&mut self.clock as &mut dyn Focusable<Tui>,
]
}
}
impl Focusable<Tui> for TransportToolbar<Tui> {
fn is_focused (&self) -> bool {
self.focused
}
fn set_focused (&mut self, focused: bool) {
self.focused = focused
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
@ -260,8 +260,8 @@ impl Content for TransportPlayPauseButton<Tui> {
type Engine = Tui; type Engine = Tui;
fn content (&self) -> impl Widget<Engine = Tui> { fn content (&self) -> impl Widget<Engine = Tui> {
Layers::new(|add|{ Layers::new(|add|{
//add(&self.focused.then_some(CORNERS))?; add(&self.focused.then_some(CORNERS))?;
add(&Styled(match self.value { add(&Plus::X(1, Min::Y(2, Styled(match self.value {
Some(TransportState::Stopped) => Some(GRAY_DIM.bold()), Some(TransportState::Stopped) => Some(GRAY_DIM.bold()),
Some(TransportState::Starting) => Some(GRAY_NOT_DIM_BOLD), Some(TransportState::Starting) => Some(GRAY_NOT_DIM_BOLD),
Some(TransportState::Rolling) => Some(WHITE_NOT_DIM_BOLD), Some(TransportState::Rolling) => Some(WHITE_NOT_DIM_BOLD),
@ -271,7 +271,7 @@ impl Content for TransportPlayPauseButton<Tui> {
Some(TransportState::Starting) => "READY ...", Some(TransportState::Starting) => "READY ...",
Some(TransportState::Stopped) => "⏹ STOPPED", Some(TransportState::Stopped) => "⏹ STOPPED",
_ => unreachable!(), _ => unreachable!(),
}))?; }))))?;
Ok(()) Ok(())
}) })
} }