mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
wip: focus refactor, e40
This commit is contained in:
parent
94a16b9dbc
commit
364769a2e0
10 changed files with 198 additions and 279 deletions
|
|
@ -39,7 +39,7 @@ pub trait HasPhrase: ClockApi {
|
|||
|
||||
pub trait MidiInputApi: ClockApi + HasPhrase {
|
||||
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 {
|
||||
self.midi_ins().len() > 0
|
||||
}
|
||||
|
|
@ -69,32 +69,34 @@ pub trait MidiInputApi: ClockApi + HasPhrase {
|
|||
midi_buf: &mut Vec<Vec<Vec<u8>>>,
|
||||
) {
|
||||
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 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();
|
||||
// For highlighting keys and note repeat
|
||||
let mut notes_in = self.notes_in().write().unwrap();
|
||||
// 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 (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<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 (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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ use crate::*;
|
|||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum FocusState<T: Copy + Debug + PartialEq> {
|
||||
Exited(T),
|
||||
Focused(T),
|
||||
Entered(T),
|
||||
}
|
||||
|
|
@ -10,30 +9,22 @@ pub enum FocusState<T: Copy + Debug + PartialEq> {
|
|||
impl<T: Copy + Debug + PartialEq> FocusState<T> {
|
||||
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<T: Copy + Debug + PartialEq> FocusState<T> {
|
|||
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum FocusCommand {
|
||||
Next,
|
||||
Prev,
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
Next,
|
||||
Prev,
|
||||
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> {
|
||||
use FocusCommand::*;
|
||||
match self {
|
||||
|
|
@ -72,37 +63,50 @@ impl<F: HasFocus + FocusGrid> Command<F> for FocusCommand {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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<Self::Item> { 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<T, U> HasFocus for U
|
||||
where
|
||||
T: Copy + PartialEq + Debug,
|
||||
U: FocusGrid<Item = T>
|
||||
{
|
||||
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<T: FocusGrid + HasEnter> 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();
|
||||
|
|
|
|||
|
|
@ -3,15 +3,16 @@ use crate::*;
|
|||
/// Stores and displays time-related info.
|
||||
pub struct TransportTui {
|
||||
pub jack: Arc<RwLock<JackClient>>,
|
||||
pub state: TransportModel,
|
||||
pub clock: ClockModel,
|
||||
pub size: Measure<Tui>,
|
||||
pub cursor: (usize, usize),
|
||||
pub focus: FocusState<AppFocus<TransportFocus>>,
|
||||
}
|
||||
|
||||
/// Root view for standalone `tek_sequencer`.
|
||||
pub struct SequencerTui {
|
||||
pub jack: Arc<RwLock<JackClient>>,
|
||||
pub transport: TransportModel,
|
||||
pub clock: ClockModel,
|
||||
pub phrases: PhrasesModel,
|
||||
pub player: PhrasePlayerModel,
|
||||
pub editor: PhraseEditorModel,
|
||||
|
|
@ -19,16 +20,15 @@ pub struct SequencerTui {
|
|||
pub cursor: (usize, usize),
|
||||
pub split: u16,
|
||||
pub entered: bool,
|
||||
/// MIDI output buffer
|
||||
pub note_buf: Vec<u8>,
|
||||
/// MIDI output buffer
|
||||
pub midi_buf: Vec<Vec<Vec<u8>>>,
|
||||
pub focus: FocusState<AppFocus<SequencerFocus>>,
|
||||
}
|
||||
|
||||
/// Root view for standalone `tek_arranger`
|
||||
pub struct ArrangerTui {
|
||||
pub jack: Arc<RwLock<JackClient>>,
|
||||
pub transport: TransportModel,
|
||||
pub clock: ClockModel,
|
||||
pub phrases: PhrasesModel,
|
||||
pub tracks: Vec<ArrangerTrack>,
|
||||
pub scenes: Vec<ArrangerScene>,
|
||||
|
|
@ -43,10 +43,8 @@ pub struct ArrangerTui {
|
|||
pub menu_bar: Option<MenuBar<Tui, Self, ArrangerCommand>>,
|
||||
pub status_bar: Option<ArrangerStatus>,
|
||||
pub history: Vec<ArrangerCommand>,
|
||||
/// MIDI output buffer
|
||||
pub note_buf: Vec<u8>,
|
||||
/// MIDI output buffer
|
||||
pub midi_buf: Vec<Vec<Vec<u8>>>,
|
||||
/// MIDI editor state
|
||||
pub editor: PhraseEditorModel,
|
||||
pub focus: FocusState<AppFocus<ArrangerFocus>>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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<T: Copy + Debug + PartialEq> {
|
||||
/// 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<Engine = Tui>> (
|
||||
|
|
@ -68,134 +69,66 @@ impl TransportFocus {
|
|||
}
|
||||
}
|
||||
|
||||
impl FocusGrid for TransportTui {
|
||||
type Item = TransportFocus;
|
||||
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) -> &[&[Self::Item]] {
|
||||
use TransportFocus::*;
|
||||
&[
|
||||
fn focus_layout (&self) -> &[&[AppFocus<$Focus>]] {
|
||||
use AppFocus::*;
|
||||
use $Focus::*;
|
||||
&$Grid
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_focus!(TransportTui TransportFocus [
|
||||
&[Menu],
|
||||
&[Bpm, Sync, PlayPause, Clock, Quant],
|
||||
]
|
||||
}
|
||||
fn focus_update (&mut self) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
&[Content(Bpm), Content(Sync), Content(PlayPause), Content(Clock), Content(Quant)],
|
||||
]);
|
||||
|
||||
//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<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::*;
|
||||
&[
|
||||
impl_focus!(SequencerTui SequencerFocus [
|
||||
&[Menu, Menu ],
|
||||
&[Transport, Transport ],
|
||||
&[Phrases, PhraseEditor],
|
||||
]
|
||||
}
|
||||
fn focus_update (&mut self) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
&[Content(Transport), Content(Transport) ],
|
||||
&[Content(Phrases), Content(PhraseEditor)],
|
||||
]);
|
||||
|
||||
//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::*;
|
||||
&[
|
||||
impl_focus!(ArrangerTui 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
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
&[Content(Transport), Content(Transport) ],
|
||||
&[Content(Arranger), Content(Arranger) ],
|
||||
&[Content(Phrases), Content(PhraseEditor)],
|
||||
]);
|
||||
|
||||
/// Focused field of `PhraseLength`
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
|
|
|||
|
|
@ -72,45 +72,45 @@ macro_rules! impl_midi_player {
|
|||
}
|
||||
impl MidiInputApi for $Struct {
|
||||
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>> {
|
||||
todo!("midi_ins_mut")
|
||||
fn midi_ins_mut (&mut self) -> &mut Vec<Port<jack::MidiIn>> {
|
||||
&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<RwLock<[bool; 128]>> {
|
||||
todo!("notes_in")
|
||||
&self$(.$field)*.notes_in
|
||||
}
|
||||
}
|
||||
impl MidiOutputApi for $Struct {
|
||||
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>> {
|
||||
todo!("midi_outs_mut")
|
||||
&mut self$(.$field)*.midi_outs
|
||||
}
|
||||
fn midi_note (&mut self) -> &mut Vec<u8> {
|
||||
todo!("midi_note")
|
||||
&mut self$(.$field)*.note_buf
|
||||
}
|
||||
fn notes_out (&self) -> &Arc<RwLock<[bool; 128]>> {
|
||||
todo!("notes_out")
|
||||
&self$(.$field)*.notes_in
|
||||
}
|
||||
}
|
||||
impl MidiPlayerApi for $Struct {}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@ impl TryFrom<&Arc<RwLock<JackClient>>> for TransportTui {
|
|||
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
|
||||
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<RwLock<JackClient>>> for TransportTui {
|
|||
impl TryFrom<&Arc<RwLock<JackClient>>> for SequencerTui {
|
||||
type Error = Box<dyn std::error::Error>;
|
||||
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 {
|
||||
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<RwLock<JackClient>>> 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<RwLock<JackClient>>> for ArrangerTui {
|
|||
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
|
||||
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<RwLock<JackClient>>> for ArrangerTui {
|
|||
status_bar: None,
|
||||
midi_buf: vec![vec![];65536],
|
||||
note_buf: vec![],
|
||||
focus: FocusState::Entered(AppFocus::Content(ArrangerFocus::Transport(TransportFocus::PlayPause)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
pub struct PhrasePlayerModel {
|
||||
/// State of clock and playhead
|
||||
|
|
@ -72,6 +56,8 @@ pub struct PhrasePlayerModel {
|
|||
pub(crate) notes_in: Arc<RwLock<[bool; 128]>>,
|
||||
/// Notes currently held at output
|
||||
pub(crate) notes_out: Arc<RwLock<[bool; 128]>>,
|
||||
/// MIDI output buffer
|
||||
pub note_buf: Vec<u8>,
|
||||
}
|
||||
|
||||
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<FixedAxis<usize>>,
|
||||
/// Cursor/scroll/zoom in time axis
|
||||
pub(crate) time_axis: RwLock<ScaledAxis<usize>>,
|
||||
/// 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<PhrasesMode>,
|
||||
/// 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(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<PhrasesMode>;
|
||||
}
|
||||
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<Arc<RwLock<Phrase>>> {
|
||||
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<Arc<RwLock<Phrase>>>;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue