diff --git a/crates/tek_core/src/command.rs b/crates/tek_core/src/command.rs index 4883ed34..870681d5 100644 --- a/crates/tek_core/src/command.rs +++ b/crates/tek_core/src/command.rs @@ -25,25 +25,35 @@ pub const fn shift (key: KeyEvent) -> KeyEvent { KeyEvent { modifiers: key.modifiers.union(KeyModifiers::SHIFT), ..key } } -pub trait HandleKey + 'static>: Sized { +pub trait HandleKey + Clone + 'static>: Sized { const HANDLE_KEY_MAP: &'static [(KeyEvent, C)] = &[]; // FIXME: needs to be method - #[inline] fn match_input (from: &TuiInput) -> Option<&'static C> { + #[inline] fn match_input_static (from: &TuiInput) -> Option { if let TuiEvent::Input(crossterm::event::Event::Key(key)) = from.event() { return Self::match_key_static(&key) } None } - #[inline] fn match_key_static (key: &KeyEvent) -> Option<&'static C> { + #[inline] fn match_key_static (key: &KeyEvent) -> Option { for (binding, command) in Self::HANDLE_KEY_MAP.iter() { if key == binding { - return Some(command); + return Some(command.clone()); } } None } - #[inline] fn match_key (&self, key: &KeyEvent) -> Option<&'static C> { + #[inline] fn match_key (&self, key: &KeyEvent) -> Option { Self::match_key_static(key) } + #[inline] fn match_input (&self, input: &TuiInput) -> Option { + Self::match_input_static(input) + } + #[inline] fn handle_input (&mut self, input: &TuiInput) -> Perhaps { + if let Some(command) = self.match_input(input) { + command.run(self) + } else { + Ok(None) + } + } #[inline] fn handle_key (&mut self, key: &KeyEvent) -> Perhaps { if let Some(command) = self.match_key(key) { command.run(self) diff --git a/crates/tek_sequencer/src/sequencer_cmd.rs b/crates/tek_sequencer/src/sequencer_cmd.rs index 609240e6..1f4f6987 100644 --- a/crates/tek_sequencer/src/sequencer_cmd.rs +++ b/crates/tek_sequencer/src/sequencer_cmd.rs @@ -9,7 +9,7 @@ enum SequencerCommand { FocusLeft, FocusRight, Transport(TransportCommand), - Phrase(PhrasePoolCommand), + Phrases(PhrasePoolCommand), Editor(PhraseEditorCommand), } #[derive(Clone, PartialEq)] @@ -24,11 +24,11 @@ pub enum PhrasePoolCommand { Duplicate, RandomColor, Edit, - Name(PhraseNameCommand), + Rename(PhraseRenameCommand), Length(PhraseLengthCommand), } #[derive(Clone, PartialEq)] -pub enum PhraseNameCommand { +pub enum PhraseRenameCommand { Begin, Backspace, Append(char), @@ -67,84 +67,109 @@ pub enum PhraseEditorCommand { GoRight, } impl HandleKey for Sequencer { - const HANDLE_KEY_MAP: &'static [(KeyEvent, SequencerCommand)] = &[ - (key(KeyCode::Tab), SequencerCommand::FocusNext), - (shift(key(KeyCode::Tab)), SequencerCommand::FocusPrev), - (key(KeyCode::BackTab), SequencerCommand::FocusPrev), - (shift(key(KeyCode::BackTab)), SequencerCommand::FocusPrev), - (key(KeyCode::Up), SequencerCommand::FocusUp), - (key(KeyCode::Down), SequencerCommand::FocusDown), - (key(KeyCode::Left), SequencerCommand::FocusLeft), - (key(KeyCode::Right), SequencerCommand::FocusRight), - (key(KeyCode::Char(' ')), SequencerCommand::Transport(TransportCommand::TogglePlay)), // FIXME go through transport - ]; + fn match_input (&self, from: &TuiInput) -> Option { + match from.event() { + key!(KeyCode::Tab) => Some(SequencerCommand::FocusNext), + key!(Shift-KeyCode::Tab) => Some(SequencerCommand::FocusPrev), + key!(KeyCode::BackTab) => Some(SequencerCommand::FocusPrev), + key!(Shift-KeyCode::BackTab) => Some(SequencerCommand::FocusPrev), + key!(KeyCode::Up) => Some(SequencerCommand::FocusUp), + key!(KeyCode::Down) => Some(SequencerCommand::FocusDown), + key!(KeyCode::Left) => Some(SequencerCommand::FocusLeft), + key!(KeyCode::Right) => Some(SequencerCommand::FocusRight), + key!(KeyCode::Char(' ')) => Some(SequencerCommand::Transport( + TransportCommand::TogglePlay)), // FIXME go through transport + _ => match self.focused() { + SequencerFocus::Transport => self.transport.as_ref() + .map(|t|t.read().unwrap().match_input(from).map(SequencerCommand::Transport)) + .flatten(), + SequencerFocus::PhrasePool => self.phrases.read().unwrap() + .match_input(from) + .map(SequencerCommand::Phrases), + SequencerFocus::PhraseEditor => self.editor + .match_input(from) + .map(SequencerCommand::Editor), + } + } + } } impl HandleKey for PhrasePool { - const HANDLE_KEY_MAP: &'static [(KeyEvent, PhrasePoolCommand)] = &[ - (key(KeyCode::Up), PhrasePoolCommand::Previous), - (key(KeyCode::Down), PhrasePoolCommand::Next), - (key(KeyCode::Char(',')), PhrasePoolCommand::MoveUp), - (key(KeyCode::Char('.')), PhrasePoolCommand::MoveDown), - (key(KeyCode::Delete), PhrasePoolCommand::Delete), - (key(KeyCode::Char('a')), PhrasePoolCommand::Append), - (key(KeyCode::Char('i')), PhrasePoolCommand::Insert), - (key(KeyCode::Char('d')), PhrasePoolCommand::Duplicate), - (key(KeyCode::Char('c')), PhrasePoolCommand::RandomColor), - (key(KeyCode::Char('n')), PhrasePoolCommand::Name(PhraseNameCommand::Begin)), - (key(KeyCode::Char('t')), PhrasePoolCommand::Length(PhraseLengthCommand::Begin)), - ]; + fn match_input (&self, from: &TuiInput) -> Option { + match from.event() { + key!(KeyCode::Up) => Some(PhrasePoolCommand::Previous), + key!(KeyCode::Down) => Some(PhrasePoolCommand::Next), + key!(KeyCode::Char(',')) => Some(PhrasePoolCommand::MoveUp), + key!(KeyCode::Char('.')) => Some(PhrasePoolCommand::MoveDown), + key!(KeyCode::Delete) => Some(PhrasePoolCommand::Delete), + key!(KeyCode::Char('a')) => Some(PhrasePoolCommand::Append), + key!(KeyCode::Char('i')) => Some(PhrasePoolCommand::Insert), + key!(KeyCode::Char('d')) => Some(PhrasePoolCommand::Duplicate), + key!(KeyCode::Char('c')) => Some(PhrasePoolCommand::RandomColor), + key!(KeyCode::Char('n')) => Some(PhrasePoolCommand::Rename(PhraseRenameCommand::Begin)), + key!(KeyCode::Char('t')) => Some(PhrasePoolCommand::Length(PhraseLengthCommand::Begin)), + _ => match self.mode { + Some(PhrasePoolMode::Rename(..)) => HandleKey:: + ::match_input(self, from).map(PhrasePoolCommand::Rename), + Some(PhrasePoolMode::Length(..)) => HandleKey:: + ::match_input(self, from).map(PhrasePoolCommand::Length), + _ => None + } + } + } } -impl HandleKey for PhrasePool { - const HANDLE_KEY_MAP: &'static [(KeyEvent, PhraseNameCommand)] = &[ - (key(KeyCode::Backspace), PhraseNameCommand::Backspace), - (key(KeyCode::Enter), PhraseNameCommand::Confirm), - (key(KeyCode::Esc), PhraseNameCommand::Cancel), - ]; +impl HandleKey for PhrasePool { + fn match_input (&self, from: &TuiInput) -> Option { + match from.event() { + key!(KeyCode::Backspace) => Some(PhraseRenameCommand::Backspace), + key!(KeyCode::Enter) => Some(PhraseRenameCommand::Confirm), + key!(KeyCode::Esc) => Some(PhraseRenameCommand::Cancel), + key!(KeyCode::Char(c)) => Some(PhraseRenameCommand::Append(*c)), + _ => None + } + } } impl HandleKey for PhrasePool { - const HANDLE_KEY_MAP: &'static [(KeyEvent, PhraseLengthCommand)] = &[ - (key(KeyCode::Up), PhraseLengthCommand::Increment), - (key(KeyCode::Down), PhraseLengthCommand::Decrement), - (key(KeyCode::Right), PhraseLengthCommand::Next), - (key(KeyCode::Left), PhraseLengthCommand::Previous), - (key(KeyCode::Enter), PhraseLengthCommand::Confirm), - (key(KeyCode::Esc), PhraseLengthCommand::Cancel), - ]; + fn match_input (&self, from: &TuiInput) -> Option { + match from.event() { + key!(KeyCode::Up) => Some(PhraseLengthCommand::Increment), + key!(KeyCode::Down) => Some(PhraseLengthCommand::Decrement), + key!(KeyCode::Right) => Some(PhraseLengthCommand::Next), + key!(KeyCode::Left) => Some(PhraseLengthCommand::Previous), + key!(KeyCode::Enter) => Some(PhraseLengthCommand::Confirm), + key!(KeyCode::Esc) => Some(PhraseLengthCommand::Cancel), + _ => None + } + } } impl HandleKey for PhraseEditor { - const HANDLE_KEY_MAP: &'static [(KeyEvent, PhraseEditorCommand)] = &[ - (key(KeyCode::Char('`')), PhraseEditorCommand::ToggleDirection), - (key(KeyCode::Enter), PhraseEditorCommand::EnterEditMode), - (key(KeyCode::Esc), PhraseEditorCommand::ExitEditMode), - (key(KeyCode::Char('[')), PhraseEditorCommand::NoteLengthDecrement), - (key(KeyCode::Char(']')), PhraseEditorCommand::NoteLengthIncrement), - (key(KeyCode::Char('a')), PhraseEditorCommand::NoteAppend), - (key(KeyCode::Char('s')), PhraseEditorCommand::NoteSet), - (key(KeyCode::Char('-')), PhraseEditorCommand::TimeZoomOut), - (key(KeyCode::Char('_')), PhraseEditorCommand::TimeZoomOut), - (key(KeyCode::Char('=')), PhraseEditorCommand::TimeZoomIn), - (key(KeyCode::Char('+')), PhraseEditorCommand::TimeZoomIn), - (key(KeyCode::PageUp), PhraseEditorCommand::NotePageUp), - (key(KeyCode::PageDown), PhraseEditorCommand::NotePageDown), - (key(KeyCode::Up), PhraseEditorCommand::GoUp), - (key(KeyCode::Down), PhraseEditorCommand::GoDown), - (key(KeyCode::Left), PhraseEditorCommand::GoLeft), - (key(KeyCode::Right), PhraseEditorCommand::GoRight), - ]; + fn match_input (&self, from: &TuiInput) -> Option { + match from.event() { + key!(KeyCode::Char('`')) => Some(PhraseEditorCommand::ToggleDirection), + key!(KeyCode::Enter) => Some(PhraseEditorCommand::EnterEditMode), + key!(KeyCode::Esc) => Some(PhraseEditorCommand::ExitEditMode), + key!(KeyCode::Char('[')) => Some(PhraseEditorCommand::NoteLengthDecrement), + key!(KeyCode::Char(']')) => Some(PhraseEditorCommand::NoteLengthIncrement), + key!(KeyCode::Char('a')) => Some(PhraseEditorCommand::NoteAppend), + key!(KeyCode::Char('s')) => Some(PhraseEditorCommand::NoteSet), + key!(KeyCode::Char('-')) => Some(PhraseEditorCommand::TimeZoomOut), + key!(KeyCode::Char('_')) => Some(PhraseEditorCommand::TimeZoomOut), + key!(KeyCode::Char('=')) => Some(PhraseEditorCommand::TimeZoomIn), + key!(KeyCode::Char('+')) => Some(PhraseEditorCommand::TimeZoomIn), + key!(KeyCode::PageUp) => Some(PhraseEditorCommand::NotePageUp), + key!(KeyCode::PageDown) => Some(PhraseEditorCommand::NotePageDown), + key!(KeyCode::Up) => Some(PhraseEditorCommand::GoUp), + key!(KeyCode::Down) => Some(PhraseEditorCommand::GoDown), + key!(KeyCode::Left) => Some(PhraseEditorCommand::GoLeft), + key!(KeyCode::Right) => Some(PhraseEditorCommand::GoRight), + _ => None + } + } } /// Handle top-level events in standalone sequencer. impl Handle for Sequencer { fn handle (&mut self, from: &TuiInput) -> Perhaps { - let handled = match self.focused() { - SequencerFocus::Transport => self.transport.handle(from)?, - SequencerFocus::PhrasePool => self.phrases.handle(from)?, - SequencerFocus::PhraseEditor => self.editor.handle(from)? - }.unwrap_or(false); - if handled { - return Ok(Some(true)) - } - if let TuiEvent::Input(crossterm::event::Event::Key(key)) = from.event() { - let _undo = self.handle_key(key)?; + if let Some(command) = self.match_input(from) { + let _undo = command.run(self)?; return Ok(Some(true)) } Ok(None) @@ -152,34 +177,17 @@ impl Handle for Sequencer { } impl Handle for PhrasePool { fn handle (&mut self, from: &TuiInput) -> Perhaps { - if let TuiEvent::Input(crossterm::event::Event::Key(key)) = from.event() { - match self.mode { - Some(PhrasePoolMode::Rename(..)) => { - if HandleKey::::match_key_static(key).is_some() { - let _undo = HandleKey::::handle_key(self, key)?; - return Ok(Some(true)) - } else if let KeyEvent { code: KeyCode::Char(c), .. } = key { - PhraseNameCommand::Append(*c).run(self)?; - return Ok(Some(true)) - } - }, - Some(PhrasePoolMode::Length(..)) => { - let _undo = HandleKey::::handle_key(self, key)?; - return Ok(Some(true)) - }, - None => { - let _undo = HandleKey::::handle_key(self, key)?; - return Ok(Some(true)) - } - } + if let Some(command) = HandleKey::::match_input(self, from) { + let _undo = command.run(self)?; + return Ok(Some(true)) } Ok(None) } } impl Handle for PhraseEditor { fn handle (&mut self, from: &TuiInput) -> Perhaps { - if let TuiEvent::Input(crossterm::event::Event::Key(key)) = from.event() { - let _undo = self.handle_key(key)?; + if let Some(command) = self.match_input(from) { + let _undo = command.run(self)?; return Ok(Some(true)) } Ok(None) @@ -188,35 +196,21 @@ impl Handle for PhraseEditor { impl Command> for SequencerCommand { fn run (&self, state: &mut Sequencer) -> Perhaps { match self { - Self::FocusNext => { - state.focus_next(); + Self::FocusNext => { state.focus_next(); }, + Self::FocusPrev => { state.focus_prev(); }, + Self::FocusUp => { state.focus_up(); }, + Self::FocusDown => { state.focus_down(); }, + Self::FocusLeft => { state.focus_left(); }, + Self::FocusRight => { state.focus_right(); }, + Self::Transport(command) => if let Some(ref transport) = state.transport { + return command + .run(&mut*transport.write().unwrap()) + .map(|x|x.map(SequencerCommand::Transport)) }, - Self::FocusPrev => { - state.focus_prev(); - }, - Self::FocusUp => { - state.focus_up(); - }, - Self::FocusDown => { - state.focus_down(); - }, - Self::FocusLeft => { - state.focus_left(); - }, - Self::FocusRight => { - state.focus_right(); - }, - Self::Transport(command) => { - if let Some(ref transport) = state.transport { - return command - .run(&mut*transport.write().unwrap()) - .map(|x|x.map(SequencerCommand::Transport)) - } - }, - Self::Phrase(command) => { + Self::Phrases(command) => { return command .run(&mut*state.phrases.write().unwrap()) - .map(|x|x.map(SequencerCommand::Phrase)) + .map(|x|x.map(SequencerCommand::Phrases)) }, Self::Editor(command) => { return command @@ -260,10 +254,10 @@ impl Command> for PhrasePoolCommand { Self::MoveDown => { state.move_down() }, - Self::Name(PhraseNameCommand::Begin) => { + Self::Rename(PhraseRenameCommand::Begin) => { state.begin_rename() }, - Self::Name(_) => { + Self::Rename(_) => { unreachable!() }, Self::Length(PhraseLengthCommand::Begin) => { @@ -276,7 +270,7 @@ impl Command> for PhrasePoolCommand { Ok(None) } } -impl Command> for PhraseNameCommand { +impl Command> for PhraseRenameCommand { fn run (&self, state: &mut PhrasePool) -> Perhaps { if let Some(PhrasePoolMode::Rename(phrase, ref mut old_name)) = state.mode { match self {