diff --git a/crates/tek_core/src/command.rs b/crates/tek_core/src/command.rs new file mode 100644 index 00000000..d2653176 --- /dev/null +++ b/crates/tek_core/src/command.rs @@ -0,0 +1,44 @@ +use crate::*; + +pub const fn key (code: KeyCode) -> KeyEvent { + KeyEvent { + code, + modifiers: KeyModifiers::NONE, + kind: crossterm::event::KeyEventKind::Press, + state: crossterm::event::KeyEventState::NONE + } +} + +pub const fn ctrl (key: KeyEvent) -> KeyEvent { + KeyEvent { modifiers: key.modifiers.union(KeyModifiers::CONTROL), ..key } +} + +pub const fn alt (key: KeyEvent) -> KeyEvent { + KeyEvent { modifiers: key.modifiers.union(KeyModifiers::ALT), ..key } +} + +pub const fn shift (key: KeyEvent) -> KeyEvent { + KeyEvent { modifiers: key.modifiers.union(KeyModifiers::SHIFT), ..key } +} + +pub trait Command: Sized { + fn run (&self, state: &mut T) -> Perhaps; +} + +pub trait HandleKey + 'static>: Sized { + const HANDLE_KEY_MAP: &'static [(KeyEvent, C)]; + fn handle_key (&mut self, key: &KeyEvent) -> Perhaps { + let mut run_command: Option<&'static C> = None; + for (binding, command) in Self::HANDLE_KEY_MAP.iter() { + if key == binding { + run_command = Some(command); + break + } + } + if let Some(command) = run_command { + command.run(self) + } else { + Ok(None) + } + } +} diff --git a/crates/tek_core/src/lib.rs b/crates/tek_core/src/lib.rs index 8f132bf8..6a8bc071 100644 --- a/crates/tek_core/src/lib.rs +++ b/crates/tek_core/src/lib.rs @@ -38,6 +38,7 @@ use std::fmt::{Debug, Display}; submod! { audio color + command edn engine focus diff --git a/crates/tek_sequencer/src/sequencer_cmd.rs b/crates/tek_sequencer/src/sequencer_cmd.rs index 7d9ee33c..21cbb653 100644 --- a/crates/tek_sequencer/src/sequencer_cmd.rs +++ b/crates/tek_sequencer/src/sequencer_cmd.rs @@ -1,4 +1,26 @@ use crate::*; + +enum SequencerCommand { + FocusNext, + FocusPrev, + FocusUp, + FocusDown, + FocusLeft, + FocusRight, + Transport(TransportCommand), + Phrase(PhrasePoolCommand), + Editor(PhraseEditorCommand), +} + +enum PhrasePoolCommand { +} + +enum PhraseLengthCommand { +} + +enum PhraseEditorCommand { +} + /// Handle top-level events in standalone sequencer. impl Handle for Sequencer { fn handle (&mut self, from: &TuiInput) -> Perhaps { diff --git a/crates/tek_sequencer/src/transport_cmd.rs b/crates/tek_sequencer/src/transport_cmd.rs index 8b22a0a7..8523fb4d 100644 --- a/crates/tek_sequencer/src/transport_cmd.rs +++ b/crates/tek_sequencer/src/transport_cmd.rs @@ -1,55 +1,172 @@ use crate::*; + impl Handle for TransportToolbar { fn handle (&mut self, from: &TuiInput) -> Perhaps { - match from.event() { - key!(KeyCode::Left) => { self.focus.prev(); }, - key!(KeyCode::Right) => { self.focus.next(); }, - _ => match self.focus { - TransportToolbarFocus::PlayPause => return self.handle_play_pause(from), - TransportToolbarFocus::Bpm => return self.handle_bpm(from), - TransportToolbarFocus::Quant => return self.handle_quant(from), - TransportToolbarFocus::Sync => return self.handle_sync(from), - TransportToolbarFocus::Clock => {/*todo*/}, - } + if let TuiEvent::Input(crossterm::event::Event::Key(event)) = from.event() { + let _undo = self.handle_key(event)?; + return Ok(Some(true)) } - Ok(Some(true)) + Ok(None) } } -impl TransportToolbar { - fn handle_play_pause (&mut self, from: &TuiInput) -> Perhaps { - match from.event() { - key!(KeyCode::Enter) => self.toggle_play().map(|_|())?, - _ => return Ok(None) + +#[derive(Clone)] +pub enum TransportCommand { + FocusNext, + FocusPrev, + PlayFromStart, + TogglePlay, + Increment, + Decrement, + FineIncrement, + FineDecrement, + SeekUsec(f64), + SeekSample(f64), + SeekPulse(f64), + SetBpm(f64), + SetQuant(f64), + SetSync(f64), +} + +impl HandleKey for TransportToolbar { + const HANDLE_KEY_MAP: &'static [(KeyEvent, TransportCommand)] = &[ + ( key(KeyCode::Char(' ')), TransportCommand::FocusPrev), + (shift(key(KeyCode::Char(' '))), TransportCommand::FocusPrev), + ( key(KeyCode::Left), TransportCommand::FocusPrev), + ( key(KeyCode::Right), TransportCommand::FocusNext), + ( key(KeyCode::Char('.')), TransportCommand::Increment), + ( key(KeyCode::Char(',')), TransportCommand::Decrement), + ( key(KeyCode::Char('>')), TransportCommand::FineIncrement), + ( key(KeyCode::Char('<')), TransportCommand::FineDecrement), + ]; +} + +impl Command> for TransportCommand { + fn run (&self, state: &mut TransportToolbar) -> Perhaps { + match self { + Self::FocusNext => { + state.focus.next(); + }, + Self::FocusPrev => { + state.focus.prev(); + }, + Self::TogglePlay => { + state.toggle_play()?; + }, + Self::Increment => { + match state.focus { + TransportToolbarFocus::Bpm => { + let bpm = state.clock.timebase().bpm.get(); + return Self::SetBpm(bpm + 1.0).run(state) + }, + TransportToolbarFocus::Quant => { + let quant = state.clock.quant.get() as usize; + let quant = next_note_length(quant) as f64; + return Self::SetQuant(quant).run(state) + }, + TransportToolbarFocus::Sync => { + let sync = state.clock.sync.get() as usize; + let sync = next_note_length(sync) as f64; + return Self::SetSync(sync + 1.0).run(state) + }, + TransportToolbarFocus::PlayPause => { + /*todo seek*/ + }, + TransportToolbarFocus::Clock => { + /*todo seek*/ + }, + } + }, + Self::FineIncrement => { + match state.focus { + TransportToolbarFocus::Bpm => { + let bpm = state.clock.timebase().bpm.get(); + return Self::SetBpm(bpm + 0.001).run(state) + }, + TransportToolbarFocus::Quant => { + return Self::Increment.run(state) + }, + TransportToolbarFocus::Sync => { + return Self::Increment.run(state) + }, + TransportToolbarFocus::PlayPause => { + /*todo seek*/ + }, + TransportToolbarFocus::Clock => { + /*todo seek*/ + }, + } + }, + Self::Decrement => { + match state.focus { + TransportToolbarFocus::Bpm => { + let bpm = state.clock.timebase().bpm.get(); + return Self::SetBpm(bpm - 1.0).run(state) + }, + TransportToolbarFocus::Quant => { + let quant = state.clock.quant.get() as usize; + let quant = prev_note_length(quant) as f64; + return Self::SetQuant(quant).run(state) + }, + TransportToolbarFocus::Sync => { + let sync = state.clock.sync.get() as usize; + let sync = prev_note_length(sync) as f64; + return Self::SetSync(sync).run(state) + }, + TransportToolbarFocus::PlayPause => { + /*todo seek*/ + }, + TransportToolbarFocus::Clock => { + /*todo seek*/ + }, + } + }, + Self::FineDecrement => { + match state.focus { + TransportToolbarFocus::Bpm => { + let bpm = state.clock.timebase().bpm.get(); + return Self::SetBpm(bpm - 0.001).run(state) + }, + TransportToolbarFocus::Quant => { + return Self::Decrement.run(state) + }, + TransportToolbarFocus::Sync => { + return Self::Decrement.run(state) + }, + TransportToolbarFocus::PlayPause => { + /*todo seek*/ + }, + TransportToolbarFocus::Clock => { + /*todo seek*/ + }, + } + }, + Self::SeekUsec(usec) => { + state.clock.current.update_from_usec(*usec); + }, + Self::SeekSample(sample) => { + state.clock.current.update_from_sample(*sample); + }, + Self::SeekPulse(pulse) => { + state.clock.current.update_from_pulse(*pulse); + }, + Self::SetBpm(bpm) => { + let old_bpm = state.clock.timebase().bpm.get(); + state.clock.timebase().bpm.set(*bpm); + return Ok(Some(Self::SetBpm(old_bpm))) + }, + Self::SetQuant(quant) => { + let old_quant = state.clock.quant.get(); + state.clock.quant.set(*quant); + return Ok(Some(Self::SetQuant(old_quant))) + }, + Self::SetSync(sync) => { + let old_sync = state.clock.sync.get(); + state.clock.sync.set(*sync); + return Ok(Some(Self::SetSync(old_sync))) + }, + _ => { unimplemented!() } } - Ok(Some(true)) - } - fn handle_bpm (&mut self, from: &TuiInput) -> Perhaps { - let bpm = self.clock.timebase().bpm.get(); - match from.event() { - key!(KeyCode::Char(',')) => { self.clock.timebase().bpm.set(bpm - 1.0); }, - key!(KeyCode::Char('.')) => { self.clock.timebase().bpm.set(bpm + 1.0); }, - key!(KeyCode::Char('<')) => { self.clock.timebase().bpm.set(bpm - 0.001); }, - key!(KeyCode::Char('>')) => { self.clock.timebase().bpm.set(bpm + 0.001); }, - _ => return Ok(None) - } - Ok(Some(true)) - } - fn handle_quant (&mut self, from: &TuiInput) -> Perhaps { - let quant = self.clock.quant.get() as usize; - match from.event() { - key!(KeyCode::Char(',')) => { self.clock.quant.set(prev_note_length(quant) as f64); }, - key!(KeyCode::Char('.')) => { self.clock.quant.set(next_note_length(quant) as f64); }, - _ => return Ok(None) - } - return Ok(Some(true)) - } - fn handle_sync (&mut self, from: &TuiInput) -> Perhaps { - let sync = self.clock.sync.get() as usize; - match from.event() { - key!(KeyCode::Char(',')) => { self.clock.quant.set(prev_note_length(sync) as f64); }, - key!(KeyCode::Char('.')) => { self.clock.quant.set(next_note_length(sync) as f64); }, - _ => return Ok(None) - } - return Ok(Some(true)) + Ok(None) } }