diff --git a/crates/tek/src/tui/phrase_editor.rs b/crates/tek/src/tui/phrase_editor.rs index 0ab2d568..f678e15e 100644 --- a/crates/tek/src/tui/phrase_editor.rs +++ b/crates/tek/src/tui/phrase_editor.rs @@ -1,5 +1,5 @@ use crate::*; -use KeyCode::{Char, Up, Down, Left, Right}; +use KeyCode::{Char, Up, Down, Left, Right, Enter}; use PhraseCommand::*; pub trait HasEditor { @@ -30,68 +30,46 @@ pub enum PhraseCommand { ToggleDirection, } +impl InputToCommand for PhraseCommand { + fn input_to_command (state: &PhraseEditorModel, from: &TuiInput) -> Option { + return state.to_editor_command(from) + } +} + impl PhraseEditorModel { fn to_editor_command (&self, from: &TuiInput) -> Option { - EventMap([ - (&key_expr!(Ctrl-Alt-Up), - &|q: &Self|SetNoteScroll(q.note_point() + 3)), - (&key_expr!(Ctrl-Alt-Down), - &|q: &Self|SetNoteScroll(q.note_point().saturating_sub(3))), - (&key_expr!(Ctrl-Alt-Left), - &|q: &Self|SetTimeScroll(q.time_point().saturating_sub(q.time_zoom().get()))), - (&key_expr!(Ctrl-Alt-Right), - &|q: &Self|SetTimeScroll((q.time_point() + q.time_zoom()) % q.phrase_length().get())), - (&key_expr!(Ctrl-Up), - &|q: &Self|SetNoteScroll(q.note_lo().get() + 1)), - (&key_expr!(Ctrl-Down), - &|q: &Self|SetNoteScroll(q.note_lo().get().saturating_sub(1))), - (&key_expr!(Ctrl-Left), - &|q: &Self|SetTimeScroll(q.time_start().get().saturating_sub(q.note_len()))), - (&key_expr!(Ctrl-Right), - &|q: &Self|SetTimeScroll(q.time_start().get() + q.note_len())), - (&key_expr!(Alt-Up), - &|q: &Self|SetNoteCursor(q.note_point().get() + 3)), - (&key_expr!(Alt-Down), - &|q: &Self|SetNoteCursor(q.note_point().get().saturating_sub(3))), - (&key_expr!(Alt-Left), - &|q: &Self|SetTimeCursor(q.time_point().get().saturating_sub(q.time_zoom()))), - (&key_expr!(Alt-Right), - &|q: &Self|SetTimeCursor((q.time_point().get() + q.time_zoom().get()) % q.phrase_length().get())), - (&key_expr!(Up), - &|q: &Self|SetNoteCursor(q.note_point().get() + 1)), - (&key_expr!(Down), - &|q: &Self|SetNoteCursor(q.note_point().get().saturating_sub(1))), - (&key_expr!(Left), - &|q: &Self|SetTimeCursor(q.time_point().get().saturating_sub(q.note_len()))), - (&key_expr!(Right), - &|q: &Self|SetTimeCursor((q.time_point().get() + q.note_len().get()) % q.phrase_length().get())), - (&key_pat!(Char('`')), - &|q: &Self|ToggleDirection), - (&key_pat!(Char('z')), - &|q: &Self|SetTimeLock(!q.time_lock().get())), - (&key_pat!(Char('-')), - &|q: &Self|SetTimeZoom(Note::next(q.time_zoom().get()))), - (&key_pat!(Char('_')), - &|q: &Self|SetTimeZoom(Note::next(q.time_zoom().get()))), - (&key_pat!(Char('=')), - &|q: &Self|SetTimeZoom(Note::prev(q.time_zoom().get()))), - (&key_pat!(Char('+')), - &|q: &Self|SetTimeZoom(Note::prev(q.time_zoom().get()))), - (&key_expr!(Enter), - &|q: &Self|PutNote), - (&key_expr!(Ctrl-Enter), - &|q: &Self|AppendNote), - (&key_pat!(Char(',')), - &|q: &Self|SetNoteLength(Note::prev(q.note_len().get()))), // TODO: no 3plet - (&key_pat!(Char('.')), - &|q: &Self|SetNoteLength(Note::next(q.note_len().get()))), - (&key_pat!(Char('<')), - &|q: &Self|SetNoteLength(Note::prev(q.note_len().get()))), // TODO: 3plet - (&key_pat!(Char('>')), - &|q: &Self|SetNoteLength(Note::next(q.note_len().get()))), + event_map!([ + (kexp!(Ctrl-Alt-Up), &|state: &Self|SetNoteScroll(state.note_point() + 3)), + (kexp!(Ctrl-Alt-Down), &|state: &Self|SetNoteScroll(state.note_point().saturating_sub(3))), + (kexp!(Ctrl-Alt-Left), &|state: &Self|SetTimeScroll(state.time_point().saturating_sub(state.time_zoom().get()))), + (kexp!(Ctrl-Alt-Right), &|state: &Self|SetTimeScroll((state.time_point() + state.time_zoom().get()) % state.phrase_length().get())), + (kexp!(Ctrl-Up), &|state: &Self|SetNoteScroll(state.note_lo().get() + 1)), + (kexp!(Ctrl-Down), &|state: &Self|SetNoteScroll(state.note_lo().get().saturating_sub(1))), + (kexp!(Ctrl-Left), &|state: &Self|SetTimeScroll(state.time_start().get().saturating_sub(state.note_len()))), + (kexp!(Ctrl-Right), &|state: &Self|SetTimeScroll(state.time_start().get() + state.note_len())), + (kexp!(Alt-Up), &|state: &Self|SetNoteCursor(state.note_point().get() + 3)), + (kexp!(Alt-Down), &|state: &Self|SetNoteCursor(state.note_point().get().saturating_sub(3))), + (kexp!(Alt-Left), &|state: &Self|SetTimeCursor(state.time_point().get().saturating_sub(state.time_zoom()))), + (kexp!(Alt-Right), &|state: &Self|SetTimeCursor((state.time_point().get() + state.time_zoom().get()) % state.phrase_length().get())), + (kexp!(Up), &|state: &Self|SetNoteCursor(state.note_point().get() + 1)), + (kexp!(Down), &|state: &Self|SetNoteCursor(state.note_point().get().saturating_sub(1))), + (kexp!(Left), &|state: &Self|SetTimeCursor(state.time_point().get().saturating_sub(state.note_len()))), + (kexp!(Right), &|state: &Self|SetTimeCursor((state.time_point().get() + state.note_len().get()) % state.phrase_length().get())), + (kexp!(Char('z')), &|state: &Self|SetTimeLock(!state.time_lock().get())), + (kexp!(Char('-')), &|state: &Self|SetTimeZoom(Note::next(state.time_zoom().get()))), + (kexp!(Char('_')), &|state: &Self|SetTimeZoom(Note::next(state.time_zoom().get()))), + (kexp!(Char('=')), &|state: &Self|SetTimeZoom(Note::prev(state.time_zoom().get()))), + (kexp!(Char('+')), &|state: &Self|SetTimeZoom(Note::prev(state.time_zoom().get()))), + (kexp!(Enter), &|state: &Self|PutNote), + (kexp!(Ctrl-Enter), &|state: &Self|AppendNote), + (kexp!(Char(',')), &|state: &Self|SetNoteLength(Note::prev(state.note_len().get()))), // TODO: no 3plet + (kexp!(Char('.')), &|state: &Self|SetNoteLength(Note::next(state.note_len().get()))), + (kexp!(Char('<')), &|state: &Self|SetNoteLength(Note::prev(state.note_len().get()))), // TODO: 3plet + (kexp!(Char('>')), &|state: &Self|SetNoteLength(Note::next(state.note_len().get()))), + //key_pat!(Char('`')) -> ToggleDirection, //// TODO: key_pat!(Char('/')) => // toggle 3plet //// TODO: key_pat!(Char('?')) => // toggle dotted - ]).handle(state, from.event()) + ]).handle(self, from.event()) } } diff --git a/crates/tek/src/tui/tui_input.rs b/crates/tek/src/tui/tui_input.rs index 9b4dae57..621f1111 100644 --- a/crates/tek/src/tui/tui_input.rs +++ b/crates/tek/src/tui/tui_input.rs @@ -60,6 +60,37 @@ impl Input for TuiInput { }; } +pub struct EventMap<'a, const N: usize, E, T, U>(pub [(E, &'a dyn Fn(T) -> U); N]); + +impl<'a, const N: usize, E: PartialEq, T, U> EventMap<'a, N, E, T, U> { + pub fn handle (&self, context: T, event: E) -> Option { + for (binding, handler) in self.0.iter() { + if event == *binding { + return Some(handler(context)) + } + } + return None + } +} + +#[macro_export] macro_rules! event_map { + ($events:expr) => { + EventMap($events) + }; + (|$state:ident|{$([$char:expr] = [$handler:expr]),*}) => { + EventMap([$((&$char, &|$state|$handler),),*]) + } +} + +#[macro_export] macro_rules! kexp { + (Ctrl-Alt-$code:ident) => { key_event_expr!($code, KeyModifiers::CONTROL | KeyModifiers::ALT) }; + (Ctrl-$code:ident) => { key_event_expr!($code, KeyModifiers::CONTROL) }; + (Alt-$code:ident) => { key_event_expr!($code, KeyModifiers::ALT) }; + (Shift-$code:ident) => { key_event_expr!($code, KeyModifiers::SHIFT) }; + ($code:ident) => { key_event_expr!($code) }; + ($code:expr) => { key_event_expr!($code) }; +} + #[macro_export] macro_rules! key_expr { (Ctrl-Alt-$code:ident) => { key_event_expr!($code, KeyModifiers::CONTROL | KeyModifiers::ALT) }; (Ctrl-$code:ident) => { key_event_expr!($code, KeyModifiers::CONTROL) }; @@ -69,7 +100,7 @@ impl Input for TuiInput { } #[macro_export] macro_rules! key_event_expr { - ($code:ident, $modifiers: expr) => { + ($code:expr, $modifiers: expr) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code, modifiers: $modifiers, @@ -77,7 +108,7 @@ impl Input for TuiInput { state: KeyEventState::NONE })) }; - ($code:ident) => { + ($code:expr) => { TuiEvent::Input(crossterm::event::Event::Key(KeyEvent { code: $code, modifiers: KeyModifiers::NONE, @@ -148,16 +179,3 @@ pub type KeyBinding = (KeyCode, KeyModifiers, &'static str, &'static str, Key pub type KeyMap = [KeyBinding]; */ - -pub struct EventMap<'a, const N: usize, E, T, U>(pub [(E, &'a dyn Fn(T) -> U); N]); - -impl<'a, const N: usize, E, T, U> EventMap<'a, N, E, T, U> { - pub fn handle (&self, context: T, event: E) -> Option { - for (binding, handler) in self.0.iter() { - if event == binding { - return Some(handler(context)) - } - } - return None - } -}