From 0b71a57eadf89096017010154c0a8e71062e69b1 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Fri, 18 Oct 2024 15:55:45 +0300 Subject: [PATCH] remove VerticalArrangerGrid/Cursor --- crates/tek_sequencer/src/arranger.rs | 72 ++++++-- crates/tek_sequencer/src/arranger_cmd.rs | 106 +++++------- crates/tek_sequencer/src/arranger_tui.rs | 211 ++++++++++------------- shell.nix | 12 +- 4 files changed, 196 insertions(+), 205 deletions(-) diff --git a/crates/tek_sequencer/src/arranger.rs b/crates/tek_sequencer/src/arranger.rs index 7ab21603..2b7553d2 100644 --- a/crates/tek_sequencer/src/arranger.rs +++ b/crates/tek_sequencer/src/arranger.rs @@ -62,7 +62,7 @@ pub struct Scene { #[derive(PartialEq, Clone, Copy)] /// Represents the current user selection in the arranger pub enum ArrangementFocus { - /** The whole mix is selected */ + /// The whole mix is selected Mix, /// A track is selected. Track(usize), @@ -77,19 +77,27 @@ pub enum ArrangementViewMode { Horizontal, Vertical(usize), } -/// A collection of phrases to play on each track. +/// Arrangement, rendered vertically (session/grid mode). pub struct VerticalArranger<'a, E: Engine>( pub &'a Arrangement, pub usize ); -pub struct VerticalArrangerGrid<'a>( - pub u16, pub &'a [(usize, usize)], pub &'a [(usize, usize)] -); -pub struct VerticalArrangerCursor<'a>( - pub bool, pub ArrangementFocus, pub u16, pub &'a [(usize, usize)], pub &'a [(usize, usize)], -); +/// Arrangement, rendered horizontally (arrangement/track mode). pub struct HorizontalArranger<'a, E: Engine>( pub &'a Arrangement ); +impl Arranger { + pub fn edit_phrase (&mut self) { + self.editor.phrase = self.arrangement.phrase().clone(); + self.focus(ArrangerFocus::PhraseEditor); + } + pub fn toggle_play (&mut self) -> Perhaps { + match self.transport { + Some(ref mut transport) => { transport.write().unwrap().toggle_play()?; }, + None => { return Ok(None) } + } + Ok(Some(true)) + } +} /// Focus layout of arranger app impl FocusGrid for Arranger { fn cursor (&self) -> (usize, usize) { self.focus_cursor } @@ -175,13 +183,53 @@ impl Arrangement { pub fn is_last_row (&self) -> bool { let selected = self.selected; (self.scenes.len() == 0 && (selected.is_mix() || selected.is_track())) || match selected { - ArrangementFocus::Scene(s) => - s == self.scenes.len() - 1, - ArrangementFocus::Clip(_, s) => - s == self.scenes.len() - 1, + ArrangementFocus::Scene(s) => s == self.scenes.len() - 1, + ArrangementFocus::Clip(_, s) => s == self.scenes.len() - 1, _ => false } } + pub fn toggle_loop (&mut self) { + if let Some(phrase) = self.phrase() { + phrase.write().unwrap().toggle_loop() + } + } + pub fn go_up (&mut self) { + match self.mode { + ArrangementViewMode::Horizontal => self.track_prev(), + _ => self.scene_prev(), + }; + self.show_phrase(); + } + pub fn go_down (&mut self) { + match self.mode { + ArrangementViewMode::Horizontal => self.track_next(), + _ => self.scene_next(), + }; + self.show_phrase(); + } + pub fn go_left (&mut self) { + match self.mode { + ArrangementViewMode::Horizontal => self.scene_prev(), + _ => self.track_prev(), + }; + self.show_phrase(); + } + pub fn go_right (&mut self) { + match self.mode { + ArrangementViewMode::Horizontal => self.scene_next(), + _ => self.track_next(), + }; + self.show_phrase(); + } + pub fn move_back (&mut self) { + todo!("arrangement: move back") + } + pub fn move_forward (&mut self) { + todo!("arrangement: move forward") + } + pub fn rename_selected (&mut self) { + todo!("arrangement: rename selected") + } } /// Methods for tracks in arrangement impl Arrangement { diff --git a/crates/tek_sequencer/src/arranger_cmd.rs b/crates/tek_sequencer/src/arranger_cmd.rs index b63ef457..39339787 100644 --- a/crates/tek_sequencer/src/arranger_cmd.rs +++ b/crates/tek_sequencer/src/arranger_cmd.rs @@ -2,51 +2,51 @@ use crate::*; /// Handle top-level events in standalone arranger. impl Handle for Arranger { fn handle (&mut self, from: &TuiInput) -> Perhaps { - if !match self.focused() { - ArrangerFocus::Transport => self.transport.handle(from)?, - ArrangerFocus::PhrasePool => self.phrases.handle(from)?, - ArrangerFocus::PhraseEditor => self.editor.handle(from)?, - ArrangerFocus::Arrangement => { - let mut handle_phrase = ||{ - let result = self.phrases.handle(from); - self.arrangement.phrase_put(); - result - }; - match from.event() { - key!(KeyCode::Char('a')) => handle_phrase()?, - key!(KeyCode::Char('c')) => handle_phrase()?, - key!(KeyCode::Char('i')) => handle_phrase()?, - key!(KeyCode::Char('d')) => handle_phrase()?, - _ => self.arrangement.handle(from)? - } - }, - }.unwrap_or(false) { + if !self.handle_focused(from)?.unwrap_or(false) { match from.event() { - // Tab navigation key!(KeyCode::Tab) => { self.focus_next(); }, key!(Shift-KeyCode::Tab) => { self.focus_prev(); }, key!(KeyCode::BackTab) => { self.focus_prev(); }, key!(Shift-KeyCode::BackTab) => { self.focus_prev(); }, - // Directional navigation key!(KeyCode::Up) => { self.focus_up(); }, key!(KeyCode::Down) => { self.focus_down(); }, key!(KeyCode::Left) => { self.focus_left(); }, key!(KeyCode::Right) => { self.focus_right(); }, - key!(KeyCode::Char('e')) => { - self.editor.phrase = self.arrangement.phrase().clone(); - self.focus(ArrangerFocus::PhraseEditor); - } - // Global play/pause binding - key!(KeyCode::Char(' ')) => match self.transport { - Some(ref mut transport) => { transport.write().unwrap().toggle_play()?; }, - None => { return Ok(None) } - }, - _ => {} + key!(KeyCode::Char('e')) => { self.edit_phrase(); }, + key!(KeyCode::Char(' ')) => { self.toggle_play(); }, + _ => return Ok(None) } }; Ok(Some(true)) } } +impl Arranger { + /// Helper for event passthru to focused component + fn handle_focused (&mut self, from: &TuiInput) -> Perhaps { + match self.focused() { + ArrangerFocus::Transport => self.transport.handle(from), + ArrangerFocus::PhrasePool => self.phrases.handle(from), + ArrangerFocus::PhraseEditor => self.editor.handle(from), + ArrangerFocus::Arrangement => self.handle_arrangement(from), + } + } + /// Helper for phrase event passthru when arrangement is focused + fn handle_arrangement (&mut self, from: &TuiInput) -> Perhaps { + let mut handle_phrase = ||{ + let result = self.phrases.handle(from); + self.arrangement.phrase_put(); + result + }; + match from.event() { + key!(KeyCode::Char('a')) => handle_phrase(), + key!(KeyCode::Char('c')) => handle_phrase(), + key!(KeyCode::Char('i')) => handle_phrase(), + key!(KeyCode::Char('d')) => handle_phrase(), + _ => self.arrangement.handle(from) + } + } +} +/// Handle events for arrangement. impl Handle for Arrangement { fn handle (&mut self, from: &TuiInput) -> Perhaps { match from.event() { @@ -55,46 +55,18 @@ impl Handle for Arrangement { key!(KeyCode::Enter) => { self.activate(); }, key!(KeyCode::Char('.')) => { self.increment(); }, key!(KeyCode::Char(',')) => { self.decrement(); }, - //key!(KeyCode::Char('<')) => { self.move_back(); }, - //key!(KeyCode::Char('>')) => { self.move_forward(); }, - // Put selected phrase at position + key!(KeyCode::Char('<')) => { self.move_back(); }, + key!(KeyCode::Char('>')) => { self.move_forward(); }, key!(KeyCode::Char('s')) => { self.phrase_put(); }, - // Select phrase at current position key!(KeyCode::Char('g')) => { self.phrase_get(); }, key!(Ctrl-KeyCode::Char('a')) => { self.scene_add(None, None)?; }, key!(Ctrl-KeyCode::Char('t')) => { self.track_add(None, None)?; }, - key!(KeyCode::Char('n')) => { todo!("rename selected"); }, - key!(KeyCode::Char('l')) => if let Some(phrase) = self.phrase() { - phrase.write().unwrap().toggle_loop() - }, - key!(KeyCode::Up) => { - match self.mode { - ArrangementViewMode::Horizontal => self.track_prev(), - _ => self.scene_prev(), - }; - self.show_phrase(); - }, - key!(KeyCode::Down) => { - match self.mode { - ArrangementViewMode::Horizontal => self.track_next(), - _ => self.scene_next(), - }; - self.show_phrase(); - }, - key!(KeyCode::Left) => { - match self.mode { - ArrangementViewMode::Horizontal => self.scene_prev(), - _ => self.track_prev(), - }; - self.show_phrase(); - }, - key!(KeyCode::Right) => { - match self.mode { - ArrangementViewMode::Horizontal => self.scene_next(), - _ => self.track_next(), - }; - self.show_phrase(); - }, + key!(KeyCode::Char('n')) => { self.rename_selected(); }, + key!(KeyCode::Char('l')) => { self.toggle_loop() }, + key!(KeyCode::Up) => { self.go_up() }, + key!(KeyCode::Down) => { self.go_down() }, + key!(KeyCode::Left) => { self.go_left() }, + key!(KeyCode::Right) => { self.go_right() }, _ => return Ok(None) } Ok(Some(true)) diff --git a/crates/tek_sequencer/src/arranger_tui.rs b/crates/tek_sequencer/src/arranger_tui.rs index bec9f81c..1e0cb564 100644 --- a/crates/tek_sequencer/src/arranger_tui.rs +++ b/crates/tek_sequencer/src/arranger_tui.rs @@ -79,13 +79,98 @@ impl<'a> Content for VerticalArranger<'a, Tui> { //let height = rows.last().map(|(w,y)|(y+w)/PPQ).unwrap_or(16); let tracks: &[ArrangementTrack] = state.tracks.as_ref(); let scenes: &[Scene] = state.scenes.as_ref(); - let offset = 3 + Scene::longest_name(scenes) as u16; - let content = Layers::new(move |add|{ + let offset = 3 + Scene::longest_name(scenes) as u16; + let title_fg = if self.0.focused{Color::Rgb(150, 160, 90)}else{Color::Rgb(120, 130, 100)}; + let border_fg = if self.0.focused{Color::Rgb(100, 110, 40)}else{Color::Rgb( 70, 80, 50)}; + let border_bg = Color::Rgb(40, 50, 30); + let border = Lozenge(Style::default().bg(border_bg).fg(border_fg)); + let content = Layers::new(move |add|{ let rows: &[(usize, usize)] = rows.as_ref(); let cols: &[(usize, usize)] = cols.as_ref(); + add(&CustomWidget::new(|_|Ok(Some([0,0])), move|to: &mut TuiOutput|{ + let area = to.area(); + let style = Some(Style::default().fg(COLOR_SEPARATOR)); + for (_, x) in cols.iter() { + let x = offset + area.x() + *x as u16 - 1; + for y in area.y()..area.y2() { + to.blit(&"▎", x, y, style); + } + } + for (_, y) in rows.iter() { + let y = area.y() + (*y / PPQ) as u16 + 1; + if y >= to.buffer.area.height { + break + } + for x in area.x()..area.x2().saturating_sub(2) { + if x < to.buffer.area.x && y < to.buffer.area.y { + let cell = to.buffer.get_mut(x, y); + cell.modifier = Modifier::UNDERLINED; + cell.underline_color = COLOR_SEPARATOR; + } + } + } + Ok(()) + }))?; + + add(&CustomWidget::new(|_|Ok(Some([0,0])), move|to: &mut TuiOutput|{ + let area = to.area(); + let focused = state.focused; + let selected = state.selected; + let get_track_area = |t: usize| [ + offset + area.x() + cols[t].1 as u16 - 1, area.y(), + cols[t].0 as u16, area.h(), + ]; + let get_scene_area = |s: usize| [ + area.x(), 2 + area.y() + (rows[s].1 / PPQ) as u16, + area.w(), (rows[s].0 / PPQ) as u16 + ]; + let get_clip_area = |t: usize, s: usize| [ + offset+area.x()+cols[t].1 as u16 - 1, 2+area.y()+(rows[s].1/PPQ) as u16, + cols[t].0 as u16, (rows[s].0 / PPQ) as u16 + ]; + let mut track_area: Option<[u16;4]> = None; + let mut scene_area: Option<[u16;4]> = None; + let mut clip_area: Option<[u16;4]> = None; + let area = match selected { + ArrangementFocus::Mix => { + if focused { to.fill_bg(area, Color::Rgb(40, 50, 30)); } + area + }, + ArrangementFocus::Track(t) => { track_area = Some(get_track_area(t)); area }, + ArrangementFocus::Scene(s) => { scene_area = Some(get_scene_area(s)); area }, + ArrangementFocus::Clip(t, s) => { + track_area = Some(get_track_area(t)); + scene_area = Some(get_scene_area(s)); + clip_area = Some(get_clip_area(t, s)); + area + }, + }; + if let Some([x, y, width, height]) = track_area { + to.fill_fg([x, y, 1, height], Color::Rgb(70, 80, 50)); + to.fill_fg([x + width, y, 1, height], Color::Rgb(70, 80, 50)); + } + if let Some([_, y, _, height]) = scene_area { + to.fill_ul([area.x(), y - 1, area.w(), 1], Color::Rgb(70, 80, 50)); + to.fill_ul([area.x(), y + height - 1, area.w(), 1], Color::Rgb(70, 80, 50)); + } + if focused { + if let Some(clip_area) = clip_area { + to.render_in(clip_area, &CORNERS)?; + to.fill_bg(clip_area, Color::Rgb(40, 50, 30)); + } else if let Some(track_area) = track_area { + to.render_in(track_area.clip_h(2), &CORNERS)?; + to.fill_bg(track_area, Color::Rgb(40, 50, 30)); + } else if let Some(scene_area) = scene_area { + to.render_in(scene_area.clip_w(offset-1), &CORNERS)?; + to.fill_bg(scene_area, Color::Rgb(40, 50, 30)); + } + } + Ok(()) + }))?; + let track_titles = row!((track, (w, _)) in tracks.iter().zip(cols) => - (&format!(" {}", &track.name.read().unwrap()).as_str() as &dyn Widget) + (&format!("▎{}", &track.name.read().unwrap()).as_str() as &dyn Widget) .min_xy(*w as u16, 2) .bg(track.color) .push_x(offset - 1)); @@ -126,123 +211,9 @@ impl<'a> Content for VerticalArranger<'a, Tui> { }).fixed_y(height) }); - add(&VerticalArrangerGrid(offset, &rows, &cols))?; - add(&VerticalArrangerCursor(state.focused, state.selected, offset, &cols, &rows))?; - add(&col!(track_titles, tracks_clips))?; - Ok(()) - }) - .bg(Color::Rgb(28, 35, 25)) - .border(Lozenge(Style::default() - .bg(Color::Rgb(40, 50, 30)) - .fg(if self.0.focused { - Color::Rgb(100, 110, 40) - } else { - Color::Rgb(70, 80, 50) - }))); - lay!(content, TuiStyle::fg("Session", if self.0.focused { - Color::Rgb(150, 160, 90) - } else { - Color::Rgb(120, 130, 100) - }).push_x(1)) - } -} -impl<'a> Widget for VerticalArrangerGrid<'a> { - type Engine = Tui; - fn render (&self, to: &mut TuiOutput) -> Usually<()> { - let area = to.area(); - let Self(offset, rows, cols) = self; - let style = Some(Style::default().fg(COLOR_SEPARATOR)); - for (_, x) in cols.iter() { - let x = offset + area.x() + *x as u16 - 1; - for y in area.y()..area.y2() { - to.blit(&"▎", x, y, style); - } - } - for (_, y) in rows.iter() { - let y = area.y() + (*y / PPQ) as u16 + 1; - if y >= to.buffer.area.height { - break - } - for x in area.x()..area.x2().saturating_sub(2) { - if x < to.buffer.area.x && y < to.buffer.area.y { - let cell = to.buffer.get_mut(x, y); - cell.modifier = Modifier::UNDERLINED; - cell.underline_color = COLOR_SEPARATOR; - } - } - } - Ok(()) - } -} -impl<'a> Widget for VerticalArrangerCursor<'a> { - type Engine = Tui; - fn render (&self, to: &mut TuiOutput) -> Usually<()> { - let area = to.area(); - let Self(focused, selected, offset, cols, rows) = *self; - let get_track_area = |t: usize| [ - offset + area.x() + cols[t].1 as u16 - 1, - area.y(), - cols[t].0 as u16, - area.h() - ]; - let get_scene_area = |s: usize| [ - area.x(), - 2 + area.y() + (rows[s].1 / PPQ) as u16, - area.w(), - (rows[s].0 / PPQ) as u16 - ]; - let get_clip_area = |t: usize, s: usize| [ - offset + area.x() + cols[t].1 as u16 - 1, - 2 + area.y() + (rows[s].1 / PPQ) as u16, - cols[t].0 as u16, - (rows[s].0 / PPQ) as u16 - ]; - let mut track_area: Option<[u16;4]> = None; - let mut scene_area: Option<[u16;4]> = None; - let mut clip_area: Option<[u16;4]> = None; - let area = match selected { - ArrangementFocus::Mix => { - if focused { - to.fill_bg(area, Color::Rgb(40, 50, 30)); - } - area - }, - ArrangementFocus::Track(t) => { - track_area = Some(get_track_area(t)); - area - }, - ArrangementFocus::Scene(s) => { - scene_area = Some(get_scene_area(s)); - area - }, - ArrangementFocus::Clip(t, s) => { - track_area = Some(get_track_area(t)); - scene_area = Some(get_scene_area(s)); - clip_area = Some(get_clip_area(t, s)); - area - }, - }; - if let Some([x, y, width, height]) = track_area { - to.fill_fg([x, y, 1, height], Color::Rgb(70, 80, 50)); - to.fill_fg([x + width, y, 1, height], Color::Rgb(70, 80, 50)); - } - if let Some([_, y, _, height]) = scene_area { - to.fill_ul([area.x(), y - 1, area.w(), 1], Color::Rgb(70, 80, 50)); - to.fill_ul([area.x(), y + height - 1, area.w(), 1], Color::Rgb(70, 80, 50)); - } - if focused { - if let Some(clip_area) = clip_area { - to.render_in(clip_area, &CORNERS)?; - to.fill_bg(clip_area, Color::Rgb(40, 50, 30)); - } else if let Some(track_area) = track_area { - to.render_in(track_area.clip_h(2), &CORNERS)?; - to.fill_bg(track_area, Color::Rgb(40, 50, 30)); - } else if let Some(scene_area) = scene_area { - to.render_in(scene_area.clip_w(offset-1), &CORNERS)?; - to.fill_bg(scene_area, Color::Rgb(40, 50, 30)); - } - } - Ok(()) + add(&col!(track_titles, tracks_clips)) + }).bg(Color::Rgb(28, 35, 25)).border(border); + lay!(content, TuiStyle::fg("Session", title_fg).push_x(1)) } } impl<'a> Content for HorizontalArranger<'a, Tui> { diff --git a/shell.nix b/shell.nix index 3cf17315..cd5d101c 100755 --- a/shell.nix +++ b/shell.nix @@ -1,23 +1,23 @@ #!/usr/bin/env nix-shell {pkgs?import{}}:let - suil = pkgs.enableDebugging (pkgs.suil.overrideAttrs (a: b: { - dontStrip = true; separateDebugInfo = true; - })); + #suil = pkgs.enableDebugging (pkgs.suil.overrideAttrs (a: b: { + #dontStrip = true; separateDebugInfo = true; + #})); in pkgs.mkShell { nativeBuildInputs = with pkgs; [ pkg-config freetype libclang - bear + #bear ]; buildInputs = with pkgs; [ jack2 lilv serd libclang - suil + #suil glib gtk3 ]; @@ -33,7 +33,7 @@ in pkgs.mkShell { xorg.libXcursor xorg.libXi libxkbcommon - suil + #suil # for Helm: alsa-lib curl