tek/crates/tek_core/src/engine.rs

708 lines
21 KiB
Rust

use crate::*;
/// Entry point for main loop
pub trait App<T: Engine> {
fn run (self, context: T) -> Usually<T>;
}
/// Platform backend.
pub trait Engine: Send + Sync + Sized {
fn setup (&mut self) -> Usually<()> { Ok(()) }
fn exited (&self) -> bool;
fn teardown (&mut self) -> Usually<()> { Ok(()) }
/// Unit of distance.
type Unit: Number;
type Area: Area<Self::Unit> + From<[Self::Unit;4]> + Debug;
type HandleInput;
type Handled;
// FIXME
fn area (&self) -> Self::Area;
// FIXME
fn with_area (&mut self, x: Self::Unit, y: Self::Unit, w: Self::Unit, h: Self::Unit)
-> &mut Self;
// FIXME
fn render_in (
&mut self, area: Self::Area, widget: &impl Widget<Engine = Self>
) -> Perhaps<Self::Area>;
}
pub trait Widget: Send + Sync {
type Engine: Engine;
fn layout (&self, to: <<Self as Widget>::Engine as Engine>::Area) ->
Perhaps<<<Self as Widget>::Engine as Engine>::Area>
{
Ok(Some(to))
}
fn render (&self, to: &mut Self::Engine) ->
Perhaps<<<Self as Widget>::Engine as Engine>::Area>;
}
impl<'a, E: Engine> Widget for Box<dyn Widget<Engine = E> + 'a> {
type Engine = E;
fn layout (&self, to: E::Area) -> Perhaps<E::Area> {
(**self).layout(to)
}
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
(**self).render(to)
}
}
impl<E: Engine> Widget for &dyn Widget<Engine = E> {
type Engine = E;
fn layout (&self, to: E::Area) -> Perhaps<E::Area> {
(*self).layout(to)
}
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
(*self).render(to)
}
}
impl<E: Engine> Widget for &mut dyn Widget<Engine = E> {
type Engine = E;
fn layout (&self, to: E::Area) -> Perhaps<E::Area> {
(**self).layout(to)
}
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
(**self).render(to)
}
}
impl<E: Engine, W: Widget<Engine = E>> Widget for Arc<W> {
type Engine = E;
fn layout (&self, to: E::Area) -> Perhaps<E::Area> {
self.as_ref().layout(to)
}
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
self.as_ref().render(to)
}
}
impl<E: Engine, W: Widget<Engine = E>> Widget for Mutex<W> {
type Engine = E;
fn layout (&self, to: E::Area) -> Perhaps<E::Area> {
self.lock().unwrap().layout(to)
}
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
self.lock().unwrap().render(to)
}
}
impl<E: Engine, W: Widget<Engine = E>> Widget for RwLock<W> {
type Engine = E;
fn layout (&self, to: E::Area) -> Perhaps<E::Area> {
self.read().unwrap().layout(to)
}
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
self.read().unwrap().render(to)
}
}
impl<E: Engine, W: Widget<Engine = E>> Widget for Option<W> {
type Engine = E;
fn layout (&self, to: E::Area) -> Perhaps<E::Area> {
Ok(self.as_ref().map(|widget|widget.layout(to)).transpose()?.flatten())
}
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
Ok(self.as_ref().map(|widget|widget.render(to)).transpose()?.flatten())
}
}
pub trait Content: Send + Sync {
type Engine: Engine;
fn content (&self) -> impl Widget<Engine = <Self as Content>::Engine>;
}
//impl<E> Content<E> for () where E: Engine {
//fn content (&self) -> impl Widget<E> {
//()
//}
//}
impl<E: Engine, W: Content<Engine = E>> Widget for W {
type Engine = E;
fn layout (&self, to: E::Area) -> Perhaps<E::Area> {
self.content().layout(to)
}
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
match self.layout(to.area())? {
Some(area) => to.render_in(area, &self.content()),
None => Ok(None)
}
}
}
/// A UI component.
pub trait Component<E: Engine>: Widget<Engine = E> + Handle<E> {}
/// Everything that implements [Render] and [Handle] is a [Component].
impl<E: Engine, C: Widget<Engine = E> + Handle<E>> Component<E> for C {}
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)
}
}
impl<E: Engine, C: Component<E> + Exit> ExitableComponent<E> for C {}
/// Handle input
pub trait Handle<E: Engine>: Send + Sync {
fn handle (&mut self, context: &E::HandleInput) -> Perhaps<E::Handled>;
}
impl<H, E: Engine> Handle<E> for &mut H where H: Handle<E> {
fn handle (&mut self, context: &E::HandleInput) -> Perhaps<E::Handled> {
(*self).handle(context)
}
}
impl<H, E: Engine> Handle<E> for Option<H> where H: Handle<E> {
fn handle (&mut self, context: &E::HandleInput) -> 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::HandleInput) -> 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::HandleInput) -> 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::HandleInput) -> 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::HandleInput) -> Perhaps<E::Handled> {
self.write().unwrap().handle(context)
}
}
pub type KeyHandler<T> = &'static dyn Fn(&mut T)->Usually<bool>;
pub type KeyBinding<T> = (
KeyCode, KeyModifiers, &'static str, &'static str, KeyHandler<T>
);
pub type KeyMap<T> = [KeyBinding<T>];
pub fn handle_keymap <T> (
state: &mut T, event: &TuiEvent, keymap: &KeyMap<T>,
) -> Usually<bool> {
match event {
TuiEvent::Input(crossterm::event::Event::Key(event)) => {
for (code, modifiers, _, _, command) in keymap.iter() {
if *code == event.code && modifiers.bits() == event.modifiers.bits() {
return command(state)
}
}
},
_ => {}
};
Ok(false)
}
/// Define a keymap
#[macro_export] macro_rules! keymap {
($T:ty { $([$k:ident $(($char:literal))?, $m:ident, $n: literal, $d: literal, $f: expr]),* $(,)? }) => {
&[
$((KeyCode::$k $(($char))?, KeyModifiers::$m, $n, $d, &$f as KeyHandler<$T>)),*
] as &'static [KeyBinding<$T>]
}
}
#[macro_export] macro_rules! map_key {
($k:ident $(($char:literal))?, $m:ident, $n: literal, $d: literal, $f: expr) => {
(KeyCode::$k $(($char))?, KeyModifiers::$m, $n, $d, &$f as &dyn Fn()->Usually<bool>)
}
}
#[macro_export] macro_rules! key {
($code:pat) => {
TuiEvent::Input(crossterm::event::Event::Key(crossterm::event::KeyEvent {
code: $code,
modifiers: crossterm::event::KeyModifiers::NONE,
kind: crossterm::event::KeyEventKind::Press,
state: crossterm::event::KeyEventState::NONE
}))
};
(Ctrl-$code:pat) => {
TuiEvent::Input(crossterm::event::Event::Key(crossterm::event::KeyEvent {
code: $code,
modifiers: crossterm::event::KeyModifiers::CONTROL,
kind: crossterm::event::KeyEventKind::Press,
state: crossterm::event::KeyEventState::NONE
}))
};
(Alt-$code:pat) => {
TuiEvent::Input(crossterm::event::Event::Key(crossterm::event::KeyEvent {
code: $code,
modifiers: crossterm::event::KeyModifiers::ALT,
kind: crossterm::event::KeyEventKind::Press,
state: crossterm::event::KeyEventState::NONE
}))
}
}
#[macro_export] macro_rules! match_key {
($event:expr, {
$($key:pat=>$block:expr),* $(,)?
}) => {
match $event {
$(crossterm::event::Event::Key(crossterm::event::KeyEvent {
code: $key,
modifiers: crossterm::event::KeyModifiers::NONE,
kind: crossterm::event::KeyEventKind::Press,
state: crossterm::event::KeyEventState::NONE
}) => {
$block
})*
_ => Ok(None)
}
}
}
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 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::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,
}
}
}
/// 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::Area) -> Perhaps<E::Area> {
Ok(self.inner().layout(to)?.map(|to|match *self {
Self::X(w, _) => [to.x(), to.y(), to.w().max(w), to.h()],
Self::Y(h, _) => [to.x(), to.y(), to.w(), to.h().max(h)],
Self::XY(w, h, _) => [to.x(), to.y(), to.w().max(w), to.h().max(h)],
}.into()))
}
// TODO: 🡘 🡙 ←🡙→
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
Ok(self.layout(to.area())?.map(|a|to.render_in(a, self.inner())).transpose()?.flatten())
}
}
/// 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::Area) -> Perhaps<E::Area> {
Ok(self.inner().layout(to)?.map(|to|match *self {
Self::X(w, _) => [to.x(), to.y(), to.w().min(w), to.h()],
Self::Y(h, _) => [to.x(), to.y(), to.w(), to.h().min(h)],
Self::XY(w, h, _) => [to.x(), to.y(), to.w().min(w), to.h().min(h)],
}.into()))
}
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
Ok(self.layout(to.area())?.map(|a|to.render_in(a, self.inner())).transpose()?.flatten())
}
}
/// 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::Area) -> Perhaps<E::Area> {
Ok(self.inner().layout(to)?.map(|to|match *self {
Self::X(w, _) => [to.x(), to.y(), to.w() + w, to.h()],
Self::Y(h, _) => [to.x(), to.y(), to.w(), to.h() + h],
Self::XY(w, h, _) => [to.x(), to.y(), to.w() + w, to.h() + h],
}.into()))
}
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
Ok(self.layout(to.area())?.map(|a|to.render_in(a, self.inner())).transpose()?.flatten())
}
}
/// 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::Area) -> Perhaps<E::Area> {
Ok(self.inner().layout(to)?.map(|to|match *self {
Self::X(w, _) => [to.x(), to.y(), to.w() - w, to.h()],
Self::Y(h, _) => [to.x(), to.y(), to.w(), to.h() - h],
Self::XY(w, h, _) => [to.x(), to.y(), to.w() - w, to.h() - h]
}.into()))
}
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
Ok(self.layout(to.area())?.map(|a|to.render_in(a, self.inner())).transpose()?.flatten())
}
}
/// 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> {
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>> Content for Inset<E::Unit, T> {
type Engine = E;
fn content (&self) -> impl Widget<Engine = E> {
Align::Center(match *self {
Self::X(x, inner) => Shrink::X(x + x, inner),
Self::Y(y, inner) => Shrink::X(y + y, inner),
Self::XY(x, y, inner) => Shrink::XY(x, y, inner),
})
}
}
/// 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> {
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>> Content for Outset<E::Unit, T> {
type Engine = E;
fn content (&self) -> impl Widget<Engine = E> {
Align::Center(match self {
Self::X(x, inner) => Grow::X(*x + *x, inner),
Self::Y(y, inner) => Grow::X(*y + *y, inner),
Self::XY(x, y, inner) => Grow::XY(*x, *y, inner),
})
}
}
/// Move origin point of drawing area
pub enum Offset<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> Offset<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 Offset<E::Unit, T> {
type Engine = E;
fn layout (&self, to: E::Area) -> Perhaps<E::Area> {
Ok(self.inner().layout(to)?.map(|to|match *self {
Self::X(x, _) => [to.x() + x, to.y(), to.w(), to.h()],
Self::Y(y, _) => [to.x(), to.y() + y, to.w(), to.h()],
Self::XY(x, y, _) => [to.x() + x, to.y() + y, to.w(), to.h()]
}.into()))
}
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
Ok(self.layout(to.area())?.map(|a|to.render_in(a, self.inner())).transpose()?.flatten())
}
}
/// A component that may contain [Focusable] components.
pub trait Focus <const N: usize, E: Engine>: Widget<Engine = E> + Handle<E> {
fn focus (&self) -> usize;
fn focus_mut (&mut self) -> &mut usize;
fn focusable (&self) -> [&dyn Focusable<E>;N];
fn focusable_mut (&mut self) -> [&mut dyn Focusable<E>;N];
fn focused (&self) -> &dyn Focusable<E> {
let focus = self.focus();
self.focusable()[focus]
}
fn focused_mut (&mut self) -> &mut dyn Focusable<E> {
let focus = self.focus();
self.focusable_mut()[focus]
}
fn focus_prev (&mut self) {
let focus = self.focus();
self.focus_set(if focus > 0 { focus - 1 } else { N - 1 });
}
fn focus_next (&mut self) {
let focus = self.focus();
self.focus_set(if focus < N - 1 { focus + 1 } else { 0 });
}
fn focus_set (&mut self, focus: usize) {
*self.focus_mut() = focus;
let focusable = self.focusable_mut();
for index in 0..N {
focusable[index].set_focused(index == focus);
}
}
}
/// A component that may be focused.
pub trait Focusable<E: Engine>: Widget<Engine = E> + Handle<E> {
fn is_focused (&self) -> bool;
fn set_focused (&mut self, focused: bool);
}
impl<F: Focusable<E>, E: Engine> Focusable<E> for Option<F>
where Option<F>: Widget<Engine = E>
{
fn is_focused (&self) -> bool {
match self {
Some(focusable) => focusable.is_focused(),
None => false
}
}
fn set_focused (&mut self, focused: bool) {
if let Some(focusable) = self {
focusable.set_focused(focused)
}
}
}
/// Implement the [Focus] trait for a component.
#[macro_export] macro_rules! focus {
($struct:ident ($focus:ident) : $count:expr => [
$($focusable:ident),*
]) => {
impl Focus<$count, E> for $struct {
fn focus (&self) -> usize {
self.$focus
}
fn focus_mut (&mut self) -> &mut usize {
&mut self.$focus
}
fn focusable (&self) -> [&dyn Focusable<E>;$count] {
[
$(&self.$focusable as &dyn Focusable<E>,)*
]
}
fn focusable_mut (&mut self) -> [&mut dyn Focusable<E>;$count] {
[
$(&mut self.$focusable as &mut dyn Focusable<E>,)*
]
}
}
}
}
/// Implement the [Focusable] trait for a component.
#[macro_export] macro_rules! focusable {
($struct:ident) => {
focusable!($struct (focused));
};
($struct:ident ($focused:ident)) => {
impl Focusable for $struct {
fn is_focused (&self) -> bool {
self.$focused
}
fn set_focused (&mut self, focused: bool) {
self.$focused = focused
}
}
}
}