wip: fix autoscroll

This commit is contained in:
🪞👃🪞 2024-12-14 22:49:09 +01:00
parent 8f0decbe4d
commit aa8a1a3bd9
8 changed files with 106 additions and 74 deletions

View file

@ -4,6 +4,14 @@ pub trait HasClock: Send + Sync {
fn clock (&self) -> &ClockModel; 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)] #[derive(Clone, Debug, PartialEq)]
pub enum ClockCommand { pub enum ClockCommand {
Play(Option<u32>), Play(Option<u32>),

View file

@ -53,36 +53,71 @@ impl Default for MIDIPointModel {
pub trait MIDIRange { pub trait MIDIRange {
fn time_zoom (&self) -> usize; fn time_zoom (&self) -> usize;
fn set_time_zoom (&self, x: usize); fn set_time_zoom (&self, x: usize);
fn time_lock (&self) -> bool; fn time_lock (&self) -> bool;
fn set_time_lock (&self, x: bool); fn set_time_lock (&self, x: bool);
fn time_start (&self) -> usize; fn time_start (&self) -> usize;
fn set_time_start (&self, x: usize); fn set_time_start (&self, x: usize);
fn note_lo (&self) -> usize; fn note_lo (&self) -> usize;
fn set_note_lo (&self, x: usize); fn set_note_lo (&self, x: usize);
fn note_axis (&self) -> 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 { pub trait MIDIPoint {
fn note_len (&self) -> usize; fn note_len (&self) -> usize;
fn set_note_len (&self, x: usize); fn set_note_len (&self, x: usize);
fn note_point (&self) -> usize; fn note_point (&self) -> usize;
fn set_note_point (&self, x: usize); fn set_note_point (&self, x: usize);
fn time_point (&self) -> usize; fn time_point (&self) -> usize;
fn set_time_point (&self, x: usize); fn set_time_point (&self, x: usize);
fn note_end (&self) -> usize { self.note_point() + self.note_len() }
}
pub trait MIDIViewport<E: Engine>: MIDIRange + MIDIPoint + HasSize<E> {
/// 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 { impl MIDIRange for MIDIRangeModel {
fn time_zoom (&self) -> usize { self.time_zoom.load(Relaxed) } fn time_zoom (&self) -> usize { self.time_zoom.load(Relaxed) }
fn set_time_zoom (&self, x: usize) { self.time_zoom.store(x, 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 time_lock (&self) -> bool { self.time_lock.load(Relaxed) }
fn set_time_lock (&self, x: bool) { self.time_lock.store(x, 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 time_start (&self) -> usize { self.time_start.load(Relaxed) }
fn set_time_start (&self, x: usize) { self.time_start.store(x, 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_lo (&self) -> usize { self.note_lo.load(Relaxed) }
fn note_axis (&self) -> usize { self.note_lo.load(Relaxed) } fn set_note_lo (&self, x: usize) { self.note_lo.store(x, Relaxed); }
fn note_hi (&self) -> usize { self.note_lo() + self.note_axis() }
fn note_axis (&self) -> usize { self.note_axis.load(Relaxed) }
} }
impl MIDIPoint for MIDIPointModel { impl MIDIPoint for MIDIPointModel {
fn note_len (&self) -> usize { self.note_len.load(Relaxed)} fn note_len (&self) -> usize { self.note_len.load(Relaxed)}

View file

@ -70,11 +70,7 @@ impl From<(&ClockModel, &Arc<RwLock<Phrase>>)> for PhrasePlayerModel {
} }
} }
impl HasClock for PhrasePlayerModel { has_clock!(|self:PhrasePlayerModel|&self.clock);
fn clock (&self) -> &ClockModel {
&self.clock
}
}
impl HasMidiIns for PhrasePlayerModel { impl HasMidiIns for PhrasePlayerModel {
fn midi_ins (&self) -> &Vec<Port<jack::MidiIn>> { fn midi_ins (&self) -> &Vec<Port<jack::MidiIn>> {

View file

@ -1,5 +1,23 @@
use crate::*; use crate::*;
pub trait HasSize<E: Engine> {
fn size (&self) -> &Measure<E>;
}
#[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<E: Engine> LayoutDebug<E> for E {} impl<E: Engine> LayoutDebug<E> for E {}
pub trait LayoutDebug<E: Engine> { pub trait LayoutDebug<E: Engine> {

View file

@ -412,16 +412,9 @@ render!(|self: ArrangerTui|{
]) ])
}); });
impl HasClock for ArrangerTui { has_clock!(|self:ArrangerTui|&self.clock);
fn clock (&self) -> &ClockModel { has_clock!(|self:ArrangerTrack|self.player.clock());
&self.clock
}
}
impl HasClock for ArrangerTrack {
fn clock (&self) -> &ClockModel {
&self.player.clock()
}
}
impl HasPhrases for ArrangerTui { impl HasPhrases for ArrangerTui {
fn phrases (&self) -> &Vec<Arc<RwLock<Phrase>>> { fn phrases (&self) -> &Vec<Arc<RwLock<Phrase>>> {
&self.phrases.phrases &self.phrases.phrases

View file

@ -176,26 +176,32 @@ pub fn to_sequencer_command (state: &SequencerTui, input: &TuiInput) -> Option<S
render!(|self: SequencerTui|lay!([self.size, Tui::split_up(false, 1, render!(|self: SequencerTui|lay!([self.size, Tui::split_up(false, 1,
Tui::fill_xy(SequencerStatusBar::from(self)), Tui::fill_xy(SequencerStatusBar::from(self)),
Tui::split_right(false, if self.size.w() > 60 { 20 } else if self.size.w() > 40 { 15 } else { 10 }, Tui::split_right(false, if self.size.w() > 60 {
Tui::fixed_x(20, Tui::split_down(false, 4, col!([ 20
PhraseSelector::play_phrase(&self.player), } else if self.size.w() > 40 {
PhraseSelector::next_phrase(&self.player), 15
]), Tui::split_up(false, 2, } else {
PhraseSelector::edit_phrase(self.editor.phrase()), 10
PhraseListView::from(self), },
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::fill_xy(&self.editor)
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)
]),
) )
)])); )]));
@ -275,11 +281,7 @@ impl TransportControl<SequencerFocus> for SequencerTui {
} }
} }
impl HasClock for SequencerTui { has_clock!(|self:SequencerTui|&self.clock);
fn clock (&self) -> &ClockModel {
&self.clock
}
}
impl HasPhrases for SequencerTui { impl HasPhrases for SequencerTui {
fn phrases (&self) -> &Vec<Arc<RwLock<Phrase>>> { fn phrases (&self) -> &Vec<Arc<RwLock<Phrase>>> {

View file

@ -37,11 +37,7 @@ impl std::fmt::Debug for TransportTui {
} }
} }
impl HasClock for TransportTui { has_clock!(|self:TransportTui|&self.clock);
fn clock (&self) -> &ClockModel {
&self.clock
}
}
impl Audio for TransportTui { impl Audio for TransportTui {
fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control { fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control {

View file

@ -83,14 +83,13 @@ impl Command<PhraseEditorModel> for PhraseCommand {
SetTimeScroll(x) => { state.set_time_start(x); }, SetTimeScroll(x) => { state.set_time_start(x); },
SetNoteScroll(x) => { state.set_note_lo(x); }, SetNoteScroll(x) => { state.set_note_lo(x); },
SetNoteLength(x) => { state.set_note_len(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) => { SetNoteCursor(note) => {
let note = 127.min(note); state.set_note_point(note.min(127));
let start = state.note_lo(); state.autoscroll();
state.set_note_point(note);
if note < start {
state.set_note_lo(note)
}
}, },
_ => todo!("{:?}", self) _ => todo!("{:?}", self)
} }
@ -123,6 +122,10 @@ pub trait PhraseViewMode: Render<Tui> + MIDIRange + MIDIPoint + Debug + Send + S
} }
} }
impl MIDIViewport<Tui> for PhraseEditorModel {}
has_size!(|self:PhraseEditorModel|&self.size);
impl MIDIRange for PhraseEditorModel { impl MIDIRange for PhraseEditorModel {
fn time_zoom (&self) -> usize { self.mode.time_zoom() } fn time_zoom (&self) -> usize { self.mode.time_zoom() }
fn set_time_zoom (&self, x: usize) { self.mode.set_time_zoom(x); } fn set_time_zoom (&self, x: usize) { self.mode.set_time_zoom(x); }
@ -192,25 +195,6 @@ impl PhraseEditorModel {
self.mode.redraw(); 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<RwLock<Phrase>>> for PhraseEditorModel { impl From<&Arc<RwLock<Phrase>>> for PhraseEditorModel {