From 876d26e287b24f226d9d4c6b34df8b7f9751e91f Mon Sep 17 00:00:00 2001 From: unspeaker Date: Tue, 9 Jul 2024 13:30:08 +0300 Subject: [PATCH] modularize keymaps --- src/control.rs | 200 ++++++--------------------------------- src/control/arranger.rs | 82 ++++++++++++++++ src/control/chain.rs | 31 ++++++ src/control/sequencer.rs | 37 ++++++++ src/core.rs | 7 +- src/model/sampler.rs | 2 - src/view.rs | 99 ++++++++++--------- src/view/split.rs | 52 ---------- 8 files changed, 235 insertions(+), 275 deletions(-) create mode 100644 src/control/arranger.rs create mode 100644 src/control/chain.rs create mode 100644 src/control/sequencer.rs delete mode 100644 src/view/split.rs diff --git a/src/control.rs b/src/control.rs index 3e107813..6d7ba643 100644 --- a/src/control.rs +++ b/src/control.rs @@ -1,34 +1,33 @@ -pub mod mixer; -pub mod plugin; -pub mod sampler; - use crate::{core::*, handle, App, AppSection}; -handle!(App |self, e| { - if let Some(ref mut modal) = self.modal { - if modal.handle(e)? { - self.modal = None; - return Ok(true) - }; +pubmod!{ arranger chain mixer plugin sampler sequencer } + +handle!{ + App |self, e| { + if let Some(ref mut modal) = self.modal { + if modal.handle(e)? { + self.modal = None; + return Ok(true) + }; + } + let handle_focused = |state: &mut Self|handle_keymap( + state, e, match state.section { + AppSection::Arranger => self::arranger::KEYMAP_ARRANGER, + AppSection::Sequencer => self::sequencer::KEYMAP_SEQUENCER, + AppSection::Chain => self::chain::KEYMAP_CHAIN, + } + ); + Ok(if self.entered { + handle_focused(self)? + || handle_keymap(self, e, KEYMAP)? + || handle_keymap(self, e, KEYMAP_FOCUS)? + } else { + handle_keymap(self, e, KEYMAP)? + || handle_keymap(self, e, KEYMAP_FOCUS)? + || handle_focused(self)? + }) } - let handle_focused = |state: &mut Self|match state.section { - AppSection::Arranger => - handle_keymap(state, e, KEYMAP_ARRANGER), - AppSection::Sequencer => - handle_keymap(state, e, KEYMAP_SEQUENCER), - AppSection::Chain => - handle_keymap(state, e, KEYMAP_CHAIN), - }; - Ok(if self.entered { - handle_focused(self)? - || handle_keymap(self, e, KEYMAP)? - || handle_keymap(self, e, KEYMAP_FOCUS)? - } else { - handle_keymap(self, e, KEYMAP)? - || handle_keymap(self, e, KEYMAP_FOCUS)? - || handle_focused(self)? - }) -}); +} const KEYMAP_FOCUS: &'static [KeyBinding] = keymap!(App { [Tab, NONE, "focus_next", "focus next area", focus_next], @@ -142,151 +141,6 @@ const KEYMAP: &'static [KeyBinding] = keymap!(App { // [Char('s'), NONE, "stop_and_rewind", "Stop and rewind", stop_and_rewind], }); -const KEYMAP_ARRANGER: &'static [KeyBinding] = keymap!(App { - [Up, NONE, "arranger_cursor_up", "move cursor up", |app: &mut App| Ok( - match app.arranger_mode { - false => {app.scene_cursor = app.scene_cursor.saturating_sub(1); true}, - true => {app.track_cursor = app.track_cursor.saturating_sub(1); true}, - } - )], - [Down, NONE, "arranger_cursor_down", "move cursor down", |app: &mut App| Ok( - match app.arranger_mode { - false => {app.scene_cursor = app.scenes.len().min(app.scene_cursor + 1); true}, - true => {app.track_cursor = app.tracks.len().min(app.track_cursor + 1); true}, - } - )], - [Left, NONE, "arranger_cursor_left", "move cursor left", |app: &mut App| Ok( - match app.arranger_mode { - false => {app.track_cursor = app.track_cursor.saturating_sub(1); true}, - true => {app.scene_cursor = app.scene_cursor.saturating_sub(1); true}, - } - )], - [Right, NONE, "arranger_cursor_right", "move cursor right", |app: &mut App| Ok( - match app.arranger_mode { - false => {app.track_cursor = app.tracks.len().min(app.track_cursor + 1); true}, - true => {app.scene_cursor = app.scenes.len().min(app.scene_cursor + 1); true}, - } - )], - [Enter, NONE, "arranger_activate", "activate item at cursor", |app: &mut App| Ok( - if app.scene_cursor == 0 { - false - } else { - let scene = &app.scenes[app.scene_cursor - 1]; - if app.track_cursor == 0 { - for (i, track) in app.tracks.iter_mut().enumerate() { - track.sequence = scene.clips[i]; - track.reset = true; - } - } else { - let track = &mut app.tracks[app.track_cursor - 1]; - track.sequence = scene.clips[app.track_cursor - 1]; - track.reset = true; - }; - true - } - )], - [Char('.'), NONE, "arranger_increment", "set next clip at cursor", |app: &mut App| { - //if state.cursor.0 >= 1 && state.cursor.1 >= 1 { - //let scene_id = state.cursor.1 - 1; - //let clip_id = state.cursor.0 - 1; - //let scene = &mut state.scenes[scene_id]; - //scene.clips[clip_id] = match scene.clips[clip_id] { - //None => Some(0), - //Some(i) => if i >= state.tracks[clip_id].sequencer.phrases.len().saturating_sub(1) { - //None - //} else { - //Some(i + 1) - //} - //}; - //} - Ok(true) - }], - [Char(','), NONE, "arranger_decrement", "set previous clip at cursor", |app: &mut App| { - //if state.cursor.0 >= 1 && state.cursor.1 >= 1 { - //let scene_id = state.cursor.1 - 1; - //let clip_id = state.cursor.0 - 1; - //let scene = &mut state.scenes[scene_id]; - //scene.clips[clip_id] = match scene.clips[clip_id] { - //None => Some(state.tracks[clip_id].sequencer.phrases.len().saturating_sub(1)), - //Some(i) => if i == 0 { - //None - //} else { - //Some(i - 1) - //} - //}; - //} - Ok(true) - }], - [Char('`'), NONE, "arranger_mode_switch", "switch the display mode", |app: &mut App| { - app.arranger_mode = !app.seq_mode; - Ok(true) - }], -}); - -const KEYMAP_SEQUENCER: &'static [KeyBinding] = keymap!(App { - [Up, NONE, "seq_cursor_up", "move cursor up", |app: &mut App| { - app.note_cursor = app.note_cursor.saturating_sub(1); - Ok(true) - }], - [Down, NONE, "seq_cursor_down", "move cursor up", |app: &mut App| { - app.note_cursor = app.note_cursor + 1; - Ok(true) - }], - [Left, NONE, "seq_cursor_left", "move cursor up", |app: &mut App| { - if app.entered { - app.time_cursor = app.time_cursor.saturating_sub(1); - } else { - app.time_start = app.time_start.saturating_sub(1); - } - Ok(true) - }], - [Right, NONE, "seq_cursor_right", "move cursor up", |app: &mut App| { - if app.entered { - app.time_cursor = app.time_cursor + 1; - } else { - app.time_start = app.time_start + 1; - } - Ok(true) - }], - [Char('`'), NONE, "seq_mode_switch", "switch the display mode", |app: &mut App| { - app.seq_mode = !app.seq_mode; - Ok(true) - }], -// [Char('a'), NONE, "note_add", "Add note", note_add], -// [Char('z'), NONE, "note_del", "Delete note", note_del], -// [CapsLock, NONE, "advance", "Toggle auto advance", nop], -// [Char('w'), NONE, "rest", "Advance by note duration", nop], -}); - -const KEYMAP_CHAIN: &'static [KeyBinding] = keymap!(App { - [Up, NONE, "chain_cursor_up", "move cursor up", |_: &mut App| { - Ok(true) - }], - [Down, NONE, "chain_cursor_down", "move cursor down", |_: &mut App| { - Ok(true) - }], - [Left, NONE, "chain_cursor_left", "move cursor left", |app: &mut App| { - if let Some((_, track)) = app.track_mut() { - track.device = track.device.saturating_sub(1); - Ok(true) - } else { - Ok(false) - } - }], - [Right, NONE, "chain_cursor_right", "move cursor right", |app: &mut App| { - if let Some((_, track)) = app.track_mut() { - track.device = (track.device + 1).min(track.devices.len().saturating_sub(1)); - Ok(true) - } else { - Ok(false) - } - }], - [Char('`'), NONE, "chain_mode_switch", "switch the display mode", |app: &mut App| { - app.chain_mode = !app.seq_mode; - Ok(true) - }], -}); - fn focus_next (app: &mut App) -> Usually { app.section.next(); Ok(true) diff --git a/src/control/arranger.rs b/src/control/arranger.rs new file mode 100644 index 00000000..659b0422 --- /dev/null +++ b/src/control/arranger.rs @@ -0,0 +1,82 @@ +use crate::{core::*, model::App}; + +pub const KEYMAP_ARRANGER: &'static [KeyBinding] = keymap!(App { + [Up, NONE, "arranger_cursor_up", "move cursor up", |app: &mut App| Ok( + match app.arranger_mode { + false => {app.scene_cursor = app.scene_cursor.saturating_sub(1); true}, + true => {app.track_cursor = app.track_cursor.saturating_sub(1); true}, + } + )], + [Down, NONE, "arranger_cursor_down", "move cursor down", |app: &mut App| Ok( + match app.arranger_mode { + false => {app.scene_cursor = app.scenes.len().min(app.scene_cursor + 1); true}, + true => {app.track_cursor = app.tracks.len().min(app.track_cursor + 1); true}, + } + )], + [Left, NONE, "arranger_cursor_left", "move cursor left", |app: &mut App| Ok( + match app.arranger_mode { + false => {app.track_cursor = app.track_cursor.saturating_sub(1); true}, + true => {app.scene_cursor = app.scene_cursor.saturating_sub(1); true}, + } + )], + [Right, NONE, "arranger_cursor_right", "move cursor right", |app: &mut App| Ok( + match app.arranger_mode { + false => {app.track_cursor = app.tracks.len().min(app.track_cursor + 1); true}, + true => {app.scene_cursor = app.scenes.len().min(app.scene_cursor + 1); true}, + } + )], + [Enter, NONE, "arranger_activate", "activate item at cursor", |app: &mut App| Ok( + if app.scene_cursor == 0 { + false + } else { + let scene = &app.scenes[app.scene_cursor - 1]; + if app.track_cursor == 0 { + for (i, track) in app.tracks.iter_mut().enumerate() { + track.sequence = scene.clips[i]; + track.reset = true; + } + } else { + let track = &mut app.tracks[app.track_cursor - 1]; + track.sequence = scene.clips[app.track_cursor - 1]; + track.reset = true; + }; + true + } + )], + [Char('.'), NONE, "arranger_increment", "set next clip at cursor", |app: &mut App| { + //if state.cursor.0 >= 1 && state.cursor.1 >= 1 { + //let scene_id = state.cursor.1 - 1; + //let clip_id = state.cursor.0 - 1; + //let scene = &mut state.scenes[scene_id]; + //scene.clips[clip_id] = match scene.clips[clip_id] { + //None => Some(0), + //Some(i) => if i >= state.tracks[clip_id].sequencer.phrases.len().saturating_sub(1) { + //None + //} else { + //Some(i + 1) + //} + //}; + //} + Ok(true) + }], + [Char(','), NONE, "arranger_decrement", "set previous clip at cursor", |app: &mut App| { + //if state.cursor.0 >= 1 && state.cursor.1 >= 1 { + //let scene_id = state.cursor.1 - 1; + //let clip_id = state.cursor.0 - 1; + //let scene = &mut state.scenes[scene_id]; + //scene.clips[clip_id] = match scene.clips[clip_id] { + //None => Some(state.tracks[clip_id].sequencer.phrases.len().saturating_sub(1)), + //Some(i) => if i == 0 { + //None + //} else { + //Some(i - 1) + //} + //}; + //} + Ok(true) + }], + [Char('`'), NONE, "arranger_mode_switch", "switch the display mode", |app: &mut App| { + app.arranger_mode = !app.seq_mode; + Ok(true) + }], +}); diff --git a/src/control/chain.rs b/src/control/chain.rs new file mode 100644 index 00000000..280eefb0 --- /dev/null +++ b/src/control/chain.rs @@ -0,0 +1,31 @@ +use crate::{core::*, model::App}; + +pub const KEYMAP_CHAIN: &'static [KeyBinding] = keymap!(App { + [Up, NONE, "chain_cursor_up", "move cursor up", |_: &mut App| { + Ok(true) + }], + [Down, NONE, "chain_cursor_down", "move cursor down", |_: &mut App| { + Ok(true) + }], + [Left, NONE, "chain_cursor_left", "move cursor left", |app: &mut App| { + if let Some((_, track)) = app.track_mut() { + track.device = track.device.saturating_sub(1); + Ok(true) + } else { + Ok(false) + } + }], + [Right, NONE, "chain_cursor_right", "move cursor right", |app: &mut App| { + if let Some((_, track)) = app.track_mut() { + track.device = (track.device + 1).min(track.devices.len().saturating_sub(1)); + Ok(true) + } else { + Ok(false) + } + }], + [Char('`'), NONE, "chain_mode_switch", "switch the display mode", |app: &mut App| { + app.chain_mode = !app.seq_mode; + Ok(true) + }], +}); + diff --git a/src/control/sequencer.rs b/src/control/sequencer.rs new file mode 100644 index 00000000..3fd5d4e5 --- /dev/null +++ b/src/control/sequencer.rs @@ -0,0 +1,37 @@ +use crate::{core::*, model::App}; + +pub const KEYMAP_SEQUENCER: &'static [KeyBinding] = keymap!(App { + [Up, NONE, "seq_cursor_up", "move cursor up", |app: &mut App| { + app.note_cursor = app.note_cursor.saturating_sub(1); + Ok(true) + }], + [Down, NONE, "seq_cursor_down", "move cursor up", |app: &mut App| { + app.note_cursor = app.note_cursor + 1; + Ok(true) + }], + [Left, NONE, "seq_cursor_left", "move cursor up", |app: &mut App| { + if app.entered { + app.time_cursor = app.time_cursor.saturating_sub(1); + } else { + app.time_start = app.time_start.saturating_sub(1); + } + Ok(true) + }], + [Right, NONE, "seq_cursor_right", "move cursor up", |app: &mut App| { + if app.entered { + app.time_cursor = app.time_cursor + 1; + } else { + app.time_start = app.time_start + 1; + } + Ok(true) + }], + [Char('`'), NONE, "seq_mode_switch", "switch the display mode", |app: &mut App| { + app.seq_mode = !app.seq_mode; + Ok(true) + }], +// [Char('a'), NONE, "note_add", "Add note", note_add], +// [Char('z'), NONE, "note_del", "Delete note", note_del], +// [CapsLock, NONE, "advance", "Toggle auto advance", nop], +// [Char('w'), NONE, "rest", "Advance by note duration", nop], +}); + diff --git a/src/core.rs b/src/core.rs index 3d3e0ea0..7c52a65b 100644 --- a/src/core.rs +++ b/src/core.rs @@ -27,6 +27,11 @@ use crossterm::terminal::{ ($($name:ident)*) => { $(mod $name; pub use self::$name::*;)* }; } +/// Define and reexport public modules. +#[macro_export] macro_rules! pubmod { + ($($name:ident)*) => { $(pub mod $name;)* }; +} + submod!( handle midi render time ); /// Standard result type. @@ -55,7 +60,7 @@ pub trait Device: Render + Handle + Process + Send + Sync { impl Device for T {} // Reexport macros: -pub use crate::{submod, render, handle, process, phrase, keymap, key, ports}; +pub use crate::{submod, pubmod, render, handle, process, phrase, keymap, key, ports}; // Reexport JACK proto-lib: pub use crate::jack::*; diff --git a/src/model/sampler.rs b/src/model/sampler.rs index e714855b..d36778b2 100644 --- a/src/model/sampler.rs +++ b/src/model/sampler.rs @@ -166,8 +166,6 @@ pub struct Voice { pub velocity: f32, } -const BUFFER: [f32;64] = [0.0f32;64]; - impl Iterator for Voice { type Item = [f32;2]; fn next (&mut self) -> Option { diff --git a/src/view.rs b/src/view.rs index 9df999d4..128d3681 100644 --- a/src/view.rs +++ b/src/view.rs @@ -6,17 +6,15 @@ pub mod sequencer; pub mod transport; pub mod plugin; pub mod border; -pub mod split; pub use self::border::*; pub use self::layout::*; -pub use self::split::*; pub use self::transport::TransportView; pub use self::arranger::*; pub use self::chain::ChainView; pub use self::sequencer::SequencerView; -use crate::{render, App, AppSection, core::*}; +use crate::{render, App, core::*}; render!(App |self, buf, area| { let track = self.track_cursor; @@ -34,48 +32,55 @@ render!(App |self, buf, area| { modal.render(buf, area)?; } Ok(area) - - //let transport = transport.render(buf, area)?; - //let y = y + transport.height; - - //let arranger = arranger.render(buf, Rect { x, y, width, height })?; - //let style_entered = if self.entered { - //Style::default().green() - //} else { - //Style::default().green().dim() - //}; - //if self.section == AppSection::Arranger { - //QuarterV(style_entered).draw(buf, arranger) - //} - //let y = y + arranger.height; - //if self.track_cursor > 0 { - //let chain = ChainView::new(&self, false); - //let phrase = SequencerView::new(&self); - - //let chain = chain.render(buf, chain_area)?; - //if self.section == AppSection::Chain { - //QuarterV(style_entered).draw(buf, Rect { width, ..chain }) - //} - //let phrase = phrase.render(buf, Rect { - //x, y, width, height: height - height / 3 - //})?; - //if self.section == AppSection::Sequencer { - //QuarterV(style_entered).draw(buf, phrase) - //} - //} else { - //let area = Rect { x, y, width, height }; - //let mut x = area.x; - //for track in self.tracks.iter() { - //let chain = ChainView { - //focused: self.section == AppSection::Chain, - //track: Some(track), - //vertical: true, - //}; - //track.name.blit(buf, x + 1, area.y, Some(Style::default().white().bold())); - //let chain = chain.render(buf, Rect { - //x, y: area.y + 1, width: width, height: height - y - 1 - //})?; - //x = x + chain.width.max(track.name.len() as u16); - //} - //}; }); + +pub struct If<'a>(pub bool, pub &'a (dyn Render + Sync)); + +impl<'a> Render for If<'a> { + fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { + if self.0 { + self.1.render(buf, area) + } else { + ().render(buf, area) + } + } +} + +pub enum Direction { + Down, + Right, +} + +pub struct Split<'a, const N: usize>(pub Direction, pub [&'a (dyn Render + Sync);N]); + +impl<'a, const N: usize> Split<'a, N> { + pub fn down (items: [&'a (dyn Render + Sync);N]) -> Self { + Self(Direction::Down, items) + } + pub fn right (items: [&'a (dyn Render + Sync);N]) -> Self { + Self(Direction::Right, items) + } +} + +impl<'a, const N: usize> Render for Split<'a, N> { + fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { + let Rect { mut x, mut y, mut width, mut height } = area; + for item in self.1 { + if width == 0 || height == 0 { + break + } + let result = item.render(buf, Rect { x, y, width, height })?; + match self.0 { + Direction::Down => { + y = y + result.height; + height = height.saturating_sub(result.height); + }, + Direction::Right => { + x = x + result.width; + width = width.saturating_sub(result.width); + }, + } + } + Ok(area) + } +} diff --git a/src/view/split.rs b/src/view/split.rs deleted file mode 100644 index 911d84d0..00000000 --- a/src/view/split.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::core::*; - -pub struct If<'a>(pub bool, pub &'a (dyn Render + Sync)); - -impl<'a> Render for If<'a> { - fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { - if self.0 { - self.1.render(buf, area) - } else { - ().render(buf, area) - } - } -} - -pub enum Direction { - Down, - Right, -} - -pub struct Split<'a, const N: usize>(pub Direction, pub [&'a (dyn Render + Sync);N]); - -impl<'a, const N: usize> Split<'a, N> { - pub fn down (items: [&'a (dyn Render + Sync);N]) -> Self { - Self(Direction::Down, items) - } - pub fn right (items: [&'a (dyn Render + Sync);N]) -> Self { - Self(Direction::Right, items) - } -} - -impl<'a, const N: usize> Render for Split<'a, N> { - fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { - let Rect { mut x, mut y, mut width, mut height } = area; - for item in self.1 { - if width == 0 || height == 0 { - break - } - let result = item.render(buf, Rect { x, y, width, height })?; - match self.0 { - Direction::Down => { - y = y + result.height; - height = height.saturating_sub(result.height); - }, - Direction::Right => { - x = x + result.width; - width = width.saturating_sub(result.width); - }, - } - } - Ok(area) - } -}