From 3a7aa9e9a3ec1f63a34d4e206f3f942085206e03 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sat, 24 Aug 2024 00:27:24 +0300 Subject: [PATCH] wip: unified focus system --- crates/tek/src/control.rs | 12 +- crates/tek_core/src/handle.rs | 33 ----- crates/tek_core/src/handle_focus.rs | 51 ++++++++ crates/tek_core/src/handle_keymap.rs | 34 +++++ crates/tek_core/src/lib.rs | 2 + crates/tek_core/src/render.rs | 10 +- crates/tek_sequencer/src/arranger.rs | 20 +-- crates/tek_sequencer/src/arranger_main.rs | 135 ++++++++++++-------- crates/tek_sequencer/src/arranger_view_h.rs | 2 +- crates/tek_sequencer/src/arranger_view_v.rs | 18 +-- 10 files changed, 194 insertions(+), 123 deletions(-) create mode 100644 crates/tek_core/src/handle_focus.rs create mode 100644 crates/tek_core/src/handle_keymap.rs diff --git a/crates/tek/src/control.rs b/crates/tek/src/control.rs index 366cccdc..46bd349f 100644 --- a/crates/tek/src/control.rs +++ b/crates/tek/src/control.rs @@ -144,14 +144,14 @@ pub const KEYMAP_FOCUS: &'static [KeyBinding] = keymap!(App { [Esc, NONE, "focus_exit", "unfocus", |app: &mut App|{ app.entered = false; app.transport.entered = app.entered; - app.arranger.entered = app.entered; + //app.arranger.entered = app.entered; app.arranger.sequencer_mut().map(|s|s.entered = app.entered); Ok(true) }], [Enter, NONE, "focus_enter", "activate item at cursor", |app: &mut App|{ app.entered = true; app.transport.entered = app.entered; - app.arranger.entered = app.entered; + //app.arranger.entered = app.entered; app.arranger.sequencer_mut().map(|s|s.entered = app.entered); Ok(true) }], @@ -161,8 +161,8 @@ pub fn focus_next (app: &mut App) -> Usually { app.section.next(); app.transport.focused = app.section == AppFocus::Transport; app.transport.entered = app.entered; - app.arranger.focused = app.section == AppFocus::Arranger; - app.arranger.entered = app.entered; + //app.arranger.focused = app.section == AppFocus::Arranger; + //app.arranger.entered = app.entered; app.arranger.sequencer_mut().map(|s|{ s.focused = app.section == AppFocus::Sequencer; s.entered = app.entered; @@ -174,8 +174,8 @@ pub fn focus_prev (app: &mut App) -> Usually { app.section.prev(); app.transport.focused = app.section == AppFocus::Transport; app.transport.entered = app.entered; - app.arranger.focused = app.section == AppFocus::Arranger; - app.arranger.entered = app.entered; + //app.arranger.focused = app.section == AppFocus::Arranger; + //app.arranger.entered = app.entered; app.arranger.sequencer_mut().map(|s|{ s.focused = app.section == AppFocus::Sequencer; s.entered = app.entered; diff --git a/crates/tek_core/src/handle.rs b/crates/tek_core/src/handle.rs index fd767afb..c95aef8a 100644 --- a/crates/tek_core/src/handle.rs +++ b/crates/tek_core/src/handle.rs @@ -73,36 +73,3 @@ pub enum AppEvent { // /// JACK notification // Jack(JackEvent) } - -pub type KeyHandler = &'static dyn Fn(&mut T)->Usually; - -pub type KeyBinding = ( - KeyCode, KeyModifiers, &'static str, &'static str, KeyHandler -); - -pub type KeyMap = [KeyBinding]; - -pub fn handle_keymap ( - state: &mut T, event: &AppEvent, keymap: &KeyMap, -) -> Usually { - match event { - AppEvent::Input(crossterm::event::Event::Key(event)) => { - for (code, modifiers, _, _, command) in keymap.iter() { - if *code == event.code && modifiers.bits() == event.modifiers.bits() { - return command(state) - } - } - }, - _ => {} - }; - Ok(false) -} - -/// Define a keymap -#[macro_export] macro_rules! keymap { - ($T:ty { $([$k:ident $(($char:literal))?, $m:ident, $n: literal, $d: literal, $f: expr]),* $(,)? }) => { - &[ - $((KeyCode::$k $(($char))?, KeyModifiers::$m, $n, $d, &$f as KeyHandler<$T>)),* - ] as &'static [KeyBinding<$T>] - } -} diff --git a/crates/tek_core/src/handle_focus.rs b/crates/tek_core/src/handle_focus.rs new file mode 100644 index 00000000..3aec81b5 --- /dev/null +++ b/crates/tek_core/src/handle_focus.rs @@ -0,0 +1,51 @@ +use crate::*; + +/// A collection of [Focusable] items. +pub struct Focus { + index: usize, + items: Vec +} + +impl Focus { + pub fn new (items: Vec) -> Self { + Self { + index: 0, + items + } + } + pub fn item_mut (&mut self) -> &mut impl Handle { + &mut self.items[self.index] + } + /// Select next item. + pub fn next (&mut self) -> Usually { + self.index = (self.index + 1) % self.items.len(); + Ok(true) + } + /// Select previous item. + pub fn prev (&mut self) -> Usually { + self.index = match self.index { + 0 => self.items.len().saturating_sub(1), + _ => self.index - 1 + }; + Ok(true) + } +} + +handle!(Focus |self, e| Ok( + handle_keymap(self, e, KEYMAP_FOCUS)? || self.item_mut().handle(e)? +)); + +pub const KEYMAP_FOCUS: &'static [KeyBinding] = keymap!(Focus { + [Tab, NONE, "focus_next", "focus next item", Focus::next], + [Tab, SHIFT, "focus_prev", "focus previous item", Focus::prev], +}); + +/// A wrapper around items that can be focused. +pub enum Focusable { + /// A monolithic focus item. + Mono(Arc), + /// A focus item that contains other focus items. + Poly(Box), +} + +handle!(Focusable |self, e| { todo!("{e:?}"); Ok(false) }); diff --git a/crates/tek_core/src/handle_keymap.rs b/crates/tek_core/src/handle_keymap.rs new file mode 100644 index 00000000..dd5caad4 --- /dev/null +++ b/crates/tek_core/src/handle_keymap.rs @@ -0,0 +1,34 @@ +use crate::*; + +pub type KeyHandler = &'static dyn Fn(&mut T)->Usually; + +pub type KeyBinding = ( + KeyCode, KeyModifiers, &'static str, &'static str, KeyHandler +); + +pub type KeyMap = [KeyBinding]; + +pub fn handle_keymap ( + state: &mut T, event: &AppEvent, keymap: &KeyMap, +) -> Usually { + match event { + AppEvent::Input(crossterm::event::Event::Key(event)) => { + for (code, modifiers, _, _, command) in keymap.iter() { + if *code == event.code && modifiers.bits() == event.modifiers.bits() { + return command(state) + } + } + }, + _ => {} + }; + Ok(false) +} + +/// Define a keymap +#[macro_export] macro_rules! keymap { + ($T:ty { $([$k:ident $(($char:literal))?, $m:ident, $n: literal, $d: literal, $f: expr]),* $(,)? }) => { + &[ + $((KeyCode::$k $(($char))?, KeyModifiers::$m, $n, $d, &$f as KeyHandler<$T>)),* + ] as &'static [KeyBinding<$T>] + } +} diff --git a/crates/tek_core/src/lib.rs b/crates/tek_core/src/lib.rs index eb253797..96e863d2 100644 --- a/crates/tek_core/src/lib.rs +++ b/crates/tek_core/src/lib.rs @@ -46,6 +46,8 @@ use crossterm::terminal::{ submod! { exit handle + handle_focus + handle_keymap jack_core jack_device jack_event diff --git a/crates/tek_core/src/render.rs b/crates/tek_core/src/render.rs index 67d91b80..b58a1c35 100644 --- a/crates/tek_core/src/render.rs +++ b/crates/tek_core/src/render.rs @@ -123,13 +123,19 @@ impl<'a> Render for Box Usually + Send + Sync } } -impl Render for Arc> { +impl Render for Arc { + fn render (&self, b: &mut Buffer, a: Rect) -> Usually { + self.as_ref().render(b, a) + } +} + +impl Render for Mutex { fn render (&self, b: &mut Buffer, a: Rect) -> Usually { self.lock().unwrap().render(b, a) } } -impl Render for Arc> { +impl Render for RwLock { fn render (&self, b: &mut Buffer, a: Rect) -> Usually { self.read().unwrap().render(b, a) } diff --git a/crates/tek_sequencer/src/arranger.rs b/crates/tek_sequencer/src/arranger.rs index 0d91d0a6..b4487892 100644 --- a/crates/tek_sequencer/src/arranger.rs +++ b/crates/tek_sequencer/src/arranger.rs @@ -15,25 +15,19 @@ pub struct Arranger { pub selected: ArrangerFocus, /// Display mode of arranger pub mode: ArrangerViewMode, - - pub focused: bool, - pub entered: bool, - pub focus_sequencer: bool, + /// Slot for modal dialog displayed on top of app. pub modal: Option>, } impl Arranger { pub fn new (name: &str) -> Self { Self { - name: Arc::new(RwLock::new(name.into())), - mode: ArrangerViewMode::VerticalCompact2, - selected: ArrangerFocus::Clip(0, 0), - scenes: vec![], - tracks: vec![], - entered: true, - focused: true, - focus_sequencer: false, - modal: None + name: Arc::new(RwLock::new(name.into())), + mode: ArrangerViewMode::VerticalCompact2, + selected: ArrangerFocus::Clip(0, 0), + scenes: vec![], + tracks: vec![], + modal: None } } pub fn activate (&mut self) { diff --git a/crates/tek_sequencer/src/arranger_main.rs b/crates/tek_sequencer/src/arranger_main.rs index 45e52447..d4cec845 100644 --- a/crates/tek_sequencer/src/arranger_main.rs +++ b/crates/tek_sequencer/src/arranger_main.rs @@ -8,11 +8,22 @@ pub fn main () -> Usually<()> { } struct ArrangerStandalone { - arranger: Arranger, - transport: Option>>, - show_sequencer: Option, + /// Contains all the sequencers. + arranger: Arc, + /// Controls the JACK transport. + transport: Option>>, + /// This allows the sequencer view to be moved or hidden. + show_sequencer: Option, + /// Proxies input events to the currently active sequencer. + sequencer_proxy: SequencerProxy, + /// + focus_order: Vec, } +struct SequencerProxy; + +impl Focus for SequencerProxy {} + #[derive(Debug, Parser)] #[command(version, about, long_about = None)] pub struct ArrangerCli { @@ -28,17 +39,24 @@ pub struct ArrangerCli { #[arg(short, long, default_value_t = 8)] scenes: usize, } -impl ArrangerStandalone { +impl<'a> ArrangerStandalone<'a> { pub fn from_args () -> Usually { let args = ArrangerCli::parse(); - let mut app = ArrangerStandalone { - arranger: Arranger::new(""), - transport: match args.transport { - Some(true) => Some(Arc::new(RwLock::new(TransportToolbar::new(None)))), - _ => None - }, - show_sequencer: Some(tek_core::Direction::Down), + let arranger = Arranger::new(""); + let transport = match args.transport { + Some(true) => Some(Arc::new(RwLock::new(TransportToolbar::new(None)))), + _ => None }; + let sequencer_proxy = SequencerProxy; + let mut app = ArrangerStandalone { + transport, + show_sequencer: Some(tek_core::Direction::Down), + arranger, + sequencer_proxy, + focus_order: vec![], + }; + app.focus_order.push(FocusItem::Mono(&app.arranger)); + app.focus_order.push(FocusItem::Poly(&app.sequencer_proxy)); if let Some(name) = args.name { *app.arranger.name.write().unwrap() = name.clone(); } @@ -60,52 +78,57 @@ impl ArrangerStandalone { } } -render!(ArrangerStandalone |self, buf, area| { - let mut layout = Split::down(); - if let Some(transport) = &self.transport { - layout = layout.add_ref(transport); - } - let sequencer = self.arranger.sequencer(); - if let Some(direction) = self.show_sequencer { - layout = layout.add(Split::new(direction) - .add_ref(&self.arranger) - .add(sequencer)) - } else { - layout = layout.add_ref(&self.arranger) - } - let result = layout.render(buf, area)?; - if let Some(ref modal) = self.arranger.modal { - fill_bg(buf, area, Nord::bg_lo(false, false)); - fill_fg(buf, area, Nord::bg_hi(false, false)); - modal.render(buf, area)?; - } - Ok(result) -}); - -handle!(ArrangerStandalone |self, e| { - if let Some(modal) = self.arranger.modal.as_mut() { - let result = modal.handle(e)?; - if modal.exited() { - self.arranger.modal = None; +impl Render for ArrangerStandalone { + fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { + let mut layout = Split::down(); + if let Some(transport) = &self.transport { + layout = layout.add_ref(transport); + } + let sequencer = self.arranger.sequencer(); + if let Some(direction) = self.show_sequencer { + layout = layout.add(Split::new(direction) + .add_ref(&self.arranger) + .add(sequencer)) + } else { + layout = layout.add_ref(&self.arranger) + } + let result = layout.render(buf, area)?; + if let Some(ref modal) = self.arranger.modal { + fill_bg(buf, area, Nord::bg_lo(false, false)); + fill_fg(buf, area, Nord::bg_hi(false, false)); + modal.render(buf, area)?; } Ok(result) - } else { - match e { - AppEvent::Input(Event::Key(k)) => { - if k.code == KeyCode::Tab { - self.arranger.focus_sequencer = !self.arranger.focus_sequencer; - Ok(true) - } else if self.arranger.focus_sequencer { - if let Some(sequencer) = self.arranger.sequencer_mut() { - handle_keymap(sequencer, e, KEYMAP_SEQUENCER) - } else { - Ok(false) - } - } else { - handle_keymap(&mut self.arranger, e, KEYMAP_ARRANGER) - } - }, - _ => Ok(false), + } +} + +impl Handle for ArrangerStandalone { + fn handle (&mut self, e: &AppEvent) -> Usually { + if let Some(modal) = self.arranger.modal.as_mut() { + let result = modal.handle(e)?; + if modal.exited() { + self.arranger.modal = None; + } + Ok(result) + } else { + match e { + AppEvent::Input(Event::Key(k)) => { + Ok(false) + //if k.code == KeyCode::Tab { + //self.arranger.focus_sequencer = !self.arranger.focus_sequencer; + //Ok(true) + //} else if self.arranger.focus_sequencer { + //if let Some(sequencer) = self.arranger.sequencer_mut() { + //handle_keymap(sequencer, e, KEYMAP_SEQUENCER) + //} else { + //Ok(false) + //} + //} else { + //handle_keymap(&mut self.arranger, e, KEYMAP_ARRANGER) + //} + }, + _ => Ok(false), + } } } -}); +} diff --git a/crates/tek_sequencer/src/arranger_view_h.rs b/crates/tek_sequencer/src/arranger_view_h.rs index 923c801c..9609521a 100644 --- a/crates/tek_sequencer/src/arranger_view_h.rs +++ b/crates/tek_sequencer/src/arranger_view_h.rs @@ -4,7 +4,7 @@ pub fn draw (state: &Arranger, buf: &mut Buffer, mut area: Rect) -> Usually Usuall draw(state, buf, area, track_cols.as_slice(), scene_rows.as_slice()) } -/// Draw arranger with number of rows per scene corresponding to duration of scene. +/// Draw arranger with number of rows per scene proportional to duration of scene. pub fn draw_expanded (state: &Arranger, buf: &mut Buffer, area: Rect) -> Usually { let track_cols = track_clip_name_lengths(state.tracks.as_slice()); let scene_rows = scene_ppqs(state.tracks.as_slice(), state.scenes.as_slice()); @@ -30,14 +30,13 @@ pub fn draw ( ) -> Usually { area.height = 2 + (rows[rows.len() - 1].1 / 96) as u16; let offset = 3 + scene_name_max_len(state.scenes.as_ref()) as u16; - let Arranger { focus_sequencer, focused, entered, selected, .. } = *state; let tracks = state.tracks.as_ref(); let scenes = state.scenes.as_ref(); Layered::new() //.add_ref(&FillBg(Color::Rgb(30, 33, 36)))//COLOR_BG1))//bg_lo(state.focused, state.entered))) .add_ref(&ColumnSeparators(offset, cols)) .add_ref(&RowSeparators(rows)) - .add_ref(&CursorFocus(focus_sequencer, focused, entered, selected, offset, cols, rows)) + .add_ref(&CursorFocus(state.selected, offset, cols, rows)) .add_ref(&Split::down() .add_ref(&TracksHeader(offset, cols, tracks)) .add_ref(&SceneRows(offset, cols, rows, tracks, scenes))) @@ -81,12 +80,12 @@ impl<'a> Render for RowSeparators<'a> { } struct CursorFocus<'a>( - bool, bool, bool, ArrangerFocus, u16, &'a [(usize, usize)], &'a [(usize, usize)] + ArrangerFocus, u16, &'a [(usize, usize)], &'a [(usize, usize)] ); impl<'a> Render for CursorFocus<'a> { fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { - let Self(focus_sequencer, focused, entered, selected, offset, cols, rows) = *self; + let Self(selected, offset, cols, rows) = *self; let get_track_area = |t: usize| Rect { x: offset + area.x + cols[t].1 as u16 - 1, y: area.y, @@ -109,11 +108,9 @@ impl<'a> Render for CursorFocus<'a> { let mut scene_area: Option = None; let mut clip_area: Option = None; let area = match selected { - ArrangerFocus::Mix => if focused && entered && selected == ArrangerFocus::Mix { + ArrangerFocus::Mix => { fill_bg(buf, area, COLOR_BG0); area - } else { - area }, ArrangerFocus::Track(t) => { track_area = Some(get_track_area(t)); @@ -145,9 +142,6 @@ impl<'a> Render for CursorFocus<'a> { } else if let Some(scene_area) = scene_area { fill_bg(buf, scene_area, COLOR_BG0); } - if !focus_sequencer { - Corners(Style::default().green().not_dim()).draw(buf, area)?; - } Ok(area) } } @@ -212,7 +206,7 @@ impl<'a> Render for SceneRow<'a> { let playing = scene.is_playing(tracks); (if playing { "▶" } else { " " }).blit(buf, x, y, None)?; scene.name.read().unwrap().blit(buf, x + 1, y, Some(Style::default().white()))?; - fill_bg(buf, Rect { x: x, y, width: offset.saturating_sub(1), height: 2 }, COLOR_BG1); + fill_bg(buf, Rect { x: x, y, width: offset.saturating_sub(1), height: area.height }, COLOR_BG1); for (track, (w, x)) in track_cols.iter().enumerate() { let x = *x as u16 + offset;