From 627c7d88205616618d23f5e4b9ad200ecc670f06 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sun, 17 Nov 2024 04:03:06 +0100 Subject: [PATCH] wip: p.45, e=33, lotta todo! --- crates/tek_tui/src/tui_arranger.rs | 4 +- crates/tek_tui/src/tui_arranger_cmd.rs | 42 +++++------ crates/tek_tui/src/tui_arranger_focus.rs | 29 ++++---- crates/tek_tui/src/tui_arranger_scene.rs | 2 +- crates/tek_tui/src/tui_arranger_status.rs | 39 +++++----- crates/tek_tui/src/tui_arranger_track.rs | 4 +- crates/tek_tui/src/tui_arranger_view.rs | 10 +-- crates/tek_tui/src/tui_phrase.rs | 36 ++++----- crates/tek_tui/src/tui_pool.rs | 6 +- crates/tek_tui/src/tui_pool_cmd.rs | 16 ++-- crates/tek_tui/src/tui_pool_view.rs | 58 +++++++++++---- crates/tek_tui/src/tui_sequencer.rs | 20 ++--- crates/tek_tui/src/tui_sequencer_cmd.rs | 31 +++----- crates/tek_tui/src/tui_transport.rs | 4 +- crates/tek_tui/src/tui_transport_cmd.rs | 89 ++++++++--------------- crates/tek_tui/src/tui_transport_focus.rs | 20 ++--- crates/tek_tui/src/tui_transport_view.rs | 14 ++-- 17 files changed, 198 insertions(+), 226 deletions(-) diff --git a/crates/tek_tui/src/tui_arranger.rs b/crates/tek_tui/src/tui_arranger.rs index 1d33b221..c43cc311 100644 --- a/crates/tek_tui/src/tui_arranger.rs +++ b/crates/tek_tui/src/tui_arranger.rs @@ -25,8 +25,8 @@ pub struct ArrangerTui { pub midi_buf: Vec>>, pub cursor: (usize, usize), pub menu_bar: Option>, - pub status_bar: Option, - pub history: Vec, + pub status_bar: Option, + pub history: Vec, } impl TryFrom<&Arc>> for ArrangerTui { diff --git a/crates/tek_tui/src/tui_arranger_cmd.rs b/crates/tek_tui/src/tui_arranger_cmd.rs index 510fd5f9..a64b4226 100644 --- a/crates/tek_tui/src/tui_arranger_cmd.rs +++ b/crates/tek_tui/src/tui_arranger_cmd.rs @@ -20,13 +20,19 @@ pub enum ArrangerCommand { Clip(ArrangerClipCommand), Select(ArrangerSelection), Zoom(usize), - Phrases(PhrasePoolViewCommand), + Phrases(PhrasePoolCommand), Editor(PhraseEditorCommand), EditPhrase(Option>>), } -impl InputToCommand for ArrangerCommand { - fn input_to_command (view: &ArrangerTui, input: &TuiInput) -> Option { +pub trait ArrangerControl { +} + +impl ArrangerControl for ArrangerTui { +} + +impl InputToCommand for ArrangerCommand { + fn input_to_command (view: &T, input: &TuiInput) -> Option { use FocusCommand::*; use ArrangerCommand::*; Some(match input.event() { @@ -44,7 +50,7 @@ impl InputToCommand for ArrangerCommand { Self::App(Playhead(PlayheadCommand::Play(None))) }, _ => Self::App(match view.focused() { - Content(ArrangerFocus::Transport) => { + ArrangerFocus::Transport => { use TransportCommand::{Clock, Playhead}; match TransportCommand::input_to_command(view, input)? { Clock(command) => { @@ -55,18 +61,18 @@ impl InputToCommand for ArrangerCommand { }, } }, - Content(ArrangerFocus::PhraseEditor) => Editor( - PhraseEditorCommand::input_to_command(&view.editor, input)? + ArrangerFocus::PhraseEditor => Editor( + PhraseCommand::input_to_command(&view.editor, input)? ), - Content(ArrangerFocus::PhrasePool) => match input.event() { + ArrangerFocus::PhrasePool => match input.event() { key!(KeyCode::Char('e')) => EditPhrase( Some(view.phrase().clone()) ), _ => Phrases( - PhrasePoolViewCommand::input_to_command(view, input)? + PhrasePoolCommand::input_to_command(view, input)? ) }, - Content(ArrangerFocus::Arranger) => { + ArrangerFocus::Arranger => { use ArrangerSelection as Select; use ArrangerTrackCommand as Track; use ArrangerClipCommand as Clip; @@ -183,23 +189,11 @@ impl InputToCommand for ArrangerCommand { } } -impl Command for ArrangerCommand { - fn execute (self, state: &mut ArrangerTui) -> Perhaps { - let undo = match self { - Focus(cmd) => { delegate(cmd, Focus, state) }, - App(cmd) => { delegate(cmd, App, state) } - _ => {todo!()} - }?; - state.show_phrase(); - state.update_status(); - return Ok(undo); - } -} - -impl Command for ArrangerCommand { - fn execute (self, state: &mut ArrangerTui) -> Perhaps { +impl Command for ArrangerCommand { + fn execute (self, state: &mut T) -> Perhaps { use ArrangerCommand::*; match self { + Focus(cmd) => { delegate(cmd, Focus, state) }, Scene(cmd) => { delegate(cmd, Scene, &mut state) }, Track(cmd) => { delegate(cmd, Track, &mut state) }, Clip(cmd) => { delegate(cmd, Clip, &mut state) }, diff --git a/crates/tek_tui/src/tui_arranger_focus.rs b/crates/tek_tui/src/tui_arranger_focus.rs index df9e75ea..70ce996a 100644 --- a/crates/tek_tui/src/tui_arranger_focus.rs +++ b/crates/tek_tui/src/tui_arranger_focus.rs @@ -3,6 +3,8 @@ use crate::*; /// Sections in the arranger app that may be focused #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum ArrangerFocus { + /// The menu bar is focused + Menu, /// The transport (toolbar) is focused Transport, /// The arrangement (grid) is focused @@ -13,16 +15,15 @@ pub enum ArrangerFocus { PhraseEditor, } -impl FocusEnter for ArrangerApp { - type Item = AppViewFocus; +impl FocusEnter for ArrangerTui { + type Item = ArrangerFocus; fn focus_enter (&mut self) { - use AppViewFocus::*; use ArrangerFocus::*; let focused = self.focused(); if !self.entered { - self.entered = focused == Content(Arranger); - self.app.editor.entered = focused == Content(PhraseEditor); - self.app.phrases.entered = focused == Content(PhrasePool); + self.entered = focused == Arranger; + self.app.editor.entered = focused == PhraseEditor; + self.app.phrases.entered = focused == PhrasePool; } } fn focus_exit (&mut self) { @@ -42,8 +43,8 @@ impl FocusEnter for ArrangerApp { } /// Focus layout of arranger app -impl FocusGrid for ArrangerApp { - type Item = AppViewFocus; +impl FocusGrid for ArrangerTui { + type Item = ArrangerFocus; fn focus_cursor (&self) -> (usize, usize) { self.cursor } @@ -51,24 +52,22 @@ impl FocusGrid for ArrangerApp { &mut self.cursor } fn focus_layout (&self) -> &[&[Self::Item]] { - use AppViewFocus::*; use ArrangerFocus::*; &[ - &[Menu, Menu ], - &[Content(Transport), Content(Transport) ], - &[Content(Arranger), Content(Arranger) ], - &[Content(PhrasePool), Content(PhraseEditor)], + &[Menu, Menu ], + &[Transport, Transport ], + &[Arranger, Arranger ], + &[PhrasePool, PhraseEditor], ] } fn focus_update (&mut self) { - use AppViewFocus::*; use ArrangerFocus::*; let focused = self.focused(); if let Some(mut status_bar) = self.status_bar { status_bar.update(&( self.focused(), self.app.selected, - focused == Content(PhraseEditor) && self.entered + focused == PhraseEditor && self.entered )) } } diff --git a/crates/tek_tui/src/tui_arranger_scene.rs b/crates/tek_tui/src/tui_arranger_scene.rs index e4864436..a1bdcf7c 100644 --- a/crates/tek_tui/src/tui_arranger_scene.rs +++ b/crates/tek_tui/src/tui_arranger_scene.rs @@ -1,6 +1,6 @@ use crate::*; -impl HasScenes for ArrangerView { +impl HasScenes for ArrangerTui { fn scenes (&self) -> &Vec { &self.scenes } diff --git a/crates/tek_tui/src/tui_arranger_status.rs b/crates/tek_tui/src/tui_arranger_status.rs index 463f37c4..f3069075 100644 --- a/crates/tek_tui/src/tui_arranger_status.rs +++ b/crates/tek_tui/src/tui_arranger_status.rs @@ -2,7 +2,7 @@ use crate::*; /// Status bar for arranger app #[derive(Copy, Clone, Debug)] -pub enum ArrangerStatusBar { +pub enum ArrangerStatus { Transport, ArrangerMix, ArrangerTrack, @@ -13,35 +13,30 @@ pub enum ArrangerStatusBar { PhraseEdit, } -impl StatusBar for ArrangerStatusBar { - - type State = (AppViewFocus, ArrangerSelection, bool); - +impl StatusBar for ArrangerStatus { + type State = (ArrangerFocus, ArrangerSelection, bool); fn hotkey_fg () -> Color where Self: Sized { TuiTheme::hotkey_fg() } fn update (&mut self, (focused, selected, entered): &Self::State) { - use AppViewFocus::*; - if let Content(focused) = focused { - *self = match focused { - ArrangerFocus::Transport => ArrangerStatusBar::Transport, - ArrangerFocus::Arranger => match selected { - ArrangerSelection::Mix => ArrangerStatusBar::ArrangerMix, - ArrangerSelection::Track(_) => ArrangerStatusBar::ArrangerTrack, - ArrangerSelection::Scene(_) => ArrangerStatusBar::ArrangerScene, - ArrangerSelection::Clip(_, _) => ArrangerStatusBar::ArrangerClip, - }, - ArrangerFocus::PhrasePool => ArrangerStatusBar::PhrasePool, - ArrangerFocus::PhraseEditor => match entered { - true => ArrangerStatusBar::PhraseEdit, - false => ArrangerStatusBar::PhraseView, - }, - } + *self = match focused { + ArrangerFocus::Transport => ArrangerStatus::Transport, + ArrangerFocus::Arranger => match selected { + ArrangerSelection::Mix => ArrangerStatus::ArrangerMix, + ArrangerSelection::Track(_) => ArrangerStatus::ArrangerTrack, + ArrangerSelection::Scene(_) => ArrangerStatus::ArrangerScene, + ArrangerSelection::Clip(_, _) => ArrangerStatus::ArrangerClip, + }, + ArrangerFocus::PhrasePool => ArrangerStatus::PhrasePool, + ArrangerFocus::PhraseEditor => match entered { + true => ArrangerStatus::PhraseEdit, + false => ArrangerStatus::PhraseView, + }, } } } -impl Content for ArrangerStatusBar { +impl Content for ArrangerStatus { type Engine = Tui; fn content (&self) -> impl Widget { let label = match self { diff --git a/crates/tek_tui/src/tui_arranger_track.rs b/crates/tek_tui/src/tui_arranger_track.rs index 174c1a45..92a7c295 100644 --- a/crates/tek_tui/src/tui_arranger_track.rs +++ b/crates/tek_tui/src/tui_arranger_track.rs @@ -1,6 +1,6 @@ use crate::*; -impl HasTracks for ArrangerView { +impl HasTracks for ArrangerTui { fn tracks (&self) -> &Vec { &self.tracks } @@ -9,7 +9,7 @@ impl HasTracks for ArrangerView { } } -impl ArrangerTracksApi for ArrangerView { +impl ArrangerTracksApi for ArrangerTui { fn track_add (&mut self, name: Option<&str>, color: Option) -> Usually<&mut ArrangerTrack> { diff --git a/crates/tek_tui/src/tui_arranger_view.rs b/crates/tek_tui/src/tui_arranger_view.rs index 82ba5c38..13a6b2a1 100644 --- a/crates/tek_tui/src/tui_arranger_view.rs +++ b/crates/tek_tui/src/tui_arranger_view.rs @@ -24,7 +24,7 @@ impl ArrangerMode { } /// Layout for standalone arranger app. -impl Content for ArrangerView { +impl Content for ArrangerTui { type Engine = Tui; fn content (&self) -> impl Widget { Split::up( @@ -65,8 +65,8 @@ impl Content for ArrangerView { } } -impl TransportViewState for ArrangerView { - fn focus (&self) -> TransportViewFocus { +impl TransportViewState for ArrangerTui { + fn focus (&self) -> TransportFocus { self.focus } fn focused (&self) -> bool { @@ -102,7 +102,7 @@ fn track_widths (tracks: &[ArrangerTrack]) -> Vec<(usize, usize)> { } pub fn arranger_content_vertical ( - view: &ArrangerView, + view: &ArrangerTui, factor: usize ) -> impl Widget + use<'_> { let timebase = view.timebase(); @@ -289,7 +289,7 @@ pub fn arranger_content_vertical ( } pub fn arranger_content_horizontal ( - view: &ArrangerView, + view: &ArrangerTui, ) -> impl Widget + use<'_> { let focused = view.focused; let _tracks = view.tracks(); diff --git a/crates/tek_tui/src/tui_phrase.rs b/crates/tek_tui/src/tui_phrase.rs index 82152201..41d72c8f 100644 --- a/crates/tek_tui/src/tui_phrase.rs +++ b/crates/tek_tui/src/tui_phrase.rs @@ -1,7 +1,7 @@ use crate::*; /// Contains state for viewing and editing a phrase -pub struct PhraseEditor { +pub struct PhraseTui { _engine: PhantomData, /// Phrase being played pub phrase: Option>>, @@ -31,7 +31,7 @@ pub struct PhraseEditor { pub size: Measure } -impl Widget for PhraseEditor { +impl Widget for PhraseTui { type Engine = Tui; fn layout (&self, to: [u16;2]) -> Perhaps<[u16;2]> { PhraseView(&self, Default::default()).layout(to) @@ -41,9 +41,9 @@ impl Widget for PhraseEditor { } } -pub struct PhraseView<'a, T: PhraseEditorViewState>(pub &'a T); +pub struct PhraseView<'a, T: PhraseTuiViewState>(pub &'a T); -pub trait PhraseEditorViewState: Send + Sync { +pub trait PhraseTuiViewState: Send + Sync { fn focused (&self) -> bool; fn entered (&self) -> bool; fn keys (&self) -> &Buffer; @@ -56,7 +56,7 @@ pub trait PhraseEditorViewState: Send + Sync { fn now (&self) -> &Arc; } -impl PhraseEditorViewState for PhraseEditor { +impl PhraseTuiViewState for PhraseTui { fn focused (&self) -> bool { &self.focused } @@ -89,7 +89,7 @@ impl PhraseEditorViewState for PhraseEditor { } } -impl<'a, T: PhraseEditorViewState> Content for PhraseView<'a, T> { +impl<'a, T: PhraseTuiViewState> Content for PhraseView<'a, T> { type Engine = Tui; fn content (&self) -> impl Widget { let phrase = self.0.phrase(); @@ -223,7 +223,7 @@ impl<'a, T: PhraseEditorViewState> Content for PhraseView<'a, T> { } } -impl PhraseEditor { +impl PhraseTui { pub fn new () -> Self { Self { _engine: Default::default(), @@ -410,7 +410,7 @@ const NTH_OCTAVE: [&'static str; 11] = [ ]; #[derive(Clone, PartialEq, Debug)] -pub enum PhraseEditorCommand { +pub enum PhraseCommand { // TODO: 1-9 seek markers that by default start every 8th of the phrase ToggleDirection, EnterEditMode, @@ -425,15 +425,15 @@ pub enum PhraseEditorCommand { TimeZoomSet(usize), } -impl Handle for PhraseEditor { +impl Handle for PhraseTui { fn handle (&mut self, from: &TuiInput) -> Perhaps { - PhraseEditorCommand::execute_with_state(self, from) + PhraseCommand::execute_with_state(self, from) } } -impl InputToCommand> for PhraseEditorCommand { - fn input_to_command (state: &PhraseEditor, from: &TuiInput) -> Option { - use PhraseEditorCommand::*; +impl InputToCommand> for PhraseCommand { + fn input_to_command (state: &PhraseTui, from: &TuiInput) -> Option { + use PhraseCommand::*; Some(match from.event() { key!(KeyCode::Char('`')) => ToggleDirection, key!(KeyCode::Enter) => EnterEditMode, @@ -469,9 +469,9 @@ impl InputToCommand> for PhraseEditorCommand { } } -impl Command> for PhraseEditorCommand { - //fn translate (self, state: &PhraseEditor) -> Self { - //use PhraseEditorCommand::*; +impl Command> for PhraseCommand { + //fn translate (self, state: &PhraseTui) -> Self { + //use PhraseCommand::*; //match self { //GoUp => match state.entered { true => NoteCursorInc, false => NoteScrollInc, }, //GoDown => match state.entered { true => NoteCursorDec, false => NoteScrollDec, }, @@ -480,8 +480,8 @@ impl Command> for PhraseEditorCommand { //_ => self //} //} - fn execute (self, state: &mut PhraseEditor) -> Perhaps { - use PhraseEditorCommand::*; + fn execute (self, state: &mut PhraseTui) -> Perhaps { + use PhraseCommand::*; match self.translate(state) { ToggleDirection => { state.mode = !state.mode; diff --git a/crates/tek_tui/src/tui_pool.rs b/crates/tek_tui/src/tui_pool.rs index 249546b2..9b7ea537 100644 --- a/crates/tek_tui/src/tui_pool.rs +++ b/crates/tek_tui/src/tui_pool.rs @@ -8,7 +8,7 @@ pub struct PhrasesTui { /// Scroll offset pub scroll: usize, /// Mode switch - pub mode: Option, + pub mode: Option, /// Whether this widget is focused pub focused: bool, /// Whether this widget is entered @@ -16,7 +16,7 @@ pub struct PhrasesTui { } /// Modes for phrase pool -pub enum PhrasePoolMode { +pub enum PhrasesMode { /// Renaming a pattern Rename(usize, String), /// Editing the length of a pattern @@ -103,7 +103,7 @@ pub struct PhraseLength { pub focus: Option, } -impl PhraseLength { +impl PhraseLength { pub fn new (pulses: usize, focus: Option) -> Self { Self { _engine: Default::default(), ppq: PPQ, bpb: 4, pulses, focus } } diff --git a/crates/tek_tui/src/tui_pool_cmd.rs b/crates/tek_tui/src/tui_pool_cmd.rs index 8f3fc6a1..c2d6a822 100644 --- a/crates/tek_tui/src/tui_pool_cmd.rs +++ b/crates/tek_tui/src/tui_pool_cmd.rs @@ -2,12 +2,12 @@ use crate::*; impl Handle for PhrasePoolView { fn handle (&mut self, from: &TuiInput) -> Perhaps { - PhrasePoolViewCommand::execute_with_state(self, from) + PhrasesCommand::execute_with_state(self, from) } } #[derive(Clone, PartialEq, Debug)] -pub enum PhrasePoolViewCommand { +pub enum PhrasesCommand { Select(usize), Edit(PhrasePoolCommand), Rename(PhraseRenameCommand), @@ -33,12 +33,12 @@ pub enum PhraseLengthCommand { Cancel, } -impl InputToCommand> for PhrasePoolViewCommand { +impl InputToCommand> for PhrasesCommand { fn input_to_command (state: &PhrasePoolView, input: &TuiInput) -> Option { - use PhrasePoolViewCommand as Cmd; - use PhrasePoolCommand as Edit; - use PhraseRenameCommand as Rename; - use PhraseLengthCommand as Length; + use PhrasesCommand as Cmd; + use PhrasePoolCommand as Edit; + use PhraseRenameCommand as Rename; + use PhraseLengthCommand as Length; match input.event() { key!(KeyCode::Up) => Some(Cmd::Select(0)), key!(KeyCode::Down) => Some(Cmd::Select(0)), @@ -64,7 +64,7 @@ impl InputToCommand> for PhrasePoolViewCommand { } } -impl Command> for PhrasePoolViewCommand { +impl Command> for PhrasesCommand { fn execute (self, view: &mut PhrasePoolView) -> Perhaps { use PhraseRenameCommand as Rename; use PhraseLengthCommand as Length; diff --git a/crates/tek_tui/src/tui_pool_view.rs b/crates/tek_tui/src/tui_pool_view.rs index 5288f12c..56144e46 100644 --- a/crates/tek_tui/src/tui_pool_view.rs +++ b/crates/tek_tui/src/tui_pool_view.rs @@ -1,46 +1,78 @@ use crate::*; -impl Widget for TransportTui { +impl Widget for PhrasesTui { type Engine = Tui; fn layout (&self, to: [u16;2]) -> Perhaps<[u16;2]> { - TransportView(&self, Default::default()).layout(to) + PhrasesView(&self).layout(to) } fn render (&self, to: &mut TuiOutput) -> Usually<()> { - TransportView(&self, Default::default()).render(to) + PhrasesView(&self).render(to) } } +pub trait PhrasesViewState { + fn focused (&self) -> bool; + fn entered (&self) -> bool; + fn phrases (&self) -> Vec<()>; + fn phrase (&self) -> (); + fn mode (&self) -> PhrasesMode; +} + +impl PhrasesViewState for PhrasesTui { + fn focused (&self) -> bool { + todo!() + } + fn entered (&self) -> bool { + todo!() + } + fn phrases (&self) -> Vec<()> { + todo!() + } + fn phrase (&self) -> () { + todo!() + } + fn mode (&self) -> PhrasesMode { + todo!() + } +} + +pub struct PhrasesView<'a, T: PhrasesViewState>(&'a T); + // TODO: Display phrases always in order of appearance -impl Content for PhrasePoolView { +impl<'a, T: PhrasesViewState> Content for PhrasesView<'a, T> { type Engine = Tui; fn content (&self) -> impl Widget { - let Self { focused, phrases, mode, .. } = self; + let focused = self.focused(); + let entered = self.entered(); + let phrases = self.phrases(); + let phrase = self.phrase(); + let mode = self.mode(); let content = col!( (i, phrase) in phrases.iter().enumerate() => Layers::new(|add|{ let Phrase { ref name, color, length, .. } = *phrase.read().unwrap(); let mut length = PhraseLength::new(length, None); - if let Some(PhrasePoolMode::Length(phrase, new_length, focus)) = mode { - if *focused && i == *phrase { - length.pulses = *new_length; - length.focus = Some(*focus); + if let Some(PhrasesMode::Length(phrase, new_length, focus)) = mode { + if *focused && i == phrase { + length.pulses = new_length; + length.focus = Some(focus); } } let length = length.align_e().fill_x(); let row1 = lay!(format!(" {i}").align_w().fill_x(), length).fill_x(); let mut row2 = format!(" {name}"); - if let Some(PhrasePoolMode::Rename(phrase, _)) = mode { - if *focused && i == *phrase { row2 = format!("{row2}▄"); } + if let Some(PhrasesMode::Rename(phrase, _)) = mode { + if *focused && i == phrase { row2 = format!("{row2}▄"); } }; let row2 = TuiStyle::bold(row2, true); add(&col!(row1, row2).fill_x().bg(color.base.rgb))?; - Ok(if *focused && i == self.phrase { add(&CORNERS)?; }) + Ok(if *focused && i == phrase { add(&CORNERS)?; }) }) ); let border_color = if *focused {Color::Rgb(100, 110, 40)} else {Color::Rgb(70, 80, 50)}; let border = Lozenge(Style::default().bg(Color::Rgb(40, 50, 30)).fg(border_color)); let content = content.fill_xy().bg(Color::Rgb(28, 35, 25)).border(border); let title_color = if *focused {Color::Rgb(150, 160, 90)} else {Color::Rgb(120, 130, 100)}; - let upper_left = format!("[{}] Phrases", if self.entered {"■"} else {" "}); + let upper_left = format!("[{}] Phrases", if entered {"■"} else {" "}); let upper_right = format!("({})", phrases.len()); lay!( content, diff --git a/crates/tek_tui/src/tui_sequencer.rs b/crates/tek_tui/src/tui_sequencer.rs index 727f09b7..e840a1fb 100644 --- a/crates/tek_tui/src/tui_sequencer.rs +++ b/crates/tek_tui/src/tui_sequencer.rs @@ -3,14 +3,13 @@ use crate::*; impl TryFrom<&Arc>> for SequencerTui { type Error = Box; fn try_from (jack: &Arc>) -> Usually { - let clock = Arc::new(Clock::from(Instant::default())); - Ok(Self::new(SequencerView { + Ok(Self::new(SequencerTui { phrases: vec![], metronome: false, transport: jack.read().unwrap().transport(), jack: jack.clone(), focused: false, - focus: TransportViewFocus::PlayPause, + focus: TransportFocus::PlayPause, size: Measure::new(), phrases: vec![], editor: PhraseEditor::new(), @@ -60,6 +59,8 @@ pub struct SequencerTui { /// Sections in the sequencer app that may be focused #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum SequencerFocus { + /// The menu bar is focused + Menu, /// The transport (toolbar) is focused Transport, /// The phrase list (pool) is focused @@ -114,7 +115,7 @@ impl Content for SequencerStatusBar { } impl HasFocus for SequencerTui { - type Item = AppViewFocus; + type Item = SequencerFocus; } impl FocusEnter for SequencerTui { @@ -141,7 +142,7 @@ impl FocusEnter for SequencerTui { } impl FocusGrid for SequencerTui { - type Item = AppViewFocus; + type Item = SequencerFocus; fn focus_cursor (&self) -> (usize, usize) { self.cursor } @@ -149,12 +150,11 @@ impl FocusGrid for SequencerTui { &mut self.cursor } fn focus_layout (&self) -> &[&[Self::Item]] { - use AppViewFocus::*; use SequencerFocus::*; &[ - &[Menu, Menu ], - &[Content(Transport), Content(Transport) ], - &[Content(PhrasePool), Content(PhraseEditor)], + &[Menu, Menu ], + &[Transport, Transport ], + &[PhrasePool, PhraseEditor], ] } fn focus_update (&mut self) { @@ -273,7 +273,7 @@ impl PlayheadApi for SequencerTui { impl PlayerApi for SequencerTui {} impl TransportViewState for SequencerTui { - fn focus (&self) -> TransportViewFocus { + fn focus (&self) -> TransportFocus { self.focus } fn focused (&self) -> bool { diff --git a/crates/tek_tui/src/tui_sequencer_cmd.rs b/crates/tek_tui/src/tui_sequencer_cmd.rs index 72e27783..88ddcbc9 100644 --- a/crates/tek_tui/src/tui_sequencer_cmd.rs +++ b/crates/tek_tui/src/tui_sequencer_cmd.rs @@ -14,13 +14,12 @@ pub enum SequencerCommand { Clear, Clock(ClockCommand), Playhead(PlayheadCommand), - Phrases(PhrasePoolViewCommand), - Editor(PhraseEditorCommand), + Phrases(PhrasesCommand), + Editor(PhraseCommand), } impl InputToCommand for SequencerCommand { fn input_to_command (state: &SequencerTui, input: &TuiInput) -> Option { - use AppViewFocus::*; use FocusCommand::*; use SequencerCommand::*; match input.event() { @@ -33,35 +32,23 @@ impl InputToCommand for SequencerCommand { key!(KeyCode::Left) => Some(Self::Focus(Left)), key!(KeyCode::Right) => Some(Self::Focus(Right)), _ => Some(Self::App(match state.focused() { - Content(SequencerFocus::Transport) => - TransportCommand::input_to_command(&state, input) - .map(Transport), - Content(SequencerFocus::PhrasePool) => - PhrasePoolViewCommand::input_to_command(&state.phrases, input) - .map(Phrases), - Content(SequencerFocus::PhraseEditor) => - PhraseEditorCommand::input_to_command(&state.editor, input) - .map(Editor), + SequencerFocus::Transport => + TransportCommand::input_to_command(&state, input).map(Transport), + SequencerFocus::Phrases => + PhrasesCommand::input_to_command(&state.phrases, input).map(Phrases), + SequencerFocus::PhraseEditor => + PhraseCommand::input_to_command(&state.editor, input).map(Editor), _ => return None, })) } } } -impl Command for SequencerCommand { - fn execute (self, state: &mut SequencerTui) -> Perhaps { - use AppViewCommand::*; - match self { - Focus(cmd) => delegate(cmd, Focus, state), - App(cmd) => delegate(cmd, App, state), - } - } -} - impl Command for SequencerCommand { fn execute (self, state: &mut SequencerTui) -> Perhaps { use SequencerCommand::*; match self { + Focus(cmd) => delegate(cmd, Focus, state), Phrases(cmd) => delegate(cmd, Phrases, &mut state.phrases), Editor(cmd) => delegate(cmd, Editor, &mut state.editor), Transport(cmd) => delegate(cmd, Transport, &mut state.transport) diff --git a/crates/tek_tui/src/tui_transport.rs b/crates/tek_tui/src/tui_transport.rs index 6f4f40c9..1bd4f4c1 100644 --- a/crates/tek_tui/src/tui_transport.rs +++ b/crates/tek_tui/src/tui_transport.rs @@ -17,7 +17,7 @@ pub struct TransportTui { transport: jack::Transport, /// Enable metronome? metronome: bool, - focus: TransportViewFocus, + focus: TransportFocus, focused: bool, size: Measure, } @@ -31,7 +31,7 @@ impl TryFrom<&Arc>> for TransportTui { transport: jack.read().unwrap().transport(), jack: jack.clone(), focused: false, - focus: TransportViewFocus::PlayPause, + focus: TransportFocus::PlayPause, size: Measure::new(), }) } diff --git a/crates/tek_tui/src/tui_transport_cmd.rs b/crates/tek_tui/src/tui_transport_cmd.rs index a90a8d3e..cf2ba662 100644 --- a/crates/tek_tui/src/tui_transport_cmd.rs +++ b/crates/tek_tui/src/tui_transport_cmd.rs @@ -3,7 +3,7 @@ use crate::*; /// Handle input. impl Handle for TransportTui { fn handle (&mut self, from: &TuiInput) -> Perhaps { - AppViewCommand::::execute_with_state(self, from) + TransportCommand::execute_with_state(self, from) } } @@ -14,58 +14,46 @@ pub enum TransportCommand { Playhead(PlayheadCommand), } -impl InputToCommand for AppViewCommand { - fn input_to_command (app: &TransportTui, input: &TuiInput) -> Option { - use KeyCode::{Left, Right}; - use FocusCommand::{Prev, Next}; - use TransportCommand::{Focus, Clock, Playhead}; - Some(match input.event() { - key!(Left) => Focus(Prev), - key!(Right) => Focus(Next), - _ => TransportCommand::input_to_command(&app, input).map(App)? - }) - } -} - impl InputToCommand for TransportCommand { fn input_to_command (state: &T, input: &TuiInput) -> Option { use KeyCode::Char; - use AppViewFocus::Content; use ClockCommand::{SetBpm, SetQuant, SetSync}; - use TransportViewFocus as Focus; - use TransportCommand::{Clock, Playhead}; + use TransportFocus as Focused; + use TransportCommand::{Focus, Clock, Playhead}; let focused = state.focused(); Some(match input.event() { + key!(Left) => Focus(FocusCommand::Prev), + key!(Right) => Focus(FocusCommand::Next), key!(Char('.')) => match focused { - Content(Focus::Bpm) => Clock(SetBpm(state.bpm().get() + 1.0)), - Content(Focus::Quant) => Clock(SetQuant(state.next_quant())), - Content(Focus::Sync) => Clock(SetSync(state.next_sync())), - Content(Focus::PlayPause) => Playhead(todo!()), - Content(Focus::Clock) => Playhead(todo!()), + Focused::Bpm => Clock(SetBpm(state.bpm().get() + 1.0)), + Focused::Quant => Clock(SetQuant(state.next_quant())), + Focused::Sync => Clock(SetSync(state.next_sync())), + Focused::PlayPause => Playhead(todo!()), + Focused::Clock => Playhead(todo!()), _ => {todo!()} }, key!(KeyCode::Char(',')) => match focused { - Content(Focus::Bpm) => Clock(SetBpm(state.bpm().get() - 1.0)), - Content(Focus::Quant) => Clock(SetQuant(state.prev_quant())), - Content(Focus::Sync) => Clock(SetSync(state.prev_sync())), - Content(Focus::PlayPause) => Playhead(todo!()), - Content(Focus::Clock) => Playhead(todo!()), + Focused::Bpm => Clock(SetBpm(state.bpm().get() - 1.0)), + Focused::Quant => Clock(SetQuant(state.prev_quant())), + Focused::Sync => Clock(SetSync(state.prev_sync())), + Focused::PlayPause => Playhead(todo!()), + Focused::Clock => Playhead(todo!()), _ => {todo!()} }, key!(KeyCode::Char('>')) => match focused { - Content(Focus::Bpm) => Clock(SetBpm(state.bpm().get() + 0.001)), - Content(Focus::Quant) => Clock(SetQuant(state.next_quant())), - Content(Focus::Sync) => Clock(SetSync(state.next_sync())), - Content(Focus::PlayPause) => Playhead(todo!()), - Content(Focus::Clock) => Playhead(todo!()), + Focused::Bpm => Clock(SetBpm(state.bpm().get() + 0.001)), + Focused::Quant => Clock(SetQuant(state.next_quant())), + Focused::Sync => Clock(SetSync(state.next_sync())), + Focused::PlayPause => Playhead(todo!()), + Focused::Clock => Playhead(todo!()), _ => {todo!()} }, key!(KeyCode::Char('<')) => match focused { - Content(Focus::Bpm) => Clock(SetBpm(state.bpm().get() - 0.001)), - Content(Focus::Quant) => Clock(SetQuant(state.prev_quant())), - Content(Focus::Sync) => Clock(SetSync(state.prev_sync())), - Content(Focus::PlayPause) => Playhead(todo!()), - Content(Focus::Clock) => Playhead(todo!()), + Focused::Bpm => Clock(SetBpm(state.bpm().get() - 0.001)), + Focused::Quant => Clock(SetQuant(state.prev_quant())), + Focused::Sync => Clock(SetSync(state.prev_sync())), + Focused::PlayPause => Playhead(todo!()), + Focused::Clock => Playhead(todo!()), _ => {todo!()} }, _ => return None @@ -73,7 +61,7 @@ impl InputToCommand for TransportCommand { } } -pub trait TransportControl: FocusGrid> { +pub trait TransportControl: FocusGrid { fn quant (&self) -> &Quantize; fn bpm (&self) -> &BeatsPerMinute; fn next_quant (&self) -> f64 { @@ -103,31 +91,14 @@ impl TransportControl for TransportTui { } } -impl Command for AppViewCommand { - fn execute (self, state: &mut TransportTui) -> Perhaps { - use AppViewCommand::{Focus, App}; - use FocusCommand::{Next, Prev}; - Ok(Some(match self { - App(command) => if let Some(undo) = TransportCommand::execute(command, &mut state.app)? { - App(undo) - } else { - return Ok(None) - }, - Focus(command) => Focus(match command { - Next => { todo!() }, - Prev => { todo!() }, - _ => { todo!() } - }), - _ => todo!() - })) - } -} - impl Command for TransportCommand { fn execute (self, state: &mut T) -> Perhaps { - use TransportCommand::{Clock, Playhead}; + use TransportCommand::{Focus, Clock, Playhead}; use ClockCommand::{SetBpm, SetQuant, SetSync}; Ok(Some(match self { + Focus(Next) => { todo!() } + Focus(Prev) => { todo!() }, + Focus(_) => { unimplemented!() }, Clock(SetBpm(bpm)) => Clock(SetBpm(state.bpm().set(bpm))), Clock(SetQuant(quant)) => Clock(SetQuant(state.quant().set(quant))), Clock(SetSync(sync)) => Clock(SetSync(state.sync().set(sync))), diff --git a/crates/tek_tui/src/tui_transport_focus.rs b/crates/tek_tui/src/tui_transport_focus.rs index ebb71bdd..a55d5ad9 100644 --- a/crates/tek_tui/src/tui_transport_focus.rs +++ b/crates/tek_tui/src/tui_transport_focus.rs @@ -2,7 +2,8 @@ use crate::*; /// Which item of the transport toolbar is focused #[derive(Clone, Copy, Debug, PartialEq)] -pub enum TransportViewFocus { +pub enum TransportFocus { + Menu, Bpm, Sync, PlayPause, @@ -10,7 +11,7 @@ pub enum TransportViewFocus { Quant, } -impl TransportViewFocus { +impl TransportFocus { pub fn next (&mut self) { *self = match self { Self::PlayPause => Self::Bpm, @@ -40,7 +41,7 @@ impl TransportViewFocus { } impl HasFocus for TransportTui { - type Item = AppViewFocus; + type Item = TransportFocus; } impl FocusEnter for TransportTui { @@ -60,7 +61,7 @@ impl FocusEnter for TransportTui { } impl FocusGrid for TransportTui { - type Item = AppViewFocus; + type Item = TransportFocus; fn focus_cursor (&self) -> (usize, usize) { self.cursor } @@ -68,17 +69,10 @@ impl FocusGrid for TransportTui { &mut self.cursor } fn focus_layout (&self) -> &[&[Self::Item]] { - use AppViewFocus::*; - use TransportViewFocus::*; + use TransportFocus::*; &[ &[Menu], - &[ - Content(Bpm), - Content(Sync), - Content(PlayPause), - Content(Clock), - Content(Quant), - ], + &[Bpm, Sync, PlayPause, Clock, Quant], ] } fn focus_update (&mut self) { diff --git a/crates/tek_tui/src/tui_transport_view.rs b/crates/tek_tui/src/tui_transport_view.rs index cca53508..3fc3e8c2 100644 --- a/crates/tek_tui/src/tui_transport_view.rs +++ b/crates/tek_tui/src/tui_transport_view.rs @@ -13,7 +13,7 @@ impl Widget for TransportTui { pub struct TransportView<'a, T: TransportViewState>(pub &'a T); pub trait TransportViewState: Send + Sync { - fn focus (&self) -> TransportViewFocus; + fn focus (&self) -> TransportFocus; fn focused (&self) -> bool; fn transport_state (&self) -> Option; fn bpm_value (&self) -> f64; @@ -23,7 +23,7 @@ pub trait TransportViewState: Send + Sync { } impl TransportViewState for TransportTui { - fn focus (&self) -> TransportViewFocus { + fn focus (&self) -> TransportFocus { self.focus } fn focused (&self) -> bool { @@ -51,7 +51,7 @@ impl<'a, T: TransportViewState> Content for TransportView<'a, T> { fn content (&self) -> impl Widget { let state = self.0; lay!( - state.focus().wrap(state.focused(), TransportViewFocus::PlayPause, &Styled( + state.focus().wrap(state.focused(), TransportFocus::PlayPause, &Styled( None, match state.transport_state() { Some(TransportState::Rolling) => "▶ PLAYING", @@ -62,19 +62,19 @@ impl<'a, T: TransportViewState> Content for TransportView<'a, T> { ).min_xy(11, 2).push_x(1)).align_x().fill_x(), row!( - state.focus().wrap(state.focused(), TransportViewFocus::Bpm, &Outset::X(1u16, { + state.focus().wrap(state.focused(), TransportFocus::Bpm, &Outset::X(1u16, { let bpm = state.bpm_value(); row! { "BPM ", format!("{}.{:03}", bpm as usize, (bpm * 1000.0) % 1000.0) } })), - //let quant = state.focus().wrap(state.focused(), TransportViewFocus::Quant, &Outset::X(1u16, row! { + //let quant = state.focus().wrap(state.focused(), TransportFocus::Quant, &Outset::X(1u16, row! { //"QUANT ", ppq_to_name(state.0.quant as usize) //})), - state.focus().wrap(state.focused(), TransportViewFocus::Sync, &Outset::X(1u16, row! { + state.focus().wrap(state.focused(), TransportFocus::Sync, &Outset::X(1u16, row! { "SYNC ", pulses_to_name(state.sync_value() as usize) })) ).align_w().fill_x(), - state.focus().wrap(state.focused(), TransportViewFocus::Clock, &{ + state.focus().wrap(state.focused(), TransportFocus::Clock, &{ let time1 = state.format_beat(); let time2 = state.format_msu(); row!("B" ,time1.as_str(), " T", time2.as_str()).outset_x(1)