From 2c8f4857ddbc934e8a1fe78cedecb65df9413263 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Thu, 11 Jul 2024 21:32:12 +0300 Subject: [PATCH] reenable inc/dec phrase --- demos/project.edn | 19 +-- src/config.rs | 2 +- src/control/arranger.rs | 2 +- src/control/plugin.rs | 99 +++++++------- src/jack/device.rs | 3 + src/model/track.rs | 2 +- src/view.rs | 28 +++- src/view/arranger.rs | 90 +++++++------ src/view/chain.rs | 286 ++++++++++------------------------------ src/view/layout.rs | 217 ------------------------------ src/view/transport.rs | 54 +++----- 11 files changed, 217 insertions(+), 585 deletions(-) delete mode 100644 src/view/layout.rs diff --git a/demos/project.edn b/demos/project.edn index f21b3940..e47dcb2c 100644 --- a/demos/project.edn +++ b/demos/project.edn @@ -48,19 +48,19 @@ (:14 (44 100))) (phrase { :name "Trapping" :beats 8 :steps 96 } - (:00 (42 100) (36 100) (34 120)) + (:00 (42 100) (36 100) (34 120) (49 100)) (:01 (42 100)) (:02 (42 100)) - (:06 (42 100) (36 100)) - (:06 (42 100) (36 100) (34 100)) + (:06 (42 100) (35 80) (36 80) (49 100)) (:07 (42 100)) (:08 (42 100)) (:12 (42 100)) + (:15 (39 100) (34 100)) (:18 (42 100)) (:24 (42 100) (38 50) (40 50)) (:27 (42 100) (36 50)) - (:30 (42 100) (34 100)) - (:33 (42 100) (36 50)) + (:30 (42 100)) + (:33 (42 100) (36 50) (34 100)) (:36 (42 90)) (:39 (42 80)) (:42 (42 70)) @@ -68,11 +68,12 @@ (:48 (42 100) (36 100) (34 100)) (:50 (42 100)) - (:52 (42 100)) - (:54 (42 100)) - (:56 (42 100)) + (:52 (42 110)) + (:54 (46 50) (42 120)) + (:56 (42 90)) (:58 (42 100)) (:60 (42 100) (35 100)) + (:64 (39 100)) (:66 (42 100) (34 100)) (:70 (42 100)) @@ -81,7 +82,7 @@ (:75 (42 100) (36 50) (34 80)) (:78 (42 100)) (:81 (42 100) (36 50)) - (:84 (42 90) (38 40) (40 50) (34 90)) + (:84 (38 40) (40 50) (34 90)) (:87 (42 90) (35 40)) (:90 (42 70))) diff --git a/src/config.rs b/src/config.rs index 0bab5318..d54b033b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -73,7 +73,7 @@ render!(SetupModal |self, buf, area| { let width = lines[0].0.len() as u16; let x = area.x + (area.width - width) / 2; for (i, (line, style)) in lines.iter().enumerate() { - line.blit(buf, x, area.y + area.height / 2 - (lines.len() / 2) as u16 + i as u16, Some(*style)); + line.blit(buf, x, area.y + area.height / 2 - (lines.len() / 2) as u16 + i as u16, Some(*style))?; } Ok(area) }); diff --git a/src/control/arranger.rs b/src/control/arranger.rs index 08f7caa4..592e5998 100644 --- a/src/control/arranger.rs +++ b/src/control/arranger.rs @@ -2,7 +2,7 @@ use crate::{core::*, model::App}; pub const KEYMAP_ARRANGER: &'static [KeyBinding] = keymap!(App { [Char('`'), NONE, "arranger_mode_switch", "switch the display mode", |app: &mut App| { - app.arranger_mode = !app.seq_mode; + app.arranger_mode = !app.arranger_mode; Ok(true) }], [Up, NONE, "arranger_cursor_up", "move cursor up", |app: &mut App| Ok( diff --git a/src/control/plugin.rs b/src/control/plugin.rs index 4fbbdde4..5f9adafa 100644 --- a/src/control/plugin.rs +++ b/src/control/plugin.rs @@ -5,59 +5,50 @@ pub fn handle (state: &mut Plugin, event: &AppEvent) -> Usually { } pub const KEYMAP: &'static [KeyBinding] = keymap!(Plugin { - [Up, NONE, "cursor_up", "move cursor up", cursor_up], - [Down, NONE, "cursor_down", "move cursor down", cursor_down], - [Char(','), NONE, "decrement", "decrement value", decrement], - [Char('.'), NONE, "increment", "increment value", increment], -}); - -fn cursor_up (s: &mut Plugin) -> Usually { - if s.selected > 0 { - s.selected = s.selected - 1 - } else { - s.selected = match &s.plugin { + [Up, NONE, "cursor_up", "move cursor up", |s: &mut Plugin|{ + s.selected = s.selected.saturating_sub(1); + Ok(true) + }], + [Down, NONE, "cursor_down", "move cursor down", |s: &mut Plugin|{ + s.selected = (s.selected + 1).min(match &s.plugin { Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1, - _ => 0 + _ => unimplemented!() + }); + Ok(true) + }], + [PageUp, NONE, "cursor_up", "move cursor up", |s: &mut Plugin|{ + s.selected = s.selected.saturating_sub(8); + Ok(true) + }], + [PageDown, NONE, "cursor_down", "move cursor down", |s: &mut Plugin|{ + s.selected = (s.selected + 10).min(match &s.plugin { + Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1, + _ => unimplemented!() + }); + Ok(true) + }], + [Char(','), NONE, "decrement", "decrement value", |s: &mut Plugin|{ + match s.plugin.as_mut() { + Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => { + let index = port_list[s.selected].index; + if let Some(value) = instance.control_input(index) { + instance.set_control_input(index, value - 0.01); + } + }, + _ => {} } - } - Ok(true) -} - -fn cursor_down (s: &mut Plugin) -> Usually { - s.selected = s.selected + 1; - match &s.plugin { - Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => { - if s.selected >= port_list.len() { - s.selected = 0; - } - }, - _ => {} - } - Ok(true) -} - -fn decrement (s: &mut Plugin) -> Usually { - match s.plugin.as_mut() { - Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => { - let index = port_list[s.selected].index; - if let Some(value) = instance.control_input(index) { - instance.set_control_input(index, value - 0.01); - } - }, - _ => {} - } - Ok(true) -} - -fn increment (s: &mut Plugin) -> Usually { - match s.plugin.as_mut() { - Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => { - let index = port_list[s.selected].index; - if let Some(value) = instance.control_input(index) { - instance.set_control_input(index, value + 0.01); - } - }, - _ => {} - } - Ok(true) -} + Ok(true) + }], + [Char('.'), NONE, "increment", "increment value", |s: &mut Plugin|{ + match s.plugin.as_mut() { + Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => { + let index = port_list[s.selected].index; + if let Some(value) = instance.control_input(index) { + instance.set_control_input(index, value + 0.01); + } + }, + _ => {} + } + Ok(true) + }], +}); diff --git a/src/jack/device.rs b/src/jack/device.rs index 22186616..426b5616 100644 --- a/src/jack/device.rs +++ b/src/jack/device.rs @@ -10,6 +10,9 @@ pub struct JackDevice { /// The "real" readable/writable `Port`s are owned by the `state`. pub ports: UnownedJackPorts, } +render!(JackDevice |self, buf, area| { + self.state.read().unwrap().render(buf, area) +}); ports!(JackDevice { audio: { ins: |s|Ok(s.ports.audio_ins.values().collect()), diff --git a/src/model/track.rs b/src/model/track.rs index 7df0deba..e7d75831 100644 --- a/src/model/track.rs +++ b/src/model/track.rs @@ -124,7 +124,7 @@ impl Track { Ok(match self.devices.get(self.devices.len().saturating_sub(1)) { Some(device) => { app.audio_out(0).map(|left|device.connect_audio_out(0, &left)).transpose()?; - app.audio_out(1).map(|right|device.connect_audio_out(0, &right)).transpose()?; + app.audio_out(1).map(|right|device.connect_audio_out(1, &right)).transpose()?; () }, None => () diff --git a/src/view.rs b/src/view.rs index ba8c04fe..963fd197 100644 --- a/src/view.rs +++ b/src/view.rs @@ -1,6 +1,5 @@ pub mod chain; pub mod arranger; -pub mod layout; pub mod sampler; pub mod sequencer; pub mod transport; @@ -8,7 +7,6 @@ pub mod plugin; pub mod border; pub use self::border::*; -pub use self::layout::*; pub use self::transport::TransportView; pub use self::arranger::*; pub use self::chain::ChainView; @@ -22,7 +20,7 @@ render!(App |self, buf, area| { &ArrangerView::new(&self, !self.arranger_mode), &If(self.track_cursor > 0, &Split::right([ &ChainView::vertical(&self), - &self.seq_buf, + &SequencerView::new(&self), ])) ]).render(buf, area)?; if let Some(ref modal) = self.modal { @@ -43,12 +41,28 @@ impl<'a> Render for If<'a> { } } -pub enum Direction { - Down, - Right, +pub trait Modal: Device { + fn handle_with_state (&self, state: &mut T, event: &AppEvent) -> Usually; } -pub struct Split<'a, const N: usize>(pub Direction, pub [&'a (dyn Render + Sync);N]); +#[derive(Copy, Clone)] +pub enum Direction { Down, Right } + +impl Direction { + pub fn split <'a, const N: usize> (&self, items: [&'a (dyn Render + Sync);N]) -> Split<'a, N> { + Split(*self, items) + } + pub fn is_down (&self) -> bool { + match self { Self::Down => true, _ => false } + } + pub fn is_right (&self) -> bool { + match self { Self::Right => true, _ => false } + } +} + +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 { diff --git a/src/view/arranger.rs b/src/view/arranger.rs index d32e47a6..c10aac08 100644 --- a/src/view/arranger.rs +++ b/src/view/arranger.rs @@ -3,12 +3,12 @@ use crate::model::*; use crate::view::*; pub struct ArrangerView<'a> { - pub scenes: &'a[Scene], - pub tracks: &'a[Track], - pub cursor: (usize, usize), - pub focused: bool, - pub entered: bool, - pub vertical: bool, + scenes: &'a[Scene], + tracks: &'a[Track], + cursor: (usize, usize), + focused: bool, + entered: bool, + vertical: bool, } impl<'a> ArrangerView<'a> { @@ -101,9 +101,9 @@ impl<'a> ArrangerView<'a> { bg_color ); if i == 0 { - self.vertical_scenes(buf, x2+1, y2+2, area.height); + self.vertical_scenes(buf, x2+1, y2+2, area.height)?; } else if i < columns.len() { - self.vertical_clips(buf, x2+1, y2+2, area.height, i - 1); + self.vertical_clips(buf, x2+1, y2+2, area.height, i - 1)?; }; let style = Some(highlight(self.focused, focus_column).bold()); if i > 0 { @@ -113,9 +113,9 @@ impl<'a> ArrangerView<'a> { Style::default().green() } else { Style::default().black() - })); + }))?; } - title.blit(buf, x2+2, y2, style); + title.blit(buf, x2+2, y2, style)?; x2 = x2 + w + 3; } fill_bg( @@ -128,12 +128,12 @@ impl<'a> ArrangerView<'a> { bg_color ); if self.focused { - HELP.blit(buf, x + 2, y + height - 1, Some(Style::default().dim())); + HELP.blit(buf, x + 2, y + height - 1, Some(Style::default().dim()))?; } Ok(area) } - fn vertical_scenes (&self, buf: &mut Buffer, x: u16, y: u16, height: u16) -> u16 { + fn vertical_scenes (&self, buf: &mut Buffer, x: u16, y: u16, height: u16) -> Usually { let mut index = 0usize; loop { if index >= self.scenes.len() { @@ -142,24 +142,25 @@ impl<'a> ArrangerView<'a> { if y + index as u16 >= height { break } - self.vertical_scene(buf, x, y + index as u16, index); + self.vertical_scene(buf, x, y + index as u16, index)?; index = index + 1; } - longest_scene_name(&self.scenes) + Ok(longest_scene_name(&self.scenes)) } - fn vertical_scene (&self, buf: &mut Buffer, x: u16, y: u16, index: usize) { + fn vertical_scene (&self, buf: &mut Buffer, x: u16, y: u16, index: usize) -> Usually<()> { if let Some(scene) = self.scenes.get(index) { let style = Some(highlight( self.focused, (0 == self.cursor.0) && (index + 1 == self.cursor.1) ).bold()); - "⯈".blit(buf, x, y, style); - scene.name.blit(buf, x + 2, y, style); + "⯈".blit(buf, x, y, style)?; + scene.name.blit(buf, x + 2, y, style)?; } + Ok(()) } - fn vertical_clips (&self, buf: &mut Buffer, x: u16, y: u16, height: u16, track: usize) -> u16 { + fn vertical_clips (&self, buf: &mut Buffer, x: u16, y: u16, height: u16, track: usize) -> Usually { let mut index = 0; let mut width = 10; loop { @@ -169,14 +170,14 @@ impl<'a> ArrangerView<'a> { if y + index as u16 >= height { break } - width = 10.max(self.vertical_clip(buf, x, y, track, index)); + width = 10.max(self.vertical_clip(buf, x, y, track, index)?); index = index + 1; } - width + Ok(width) } - fn vertical_clip (&self, buf: &mut Buffer, x: u16, y: u16, track: usize, index: usize) -> u16 { - if let Some(scene) = self.scenes.get(index) { + fn vertical_clip (&self, buf: &mut Buffer, x: u16, y: u16, track: usize, index: usize) -> Usually { + Ok(if let Some(scene) = self.scenes.get(index) { let hi = (track + 1 == self.cursor.0) && (index + 1 == self.cursor.1); let style = Some(highlight(self.focused, hi)); @@ -195,11 +196,11 @@ impl<'a> ArrangerView<'a> { } else { format!(" ·········") }; - label.blit(buf, x, y + index, style); + label.blit(buf, x, y + index, style)?; label.len() as u16 } else { 0u16 - } + }) } } @@ -217,16 +218,16 @@ impl<'a> ArrangerView<'a> { }); if self.focused && self.entered { //RailV::draw(buf, Rect { x, y, width, height }); - QuarterV(Style::default().green().dim()).draw(buf, Rect { x, y, width, height }); + QuarterV(Style::default().green().dim()).draw(buf, Rect { x, y, width, height })?; //lozenge_left(buf, x, y, height, style); //lozenge_right(buf, x + width - 1, y, height, style); } let mut x2 = 0; - self.horizontal_tracks(buf, area, &mut x2); + self.horizontal_tracks(buf, area, &mut x2)?; x2 = x2 + 1; - self.horizontal_scenes(buf, area, &mut x2); + self.horizontal_scenes(buf, area, &mut x2)?; if self.focused { - HELP.blit(buf, x + 2, y + height - 1, Some(Style::default().dim())); + HELP.blit(buf, x + 2, y + height - 1, Some(Style::default().dim()))?; } Ok(area) } @@ -235,51 +236,58 @@ impl<'a> ArrangerView<'a> { let h = (2 + self.tracks.len()) as u16; (w, h) } - fn horizontal_tracks (&self, buf: &mut Buffer, area: Rect, x2: &mut u16) { + fn horizontal_tracks (&self, buf: &mut Buffer, area: Rect, x2: &mut u16) -> Usually<()> { let style = Some(Style::default().bold().not_dim().white()); let Rect { x, y, height, .. } = area; - "Mix".blit(buf, x + 1, y, style); + "Mix".blit(buf, x + 1, y, style)?; let style = Some(Style::default().bold()); for (i, track) in self.tracks.iter().enumerate() { let y2 = y + 1 + i as u16 * 2; + if self.cursor.0 > 0 { + if i == self.cursor.0 - 1 { + ">".blit(buf, x, y2, style)?; + } + } let label = format!(" {:8}", &track.name); - label.blit(buf, x + 1, y2, style); - "RDM".blit(buf, x + 10, y2, Some(Style::default().dim())); - " 0.0dB".blit(buf, x + 15, y2, Some(Style::default().dim())); + label.blit(buf, x + 1, y2, style)?; + "RDM".blit(buf, x + 10, y2, Some(Style::default().dim()))?; + " 0.0dB".blit(buf, x + 15, y2, Some(Style::default().dim()))?; *x2 = (*x2).max(label.len() as u16 + 13); } - "╷".blit(buf, x + *x2, y, style); + "╷".blit(buf, x + *x2, y, style)?; for y in y+1..y+height-1 { - "│".blit(buf, x + *x2, y, style); + "│".blit(buf, x + *x2, y, style)?; } //"╵".blit(buf, x + x2, y+height-1, style); + Ok(()) } - fn horizontal_scenes (&self, buf: &mut Buffer, area: Rect, x2: &mut u16) { + fn horizontal_scenes (&self, buf: &mut Buffer, area: Rect, x2: &mut u16) -> Usually<()> { let Rect { x, y, height, .. } = area; let bold = Some(Style::default().bold()); let sep = Some(Style::default().dim()); for scene in self.scenes.iter() { let mut x3 = scene.name.len() as u16; - scene.name.blit(buf, x + *x2, y, bold); + scene.name.blit(buf, x + *x2, y, bold)?; for (i, clip) in scene.clips.iter().enumerate() { if let Some(clip) = clip { if let Some(phrase) = self.tracks[i].phrases.get(*clip) { let y2 = y + 1 + i as u16 * 2; let label = format!("{}", &phrase.name); - label.blit(buf, x + *x2, y2, None); + label.blit(buf, x + *x2, y2, None)?; x3 = x3.max(label.len() as u16) } } } *x2 = *x2 + x3; - "╷".blit(buf, x + *x2, y, sep); + "╷".blit(buf, x + *x2, y, sep)?; for y in y+1..y+height-1 { - "┊".blit(buf, x + *x2, y, sep); + "┊".blit(buf, x + *x2, y, sep)?; } - "╵".blit(buf, x + *x2, y+height-1, sep); + "╵".blit(buf, x + *x2, y+height-1, sep)?; *x2 = *x2 + 1; } + Ok(()) } } diff --git a/src/view/chain.rs b/src/view/chain.rs index 6273f330..e1b0221f 100644 --- a/src/view/chain.rs +++ b/src/view/chain.rs @@ -1,23 +1,23 @@ use crate::core::*; -use crate::view::*; use crate::model::*; +use crate::view::{*, Direction}; pub struct ChainView<'a> { - pub focused: bool, - pub track: Option<&'a Track>, - pub vertical: bool, + pub track: Option<&'a Track>, + pub direction: Direction, + pub focused: bool, } impl<'a> ChainView<'a> { pub fn horizontal (app: &'a App) -> Self { - Self::new(app, false) + Self::new(app, Direction::Right) } pub fn vertical (app: &'a App) -> Self { - Self::new(app, true) + Self::new(app, Direction::Down) } - pub fn new (app: &'a App, vertical: bool) -> Self { + pub fn new (app: &'a App, direction: Direction) -> Self { Self { - vertical, + direction, focused: app.section == AppSection::Chain, track: match app.track_cursor { 0 => None, @@ -28,220 +28,66 @@ impl<'a> ChainView<'a> { } impl<'a> Render for ChainView<'a> { - fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { - let style = Some(if self.focused { - Style::default().green().dim() - } else { - Style::default().dim() - }); - let Rect { x, y, width, height } = area; - if self.track.is_none() { - let label = "No track selected"; - label.blit( - buf, - x + (width - label.len() as u16) / 2, - y + height / 2, - Some(Style::default().dim().bold()) - ); - } - fill_bg(buf, area, if self.focused { - Color::Rgb(20, 45, 5) - } else { - Color::Reset - }); - //lozenge_left(buf, x, y, height, style); - let (area, _plugins) = if self.track.is_some() { - if self.vertical { - self.draw_as_column(buf, area, style)? - } else { - self.draw_as_row(buf, area, style)? + fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually { + if let Some(track) = self.track { + match self.direction { + Direction::Down => area.width = area.width.min(40), + Direction::Right => area.width = area.width.min(10), } + fill_bg(buf, area, if self.focused { Color::Rgb(20, 45, 5) } else { Color::Reset }); + self.direction + .split_focus(0, track.devices.as_slice(), if self.focused { + Style::default().green().dim() + } else { + Style::default().dim() + }) + .render(buf, area) } else { - (area, vec![]) - }; - //lozenge_right(buf, x + width - 1, y, height, style); + let Rect { x, y, width, height } = area; + let label = "No track selected"; + let x = x + (width - label.len() as u16) / 2; + let y = y + height / 2; + label.blit(buf, x, y, Some(Style::default().dim().bold()))?; + Ok(area) + } + } +} + +type Renderables<'a> = &'a [JackDevice]; + +impl<'a> Direction { + fn split_focus (&self, index: usize, items: Renderables<'a>, style: Style) -> SplitFocus<'a> { + SplitFocus(*self, index, items, style) + } +} + +pub struct SplitFocus<'a>(pub Direction, pub usize, pub Renderables<'a>, pub Style); + +impl<'a> Render for SplitFocus<'a> { + fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { + let Rect { mut x, mut y, mut width, mut height } = area; + let mut results = vec![]; + for item in self.2.iter() { + if width == 0 || height == 0 { + break + } + let result = item.render(buf, Rect { x, y, width, height })?; + results.push(result); + 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); + }, + } + } + results + .get(self.1) + .map(|focused|Lozenge(self.3).draw(buf, *focused)) + .transpose()?; Ok(area) } } - -impl<'a> ChainView<'a> { - pub fn draw_as_row ( - &self, buf: &mut Buffer, area: Rect, style: Option