use crate::*; /// Entry point for main loop pub trait App { fn run (self, context: T) -> Usually; } /// 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 + 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 ) -> Perhaps; } pub trait Widget: Send + Sync { type Engine: Engine; fn layout (&self, to: <::Engine as Engine>::Area) -> Perhaps<<::Engine as Engine>::Area> { Ok(Some(to)) } fn render (&self, to: &mut Self::Engine) -> Perhaps<<::Engine as Engine>::Area>; } impl<'a, E: Engine> Widget for Box + 'a> { type Engine = E; fn layout (&self, to: E::Area) -> Perhaps { (**self).layout(to) } fn render (&self, to: &mut E) -> Perhaps { (**self).render(to) } } impl Widget for &dyn Widget { type Engine = E; fn layout (&self, to: E::Area) -> Perhaps { (*self).layout(to) } fn render (&self, to: &mut E) -> Perhaps { (*self).render(to) } } impl Widget for &mut dyn Widget { type Engine = E; fn layout (&self, to: E::Area) -> Perhaps { (**self).layout(to) } fn render (&self, to: &mut E) -> Perhaps { (**self).render(to) } } impl> Widget for Arc { type Engine = E; fn layout (&self, to: E::Area) -> Perhaps { self.as_ref().layout(to) } fn render (&self, to: &mut E) -> Perhaps { self.as_ref().render(to) } } impl> Widget for Mutex { type Engine = E; fn layout (&self, to: E::Area) -> Perhaps { self.lock().unwrap().layout(to) } fn render (&self, to: &mut E) -> Perhaps { self.lock().unwrap().render(to) } } impl> Widget for RwLock { type Engine = E; fn layout (&self, to: E::Area) -> Perhaps { self.read().unwrap().layout(to) } fn render (&self, to: &mut E) -> Perhaps { self.read().unwrap().render(to) } } impl> Widget for Option { type Engine = E; fn layout (&self, to: E::Area) -> Perhaps { Ok(self.as_ref().map(|widget|widget.layout(to)).transpose()?.flatten()) } fn render (&self, to: &mut E) -> Perhaps { 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>; } //impl Content for () where E: Engine { //fn content (&self) -> impl Widget { //() //} //} impl> Widget for W { type Engine = E; fn layout (&self, to: E::Area) -> Perhaps { self.content().layout(to) } fn render (&self, to: &mut E) -> Perhaps { match self.layout(to.area())? { Some(area) => to.render_in(area, &self.content()), None => Ok(None) } } } /// A UI component. pub trait Component: Widget + Handle {} /// Everything that implements [Render] and [Handle] is a [Component]. impl + Handle> Component for C {} pub trait Exit: Send { fn exited (&self) -> bool; fn exit (&mut self); fn boxed (self) -> Box where Self: Sized + 'static { Box::new(self) } } /// Marker trait for [Component]s that can [Exit] pub trait ExitableComponent: Exit + Component where E: Engine { /// Perform type erasure for collecting heterogeneous components. fn boxed (self) -> Box> where Self: Sized + 'static { Box::new(self) } } impl + Exit> ExitableComponent for C {} /// Handle input pub trait Handle: Send + Sync { fn handle (&mut self, context: &E::HandleInput) -> Perhaps; } impl Handle for &mut H where H: Handle { fn handle (&mut self, context: &E::HandleInput) -> Perhaps { (*self).handle(context) } } impl Handle for Option where H: Handle { fn handle (&mut self, context: &E::HandleInput) -> Perhaps { if let Some(ref mut handle) = self { handle.handle(context) } else { Ok(None) } } } impl Handle for Mutex where H: Handle { fn handle (&mut self, context: &E::HandleInput) -> Perhaps { self.lock().unwrap().handle(context) } } impl Handle for Arc> where H: Handle { fn handle (&mut self, context: &E::HandleInput) -> Perhaps { self.lock().unwrap().handle(context) } } impl Handle for RwLock where H: Handle { fn handle (&mut self, context: &E::HandleInput) -> Perhaps { self.write().unwrap().handle(context) } } impl Handle for Arc> where H: Handle { fn handle (&mut self, context: &E::HandleInput) -> Perhaps { self.write().unwrap().handle(context) } } pub type KeyHandler = &'static dyn Fn(&mut T)->Usually; pub type KeyBinding = ( KeyCode, KeyModifiers, &'static str, &'static str, KeyHandler ); pub type KeyMap = [KeyBinding]; pub fn handle_keymap ( state: &mut T, event: &TuiEvent, keymap: &KeyMap, ) -> Usually { 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) } } #[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)->Usually<()>)->Usually<()> >(pub F, pub Direction, PhantomData); impl< E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Widget)->Usually<()>)->Usually<()> > Split { #[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)->Usually<()>)->Usually<()> >(pub F, PhantomData); impl< E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Widget)->Usually<()>)->Usually<()> > Layers { #[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;N] //) -> impl Send + Sync + Fn(&'a mut dyn FnMut(&'a dyn Widget)->Usually<()>)->Usually<()> + '_ { //|add: &'a mut dyn FnMut(&'a dyn Widget)->Usually<()>| { //for item in items.iter() { //add(item)?; //} //Ok(()) //} //} //`Layers<_, impl (Fn(&mut dyn FnMut(&dyn Widget) -> Result<(), Box<...>>) -> ... ) + Send + Sync>` //`Layers) -> 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 { /// 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 Align { 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 { /// Enforce fixed width X(U, T), /// Enforce fixed height Y(U, T), /// Enforce fixed width and height XY(U, U, T), } impl Fixed { 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 { /// Enforce minimum width X(U, T), /// Enforce minimum height Y(U, T), /// Enforce minimum width and height XY(U, U, T), } impl Min { pub fn inner (&self) -> &T { match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, } } } impl> Widget for Min { type Engine = E; fn layout (&self, to: E::Area) -> Perhaps { 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 { Ok(self.layout(to.area())?.map(|a|to.render_in(a, self.inner())).transpose()?.flatten()) } } /// Enforce maximum size of drawing area pub enum Max { /// Enforce maximum width X(U, T), /// Enforce maximum height Y(U, T), /// Enforce maximum width and height XY(U, U, T), } impl Max { fn inner (&self) -> &T { match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, } } } impl> Widget for Max { type Engine = E; fn layout (&self, to: E::Area) -> Perhaps { 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 { Ok(self.layout(to.area())?.map(|a|to.render_in(a, self.inner())).transpose()?.flatten()) } } /// Expand drawing area pub enum Grow { /// Increase width X(N, T), /// Increase height Y(N, T), /// Increase width and height XY(N, N, T) } impl Grow { fn inner (&self) -> &T { match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, } } } impl> Widget for Grow { type Engine = E; fn layout (&self, to: E::Area) -> Perhaps { 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 { Ok(self.layout(to.area())?.map(|a|to.render_in(a, self.inner())).transpose()?.flatten()) } } /// Shrink drawing area pub enum Shrink { /// Decrease width X(N, T), /// Decrease height Y(N, T), /// Decrease width and height XY(N, N, T), } impl Shrink { fn inner (&self) -> &T { match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, } } } impl> Widget for Shrink { type Engine = E; fn layout (&self, to: E::Area) -> Perhaps { 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 { Ok(self.layout(to.area())?.map(|a|to.render_in(a, self.inner())).transpose()?.flatten()) } } /// Shrink from each side pub enum Inset { /// Decrease width X(N, T), /// Decrease height Y(N, T), /// Decrease width and height XY(N, N, T), } impl Inset { fn inner (&self) -> &T { match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, } } } impl> Content for Inset { type Engine = E; fn content (&self) -> impl Widget { 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 { /// Increase width X(N, T), /// Increase height Y(N, T), /// Increase width and height XY(N, N, T), } impl Outset { fn inner (&self) -> &T { match self { Self::X(_, i) => i, Self::Y(_, i) => i, Self::XY(_, _, i) => i, } } } impl> Content for Outset { type Engine = E; fn content (&self) -> impl Widget { 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 { /// 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 Offset { 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> Widget for Offset { type Engine = E; fn layout (&self, to: E::Area) -> Perhaps { 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 { 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 : Widget + Handle { fn focus (&self) -> usize; fn focus_mut (&mut self) -> &mut usize; fn focusable (&self) -> [&dyn Focusable;N]; fn focusable_mut (&mut self) -> [&mut dyn Focusable;N]; fn focused (&self) -> &dyn Focusable { let focus = self.focus(); self.focusable()[focus] } fn focused_mut (&mut self) -> &mut dyn Focusable { 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: Widget + Handle { fn is_focused (&self) -> bool; fn set_focused (&mut self, focused: bool); } impl, E: Engine> Focusable for Option where Option: Widget { 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;$count] { [ $(&self.$focusable as &dyn Focusable,)* ] } fn focusable_mut (&mut self) -> [&mut dyn Focusable;$count] { [ $(&mut self.$focusable as &mut dyn Focusable,)* ] } } } } /// 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 } } } }