generalize some of the command logic

This commit is contained in:
🪞👃🪞 2025-01-05 10:34:31 +01:00
parent 905486edbd
commit 1a9077427c
5 changed files with 48 additions and 48 deletions

View file

@ -1,87 +0,0 @@
use crate::*;
#[macro_export] macro_rules! keymap {
(
$(<$lt:lifetime>)? $KEYS:ident = |$state:ident: $State:ty, $input:ident: $Input:ty| $Command:ty
{ $($key:expr => $handler:expr),* $(,)? } $(,)?
) => {
pub const $KEYS: EventMap<'static, $State, $Input, $Command> = EventMap {
fallback: None,
bindings: &[ $(($key, &|$state|Some($handler)),)* ]
};
input_to_command!($(<$lt>)? $Command: |$state: $State, input: $Input|$KEYS.handle($state, input)?);
};
(
$(<$lt:lifetime>)? $KEYS:ident = |$state:ident: $State:ty, $input:ident: $Input:ty| $Command:ty
{ $($key:expr => $handler:expr),* $(,)? }, $default:expr
) => {
pub const $KEYS: EventMap<'static, $State, $Input, $Command> = EventMap {
fallback: Some(&|$state, $input|Some($default)),
bindings: &[ $(($key, &|$state|Some($handler)),)* ]
};
input_to_command!($(<$lt>)? $Command: |$state: $State, input: $Input|$KEYS.handle($state, input)?);
};
}
pub struct EventMap<'a, S, I: PartialEq, C> {
pub bindings: &'a [(I, &'a dyn Fn(&S) -> Option<C>)],
pub fallback: Option<&'a dyn Fn(&S, &I) -> Option<C>>
}
impl<'a, S, I: PartialEq, C> EventMap<'a, S, I, C> {
pub fn handle (&self, state: &S, input: &I) -> Option<C> {
for (binding, handler) in self.bindings.iter() {
if input == binding {
return handler(state)
}
}
if let Some(fallback) = self.fallback {
fallback(state, input)
} else {
None
}
}
}
pub trait Command<S>: Send + Sync + Sized {
fn execute (self, state: &mut S) -> Perhaps<Self>;
fn delegate <T> (self, state: &mut S, wrap: impl Fn(Self)->T) -> Perhaps<T> {
Ok(self.execute(state)?.map(wrap))
}
}
#[macro_export] macro_rules! input_to_command {
(<$($l:lifetime),+> $Command:ty: |$state:ident:$State:ty, $input:ident:$Input:ty| $handler:expr) => {
impl<$($l),+> InputToCommand<$Input, $State> for $Command {
fn input_to_command ($state: &$State, $input: &$Input) -> Option<Self> {
Some($handler)
}
}
};
($Command:ty: |$state:ident:$State:ty, $input:ident:$Input:ty| $handler:expr) => {
impl InputToCommand<$Input, $State> for $Command {
fn input_to_command ($state: &$State, $input: &$Input) -> Option<Self> {
Some($handler)
}
}
}
}
pub trait InputToCommand<I, S>: Command<S> + Sized {
fn input_to_command (state: &S, input: &I) -> Option<Self>;
fn execute_with_state (state: &mut S, input: &I) -> Perhaps<bool> {
Ok(if let Some(command) = Self::input_to_command(state, input) {
let _undo = command.execute(state)?;
Some(true)
} else {
None
})
}
}
#[macro_export] macro_rules! command {
($(<$($l:lifetime),+>)?|$self:ident:$Command:ty,$state:ident:$State:ty|$handler:expr) => {
impl$(<$($l),+>)? Command<$State> for $Command {
fn execute ($self, $state: &mut $State) -> Perhaps<Self> {
Ok($handler)
}
}
};
}

View file

@ -12,10 +12,9 @@ pub(crate) use ::tek_layout::{
*,
tek_engine::{
Usually, Perhaps,
Engine, Size, Area,
Output, Content, Render, Thunk, render,
Input, Handle, handle,
kexp, kpat,
Output, Content, Render, Thunk, render, Engine, Size, Area,
Input, handle, Handle, command, Command, input_to_command, InputToCommand,
keymap, kexp, kpat, EventMap,
tui::{
Tui,
TuiIn, key, ctrl, shift, alt,
@ -57,7 +56,6 @@ pub mod arranger; pub use self::arranger::*;
pub mod border; pub use self::border::*;
pub mod clock; pub use self::clock::*;
pub mod color; pub use self::color::*;
pub mod command; pub use self::command::*;
pub mod field; pub use self::field::*;
pub mod file; pub use self::file::*;
pub mod focus; pub use self::focus::*;