From 0d1d7a05b975d5ea624d9424083fdfd4fe25c965 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Mon, 16 Dec 2024 21:10:59 +0100 Subject: [PATCH] very colorized --- crates/tek/src/tui/app_sequencer.rs | 70 +++++++++++++++----------- crates/tek/src/tui/phrase_editor.rs | 5 +- crates/tek/src/tui/piano_horizontal.rs | 2 +- 3 files changed, 46 insertions(+), 31 deletions(-) diff --git a/crates/tek/src/tui/app_sequencer.rs b/crates/tek/src/tui/app_sequencer.rs index 7d29202e..f933a0e2 100644 --- a/crates/tek/src/tui/app_sequencer.rs +++ b/crates/tek/src/tui/app_sequencer.rs @@ -2,6 +2,7 @@ use crate::{*, api::ClockCommand::{Play, Pause}}; use KeyCode::{Tab, BackTab, Char}; use SequencerCommand::*; use PhraseCommand::*; +use PhrasePoolCommand::*; /// Create app state from JACK handle. impl TryFrom<&Arc>> for SequencerTui { @@ -58,17 +59,29 @@ impl Command for SequencerCommand { fn execute (self, state: &mut SequencerTui) -> Perhaps { Ok(match self { Self::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 = cmd.execute(&mut state.phrases)?.map(Phrases); - Editor(Show(Some(state.phrases.phrase().clone()))).execute(state)?; + let undo = default(cmd)?; + state.editor.set_phrase(Some(state.phrases.phrase())); undo }, - _ => cmd.execute(&mut state.phrases)?.map(Phrases) + // update color in all places simultaneously + PhrasesCommand::Phrase(SetColor(index, _)) => { + let undo = default(cmd)?; + state.editor.set_phrase(Some(state.phrases.phrase())); + undo + }, + _ => default(cmd)? + } + }, + Self::Editor(cmd) => { + let default = ||cmd.execute(&mut state.editor).map(|x|x.map(Editor)); + match cmd { + _ => default()? } }, - Self::Editor(cmd) => cmd.execute(&mut state.editor)?.map(Editor), Self::Clock(cmd) => cmd.execute(state)?.map(Clock), Self::Enqueue(phrase) => { state.player.enqueue_next(phrase.as_ref()); @@ -85,41 +98,41 @@ impl Command for SequencerCommand { impl InputToCommand for SequencerCommand { fn input_to_command (state: &SequencerTui, input: &TuiInput) -> Option { Some(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") }, - - // Toggle visibility of phrase pool column + // Tab: Toggle visibility of phrase pool column key_pat!(Tab) => ShowPool(!state.show_pool), - // Enqueue currently edited phrase + // q: Enqueue currently edited phrase key_pat!(Char('q')) => Enqueue(Some(state.phrases.phrase().clone())), // 0: Enqueue phrase 0 (stop all) key_pat!(Char('0')) => Enqueue(Some(state.phrases.phrases()[0].clone())), - // E: Toggle between editing currently playing or other phrase - key_pat!(Char('e')) => if let Some((_, Some(playing_phrase))) = state.player.play_phrase() { - let editing_phrase = state.editor.phrase().as_ref().map(|p|p.read().unwrap().clone()); - let selected_phrase = state.phrases.phrase().clone(); - if Some(selected_phrase.read().unwrap().clone()) != editing_phrase { - Editor(Show(Some(selected_phrase))) + // e: Toggle between editing currently playing or other phrase + key_pat!(Char('e')) => if let Some((_, Some(playing))) = state.player.play_phrase() { + let editing = state.editor.phrase().as_ref().map(|p|p.read().unwrap().clone()); + let selected = state.phrases.phrase().clone(); + Editor(Show(Some(if Some(selected.read().unwrap().clone()) != editing { + selected } else { - Editor(Show(Some(playing_phrase.clone()))) - } + playing.clone() + }))) } else { return None }, + // Transport: Play/pause key_pat!(Char(' ')) => Clock(if state.clock().is_stopped() { - Play(None) - } else { - Pause(None) - }), + Play(None) } else { Pause(None) }), + // Transport: Play from start or rewind to start key_pat!(Shift-Char(' ')) => Clock(if state.clock().is_stopped() { - Play(Some(0)) - } else { - Pause(Some(0)) - }), - // Delegate to components: + Play(Some(0)) } else { Pause(Some(0)) }), + + // For the rest, use the default keybindings of the components. + // The ones defined above supersede them. _ => if let Some(command) = PhraseCommand::input_to_command(&state.editor, input) { Editor(command) } else if let Some(command) = PhrasesCommand::input_to_command(&state.phrases, input) { @@ -313,10 +326,11 @@ render!(|self: SequencerStatusBar|Tui::fixed_y(2, lay!([ ]); Tui::fg_bg(TuiTheme::g(255), TuiTheme::g(50), row!([ single("SPACE", "play/pause"), - double((" ✣", "cursor"), ("C-✣", "scroll"), ), - double((",.", "note"), ("<>", "triplet"),), - double(("[]", "phrase"), ("{}", "order"), ), - double(("q", "enqueue"), ("e", "edit"), ), + double((" ✣", "cursor"), ("C-✣", "scroll"), ), + double(("a", "append"), ("s", "set note"),), + double((",.", "note"), ("<>", "triplet"), ), + double(("[]", "phrase"), ("{}", "order"), ), + double(("q", "enqueue"), ("e", "edit"), ), ])) }, Tui::fill_xy(Tui::at_se({ diff --git a/crates/tek/src/tui/phrase_editor.rs b/crates/tek/src/tui/phrase_editor.rs index 0c253b53..f23c704d 100644 --- a/crates/tek/src/tui/phrase_editor.rs +++ b/crates/tek/src/tui/phrase_editor.rs @@ -107,13 +107,14 @@ impl Command for PhraseCommand { pub struct PhraseEditorModel { /// Renders the phrase pub mode: Box, - pub size: Measure } impl Default for PhraseEditorModel { fn default () -> Self { - Self { mode: Box::new(PianoHorizontal::new(None)), size: Measure::new() } + let mut mode = Box::new(PianoHorizontal::new(None)); + mode.redraw(); + Self { mode, size: Measure::new() } } } diff --git a/crates/tek/src/tui/piano_horizontal.rs b/crates/tek/src/tui/piano_horizontal.rs index 53c08bc3..99fb53da 100644 --- a/crates/tek/src/tui/piano_horizontal.rs +++ b/crates/tek/src/tui/piano_horizontal.rs @@ -47,7 +47,7 @@ render!(|self: PianoHorizontal|{ let notes = move||PianoHorizontalNotes { range, buffer }; let cursor = move||PianoHorizontalCursor { range, point }; let keys_width = 5; - Tui::fill_xy(Tui::bg(color.dark.rgb, Bsp::s( + Tui::fill_xy(Tui::bg(color.darker.rgb, Bsp::s( Tui::fill_x(Tui::push_x(keys_width, timeline())), Bsp::w( Tui::shrink_x(keys_width, Tui::fill_xy(