From 6ed06270567f3a0bfee65e6de710d1f75c565a89 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Fri, 2 May 2025 18:56:49 +0300 Subject: [PATCH] app: wrap keys and view in Configuration --- crates/app/src/api.rs | 3 +- crates/app/src/model.rs | 21 +++-- crates/app/src/view.rs | 4 +- crates/cli/tek.rs | 168 ++++++++++++++++++++-------------------- 4 files changed, 102 insertions(+), 94 deletions(-) diff --git a/crates/app/src/api.rs b/crates/app/src/api.rs index 45918077..08e4b5a0 100644 --- a/crates/app/src/api.rs +++ b/crates/app/src/api.rs @@ -75,8 +75,7 @@ provide!(usize: |self: MidiEditor| { ":time-zoom-prev" => self.time_zoom().get().saturating_sub(1).max(1), }); - -handle!(TuiIn: |self: Tek, input|Ok(if let Some(command) = self.keys.command(self, input) { +handle!(TuiIn: |self: Tek, input|Ok(if let Some(command) = self.config.keys.command(self, input) { let undo = command.execute(self)?; if let Some(undo) = undo { self.history.push(undo); diff --git a/crates/app/src/model.rs b/crates/app/src/model.rs index d3e0b8d0..06529e66 100644 --- a/crates/app/src/model.rs +++ b/crates/app/src/model.rs @@ -48,14 +48,12 @@ pub struct Tek { pub history: Vec, /// Port handles pub ports: std::collections::BTreeMap>, - /// View definition - pub view: SourceIter<'static>, // Cache of formatted strings pub view_cache: Arc>, // Modal overlay pub modal: Option, - // Input keymap - pub keys: InputMap<'static, Self, TekCommand, TuiIn, TokenIter<'static>> + // View and input definition + pub config: Configuration } impl Tek { @@ -421,6 +419,15 @@ pub trait HasSelection { fn selected_mut (&mut self) -> &mut Selection; } +/// Configuration +#[derive(Default, Debug)] +pub struct Configuration { + /// View definition + pub view: SourceIter<'static>, + // Input keymap + pub keys: InputMap<'static, Tek, TekCommand, TuiIn, TokenIter<'static>> +} + /// Various possible modal overlays #[derive(PartialEq, Clone, Copy, Debug)] pub enum Modal { @@ -442,11 +449,11 @@ pub enum Selection { /// A track is selected. Track(usize), /// A clip (track × scene) is selected. - TrackClip { track: usize, scene: usize }, + TrackClip { track: usize, scene: usize }, /// A track's MIDI input connection is selected. - TrackInput { track: usize, port: usize }, + TrackInput { track: usize, port: usize }, /// A track's MIDI output connection is selected. - TrackOutput { track: usize, port: usize }, + TrackOutput { track: usize, port: usize }, /// A track device slot is selected. TrackDevice { track: usize, device: usize }, } diff --git a/crates/app/src/view.rs b/crates/app/src/view.rs index 5414c5bf..c3b8a907 100644 --- a/crates/app/src/view.rs +++ b/crates/app/src/view.rs @@ -2,7 +2,7 @@ use crate::*; pub(crate) use std::fmt::Write; pub(crate) use ::tengri::tui::ratatui::prelude::Position; -view!(TuiOut: |self: Tek| self.size.of(View(self, self.view)); { +view!(TuiOut: |self: Tek| self.size.of(View(self, self.config.view)); { ":nil" => Box::new("nil"), ":modal" => self.view_modal(), ":status" => self.view_status(), @@ -42,7 +42,7 @@ impl Tek { } fn view_modal_help (&self) -> impl Content + use<'_> { - let bindings = ||self.keys.layers.iter() + let bindings = ||self.config.keys.layers.iter() .filter_map(|a|(a.0)(self).then_some(a.1)) .flat_map(|a|a) .filter_map(|x|if let Value::Exp(_, iter)=x.value{ diff --git a/crates/cli/tek.rs b/crates/cli/tek.rs index 09f4e58f..eafb60ca 100644 --- a/crates/cli/tek.rs +++ b/crates/cli/tek.rs @@ -105,89 +105,6 @@ impl Cli { jack: jack.clone(), color: ItemTheme::random(), clock: Clock::new(jack, self.bpm)?, - view: SourceIter(match mode { - LaunchMode::Clock => - include_str!("../../config/view_transport.edn"), - LaunchMode::Sequencer => - include_str!("../../config/view_sequencer.edn"), - LaunchMode::Groovebox => - include_str!("../../config/view_groovebox.edn"), - LaunchMode::Arranger { .. } => - include_str!("../../config/view_arranger.edn"), - LaunchMode::Sampler => - include_str!("../../config/view_sampler.edn"), - _ => todo!("{mode:?}"), - }), - keys: match mode { - LaunchMode::Sampler => InputMap::default() - .layer(SourceIter(include_str!("../../config/keys_global.edn")).into()) - .layer(SourceIter(include_str!("../../config/keys_sampler.edn")).into()), - LaunchMode::Clock => InputMap::default() - .layer(SourceIter(include_str!("../../config/keys_global.edn")).into()) - .layer(SourceIter(include_str!("../../config/keys_clock.edn")).into()), - LaunchMode::Sequencer => InputMap::default() - .layer_if(|state: &Tek|matches!( - state.pool.as_ref().map(|p|p.mode.as_ref()).flatten(), - Some(PoolMode::Import(..))|Some(PoolMode::Export(..)) - ), SourceIter(include_str!("../../config/keys_pool_file.edn")).into()) - .layer_if(|state: &Tek|matches!( - state.pool.as_ref().map(|p|p.mode.as_ref()).flatten(), - Some(PoolMode::Rename(..)) - ), SourceIter(include_str!("../../config/keys_clip_rename.edn")).into()) - .layer_if(|state: &Tek|matches!( - state.pool.as_ref().map(|p|p.mode.as_ref()).flatten(), - Some(PoolMode::Length(..)) - ), SourceIter(include_str!("../../config/keys_clip_length.edn")).into()) - .layer(SourceIter(include_str!("../../config/keys_global.edn")).into()) - .layer(SourceIter(include_str!("../../config/keys_editor.edn")).into()) - .layer(SourceIter(include_str!("../../config/keys_clock.edn")).into()) - .layer(SourceIter(include_str!("../../config/keys_sequencer.edn")).into()), - LaunchMode::Groovebox => InputMap::default() - .layer_if(|state: &Tek|matches!( - state.pool.as_ref().map(|p|p.mode.as_ref()).flatten(), - Some(PoolMode::Import(..))|Some(PoolMode::Export(..)) - ), SourceIter(include_str!("../../config/keys_pool_file.edn")).into()) - .layer_if(|state: &Tek|matches!( - state.pool.as_ref().map(|p|p.mode.as_ref()).flatten(), - Some(PoolMode::Rename(..)) - ), SourceIter(include_str!("../../config/keys_clip_rename.edn")).into()) - .layer_if(|state: &Tek|matches!( - state.pool.as_ref().map(|p|p.mode.as_ref()).flatten(), - Some(PoolMode::Length(..)) - ), SourceIter(include_str!("../../config/keys_clip_length.edn")).into()) - .layer(SourceIter(include_str!("../../config/keys_global.edn")).into()) - .layer(SourceIter(include_str!("../../config/keys_editor.edn")).into()) - .layer(SourceIter(include_str!("../../config/keys_clock.edn")).into()) - .layer(SourceIter(include_str!("../../config/keys_sequencer.edn")).into()) - .layer(SourceIter(include_str!("../../config/keys_groovebox.edn")).into()), - LaunchMode::Arranger {..} => InputMap::default() - .layer_if(|state: &Tek|matches!( - state.pool.as_ref().map(|p|p.mode.as_ref()).flatten(), - Some(PoolMode::Import(..))|Some(PoolMode::Export(..)) - ), SourceIter(include_str!("../../config/keys_pool_file.edn")).into()) - .layer_if(|state: &Tek|matches!( - state.pool.as_ref().map(|p|p.mode.as_ref()).flatten(), - Some(PoolMode::Rename(..)) - ), SourceIter(include_str!("../../config/keys_clip_rename.edn")).into()) - .layer_if(|state: &Tek|matches!( - state.pool.as_ref().map(|p|p.mode.as_ref()).flatten(), - Some(PoolMode::Length(..)) - ), SourceIter(include_str!("../../config/keys_clip_length.edn")).into()) - .layer(SourceIter(include_str!("../../config/keys_global.edn")).into()) - .layer_if(|state: &Tek|state.is_editing(), - SourceIter(include_str!("../../config/keys_editor.edn")).into()) - .layer_if(|state: &Tek|state.selected.is_clip()&&!state.is_editing(), - SourceIter(include_str!("../../config/keys_clip.edn")).into()) - .layer_if(|state: &Tek|state.selected.is_track()&&!state.is_editing(), - SourceIter(include_str!("../../config/keys_track.edn")).into()) - .layer_if(|state: &Tek|state.selected.is_scene()&&!state.is_editing(), - SourceIter(include_str!("../../config/keys_scene.edn")).into()) - .layer_if(|state: &Tek|state.selected.is_mix()&&!state.is_editing(), - SourceIter(include_str!("../../config/keys_mix.edn")).into()) - .layer(SourceIter(include_str!("../../config/keys_clock.edn")).into()) - .layer(SourceIter(include_str!("../../config/keys_arranger.edn")).into()), - _ => todo!("{mode:?}"), - }, pool: match mode { LaunchMode::Sequencer | LaunchMode::Groovebox => clip.as_ref().map(Into::into), LaunchMode::Arranger { .. } => Some(Default::default()), @@ -222,6 +139,91 @@ impl Cli { }, scenes, selected: Selection::TrackClip { track: 0, scene: 0 }, + config: Configuration { + view: SourceIter(match mode { + LaunchMode::Clock => + include_str!("../../config/view_transport.edn"), + LaunchMode::Sequencer => + include_str!("../../config/view_sequencer.edn"), + LaunchMode::Groovebox => + include_str!("../../config/view_groovebox.edn"), + LaunchMode::Arranger { .. } => + include_str!("../../config/view_arranger.edn"), + LaunchMode::Sampler => + include_str!("../../config/view_sampler.edn"), + _ => todo!("{mode:?}"), + }), + keys: match mode { + LaunchMode::Sampler => InputMap::default() + .layer(SourceIter(include_str!("../../config/keys_global.edn")).into()) + .layer(SourceIter(include_str!("../../config/keys_sampler.edn")).into()), + LaunchMode::Clock => InputMap::default() + .layer(SourceIter(include_str!("../../config/keys_global.edn")).into()) + .layer(SourceIter(include_str!("../../config/keys_clock.edn")).into()), + LaunchMode::Sequencer => InputMap::default() + .layer_if(|state: &Tek|matches!( + state.pool.as_ref().map(|p|p.mode.as_ref()).flatten(), + Some(PoolMode::Import(..))|Some(PoolMode::Export(..)) + ), SourceIter(include_str!("../../config/keys_pool_file.edn")).into()) + .layer_if(|state: &Tek|matches!( + state.pool.as_ref().map(|p|p.mode.as_ref()).flatten(), + Some(PoolMode::Rename(..)) + ), SourceIter(include_str!("../../config/keys_clip_rename.edn")).into()) + .layer_if(|state: &Tek|matches!( + state.pool.as_ref().map(|p|p.mode.as_ref()).flatten(), + Some(PoolMode::Length(..)) + ), SourceIter(include_str!("../../config/keys_clip_length.edn")).into()) + .layer(SourceIter(include_str!("../../config/keys_global.edn")).into()) + .layer(SourceIter(include_str!("../../config/keys_editor.edn")).into()) + .layer(SourceIter(include_str!("../../config/keys_clock.edn")).into()) + .layer(SourceIter(include_str!("../../config/keys_sequencer.edn")).into()), + LaunchMode::Groovebox => InputMap::default() + .layer_if(|state: &Tek|matches!( + state.pool.as_ref().map(|p|p.mode.as_ref()).flatten(), + Some(PoolMode::Import(..))|Some(PoolMode::Export(..)) + ), SourceIter(include_str!("../../config/keys_pool_file.edn")).into()) + .layer_if(|state: &Tek|matches!( + state.pool.as_ref().map(|p|p.mode.as_ref()).flatten(), + Some(PoolMode::Rename(..)) + ), SourceIter(include_str!("../../config/keys_clip_rename.edn")).into()) + .layer_if(|state: &Tek|matches!( + state.pool.as_ref().map(|p|p.mode.as_ref()).flatten(), + Some(PoolMode::Length(..)) + ), SourceIter(include_str!("../../config/keys_clip_length.edn")).into()) + .layer(SourceIter(include_str!("../../config/keys_global.edn")).into()) + .layer(SourceIter(include_str!("../../config/keys_editor.edn")).into()) + .layer(SourceIter(include_str!("../../config/keys_clock.edn")).into()) + .layer(SourceIter(include_str!("../../config/keys_sequencer.edn")).into()) + .layer(SourceIter(include_str!("../../config/keys_groovebox.edn")).into()), + LaunchMode::Arranger {..} => InputMap::default() + .layer_if(|state: &Tek|matches!( + state.pool.as_ref().map(|p|p.mode.as_ref()).flatten(), + Some(PoolMode::Import(..))|Some(PoolMode::Export(..)) + ), SourceIter(include_str!("../../config/keys_pool_file.edn")).into()) + .layer_if(|state: &Tek|matches!( + state.pool.as_ref().map(|p|p.mode.as_ref()).flatten(), + Some(PoolMode::Rename(..)) + ), SourceIter(include_str!("../../config/keys_clip_rename.edn")).into()) + .layer_if(|state: &Tek|matches!( + state.pool.as_ref().map(|p|p.mode.as_ref()).flatten(), + Some(PoolMode::Length(..)) + ), SourceIter(include_str!("../../config/keys_clip_length.edn")).into()) + .layer(SourceIter(include_str!("../../config/keys_global.edn")).into()) + .layer_if(|state: &Tek|state.is_editing(), + SourceIter(include_str!("../../config/keys_editor.edn")).into()) + .layer_if(|state: &Tek|state.selected.is_clip()&&!state.is_editing(), + SourceIter(include_str!("../../config/keys_clip.edn")).into()) + .layer_if(|state: &Tek|state.selected.is_track()&&!state.is_editing(), + SourceIter(include_str!("../../config/keys_track.edn")).into()) + .layer_if(|state: &Tek|state.selected.is_scene()&&!state.is_editing(), + SourceIter(include_str!("../../config/keys_scene.edn")).into()) + .layer_if(|state: &Tek|state.selected.is_mix()&&!state.is_editing(), + SourceIter(include_str!("../../config/keys_mix.edn")).into()) + .layer(SourceIter(include_str!("../../config/keys_clock.edn")).into()) + .layer(SourceIter(include_str!("../../config/keys_arranger.edn")).into()), + _ => todo!("{mode:?}"), + }, + }, ..Default::default() }; if let &LaunchMode::Arranger { scenes, tracks, track_width, .. } = mode {