diff --git a/input/src/edn_keymap.rs b/input/src/edn_keymap.rs index 067ef9cf..877801b7 100644 --- a/input/src/edn_keymap.rs +++ b/input/src/edn_keymap.rs @@ -1,6 +1,38 @@ use crate::*; use EdnItem::*; +pub struct EdnKeyMapToCommand<'a>(Vec>); +impl<'a> EdnKeyMapToCommand<'a> { + /// Construct keymap from source text or fail + pub fn new (keymap: &'a str) -> Usually { + Ok(Self(EdnItem::<&str>::read_all(keymap)?)) + } + /// Try to find a binding matching the currently pressed key + pub fn from (&self, state: &T, input: &impl EdnInput) -> Option + where + C: Command + EdnCommand + { + use EdnItem::*; + let mut command: Option = None; + for item in self.0.iter() { + if let Exp(e) = item { + match e.as_slice() { + [Sym(key), c, args @ ..] if input.matches_edn(key) => { + command = C::from_edn(state, c, args); + if command.is_some() { + break + } + }, + _ => {} + } + } else { + panic!("invalid config") + } + } + command + } +} + pub struct EdnKeymap<'a>(pub Vec>); impl<'a> EdnKeymap<'a> { diff --git a/tek/src/keys.edn b/tek/src/keys.edn index 1f5d4d18..bd5aa985 100644 --- a/tek/src/keys.edn +++ b/tek/src/keys.edn @@ -1,8 +1,8 @@ -(:u undo 1) -(:shift-u redo 1) -(:space play/toggle) -(:shift-space play/start-toggle) -(:e editor/show :pool-phrase) -(:ctrl-a scene/add) -(:ctrl-t track/add) -(:tab pool/toggle) +(@u undo 1) +(@shift-u redo 1) +(@space play/toggle) +(@shift-space play/start-toggle) +(@e editor/show :pool-phrase) +(@ctrl-a scene add) +(@ctrl-t track add) +(@tab pool/toggle) diff --git a/tek/src/keys_clip.edn b/tek/src/keys_clip.edn index 0822d8a4..f7c63a9d 100644 --- a/tek/src/keys_clip.edn +++ b/tek/src/keys_clip.edn @@ -1,10 +1,10 @@ -(q :clip-launch) -(c :clip-color) -(g :clip-get) -(p :clip-put) -(del :clip-del) -(, :clip-prev) -(. :clip-next) -(< :clip-swap-prev) -(> :clip-swap-next) -(l :clip-loop-toggle) +(@q clip launch) +(@c clip color) +(@g clip get) +(@p clip put) +(@delete clip del) +(@comma clip prev) +(@period clip next) +(@lt clip swap-prev) +(@gt clip swap-next) +(@l clip loop-toggle) diff --git a/tek/src/keys_mix.edn b/tek/src/keys_mix.edn new file mode 100644 index 00000000..2126b683 --- /dev/null +++ b/tek/src/keys_mix.edn @@ -0,0 +1,2 @@ +(@down select 0 1) +(@right select 1 0) diff --git a/tek/src/keys_scene.edn b/tek/src/keys_scene.edn index 03866609..89a795e9 100644 --- a/tek/src/keys_scene.edn +++ b/tek/src/keys_scene.edn @@ -1,7 +1,7 @@ -(q :scene-launch) -(c :scene-color) -(, :scene-prev) -(. :scene-next) -(< :scene-swap-prev) -(> :scene-swap-next) -(del :scene-delete) +(@q scene launch) +(@c scene color) +(@comma scene prev) +(@period scene next) +(@lt scene swap-prev) +(@gt scene swap-next) +(@delete scene delete) diff --git a/tek/src/keys_track.edn b/tek/src/keys_track.edn index a756063b..623cc9e5 100644 --- a/tek/src/keys_track.edn +++ b/tek/src/keys_track.edn @@ -1,7 +1,7 @@ -(q :track-launch) -(c :track-color) -(, :track-prev) -(. :track-next) -(< :track-swap-prev) -(> :track-swap-next) -(del :track-delete) +(@q track launch) +(@c track color) +(@comma track prev) +(@period track next) +(@lt track swap-prev) +(@gt track swap-next) +(@delete track delete) diff --git a/tek/src/lib.rs b/tek/src/lib.rs index 9efddf6c..558224a1 100644 --- a/tek/src/lib.rs +++ b/tek/src/lib.rs @@ -45,6 +45,7 @@ pub use ::tek_tui::{ pub size: Measure, pub perf: PerfModel, pub compact: bool, + pub history: Vec, } has_size!(|self: App|&self.size); has_clock!(|self: App|&self.clock); @@ -373,37 +374,34 @@ impl App { } Ok(()) } - const KEYS_APP: &str = include_str!("keys.edn"); - const KEYS_CLIP: &str = include_str!("keys_clip.edn"); - const KEYS_TRACK: &str = include_str!("keys_track.edn"); - const KEYS_SCENE: &str = include_str!("keys_scene.edn"); } -handle!(TuiIn: |self: App, input|{ +const KEYS_APP: &str = include_str!("keys.edn"); +const KEYS_CLIP: &str = include_str!("keys_clip.edn"); +const KEYS_TRACK: &str = include_str!("keys_track.edn"); +const KEYS_SCENE: &str = include_str!("keys_scene.edn"); +const KEYS_MIX: &str = include_str!("keys_mix.edn"); +handle!(TuiIn: |self: App, input|Ok({ use EdnItem::*; let mut command: Option = None; - let edns: Vec> = EdnItem::read_all(Self::KEYS_APP)?; - for item in edns { - if let Exp(e) = item { - match e.as_slice() { - [Sym(key), c, args @ ..] if input.matches_edn(key) => { - command = AppCommand::from_edn(self, c, args); - if command.is_some() { - break - } - }, - _ => {} - } - } else { - panic!("invalid config") - } + // Handle from root keymap + if let Some(command) = EdnKeyMapToCommand::new(KEYS_APP)?.from::<_, AppCommand>(self, input) { + if let Some(undo) = command.execute(self)? { self.history.push(undo); } + return Ok(Some(true)) } - Ok(if let Some(command) = command { - let _undo = command.execute(self)?; - Some(true) - } else { - None - }) -}); + // Handle from selection-dependent keymaps + if let Some(command) = EdnKeyMapToCommand::new(match self.selected() { + Selection::Clip(t, s) => KEYS_CLIP, + Selection::Track(t) => KEYS_TRACK, + Selection::Scene(s) => KEYS_SCENE, + Selection::Mix => KEYS_MIX, + })?.from::<_, AppCommand>( + self, input + ) { + if let Some(undo) = command.execute(self)? { self.history.push(undo); } + return Ok(Some(true)) + } + None +})); #[derive(Clone, Debug)] pub enum AppCommand { Clip(ClipCommand), Clock(ClockCommand), @@ -427,7 +425,12 @@ edn_command!(AppCommand: |state: App| { ("undo" [d: usize] Self::History(-(d.unwrap_or(0)as isize))) ("redo" [d: usize] Self::History(d.unwrap_or(0) as isize)) ("zoom" [z: usize] Self::Zoom(z)) - ("select" [s: Selection] Self::Select(s.expect("no selection"))) + ("select" [t: usize, s: usize] match (t.expect("no track"), s.expect("no scene")) { + (0, 0) => Self::Select(Selection::Mix), + (t, 0) => Self::Select(Selection::Track(t - 1)), + (0, s) => Self::Select(Selection::Scene(s - 1)), + (t, s) => Self::Select(Selection::Clip(t - 1, s - 1)), + }) ("enqueue" [c: Arc>] Self::Enqueue(c)) ("clip" [a, ..b] Self::Clip( diff --git a/tui/src/tui_edn_keymap.rs b/tui/src/tui_edn_keymap.rs index e73ca37b..3ef606c5 100644 --- a/tui/src/tui_edn_keymap.rs +++ b/tui/src/tui_edn_keymap.rs @@ -21,7 +21,7 @@ impl KeyMatcher { let token = token.as_ref(); if token.len() < 2 { Self { valid: false, key: None, mods: KeyModifiers::NONE } - } else if token.chars().next() != Some(':') { + } else if token.chars().next() != Some('@') { Self { valid: false, key: None, mods: KeyModifiers::NONE } } else { Self { valid: true, key: None, mods: KeyModifiers::NONE }.next(&token[1..])