From 37ac7823af29c0bf2d33d0a0532d4a7bffffe796 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Mon, 18 Nov 2024 22:05:11 +0100 Subject: [PATCH] wip: p.55, e=95 --- crates/tek_api/src/api_clock.rs | 13 +++ crates/tek_api/src/api_phrase.rs | 2 +- crates/tek_tui/src/tui_apis.rs | 16 ++-- crates/tek_tui/src/tui_command.rs | 147 ++++++++++++++--------------- crates/tek_tui/src/tui_content.rs | 18 ++-- crates/tek_tui/src/tui_control.rs | 148 ++++++++++++++++++++---------- crates/tek_tui/src/tui_init.rs | 1 - crates/tek_tui/src/tui_input.rs | 92 +++++++++---------- crates/tek_tui/src/tui_model.rs | 8 +- crates/tek_tui/src/tui_view.rs | 46 +++++----- 10 files changed, 272 insertions(+), 219 deletions(-) diff --git a/crates/tek_api/src/api_clock.rs b/crates/tek_api/src/api_clock.rs index b023967e..436c73cf 100644 --- a/crates/tek_api/src/api_clock.rs +++ b/crates/tek_api/src/api_clock.rs @@ -35,4 +35,17 @@ pub trait ClockApi: Send + Sync { fn ppq (&self) -> &PulsesPerQuaver { &self.timebase().ppq } + + fn next_quant (&self) -> f64 { + next_note_length(self.quant().get() as usize) as f64 + } + fn prev_quant (&self) -> f64 { + prev_note_length(self.quant().get() as usize) as f64 + } + fn next_sync (&self) -> f64 { + next_note_length(self.sync().get() as usize) as f64 + } + fn prev_sync (&self) -> f64 { + prev_note_length(self.sync().get() as usize) as f64 + } } diff --git a/crates/tek_api/src/api_phrase.rs b/crates/tek_api/src/api_phrase.rs index a105ec3c..b43c3554 100644 --- a/crates/tek_api/src/api_phrase.rs +++ b/crates/tek_api/src/api_phrase.rs @@ -5,7 +5,7 @@ pub trait HasPhrases { fn phrases_mut (&mut self) -> &mut Vec>>; } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub enum PhrasePoolCommand { Add(usize), Delete(usize), diff --git a/crates/tek_tui/src/tui_apis.rs b/crates/tek_tui/src/tui_apis.rs index 7f4bfb28..ce985dd8 100644 --- a/crates/tek_tui/src/tui_apis.rs +++ b/crates/tek_tui/src/tui_apis.rs @@ -256,15 +256,13 @@ impl ArrangerTui { } pub fn activate (&mut self) { - let scenes = self.scenes(); - let tracks = self.tracks_mut(); - match self.selected { + let selected = self.selected; + match selected { ArrangerSelection::Scene(s) => { - for (t, track) in tracks.iter_mut().enumerate() { - let player = &mut track.player; - let clip = scenes[s].clips[t].as_ref(); - if player.phrase.is_some() || clip.is_some() { - player.enqueue_next(clip); + for (t, track) in self.tracks_mut().iter_mut().enumerate() { + let clip = self.scenes()[s].clips[t].as_ref(); + if track.play_phrase.is_some() || clip.is_some() { + track.enqueue_next(clip); } } // TODO make transport available here, so that @@ -274,7 +272,7 @@ impl ArrangerTui { //} }, ArrangerSelection::Clip(t, s) => { - tracks[t].player.enqueue_next(scenes[s].clips[t]); + self.tracks_mut()[t].enqueue_next(self.scenes()[s].clips[t].as_ref()); }, _ => {} } diff --git a/crates/tek_tui/src/tui_command.rs b/crates/tek_tui/src/tui_command.rs index 14684328..ce818dbb 100644 --- a/crates/tek_tui/src/tui_command.rs +++ b/crates/tek_tui/src/tui_command.rs @@ -97,20 +97,26 @@ impl Command for TransportCommand { } } -impl Command for SequencerCommand { - fn execute (self, state: &mut SequencerTui) -> Perhaps { +impl Command for SequencerCommand +where + T: FocusGrid + PhrasesControl + PhraseControl + ClockApi + PlayheadApi +{ + fn execute (self, state: T) -> Perhaps { use SequencerCommand::*; match self { - Focus(cmd) => delegate(cmd, Focus, &mut state), - Phrases(cmd) => delegate(cmd, Phrases, &mut state.phrases), - Editor(cmd) => delegate(cmd, Editor, &mut state.editor), - Clock(cmd) => delegate(cmd, Clock, &mut state.transport), - Playhead(cmd) => delegate(cmd, Playhead, &mut state.transport) + Focus(cmd) => delegate(cmd, Focus, state), + Phrases(cmd) => delegate(cmd, Phrases, state), + Editor(cmd) => delegate(cmd, Editor, state), + Clock(cmd) => delegate(cmd, Clock, state), + Playhead(cmd) => delegate(cmd, Playhead, state) } } } -impl Command for ArrangerCommand { +impl Command for ArrangerCommand +where + T: FocusGrid + ArrangerControl + HasPhrases + PhraseControl + ClockApi + PlayheadApi +{ fn execute (self, state: &mut T) -> Perhaps { use ArrangerCommand::*; match self { @@ -134,71 +140,59 @@ impl Command for ArrangerCommand { } } -impl Command for ArrangerSceneCommand { +impl Command for ArrangerSceneCommand { fn execute (self, state: &mut T) -> Perhaps { - todo!() + todo!(); + Ok(None) } } -impl Command for ArrangerTrackCommand { +impl Command for ArrangerTrackCommand { fn execute (self, state: &mut T) -> Perhaps { - todo!() + todo!(); + Ok(None) } } -impl Command for ArrangerClipCommand { +impl Command for ArrangerClipCommand { fn execute (self, state: &mut T) -> Perhaps { - todo!() + todo!(); + Ok(None) } } -impl Command for PhrasesCommand { - fn execute (self, view: &mut PhrasesTui) -> Perhaps { +impl Command for PhrasesCommand { + fn execute (self, state: &mut T) -> Perhaps { use PhraseRenameCommand as Rename; use PhraseLengthCommand as Length; match self { Self::Select(phrase) => { - view.phrase = phrase + state.phrase = phrase }, Self::Edit(command) => { - return Ok(command.execute(&mut view)?.map(Self::Edit)) + return Ok(command.execute(&mut state)?.map(Self::Edit)) } Self::Rename(command) => match command { - Rename::Begin => { - view.mode = Some(PhrasesMode::Rename( - view.phrase, - view.phrases[view.phrase].read().unwrap().name.clone() - )) - }, - _ => { - return Ok(command.execute(view)?.map(Self::Rename)) - } + Rename::Begin => self.phrases_rename_begin(), + _ => return Ok(command.execute(state)?.map(Self::Rename)), }, Self::Length(command) => match command { - Length::Begin => { - view.mode = Some(PhrasesMode::Length( - view.phrase, - view.phrases[view.phrase].read().unwrap().length, - PhraseLengthFocus::Bar - )) - }, - _ => { - return Ok(command.execute(view)?.map(Self::Length)) - } + Length::Begin => self.phrases_length_begin(), + _ => return Ok(command.execute(state)?.map(Self::Length)), }, } Ok(None) } } -impl Command for PhraseLengthCommand { - fn execute (self, view: &mut PhrasesTui) -> Perhaps { +impl Command for PhraseLengthCommand { + fn execute (self, state: &mut T) -> Perhaps { use PhraseLengthFocus::*; use PhraseLengthCommand::*; - if let Some(PhrasesMode::Length(phrase, ref mut length, ref mut focus)) = view.mode { + if let Some(PhrasesMode::Length(phrase, ref mut length, ref mut focus)) = state.mode { match self { Self::Cancel => { - view.mode = None; + state.mode = None; }, Self::Prev => { focus.prev() @@ -217,21 +211,17 @@ impl Command for PhraseLengthCommand { Tick => { *length = length.saturating_sub(1) }, }, Self::Set(length) => { - let mut phrase = view.phrases[phrase].write().unwrap(); + let mut phrase = state.phrases[phrase].write().unwrap(); let old_length = phrase.length; phrase.length = length; - view.mode = None; + state.mode = None; return Ok(Some(Self::Set(old_length))) }, _ => unreachable!() } Ok(None) } else if self == Begin { - view.mode = Some(PhrasesMode::Length( - view.phrase, - view.phrases[view.phrase].read().unwrap().length, - PhraseLengthFocus::Bar - )); + self.phrases_length_begin(); Ok(None) } else { unreachable!() @@ -239,32 +229,29 @@ impl Command for PhraseLengthCommand { } } -impl Command for PhraseRenameCommand { - fn execute (self, view: &mut PhrasesTui) -> Perhaps { +impl Command for PhraseRenameCommand { + fn execute (self, state: &mut T) -> Perhaps { use PhraseRenameCommand::*; - if let Some(PhrasesMode::Rename(phrase, ref mut old_name)) = view.mode { + if let Some(PhrasesMode::Rename(phrase, ref mut old_name)) = state.mode { match self { Set(s) => { - view.phrases[phrase].write().unwrap().name = s.into(); + state.phrases[phrase].write().unwrap().name = s.into(); return Ok(Some(Self::Set(old_name.clone()))) }, Confirm => { let old_name = old_name.clone(); - view.mode = None; + state.mode = None; return Ok(Some(Self::Set(old_name))) }, Cancel => { - let mut phrase = view.phrases[phrase].write().unwrap(); + let mut phrase = state.phrases[phrase].write().unwrap(); phrase.name = old_name.clone(); }, _ => unreachable!() }; Ok(None) } else if self == Begin { - view.mode = Some(PhrasesMode::Rename( - view.phrase, - view.phrases[view.phrase].read().unwrap().name.clone() - )); + self.phrases_rename_begin(); Ok(None) } else { unreachable!() @@ -272,7 +259,7 @@ impl Command for PhraseRenameCommand { } } -impl Command for PhraseCommand { +impl Command for PhraseCommand { //fn translate (self, state: &PhraseTui) -> Self { //use PhraseCommand::*; //match self { @@ -283,7 +270,7 @@ impl Command for PhraseCommand { //_ => self //} //} - fn execute (self, state: &mut PhraseTui) -> Perhaps { + fn execute (self, state: &mut T) -> Perhaps { use PhraseCommand::*; match self.translate(state) { ToggleDirection => { @@ -296,58 +283,58 @@ impl Command for PhraseCommand { state.entered = false; }, TimeZoomOut => { - let scale = state.time_axis.read().unwrap().scale; - state.time_axis.write().unwrap().scale = next_note_length(scale) + let scale = state.time_axis().read().unwrap().scale; + state.time_axis().write().unwrap().scale = next_note_length(scale) }, TimeZoomIn => { - let scale = state.time_axis.read().unwrap().scale; - state.time_axis.write().unwrap().scale = prev_note_length(scale) + let scale = state.time_axis().read().unwrap().scale; + state.time_axis().write().unwrap().scale = prev_note_length(scale) }, TimeCursorDec => { - let scale = state.time_axis.read().unwrap().scale; - state.time_axis.write().unwrap().point_dec(scale); + let scale = state.time_axis().read().unwrap().scale; + state.time_axis().write().unwrap().point_dec(scale); }, TimeCursorInc => { - let scale = state.time_axis.read().unwrap().scale; - state.time_axis.write().unwrap().point_inc(scale); + let scale = state.time_axis().read().unwrap().scale; + state.time_axis().write().unwrap().point_inc(scale); }, TimeScrollDec => { - let scale = state.time_axis.read().unwrap().scale; - state.time_axis.write().unwrap().start_dec(scale); + let scale = state.time_axis().read().unwrap().scale; + state.time_axis().write().unwrap().start_dec(scale); }, TimeScrollInc => { - let scale = state.time_axis.read().unwrap().scale; - state.time_axis.write().unwrap().start_inc(scale); + let scale = state.time_axis().read().unwrap().scale; + state.time_axis().write().unwrap().start_inc(scale); }, NoteCursorDec => { - let mut axis = state.note_axis.write().unwrap(); + let mut axis = state.note_axis().write().unwrap(); axis.point_inc(1); if let Some(point) = axis.point { if point > 73 { axis.point = Some(73); } } }, NoteCursorInc => { - let mut axis = state.note_axis.write().unwrap(); + let mut axis = state.note_axis().write().unwrap(); axis.point_dec(1); if let Some(point) = axis.point { if point < axis.start { axis.start = (point / 2) * 2; } } }, NoteScrollDec => { - state.note_axis.write().unwrap().start_inc(1); + state.note_axis().write().unwrap().start_inc(1); }, NoteScrollInc => { - state.note_axis.write().unwrap().start_dec(1); + state.note_axis().write().unwrap().start_dec(1); }, NoteLengthDec => { - state.note_len = prev_note_length(state.note_len) + *state.note_len_mut() = prev_note_length(state.note_len()) }, NoteLengthInc => { - state.note_len = next_note_length(state.note_len) + *state.note_len_mut() = next_note_length(state.note_len()) }, NotePageUp => { - let mut axis = state.note_axis.write().unwrap(); + let mut axis = state.note_axis().write().unwrap(); axis.start_dec(3); axis.point_dec(3); }, NotePageDown => { - let mut axis = state.note_axis.write().unwrap(); + let mut axis = state.note_axis().write().unwrap(); axis.start_inc(3); axis.point_inc(3); }, diff --git a/crates/tek_tui/src/tui_content.rs b/crates/tek_tui/src/tui_content.rs index 34aeef4a..52d64017 100644 --- a/crates/tek_tui/src/tui_content.rs +++ b/crates/tek_tui/src/tui_content.rs @@ -4,8 +4,9 @@ impl<'a, T: TransportViewState> Content for TransportView<'a, T> { type Engine = Tui; fn content (&self) -> impl Widget { let state = self.0; + let focused = state.transport_focused(); lay!( - state.focus().wrap(state.is_focused(), TransportFocus::PlayPause, &Styled( + state.transport_selected().wrap(focused, TransportFocus::PlayPause, &Styled( None, match state.transport_state() { Some(TransportState::Rolling) => "▶ PLAYING", @@ -16,19 +17,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.is_focused(), TransportFocus::Bpm, &Outset::X(1u16, { + state.transport_selected().wrap(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(), TransportFocus::Quant, &Outset::X(1u16, row! { //"QUANT ", ppq_to_name(state.0.quant as usize) //})), - state.focus().wrap(state.is_focused(), TransportFocus::Sync, &Outset::X(1u16, row! { + state.transport_selected().wrap(focused, TransportFocus::Sync, &Outset::X(1u16, row! { "SYNC ", pulses_to_name(state.sync_value() as usize) })) ).align_w().fill_x(), - state.focus().wrap(state.is_focused(), TransportFocus::Clock, &{ + state.transport_selected().wrap(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) @@ -55,6 +56,7 @@ impl Content for SequencerTui { impl Content for ArrangerTui { type Engine = Tui; fn content (&self) -> impl Widget { + let arranger_focused = self.arranger_focused(); Split::up( 1, widget(&TransportView(self)), @@ -73,14 +75,14 @@ impl Content for ArrangerTui { .grow_y(1) .border(Lozenge(Style::default() .bg(TuiTheme::border_bg()) - .fg(TuiTheme::border_fg(ArrangerViewState::focused(self))))), + .fg(TuiTheme::border_fg(arranger_focused)))), widget(&self.size), widget(&format!("[{}] Arranger", if self.entered { "■" } else { " " })) - .fg(TuiTheme::title_fg(ArrangerViewState::focused(self))) + .fg(TuiTheme::title_fg(arranger_focused)) .push_x(1), ), Split::right( @@ -97,7 +99,7 @@ impl Content for ArrangerTui { impl<'a, T: PhrasesViewState> Content for PhrasesView<'a, T> { type Engine = Tui; fn content (&self) -> impl Widget { - let focused = self.0.focused(); + let focused = self.0.phrases_focused(); let entered = self.0.entered(); let phrases = self.0.phrases(); let selected_phrase = self.0.phrase(); @@ -147,7 +149,7 @@ impl<'a, T: PhraseViewState> Content for PhraseView<'a, T> { fn content (&self) -> impl Widget { let phrase = self.0.phrase(); let size = self.0.size(); - let focused = self.0.focused(); + let focused = self.0.phrase_focused(); let entered = self.0.entered(); let keys = self.0.keys(); let buffer = self.0.buffer(); diff --git a/crates/tek_tui/src/tui_control.rs b/crates/tek_tui/src/tui_control.rs index e483942f..c286930a 100644 --- a/crates/tek_tui/src/tui_control.rs +++ b/crates/tek_tui/src/tui_control.rs @@ -1,58 +1,16 @@ use crate::*; -pub trait TransportControl { - fn quant (&self) -> &Quantize; - fn bpm (&self) -> &BeatsPerMinute; - fn next_quant (&self) -> f64 { - next_note_length(self.quant().get() as usize) as f64 - } - fn prev_quant (&self) -> f64 { - prev_note_length(self.quant().get() as usize) as f64 - } - fn sync (&self) -> &LaunchSync; - fn next_sync (&self) -> f64 { - next_note_length(self.sync().get() as usize) as f64 - } - fn prev_sync (&self) -> f64 { - prev_note_length(self.sync().get() as usize) as f64 - } -} +pub trait TransportControl: ClockApi {} -impl TransportControl for TransportTui { - fn bpm (&self) -> &BeatsPerMinute { - &self.current.timebase.bpm - } - fn quant (&self) -> &Quantize { - &self.quant - } - fn sync (&self) -> &LaunchSync { - &self.sync - } -} +impl TransportControl for TransportTui {} -impl TransportControl for SequencerTui { - fn bpm (&self) -> &BeatsPerMinute { - &self.current.timebase.bpm - } - fn quant (&self) -> &Quantize { - &self.quant - } - fn sync (&self) -> &LaunchSync { - &self.sync - } -} +impl TransportControl for SequencerTui {} -impl TransportControl for ArrangerTui { - fn bpm (&self) -> &BeatsPerMinute { - &self.current.timebase.bpm - } - fn quant (&self) -> &Quantize { - &self.quant - } - fn sync (&self) -> &LaunchSync { - &self.sync - } -} +impl TransportControl for ArrangerTui {} + +pub trait SequencerControl {} + +impl SequencerControl for SequencerTui {} pub trait ArrangerControl { fn selected (&self) -> ArrangerSelection; @@ -63,3 +21,93 @@ impl ArrangerControl for ArrangerTui { self.selected } } + +pub trait PhrasesControl { + fn phrases_mode (&self) -> &Option; + fn phrases_mode_mut (&mut self) -> &mut Option; + fn phrase_rename_begin (&mut self) { + *self.phrases_mode_mut() = Some(PhrasesMode::Rename( + self.phrase, + self.phrases[self.phrase].read().unwrap().name.clone() + )) + } + fn phrase_length_begin (&mut self) { + *self.phrases_mode_mut() = Some(PhrasesMode::Length( + self.phrase, + self.phrases[self.phrase].read().unwrap().length, + PhraseLengthFocus::Bar + )) + } +} + +impl PhrasesControl for SequencerTui { + fn phrases_mode (&self) -> &Option { + &self.phrases_mode + } + fn phrases_mode_mut (&mut self) -> &mut Option { + &mut self.phrases_mode + } +} + +impl PhrasesControl for ArrangerTui { + fn phrases_mode (&self) -> &Option { + &self.phrases_mode + } + fn phrases_mode_mut (&mut self) -> &mut Option { + &mut self.phrases_mode + } +} + +impl PhrasesControl for PhrasesTui { + fn phrases_mode (&self) -> &Option { + &self.mode + } + fn phrases_mode_mut (&mut self) -> &mut Option { + &mut self.mode + } +} + + +pub trait PhraseControl { + fn phrase_entered (&self) -> bool; + fn time_axis (&self) -> &RwLock>; + fn note_axis (&self) -> &RwLock>; + fn note_len (&self) -> usize; + fn note_len_mut (&mut self) -> &mut usize; +} + +impl PhraseControl for SequencerTui { + fn phrase_entered (&self) -> bool { + self.entered && self.focused() == SequencerFocus::PhraseEditor + } + fn time_axis (&self) -> &RwLock> { + todo!() + } + fn note_axis (&self) -> &RwLock> { + todo!() + } + fn note_len (&self) -> usize { + todo!() + } + fn note_len_mut (&mut self) -> &mut usize { + todo!() + } +} + +impl PhraseControl for ArrangerTui { + fn phrase_entered (&self) -> bool { + self.entered && self.focused() == ArrangerFocus::PhraseEditor + } + fn time_axis (&self) -> &RwLock> { + todo!() + } + fn note_axis (&self) -> &RwLock> { + todo!() + } + fn note_len (&self) -> usize { + todo!() + } + fn note_len_mut (&mut self) -> &mut usize { + todo!() + } +} diff --git a/crates/tek_tui/src/tui_init.rs b/crates/tek_tui/src/tui_init.rs index 11a552db..fb9ee41e 100644 --- a/crates/tek_tui/src/tui_init.rs +++ b/crates/tek_tui/src/tui_init.rs @@ -24,7 +24,6 @@ impl TryFrom<&Arc>> for SequencerTui { transport: jack.read().unwrap().transport(), jack: jack.clone(), focused: false, - focus: TransportFocus::PlayPause, size: Measure::new(), }.into(), None, None)) } diff --git a/crates/tek_tui/src/tui_input.rs b/crates/tek_tui/src/tui_input.rs index 22761b4d..b69a1235 100644 --- a/crates/tek_tui/src/tui_input.rs +++ b/crates/tek_tui/src/tui_input.rs @@ -60,10 +60,10 @@ impl InputToCommand for SequencerCommand { key!(KeyCode::Down) => Some(Self::Focus(Down)), key!(KeyCode::Left) => Some(Self::Focus(Left)), key!(KeyCode::Right) => Some(Self::Focus(Right)), - _ => Some(Self::App(match state.focused() { + _ => Some(match state.focused() { SequencerFocus::Transport => { use TransportCommand::{Clock, Playhead}; - match TransportCommand::input_to_command(view, input)? { + match TransportCommand::input_to_command(state, input)? { Clock(command) => { todo!() }, @@ -72,18 +72,20 @@ impl InputToCommand for SequencerCommand { }, } }, - SequencerFocus::Phrases => - PhrasesCommand::input_to_command(&state.phrases, input).map(Phrases), - SequencerFocus::PhraseEditor => - PhraseCommand::input_to_command(&state.editor, input).map(Editor), + SequencerFocus::Phrases => { + PhrasesCommand::input_to_command(state, input).map(Phrases) + }, + SequencerFocus::PhraseEditor => { + PhraseCommand::input_to_command(state, input).map(Editor) + }, _ => return None, - })) + }) } } } impl InputToCommand for ArrangerCommand { - fn input_to_command (view: &T, input: &TuiInput) -> Option { + fn input_to_command (state: &T, input: &TuiInput) -> Option { use FocusCommand::*; use ArrangerCommand::*; Some(match input.event() { @@ -97,13 +99,11 @@ impl InputToCommand for ArrangerCommand { key!(KeyCode::Right) => Self::Focus(Right), key!(KeyCode::Enter) => Self::Focus(Enter), key!(KeyCode::Esc) => Self::Focus(Exit), - key!(KeyCode::Char(' ')) => { - Self::App(Playhead(PlayheadCommand::Play(None))) - }, - _ => Self::App(match view.focused() { + key!(KeyCode::Char(' ')) => Self::Playhead(PlayheadCommand::Play(None)), + _ => match state.focused() { ArrangerFocus::Transport => { use TransportCommand::{Clock, Playhead}; - match TransportCommand::input_to_command(view, input)? { + match TransportCommand::input_to_command(state, input)? { Clock(command) => { todo!() }, @@ -113,14 +113,14 @@ impl InputToCommand for ArrangerCommand { } }, ArrangerFocus::PhraseEditor => Editor( - PhraseCommand::input_to_command(&view.editor, input)? + PhraseCommand::input_to_command(state, input)? ), ArrangerFocus::PhrasePool => match input.event() { key!(KeyCode::Char('e')) => EditPhrase( - Some(view.phrase().clone()) + Some(state.phrase().clone()) ), _ => Phrases( - PhrasePoolCommand::input_to_command(view, input)? + PhrasePoolCommand::input_to_command(state, input)? ) }, ArrangerFocus::Arranger => { @@ -129,32 +129,32 @@ impl InputToCommand for ArrangerCommand { use ArrangerClipCommand as Clip; use ArrangerSceneCommand as Scene; match input.event() { - key!(KeyCode::Char('e')) => EditPhrase(view.phrase()), + key!(KeyCode::Char('e')) => EditPhrase(state.phrase()), _ => match input.event() { // FIXME: boundary conditions - key!(KeyCode::Up) => match view.selected() { + key!(KeyCode::Up) => match state.selected() { Select::Mix => return None, Select::Track(t) => return None, Select::Scene(s) => Select(Select::Scene(s - 1)), Select::Clip(t, s) => Select(Select::Clip(t, s - 1)), }, - key!(KeyCode::Down) => match view.selected() { + key!(KeyCode::Down) => match state.selected() { Select::Mix => Select(Select::Scene(0)), Select::Track(t) => Select(Select::Clip(t, 0)), Select::Scene(s) => Select(Select::Scene(s + 1)), Select::Clip(t, s) => Select(Select::Clip(t, s + 1)), }, - key!(KeyCode::Left) => match view.selected() { + key!(KeyCode::Left) => match state.selected() { Select::Mix => return None, Select::Track(t) => Select(Select::Track(t - 1)), Select::Scene(s) => return None, Select::Clip(t, s) => Select(Select::Clip(t - 1, s)), }, - key!(KeyCode::Right) => match view.selected() { + key!(KeyCode::Right) => match state.selected() { Select::Mix => return None, Select::Track(t) => Select(Select::Track(t + 1)), Select::Scene(s) => Select(Select::Clip(0, s)), @@ -169,44 +169,44 @@ impl InputToCommand for ArrangerCommand { key!(KeyCode::Char('-')) => Zoom(0), - key!(KeyCode::Char('`')) => { todo!("toggle view mode") }, + key!(KeyCode::Char('`')) => { todo!("toggle state mode") }, - key!(KeyCode::Char(',')) => match view.selected() { + key!(KeyCode::Char(',')) => match state.selected() { Select::Mix => Zoom(0), Select::Track(t) => Track(Track::Swap(t, t - 1)), Select::Scene(s) => Scene(Scene::Swap(s, s - 1)), Select::Clip(t, s) => Clip(Clip::Set(t, s, None)), }, - key!(KeyCode::Char('.')) => match view.selected() { + key!(KeyCode::Char('.')) => match state.selected() { Select::Mix => Zoom(0), Select::Track(t) => Track(Track::Swap(t, t + 1)), Select::Scene(s) => Scene(Scene::Swap(s, s + 1)), Select::Clip(t, s) => Clip(Clip::Set(t, s, None)), }, - key!(KeyCode::Char('<')) => match view.selected() { + key!(KeyCode::Char('<')) => match state.selected() { Select::Mix => Zoom(0), Select::Track(t) => Track(Track::Swap(t, t - 1)), Select::Scene(s) => Scene(Scene::Swap(s, s - 1)), Select::Clip(t, s) => Clip(Clip::Set(t, s, None)), }, - key!(KeyCode::Char('>')) => match view.selected() { + key!(KeyCode::Char('>')) => match state.selected() { Select::Mix => Zoom(0), Select::Track(t) => Track(Track::Swap(t, t + 1)), Select::Scene(s) => Scene(Scene::Swap(s, s + 1)), Select::Clip(t, s) => Clip(Clip::Set(t, s, None)), }, - key!(KeyCode::Enter) => match view.selected() { + key!(KeyCode::Enter) => match state.selected() { Select::Mix => return None, Select::Track(t) => return None, Select::Scene(s) => Scene(Scene::Play(s)), Select::Clip(t, s) => return None, }, - key!(KeyCode::Delete) => match view.selected() { + key!(KeyCode::Delete) => match state.selected() { Select::Mix => Clear, Select::Track(t) => Track(Track::Delete(t)), Select::Scene(s) => Scene(Scene::Delete(s)), @@ -215,12 +215,12 @@ impl InputToCommand for ArrangerCommand { key!(KeyCode::Char('c')) => Clip(Clip::RandomColor), - key!(KeyCode::Char('s')) => match view.selected() { + key!(KeyCode::Char('s')) => match state.selected() { Select::Clip(t, s) => Clip(Clip::Set(t, s, None)), _ => return None, }, - key!(KeyCode::Char('g')) => match view.selected() { + key!(KeyCode::Char('g')) => match state.selected() { Select::Clip(t, s) => Clip(Clip::Get(t, s)), _ => return None, }, @@ -235,13 +235,13 @@ impl InputToCommand for ArrangerCommand { } } } - }) + } }) } } -impl InputToCommand for PhrasesCommand { - fn input_to_command (state: &PhrasesTui, input: &TuiInput) -> Option { +impl InputToCommand for PhrasesCommand { + fn input_to_command (state: &T, input: &TuiInput) -> Option { use PhrasesCommand as Cmd; use PhrasePoolCommand as Edit; use PhraseRenameCommand as Rename; @@ -258,7 +258,7 @@ impl InputToCommand for PhrasesCommand { key!(KeyCode::Char('c')) => Some(Cmd::Edit(Edit::RandomColor(0))), key!(KeyCode::Char('n')) => Some(Cmd::Rename(Rename::Begin)), key!(KeyCode::Char('t')) => Some(Cmd::Length(Length::Begin)), - _ => match state.mode { + _ => match state.phrases_mode() { Some(PhrasesMode::Rename(..)) => { Rename::input_to_command(state, input).map(Cmd::Rename) }, @@ -271,9 +271,9 @@ impl InputToCommand for PhrasesCommand { } } -impl InputToCommand for PhraseLengthCommand { - fn input_to_command (view: &PhrasesTui, from: &TuiInput) -> Option { - if let Some(PhrasesMode::Length(_, length, _)) = view.mode { +impl InputToCommand for PhraseLengthCommand { + fn input_to_command (state: &T, from: &TuiInput) -> Option { + if let Some(PhrasesMode::Length(_, length, _)) = state.phrases_mode() { Some(match from.event() { key!(KeyCode::Up) => Self::Inc, key!(KeyCode::Down) => Self::Dec, @@ -289,9 +289,9 @@ impl InputToCommand for PhraseLengthCommand { } } -impl InputToCommand for PhraseRenameCommand { - fn input_to_command (view: &PhrasesTui, from: &TuiInput) -> Option { - if let Some(PhrasesMode::Rename(_, ref old_name)) = view.mode { +impl InputToCommand for PhraseRenameCommand { + fn input_to_command (state: &T, from: &TuiInput) -> Option { + if let Some(PhrasesMode::Rename(_, ref old_name)) = state.phrases_mode() { Some(match from.event() { key!(KeyCode::Char(c)) => { let mut new_name = old_name.clone(); @@ -313,8 +313,8 @@ impl InputToCommand for PhraseRenameCommand { } } -impl InputToCommand for PhraseCommand { - fn input_to_command (state: &PhraseTui, from: &TuiInput) -> Option { +impl InputToCommand for PhraseCommand { + fn input_to_command (state: &T, from: &TuiInput) -> Option { use PhraseCommand::*; Some(match from.event() { key!(KeyCode::Char('`')) => ToggleDirection, @@ -330,19 +330,19 @@ impl InputToCommand for PhraseCommand { key!(KeyCode::Char('+')) => TimeZoomSet(0), key!(KeyCode::PageUp) => NoteScrollSet(0), key!(KeyCode::PageDown) => NoteScrollSet(0), - key!(KeyCode::Up) => match state.entered { + key!(KeyCode::Up) => match state.phrase_entered() { true => NoteCursorSet(0), false => NoteScrollSet(0), }, - key!(KeyCode::Down) => match state.entered { + key!(KeyCode::Down) => match state.phrase_entered() { true => NoteCursorSet(0), false => NoteScrollSet(0), }, - key!(KeyCode::Left) => match state.entered { + key!(KeyCode::Left) => match state.phrase_entered() { true => TimeCursorSet(0), false => TimeScrollSet(0), }, - key!(KeyCode::Right) => match state.entered { + key!(KeyCode::Right) => match state.phrase_entered() { true => TimeCursorSet(0), false => TimeScrollSet(0), }, diff --git a/crates/tek_tui/src/tui_model.rs b/crates/tek_tui/src/tui_model.rs index ceb11d1a..1beb0ef7 100644 --- a/crates/tek_tui/src/tui_model.rs +++ b/crates/tek_tui/src/tui_model.rs @@ -72,6 +72,10 @@ pub struct SequencerTui { pub(crate) entered: bool, pub(crate) cursor: (usize, usize), + pub(crate) size: Measure, + + /// Mode switch + pub(crate) phrases_mode: Option, } /// Root view for standalone `tek_arranger` @@ -148,7 +152,9 @@ pub struct ArrangerTrack { /// Whether this widget is focused pub(crate) focused: bool, /// Width and height of notes area at last render - pub(crate) size: Measure + pub(crate) size: Measure, + /// Mode switch + pub(crate) phrases_mode: Option, } pub struct PhrasesTui { diff --git a/crates/tek_tui/src/tui_view.rs b/crates/tek_tui/src/tui_view.rs index aaaafab1..208d2a1f 100644 --- a/crates/tek_tui/src/tui_view.rs +++ b/crates/tek_tui/src/tui_view.rs @@ -3,8 +3,8 @@ use crate::*; pub struct TransportView<'a, T: TransportViewState>(pub &'a T); pub trait TransportViewState: Send + Sync { - fn focus (&self) -> TransportFocus; - fn is_focused (&self) -> bool; + fn transport_selected (&self) -> TransportFocus; + fn transport_focused (&self) -> bool; fn transport_state (&self) -> Option; fn bpm_value (&self) -> f64; fn sync_value (&self) -> f64; @@ -13,10 +13,10 @@ pub trait TransportViewState: Send + Sync { } impl TransportViewState for TransportTui { - fn focus (&self) -> TransportFocus { + fn transport_selected (&self) -> TransportFocus { self.focus } - fn is_focused (&self) -> bool { + fn transport_focused (&self) -> bool { true } fn transport_state (&self) -> Option { @@ -37,10 +37,10 @@ impl TransportViewState for TransportTui { } impl TransportViewState for SequencerTui { - fn focus (&self) -> TransportFocus { + fn transport_selected (&self) -> TransportFocus { self.focus } - fn is_focused (&self) -> bool { + fn transport_focused (&self) -> bool { self.focused() == SequencerFocus::Transport } fn transport_state (&self) -> Option { @@ -61,10 +61,10 @@ impl TransportViewState for SequencerTui { } impl TransportViewState for ArrangerTui { - fn focus (&self) -> TransportFocus { + fn transport_selected (&self) -> TransportFocus { self.focus } - fn is_focused (&self) -> bool { + fn transport_focused (&self) -> bool { self.focused() == ArrangerFocus::Transport } fn transport_state (&self) -> Option { @@ -85,11 +85,11 @@ impl TransportViewState for ArrangerTui { } pub trait ArrangerViewState { - fn is_focused (&self) -> bool; + fn arranger_focused (&self) -> bool; } impl ArrangerViewState for ArrangerTui { - fn is_focused (&self) -> bool { + fn arranger_focused (&self) -> bool { self.focused() == ArrangerFocus::Arranger } } @@ -97,7 +97,7 @@ impl ArrangerViewState for ArrangerTui { pub struct PhrasesView<'a, T: PhrasesViewState>(pub &'a T); pub trait PhrasesViewState: Send + Sync { - fn focused (&self) -> bool; + fn phrases_focused (&self) -> bool; fn entered (&self) -> bool; fn phrases (&self) -> Vec>>; fn phrase (&self) -> usize; @@ -105,7 +105,7 @@ pub trait PhrasesViewState: Send + Sync { } impl PhrasesViewState for PhrasesTui { - fn focused (&self) -> bool { + fn phrases_focused (&self) -> bool { todo!() } fn entered (&self) -> bool { @@ -123,7 +123,7 @@ impl PhrasesViewState for PhrasesTui { } impl PhrasesViewState for SequencerTui { - fn focused (&self) -> bool { + fn phrases_focused (&self) -> bool { todo!() } fn entered (&self) -> bool { @@ -136,12 +136,12 @@ impl PhrasesViewState for SequencerTui { todo!() } fn mode (&self) -> &Option { - &self.mode + &self.phrases_mode } } impl PhrasesViewState for ArrangerTui { - fn focused (&self) -> bool { + fn phrases_focused (&self) -> bool { todo!() } fn entered (&self) -> bool { @@ -154,14 +154,14 @@ impl PhrasesViewState for ArrangerTui { todo!() } fn mode (&self) -> &Option { - &self.mode + &self.phrases_mode } } pub struct PhraseView<'a, T: PhraseViewState>(pub &'a T); pub trait PhraseViewState: Send + Sync { - fn focused (&self) -> bool; + fn phrase_focused (&self) -> bool; fn entered (&self) -> bool; fn keys (&self) -> &Buffer; fn phrase (&self) -> &Option>>; @@ -174,7 +174,7 @@ pub trait PhraseViewState: Send + Sync { } impl PhraseViewState for PhraseTui { - fn focused (&self) -> bool { + fn phrase_focused (&self) -> bool { self.focused } fn entered (&self) -> bool { @@ -207,7 +207,7 @@ impl PhraseViewState for PhraseTui { } impl PhraseViewState for SequencerTui { - fn focused (&self) -> bool { + fn phrase_focused (&self) -> bool { todo!() } fn entered (&self) -> bool { @@ -240,7 +240,7 @@ impl PhraseViewState for SequencerTui { } impl PhraseViewState for ArrangerTui { - fn focused (&self) -> bool { + fn phrase_focused (&self) -> bool { todo!() } fn entered (&self) -> bool { @@ -471,7 +471,7 @@ pub fn arranger_content_vertical ( // cursor add(&CustomWidget::new(any_size, move|to: &mut TuiOutput|{ let area = to.area(); - let focused = view.is_focused(); + let focused = view.arranger_focused(); let selected = view.selected; let get_track_area = |t: usize| [ scenes_w + area.x() + cols[t].1 as u16, area.y(), @@ -524,7 +524,7 @@ pub fn arranger_content_vertical ( }) })) }).bg(bg.rgb); - let color = TuiTheme::title_fg(view.is_focused()); + let color = TuiTheme::title_fg(view.arranger_focused()); let size = format!("{}x{}", view.size.w(), view.size.h()); let lower_right = TuiStyle::fg(size, color).pull_x(1).align_se().fill_xy(); lay!(arrangement, lower_right) @@ -533,7 +533,7 @@ pub fn arranger_content_vertical ( pub fn arranger_content_horizontal ( view: &ArrangerTui, ) -> impl Widget + use<'_> { - let focused = view.is_focused(); + let focused = view.arranger_focused(); let _tracks = view.tracks(); lay!( focused.then_some(Background(TuiTheme::border_bg())),