pub(crate) use tek_core::crossterm::event::{KeyCode, KeyModifiers}; pub(crate) use tek_core::midly::{num::u7, live::LiveEvent, MidiMessage}; pub(crate) use tek_core::{*, jack::*}; pub(crate) use tek_api::*; pub(crate) use std::collections::BTreeMap; pub(crate) use std::sync::{Arc, Mutex, RwLock}; pub(crate) use std::path::PathBuf; pub(crate) use std::ffi::OsString; pub(crate) use std::fs::read_dir; submod! { tui_app_arranger tui_app_sequencer tui_app_transport tui_jack_transport tui_jack_sequencer tui_jack_arranger tui_control_arranger tui_control_file_browser tui_control_phrase_editor tui_control_phrase_length tui_control_phrase_list tui_control_phrase_rename tui_control_sequencer tui_control_transport tui_model_arranger tui_model_clock tui_model_file_browser tui_model_phrase_editor tui_model_phrase_length tui_model_phrase_list tui_model_phrase_player tui_view_arranger tui_view_file_browser tui_view_phrase_editor tui_view_phrase_length tui_view_phrase_list tui_view_phrase_selector tui_view_sequencer tui_view_transport } pub fn to_focus_command (input: &TuiInput) -> Option { use KeyCode::{Tab, BackTab, Up, Down, Left, Right, Enter, Esc}; Some(match input.event() { key!(Tab) => FocusCommand::Next, key!(Shift-Tab) => FocusCommand::Prev, key!(BackTab) => FocusCommand::Prev, key!(Shift-BackTab) => FocusCommand::Prev, key!(Up) => FocusCommand::Up, key!(Down) => FocusCommand::Down, key!(Left) => FocusCommand::Left, key!(Right) => FocusCommand::Right, key!(Enter) => FocusCommand::Enter, key!(Esc) => FocusCommand::Exit, _ => return None }) } pub struct TuiTheme; impl TuiTheme { pub fn border_bg () -> Color { Color::Rgb(40, 50, 30) } pub fn border_fg (focused: bool) -> Color { if focused { Color::Rgb(100, 110, 40) } else { Color::Rgb(70, 80, 50) } } pub fn title_fg (focused: bool) -> Color { if focused { Color::Rgb(150, 160, 90) } else { Color::Rgb(120, 130, 100) } } pub fn separator_fg (_: bool) -> Color { Color::Rgb(0, 0, 0) } pub const fn hotkey_fg () -> Color { Color::Rgb(255, 255, 0) } pub fn mode_bg () -> Color { Color::Rgb(150, 160, 90) } pub fn mode_fg () -> Color { Color::Rgb(255, 255, 255) } pub fn status_bar_bg () -> Color { Color::Rgb(28, 35, 25) } } macro_rules! impl_midi_player { ($Struct:ident $(:: $field:ident)*) => { impl HasPhrase for $Struct { fn reset (&self) -> bool { self$(.$field)*.reset } fn reset_mut (&mut self) -> &mut bool { &mut self$(.$field)*.reset } fn play_phrase (&self) -> &Option<(Instant, Option>>)> { &self$(.$field)*.play_phrase } fn play_phrase_mut (&mut self) -> &mut Option<(Instant, Option>>)> { &mut self$(.$field)*.play_phrase } fn next_phrase (&self) -> &Option<(Instant, Option>>)> { &self$(.$field)*.next_phrase } fn next_phrase_mut (&mut self) -> &mut Option<(Instant, Option>>)> { &mut self$(.$field)*.next_phrase } } impl MidiInputApi for $Struct { fn midi_ins (&self) -> &Vec> { &self$(.$field)*.midi_ins } fn midi_ins_mut (&mut self) -> &mut Vec> { &mut self$(.$field)*.midi_ins } fn recording (&self) -> bool { self$(.$field)*.recording } fn recording_mut (&mut self) -> &mut bool { &mut self$(.$field)*.recording } fn monitoring (&self) -> bool { self$(.$field)*.monitoring } fn monitoring_mut (&mut self) -> &mut bool { &mut self$(.$field)*.monitoring } fn overdub (&self) -> bool { self$(.$field)*.overdub } fn overdub_mut (&mut self) -> &mut bool { &mut self$(.$field)*.overdub } fn notes_in (&self) -> &Arc> { &self$(.$field)*.notes_in } } impl MidiOutputApi for $Struct { fn midi_outs (&self) -> &Vec> { &self$(.$field)*.midi_outs } fn midi_outs_mut (&mut self) -> &mut Vec> { &mut self$(.$field)*.midi_outs } fn midi_note (&mut self) -> &mut Vec { &mut self$(.$field)*.note_buf } fn notes_out (&self) -> &Arc> { &self$(.$field)*.notes_in } } impl MidiPlayerApi for $Struct {} } } impl_midi_player!(SequencerTui::player); impl_midi_player!(ArrangerTrack::player); impl_midi_player!(PhrasePlayerModel); use std::fmt::{Debug, Formatter, Error}; type DebugResult = std::result::Result<(), Error>; impl Debug for TransportTui { fn fmt (&self, f: &mut Formatter<'_>) -> DebugResult { f.debug_struct("Measure") .field("jack", &self.jack) .field("size", &self.size) .field("cursor", &self.cursor) .finish() } } impl Debug for PhraseEditorModel { fn fmt (&self, f: &mut Formatter<'_>) -> DebugResult { f.debug_struct("editor") .field("note_axis", &self.time_axis) .field("time_axis", &self.note_axis) .finish() } } impl Debug for PhrasePlayerModel { fn fmt (&self, f: &mut Formatter<'_>) -> DebugResult { f.debug_struct("editor") .field("clock", &self.clock) .field("play_phrase", &self.play_phrase) .field("next_phrase", &self.next_phrase) .finish() } } 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 = $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) -> &[&[$Focus]] { use $Focus::*; &$Grid } } } } pub trait StatusBar: 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 with <'a> (state: &'a Self::State, content: impl Widget) -> impl Widget where Self: Sized, &'a Self::State: Into { Split::up(1, state.into(), content) } } 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) ) ) }