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 {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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,
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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 {}
|
||||||
|
|
|
||||||
|
|
@ -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)))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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(()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>>>;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue