From 0eb063db1c32fd015e39f7508631326cb233db16 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Tue, 8 Oct 2024 18:33:21 +0300 Subject: [PATCH] wip: reenable standalone sequencer --- Justfile | 2 + crates/tek_sequencer/src/arranger.rs | 11 +-- crates/tek_sequencer/src/arranger_cli.rs | 12 +-- crates/tek_sequencer/src/arranger_tui.rs | 30 ++++---- crates/tek_sequencer/src/sequencer.rs | 7 +- crates/tek_sequencer/src/sequencer_cli.rs | 8 +- crates/tek_sequencer/src/sequencer_tui.rs | 92 ++++++++++++++++++++++- 7 files changed, 125 insertions(+), 37 deletions(-) diff --git a/Justfile b/Justfile index 10921cc1..a7f5f7c2 100644 --- a/Justfile +++ b/Justfile @@ -12,3 +12,5 @@ fpush: git push -fu origin main arranger: cargo run --bin tek_arranger +sequencer: + cargo run --bin tek_sequencer diff --git a/crates/tek_sequencer/src/arranger.rs b/crates/tek_sequencer/src/arranger.rs index c6a2d27a..0725325b 100644 --- a/crates/tek_sequencer/src/arranger.rs +++ b/crates/tek_sequencer/src/arranger.rs @@ -457,16 +457,7 @@ impl Scene { } /// Returns the pulse length of the longest phrase in the scene pub fn pulses (&self, tracks: &[ArrangementTrack]) -> usize { - self.clips.iter().enumerate() - .filter_map(|(i, c)|c - .map(|c|tracks - .get(i) - .map(|track|track - .phrases - .get(c)))) - .filter_map(|p|p) - .filter_map(|p|p) - .fold(0, |a, p|a.max(p.read().unwrap().length)) + self.clips.iter().fold(0, |a, p|a.max(p.as_ref().map(|q|q.read().unwrap().length).unwrap_or(0))) } /// Returns true if all phrases in the scene are currently playing pub fn is_playing (&self, tracks: &[ArrangementTrack]) -> bool { diff --git a/crates/tek_sequencer/src/arranger_cli.rs b/crates/tek_sequencer/src/arranger_cli.rs index 3921bc7f..33f8c4cd 100644 --- a/crates/tek_sequencer/src/arranger_cli.rs +++ b/crates/tek_sequencer/src/arranger_cli.rs @@ -36,12 +36,12 @@ impl ArrangerCli { *arrangement.name.write().unwrap() = name.clone(); } for _ in 0..self.tracks { - let track = arrangement.track_add(None)?; - for _ in 0..self.scenes { - track.phrases.push( - Arc::new(RwLock::new(Phrase::new("", true, PPQ * 4, None))) - ); - } + //let track = arrangement.track_add(None)?; + //for _ in 0..self.scenes { + //track.phrases.push( + //Arc::new(RwLock::new(Phrase::new("", true, PPQ * 4, None))) + //); + //} } for _ in 0..self.scenes { let _scene = arrangement.scene_add(None)?; diff --git a/crates/tek_sequencer/src/arranger_tui.rs b/crates/tek_sequencer/src/arranger_tui.rs index fb39d3c4..0b129214 100644 --- a/crates/tek_sequencer/src/arranger_tui.rs +++ b/crates/tek_sequencer/src/arranger_tui.rs @@ -25,6 +25,21 @@ impl Content for Arranger { }) } } +/// Focusable items in standalone arranger. +impl Focus<3, Tui> for Arranger { + fn focus (&self) -> usize { + self.focus + } + fn focus_mut (&mut self) -> &mut usize { + &mut self.focus + } + fn focusable (&self) -> [&dyn Focusable;3] { + focusables!(self.transport, self.arrangement, self.editor) + } + fn focusable_mut (&mut self) -> [&mut dyn Focusable;3] { + focusables_mut!(self.transport, self.arrangement, self.editor) + } +} /// Handle top-level events in standalone arranger. impl Handle for Arranger { fn handle (&mut self, from: &TuiInput) -> Perhaps { @@ -73,21 +88,6 @@ impl Handle for Arranger { Ok(Some(true)) } } -/// Focusable items in standalone arranger. -impl Focus<3, Tui> for Arranger { - fn focus (&self) -> usize { - self.focus - } - fn focus_mut (&mut self) -> &mut usize { - &mut self.focus - } - fn focusable (&self) -> [&dyn Focusable;3] { - focusables!(self.transport, self.arrangement, self.editor) - } - fn focusable_mut (&mut self) -> [&mut dyn Focusable;3] { - focusables_mut!(self.transport, self.arrangement, self.editor) - } -} impl Arranger { pub fn rename_selected (&mut self) { let Arrangement { selected, ref name, ref tracks, ref scenes, .. } = self.arrangement; diff --git a/crates/tek_sequencer/src/sequencer.rs b/crates/tek_sequencer/src/sequencer.rs index 09770c8e..5f5b720c 100644 --- a/crates/tek_sequencer/src/sequencer.rs +++ b/crates/tek_sequencer/src/sequencer.rs @@ -13,12 +13,16 @@ pub struct Sequencer { pub phrases: Arc>>, /// Phrase editor view pub editor: PhraseEditor, + /// Which view is focused + pub focus: usize } /// Contains all phrases in a project pub struct PhrasePool { _engine: PhantomData, /// Phrases in the pool pub phrases: Vec>>>, + /// Whether this widget is focused + pub focused: bool, } /// A MIDI sequence. #[derive(Debug)] @@ -93,7 +97,8 @@ impl PhrasePool { pub fn new () -> Self { Self { _engine: Default::default(), - phrases: vec![Arc::new(RwLock::new(Some(Phrase::default())))] + phrases: vec![Arc::new(RwLock::new(Some(Phrase::default())))], + focused: false } } } diff --git a/crates/tek_sequencer/src/sequencer_cli.rs b/crates/tek_sequencer/src/sequencer_cli.rs index b67c07be..81471ad4 100644 --- a/crates/tek_sequencer/src/sequencer_cli.rs +++ b/crates/tek_sequencer/src/sequencer_cli.rs @@ -19,8 +19,12 @@ pub struct SequencerCli { impl SequencerCli { fn run (&self) -> Usually<()> { let seq = Sequencer { - transport: self.transport.unwrap_or(false) - .then_some(Arc::new(RwLock::new(TransportToolbar::new(None)))), + focus: 0, + editor: PhraseEditor::new(), + phrases: Arc::new(RwLock::new(PhrasePool::new())), + transport: self.transport.unwrap_or(false).then_some( + Arc::new(RwLock::new(TransportToolbar::new(None))) + ), }; if let Some(name) = self.name.as_ref() { // TODO diff --git a/crates/tek_sequencer/src/sequencer_tui.rs b/crates/tek_sequencer/src/sequencer_tui.rs index 94c5d1cf..1ddf9862 100644 --- a/crates/tek_sequencer/src/sequencer_tui.rs +++ b/crates/tek_sequencer/src/sequencer_tui.rs @@ -1,4 +1,72 @@ use crate::*; +impl Content for Sequencer { + type Engine = Tui; + fn content (&self) -> impl Widget { + Stack::down(move|add|{ + add(&self.transport)?; + add(&self.phrases.clone() + .split(Direction::Right, 20, &self.editor as &dyn Widget) + .min_y(20) + .fill_y()) + }) + } +} +/// Focusable items in standalone arranger. +impl Focus<3, Tui> for Sequencer { + fn focus (&self) -> usize { + self.focus + } + fn focus_mut (&mut self) -> &mut usize { + &mut self.focus + } + fn focusable (&self) -> [&dyn Focusable;3] { + focusables!(self.transport, self.phrases, self.editor) + } + fn focusable_mut (&mut self) -> [&mut dyn Focusable;3] { + focusables_mut!(self.transport, self.phrases, self.editor) + } +} +/// Handle top-level events in standalone arranger. +impl Handle for Sequencer { + fn handle (&mut self, from: &TuiInput) -> Perhaps { + let focus = self.focus; + let is_first_row = false; + let is_last_row = false; + match from.event() { + key!(KeyCode::Char(' ')) => { + if let Some(ref mut transport) = self.transport { + transport.write().unwrap().toggle_play()?; + } else { + return Ok(None) + } + }, + key!(KeyCode::Tab) => { + self.focus_next(); + }, + key!(KeyCode::BackTab) => { + self.focus_prev(); + }, + key!(KeyCode::Down) => { + if focus == 0 { + self.focus_next(); + } else if focus == 1 && is_last_row { + self.focus_next(); + } else { + return self.focused_mut().handle(from) + } + }, + key!(KeyCode::Up) => { + if focus == 1 && is_first_row { + self.focus_prev(); + } else { + return self.focused_mut().handle(from) + } + }, + _ => return self.focused_mut().handle(from) + } + Ok(Some(true)) + } +} // TODO: Display phrases always in order of appearance impl Content for PhrasePool { type Engine = Tui; @@ -14,6 +82,24 @@ impl Content for PhrasePool { .fg(Color::Rgb(70, 80, 50)))) } } +impl Focusable for PhrasePool { + fn is_focused (&self) -> bool { + self.focused + } + fn set_focused (&mut self, focused: bool) { + self.focused = focused + } +} +impl Handle for PhrasePool { + fn handle (&mut self, from: &TuiInput) -> Perhaps { + match from.event() { + key!(KeyCode::Up) => todo!(), + key!(KeyCode::Down) => todo!(), + _ => return Ok(None), + } + return Ok(Some(true)) + } +} impl Content for PhraseEditor { type Engine = Tui; fn content (&self) -> impl Widget { @@ -21,7 +107,7 @@ impl Content for PhraseEditor { let toolbar = Stack::down(move|add|{ //let name = format!("{:>9}", self.name.read().unwrap().as_str()); //add(&col!("Track:", TuiStyle::bg(name.as_str(), field_bg)))?; - if let Some(phrase) = self.phrase { + if let Some(phrase) = &self.phrase { let phrase = phrase.read().unwrap(); let length = format!("{}q{}p", phrase.length / PPQ, phrase.length % PPQ); let length = format!("{:>9}", &length); @@ -49,10 +135,10 @@ impl Content for PhraseEditor { }).fill_y(), // playhead CustomWidget::new(|_|Ok(Some([32,2])), |to: &mut TuiOutput|{ - if let Some(phrase) = self.phrase { + if let Some(phrase) = &self.phrase { let time_0 = self.time_axis.start; let time_z = self.time_axis.scale; - let now = self.now % phrase.read().unwrap().length; + let now = 0; // TODO FIXME: self.now % phrase.read().unwrap().length; let [x, y, width, _] = to.area(); let x2 = x as usize + Self::H_KEYS_OFFSET; let x3 = x as usize + width as usize;