wip: focus refactor, e40

This commit is contained in:
🪞👃🪞 2024-11-22 01:36:34 +01:00
parent 94a16b9dbc
commit 364769a2e0
10 changed files with 198 additions and 279 deletions

View file

@ -39,7 +39,7 @@ pub trait HasPhrase: ClockApi {
pub trait MidiInputApi: ClockApi + HasPhrase { pub trait MidiInputApi: ClockApi + HasPhrase {
fn midi_ins (&self) -> &Vec<Port<MidiIn>>; fn midi_ins (&self) -> &Vec<Port<MidiIn>>;
fn midi_ins_mut (&self) -> &mut Vec<Port<MidiIn>>; fn midi_ins_mut (&mut self) -> &mut Vec<Port<MidiIn>>;
fn has_midi_ins (&self) -> bool { fn has_midi_ins (&self) -> bool {
self.midi_ins().len() > 0 self.midi_ins().len() > 0
} }
@ -69,32 +69,34 @@ pub trait MidiInputApi: ClockApi + HasPhrase {
midi_buf: &mut Vec<Vec<Vec<u8>>>, midi_buf: &mut Vec<Vec<Vec<u8>>>,
) { ) {
let sample0 = scope.last_frame_time() as usize; let sample0 = scope.last_frame_time() as usize;
if let (true, Some((started, phrase))) = (self.is_rolling(), self.phrase()) { // For highlighting keys and note repeat
let start = started.sample.get() as usize; let notes_in = self.notes_in().clone();
let quant = self.quant().get(); if let (true, Some((started, ref phrase))) = (self.is_rolling(), self.phrase().clone()) {
// For highlighting keys and note repeat let start = started.sample.get() as usize;
let mut notes_in = self.notes_in().write().unwrap(); let quant = self.quant().get();
// Record from each input let timebase = self.timebase().clone();
let monitoring = self.monitoring();
let recording = self.recording();
for input in self.midi_ins_mut().iter() { for input in self.midi_ins_mut().iter() {
for (sample, event, bytes) in parse_midi_input(input.iter(scope)) { for (sample, event, bytes) in parse_midi_input(input.iter(scope)) {
if let LiveEvent::Midi { message, .. } = event { if let LiveEvent::Midi { message, .. } = event {
if self.monitoring() { if monitoring {
midi_buf[sample].push(bytes.to_vec()) midi_buf[sample].push(bytes.to_vec())
} }
if self.recording() { if recording {
if let Some(phrase) = phrase { if let Some(phrase) = phrase {
let mut phrase = phrase.write().unwrap(); let mut phrase = phrase.write().unwrap();
let length = phrase.length; let length = phrase.length;
phrase.record_event({ phrase.record_event({
let sample = (sample0 + sample - start) as f64; 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 quantized = (pulse / quant).round() * quant;
let looped = quantized as usize % length; let looped = quantized as usize % length;
looped looped
}, message); }, 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, scope: &ProcessScope,
midi_buf: &mut Vec<Vec<Vec<u8>>>, midi_buf: &mut Vec<Vec<Vec<u8>>>,
) { ) {
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 input in self.midi_ins_mut().iter() {
for (sample, event, bytes) in parse_midi_input(input.iter(scope)) { for (sample, event, bytes) in parse_midi_input(input.iter(scope)) {
if let LiveEvent::Midi { message, .. } = event { if let LiveEvent::Midi { message, .. } = event {
midi_buf[sample].push(bytes.to_vec()); midi_buf[sample].push(bytes.to_vec());
update_keys(&mut notes_in, &message); update_keys(&mut*notes_in.write().unwrap(), &message);
} }
} }
} }

View file

@ -2,7 +2,6 @@ use crate::*;
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum FocusState<T: Copy + Debug + PartialEq> { pub enum FocusState<T: Copy + Debug + PartialEq> {
Exited(T),
Focused(T), Focused(T),
Entered(T), Entered(T),
} }
@ -10,30 +9,22 @@ pub enum FocusState<T: Copy + Debug + PartialEq> {
impl<T: Copy + Debug + PartialEq> FocusState<T> { impl<T: Copy + Debug + PartialEq> FocusState<T> {
pub fn inner (&self) -> T { pub fn inner (&self) -> T {
match self { match self {
Self::Exited(inner) => *inner,
Self::Focused(inner) => *inner, Self::Focused(inner) => *inner,
Self::Entered(inner) => *inner, Self::Entered(inner) => *inner,
} }
} }
pub fn set_inner (&mut self, inner: T) { pub fn set_inner (&mut self, inner: T) {
*self = match self { *self = match self {
Self::Exited(_) => Self::Exited(inner),
Self::Focused(_) => Self::Focused(inner), Self::Focused(_) => Self::Focused(inner),
Self::Entered(_) => Self::Entered(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 { pub fn is_focused (&self) -> bool {
if let Self::Focused(_) = self { true } else { false } if let Self::Focused(_) = self { true } else { false }
} }
pub fn is_entered (&self) -> bool { pub fn is_entered (&self) -> bool {
if let Self::Entered(_) = self { true } else { false } 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) { pub fn to_focused (&mut self) {
*self = Self::Focused(self.inner()) *self = Self::Focused(self.inner())
} }
@ -44,17 +35,17 @@ impl<T: Copy + Debug + PartialEq> FocusState<T> {
#[derive(Copy, Clone, PartialEq, Debug)] #[derive(Copy, Clone, PartialEq, Debug)]
pub enum FocusCommand { pub enum FocusCommand {
Next,
Prev,
Up, Up,
Down, Down,
Left, Left,
Right, Right,
Next,
Prev,
Enter, Enter,
Exit Exit,
} }
impl<F: HasFocus + FocusGrid> Command<F> for FocusCommand { impl<F: HasFocus + HasEnter + FocusGrid + FocusOrder> Command<F> for FocusCommand {
fn execute (self, state: &mut F) -> Perhaps<FocusCommand> { fn execute (self, state: &mut F) -> Perhaps<FocusCommand> {
use FocusCommand::*; use FocusCommand::*;
match self { match self {
@ -66,43 +57,56 @@ impl<F: HasFocus + FocusGrid> Command<F> for FocusCommand {
Right => { state.focus_right(); }, Right => { state.focus_right(); },
Enter => { state.focus_enter(); }, Enter => { state.focus_enter(); },
Exit => { state.focus_exit(); }, Exit => { state.focus_exit(); },
_ => {} _ => {}
} }
Ok(None) Ok(None)
} }
} }
/// Trait for things that have ordered focusable subparts. /// Trait for things that have focusable subparts.
pub trait HasFocus { pub trait HasFocus {
/// Type that identifies of focused item.
type Item: Copy + PartialEq + Debug; type Item: Copy + PartialEq + Debug;
/// Get the currently focused item. /// Get the currently focused item.
fn focused (&self) -> Self::Item; fn focused (&self) -> Self::Item;
/// Focus the next item. /// Get the currently focused item.
fn focus_next (&mut self); fn set_focused (&mut self, to: Self::Item);
/// Focus the previous item.
fn focus_prev (&mut self);
/// Loop forward until a specific item is focused. /// Loop forward until a specific item is focused.
fn focus (&mut self, target: Self::Item) { fn focus_to (&mut self, to: Self::Item) {
while self.focused() != target { self.set_focused(to);
self.focus_next() self.focus_updated();
}
} }
/// Enter the focused item /// Run this on focus update
fn focus_enter (&mut self) {} fn focus_updated (&mut self) {}
/// Exit the focused item
fn focus_exit (&mut self) {}
/// Return the focused item, if any
fn focus_entered (&self) -> Option<Self::Item> { None }
} }
/// Trait for things that implement directional focus. /// Trait for things that have enterable subparts.
pub trait FocusGrid { pub trait HasEnter: HasFocus {
type Item: Copy + PartialEq + Debug; /// 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_layout (&self) -> &[&[Self::Item]];
fn focus_cursor (&self) -> (usize, usize); fn focus_cursor (&self) -> (usize, usize);
fn focus_cursor_mut (&mut self) -> &mut (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) { fn focus_up (&mut self) {
let layout = self.focus_layout(); let layout = self.focus_layout();
let (x, y) = self.focus_cursor(); let (x, y) = self.focus_cursor();
@ -139,16 +143,17 @@ pub trait FocusGrid {
} }
} }
impl<T, U> HasFocus for U /// Trait for things that implement next/prev navigation between focusable elements.
where pub trait FocusOrder {
T: Copy + PartialEq + Debug, /// Focus the next item.
U: FocusGrid<Item = T> fn focus_next (&mut self);
{ /// Focus the previous item.
type Item = T; fn focus_prev (&mut self);
fn focused (&self) -> Self::Item { }
let (x, y) = self.focus_cursor();
self.focus_layout()[y][x] /// Next/prev navigation for directional focusables works in the given way.
} impl<T: FocusGrid + HasEnter> FocusOrder for T {
/// Focus the next item.
fn focus_next (&mut self) { fn focus_next (&mut self) {
let current = self.focused(); let current = self.focused();
let (x, y) = self.focus_cursor(); let (x, y) = self.focus_cursor();
@ -164,6 +169,7 @@ where
self.focus_exit(); self.focus_exit();
self.focus_update(); self.focus_update();
} }
/// Focus the previous item.
fn focus_prev (&mut self) { fn focus_prev (&mut self) {
let current = self.focused(); let current = self.focused();
let (x, _) = self.focus_cursor(); let (x, _) = self.focus_cursor();

View file

@ -3,32 +3,32 @@ use crate::*;
/// Stores and displays time-related info. /// Stores and displays time-related info.
pub struct TransportTui { pub struct TransportTui {
pub jack: Arc<RwLock<JackClient>>, pub jack: Arc<RwLock<JackClient>>,
pub state: TransportModel, pub clock: ClockModel,
pub size: Measure<Tui>, pub size: Measure<Tui>,
pub cursor: (usize, usize), pub cursor: (usize, usize),
pub focus: FocusState<AppFocus<TransportFocus>>,
} }
/// Root view for standalone `tek_sequencer`. /// Root view for standalone `tek_sequencer`.
pub struct SequencerTui { pub struct SequencerTui {
pub jack: Arc<RwLock<JackClient>>, pub jack: Arc<RwLock<JackClient>>,
pub transport: TransportModel, pub clock: ClockModel,
pub phrases: PhrasesModel, pub phrases: PhrasesModel,
pub player: PhrasePlayerModel, pub player: PhrasePlayerModel,
pub editor: PhraseEditorModel, pub editor: PhraseEditorModel,
pub size: Measure<Tui>, pub size: Measure<Tui>,
pub cursor: (usize, usize), pub cursor: (usize, usize),
pub split: u16, pub split: u16,
pub entered: bool, pub entered: bool,
/// MIDI output buffer pub note_buf: Vec<u8>,
pub note_buf: Vec<u8>, pub midi_buf: Vec<Vec<Vec<u8>>>,
/// MIDI output buffer pub focus: FocusState<AppFocus<SequencerFocus>>,
pub midi_buf: Vec<Vec<Vec<u8>>>,
} }
/// Root view for standalone `tek_arranger` /// Root view for standalone `tek_arranger`
pub struct ArrangerTui { pub struct ArrangerTui {
pub jack: Arc<RwLock<JackClient>>, pub jack: Arc<RwLock<JackClient>>,
pub transport: TransportModel, pub clock: ClockModel,
pub phrases: PhrasesModel, pub phrases: PhrasesModel,
pub tracks: Vec<ArrangerTrack>, pub tracks: Vec<ArrangerTrack>,
pub scenes: Vec<ArrangerScene>, pub scenes: Vec<ArrangerScene>,
@ -43,10 +43,8 @@ pub struct ArrangerTui {
pub menu_bar: Option<MenuBar<Tui, Self, ArrangerCommand>>, pub menu_bar: Option<MenuBar<Tui, Self, ArrangerCommand>>,
pub status_bar: Option<ArrangerStatus>, pub status_bar: Option<ArrangerStatus>,
pub history: Vec<ArrangerCommand>, pub history: Vec<ArrangerCommand>,
/// MIDI output buffer pub note_buf: Vec<u8>,
pub note_buf: Vec<u8>, pub midi_buf: Vec<Vec<Vec<u8>>>,
/// MIDI output buffer pub editor: PhraseEditorModel,
pub midi_buf: Vec<Vec<Vec<u8>>>, pub focus: FocusState<AppFocus<ArrangerFocus>>,
/// MIDI editor state
pub editor: PhraseEditorModel,
} }

View file

@ -18,20 +18,20 @@ impl<'a, T: TransportViewState> Content for TransportView<'a, T> {
row!( row!(
state.transport_selected().wrap(focused, TransportFocus::Bpm, &Outset::X(1u16, { 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) } 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! { //let quant = state.focus().wrap(state.focused(), TransportFocus::Quant, &Outset::X(1u16, row! {
//"QUANT ", ppq_to_name(state.0.quant as usize) //"QUANT ", ppq_to_name(state.0.quant as usize)
//})), //})),
state.transport_selected().wrap(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) "SYNC ", pulses_to_name(state.transport_sync_value() as usize)
})) }))
).align_w().fill_x(), ).align_w().fill_x(),
state.transport_selected().wrap(focused, TransportFocus::Clock, &{ state.transport_selected().wrap(focused, TransportFocus::Clock, &{
let time1 = state.format_beat(); let time1 = state.transport_format_beat();
let time2 = state.format_msu(); let time2 = state.transport_format_msu();
row!("B" ,time1.as_str(), " T", time2.as_str()).outset_x(1) row!("B" ,time1.as_str(), " T", time2.as_str()).outset_x(1)
}).align_e().fill_x(), }).align_e().fill_x(),

View file

@ -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 { impl Debug for TransportTui {
fn fmt (&self, f: &mut Formatter<'_>) -> DebugResult { fn fmt (&self, f: &mut Formatter<'_>) -> DebugResult {
f.debug_struct("Measure") f.debug_struct("Measure")

View file

@ -1,9 +1,16 @@
use crate::*; use crate::*;
/// Which item of the transport toolbar is focused
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum TransportFocus { pub enum AppFocus<T: Copy + Debug + PartialEq> {
/// The menu bar is focused
Menu, 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, Bpm,
Sync, Sync,
PlayPause, PlayPause,
@ -13,10 +20,8 @@ pub enum TransportFocus {
/// Sections in the sequencer app that may be focused /// Sections in the sequencer app that may be focused
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum SequencerFocus { pub enum SequencerFocus {
/// The menu bar is focused
Menu,
/// The transport (toolbar) is focused /// The transport (toolbar) is focused
Transport, Transport(TransportFocus),
/// The phrase list (pool) is focused /// The phrase list (pool) is focused
Phrases, Phrases,
/// The phrase editor (sequencer) is focused /// The phrase editor (sequencer) is focused
@ -25,10 +30,8 @@ pub enum SequencerFocus {
/// Sections in the arranger app that may be focused /// Sections in the arranger app that may be focused
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum ArrangerFocus { pub enum ArrangerFocus {
/// The menu bar is focused
Menu,
/// The transport (toolbar) is focused /// The transport (toolbar) is focused
Transport, Transport(TransportFocus),
/// The arrangement (grid) is focused /// The arrangement (grid) is focused
Arranger, Arranger,
/// The phrase list (pool) is focused /// The phrase list (pool) is focused
@ -45,7 +48,6 @@ impl TransportFocus {
Self::Quant => Self::Sync, Self::Quant => Self::Sync,
Self::Sync => Self::Clock, Self::Sync => Self::Clock,
Self::Clock => Self::PlayPause, Self::Clock => Self::PlayPause,
Self::Menu => { todo!() },
} }
} }
pub fn prev (&mut self) { pub fn prev (&mut self) {
@ -55,7 +57,6 @@ impl TransportFocus {
Self::Quant => Self::Bpm, Self::Quant => Self::Bpm,
Self::Sync => Self::Quant, Self::Sync => Self::Quant,
Self::Clock => Self::Sync, Self::Clock => Self::Sync,
Self::Menu => { todo!() },
} }
} }
pub fn wrap <'a, W: Widget<Engine = Tui>> ( pub fn wrap <'a, W: Widget<Engine = Tui>> (
@ -68,135 +69,67 @@ impl TransportFocus {
} }
} }
impl FocusGrid for TransportTui { macro_rules! impl_focus {
type Item = TransportFocus; ($Struct:ident $Focus:ident $Grid:expr) => {
fn focus_cursor (&self) -> (usize, usize) { impl HasFocus for $Struct {
self.cursor type Item = AppFocus<$Focus>;
} /// Get the currently focused item.
fn focus_cursor_mut (&mut self) -> &mut (usize, usize) { fn focused (&self) -> Self::Item {
&mut self.cursor self.focus.inner()
} }
fn focus_layout (&self) -> &[&[Self::Item]] { /// Get the currently focused item.
use TransportFocus::*; fn set_focused (&mut self, to: Self::Item) {
&[ self.focus.set_inner(to)
&[Menu], }
&[Bpm, Sync, PlayPause, Clock, Quant], }
] impl HasEnter for $Struct {
} /// Get the currently focused item.
fn focus_update (&mut self) { fn entered (&self) -> bool {
// TODO self.focus.is_entered()
} }
} /// Get the currently focused item.
fn set_entered (&mut self, entered: bool) {
//impl HasFocus for SequencerTui { if entered {
//type Item = SequencerFocus; self.focus.to_entered()
//} } else {
self.focus.to_focused()
//impl FocusEnter for SequencerTui { }
//type Item = SequencerFocus; }
//fn focus_enter (&mut self) { }
//let focused = self.focused(); impl FocusGrid for $Struct {
//if !self.entered { fn focus_cursor (&self) -> (usize, usize) {
//self.entered = true; self.cursor
//// TODO }
//} fn focus_cursor_mut (&mut self) -> &mut (usize, usize) {
//} &mut self.cursor
//fn focus_exit (&mut self) { }
//if self.entered { fn focus_layout (&self) -> &[&[AppFocus<$Focus>]] {
//self.entered = false; use AppFocus::*;
//// TODO use $Focus::*;
//} &$Grid
//} }
//fn focus_entered (&self) -> Option<Self::Item> {
//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<Self::Item> {
//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
))
} }
} }
} }
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` /// Focused field of `PhraseLength`
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum PhraseLengthFocus { pub enum PhraseLengthFocus {

View file

@ -72,45 +72,45 @@ macro_rules! impl_midi_player {
} }
impl MidiInputApi for $Struct { impl MidiInputApi for $Struct {
fn midi_ins (&self) -> &Vec<Port<jack::MidiIn>> { fn midi_ins (&self) -> &Vec<Port<jack::MidiIn>> {
todo!("midi_ins") &self$(.$field)*.midi_ins
} }
fn midi_ins_mut (&self) -> &mut Vec<Port<jack::MidiIn>> { fn midi_ins_mut (&mut self) -> &mut Vec<Port<jack::MidiIn>> {
todo!("midi_ins_mut") &mut self$(.$field)*.midi_ins
} }
fn recording (&self) -> bool { fn recording (&self) -> bool {
todo!("recording") self$(.$field)*.recording
} }
fn recording_mut (&mut self) -> &mut bool { fn recording_mut (&mut self) -> &mut bool {
todo!("recording_mut") &mut self$(.$field)*.recording
} }
fn monitoring (&self) -> bool { fn monitoring (&self) -> bool {
todo!("monitoring") self$(.$field)*.monitoring
} }
fn monitoring_mut (&mut self) -> &mut bool { fn monitoring_mut (&mut self) -> &mut bool {
todo!("monitoring_mut") &mut self$(.$field)*.monitoring
} }
fn overdub (&self) -> bool { fn overdub (&self) -> bool {
todo!("overdub") self$(.$field)*.overdub
} }
fn overdub_mut (&mut self) -> &mut bool { fn overdub_mut (&mut self) -> &mut bool {
todo!("overdub_mut") &mut self$(.$field)*.overdub
} }
fn notes_in (&self) -> &Arc<RwLock<[bool; 128]>> { fn notes_in (&self) -> &Arc<RwLock<[bool; 128]>> {
todo!("notes_in") &self$(.$field)*.notes_in
} }
} }
impl MidiOutputApi for $Struct { impl MidiOutputApi for $Struct {
fn midi_outs (&self) -> &Vec<Port<jack::MidiOut>> { fn midi_outs (&self) -> &Vec<Port<jack::MidiOut>> {
todo!("midi_outs") &self$(.$field)*.midi_outs
} }
fn midi_outs_mut (&mut self) -> &mut Vec<Port<jack::MidiOut>> { fn midi_outs_mut (&mut self) -> &mut Vec<Port<jack::MidiOut>> {
todo!("midi_outs_mut") &mut self$(.$field)*.midi_outs
} }
fn midi_note (&mut self) -> &mut Vec<u8> { fn midi_note (&mut self) -> &mut Vec<u8> {
todo!("midi_note") &mut self$(.$field)*.note_buf
} }
fn notes_out (&self) -> &Arc<RwLock<[bool; 128]>> { fn notes_out (&self) -> &Arc<RwLock<[bool; 128]>> {
todo!("notes_out") &self$(.$field)*.notes_in
} }
} }
impl MidiPlayerApi for $Struct {} impl MidiPlayerApi for $Struct {}

View file

@ -6,9 +6,10 @@ impl TryFrom<&Arc<RwLock<JackClient>>> for TransportTui {
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> { fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
Ok(Self { Ok(Self {
jack: jack.clone(), jack: jack.clone(),
state: TransportModel::from(&Arc::new(jack.read().unwrap().transport())), clock: ClockModel::from(&Arc::new(jack.read().unwrap().transport())),
size: Measure::new(), size: Measure::new(),
cursor: (0, 0), cursor: (0, 0),
focus: FocusState::Entered(AppFocus::Content(TransportFocus::PlayPause))
}) })
} }
} }
@ -16,11 +17,11 @@ impl TryFrom<&Arc<RwLock<JackClient>>> for TransportTui {
impl TryFrom<&Arc<RwLock<JackClient>>> for SequencerTui { impl TryFrom<&Arc<RwLock<JackClient>>> for SequencerTui {
type Error = Box<dyn std::error::Error>; type Error = Box<dyn std::error::Error>;
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> { fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
let transport = TransportModel::from(&Arc::new(jack.read().unwrap().transport())); let clock = ClockModel::from(&Arc::new(jack.read().unwrap().transport()));
Ok(Self { Ok(Self {
jack: jack.clone(), jack: jack.clone(),
phrases: PhrasesModel::default(), phrases: PhrasesModel::default(),
player: PhrasePlayerModel::from(&transport.clock), player: PhrasePlayerModel::from(&clock),
editor: PhraseEditorModel::default(), editor: PhraseEditorModel::default(),
size: Measure::new(), size: Measure::new(),
cursor: (0, 0), cursor: (0, 0),
@ -28,7 +29,8 @@ impl TryFrom<&Arc<RwLock<JackClient>>> for SequencerTui {
split: 20, split: 20,
midi_buf: vec![vec![];65536], midi_buf: vec![vec![];65536],
note_buf: vec![], note_buf: vec![],
transport, clock,
focus: FocusState::Entered(AppFocus::Content(SequencerFocus::Transport(TransportFocus::PlayPause)))
}) })
} }
} }
@ -38,7 +40,7 @@ impl TryFrom<&Arc<RwLock<JackClient>>> for ArrangerTui {
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> { fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
Ok(Self { Ok(Self {
jack: jack.clone(), jack: jack.clone(),
transport: TransportModel::from(&Arc::new(jack.read().unwrap().transport())), clock: ClockModel::from(&Arc::new(jack.read().unwrap().transport())),
phrases: PhrasesModel::default(), phrases: PhrasesModel::default(),
editor: PhraseEditorModel::default(), editor: PhraseEditorModel::default(),
selected: ArrangerSelection::Clip(0, 0), selected: ArrangerSelection::Clip(0, 0),
@ -56,6 +58,7 @@ impl TryFrom<&Arc<RwLock<JackClient>>> for ArrangerTui {
status_bar: None, status_bar: None,
midi_buf: vec![vec![];65536], midi_buf: vec![vec![];65536],
note_buf: vec![], note_buf: vec![],
focus: FocusState::Entered(AppFocus::Content(ArrangerFocus::Transport(TransportFocus::PlayPause)))
}) })
} }
} }

View file

@ -32,22 +32,6 @@ impl From<&Arc<Transport>> 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<TransportFocus>,
}
impl From<&Arc<Transport>> for TransportModel {
fn from (transport: &Arc<Transport>) -> Self {
Self {
clock: ClockModel::from(transport),
focus: FocusState::Exited(TransportFocus::PlayPause),
}
}
}
/// Contains state for playing a phrase /// Contains state for playing a phrase
pub struct PhrasePlayerModel { pub struct PhrasePlayerModel {
/// State of clock and playhead /// State of clock and playhead
@ -72,6 +56,8 @@ pub struct PhrasePlayerModel {
pub(crate) notes_in: Arc<RwLock<[bool; 128]>>, pub(crate) notes_in: Arc<RwLock<[bool; 128]>>,
/// Notes currently held at output /// Notes currently held at output
pub(crate) notes_out: Arc<RwLock<[bool; 128]>>, pub(crate) notes_out: Arc<RwLock<[bool; 128]>>,
/// MIDI output buffer
pub note_buf: Vec<u8>,
} }
impl From<&ClockModel> for PhrasePlayerModel { impl From<&ClockModel> for PhrasePlayerModel {
@ -80,6 +66,7 @@ impl From<&ClockModel> for PhrasePlayerModel {
clock: clock.clone(), clock: clock.clone(),
midi_ins: vec![], midi_ins: vec![],
midi_outs: vec![], midi_outs: vec![],
note_buf: vec![0;8],
reset: true, reset: true,
recording: false, recording: false,
monitoring: false, monitoring: false,
@ -106,8 +93,6 @@ pub struct PhraseEditorModel {
pub(crate) note_axis: RwLock<FixedAxis<usize>>, pub(crate) note_axis: RwLock<FixedAxis<usize>>,
/// Cursor/scroll/zoom in time axis /// Cursor/scroll/zoom in time axis
pub(crate) time_axis: RwLock<ScaledAxis<usize>>, pub(crate) time_axis: RwLock<ScaledAxis<usize>>,
/// Whether this widget is focused
pub(crate) focus: FocusState<()>,
/// Display mode /// Display mode
pub(crate) mode: bool, pub(crate) mode: bool,
/// Notes currently held at input /// Notes currently held at input
@ -127,7 +112,6 @@ impl Default for PhraseEditorModel {
note_len: 24, note_len: 24,
keys: keys_vert(), keys: keys_vert(),
buffer: Default::default(), buffer: Default::default(),
focus: FocusState::Exited(()),
mode: false, mode: false,
notes_in: RwLock::new([false;128]).into(), notes_in: RwLock::new([false;128]).into(),
notes_out: RwLock::new([false;128]).into(), notes_out: RwLock::new([false;128]).into(),
@ -158,8 +142,6 @@ pub struct PhrasesModel {
pub(crate) scroll: usize, pub(crate) scroll: usize,
/// Mode switch /// Mode switch
pub(crate) mode: Option<PhrasesMode>, pub(crate) mode: Option<PhrasesMode>,
/// Whether this widget is focused
pub(crate) focus: FocusState<()>,
} }
impl Default for PhrasesModel { impl Default for PhrasesModel {
@ -169,7 +151,6 @@ impl Default for PhrasesModel {
phrase: 0.into(), phrase: 0.into(),
scroll: 0, scroll: 0,
mode: None, mode: None,
focus: FocusState::Exited(()),
} }
} }
} }

View file

@ -9,16 +9,16 @@ pub struct PhraseView<'a, T: PhraseViewState>(pub &'a T);
pub trait TransportViewState: ClockApi + Send + Sync { pub trait TransportViewState: ClockApi + Send + Sync {
fn transport_selected (&self) -> TransportFocus; fn transport_selected (&self) -> TransportFocus;
fn transport_focused (&self) -> bool; fn transport_focused (&self) -> bool;
fn bpm_value (&self) -> f64 { fn transport_bpm_value (&self) -> f64 {
self.bpm().get() self.bpm().get()
} }
fn sync_value (&self) -> f64 { fn transport_sync_value (&self) -> f64 {
self.sync().get() self.sync().get()
} }
fn format_beat (&self) -> String { fn transport_format_beat (&self) -> String {
self.current().format_beat() self.current().format_beat()
} }
fn format_msu (&self) -> String { fn transport_format_msu (&self) -> String {
self.current().usec.format_msu() self.current().usec.format_msu()
} }
} }
@ -55,13 +55,13 @@ pub trait PhrasesViewState: Send + Sync {
fn phrase_mode (&self) -> &Option<PhrasesMode>; fn phrase_mode (&self) -> &Option<PhrasesMode>;
} }
macro_rules! impl_phrases_view_state { 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 { impl PhrasesViewState for $Struct {
fn phrases_focused (&self) -> bool { fn phrases_focused (&$self) -> bool {
todo!() $focus
} }
fn phrases_entered (&self) -> bool { fn phrases_entered (&$self) -> bool {
todo!() $enter
} }
fn phrases (&self) -> Vec<Arc<RwLock<Phrase>>> { fn phrases (&self) -> Vec<Arc<RwLock<Phrase>>> {
todo!() todo!()
@ -75,9 +75,13 @@ macro_rules! impl_phrases_view_state {
} }
} }
} }
impl_phrases_view_state!(PhrasesModel); impl_phrases_view_state!(PhrasesModel [self: false] [self: false]);
impl_phrases_view_state!(SequencerTui::phrases); impl_phrases_view_state!(SequencerTui::phrases
impl_phrases_view_state!(ArrangerTui::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 { pub trait PhraseViewState: Send + Sync {
fn phrase_editing (&self) -> &Option<Arc<RwLock<Phrase>>>; fn phrase_editing (&self) -> &Option<Arc<RwLock<Phrase>>>;