From aa8a1a3bd9faab08b956d1c6f97888420fd0a267 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sat, 14 Dec 2024 22:49:09 +0100 Subject: [PATCH] wip: fix autoscroll --- crates/tek/src/api/clock.rs | 8 +++++ crates/tek/src/api/note.rs | 43 ++++++++++++++++++++++--- crates/tek/src/api/player.rs | 6 +--- crates/tek/src/layout/measure.rs | 18 +++++++++++ crates/tek/src/tui/app_arranger.rs | 13 ++------ crates/tek/src/tui/app_sequencer.rs | 50 +++++++++++++++-------------- crates/tek/src/tui/app_transport.rs | 6 +--- crates/tek/src/tui/phrase_editor.rs | 36 ++++++--------------- 8 files changed, 106 insertions(+), 74 deletions(-) diff --git a/crates/tek/src/api/clock.rs b/crates/tek/src/api/clock.rs index 6276b6b9..8636852a 100644 --- a/crates/tek/src/api/clock.rs +++ b/crates/tek/src/api/clock.rs @@ -4,6 +4,14 @@ pub trait HasClock: Send + Sync { fn clock (&self) -> &ClockModel; } +#[macro_export] macro_rules! has_clock { + (|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => { + impl $(<$($L),*$($T $(: $U)?),*>)? HasClock for $Struct $(<$($L),*$($T),*>)? { + fn clock (&$self) -> &ClockModel { $cb } + } + } +} + #[derive(Clone, Debug, PartialEq)] pub enum ClockCommand { Play(Option), diff --git a/crates/tek/src/api/note.rs b/crates/tek/src/api/note.rs index 4cf55cbd..28be9ae7 100644 --- a/crates/tek/src/api/note.rs +++ b/crates/tek/src/api/note.rs @@ -53,36 +53,71 @@ impl Default for MIDIPointModel { pub trait MIDIRange { fn time_zoom (&self) -> usize; fn set_time_zoom (&self, x: usize); + fn time_lock (&self) -> bool; fn set_time_lock (&self, x: bool); + fn time_start (&self) -> usize; fn set_time_start (&self, x: usize); + fn note_lo (&self) -> usize; fn set_note_lo (&self, x: usize); + fn note_axis (&self) -> usize; - fn note_hi (&self) -> usize; + + fn note_hi (&self) -> usize { self.note_lo() + self.note_axis() } } pub trait MIDIPoint { fn note_len (&self) -> usize; fn set_note_len (&self, x: usize); + fn note_point (&self) -> usize; fn set_note_point (&self, x: usize); + fn time_point (&self) -> usize; fn set_time_point (&self, x: usize); + + fn note_end (&self) -> usize { self.note_point() + self.note_len() } +} + +pub trait MIDIViewport: MIDIRange + MIDIPoint + HasSize { + /// Make sure cursor is within range + fn autoscroll (&self) { + let note = self.note_point(); + let height = self.size().h(); + if note < self.note_lo() { + self.set_note_lo(note) + } + let note_point = self.note_point(); + let mut note_lo = self.note_lo(); + let mut note_hi = 127.min((note_lo + height).saturating_sub(2)); + if note_point > note_hi { + note_lo += note_point - note_hi; + note_hi = note_point; + self.set_note_lo(note_lo); + } + //(note_point, (note_lo, note_hi)) + } + /// Make sure best usage of screen space is achieved by default + fn autozoom (&self) { + } } impl MIDIRange for MIDIRangeModel { fn time_zoom (&self) -> usize { self.time_zoom.load(Relaxed) } fn set_time_zoom (&self, x: usize) { self.time_zoom.store(x, Relaxed); } + fn time_lock (&self) -> bool { self.time_lock.load(Relaxed) } fn set_time_lock (&self, x: bool) { self.time_lock.store(x, Relaxed); } + fn time_start (&self) -> usize { self.time_start.load(Relaxed) } fn set_time_start (&self, x: usize) { self.time_start.store(x, Relaxed); } - fn set_note_lo (&self, x: usize) { self.note_lo.store(x, Relaxed); } + fn note_lo (&self) -> usize { self.note_lo.load(Relaxed) } - fn note_axis (&self) -> usize { self.note_lo.load(Relaxed) } - fn note_hi (&self) -> usize { self.note_lo() + self.note_axis() } + fn set_note_lo (&self, x: usize) { self.note_lo.store(x, Relaxed); } + + fn note_axis (&self) -> usize { self.note_axis.load(Relaxed) } } impl MIDIPoint for MIDIPointModel { fn note_len (&self) -> usize { self.note_len.load(Relaxed)} diff --git a/crates/tek/src/api/player.rs b/crates/tek/src/api/player.rs index 4f338961..6e72dc98 100644 --- a/crates/tek/src/api/player.rs +++ b/crates/tek/src/api/player.rs @@ -70,11 +70,7 @@ impl From<(&ClockModel, &Arc>)> for PhrasePlayerModel { } } -impl HasClock for PhrasePlayerModel { - fn clock (&self) -> &ClockModel { - &self.clock - } -} +has_clock!(|self:PhrasePlayerModel|&self.clock); impl HasMidiIns for PhrasePlayerModel { fn midi_ins (&self) -> &Vec> { diff --git a/crates/tek/src/layout/measure.rs b/crates/tek/src/layout/measure.rs index 12cd39a3..af163125 100644 --- a/crates/tek/src/layout/measure.rs +++ b/crates/tek/src/layout/measure.rs @@ -1,5 +1,23 @@ use crate::*; +pub trait HasSize { + fn size (&self) -> &Measure; +} + +#[macro_export] macro_rules! implementor { + ($name:ident => $Trait:ident) => { + #[macro_export] macro_rules! $name { + (|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => { + impl $(<$($L),*$($T $(: $U)?),*>)? $Trait for $Struct $(<$($L),*$($T),*>)? { + fn clock (&$self) -> &ClockModel { $cb } + } + } + } + } +} + +implementor!(has_size => HasSize); + impl LayoutDebug for E {} pub trait LayoutDebug { diff --git a/crates/tek/src/tui/app_arranger.rs b/crates/tek/src/tui/app_arranger.rs index 0c1080fb..13697b3f 100644 --- a/crates/tek/src/tui/app_arranger.rs +++ b/crates/tek/src/tui/app_arranger.rs @@ -412,16 +412,9 @@ render!(|self: ArrangerTui|{ ]) }); -impl HasClock for ArrangerTui { - fn clock (&self) -> &ClockModel { - &self.clock - } -} -impl HasClock for ArrangerTrack { - fn clock (&self) -> &ClockModel { - &self.player.clock() - } -} +has_clock!(|self:ArrangerTui|&self.clock); +has_clock!(|self:ArrangerTrack|self.player.clock()); + impl HasPhrases for ArrangerTui { fn phrases (&self) -> &Vec>> { &self.phrases.phrases diff --git a/crates/tek/src/tui/app_sequencer.rs b/crates/tek/src/tui/app_sequencer.rs index 15908d15..b370767e 100644 --- a/crates/tek/src/tui/app_sequencer.rs +++ b/crates/tek/src/tui/app_sequencer.rs @@ -176,26 +176,32 @@ pub fn to_sequencer_command (state: &SequencerTui, input: &TuiInput) -> Option 60 { 20 } else if self.size.w() > 40 { 15 } else { 10 }, - Tui::fixed_x(20, Tui::split_down(false, 4, col!([ - PhraseSelector::play_phrase(&self.player), - PhraseSelector::next_phrase(&self.player), - ]), Tui::split_up(false, 2, - PhraseSelector::edit_phrase(self.editor.phrase()), - PhraseListView::from(self), + Tui::split_right(false, if self.size.w() > 60 { + 20 + } else if self.size.w() > 40 { + 15 + } else { + 10 + }, + Tui::fixed_x(20, Tui::split_down(false, 4, col!([ + PhraseSelector::play_phrase(&self.player), + PhraseSelector::next_phrase(&self.player), + ]), Tui::split_up(false, 2, + PhraseSelector::edit_phrase(self.editor.phrase()), + PhraseListView::from(self), + ))), + col!([ + Tui::fixed_y(2, TransportView::from(( + self, + self.player.play_phrase().as_ref().map(|(_,p)|p.as_ref().map(|p|p.read().unwrap().color)).flatten().clone(), + if let SequencerFocus::Transport(_) = self.focus { + true + } else { + false + } ))), - col!([ - Tui::fixed_y(2, TransportView::from(( - self, - self.player.play_phrase().as_ref().map(|(_,p)|p.as_ref().map(|p|p.read().unwrap().color)).flatten().clone(), - if let SequencerFocus::Transport(_) = self.focus { - true - } else { - false - } - ))), - Tui::fill_xy(&self.editor) - ]), + Tui::fill_xy(&self.editor) + ]), ) )])); @@ -275,11 +281,7 @@ impl TransportControl for SequencerTui { } } -impl HasClock for SequencerTui { - fn clock (&self) -> &ClockModel { - &self.clock - } -} +has_clock!(|self:SequencerTui|&self.clock); impl HasPhrases for SequencerTui { fn phrases (&self) -> &Vec>> { diff --git a/crates/tek/src/tui/app_transport.rs b/crates/tek/src/tui/app_transport.rs index b67d0e31..587b1914 100644 --- a/crates/tek/src/tui/app_transport.rs +++ b/crates/tek/src/tui/app_transport.rs @@ -37,11 +37,7 @@ impl std::fmt::Debug for TransportTui { } } -impl HasClock for TransportTui { - fn clock (&self) -> &ClockModel { - &self.clock - } -} +has_clock!(|self:TransportTui|&self.clock); impl Audio for TransportTui { fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control { diff --git a/crates/tek/src/tui/phrase_editor.rs b/crates/tek/src/tui/phrase_editor.rs index 44c0e703..c2793400 100644 --- a/crates/tek/src/tui/phrase_editor.rs +++ b/crates/tek/src/tui/phrase_editor.rs @@ -83,14 +83,13 @@ impl Command for PhraseCommand { SetTimeScroll(x) => { state.set_time_start(x); }, SetNoteScroll(x) => { state.set_note_lo(x); }, SetNoteLength(x) => { state.set_note_len(x); }, - SetTimeCursor(x) => { state.set_time_point(x); }, + SetTimeCursor(x) => { + state.set_time_point(x); + state.autoscroll(); + }, SetNoteCursor(note) => { - let note = 127.min(note); - let start = state.note_lo(); - state.set_note_point(note); - if note < start { - state.set_note_lo(note) - } + state.set_note_point(note.min(127)); + state.autoscroll(); }, _ => todo!("{:?}", self) } @@ -123,6 +122,10 @@ pub trait PhraseViewMode: Render + MIDIRange + MIDIPoint + Debug + Send + S } } +impl MIDIViewport for PhraseEditorModel {} + +has_size!(|self:PhraseEditorModel|&self.size); + impl MIDIRange for PhraseEditorModel { fn time_zoom (&self) -> usize { self.mode.time_zoom() } fn set_time_zoom (&self, x: usize) { self.mode.set_time_zoom(x); } @@ -192,25 +195,6 @@ impl PhraseEditorModel { self.mode.redraw(); } } - /// Make sure cursor is within range - fn autoscroll ( - range: &impl MIDIRange, - point: &impl MIDIPoint, - height: usize - ) -> (usize, (usize, usize)) { - let note_point = point.note_point(); - let mut note_lo = range.note_lo(); - let mut note_hi = 127.min((note_lo + height).saturating_sub(2)); - if note_point > note_hi { - note_lo += note_point - note_hi; - note_hi = note_point; - range.set_note_lo(note_lo); - } - (note_point, (note_lo, note_hi)) - } - /// Make sure best usage of screen space is achieved by default - fn autozoom (&self) { - } } impl From<&Arc>> for PhraseEditorModel {