//! Handling of input events. use crate::{core::*, handle, App, AppFocus}; handle!{ App |self, e| { if handle_modal(self, e)? { return Ok(true) } Ok(if self.entered { handle_focused(self, e)? || handle_keymap(self, e, KEYMAP_GLOBAL)? || handle_keymap(self, e, crate::control::KEYMAP_FOCUS)? } else { handle_keymap(self, e, KEYMAP_GLOBAL)? || handle_keymap(self, e, crate::control::KEYMAP_FOCUS)? || handle_focused(self, e)? }) } } fn handle_modal (state: &mut App, e: &AppEvent) -> Usually { if let Some(ref mut modal) = state.modal { if modal.handle(e)? { if modal.exited() { state.modal = None; } return Ok(true) }; } Ok(false) } fn handle_focused (state: &mut App, e: &AppEvent) -> Usually { match state.section { AppFocus::Transport => handle_keymap(state, e, crate::devices::transport::KEYMAP_TRANSPORT), AppFocus::Arranger => handle_keymap(state, e, crate::devices::arranger::KEYMAP_ARRANGER), AppFocus::Sequencer => handle_keymap(state, e, crate::devices::sequencer::KEYMAP_SEQUENCER), AppFocus::Chain => Ok(if state.entered { handle_device(state, e)? || handle_keymap(state, e, crate::control::KEYMAP_CHAIN)? } else { handle_keymap(state, e, crate::control::KEYMAP_CHAIN)? || handle_device(state, e)? }) } } fn handle_device (state: &mut App, e: &AppEvent) -> Usually { state.arranger.track() .and_then(|track|track.device_mut()) .map(|mut device|device.handle(e)) .transpose() .map(|x|x.unwrap_or(false)) } /// Global key bindings. pub const KEYMAP_GLOBAL: &'static [KeyBinding] = keymap!(App { [Char(' '), NONE, "play_toggle", "play or pause", |app: &mut App| { app.transport.toggle_play()?; Ok(true) }], [Char('r'), NONE, "record_toggle", "toggle recording", |app: &mut App| { app.arranger.track_mut().map(|t|t.toggle_record()); Ok(true) }], [Char('o'), NONE, "overdub_toggle", "toggle overdub", |app: &mut App| { app.arranger.track_mut().map(|t|t.toggle_overdub()); Ok(true) }], [Char('m'), NONE, "monitor_toggle", "toggle monitor", |app: &mut App| { app.arranger.track_mut().map(|t|t.toggle_monitor()); Ok(true) }], [Char('+'), NONE, "quant_inc", "quantize coarser", |app: &mut App| { app.transport.quant = next_note_length(app.transport.quant); Ok(true) }], [Char('_'), NONE, "quant_dec", "quantize finer", |app: &mut App| { app.transport.quant = prev_note_length(app.transport.quant); Ok(true) }], [Char('='), NONE, "zoom_in", "show fewer ticks per block", |app: &mut App| { app.sequencer.time_axis.scale_mut(&prev_note_length); Ok(true) }], [Char('-'), NONE, "zoom_out", "show more ticks per block", |app: &mut App| { app.sequencer.time_axis.scale_mut(&next_note_length); Ok(true) }], [Char('x'), NONE, "extend", "double the current clip", |app: &mut App| { app.arranger.phrase().map(|x|x.write().unwrap()).map(|mut phrase|{ let mut notes = phrase.notes.clone(); notes.extend_from_slice(&mut phrase.notes); phrase.notes = notes; phrase.length = phrase.length * 2; }); app.sequencer.show(app.arranger.phrase())?; Ok(true) }], [Char('l'), NONE, "loop_toggle", "toggle looping", |_app: &mut App| { // TODO: This toggles the loop flag for the clip under the cursor. Ok(true) }], [Char('['), NONE, "loop_start_dec", "move loop start back", |_app: &mut App| { // TODO: This moves the loop start to the previous quant. Ok(true) }], [Char(']'), NONE, "loop_start_inc", "move loop start forward", |_app: &mut App| { // TODO: This moves the loop start to the next quant. Ok(true) }], [Char('{'), NONE, "loop_end_dec", "move loop end back", |_app: &mut App| { // TODO: This moves the loop end to the previous quant. Ok(true) }], [Char('}'), NONE, "loop_end_inc", "move loop end forward", |_app: &mut App| { // TODO: This moves the loop end to the next quant. Ok(true) }], [Char('a'), CONTROL, "scene_add", "add a new scene", |app: &mut App| { app.arranger.scene_add(None)?; Ok(true) }], [Char('t'), CONTROL, "track_add", "add a new track", |app: &mut App| { app.arranger.track_add(None)?; Ok(true) }], }); /// Key bindings for chain section. pub const KEYMAP_CHAIN: &'static [KeyBinding] = keymap!(App { [Up, NONE, "chain_cursor_up", "move cursor up", |_: &mut App| { Ok(true) }], [Down, NONE, "chain_cursor_down", "move cursor down", |_: &mut App| { Ok(true) }], [Left, NONE, "chain_cursor_left", "move cursor left", |app: &mut App| { if let Some(track) = app.arranger.track_mut() { track.device = track.device.saturating_sub(1); return Ok(true) } Ok(false) }], [Right, NONE, "chain_cursor_right", "move cursor right", |app: &mut App| { if let Some(track) = app.arranger.track_mut() { track.device = (track.device + 1).min(track.devices.len().saturating_sub(1)); return Ok(true) } Ok(false) }], [Char('`'), NONE, "chain_mode_switch", "switch the display mode", |app: &mut App| { app.chain_mode = !app.chain_mode; Ok(true) }], }); /// Generic key bindings for views that support focus. pub const KEYMAP_FOCUS: &'static [KeyBinding] = keymap!(App { [Char(';'), NONE, "command", "open command palette", |app: &mut App| { app.modal = Some(Box::new(crate::view::HelpModal::new())); Ok(true) }], [Tab, NONE, "focus_next", "focus next area", focus_next], [Tab, SHIFT, "focus_prev", "focus previous area", focus_prev], [Esc, NONE, "focus_exit", "unfocus", |app: &mut App|{ app.entered = false; app.transport.entered = app.entered; app.arranger.entered = app.entered; app.sequencer.entered = app.entered; Ok(true) }], [Enter, NONE, "focus_enter", "activate item at cursor", |app: &mut App|{ app.entered = true; app.transport.entered = app.entered; app.arranger.entered = app.entered; app.sequencer.entered = app.entered; Ok(true) }], }); pub fn focus_next (app: &mut App) -> Usually { app.section.next(); app.transport.focused = app.section == AppFocus::Transport; app.transport.entered = app.entered; app.arranger.focused = app.section == AppFocus::Arranger; app.arranger.entered = app.entered; app.sequencer.focused = app.section == AppFocus::Sequencer; app.sequencer.entered = app.entered; Ok(true) } pub fn focus_prev (app: &mut App) -> Usually { app.section.prev(); app.transport.focused = app.section == AppFocus::Transport; app.transport.entered = app.entered; app.arranger.focused = app.section == AppFocus::Arranger; app.arranger.entered = app.entered; app.sequencer.focused = app.section == AppFocus::Sequencer; app.sequencer.entered = app.entered; Ok(true) }