From a77536c2346f170f2e7c34f0a7f1b04cf145f112 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sun, 4 May 2025 16:23:50 +0300 Subject: [PATCH] device picker --- config/config_arranger.edn | 1 + config/keys_arranger.edn | 17 +++++++++-------- config/keys_device_add.edn | 4 ++++ crates/app/src/api.rs | 34 ++++++++++++++++++++++++++++++--- crates/app/src/model.rs | 39 ++++++++++++++++++++++++++++++++++++-- crates/app/src/view.rs | 11 +++++++++++ 6 files changed, 93 insertions(+), 13 deletions(-) create mode 100644 config/keys_device_add.edn diff --git a/config/config_arranger.edn b/config/config_arranger.edn index cd711344..3bc4b92a 100644 --- a/config/config_arranger.edn +++ b/config/config_arranger.edn @@ -11,6 +11,7 @@ :arranger)))))) (keys + (layer-if :mode-device-add "./keys_device_add.edn") (layer-if :mode-pool-import "./keys_pool_file.edn") (layer-if :mode-pool-export "./keys_pool_file.edn") (layer-if :mode-pool-rename "./keys_clip_rename.edn") diff --git a/config/keys_arranger.edn b/config/keys_arranger.edn index 9df87746..741062e0 100644 --- a/config/keys_arranger.edn +++ b/config/keys_arranger.edn @@ -1,11 +1,12 @@ -(@t select :track 0) -(@tab edit :clip) -(@c color) -(@q launch) -(@shift-I input add) -(@shift-O output add) -(@shift-S scene add) -(@shift-T track add) +(@c color) +(@q launch) +(@t select :track 0) +(@tab edit :clip) +(@shift-I input add) +(@shift-O output add) +(@shift-S scene add) +(@shift-T track add) +(@shift-Z device picker) (@up select :scene-prev) (@w select :scene-prev) diff --git a/config/keys_device_add.edn b/config/keys_device_add.edn new file mode 100644 index 00000000..0b3e1f7d --- /dev/null +++ b/config/keys_device_add.edn @@ -0,0 +1,4 @@ +(@esc device select-cancel) +(@up device pick :device-kind-prev) +(@down device pick :device-kind-next) +(@enter device add :device-kind) diff --git a/crates/app/src/api.rs b/crates/app/src/api.rs index c1c8b143..a45af91d 100644 --- a/crates/app/src/api.rs +++ b/crates/app/src/api.rs @@ -20,6 +20,7 @@ handle!(TuiIn: |self: Tek, input|Ok(if let Some(command) = self.config.keys.comm expose!([self: Tek] ([bool] (":mode-editor" self.is_editing()) + (":mode-device-add" matches!(self.modal, Some(Modal::Device(..)))) (":mode-clip" !self.is_editing() && self.selected.is_clip()) (":mode-track" !self.is_editing() && self.selected.is_track()) (":mode-scene" !self.is_editing() && self.selected.is_scene()) @@ -45,7 +46,22 @@ expose!([self: Tek] (":w-sidebar" self.w_sidebar())) ([usize] (":scene-last" self.scenes.len()) - (":track-last" self.tracks.len())) + (":track-last" self.tracks.len()) + (":device-kind" if let Some(Modal::Device(index)) = self.modal { + index + } else { + 0 + }) + (":device-kind-prev" if let Some(Modal::Device(index)) = self.modal { + index.overflowing_sub(1).0.min(self.device_kinds().len().saturating_sub(1)) + } else { + 0 + }) + (":device-kind-next" if let Some(Modal::Device(index)) = self.modal { + (index + 1) % self.device_kinds().len() + } else { + 0 + })) ([Option] (":scene" self.selected.scene()) (":track" self.selected.track())) @@ -120,6 +136,7 @@ impose!([app: Tek] ("input" [,..a] ns!(InputCommand, app, a, Self::Input)) ("output" [,..a] ns!(OutputCommand, app, a, Self::Output)) ("clip" [,..a] ns!(ClipCommand, app, a, Self::Clip)) + ("device" [,..a] ns!(DeviceCommand, app, a, Self::Device)) ("pool" [,..a] app.pool.as_ref().map(|p|ns!(PoolCommand, p, a, Self::Pool)).flatten()) ("editor" [,..a] app.editor().map(|e|ns!(MidiEditCommand, e, a, Self::Editor)).flatten()) ("sampler" [,..a] app.sampler().map(|s|ns!(SamplerCommand, s, a, Self::Sampler)).flatten()) @@ -144,6 +161,11 @@ impose!([app: Tek] (OutputCommand: ("add" [] Some(Self::Add))) + (DeviceCommand: + ("picker" [] Some(Self::Picker)) + ("pick" [index: usize] Some(Self::Pick(index.unwrap()))) + ("add" [index: usize] Some(Self::Add(index.unwrap())))) + (SceneCommand: ("add" [] Some(Self::Add)) ("delete" [a: Option] Some(Self::Del(a.flatten().unwrap()))) @@ -175,6 +197,7 @@ defcom!([self, app: Tek] (Input [cmd: InputCommand] cmd.delegate(app, Self::Input)?) (Clip [cmd: ClipCommand] cmd.delegate(app, Self::Clip)?) (Clock [cmd: ClockCommand] cmd.delegate(app, Self::Clock)?) + (Device [cmd: DeviceCommand] cmd.delegate(app, Self::Device)?) (Editor [cmd: MidiEditCommand] delegate_to_editor(app, cmd)?) (Pool [cmd: PoolCommand] delegate_to_pool(app, cmd)?) (ToggleHelp [] cmd!(app.toggle_modal(Some(Modal::Help)))) @@ -189,10 +212,15 @@ defcom!([self, app: Tek] (StopAll [] cmd!(app.stop_all()))) (InputCommand - (Add [] cmd!(app.add_midi_in()?))) + (Add [] cmd!(app.midi_in_add()?))) (OutputCommand - (Add [] cmd!(app.add_midi_out()?))) + (Add [] cmd!(app.midi_out_add()?))) + + (DeviceCommand + (Picker [] cmd!(app.device_picker_show())) + (Pick [i: usize] cmd!(app.device_pick(i))) + (Add [i: usize] cmd!(app.device_add(i)))) (TrackCommand (TogglePlay [] Some(Self::TogglePlay)) diff --git a/crates/app/src/model.rs b/crates/app/src/model.rs index b8e6471c..6a734eda 100644 --- a/crates/app/src/model.rs +++ b/crates/app/src/model.rs @@ -383,16 +383,50 @@ impl Tek { }) } - pub(crate) fn add_midi_in (&mut self) -> Usually<()> { + pub(crate) fn midi_in_add (&mut self) -> Usually<()> { self.midi_ins.push(JackMidiIn::new(&self.jack, &format!("M/{}", self.midi_ins.len()), &[])?); Ok(()) } - pub(crate) fn add_midi_out (&mut self) -> Usually<()> { + pub(crate) fn midi_out_add (&mut self) -> Usually<()> { self.midi_outs.push(JackMidiOut::new(&self.jack, &format!("{}/M", self.midi_outs.len()), &[])?); Ok(()) } + pub(crate) fn device_picker_show (&mut self) { + self.modal = Some(Modal::Device(0)); + } + + pub(crate) fn device_pick (&mut self, index: usize) { + self.modal = Some(Modal::Device(index)); + } + + pub(crate) fn device_add (&mut self, index: usize) -> Usually<()> { + match index { + 0 => { + let jack = self.jack.clone(); + self.track_mut() + .expect("no active track") + .devices + .push({ + let sampler = Sampler::new(&jack, &"sampler", &[], &[&[], &[]], &[&[], &[]])?; + Device::Sampler(sampler) + }); + self.modal = None; + Ok(()) + }, + 1 => todo!(), + _ => unreachable!(), + } + } + + pub(crate) fn device_kinds (&self) -> &'static [&'static str] { + &[ + "Sampler", + "Plugin (LV2)", + ] + } + } has_size!(|self: Tek|&self.size); @@ -424,6 +458,7 @@ pub trait HasSelection { pub enum Modal { Help, Menu, + Device(usize) } /// Represents the current user selection in the arranger diff --git a/crates/app/src/view.rs b/crates/app/src/view.rs index b4da2772..36e18146 100644 --- a/crates/app/src/view.rs +++ b/crates/app/src/view.rs @@ -70,6 +70,7 @@ impl Tek { .enclose(self.modal.map(|modal|match modal { Modal::Menu => self.view_modal_menu().boxed(), Modal::Help => self.view_modal_help().boxed(), + Modal::Device(index) => self.view_modal_device(index).boxed(), })) ))) )) @@ -109,6 +110,16 @@ impl Tek { Bsp::s(Tui::bold(true, "Help"), Bsp::s("", Map::south(1, bindings, binding))) } + fn view_modal_device (&self, index: usize) -> impl Content + use<'_> { + let choices = ||self.device_kinds().iter(); + let choice = move|label, i| + Fill::x(Tui::bg(if i == index { Rgb(64,128,32) } else { Rgb(0,0,0) }, + Bsp::e(if i == index { "[ " } else { " " }, + Bsp::w(if i == index { " ]" } else { " " }, + label)))); + Bsp::s(Tui::bold(true, "Add device"), Map::south(1, choices, choice)) + } + /// Spacing between tracks. pub(crate) const TRACK_SPACING: usize = 0;