app: wrap keys and view in Configuration

This commit is contained in:
🪞👃🪞 2025-05-02 18:56:49 +03:00
parent 0e5207a79d
commit 6ed0627056
4 changed files with 102 additions and 94 deletions

View file

@ -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);

View file

@ -48,14 +48,12 @@ pub struct Tek {
pub history: Vec<TekCommand>,
/// Port handles
pub ports: std::collections::BTreeMap<u32, Port<Unowned>>,
/// View definition
pub view: SourceIter<'static>,
// Cache of formatted strings
pub view_cache: Arc<RwLock<ViewCache>>,
// Modal overlay
pub modal: Option<Modal>,
// 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 },
}

View file

@ -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<TuiOut> + 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{

View file

@ -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 {