From 9776d3e665b08ea8cb357f8ae6820c6f280e5047 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Tue, 24 Dec 2024 01:29:46 +0100 Subject: [PATCH] extract arranger_command and remove arranger_clip --- crates/tek/src/tui.rs | 14 +- crates/tek/src/tui/app_arranger.rs | 137 ------------------ crates/tek/src/tui/arranger_clip.rs | 37 ----- crates/tek/src/tui/arranger_command.rs | 191 +++++++++++++++++++++++++ crates/tek/src/tui/arranger_scene.rs | 21 --- 5 files changed, 198 insertions(+), 202 deletions(-) delete mode 100644 crates/tek/src/tui/arranger_clip.rs create mode 100644 crates/tek/src/tui/arranger_command.rs diff --git a/crates/tek/src/tui.rs b/crates/tek/src/tui.rs index 005828f1..8b4851d0 100644 --- a/crates/tek/src/tui.rs +++ b/crates/tek/src/tui.rs @@ -26,12 +26,12 @@ mod app_arranger; pub(crate) use app_arranger::*; /////////////////////////////////////////////////////// -mod arranger_clip; pub(crate) use arranger_clip::*; -mod arranger_scene; pub(crate) use arranger_scene::*; -mod arranger_select; pub(crate) use arranger_select::*; -mod arranger_track; pub(crate) use arranger_track::*; -mod arranger_mode_h; -mod arranger_mode_v; pub(crate) use arranger_mode_v::*; +mod arranger_command; pub(crate) use arranger_command::*; +mod arranger_scene; pub(crate) use arranger_scene::*; +mod arranger_select; pub(crate) use arranger_select::*; +mod arranger_track; pub(crate) use arranger_track::*; +mod arranger_mode_h; +mod arranger_mode_v; pub(crate) use arranger_mode_v::*; //////////////////////////////////////////////////////// @@ -42,7 +42,7 @@ mod piano_horizontal; pub(crate) use piano_horizontal::*; mod phrase_length; pub(crate) use phrase_length::*; mod phrase_rename; pub(crate) use phrase_rename::*; mod phrase_list; pub(crate) use phrase_list::*; -mod port_select; +mod port_select; //////////////////////////////////////////////////////// diff --git a/crates/tek/src/tui/app_arranger.rs b/crates/tek/src/tui/app_arranger.rs index 8ee16467..72c36158 100644 --- a/crates/tek/src/tui/app_arranger.rs +++ b/crates/tek/src/tui/app_arranger.rs @@ -1,6 +1,4 @@ use crate::*; -use ClockCommand::{Play, Pause}; -use KeyCode::{Char, Delete, Tab}; /// Root view for standalone `tek_arranger` pub struct ArrangerTui { jack: Arc>, @@ -163,141 +161,6 @@ has_clock!(|self: ArrangerTui|&self.clock); has_phrases!(|self: ArrangerTui|self.phrases.phrases); has_editor!(|self: ArrangerTui|self.editor); handle!(|self: ArrangerTui, input|ArrangerCommand::execute_with_state(self, input)); -#[derive(Clone, Debug)] pub enum ArrangerCommand { - Undo, - Redo, - Clear, - StopAll, - Color(ItemPalette), - Clock(ClockCommand), - Scene(ArrangerSceneCommand), - Track(ArrangerTrackCommand), - Clip(ArrangerClipCommand), - Select(ArrangerSelection), - Zoom(usize), - Phrases(PhrasesCommand), - Editor(PhraseCommand), - ShowPool(bool), - Put(usize, usize, Option>>), -} -input_to_command!(ArrangerCommand: |state: ArrangerTui, input|{ - use ArrangerSelection as Selected; - use ArrangerCommand::*; - // WSAD navigation, Q launches, E edits, PgUp/Down pool, Arrows editor - match input.event() { - // TODO: u: undo - key_pat!(Char('u')) => { todo!("undo") }, - // TODO: Shift-U: redo - key_pat!(Char('U')) => { todo!("redo") }, - // TODO: k: toggle on-screen keyboard - key_pat!(Ctrl-Char('k')) => { todo!("keyboard") }, - // Transport: Play/pause - key_pat!(Char(' ')) => - Self::Clock(if state.clock().is_stopped() { Play(None) } else { Pause(None) }), - // Transport: Play from start or rewind to start - key_pat!(Shift-Char(' ')) => - Self::Clock(if state.clock().is_stopped() { Play(Some(0)) } else { Pause(Some(0)) }), - key_pat!(Char('e')) => - Self::Editor(PhraseCommand::Show(Some(state.phrases.phrase().clone()))), - key_pat!(Char('l')) => - Self::Clip(ArrangerClipCommand::SetLoop(false)), - key_pat!(Ctrl-Char('a')) => - Self::Scene(ArrangerSceneCommand::Add), - key_pat!(Ctrl-Char('t')) => - Self::Track(ArrangerTrackCommand::Add), - key_pat!(Char('0')) => match state.selected() { - Selected::Mix => StopAll, - Selected::Track(_t) => return None, - Selected::Scene(_s) => return None, - Selected::Clip(_t, _s) => return None, - }, - // Tab: Toggle visibility of phrase pool column - key_pat!(Tab) => - Self::ShowPool(!state.show_pool), - _ => { - let t_len = state.tracks.len(); - let s_len = state.scenes.len(); - match state.selected() { - Selected::Clip(t, s) => match input.event() { - key_pat!(Char('g')) => Some(Self::Phrases(PhrasesCommand::Select(0))), - key_pat!(Char('p')) => Some(Self::Put(t, s, Some(state.phrases.phrase().clone()))), - key_pat!(Char('q')) => { todo!("enqueue") }, - _ => to_arranger_clip_command(input, t, t_len, s, s_len) - }, - Selected::Scene(s) => to_arranger_scene_command(input, s, s_len), - Selected::Track(t) => to_arranger_track_command(input, t, t_len), - Selected::Mix => match input.event() { - // 0: Enqueue phrase 0 (stop all) - key_pat!(Char('0')) => Some(Self::StopAll), - key_pat!(Char('s')) => Some(Self::Select(Selected::Scene(0))), - key_pat!(Char('d')) => Some(Self::Select(Selected::Track(0))), - key_pat!(Delete) => Some(Self::Clear), - key_pat!(Char('c')) => Some(Self::Color(ItemPalette::random())), - _ => None - }, - } - }.or_else(||if let Some(command) = PhraseCommand::input_to_command(&state.editor, input) { - Some(Self::Editor(command)) - } else if let Some(command) = PhrasesCommand::input_to_command(&state.phrases, input) { - Some(Self::Phrases(command)) - } else { - None - })? - } -}); -command!(|self:ArrangerCommand,state:ArrangerTui|{ - use ArrangerCommand::*; - match self { - Scene(cmd) => cmd.execute(state)?.map(Scene), - Track(cmd) => cmd.execute(state)?.map(Track), - Clip(cmd) => cmd.execute(state)?.map(Clip), - Editor(cmd) => cmd.execute(&mut state.editor)?.map(Editor), - Clock(cmd) => cmd.execute(state)?.map(Clock), - Zoom(_) => { todo!(); }, - Select(selected) => { - *state.selected_mut() = selected; - None - }, - Color(palette) => { - let old = state.color; - state.color = palette; - Some(Color(old)) - }, - ShowPool(show) => { - state.show_pool = show; - None - }, - Phrases(cmd) => { - let mut default = |cmd: PhrasesCommand|cmd - .execute(&mut state.phrases) - .map(|x|x.map(Phrases)); - match cmd { - // autoselect: automatically load selected phrase in editor - PhrasesCommand::Select(_) => { - let undo = default(cmd)?; - state.editor.set_phrase(Some(state.phrases.phrase())); - undo - }, - // update color in all places simultaneously - PhrasesCommand::Phrase(PhrasePoolCommand::SetColor(index, _)) => { - let undo = default(cmd)?; - state.editor.set_phrase(Some(state.phrases.phrase())); - undo - }, - _ => default(cmd)? - } - }, - Undo => { todo!() }, - Redo => { todo!() }, - Clear => { todo!() }, - StopAll => { todo!() }, - Put(track, scene, phrase) => { - let old = state.scenes[scene].clips[track].clone(); - state.scenes[scene].clips[track] = phrase; - Some(Put(track, scene, old)) - }, - } -}); /// Display mode of arranger #[derive(Clone, PartialEq)] diff --git a/crates/tek/src/tui/arranger_clip.rs b/crates/tek/src/tui/arranger_clip.rs deleted file mode 100644 index 0a314338..00000000 --- a/crates/tek/src/tui/arranger_clip.rs +++ /dev/null @@ -1,37 +0,0 @@ -use crate::*; - -pub fn to_arranger_clip_command (input: &TuiInput, t: usize, len_t: usize, s: usize, len_s: usize) -> Option { - use KeyCode::{Char, Delete}; - use ArrangerCommand as Cmd; - use ArrangerSelection as Select; - use ArrangerClipCommand as Clip; - Some(match input.event() { - key_pat!(Char('w')) => Cmd::Select(if s > 0 { Select::Clip(t, s - 1) } else { Select::Track(t) }), - key_pat!(Char('s')) => Cmd::Select(Select::Clip(t, (s + 1).min(len_s.saturating_sub(1)))), - key_pat!(Char('a')) => Cmd::Select(if t > 0 { Select::Clip(t - 1, s) } else { Select::Scene(s) }), - key_pat!(Char('d')) => Cmd::Select(Select::Clip((t + 1).min(len_t.saturating_sub(1)), s)), - key_pat!(Char(',')) => Cmd::Clip(Clip::Set(t, s, None)), - key_pat!(Char('.')) => Cmd::Clip(Clip::Set(t, s, None)), - key_pat!(Char('<')) => Cmd::Clip(Clip::Set(t, s, None)), - key_pat!(Char('>')) => Cmd::Clip(Clip::Set(t, s, None)), - key_pat!(Delete) => Cmd::Clip(Clip::Set(t, s, None)), - //key_pat!(Char('c')) => Cmd::Clip(Clip::Color(t, s, ItemPalette::random())), - //key_pat!(Char('g')) => Cmd::Clip(Clip(Clip::Get(t, s))), - //key_pat!(Char('s')) => Cmd::Clip(Clip(Clip::Set(t, s))), - _ => return None - }) -} - -command!(|self:ArrangerClipCommand, _state:ArrangerTui|match self { - _ => None -}); - -#[derive(Clone, Debug)] -pub enum ArrangerClipCommand { - Play, - Get(usize, usize), - Set(usize, usize, Option>>), - Edit(Option>>), - SetLoop(bool), - RandomColor, -} diff --git a/crates/tek/src/tui/arranger_command.rs b/crates/tek/src/tui/arranger_command.rs new file mode 100644 index 00000000..0dde990b --- /dev/null +++ b/crates/tek/src/tui/arranger_command.rs @@ -0,0 +1,191 @@ +use crate::*; +use ClockCommand::{Play, Pause}; +use KeyCode::{Char, Delete, Tab}; + +#[derive(Clone, Debug)] pub enum ArrangerCommand { + Undo, + Redo, + Clear, + StopAll, + Color(ItemPalette), + Clock(ClockCommand), + Scene(ArrangerSceneCommand), + Track(ArrangerTrackCommand), + Clip(ArrangerClipCommand), + Select(ArrangerSelection), + Zoom(usize), + Phrases(PhrasesCommand), + Editor(PhraseCommand), + ShowPool(bool), + Put(usize, usize, Option>>), +} +input_to_command!(ArrangerCommand: |state: ArrangerTui, input|{ + use ArrangerSelection as Selected; + use ArrangerSceneCommand as Scene; + use ArrangerTrackCommand as Track; + use ArrangerClipCommand as Clip; + // WSAD navigation, Q launches, E edits, PgUp/Down pool, Arrows editor + match input.event() { + // TODO: u: undo + key_pat!(Char('u')) => { todo!("undo") }, + // TODO: Shift-U: redo + key_pat!(Char('U')) => { todo!("redo") }, + // TODO: k: toggle on-screen keyboard + key_pat!(Ctrl-Char('k')) => { todo!("keyboard") }, + // Transport: Play/pause + key_pat!(Char(' ')) => + Self::Clock(if state.clock().is_stopped() { Play(None) } else { Pause(None) }), + // Transport: Play from start or rewind to start + key_pat!(Shift-Char(' ')) => + Self::Clock(if state.clock().is_stopped() { Play(Some(0)) } else { Pause(Some(0)) }), + key_pat!(Char('e')) => + Self::Editor(PhraseCommand::Show(Some(state.phrases.phrase().clone()))), + key_pat!(Char('l')) => + Self::Clip(ArrangerClipCommand::SetLoop(false)), + key_pat!(Ctrl-Char('a')) => + Self::Scene(ArrangerSceneCommand::Add), + key_pat!(Ctrl-Char('t')) => + Self::Track(ArrangerTrackCommand::Add), + key_pat!(Char('0')) => match state.selected() { + Selected::Mix => Self::StopAll, + Selected::Track(_t) => return None, + Selected::Scene(_s) => return None, + Selected::Clip(_t, _s) => return None, + }, + // Tab: Toggle visibility of phrase pool column + key_pat!(Tab) => + Self::ShowPool(!state.show_pool), + _ => { + let t_len = state.tracks.len(); + let s_len = state.scenes.len(); + match state.selected() { + Selected::Clip(t, s) => match input.event() { + key_pat!(Char('w')) => Some(Self::Select(if s > 0 { Selected::Clip(t, s - 1) } else { Selected::Track(t) })), + key_pat!(Char('s')) => Some(Self::Select(Selected::Clip(t, (s + 1).min(s_len.saturating_sub(1))))), + key_pat!(Char('a')) => Some(Self::Select(if t > 0 { Selected::Clip(t - 1, s) } else { Selected::Scene(s) })), + key_pat!(Char('d')) => Some(Self::Select(Selected::Clip((t + 1).min(t_len.saturating_sub(1)), s))), + key_pat!(Char(',')) => Some(Self::Clip(Clip::Set(t, s, None))), + key_pat!(Char('.')) => Some(Self::Clip(Clip::Set(t, s, None))), + key_pat!(Char('<')) => Some(Self::Clip(Clip::Set(t, s, None))), + key_pat!(Char('>')) => Some(Self::Clip(Clip::Set(t, s, None))), + key_pat!(Char('g')) => Some(Self::Phrases(PhrasesCommand::Select(0))), + key_pat!(Char('p')) => Some(Self::Put(t, s, Some(state.phrases.phrase().clone()))), + key_pat!(Char('q')) => { todo!("enqueue") }, + key_pat!(Delete) => Some(Self::Clip(Clip::Set(t, s, None))), + //key_pat!(Char('c')) => Cmd::Clip(Clip::Color(t, s, ItemPalette::random())), + //key_pat!(Char('g')) => Cmd::Clip(Clip(Clip::Get(t, s))), + //key_pat!(Char('s')) => Cmd::Clip(Clip(Clip::Set(t, s))), + _ => None + }, + Selected::Scene(s) => match input.event() { + key_pat!(Char('w')) => Some(Self::Select(if s > 0 { Selected::Scene(s - 1) } else { Selected::Mix })), + key_pat!(Char('s')) => Some(Self::Select(Selected::Scene((s + 1).min(s_len.saturating_sub(1))))), + key_pat!(Char('d')) => Some(Self::Select(Selected::Clip(0, s))), + key_pat!(Char(',')) => Some(Self::Scene(Scene::Swap(s, s - 1))), + key_pat!(Char('.')) => Some(Self::Scene(Scene::Swap(s, s + 1))), + key_pat!(Char('<')) => Some(Self::Scene(Scene::Swap(s, s - 1))), + key_pat!(Char('>')) => Some(Self::Scene(Scene::Swap(s, s + 1))), + key_pat!(Char('q')) => Some(Self::Scene(Scene::Play(s))), + key_pat!(Char('c')) => Some(Self::Scene(Scene::SetColor(s, ItemPalette::random()))), + key_pat!(Delete) => Some(Self::Scene(Scene::Delete(s))), + //key_pat!(Char('c')) => Cmd::Track(Scene::Color(s, ItemPalette::random())), + _ => None + }, + Selected::Track(t) => match input.event() { + key_pat!(Char('s')) => Some(Self::Select(Selected::Clip(t, 0))), + key_pat!(Char('a')) => Some(Self::Select(if t > 0 { Selected::Track(t - 1) } else { Selected::Mix })), + key_pat!(Char('d')) => Some(Self::Select(Selected::Track((t + 1).min(t_len.saturating_sub(1))))), + key_pat!(Char('c')) => Some(Self::Track(Track::SetColor(t, ItemPalette::random()))), + key_pat!(Char(',')) => Some(Self::Track(Track::Swap(t, t - 1))), + key_pat!(Char('.')) => Some(Self::Track(Track::Swap(t, t + 1))), + key_pat!(Char('<')) => Some(Self::Track(Track::Swap(t, t - 1))), + key_pat!(Char('>')) => Some(Self::Track(Track::Swap(t, t + 1))), + key_pat!(Delete) => Some(Self::Track(Track::Delete(t))), + //key_pat!(Char('c')) => Cmd::Track(Track::Color(t, ItemPalette::random())), + _ => return None + }, + Selected::Mix => match input.event() { + // 0: Enqueue phrase 0 (stop all) + key_pat!(Char('0')) => Some(Self::StopAll), + key_pat!(Char('s')) => Some(Self::Select(Selected::Scene(0))), + key_pat!(Char('d')) => Some(Self::Select(Selected::Track(0))), + key_pat!(Delete) => Some(Self::Clear), + key_pat!(Char('c')) => Some(Self::Color(ItemPalette::random())), + _ => None + }, + } + }.or_else(||if let Some(command) = PhraseCommand::input_to_command(&state.editor, input) { + Some(Self::Editor(command)) + } else if let Some(command) = PhrasesCommand::input_to_command(&state.phrases, input) { + Some(Self::Phrases(command)) + } else { + None + })? + } +}); +command!(|self:ArrangerCommand,state:ArrangerTui|{ + use ArrangerCommand::*; + match self { + Scene(cmd) => cmd.execute(state)?.map(Scene), + Track(cmd) => cmd.execute(state)?.map(Track), + Clip(cmd) => cmd.execute(state)?.map(Clip), + Editor(cmd) => cmd.execute(&mut state.editor)?.map(Editor), + Clock(cmd) => cmd.execute(state)?.map(Clock), + Zoom(_) => { todo!(); }, + Select(selected) => { + *state.selected_mut() = selected; + None + }, + Color(palette) => { + let old = state.color; + state.color = palette; + Some(Color(old)) + }, + ShowPool(show) => { + state.show_pool = show; + None + }, + Phrases(cmd) => { + let mut default = |cmd: PhrasesCommand|cmd + .execute(&mut state.phrases) + .map(|x|x.map(Phrases)); + match cmd { + // autoselect: automatically load selected phrase in editor + PhrasesCommand::Select(_) => { + let undo = default(cmd)?; + state.editor.set_phrase(Some(state.phrases.phrase())); + undo + }, + // update color in all places simultaneously + PhrasesCommand::Phrase(PhrasePoolCommand::SetColor(index, _)) => { + let undo = default(cmd)?; + state.editor.set_phrase(Some(state.phrases.phrase())); + undo + }, + _ => default(cmd)? + } + }, + Undo => { todo!() }, + Redo => { todo!() }, + Clear => { todo!() }, + StopAll => { todo!() }, + Put(track, scene, phrase) => { + let old = state.scenes[scene].clips[track].clone(); + state.scenes[scene].clips[track] = phrase; + Some(Put(track, scene, old)) + }, + } +}); + +#[derive(Clone, Debug)] +pub enum ArrangerClipCommand { + Play, + Get(usize, usize), + Set(usize, usize, Option>>), + Edit(Option>>), + SetLoop(bool), + SetColor(ItemPalette), +} +command!(|self:ArrangerClipCommand, _state:ArrangerTui|match self { + _ => None +}); diff --git a/crates/tek/src/tui/arranger_scene.rs b/crates/tek/src/tui/arranger_scene.rs index f9d0af70..93cd5e6b 100644 --- a/crates/tek/src/tui/arranger_scene.rs +++ b/crates/tek/src/tui/arranger_scene.rs @@ -3,33 +3,12 @@ use crate::*; pub enum ArrangerSceneCommand { Add, Delete(usize), - RandomColor, Play(usize), Swap(usize, usize), SetSize(usize), SetZoom(usize), SetColor(usize, ItemPalette), } -pub fn to_arranger_scene_command (input: &TuiInput, s: usize, len: usize) -> Option { - use KeyCode::{Char, Delete}; - use ArrangerCommand as Cmd; - use ArrangerSelection as Select; - use ArrangerSceneCommand as Scene; - Some(match input.event() { - key_pat!(Char('w')) => Cmd::Select(if s > 0 { Select::Scene(s - 1) } else { Select::Mix }), - key_pat!(Char('s')) => Cmd::Select(Select::Scene((s + 1).min(len.saturating_sub(1)))), - key_pat!(Char('d')) => Cmd::Select(Select::Clip(0, s)), - key_pat!(Char(',')) => Cmd::Scene(Scene::Swap(s, s - 1)), - key_pat!(Char('.')) => Cmd::Scene(Scene::Swap(s, s + 1)), - key_pat!(Char('<')) => Cmd::Scene(Scene::Swap(s, s - 1)), - key_pat!(Char('>')) => Cmd::Scene(Scene::Swap(s, s + 1)), - key_pat!(Char('q')) => Cmd::Scene(Scene::Play(s)), - key_pat!(Char('c')) => Cmd::Scene(Scene::SetColor(s, ItemPalette::random())), - key_pat!(Delete) => Cmd::Scene(Scene::Delete(s)), - //key_pat!(Char('c')) => Cmd::Track(Scene::Color(s, ItemPalette::random())), - _ => return None - }) -} command!(|self:ArrangerSceneCommand,state:ArrangerTui|match self { //Self::Delete(index) => { state.scene_del(index); }, Self::SetColor(index, color) => {