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 {
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);
}
}
}

View file

@ -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();

View file

@ -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>>,
}

View file

@ -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(),

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

View file

@ -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)]

View file

@ -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 {}

View file

@ -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)))
})
}
}

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
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(()),
}
}
}

View file

@ -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>>>;