From 364769a2e0e79308177da25e396d86b72fbcfe6e Mon Sep 17 00:00:00 2001 From: unspeaker Date: Fri, 22 Nov 2024 01:36:34 +0100 Subject: [PATCH] wip: focus refactor, e40 --- crates/tek_api/src/api_player.rs | 29 +++-- crates/tek_core/src/focus.rs | 94 +++++++------- crates/tek_tui/src/tui_apps.rs | 40 +++--- crates/tek_tui/src/tui_content.rs | 8 +- crates/tek_tui/src/tui_debug.rs | 9 -- crates/tek_tui/src/tui_focus.rs | 203 ++++++++++-------------------- crates/tek_tui/src/tui_impls.rs | 28 ++--- crates/tek_tui/src/tui_init.rs | 13 +- crates/tek_tui/src/tui_model.rs | 25 +--- crates/tek_tui/src/tui_view.rs | 28 +++-- 10 files changed, 198 insertions(+), 279 deletions(-) diff --git a/crates/tek_api/src/api_player.rs b/crates/tek_api/src/api_player.rs index 8906b742..015d38d6 100644 --- a/crates/tek_api/src/api_player.rs +++ b/crates/tek_api/src/api_player.rs @@ -39,7 +39,7 @@ pub trait HasPhrase: ClockApi { pub trait MidiInputApi: ClockApi + HasPhrase { fn midi_ins (&self) -> &Vec>; - fn midi_ins_mut (&self) -> &mut Vec>; + fn midi_ins_mut (&mut self) -> &mut Vec>; fn has_midi_ins (&self) -> bool { self.midi_ins().len() > 0 } @@ -69,32 +69,34 @@ pub trait MidiInputApi: ClockApi + HasPhrase { midi_buf: &mut Vec>>, ) { let sample0 = scope.last_frame_time() as usize; - if let (true, Some((started, phrase))) = (self.is_rolling(), self.phrase()) { - let start = started.sample.get() as usize; - let quant = self.quant().get(); - // For highlighting keys and note repeat - let mut notes_in = self.notes_in().write().unwrap(); - // Record from each input + // For highlighting keys and note repeat + let notes_in = self.notes_in().clone(); + if let (true, Some((started, ref phrase))) = (self.is_rolling(), self.phrase().clone()) { + let start = started.sample.get() as usize; + let quant = self.quant().get(); + let timebase = self.timebase().clone(); + let monitoring = self.monitoring(); + let recording = self.recording(); for input in self.midi_ins_mut().iter() { for (sample, event, bytes) in parse_midi_input(input.iter(scope)) { if let LiveEvent::Midi { message, .. } = event { - if self.monitoring() { + if monitoring { midi_buf[sample].push(bytes.to_vec()) } - if self.recording() { + if recording { if let Some(phrase) = phrase { let mut phrase = phrase.write().unwrap(); let length = phrase.length; phrase.record_event({ let sample = (sample0 + sample - start) as f64; - let pulse = self.timebase().samples_to_pulse(sample); + let pulse = timebase.samples_to_pulse(sample); let quantized = (pulse / quant).round() * quant; let looped = quantized as usize % length; looped }, message); } } - update_keys(&mut notes_in, &message); + update_keys(&mut*notes_in.write().unwrap(), &message); } } } @@ -109,12 +111,13 @@ pub trait MidiInputApi: ClockApi + HasPhrase { scope: &ProcessScope, midi_buf: &mut Vec>>, ) { - let mut notes_in = self.notes_in().write().unwrap(); + // For highlighting keys and note repeat + let notes_in = self.notes_in().clone(); for input in self.midi_ins_mut().iter() { for (sample, event, bytes) in parse_midi_input(input.iter(scope)) { if let LiveEvent::Midi { message, .. } = event { midi_buf[sample].push(bytes.to_vec()); - update_keys(&mut notes_in, &message); + update_keys(&mut*notes_in.write().unwrap(), &message); } } } diff --git a/crates/tek_core/src/focus.rs b/crates/tek_core/src/focus.rs index b32a65ba..18bb706c 100644 --- a/crates/tek_core/src/focus.rs +++ b/crates/tek_core/src/focus.rs @@ -2,7 +2,6 @@ use crate::*; #[derive(Clone, Copy, Debug, PartialEq)] pub enum FocusState { - Exited(T), Focused(T), Entered(T), } @@ -10,30 +9,22 @@ pub enum FocusState { impl FocusState { pub fn inner (&self) -> T { match self { - Self::Exited(inner) => *inner, Self::Focused(inner) => *inner, Self::Entered(inner) => *inner, } } pub fn set_inner (&mut self, inner: T) { *self = match self { - Self::Exited(_) => Self::Exited(inner), Self::Focused(_) => Self::Focused(inner), Self::Entered(_) => Self::Entered(inner), } } - pub fn is_exited (&self) -> bool { - if let Self::Exited(_) = self { true } else { false } - } pub fn is_focused (&self) -> bool { if let Self::Focused(_) = self { true } else { false } } pub fn is_entered (&self) -> bool { if let Self::Entered(_) = self { true } else { false } } - pub fn to_exited (&mut self) { - *self = Self::Exited(self.inner()) - } pub fn to_focused (&mut self) { *self = Self::Focused(self.inner()) } @@ -44,17 +35,17 @@ impl FocusState { #[derive(Copy, Clone, PartialEq, Debug)] pub enum FocusCommand { - Next, - Prev, Up, Down, Left, Right, + Next, + Prev, Enter, - Exit + Exit, } -impl Command for FocusCommand { +impl Command for FocusCommand { fn execute (self, state: &mut F) -> Perhaps { use FocusCommand::*; match self { @@ -66,43 +57,56 @@ impl Command for FocusCommand { Right => { state.focus_right(); }, Enter => { state.focus_enter(); }, Exit => { state.focus_exit(); }, - _ => {} + _ => {} } Ok(None) } } -/// Trait for things that have ordered focusable subparts. +/// Trait for things that have focusable subparts. pub trait HasFocus { - /// Type that identifies of focused item. type Item: Copy + PartialEq + Debug; /// Get the currently focused item. fn focused (&self) -> Self::Item; - /// Focus the next item. - fn focus_next (&mut self); - /// Focus the previous item. - fn focus_prev (&mut self); + /// Get the currently focused item. + fn set_focused (&mut self, to: Self::Item); /// Loop forward until a specific item is focused. - fn focus (&mut self, target: Self::Item) { - while self.focused() != target { - self.focus_next() - } + fn focus_to (&mut self, to: Self::Item) { + self.set_focused(to); + self.focus_updated(); } - /// Enter the focused item - fn focus_enter (&mut self) {} - /// Exit the focused item - fn focus_exit (&mut self) {} - /// Return the focused item, if any - fn focus_entered (&self) -> Option { None } + /// Run this on focus update + fn focus_updated (&mut self) {} } -/// Trait for things that implement directional focus. -pub trait FocusGrid { - type Item: Copy + PartialEq + Debug; +/// Trait for things that have enterable subparts. +pub trait HasEnter: HasFocus { + /// Get the currently focused item. + fn entered (&self) -> bool; + /// Get the currently focused item. + fn set_entered (&mut self, entered: bool); + /// Enter into the currently focused component + fn focus_enter (&mut self) { + self.set_entered(true); + self.focus_updated(); + } + /// Exit the currently entered component + fn focus_exit (&mut self) { + self.set_entered(true); + self.focus_updated(); + } +} + +/// Trait for things that implement directional navigation between focusable elements. +pub trait FocusGrid: HasFocus { fn focus_layout (&self) -> &[&[Self::Item]]; fn focus_cursor (&self) -> (usize, usize); fn focus_cursor_mut (&mut self) -> &mut (usize, usize); - fn focus_update (&mut self) {} + fn focus_update (&mut self) { + let (x, y) = self.focus_cursor(); + let item = self.focus_layout()[y][x]; + self.focus_to(item) + } fn focus_up (&mut self) { let layout = self.focus_layout(); let (x, y) = self.focus_cursor(); @@ -139,16 +143,17 @@ pub trait FocusGrid { } } -impl HasFocus for U -where - T: Copy + PartialEq + Debug, - U: FocusGrid -{ - type Item = T; - fn focused (&self) -> Self::Item { - let (x, y) = self.focus_cursor(); - self.focus_layout()[y][x] - } +/// Trait for things that implement next/prev navigation between focusable elements. +pub trait FocusOrder { + /// Focus the next item. + fn focus_next (&mut self); + /// Focus the previous item. + fn focus_prev (&mut self); +} + +/// Next/prev navigation for directional focusables works in the given way. +impl FocusOrder for T { + /// Focus the next item. fn focus_next (&mut self) { let current = self.focused(); let (x, y) = self.focus_cursor(); @@ -164,6 +169,7 @@ where self.focus_exit(); self.focus_update(); } + /// Focus the previous item. fn focus_prev (&mut self) { let current = self.focused(); let (x, _) = self.focus_cursor(); diff --git a/crates/tek_tui/src/tui_apps.rs b/crates/tek_tui/src/tui_apps.rs index a3bafc88..988b3ffd 100644 --- a/crates/tek_tui/src/tui_apps.rs +++ b/crates/tek_tui/src/tui_apps.rs @@ -3,32 +3,32 @@ use crate::*; /// Stores and displays time-related info. pub struct TransportTui { pub jack: Arc>, - pub state: TransportModel, + pub clock: ClockModel, pub size: Measure, pub cursor: (usize, usize), + pub focus: FocusState>, } /// Root view for standalone `tek_sequencer`. pub struct SequencerTui { - pub jack: Arc>, - pub transport: TransportModel, - pub phrases: PhrasesModel, - pub player: PhrasePlayerModel, - pub editor: PhraseEditorModel, - pub size: Measure, - pub cursor: (usize, usize), - pub split: u16, - pub entered: bool, - /// MIDI output buffer - pub note_buf: Vec, - /// MIDI output buffer - pub midi_buf: Vec>>, + pub jack: Arc>, + pub clock: ClockModel, + pub phrases: PhrasesModel, + pub player: PhrasePlayerModel, + pub editor: PhraseEditorModel, + pub size: Measure, + pub cursor: (usize, usize), + pub split: u16, + pub entered: bool, + pub note_buf: Vec, + pub midi_buf: Vec>>, + pub focus: FocusState>, } /// Root view for standalone `tek_arranger` pub struct ArrangerTui { pub jack: Arc>, - pub transport: TransportModel, + pub clock: ClockModel, pub phrases: PhrasesModel, pub tracks: Vec, pub scenes: Vec, @@ -43,10 +43,8 @@ pub struct ArrangerTui { pub menu_bar: Option>, pub status_bar: Option, pub history: Vec, - /// MIDI output buffer - pub note_buf: Vec, - /// MIDI output buffer - pub midi_buf: Vec>>, - /// MIDI editor state - pub editor: PhraseEditorModel, + pub note_buf: Vec, + pub midi_buf: Vec>>, + pub editor: PhraseEditorModel, + pub focus: FocusState>, } diff --git a/crates/tek_tui/src/tui_content.rs b/crates/tek_tui/src/tui_content.rs index ca55d413..2cccd6a2 100644 --- a/crates/tek_tui/src/tui_content.rs +++ b/crates/tek_tui/src/tui_content.rs @@ -18,20 +18,20 @@ impl<'a, T: TransportViewState> Content for TransportView<'a, T> { row!( state.transport_selected().wrap(focused, TransportFocus::Bpm, &Outset::X(1u16, { - let bpm = state.bpm_value(); + let bpm = state.transport_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.transport_selected().wrap(focused, TransportFocus::Sync, &Outset::X(1u16, row! { - "SYNC ", pulses_to_name(state.sync_value() as usize) + "SYNC ", pulses_to_name(state.transport_sync_value() as usize) })) ).align_w().fill_x(), state.transport_selected().wrap(focused, TransportFocus::Clock, &{ - let time1 = state.format_beat(); - let time2 = state.format_msu(); + let time1 = state.transport_format_beat(); + let time2 = state.transport_format_msu(); row!("B" ,time1.as_str(), " T", time2.as_str()).outset_x(1) }).align_e().fill_x(), diff --git a/crates/tek_tui/src/tui_debug.rs b/crates/tek_tui/src/tui_debug.rs index 75ebf5a1..712195e2 100644 --- a/crates/tek_tui/src/tui_debug.rs +++ b/crates/tek_tui/src/tui_debug.rs @@ -17,15 +17,6 @@ impl Debug for ClockModel { } } -impl Debug for TransportModel { - fn fmt (&self, f: &mut Formatter<'_>) -> DebugResult { - f.debug_struct("TransportModel") - .field("clock", &self.clock) - .field("focus", &self.focus) - .finish() - } -} - impl Debug for TransportTui { fn fmt (&self, f: &mut Formatter<'_>) -> DebugResult { f.debug_struct("Measure") diff --git a/crates/tek_tui/src/tui_focus.rs b/crates/tek_tui/src/tui_focus.rs index 56e24cf6..99ead661 100644 --- a/crates/tek_tui/src/tui_focus.rs +++ b/crates/tek_tui/src/tui_focus.rs @@ -1,9 +1,16 @@ use crate::*; -/// Which item of the transport toolbar is focused #[derive(Clone, Copy, Debug, PartialEq)] -pub enum TransportFocus { +pub enum AppFocus { + /// The menu bar is focused Menu, + /// The app content is focused + Content(T) +} + +/// Which item of the transport toolbar is focused +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum TransportFocus { Bpm, Sync, PlayPause, @@ -13,10 +20,8 @@ pub enum TransportFocus { /// 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, + Transport(TransportFocus), /// The phrase list (pool) is focused Phrases, /// The phrase editor (sequencer) is focused @@ -25,10 +30,8 @@ pub enum SequencerFocus { /// 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, + Transport(TransportFocus), /// The arrangement (grid) is focused Arranger, /// The phrase list (pool) is focused @@ -45,7 +48,6 @@ impl TransportFocus { Self::Quant => Self::Sync, Self::Sync => Self::Clock, Self::Clock => Self::PlayPause, - Self::Menu => { todo!() }, } } pub fn prev (&mut self) { @@ -55,7 +57,6 @@ impl TransportFocus { Self::Quant => Self::Bpm, Self::Sync => Self::Quant, Self::Clock => Self::Sync, - Self::Menu => { todo!() }, } } pub fn wrap <'a, W: Widget> ( @@ -68,135 +69,67 @@ impl TransportFocus { } } -impl FocusGrid for TransportTui { - type Item = TransportFocus; - fn focus_cursor (&self) -> (usize, usize) { - self.cursor - } - fn focus_cursor_mut (&mut self) -> &mut (usize, usize) { - &mut self.cursor - } - fn focus_layout (&self) -> &[&[Self::Item]] { - use TransportFocus::*; - &[ - &[Menu], - &[Bpm, Sync, PlayPause, Clock, Quant], - ] - } - fn focus_update (&mut self) { - // TODO - } -} - -//impl HasFocus for SequencerTui { - //type Item = SequencerFocus; -//} - -//impl FocusEnter for SequencerTui { - //type Item = SequencerFocus; - //fn focus_enter (&mut self) { - //let focused = self.focused(); - //if !self.entered { - //self.entered = true; - //// TODO - //} - //} - //fn focus_exit (&mut self) { - //if self.entered { - //self.entered = false; - //// TODO - //} - //} - //fn focus_entered (&self) -> Option { - //if self.entered { - //Some(self.focused()) - //} else { - //None - //} - //} -//} - -impl FocusGrid for SequencerTui { - type Item = SequencerFocus; - fn focus_cursor (&self) -> (usize, usize) { - self.cursor - } - fn focus_cursor_mut (&mut self) -> &mut (usize, usize) { - &mut self.cursor - } - fn focus_layout (&self) -> &[&[Self::Item]] { - use SequencerFocus::*; - &[ - &[Menu, Menu ], - &[Transport, Transport ], - &[Phrases, PhraseEditor], - ] - } - fn focus_update (&mut self) { - // TODO - } -} - -//impl FocusEnter for ArrangerTui { - //type Item = ArrangerFocus; - //fn focus_enter (&mut self) { - //self.entered = true; - ////use ArrangerFocus::*; - ////let focused = self.focused(); - ////if !self.entered { - ////self.entered = focused == Arranger; - ////self.editor.entered = focused == PhraseEditor; - ////self.phrases.entered = focused == Phrases; - ////} - //} - //fn focus_exit (&mut self) { - //self.entered = false; - ////if self.entered { - ////self.entered = false; - ////self.editor.entered = false; - ////self.phrases.entered = false; - ////} - //} - //fn focus_entered (&self) -> Option { - //if self.entered { - //Some(self.focused()) - //} else { - //None - //} - //} -//} - -/// Focus layout of arranger app -impl FocusGrid for ArrangerTui { - type Item = ArrangerFocus; - fn focus_cursor (&self) -> (usize, usize) { - self.cursor - } - fn focus_cursor_mut (&mut self) -> &mut (usize, usize) { - &mut self.cursor - } - fn focus_layout (&self) -> &[&[Self::Item]] { - use ArrangerFocus::*; - &[ - &[Menu, Menu ], - &[Transport, Transport ], - &[Arranger, Arranger ], - &[Phrases, PhraseEditor], - ] - } - fn focus_update (&mut self) { - use ArrangerFocus::*; - let focused = self.focused(); - if let Some(mut status_bar) = self.status_bar { - status_bar.update(&( - self.focused(), - self.selected, - focused == PhraseEditor && self.entered - )) +macro_rules! impl_focus { + ($Struct:ident $Focus:ident $Grid:expr) => { + impl HasFocus for $Struct { + type Item = AppFocus<$Focus>; + /// Get the currently focused item. + fn focused (&self) -> Self::Item { + self.focus.inner() + } + /// Get the currently focused item. + fn set_focused (&mut self, to: Self::Item) { + self.focus.set_inner(to) + } + } + impl HasEnter for $Struct { + /// Get the currently focused item. + fn entered (&self) -> bool { + self.focus.is_entered() + } + /// Get the currently focused item. + fn set_entered (&mut self, entered: bool) { + if entered { + self.focus.to_entered() + } else { + self.focus.to_focused() + } + } + } + impl FocusGrid for $Struct { + fn focus_cursor (&self) -> (usize, usize) { + self.cursor + } + fn focus_cursor_mut (&mut self) -> &mut (usize, usize) { + &mut self.cursor + } + fn focus_layout (&self) -> &[&[AppFocus<$Focus>]] { + use AppFocus::*; + use $Focus::*; + &$Grid + } } } } +impl_focus!(TransportTui TransportFocus [ + &[Menu], + &[Content(Bpm), Content(Sync), Content(PlayPause), Content(Clock), Content(Quant)], +]); + +impl_focus!(SequencerTui SequencerFocus [ + &[Menu, Menu ], + &[Content(Transport), Content(Transport) ], + &[Content(Phrases), Content(PhraseEditor)], +]); + +impl_focus!(ArrangerTui ArrangerFocus [ + &[Menu, Menu ], + &[Content(Transport), Content(Transport) ], + &[Content(Arranger), Content(Arranger) ], + &[Content(Phrases), Content(PhraseEditor)], +]); + /// Focused field of `PhraseLength` #[derive(Copy, Clone, Debug)] pub enum PhraseLengthFocus { diff --git a/crates/tek_tui/src/tui_impls.rs b/crates/tek_tui/src/tui_impls.rs index 5889444f..022a607e 100644 --- a/crates/tek_tui/src/tui_impls.rs +++ b/crates/tek_tui/src/tui_impls.rs @@ -72,45 +72,45 @@ macro_rules! impl_midi_player { } impl MidiInputApi for $Struct { fn midi_ins (&self) -> &Vec> { - todo!("midi_ins") + &self$(.$field)*.midi_ins } - fn midi_ins_mut (&self) -> &mut Vec> { - todo!("midi_ins_mut") + fn midi_ins_mut (&mut self) -> &mut Vec> { + &mut self$(.$field)*.midi_ins } fn recording (&self) -> bool { - todo!("recording") + self$(.$field)*.recording } fn recording_mut (&mut self) -> &mut bool { - todo!("recording_mut") + &mut self$(.$field)*.recording } fn monitoring (&self) -> bool { - todo!("monitoring") + self$(.$field)*.monitoring } fn monitoring_mut (&mut self) -> &mut bool { - todo!("monitoring_mut") + &mut self$(.$field)*.monitoring } fn overdub (&self) -> bool { - todo!("overdub") + self$(.$field)*.overdub } fn overdub_mut (&mut self) -> &mut bool { - todo!("overdub_mut") + &mut self$(.$field)*.overdub } fn notes_in (&self) -> &Arc> { - todo!("notes_in") + &self$(.$field)*.notes_in } } impl MidiOutputApi for $Struct { fn midi_outs (&self) -> &Vec> { - todo!("midi_outs") + &self$(.$field)*.midi_outs } fn midi_outs_mut (&mut self) -> &mut Vec> { - todo!("midi_outs_mut") + &mut self$(.$field)*.midi_outs } fn midi_note (&mut self) -> &mut Vec { - todo!("midi_note") + &mut self$(.$field)*.note_buf } fn notes_out (&self) -> &Arc> { - todo!("notes_out") + &self$(.$field)*.notes_in } } impl MidiPlayerApi for $Struct {} diff --git a/crates/tek_tui/src/tui_init.rs b/crates/tek_tui/src/tui_init.rs index 01f80615..fed142cf 100644 --- a/crates/tek_tui/src/tui_init.rs +++ b/crates/tek_tui/src/tui_init.rs @@ -6,9 +6,10 @@ impl TryFrom<&Arc>> for TransportTui { fn try_from (jack: &Arc>) -> Usually { Ok(Self { jack: jack.clone(), - state: TransportModel::from(&Arc::new(jack.read().unwrap().transport())), + clock: ClockModel::from(&Arc::new(jack.read().unwrap().transport())), size: Measure::new(), cursor: (0, 0), + focus: FocusState::Entered(AppFocus::Content(TransportFocus::PlayPause)) }) } } @@ -16,11 +17,11 @@ impl TryFrom<&Arc>> for TransportTui { impl TryFrom<&Arc>> for SequencerTui { type Error = Box; fn try_from (jack: &Arc>) -> Usually { - let transport = TransportModel::from(&Arc::new(jack.read().unwrap().transport())); + let clock = ClockModel::from(&Arc::new(jack.read().unwrap().transport())); Ok(Self { jack: jack.clone(), phrases: PhrasesModel::default(), - player: PhrasePlayerModel::from(&transport.clock), + player: PhrasePlayerModel::from(&clock), editor: PhraseEditorModel::default(), size: Measure::new(), cursor: (0, 0), @@ -28,7 +29,8 @@ impl TryFrom<&Arc>> for SequencerTui { split: 20, midi_buf: vec![vec![];65536], note_buf: vec![], - transport, + clock, + focus: FocusState::Entered(AppFocus::Content(SequencerFocus::Transport(TransportFocus::PlayPause))) }) } } @@ -38,7 +40,7 @@ impl TryFrom<&Arc>> for ArrangerTui { fn try_from (jack: &Arc>) -> Usually { Ok(Self { jack: jack.clone(), - transport: TransportModel::from(&Arc::new(jack.read().unwrap().transport())), + clock: ClockModel::from(&Arc::new(jack.read().unwrap().transport())), phrases: PhrasesModel::default(), editor: PhraseEditorModel::default(), selected: ArrangerSelection::Clip(0, 0), @@ -56,6 +58,7 @@ impl TryFrom<&Arc>> for ArrangerTui { status_bar: None, midi_buf: vec![vec![];65536], note_buf: vec![], + focus: FocusState::Entered(AppFocus::Content(ArrangerFocus::Transport(TransportFocus::PlayPause))) }) } } diff --git a/crates/tek_tui/src/tui_model.rs b/crates/tek_tui/src/tui_model.rs index 702bcf89..5a7afb01 100644 --- a/crates/tek_tui/src/tui_model.rs +++ b/crates/tek_tui/src/tui_model.rs @@ -32,22 +32,6 @@ impl From<&Arc> for ClockModel { } } -pub struct TransportModel { - /// State of clock and playhead - pub(crate) clock: ClockModel, - /// Whether the transport is focused and which part of it is selected - pub(crate) focus: FocusState, -} - -impl From<&Arc> for TransportModel { - fn from (transport: &Arc) -> Self { - Self { - clock: ClockModel::from(transport), - focus: FocusState::Exited(TransportFocus::PlayPause), - } - } -} - /// Contains state for playing a phrase pub struct PhrasePlayerModel { /// State of clock and playhead @@ -72,6 +56,8 @@ pub struct PhrasePlayerModel { pub(crate) notes_in: Arc>, /// Notes currently held at output pub(crate) notes_out: Arc>, + /// MIDI output buffer + pub note_buf: Vec, } impl From<&ClockModel> for PhrasePlayerModel { @@ -80,6 +66,7 @@ impl From<&ClockModel> for PhrasePlayerModel { clock: clock.clone(), midi_ins: vec![], midi_outs: vec![], + note_buf: vec![0;8], reset: true, recording: false, monitoring: false, @@ -106,8 +93,6 @@ pub struct PhraseEditorModel { pub(crate) note_axis: RwLock>, /// Cursor/scroll/zoom in time axis pub(crate) time_axis: RwLock>, - /// Whether this widget is focused - pub(crate) focus: FocusState<()>, /// Display mode pub(crate) mode: bool, /// Notes currently held at input @@ -127,7 +112,6 @@ impl Default for PhraseEditorModel { note_len: 24, keys: keys_vert(), buffer: Default::default(), - focus: FocusState::Exited(()), mode: false, notes_in: RwLock::new([false;128]).into(), notes_out: RwLock::new([false;128]).into(), @@ -158,8 +142,6 @@ pub struct PhrasesModel { pub(crate) scroll: usize, /// Mode switch pub(crate) mode: Option, - /// Whether this widget is focused - pub(crate) focus: FocusState<()>, } impl Default for PhrasesModel { @@ -169,7 +151,6 @@ impl Default for PhrasesModel { phrase: 0.into(), scroll: 0, mode: None, - focus: FocusState::Exited(()), } } } diff --git a/crates/tek_tui/src/tui_view.rs b/crates/tek_tui/src/tui_view.rs index 9bf7fbcc..9a8c92fb 100644 --- a/crates/tek_tui/src/tui_view.rs +++ b/crates/tek_tui/src/tui_view.rs @@ -9,16 +9,16 @@ pub struct PhraseView<'a, T: PhraseViewState>(pub &'a T); pub trait TransportViewState: ClockApi + Send + Sync { fn transport_selected (&self) -> TransportFocus; fn transport_focused (&self) -> bool; - fn bpm_value (&self) -> f64 { + fn transport_bpm_value (&self) -> f64 { self.bpm().get() } - fn sync_value (&self) -> f64 { + fn transport_sync_value (&self) -> f64 { self.sync().get() } - fn format_beat (&self) -> String { + fn transport_format_beat (&self) -> String { self.current().format_beat() } - fn format_msu (&self) -> String { + fn transport_format_msu (&self) -> String { self.current().usec.format_msu() } } @@ -55,13 +55,13 @@ pub trait PhrasesViewState: Send + Sync { fn phrase_mode (&self) -> &Option; } macro_rules! impl_phrases_view_state { - ($Struct:ident $(:: $field:ident)*) => { + ($Struct:ident $(:: $field:ident)* [$self:ident: $focus:expr] [self: $enter:expr]) => { impl PhrasesViewState for $Struct { - fn phrases_focused (&self) -> bool { - todo!() + fn phrases_focused (&$self) -> bool { + $focus } - fn phrases_entered (&self) -> bool { - todo!() + fn phrases_entered (&$self) -> bool { + $enter } fn phrases (&self) -> Vec>> { todo!() @@ -75,9 +75,13 @@ macro_rules! impl_phrases_view_state { } } } -impl_phrases_view_state!(PhrasesModel); -impl_phrases_view_state!(SequencerTui::phrases); -impl_phrases_view_state!(ArrangerTui::phrases); +impl_phrases_view_state!(PhrasesModel [self: false] [self: false]); +impl_phrases_view_state!(SequencerTui::phrases + [self: self.focused() == FocusState::Focused(SequencerFocus::Phrases)] + [self: self.focused() == FocusState::Entered(SequencerFocus::Phrases)]); +impl_phrases_view_state!(ArrangerTui::phrases + [self: self.focused() == FocusState::Focused(ArrangerFocus::Phrases)] + [self: self.focused() == FocusState::Entered(SequencerFocus::Phrases)]); pub trait PhraseViewState: Send + Sync { fn phrase_editing (&self) -> &Option>>;