diff --git a/crates/tek_tui/src/lib.rs b/crates/tek_tui/src/lib.rs index 8c317545..7412334b 100644 --- a/crates/tek_tui/src/lib.rs +++ b/crates/tek_tui/src/lib.rs @@ -10,10 +10,6 @@ pub(crate) use std::ffi::OsString; pub(crate) use std::fs::read_dir; submod! { - tui_focus - tui_menu - tui_status - tui_app_arranger tui_app_sequencer tui_app_transport @@ -240,3 +236,117 @@ impl Debug for PhrasePlayerModel { .finish() } } + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum AppFocus { + /// The menu bar is focused + Menu, + /// The app content is focused + Content(T) +} + +pub trait FocusWrap { + fn wrap <'a, W: Widget> (self, focus: T, content: &'a W) + -> impl Widget + 'a; +} + +#[macro_export] 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) -> &[&[AppFocus<$Focus>]] { + use AppFocus::*; + use $Focus::*; + &$Grid + } + } + } +} + +pub trait StatusBar: Copy + Widget { + type State; + fn hotkey_fg () -> Color where Self: Sized; + fn update (&mut self, state: &Self::State) where Self: Sized; + fn command (commands: &[[impl Widget;3]]) + -> impl Widget + '_ + where + Self: Sized + { + let hotkey_fg = Self::hotkey_fg(); + Stack::right(move |add|{ + Ok(for [a, b, c] in commands.iter() { + add(&row!( + " ", + widget(a), + widget(b).bold(true).fg(hotkey_fg), + widget(c), + ))?; + }) + }) + } +} + +fn content_with_menu_and_status <'a, A, S, C> ( + content: &'a A, + menu_bar: &'a Option>, + status_bar: &'a Option +) -> impl Widget + 'a +where + A: Widget, + S: Send + Sync + 'a, + C: Command +{ + let menus = menu_bar.as_ref().map_or_else( + ||&[] as &[Menu<_, _, _>], + |m|m.menus.as_slice() + ); + Either( + menu_bar.is_none(), + Either( + status_bar.is_none(), + widget(content), + Split::up( + 1, + widget(status_bar.as_ref().unwrap()), + widget(content) + ), + ), + Split::down( + 1, + row!(menu in menus.iter() => { + row!(" ", menu.title.as_str(), " ") + }), + widget(content) + ) + ) +} diff --git a/crates/tek_tui/src/tui_app_arranger.rs b/crates/tek_tui/src/tui_app_arranger.rs index 56081273..f3db017a 100644 --- a/crates/tek_tui/src/tui_app_arranger.rs +++ b/crates/tek_tui/src/tui_app_arranger.rs @@ -66,3 +66,152 @@ pub enum ArrangerFocus { /// The phrase editor (sequencer) is focused PhraseEditor, } + +impl_focus!(ArrangerTui ArrangerFocus [ + //&[ + //Menu, + //Menu, + //Menu, + //Menu, + //Menu, + //], + &[ + Content(Transport(TransportFocus::PlayPause)), + Content(Transport(TransportFocus::Bpm)), + Content(Transport(TransportFocus::Sync)), + Content(Transport(TransportFocus::Clock)), + Content(Transport(TransportFocus::Quant)) + ], &[ + Content(Arranger), + Content(Arranger), + Content(Arranger), + Content(Arranger), + Content(Arranger), + ], &[ + Content(Phrases), + Content(Phrases), + Content(PhraseEditor), + Content(PhraseEditor), + Content(PhraseEditor), + ], +]); + +/// Status bar for arranger app +#[derive(Copy, Clone, Debug)] +pub enum ArrangerStatus { + Transport, + ArrangerMix, + ArrangerTrack, + ArrangerScene, + ArrangerClip, + PhrasePool, + PhraseView, + PhraseEdit, +} + +impl StatusBar for ArrangerStatus { + type State = (ArrangerFocus, ArrangerSelection, bool); + fn hotkey_fg () -> Color where Self: Sized { + TuiTheme::hotkey_fg() + } + fn update (&mut self, (focused, selected, entered): &Self::State) { + *self = match focused { + //ArrangerFocus::Menu => { todo!() }, + ArrangerFocus::Transport(_) => ArrangerStatus::Transport, + ArrangerFocus::Arranger => match selected { + ArrangerSelection::Mix => ArrangerStatus::ArrangerMix, + ArrangerSelection::Track(_) => ArrangerStatus::ArrangerTrack, + ArrangerSelection::Scene(_) => ArrangerStatus::ArrangerScene, + ArrangerSelection::Clip(_, _) => ArrangerStatus::ArrangerClip, + }, + ArrangerFocus::Phrases => ArrangerStatus::PhrasePool, + ArrangerFocus::PhraseEditor => match entered { + true => ArrangerStatus::PhraseEdit, + false => ArrangerStatus::PhraseView, + }, + } + } +} + +impl Content for ArrangerStatus { + type Engine = Tui; + fn content (&self) -> impl Widget { + let label = match self { + Self::Transport => "TRANSPORT", + Self::ArrangerMix => "PROJECT", + Self::ArrangerTrack => "TRACK", + Self::ArrangerScene => "SCENE", + Self::ArrangerClip => "CLIP", + Self::PhrasePool => "SEQ LIST", + Self::PhraseView => "VIEW SEQ", + Self::PhraseEdit => "EDIT SEQ", + }; + let status_bar_bg = TuiTheme::status_bar_bg(); + let mode_bg = TuiTheme::mode_bg(); + let mode_fg = TuiTheme::mode_fg(); + let mode = TuiStyle::bold(format!(" {label} "), true).bg(mode_bg).fg(mode_fg); + let commands = match self { + Self::ArrangerMix => Self::command(&[ + ["", "c", "olor"], + ["", "<>", "resize"], + ["", "+-", "zoom"], + ["", "n", "ame/number"], + ["", "Enter", " stop all"], + ]), + Self::ArrangerClip => Self::command(&[ + ["", "g", "et"], + ["", "s", "et"], + ["", "a", "dd"], + ["", "i", "ns"], + ["", "d", "up"], + ["", "e", "dit"], + ["", "c", "olor"], + ["re", "n", "ame"], + ["", ",.", "select"], + ["", "Enter", " launch"], + ]), + Self::ArrangerTrack => Self::command(&[ + ["re", "n", "ame"], + ["", ",.", "resize"], + ["", "<>", "move"], + ["", "i", "nput"], + ["", "o", "utput"], + ["", "m", "ute"], + ["", "s", "olo"], + ["", "Del", "ete"], + ["", "Enter", " stop"], + ]), + Self::ArrangerScene => Self::command(&[ + ["re", "n", "ame"], + ["", "Del", "ete"], + ["", "Enter", " launch"], + ]), + Self::PhrasePool => Self::command(&[ + ["", "a", "ppend"], + ["", "i", "nsert"], + ["", "d", "uplicate"], + ["", "Del", "ete"], + ["", "c", "olor"], + ["re", "n", "ame"], + ["leng", "t", "h"], + ["", ",.", "move"], + ["", "+-", "resize view"], + ]), + Self::PhraseView => Self::command(&[ + ["", "enter", " edit"], + ["", "arrows/pgup/pgdn", " scroll"], + ["", "+=", "zoom"], + ]), + Self::PhraseEdit => Self::command(&[ + ["", "esc", " exit"], + ["", "a", "ppend"], + ["", "s", "et"], + ["", "][", "length"], + ["", "+-", "zoom"], + ]), + _ => Self::command(&[]) + }; + //let commands = commands.iter().reduce(String::new(), |s, (a, b, c)| format!("{s} {a}{b}{c}")); + row!(mode, commands).fill_x().bg(status_bar_bg) + } +} diff --git a/crates/tek_tui/src/tui_app_sequencer.rs b/crates/tek_tui/src/tui_app_sequencer.rs index d33ad9f6..8c65d3d8 100644 --- a/crates/tek_tui/src/tui_app_sequencer.rs +++ b/crates/tek_tui/src/tui_app_sequencer.rs @@ -49,3 +49,53 @@ pub enum SequencerFocus { /// The phrase editor (sequencer) is focused PhraseEditor, } + +impl_focus!(SequencerTui SequencerFocus [ + //&[ + //Menu, + //Menu, + //Menu, + //Menu, + //Menu, + //], + &[ + Content(Transport(TransportFocus::PlayPause)), + Content(Transport(TransportFocus::Bpm)), + Content(Transport(TransportFocus::Sync)), + Content(Transport(TransportFocus::Clock)), + Content(Transport(TransportFocus::Quant)) + ], + &[ + Content(Phrases), + Content(Phrases), + Content(PhraseEditor), + Content(PhraseEditor), + Content(PhraseEditor), + ], +]); + +/// Status bar for sequencer app +#[derive(Copy, Clone)] +pub enum SequencerStatusBar { + Transport, + PhrasePool, + PhraseEditor, +} + +impl StatusBar for SequencerStatusBar { + type State = (); + fn hotkey_fg () -> Color { + TuiTheme::hotkey_fg() + } + fn update (&mut self, state: &()) { + todo!() + } +} + +impl Content for SequencerStatusBar { + type Engine = Tui; + fn content (&self) -> impl Widget { + todo!(); + "" + } +} diff --git a/crates/tek_tui/src/tui_app_transport.rs b/crates/tek_tui/src/tui_app_transport.rs index 1bf1ef18..9386c5f7 100644 --- a/crates/tek_tui/src/tui_app_transport.rs +++ b/crates/tek_tui/src/tui_app_transport.rs @@ -54,3 +54,35 @@ impl FocusWrap for Option { lay!(corners, highlight, *content) } } + +impl_focus!(TransportTui TransportFocus [ + //&[Menu], + &[ + Content(PlayPause), + Content(Bpm), + Content(Sync), + Content(Clock), + Content(Quant) + ], +]); + +#[derive(Copy, Clone)] +pub struct TransportStatusBar; + +impl StatusBar for TransportStatusBar { + type State = (); + fn hotkey_fg () -> Color { + TuiTheme::hotkey_fg() + } + fn update (&mut self, state: &()) { + todo!() + } +} + +impl Content for TransportStatusBar { + type Engine = Tui; + fn content (&self) -> impl Widget { + todo!(); + "" + } +} diff --git a/crates/tek_tui/src/tui_focus.rs b/crates/tek_tui/src/tui_focus.rs deleted file mode 100644 index 3bdc73de..00000000 --- a/crates/tek_tui/src/tui_focus.rs +++ /dev/null @@ -1,121 +0,0 @@ -use crate::*; - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum AppFocus { - /// The menu bar is focused - Menu, - /// The app content is focused - Content(T) -} - -pub trait FocusWrap { - fn wrap <'a, W: Widget> (self, focus: T, content: &'a W) - -> impl Widget + 'a; -} - -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) -> &[&[AppFocus<$Focus>]] { - use AppFocus::*; - use $Focus::*; - &$Grid - } - } - } -} - -impl_focus!(TransportTui TransportFocus [ - //&[Menu], - &[ - Content(PlayPause), - Content(Bpm), - Content(Sync), - Content(Clock), - Content(Quant) - ], -]); - -impl_focus!(SequencerTui SequencerFocus [ - //&[ - //Menu, - //Menu, - //Menu, - //Menu, - //Menu, - //], - &[ - Content(Transport(TransportFocus::PlayPause)), - Content(Transport(TransportFocus::Bpm)), - Content(Transport(TransportFocus::Sync)), - Content(Transport(TransportFocus::Clock)), - Content(Transport(TransportFocus::Quant)) - ], - &[ - Content(Phrases), - Content(Phrases), - Content(PhraseEditor), - Content(PhraseEditor), - Content(PhraseEditor), - ], -]); - -impl_focus!(ArrangerTui ArrangerFocus [ - //&[ - //Menu, - //Menu, - //Menu, - //Menu, - //Menu, - //], - &[ - Content(Transport(TransportFocus::PlayPause)), - Content(Transport(TransportFocus::Bpm)), - Content(Transport(TransportFocus::Sync)), - Content(Transport(TransportFocus::Clock)), - Content(Transport(TransportFocus::Quant)) - ], &[ - Content(Arranger), - Content(Arranger), - Content(Arranger), - Content(Arranger), - Content(Arranger), - ], &[ - Content(Phrases), - Content(Phrases), - Content(PhraseEditor), - Content(PhraseEditor), - Content(PhraseEditor), - ], -]); diff --git a/crates/tek_tui/src/tui_impls.rs b/crates/tek_tui/src/tui_impls.rs deleted file mode 100644 index c7b7e813..00000000 --- a/crates/tek_tui/src/tui_impls.rs +++ /dev/null @@ -1 +0,0 @@ -use crate::*; diff --git a/crates/tek_tui/src/tui_jack.rs b/crates/tek_tui/src/tui_jack.rs deleted file mode 100644 index c7b7e813..00000000 --- a/crates/tek_tui/src/tui_jack.rs +++ /dev/null @@ -1 +0,0 @@ -use crate::*; diff --git a/crates/tek_tui/src/tui_jack_arranger.rs b/crates/tek_tui/src/tui_jack_arranger.rs index 73615148..efdb7c5a 100644 --- a/crates/tek_tui/src/tui_jack_arranger.rs +++ b/crates/tek_tui/src/tui_jack_arranger.rs @@ -27,23 +27,23 @@ impl Audio for ArrangerTui { } // FIXME: one of these per playing track - self.now().set(0.); - if let ArrangerSelection::Clip(t, s) = self.selected { - let phrase = self.scenes().get(s).map(|scene|scene.clips.get(t)); - if let Some(Some(Some(phrase))) = phrase { - if let Some(track) = self.tracks().get(t) { - if let Some((ref started_at, Some(ref playing))) = track.player.play_phrase { - let phrase = phrase.read().unwrap(); - if *playing.read().unwrap() == *phrase { - let pulse = self.current().pulse.get(); - let start = started_at.pulse.get(); - let now = (pulse - start) % phrase.length as f64; - self.now().set(now); - } - } - } - } - } + //self.now.set(0.); + //if let ArrangerSelection::Clip(t, s) = self.selected { + //let phrase = self.scenes().get(s).map(|scene|scene.clips.get(t)); + //if let Some(Some(Some(phrase))) = phrase { + //if let Some(track) = self.tracks().get(t) { + //if let Some((ref started_at, Some(ref playing))) = track.player.play_phrase { + //let phrase = phrase.read().unwrap(); + //if *playing.read().unwrap() == *phrase { + //let pulse = self.current().pulse.get(); + //let start = started_at.pulse.get(); + //let now = (pulse - start) % phrase.length as f64; + //self.now.set(now); + //} + //} + //} + //} + //} // End profiling cycle self.perf.update(t0, scope); diff --git a/crates/tek_tui/src/tui_menu.rs b/crates/tek_tui/src/tui_menu.rs deleted file mode 100644 index 7ae59332..00000000 --- a/crates/tek_tui/src/tui_menu.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::*; - -fn content_with_menu_and_status <'a, A, S, C> ( - content: &'a A, - menu_bar: &'a Option>, - status_bar: &'a Option -) -> impl Widget + 'a -where - A: Widget, - S: Send + Sync + 'a, - C: Command -{ - let menus = menu_bar.as_ref().map_or_else( - ||&[] as &[Menu<_, _, _>], - |m|m.menus.as_slice() - ); - Either( - menu_bar.is_none(), - Either( - status_bar.is_none(), - widget(content), - Split::up( - 1, - widget(status_bar.as_ref().unwrap()), - widget(content) - ), - ), - Split::down( - 1, - row!(menu in menus.iter() => { - row!(" ", menu.title.as_str(), " ") - }), - widget(content) - ) - ) -} diff --git a/crates/tek_tui/src/tui_model.rs b/crates/tek_tui/src/tui_model.rs deleted file mode 100644 index c7b7e813..00000000 --- a/crates/tek_tui/src/tui_model.rs +++ /dev/null @@ -1 +0,0 @@ -use crate::*; diff --git a/crates/tek_tui/src/tui_model_phrase_editor.rs b/crates/tek_tui/src/tui_model_phrase_editor.rs index 5998e297..906676d1 100644 --- a/crates/tek_tui/src/tui_model_phrase_editor.rs +++ b/crates/tek_tui/src/tui_model_phrase_editor.rs @@ -52,3 +52,33 @@ impl Default for PhraseEditorModel { } } } + +pub trait HasEditor { + fn editor (&self) -> &PhraseEditorModel; + fn editor_focused (&self) -> bool; + fn editor_entered (&self) -> bool; +} + +impl HasEditor for SequencerTui { + fn editor (&self) -> &PhraseEditorModel { + &self.editor + } + fn editor_focused (&self) -> bool { + self.focused() == AppFocus::Content(SequencerFocus::PhraseEditor) + } + fn editor_entered (&self) -> bool { + self.entered() && self.editor_focused() + } +} + +impl HasEditor for ArrangerTui { + fn editor (&self) -> &PhraseEditorModel { + &self.editor + } + fn editor_focused (&self) -> bool { + self.focused() == AppFocus::Content(ArrangerFocus::PhraseEditor) + } + fn editor_entered (&self) -> bool { + self.entered() && self.editor_focused() + } +} diff --git a/crates/tek_tui/src/tui_status.rs b/crates/tek_tui/src/tui_status.rs deleted file mode 100644 index 459fb8e9..00000000 --- a/crates/tek_tui/src/tui_status.rs +++ /dev/null @@ -1,195 +0,0 @@ -use crate::*; - -pub trait StatusBar: Copy + Widget { - - type State; - - fn hotkey_fg () -> Color where Self: Sized; - - fn update (&mut self, state: &Self::State) where Self: Sized; - - fn command (commands: &[[impl Widget;3]]) - -> impl Widget + '_ - where - Self: Sized - { - let hotkey_fg = Self::hotkey_fg(); - Stack::right(move |add|{ - Ok(for [a, b, c] in commands.iter() { - add(&row!( - " ", - widget(a), - widget(b).bold(true).fg(hotkey_fg), - widget(c), - ))?; - }) - }) - } -} - -#[derive(Copy, Clone)] -pub struct TransportStatusBar; - -impl StatusBar for TransportStatusBar { - type State = (); - fn hotkey_fg () -> Color { - TuiTheme::hotkey_fg() - } - fn update (&mut self, state: &()) { - todo!() - } -} - -impl Content for TransportStatusBar { - type Engine = Tui; - fn content (&self) -> impl Widget { - todo!(); - "" - } -} - -/// Status bar for sequencer app -#[derive(Copy, Clone)] -pub enum SequencerStatusBar { - Transport, - PhrasePool, - PhraseEditor, -} - -impl StatusBar for SequencerStatusBar { - type State = (); - fn hotkey_fg () -> Color { - TuiTheme::hotkey_fg() - } - fn update (&mut self, state: &()) { - todo!() - } -} - -impl Content for SequencerStatusBar { - type Engine = Tui; - fn content (&self) -> impl Widget { - todo!(); - "" - } -} - -/// Status bar for arranger app -#[derive(Copy, Clone, Debug)] -pub enum ArrangerStatus { - Transport, - ArrangerMix, - ArrangerTrack, - ArrangerScene, - ArrangerClip, - PhrasePool, - PhraseView, - PhraseEdit, -} - -impl StatusBar for ArrangerStatus { - type State = (ArrangerFocus, ArrangerSelection, bool); - fn hotkey_fg () -> Color where Self: Sized { - TuiTheme::hotkey_fg() - } - fn update (&mut self, (focused, selected, entered): &Self::State) { - *self = match focused { - //ArrangerFocus::Menu => { todo!() }, - ArrangerFocus::Transport(_) => ArrangerStatus::Transport, - ArrangerFocus::Arranger => match selected { - ArrangerSelection::Mix => ArrangerStatus::ArrangerMix, - ArrangerSelection::Track(_) => ArrangerStatus::ArrangerTrack, - ArrangerSelection::Scene(_) => ArrangerStatus::ArrangerScene, - ArrangerSelection::Clip(_, _) => ArrangerStatus::ArrangerClip, - }, - ArrangerFocus::Phrases => ArrangerStatus::PhrasePool, - ArrangerFocus::PhraseEditor => match entered { - true => ArrangerStatus::PhraseEdit, - false => ArrangerStatus::PhraseView, - }, - } - } -} - -impl Content for ArrangerStatus { - type Engine = Tui; - fn content (&self) -> impl Widget { - let label = match self { - Self::Transport => "TRANSPORT", - Self::ArrangerMix => "PROJECT", - Self::ArrangerTrack => "TRACK", - Self::ArrangerScene => "SCENE", - Self::ArrangerClip => "CLIP", - Self::PhrasePool => "SEQ LIST", - Self::PhraseView => "VIEW SEQ", - Self::PhraseEdit => "EDIT SEQ", - }; - let status_bar_bg = TuiTheme::status_bar_bg(); - let mode_bg = TuiTheme::mode_bg(); - let mode_fg = TuiTheme::mode_fg(); - let mode = TuiStyle::bold(format!(" {label} "), true).bg(mode_bg).fg(mode_fg); - let commands = match self { - Self::ArrangerMix => Self::command(&[ - ["", "c", "olor"], - ["", "<>", "resize"], - ["", "+-", "zoom"], - ["", "n", "ame/number"], - ["", "Enter", " stop all"], - ]), - Self::ArrangerClip => Self::command(&[ - ["", "g", "et"], - ["", "s", "et"], - ["", "a", "dd"], - ["", "i", "ns"], - ["", "d", "up"], - ["", "e", "dit"], - ["", "c", "olor"], - ["re", "n", "ame"], - ["", ",.", "select"], - ["", "Enter", " launch"], - ]), - Self::ArrangerTrack => Self::command(&[ - ["re", "n", "ame"], - ["", ",.", "resize"], - ["", "<>", "move"], - ["", "i", "nput"], - ["", "o", "utput"], - ["", "m", "ute"], - ["", "s", "olo"], - ["", "Del", "ete"], - ["", "Enter", " stop"], - ]), - Self::ArrangerScene => Self::command(&[ - ["re", "n", "ame"], - ["", "Del", "ete"], - ["", "Enter", " launch"], - ]), - Self::PhrasePool => Self::command(&[ - ["", "a", "ppend"], - ["", "i", "nsert"], - ["", "d", "uplicate"], - ["", "Del", "ete"], - ["", "c", "olor"], - ["re", "n", "ame"], - ["leng", "t", "h"], - ["", ",.", "move"], - ["", "+-", "resize view"], - ]), - Self::PhraseView => Self::command(&[ - ["", "enter", " edit"], - ["", "arrows/pgup/pgdn", " scroll"], - ["", "+=", "zoom"], - ]), - Self::PhraseEdit => Self::command(&[ - ["", "esc", " exit"], - ["", "a", "ppend"], - ["", "s", "et"], - ["", "][", "length"], - ["", "+-", "zoom"], - ]), - _ => Self::command(&[]) - }; - //let commands = commands.iter().reduce(String::new(), |s, (a, b, c)| format!("{s} {a}{b}{c}")); - row!(mode, commands).fill_x().bg(status_bar_bg) - } -} diff --git a/crates/tek_tui/src/tui_theme.rs b/crates/tek_tui/src/tui_theme.rs deleted file mode 100644 index c7b7e813..00000000 --- a/crates/tek_tui/src/tui_theme.rs +++ /dev/null @@ -1 +0,0 @@ -use crate::*; diff --git a/crates/tek_tui/src/tui_view_arranger.rs b/crates/tek_tui/src/tui_view_arranger.rs index c8b4364b..6b048d41 100644 --- a/crates/tek_tui/src/tui_view_arranger.rs +++ b/crates/tek_tui/src/tui_view_arranger.rs @@ -36,7 +36,7 @@ impl Content for ArrangerTui { Split::right( self.splits[1], PhrasesView(self), - PhraseView(self), + PhraseView2::from(self), ) ) ) diff --git a/crates/tek_tui/src/tui_view_phrase_editor.rs b/crates/tek_tui/src/tui_view_phrase_editor.rs index 7d8cc860..7bf8afef 100644 --- a/crates/tek_tui/src/tui_view_phrase_editor.rs +++ b/crates/tek_tui/src/tui_view_phrase_editor.rs @@ -1,29 +1,41 @@ use crate::*; -impl Widget for PhraseEditorModel { - type Engine = Tui; - fn layout (&self, to: [u16;2]) -> Perhaps<[u16;2]> { - PhraseView(self).layout(to) - } - fn render (&self, to: &mut TuiOutput) -> Usually<()> { - PhraseView(self).render(to) +pub struct PhraseView2<'a> { + pub(crate) focused: bool, + pub(crate) entered: bool, + pub(crate) phrase: &'a Option>>, + pub(crate) size: &'a Measure, + pub(crate) keys: &'a Buffer, + pub(crate) buffer: &'a BigBuffer, + pub(crate) note_len: usize, + pub(crate) note_axis: &'a RwLock>, + pub(crate) time_axis: &'a RwLock>, + pub(crate) now: &'a Arc, +} + +impl<'a, T: HasEditor> From<&'a T> for PhraseView2<'a> { + fn from (state: &'a T) -> Self { + Self { + focused: state.editor_focused(), + entered: state.editor_entered(), + note_len: state.editor().note_len, + phrase: &state.editor().phrase, + size: &state.editor().size, + keys: &state.editor().keys, + buffer: &state.editor().buffer, + note_axis: &state.editor().note_axis, + time_axis: &state.editor().time_axis, + now: &state.editor().now + } } } -pub struct PhraseView<'a, T: PhraseViewState>(pub &'a T); - -impl<'a, T: PhraseViewState> Content for PhraseView<'a, T> { +impl<'a> Content for PhraseView2<'a> { type Engine = Tui; fn content (&self) -> impl Widget { - let phrase = self.0.phrase_editing(); - let size = self.0.size(); - let focused = self.0.phrase_editor_focused(); - let entered = self.0.phrase_editor_entered(); - let keys = self.0.keys(); - let buffer = self.0.buffer(); - let note_len = self.0.note_len(); - let note_axis = self.0.note_axis(); - let time_axis = self.0.time_axis(); + let Self { + focused, entered, phrase, size, keys, buffer, note_len, note_axis, time_axis, now + } = self; let FixedAxis { start: note_start, point: note_point, clamp: note_clamp } = *note_axis.read().unwrap(); let ScaledAxis { start: time_start, point: time_point, clamp: time_clamp, scale: time_scale } @@ -68,7 +80,7 @@ impl<'a, T: PhraseViewState> Content for PhraseView<'a, T> { }) }).fill_x(); let cursor = CustomWidget::new(|to|Ok(Some(to)), move|to: &mut TuiOutput|{ - Ok(if focused && entered { + Ok(if *focused && *entered { let area = to.area(); if let (Some(time), Some(note)) = (time_point, note_point) { let x1 = area.x() + (time / time_scale) as u16; @@ -87,7 +99,7 @@ impl<'a, T: PhraseViewState> Content for PhraseView<'a, T> { |to:[u16;2]|Ok(Some(to.clip_h(1))), move|to: &mut TuiOutput|{ if let Some(_) = phrase { - let now = self.0.now().get() as usize; // TODO FIXME: self.now % phrase.read().unwrap().length; + let now = now.get() as usize; // TODO FIXME: self.now % phrase.read().unwrap().length; let time_clamp = time_clamp .expect("time_axis of sequencer expected to be clamped"); for x in 0..(time_clamp/time_scale).saturating_sub(time_start) { @@ -103,14 +115,14 @@ impl<'a, T: PhraseViewState> Content for PhraseView<'a, T> { Ok(()) } ).push_x(6).align_sw(); - let border_color = if focused{Color::Rgb(100, 110, 40)}else{Color::Rgb(70, 80, 50)}; - let title_color = if focused{Color::Rgb(150, 160, 90)}else{Color::Rgb(120, 130, 100)}; + let border_color = if *focused{Color::Rgb(100, 110, 40)}else{Color::Rgb(70, 80, 50)}; + let title_color = if *focused{Color::Rgb(150, 160, 90)}else{Color::Rgb(120, 130, 100)}; let border = Lozenge(Style::default().bg(Color::Rgb(40, 50, 30)).fg(border_color)); let note_area = lay!(notes, cursor).fill_x(); let piano_roll = row!(keys, note_area).fill_x(); let content = piano_roll.bg(Color::Rgb(40, 50, 30)).border(border); let content = lay!(content, playhead); - let mut upper_left = format!("[{}] Sequencer", if entered {"■"} else {" "}); + let mut upper_left = format!("[{}] Sequencer", if *entered {"■"} else {" "}); if let Some(phrase) = phrase { upper_left = format!("{upper_left}: {}", phrase.read().unwrap().name); } @@ -121,10 +133,10 @@ impl<'a, T: PhraseViewState> Content for PhraseView<'a, T> { //time_start, time_point.unwrap_or(0), //time_scale, time_clamp.unwrap_or(0), //); - if focused && entered { + if *focused && *entered { lower_right = format!("┤Note: {} {}├─{lower_right}", note_axis.read().unwrap().point.unwrap(), - pulses_to_name(note_len)); + pulses_to_name(*note_len)); //lower_right = format!("Note: {} (+{}:{}|{}) {upper_right}", //pulses_to_name(*note_len), //note_start, @@ -146,74 +158,6 @@ impl<'a, T: PhraseViewState> Content for PhraseView<'a, T> { } } -pub trait PhraseViewState: Send + Sync { - fn phrase_editing (&self) -> &Option>>; - fn phrase_editor_focused (&self) -> bool; - fn phrase_editor_size (&self) -> &Measure; - fn phrase_editor_entered (&self) -> bool; - fn keys (&self) -> &Buffer; - fn buffer (&self) -> &BigBuffer; - fn note_len (&self) -> usize; - fn note_axis (&self) -> &RwLock>; - fn time_axis (&self) -> &RwLock>; - fn now (&self) -> &Arc; - fn size (&self) -> &Measure; -} - -macro_rules! impl_phrase_view_state { - ($Struct:ident $(:: $field:ident)* [$self1:ident : $focused:expr] [$self2:ident : $entered:expr]) => { - impl PhraseViewState for $Struct { - fn phrase_editing (&self) -> &Option>> { - &self$(.$field)*.phrase - } - fn phrase_editor_focused (&$self1) -> bool { - $focused - //self$(.$field)*.focus.is_focused() - } - fn phrase_editor_entered (&$self2) -> bool { - $entered - //self$(.$field)*.focus.is_entered() - } - fn phrase_editor_size (&self) -> &Measure { - todo!() - } - fn keys (&self) -> &Buffer { - &self$(.$field)*.keys - } - fn buffer (&self) -> &BigBuffer { - &self$(.$field)*.buffer - } - fn note_len (&self) -> usize { - self$(.$field)*.note_len - } - fn note_axis (&self) -> &RwLock> { - &self$(.$field)*.note_axis - } - fn time_axis (&self) -> &RwLock> { - &self$(.$field)*.time_axis - } - fn now (&self) -> &Arc { - &self$(.$field)*.now - } - fn size (&self) -> &Measure { - &self$(.$field)*.size - } - } - } -} -impl_phrase_view_state!(PhraseEditorModel - [self: true] - [self: true] -); -impl_phrase_view_state!(SequencerTui::editor - [self: self.focused() == AppFocus::Content(SequencerFocus::PhraseEditor)] - [self: self.entered() && self.focused() == AppFocus::Content(SequencerFocus::PhraseEditor)] -); -impl_phrase_view_state!(ArrangerTui::editor - [self: self.focused() == AppFocus::Content(ArrangerFocus::PhraseEditor)] - [self: self.entered() && self.focused() == AppFocus::Content(ArrangerFocus::PhraseEditor)]) -; - /// Colors of piano keys const KEY_COLORS: [(Color, Color);6] = [ (Color::Rgb(255, 255, 255), Color::Rgb(255, 255, 255)), diff --git a/crates/tek_tui/src/tui_view_sequencer.rs b/crates/tek_tui/src/tui_view_sequencer.rs index a27a7e65..657aeb4b 100644 --- a/crates/tek_tui/src/tui_view_sequencer.rs +++ b/crates/tek_tui/src/tui_view_sequencer.rs @@ -8,7 +8,7 @@ impl Content for SequencerTui { TransportView::from(self), Split::right(20, widget(&PhrasesView(self)), - widget(&PhraseView(self)), + widget(&PhraseView2::from(self)), ).min_y(20) ), self.perf.percentage()