From 479988272ed18c36e834959ca43dd5cad8b4179f Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sun, 12 Jan 2025 00:52:42 +0100 Subject: [PATCH] comment out app-specific structs/impls. only monoapp remains --- tek/src/arranger.rs | 100 ++-- tek/src/audio.rs | 196 ++++---- tek/src/control.rs | 664 +++++++++++++------------- tek/src/model.rs | 110 ++--- tek/src/select.rs | 1 - tek/src/view.rs | 1084 +++++++++++++++++++++---------------------- 6 files changed, 1080 insertions(+), 1075 deletions(-) delete mode 100644 tek/src/select.rs diff --git a/tek/src/arranger.rs b/tek/src/arranger.rs index 9963d600..17fbb306 100644 --- a/tek/src/arranger.rs +++ b/tek/src/arranger.rs @@ -1,7 +1,7 @@ use crate::*; use ClockCommand::{Play, Pause}; use self::ArrangerCommand as Cmd; - +pub const TRACK_MIN_WIDTH: usize = 9; impl Arrangement for App { fn tracks (&self) -> &Vec { &self.tracks } fn tracks_mut (&mut self) -> &mut Vec { &mut self.tracks } @@ -10,14 +10,14 @@ impl Arrangement for App { fn selected (&self) -> &ArrangerSelection { &self.selected } fn selected_mut (&mut self) -> &mut ArrangerSelection { &mut self.selected } } -impl Arrangement for Arranger { - fn tracks (&self) -> &Vec { &self.tracks } - fn tracks_mut (&mut self) -> &mut Vec { &mut self.tracks } - fn scenes (&self) -> &Vec { &self.scenes } - fn scenes_mut (&mut self) -> &mut Vec { &mut self.scenes } - fn selected (&self) -> &ArrangerSelection { &self.selected } - fn selected_mut (&mut self) -> &mut ArrangerSelection { &mut self.selected } -} +//impl Arrangement for Arranger { + //fn tracks (&self) -> &Vec { &self.tracks } + //fn tracks_mut (&mut self) -> &mut Vec { &mut self.tracks } + //fn scenes (&self) -> &Vec { &self.scenes } + //fn scenes_mut (&mut self) -> &mut Vec { &mut self.scenes } + //fn selected (&self) -> &ArrangerSelection { &self.selected } + //fn selected_mut (&mut self) -> &mut ArrangerSelection { &mut self.selected } +//} pub trait Arrangement: HasClock + HasJack { fn tracks (&self) -> &Vec; fn tracks_mut (&mut self) -> &mut Vec; @@ -117,6 +117,49 @@ pub trait Arrangement: HasClock + HasJack { } Ok(()) } + fn activate (&mut self) -> Usually<()> { + let selected = self.selected().clone(); + match selected { + ArrangerSelection::Scene(s) => { + let mut clips = vec![]; + for (t, _) in self.tracks().iter().enumerate() { + clips.push(self.scenes()[s].clips[t].clone()); + } + for (t, track) in self.tracks_mut().iter_mut().enumerate() { + if track.player.play_clip.is_some() || clips[t].is_some() { + track.player.enqueue_next(clips[t].as_ref()); + } + } + if self.clock().is_stopped() { + self.clock().play_from(Some(0))?; + } + }, + ArrangerSelection::Clip(t, s) => { + let clip = self.scenes()[s].clips[t].clone(); + self.tracks_mut()[t].player.enqueue_next(clip.as_ref()); + }, + _ => {} + } + Ok(()) + } + fn clip (&self) -> Option>> { + self.scene()?.clips.get(self.selected().track()?)?.clone() + } + fn toggle_loop (&mut self) { + if let Some(clip) = self.clip() { + clip.write().unwrap().toggle_loop() + } + } + //fn randomize_color (&mut self) { + //match self.selected { + //ArrangerSelection::Mix => { self.color = ItemPalette::random() }, + //ArrangerSelection::Track(t) => { self.tracks[t].color = ItemPalette::random() }, + //ArrangerSelection::Scene(s) => { self.scenes[s].color = ItemPalette::random() }, + //ArrangerSelection::Clip(t, s) => if let Some(clip) = &self.scenes[s].clips[t] { + //clip.write().unwrap().color = ItemPalette::random(); + //} + //} + //} } @@ -180,7 +223,7 @@ impl ArrangerTrack { self.width += 1; } fn width_dec (&mut self) { - if self.width > Arranger::TRACK_MIN_WIDTH { + if self.width > TRACK_MIN_WIDTH { self.width -= 1; } } @@ -240,40 +283,3 @@ impl ArrangerSelection { }).into() } } -impl Arranger { - pub fn activate (&mut self) -> Usually<()> { - if let ArrangerSelection::Scene(s) = self.selected { - for (t, track) in self.tracks.iter_mut().enumerate() { - let clip = self.scenes[s].clips[t].clone(); - if track.player.play_clip.is_some() || clip.is_some() { - track.player.enqueue_next(clip.as_ref()); - } - } - if self.clock().is_stopped() { - self.clock().play_from(Some(0))?; - } - } else if let ArrangerSelection::Clip(t, s) = self.selected { - let clip = self.scenes[s].clips[t].clone(); - self.tracks[t].player.enqueue_next(clip.as_ref()); - }; - Ok(()) - } - pub fn clip (&self) -> Option>> { - self.scene()?.clips.get(self.selected.track()?)?.clone() - } - pub fn toggle_loop (&mut self) { - if let Some(clip) = self.clip() { - clip.write().unwrap().toggle_loop() - } - } - pub fn randomize_color (&mut self) { - match self.selected { - ArrangerSelection::Mix => { self.color = ItemPalette::random() }, - ArrangerSelection::Track(t) => { self.tracks[t].color = ItemPalette::random() }, - ArrangerSelection::Scene(s) => { self.scenes[s].color = ItemPalette::random() }, - ArrangerSelection::Clip(t, s) => if let Some(clip) = &self.scenes[s].clips[t] { - clip.write().unwrap().color = ItemPalette::random(); - } - } - } -} diff --git a/tek/src/audio.rs b/tek/src/audio.rs index 926cc3f2..b3b98eff 100644 --- a/tek/src/audio.rs +++ b/tek/src/audio.rs @@ -2,9 +2,9 @@ use crate::*; impl HasJack for App { fn jack (&self) -> &Arc> { &self.jack } } -impl HasJack for Arranger { - fn jack (&self) -> &Arc> { &self.jack } -} +//impl HasJack for Arranger { + //fn jack (&self) -> &Arc> { &self.jack } +//} audio!(|self: App, client, scope|{ // Start profiling cycle let t0 = self.perf.get_t0(); @@ -101,111 +101,111 @@ audio!(|self: App, client, scope|{ Control::Continue }); -audio!(|self: Sequencer, client, scope|{ - // Start profiling cycle - let t0 = self.perf.get_t0(); +//audio!(|self: Sequencer, client, scope|{ + //// Start profiling cycle + //let t0 = self.perf.get_t0(); - // Update transport clock - if Control::Quit == ClockAudio(self).process(client, scope) { - return Control::Quit - } - // Update MIDI sequencer - if Control::Quit == PlayerAudio( - &mut self.player, &mut self.note_buf, &mut self.midi_buf - ).process(client, scope) { - return Control::Quit - } - - // End profiling cycle - self.perf.update(t0, scope); - - Control::Continue -}); - -audio!(|self: Groovebox, client, scope|{ - // Start profiling cycle - let t0 = self.perf.get_t0(); - - // Update transport clock - if Control::Quit == ClockAudio(&mut self.player).process(client, scope) { - return Control::Quit - } - - // Update MIDI sequencer - if Control::Quit == PlayerAudio( - &mut self.player, &mut self.note_buf, &mut self.midi_buf - ).process(client, scope) { - return Control::Quit - } - - // Update sampler - if Control::Quit == SamplerAudio(&mut self.sampler).process(client, scope) { - return Control::Quit - } - - // TODO move these to editor and sampler: - for RawMidi { time, bytes } in self.player.midi_ins[0].port.iter(scope) { - if let LiveEvent::Midi { message, .. } = LiveEvent::parse(bytes).unwrap() { - match message { - MidiMessage::NoteOn { ref key, .. } => { - self.editor.set_note_point(key.as_int() as usize); - }, - MidiMessage::Controller { controller, value } => { - if let Some(sample) = &self.sampler.mapped[self.editor.note_point()] { - sample.write().unwrap().handle_cc(controller, value) - } - } - _ => {} - } - } - } - - // End profiling cycle - self.perf.update(t0, scope); - - Control::Continue -}); - -audio!(|self: Arranger, client, scope|{ - // Start profiling cycle - let t0 = self.perf.get_t0(); - - // Update transport clock - if Control::Quit == ClockAudio(self).process(client, scope) { - return Control::Quit - } - - //// Update MIDI sequencers - //let tracks = &mut self.tracks; - //let note_buf = &mut self.note_buf; - //let midi_buf = &mut self.midi_buf; - //if Control::Quit == TracksAudio(tracks, note_buf, midi_buf).process(client, scope) { + //// Update transport clock + //if Control::Quit == ClockAudio(self).process(client, scope) { + //return Control::Quit + //} + //// Update MIDI sequencer + //if Control::Quit == PlayerAudio( + //&mut self.player, &mut self.note_buf, &mut self.midi_buf + //).process(client, scope) { //return Control::Quit //} - // FIXME: one of these per playing track - //self.now.set(0.); - //if let ArrangerSelection::Clip(t, s) = self.selected { - //let clip = self.scenes.get(s).map(|scene|scene.clips.get(t)); - //if let Some(Some(Some(clip))) = clip { - //if let Some(track) = self.tracks().get(t) { - //if let Some((ref started_at, Some(ref playing))) = track.player.play_clip { - //let clip = clip.read().unwrap(); - //if *playing.read().unwrap() == *clip { - //let pulse = self.current().pulse.get(); - //let start = started_at.pulse.get(); - //let now = (pulse - start) % clip.length as f64; - //self.now.set(now); + //// End profiling cycle + //self.perf.update(t0, scope); + + //Control::Continue +//}); + +//audio!(|self: Groovebox, client, scope|{ + //// Start profiling cycle + //let t0 = self.perf.get_t0(); + + //// Update transport clock + //if Control::Quit == ClockAudio(&mut self.player).process(client, scope) { + //return Control::Quit + //} + + //// Update MIDI sequencer + //if Control::Quit == PlayerAudio( + //&mut self.player, &mut self.note_buf, &mut self.midi_buf + //).process(client, scope) { + //return Control::Quit + //} + + //// Update sampler + //if Control::Quit == SamplerAudio(&mut self.sampler).process(client, scope) { + //return Control::Quit + //} + + //// TODO move these to editor and sampler: + //for RawMidi { time, bytes } in self.player.midi_ins[0].port.iter(scope) { + //if let LiveEvent::Midi { message, .. } = LiveEvent::parse(bytes).unwrap() { + //match message { + //MidiMessage::NoteOn { ref key, .. } => { + //self.editor.set_note_point(key.as_int() as usize); + //}, + //MidiMessage::Controller { controller, value } => { + //if let Some(sample) = &self.sampler.mapped[self.editor.note_point()] { + //sample.write().unwrap().handle_cc(controller, value) //} //} + //_ => {} //} //} //} - // End profiling cycle - self.perf.update(t0, scope); - return Control::Continue -}); + //// End profiling cycle + //self.perf.update(t0, scope); + + //Control::Continue +//}); + +//audio!(|self: Arranger, client, scope|{ + //// Start profiling cycle + //let t0 = self.perf.get_t0(); + + //// Update transport clock + //if Control::Quit == ClockAudio(self).process(client, scope) { + //return Control::Quit + //} + + ////// Update MIDI sequencers + ////let tracks = &mut self.tracks; + ////let note_buf = &mut self.note_buf; + ////let midi_buf = &mut self.midi_buf; + ////if Control::Quit == TracksAudio(tracks, note_buf, midi_buf).process(client, scope) { + ////return Control::Quit + ////} + + //// FIXME: one of these per playing track + ////self.now.set(0.); + ////if let ArrangerSelection::Clip(t, s) = self.selected { + ////let clip = self.scenes.get(s).map(|scene|scene.clips.get(t)); + ////if let Some(Some(Some(clip))) = clip { + ////if let Some(track) = self.tracks().get(t) { + ////if let Some((ref started_at, Some(ref playing))) = track.player.play_clip { + ////let clip = clip.read().unwrap(); + ////if *playing.read().unwrap() == *clip { + ////let pulse = self.current().pulse.get(); + ////let start = started_at.pulse.get(); + ////let now = (pulse - start) % clip.length as f64; + ////self.now.set(now); + ////} + ////} + ////} + ////} + ////} + + //// End profiling cycle + //self.perf.update(t0, scope); + //return Control::Continue +//}); /// Hosts the JACK callback for a collection of tracks pub struct TracksAudio<'a>( diff --git a/tek/src/control.rs b/tek/src/control.rs index 6373e8ec..7d5cb1a2 100644 --- a/tek/src/control.rs +++ b/tek/src/control.rs @@ -10,9 +10,9 @@ use MidiEditCommand as EditCmd; use MidiPoolCommand as PoolCmd; handle!(TuiIn: |self: App, input| Ok(None)); -handle!(TuiIn: |self: Sequencer, input|SequencerCommand::execute_with_state(self, input.event())); -handle!(TuiIn: |self: Groovebox, input|GrooveboxCommand::execute_with_state(self, input.event())); -handle!(TuiIn: |self: Arranger, input|ArrangerCommand::execute_with_state(self, input.event())); +//handle!(TuiIn: |self: Sequencer, input|SequencerCommand::execute_with_state(self, input.event())); +//handle!(TuiIn: |self: Groovebox, input|GrooveboxCommand::execute_with_state(self, input.event())); +//handle!(TuiIn: |self: Arranger, input|ArrangerCommand::execute_with_state(self, input.event())); #[derive(Clone, Debug)] pub enum AppCommand { Clear, @@ -239,348 +239,348 @@ command!(|self: AppCommand, state: App|match self { }, }); -command!(|self: SequencerCommand, state: Sequencer|match self { - Self::Clock(cmd) => cmd.delegate(state, Self::Clock)?, - Self::Editor(cmd) => cmd.delegate(&mut state.editor, Self::Editor)?, - Self::Enqueue(clip) => { state.player.enqueue_next(clip.as_ref()); None }, - Self::History(delta) => { todo!("undo/redo") }, +//command!(|self: SequencerCommand, state: Sequencer|match self { + //Self::Clock(cmd) => cmd.delegate(state, Self::Clock)?, + //Self::Editor(cmd) => cmd.delegate(&mut state.editor, Self::Editor)?, + //Self::Enqueue(clip) => { state.player.enqueue_next(clip.as_ref()); None }, + //Self::History(delta) => { todo!("undo/redo") }, - Self::Pool(cmd) => match cmd { - // autoselect: automatically load selected clip in editor - PoolCommand::Select(_) => { - let undo = cmd.delegate(&mut state.pool, Self::Pool)?; - state.editor.set_clip(state.pool.clip().as_ref()); - undo - }, - // update color in all places simultaneously - PoolCommand::Clip(PoolCmd::SetColor(index, _)) => { - let undo = cmd.delegate(&mut state.pool, Self::Pool)?; - state.editor.set_clip(state.pool.clip().as_ref()); - undo - }, - _ => cmd.delegate(&mut state.pool, Self::Pool)? - }, - Self::Compact(compact) => if state.compact != compact { - state.compact = compact; - Some(Self::Compact(!compact)) - } else { - None - }, -}); -command!(|self: GrooveboxCommand, state: Groovebox|match self { - Self::Clock(cmd) => cmd.delegate(state, Self::Clock)?, - Self::Editor(cmd) => cmd.delegate(&mut state.editor, Self::Editor)?, - Self::Enqueue(clip) => { state.player.enqueue_next(clip.as_ref()); None }, - Self::History(delta) => { todo!("undo/redo") }, - Self::Sampler(cmd) => cmd.delegate(&mut state.sampler, Self::Sampler)?, - - Self::Pool(cmd) => match cmd { - // autoselect: automatically load selected clip in editor - PoolCommand::Select(_) => { - let undo = cmd.delegate(&mut state.pool, Self::Pool)?; - state.editor.set_clip(state.pool.clip().as_ref()); - undo - }, - // update color in all places simultaneously - PoolCommand::Clip(PoolCmd::SetColor(index, _)) => { - let undo = cmd.delegate(&mut state.pool, Self::Pool)?; - state.editor.set_clip(state.pool.clip().as_ref()); - undo - }, - _ => cmd.delegate(&mut state.pool, Self::Pool)? - }, - Self::Compact(compact) => if state.compact != compact { - state.compact = compact; - Some(Self::Compact(!compact)) - } else { - None - }, -}); -command!(|self: ArrangerCommand, state: Arranger|match self { - Self::Clear => { todo!() }, - Self::Clip(cmd) => cmd.delegate(state, Self::Clip)?, - Self::Clock(cmd) => cmd.delegate(state, Self::Clock)?, - Self::Editor(cmd) => cmd.delegate(&mut state.editor, Self::Editor)?, - Self::History(_) => { todo!() }, - Self::Scene(cmd) => cmd.delegate(state, Self::Scene)?, - Self::Select(s) => { state.selected = s; None }, - Self::Track(cmd) => cmd.delegate(state, Self::Track)?, - Self::Zoom(_) => { todo!(); }, - - Self::StopAll => { - for track in 0..state.tracks.len() { state.tracks[track].player.enqueue_next(None); } - None - }, - Self::Color(palette) => { - let old = state.color; - state.color = palette; - Some(Self::Color(old)) - }, - Self::Pool(cmd) => { - match cmd { - // autoselect: automatically load selected clip in editor - PoolCommand::Select(_) => { - let undo = cmd.delegate(&mut state.pool, Self::Pool)?; - state.editor.set_clip(state.pool.clip().as_ref()); - undo - }, - // reload clip in editor to update color - PoolCommand::Clip(MidiPoolCommand::SetColor(index, _)) => { - let undo = cmd.delegate(&mut state.pool, Self::Pool)?; - state.editor.set_clip(state.pool.clip().as_ref()); - undo - }, - _ => cmd.delegate(&mut state.pool, Self::Pool)? - } - }, -}); -command!(|self: SceneCommand, state: Arranger|match self { - Self::Add => { state.scene_add(None, None)?; None } - Self::Del(index) => { state.scene_del(index); None }, - Self::SetColor(index, color) => { - let old = state.scenes[index].color; - state.scenes[index].color = color; - Some(Self::SetColor(index, old)) - }, - Self::Enqueue(scene) => { - for track in 0..state.tracks.len() { - state.tracks[track].player.enqueue_next(state.scenes[scene].clips[track].as_ref()); - } - None - }, - _ => None -}); -command!(|self: TrackCommand, state: Arranger|match self { - Self::Add => { state.track_add(None, None)?; None }, - Self::Del(index) => { state.track_del(index); None }, - Self::Stop(track) => { state.tracks[track].player.enqueue_next(None); None }, - Self::SetColor(index, color) => { - let old = state.tracks[index].color; - state.tracks[index].color = color; - Some(Self::SetColor(index, old)) - }, - _ => None -}); -command!(|self: ClipCommand, state: Arranger|match self { - Self::Get(track, scene) => { todo!() }, - Self::Put(track, scene, clip) => { - let old = state.scenes[scene].clips[track].clone(); - state.scenes[scene].clips[track] = clip; - Some(Self::Put(track, scene, old)) - }, - Self::Enqueue(track, scene) => { - state.tracks[track].player.enqueue_next(state.scenes[scene].clips[track].as_ref()); - None - }, - _ => None -}); -keymap!(KEYS_SEQUENCER = |state: Sequencer, input: Event| SequencerCommand { - // TODO: k: toggle on-screen keyboard - ctrl(key(Char('k'))) => { todo!("keyboard") }, - // Transport: Play/pause - key(Char(' ')) => SeqCmd::Clock(if state.clock().is_stopped() { Play(None) } else { Pause(None) }), - // Transport: Play from start or rewind to start - shift(key(Char(' '))) => SeqCmd::Clock(if state.clock().is_stopped() { Play(Some(0)) } else { Pause(Some(0)) }), - // u: undo - key(Char('u')) => SeqCmd::History(-1), - // Shift-U: redo - key(Char('U')) => SeqCmd::History( 1), - // Tab: Toggle compact mode - key(Tab) => SeqCmd::Compact(!state.compact), - // q: Enqueue currently edited clip - key(Char('q')) => SeqCmd::Enqueue(state.pool.clip().clone()), - // 0: Enqueue clip 0 (stop all) - key(Char('0')) => SeqCmd::Enqueue(Some(state.clips()[0].clone())), - // e: Toggle between editing currently playing or other clip - //key(Char('e')) => if let Some((_, Some(playing))) = state.player.play_clip() { - //let editing = state.editor.clip().as_ref().map(|p|p.read().unwrap().clone()); - //let selected = state.pool.clip().clone(); - //SeqCmd::Editor(Show(Some(if Some(selected.read().unwrap().clone()) != editing { - //selected - //} else { - //playing.clone() - //}))) - //} else { - //return None - //} -}, if let Some(command) = MidiEditCommand::input_to_command(&state.editor, input) { - SeqCmd::Editor(command) -} else if let Some(command) = PoolCommand::input_to_command(&state.pool, input) { - SeqCmd::Pool(command) -} else { - return None -}); -keymap!(<'a> KEYS_GROOVEBOX = |state: Groovebox, input: Event| GrooveboxCommand { - // Tab: Toggle compact mode - key(Tab) => GrvCmd::Compact(!state.compact), - // q: Enqueue currently edited clip - key(Char('q')) => GrvCmd::Enqueue(state.pool.clip().clone()), - // 0: Enqueue clip 0 (stop all) - key(Char('0')) => GrvCmd::Enqueue(Some(state.pool.clips()[0].clone())), - // TODO: k: toggle on-screen keyboard - ctrl(key(Char('k'))) => todo!("keyboard"), - // Transport: Play from start or rewind to start - ctrl(key(Char(' '))) => GrvCmd::Clock( - if state.clock().is_stopped() { Play(Some(0)) } else { Pause(Some(0)) } - ), - // Shift-R: toggle recording - shift(key(Char('R'))) => GrvCmd::Sampler(if state.sampler.recording.is_some() { - SmplCmd::RecordFinish - } else { - SmplCmd::RecordBegin(u7::from(state.editor.note_point() as u8)) - }), - // Shift-Del: delete sample - shift(key(Delete)) => GrvCmd::Sampler( - SmplCmd::SetSample(u7::from(state.editor.note_point() as u8), None) - ), - // e: Toggle between editing currently playing or other clip - //shift(key(Char('e'))) => if let Some((_, Some(playing))) = state.player.play_clip() { - //let editing = state.editor.clip().as_ref().map(|p|p.read().unwrap().clone()); - //let selected = state.pool.clip().clone().map(|s|s.read().unwrap().clone()); - //GrvCmd::Editor(Show(if selected != editing { - //selected - //} else { - //Some(playing.clone()) - //})) - //} else { - //return None + //Self::Pool(cmd) => match cmd { + //// autoselect: automatically load selected clip in editor + //PoolCommand::Select(_) => { + //let undo = cmd.delegate(&mut state.pool, Self::Pool)?; + //state.editor.set_clip(state.pool.clip().as_ref()); + //undo + //}, + //// update color in all places simultaneously + //PoolCommand::Clip(PoolCmd::SetColor(index, _)) => { + //let undo = cmd.delegate(&mut state.pool, Self::Pool)?; + //state.editor.set_clip(state.pool.clip().as_ref()); + //undo + //}, + //_ => cmd.delegate(&mut state.pool, Self::Pool)? //}, -}, if let Some(command) = MidiEditCommand::input_to_command(&state.editor, input) { - GrvCmd::Editor(command) -} else if let Some(command) = PoolCommand::input_to_command(&state.pool, input) { - GrvCmd::Pool(command) -} else { - return None -}); -keymap!(KEYS_ARRANGER = |state: Arranger, input: Event| ArrangerCommand { - key(Char('u')) => ArrCmd::History(-1), - key(Char('U')) => ArrCmd::History(1), - // TODO: k: toggle on-screen keyboard - ctrl(key(Char('k'))) => { todo!("keyboard") }, - // Transport: Play/pause - key(Char(' ')) => ArrCmd::Clock(if state.clock().is_stopped() { Play(None) } else { Pause(None) }), - // Transport: Play from start or rewind to start - shift(key(Char(' '))) => ArrCmd::Clock(if state.clock().is_stopped() { Play(Some(0)) } else { Pause(Some(0)) }), - key(Char('e')) => ArrCmd::Editor(MidiEditCommand::Show(state.pool.clip().clone())), - ctrl(key(Char('a'))) => ArrCmd::Scene(SceneCommand::Add), - ctrl(key(Char('A'))) => return None,//ArrCmd::Scene(SceneCommand::Add), - ctrl(key(Char('t'))) => ArrCmd::Track(TrackCommand::Add), - // Tab: Toggle visibility of clip pool column - key(Tab) => ArrCmd::Pool(PoolCommand::Show(!state.pool.visible)), -}, { - use ArrangerSelection as Selected; - use SceneCommand as Scene; - use TrackCommand as Track; - use ClipCommand as Clip; - let t_len = state.tracks.len(); - let s_len = state.scenes.len(); - match state.selected { - Selected::Clip(t, s) => clip_keymap(state, input, t, s), - Selected::Scene(s) => scene_keymap(state, input, s), - Selected::Track(t) => track_keymap(state, input, t), - Selected::Mix => match input { + //Self::Compact(compact) => if state.compact != compact { + //state.compact = compact; + //Some(Self::Compact(!compact)) + //} else { + //None + //}, +//}); +//command!(|self: GrooveboxCommand, state: Groovebox|match self { + //Self::Clock(cmd) => cmd.delegate(state, Self::Clock)?, + //Self::Editor(cmd) => cmd.delegate(&mut state.editor, Self::Editor)?, + //Self::Enqueue(clip) => { state.player.enqueue_next(clip.as_ref()); None }, + //Self::History(delta) => { todo!("undo/redo") }, + //Self::Sampler(cmd) => cmd.delegate(&mut state.sampler, Self::Sampler)?, - kpat!(Delete) => Some(ArrCmd::Clear), - kpat!(Char('0')) => Some(ArrCmd::StopAll), - kpat!(Char('c')) => Some(ArrCmd::Color(ItemPalette::random())), + //Self::Pool(cmd) => match cmd { + //// autoselect: automatically load selected clip in editor + //PoolCommand::Select(_) => { + //let undo = cmd.delegate(&mut state.pool, Self::Pool)?; + //state.editor.set_clip(state.pool.clip().as_ref()); + //undo + //}, + //// update color in all places simultaneously + //PoolCommand::Clip(PoolCmd::SetColor(index, _)) => { + //let undo = cmd.delegate(&mut state.pool, Self::Pool)?; + //state.editor.set_clip(state.pool.clip().as_ref()); + //undo + //}, + //_ => cmd.delegate(&mut state.pool, Self::Pool)? + //}, + //Self::Compact(compact) => if state.compact != compact { + //state.compact = compact; + //Some(Self::Compact(!compact)) + //} else { + //None + //}, +//}); +//command!(|self: ArrangerCommand, state: Arranger|match self { + //Self::Clear => { todo!() }, + //Self::Clip(cmd) => cmd.delegate(state, Self::Clip)?, + //Self::Clock(cmd) => cmd.delegate(state, Self::Clock)?, + //Self::Editor(cmd) => cmd.delegate(&mut state.editor, Self::Editor)?, + //Self::History(_) => { todo!() }, + //Self::Scene(cmd) => cmd.delegate(state, Self::Scene)?, + //Self::Select(s) => { state.selected = s; None }, + //Self::Track(cmd) => cmd.delegate(state, Self::Track)?, + //Self::Zoom(_) => { todo!(); }, - kpat!(Up) => return None, - kpat!(Down) => Some(ArrCmd::Select(Selected::Scene(0))), - kpat!(Left) => return None, - kpat!(Right) => Some(ArrCmd::Select(Selected::Track(0))), + //Self::StopAll => { + //for track in 0..state.tracks.len() { state.tracks[track].player.enqueue_next(None); } + //None + //}, + //Self::Color(palette) => { + //let old = state.color; + //state.color = palette; + //Some(Self::Color(old)) + //}, + //Self::Pool(cmd) => { + //match cmd { + //// autoselect: automatically load selected clip in editor + //PoolCommand::Select(_) => { + //let undo = cmd.delegate(&mut state.pool, Self::Pool)?; + //state.editor.set_clip(state.pool.clip().as_ref()); + //undo + //}, + //// reload clip in editor to update color + //PoolCommand::Clip(MidiPoolCommand::SetColor(index, _)) => { + //let undo = cmd.delegate(&mut state.pool, Self::Pool)?; + //state.editor.set_clip(state.pool.clip().as_ref()); + //undo + //}, + //_ => cmd.delegate(&mut state.pool, Self::Pool)? + //} + //}, +//}); +//command!(|self: SceneCommand, state: Arranger|match self { + //Self::Add => { state.scene_add(None, None)?; None } + //Self::Del(index) => { state.scene_del(index); None }, + //Self::SetColor(index, color) => { + //let old = state.scenes[index].color; + //state.scenes[index].color = color; + //Some(Self::SetColor(index, old)) + //}, + //Self::Enqueue(scene) => { + //for track in 0..state.tracks.len() { + //state.tracks[track].player.enqueue_next(state.scenes[scene].clips[track].as_ref()); + //} + //None + //}, + //_ => None +//}); +//command!(|self: TrackCommand, state: Arranger|match self { + //Self::Add => { state.track_add(None, None)?; None }, + //Self::Del(index) => { state.track_del(index); None }, + //Self::Stop(track) => { state.tracks[track].player.enqueue_next(None); None }, + //Self::SetColor(index, color) => { + //let old = state.tracks[index].color; + //state.tracks[index].color = color; + //Some(Self::SetColor(index, old)) + //}, + //_ => None +//}); +//command!(|self: ClipCommand, state: Arranger|match self { + //Self::Get(track, scene) => { todo!() }, + //Self::Put(track, scene, clip) => { + //let old = state.scenes[scene].clips[track].clone(); + //state.scenes[scene].clips[track] = clip; + //Some(Self::Put(track, scene, old)) + //}, + //Self::Enqueue(track, scene) => { + //state.tracks[track].player.enqueue_next(state.scenes[scene].clips[track].as_ref()); + //None + //}, + //_ => None +//}); +//keymap!(KEYS_SEQUENCER = |state: Sequencer, input: Event| SequencerCommand { + //// TODO: k: toggle on-screen keyboard + //ctrl(key(Char('k'))) => { todo!("keyboard") }, + //// Transport: Play/pause + //key(Char(' ')) => SeqCmd::Clock(if state.clock().is_stopped() { Play(None) } else { Pause(None) }), + //// Transport: Play from start or rewind to start + //shift(key(Char(' '))) => SeqCmd::Clock(if state.clock().is_stopped() { Play(Some(0)) } else { Pause(Some(0)) }), + //// u: undo + //key(Char('u')) => SeqCmd::History(-1), + //// Shift-U: redo + //key(Char('U')) => SeqCmd::History( 1), + //// Tab: Toggle compact mode + //key(Tab) => SeqCmd::Compact(!state.compact), + //// q: Enqueue currently edited clip + //key(Char('q')) => SeqCmd::Enqueue(state.pool.clip().clone()), + //// 0: Enqueue clip 0 (stop all) + //key(Char('0')) => SeqCmd::Enqueue(Some(state.clips()[0].clone())), + //// e: Toggle between editing currently playing or other clip + ////key(Char('e')) => if let Some((_, Some(playing))) = state.player.play_clip() { + ////let editing = state.editor.clip().as_ref().map(|p|p.read().unwrap().clone()); + ////let selected = state.pool.clip().clone(); + ////SeqCmd::Editor(Show(Some(if Some(selected.read().unwrap().clone()) != editing { + ////selected + ////} else { + ////playing.clone() + ////}))) + ////} else { + ////return None + ////} +//}, if let Some(command) = MidiEditCommand::input_to_command(&state.editor, input) { + //SeqCmd::Editor(command) +//} else if let Some(command) = PoolCommand::input_to_command(&state.pool, input) { + //SeqCmd::Pool(command) +//} else { + //return None +//}); +//keymap!(<'a> KEYS_GROOVEBOX = |state: Groovebox, input: Event| GrooveboxCommand { + //// Tab: Toggle compact mode + //key(Tab) => GrvCmd::Compact(!state.compact), + //// q: Enqueue currently edited clip + //key(Char('q')) => GrvCmd::Enqueue(state.pool.clip().clone()), + //// 0: Enqueue clip 0 (stop all) + //key(Char('0')) => GrvCmd::Enqueue(Some(state.pool.clips()[0].clone())), + //// TODO: k: toggle on-screen keyboard + //ctrl(key(Char('k'))) => todo!("keyboard"), + //// Transport: Play from start or rewind to start + //ctrl(key(Char(' '))) => GrvCmd::Clock( + //if state.clock().is_stopped() { Play(Some(0)) } else { Pause(Some(0)) } + //), + //// Shift-R: toggle recording + //shift(key(Char('R'))) => GrvCmd::Sampler(if state.sampler.recording.is_some() { + //SmplCmd::RecordFinish + //} else { + //SmplCmd::RecordBegin(u7::from(state.editor.note_point() as u8)) + //}), + //// Shift-Del: delete sample + //shift(key(Delete)) => GrvCmd::Sampler( + //SmplCmd::SetSample(u7::from(state.editor.note_point() as u8), None) + //), + //// e: Toggle between editing currently playing or other clip + ////shift(key(Char('e'))) => if let Some((_, Some(playing))) = state.player.play_clip() { + ////let editing = state.editor.clip().as_ref().map(|p|p.read().unwrap().clone()); + ////let selected = state.pool.clip().clone().map(|s|s.read().unwrap().clone()); + ////GrvCmd::Editor(Show(if selected != editing { + ////selected + ////} else { + ////Some(playing.clone()) + ////})) + ////} else { + ////return None + ////}, +//}, if let Some(command) = MidiEditCommand::input_to_command(&state.editor, input) { + //GrvCmd::Editor(command) +//} else if let Some(command) = PoolCommand::input_to_command(&state.pool, input) { + //GrvCmd::Pool(command) +//} else { + //return None +//}); +//keymap!(KEYS_ARRANGER = |state: Arranger, input: Event| ArrangerCommand { + //key(Char('u')) => ArrCmd::History(-1), + //key(Char('U')) => ArrCmd::History(1), + //// TODO: k: toggle on-screen keyboard + //ctrl(key(Char('k'))) => { todo!("keyboard") }, + //// Transport: Play/pause + //key(Char(' ')) => ArrCmd::Clock(if state.clock().is_stopped() { Play(None) } else { Pause(None) }), + //// Transport: Play from start or rewind to start + //shift(key(Char(' '))) => ArrCmd::Clock(if state.clock().is_stopped() { Play(Some(0)) } else { Pause(Some(0)) }), + //key(Char('e')) => ArrCmd::Editor(MidiEditCommand::Show(state.pool.clip().clone())), + //ctrl(key(Char('a'))) => ArrCmd::Scene(SceneCommand::Add), + //ctrl(key(Char('A'))) => return None,//ArrCmd::Scene(SceneCommand::Add), + //ctrl(key(Char('t'))) => ArrCmd::Track(TrackCommand::Add), + //// Tab: Toggle visibility of clip pool column + //key(Tab) => ArrCmd::Pool(PoolCommand::Show(!state.pool.visible)), +//}, { + //use ArrangerSelection as Selected; + //use SceneCommand as Scene; + //use TrackCommand as Track; + //use ClipCommand as Clip; + //let t_len = state.tracks.len(); + //let s_len = state.scenes.len(); + //match state.selected { + //Selected::Clip(t, s) => clip_keymap(state, input, t, s), + //Selected::Scene(s) => scene_keymap(state, input, s), + //Selected::Track(t) => track_keymap(state, input, t), + //Selected::Mix => match input { - _ => None - }, - } -}.or_else(||if let Some(command) = MidiEditCommand::input_to_command(&state.editor, input) { - Some(ArrCmd::Editor(command)) -} else if let Some(command) = PoolCommand::input_to_command(&state.pool, input) { - Some(ArrCmd::Pool(command)) -} else { - None -})?); + //kpat!(Delete) => Some(ArrCmd::Clear), + //kpat!(Char('0')) => Some(ArrCmd::StopAll), + //kpat!(Char('c')) => Some(ArrCmd::Color(ItemPalette::random())), -fn clip_keymap (state: &Arranger, input: &Event, t: usize, s: usize) -> Option { - use ArrangerSelection as Selected; - use SceneCommand as Scene; - use TrackCommand as Track; - use ClipCommand as Clip; - let t_len = state.tracks.len(); - let s_len = state.scenes.len(); - Some(match input { + //kpat!(Up) => return None, + //kpat!(Down) => Some(ArrCmd::Select(Selected::Scene(0))), + //kpat!(Left) => return None, + //kpat!(Right) => Some(ArrCmd::Select(Selected::Track(0))), - kpat!(Char('g')) => ArrCmd::Pool(PoolCommand::Select(0)), - kpat!(Char('q')) => ArrCmd::Clip(Clip::Enqueue(t, s)), - kpat!(Char('l')) => ArrCmd::Clip(Clip::SetLoop(t, s, false)), + //_ => None + //}, + //} +//}.or_else(||if let Some(command) = MidiEditCommand::input_to_command(&state.editor, input) { + //Some(ArrCmd::Editor(command)) +//} else if let Some(command) = PoolCommand::input_to_command(&state.pool, input) { + //Some(ArrCmd::Pool(command)) +//} else { + //None +//})?); - kpat!(Enter) => if state.scenes[s].clips[t].is_none() { - // FIXME: get this clip from the pool (autoregister via intmut) - let (_, clip) = state.add_clip(); - ArrCmd::Clip(Clip::Put(t, s, Some(clip))) - } else { - return None - }, - kpat!(Delete) => ArrCmd::Clip(Clip::Put(t, s, None)), - kpat!(Char('p')) => ArrCmd::Clip(Clip::Put(t, s, state.pool.clip().clone())), - kpat!(Char(',')) => ArrCmd::Clip(Clip::Put(t, s, None)), - kpat!(Char('.')) => ArrCmd::Clip(Clip::Put(t, s, None)), - kpat!(Char('<')) => ArrCmd::Clip(Clip::Put(t, s, None)), - kpat!(Char('>')) => ArrCmd::Clip(Clip::Put(t, s, None)), +//fn clip_keymap (state: &Arranger, input: &Event, t: usize, s: usize) -> Option { + //use ArrangerSelection as Selected; + //use SceneCommand as Scene; + //use TrackCommand as Track; + //use ClipCommand as Clip; + //let t_len = state.tracks.len(); + //let s_len = state.scenes.len(); + //Some(match input { - kpat!(Up) => ArrCmd::Select(if s > 0 { Selected::Clip(t, s - 1) } else { Selected::Track(t) }), - kpat!(Down) => ArrCmd::Select(Selected::Clip(t, (s + 1).min(s_len.saturating_sub(1)))), - kpat!(Left) => ArrCmd::Select(if t > 0 { Selected::Clip(t - 1, s) } else { Selected::Scene(s) }), - kpat!(Right) => ArrCmd::Select(Selected::Clip((t + 1).min(t_len.saturating_sub(1)), s)), + //kpat!(Char('g')) => ArrCmd::Pool(PoolCommand::Select(0)), + //kpat!(Char('q')) => ArrCmd::Clip(Clip::Enqueue(t, s)), + //kpat!(Char('l')) => ArrCmd::Clip(Clip::SetLoop(t, s, false)), - _ => return None - }) -} -fn scene_keymap (state: &Arranger, input: &Event, s: usize) -> Option { - use ArrangerSelection as Selected; - use SceneCommand as Scene; - use TrackCommand as Track; - use ClipCommand as Clip; - let t_len = state.tracks.len(); - let s_len = state.scenes.len(); - Some(match input { + //kpat!(Enter) => if state.scenes[s].clips[t].is_none() { + //// FIXME: get this clip from the pool (autoregister via intmut) + //let (_, clip) = state.add_clip(); + //ArrCmd::Clip(Clip::Put(t, s, Some(clip))) + //} else { + //return None + //}, + //kpat!(Delete) => ArrCmd::Clip(Clip::Put(t, s, None)), + //kpat!(Char('p')) => ArrCmd::Clip(Clip::Put(t, s, state.pool.clip().clone())), + //kpat!(Char(',')) => ArrCmd::Clip(Clip::Put(t, s, None)), + //kpat!(Char('.')) => ArrCmd::Clip(Clip::Put(t, s, None)), + //kpat!(Char('<')) => ArrCmd::Clip(Clip::Put(t, s, None)), + //kpat!(Char('>')) => ArrCmd::Clip(Clip::Put(t, s, None)), - kpat!(Char(',')) => ArrCmd::Scene(Scene::Swap(s, s - 1)), - kpat!(Char('.')) => ArrCmd::Scene(Scene::Swap(s, s + 1)), - kpat!(Char('<')) => ArrCmd::Scene(Scene::Swap(s, s - 1)), - kpat!(Char('>')) => ArrCmd::Scene(Scene::Swap(s, s + 1)), - kpat!(Char('q')) => ArrCmd::Scene(Scene::Enqueue(s)), - kpat!(Delete) => ArrCmd::Scene(Scene::Del(s)), - kpat!(Char('c')) => ArrCmd::Scene(Scene::SetColor(s, ItemPalette::random())), + //kpat!(Up) => ArrCmd::Select(if s > 0 { Selected::Clip(t, s - 1) } else { Selected::Track(t) }), + //kpat!(Down) => ArrCmd::Select(Selected::Clip(t, (s + 1).min(s_len.saturating_sub(1)))), + //kpat!(Left) => ArrCmd::Select(if t > 0 { Selected::Clip(t - 1, s) } else { Selected::Scene(s) }), + //kpat!(Right) => ArrCmd::Select(Selected::Clip((t + 1).min(t_len.saturating_sub(1)), s)), - kpat!(Up) => ArrCmd::Select(if s > 0 { Selected::Scene(s - 1) } else { Selected::Mix }), - kpat!(Down) => ArrCmd::Select(Selected::Scene((s + 1).min(s_len.saturating_sub(1)))), - kpat!(Left) => return None, - kpat!(Right) => ArrCmd::Select(Selected::Clip(0, s)), + //_ => return None + //}) +//} +//fn scene_keymap (state: &Arranger, input: &Event, s: usize) -> Option { + //use ArrangerSelection as Selected; + //use SceneCommand as Scene; + //use TrackCommand as Track; + //use ClipCommand as Clip; + //let t_len = state.tracks.len(); + //let s_len = state.scenes.len(); + //Some(match input { - _ => return None - }) -} -fn track_keymap (state: &Arranger, input: &Event, t: usize) -> Option { - use ArrangerSelection as Selected; - use SceneCommand as Scene; - use TrackCommand as Track; - use ClipCommand as Clip; - let t_len = state.tracks.len(); - let s_len = state.scenes.len(); - Some(match input { + //kpat!(Char(',')) => ArrCmd::Scene(Scene::Swap(s, s - 1)), + //kpat!(Char('.')) => ArrCmd::Scene(Scene::Swap(s, s + 1)), + //kpat!(Char('<')) => ArrCmd::Scene(Scene::Swap(s, s - 1)), + //kpat!(Char('>')) => ArrCmd::Scene(Scene::Swap(s, s + 1)), + //kpat!(Char('q')) => ArrCmd::Scene(Scene::Enqueue(s)), + //kpat!(Delete) => ArrCmd::Scene(Scene::Del(s)), + //kpat!(Char('c')) => ArrCmd::Scene(Scene::SetColor(s, ItemPalette::random())), - kpat!(Char(',')) => ArrCmd::Track(Track::Swap(t, t - 1)), - kpat!(Char('.')) => ArrCmd::Track(Track::Swap(t, t + 1)), - kpat!(Char('<')) => ArrCmd::Track(Track::Swap(t, t - 1)), - kpat!(Char('>')) => ArrCmd::Track(Track::Swap(t, t + 1)), - kpat!(Delete) => ArrCmd::Track(Track::Del(t)), - kpat!(Char('c')) => ArrCmd::Track(Track::SetColor(t, ItemPalette::random())), + //kpat!(Up) => ArrCmd::Select(if s > 0 { Selected::Scene(s - 1) } else { Selected::Mix }), + //kpat!(Down) => ArrCmd::Select(Selected::Scene((s + 1).min(s_len.saturating_sub(1)))), + //kpat!(Left) => return None, + //kpat!(Right) => ArrCmd::Select(Selected::Clip(0, s)), - kpat!(Up) => return None, - kpat!(Down) => ArrCmd::Select(Selected::Clip(t, 0)), - kpat!(Left) => ArrCmd::Select(if t > 0 { Selected::Track(t - 1) } else { Selected::Mix }), - kpat!(Right) => ArrCmd::Select(Selected::Track((t + 1).min(t_len.saturating_sub(1)))), + //_ => return None + //}) +//} +//fn track_keymap (state: &Arranger, input: &Event, t: usize) -> Option { + //use ArrangerSelection as Selected; + //use SceneCommand as Scene; + //use TrackCommand as Track; + //use ClipCommand as Clip; + //let t_len = state.tracks.len(); + //let s_len = state.scenes.len(); + //Some(match input { - _ => return None - }) -} + //kpat!(Char(',')) => ArrCmd::Track(Track::Swap(t, t - 1)), + //kpat!(Char('.')) => ArrCmd::Track(Track::Swap(t, t + 1)), + //kpat!(Char('<')) => ArrCmd::Track(Track::Swap(t, t - 1)), + //kpat!(Char('>')) => ArrCmd::Track(Track::Swap(t, t + 1)), + //kpat!(Delete) => ArrCmd::Track(Track::Del(t)), + //kpat!(Char('c')) => ArrCmd::Track(Track::SetColor(t, ItemPalette::random())), + + //kpat!(Up) => return None, + //kpat!(Down) => ArrCmd::Select(Selected::Clip(t, 0)), + //kpat!(Left) => ArrCmd::Select(if t > 0 { Selected::Track(t - 1) } else { Selected::Mix }), + //kpat!(Right) => ArrCmd::Select(Selected::Track((t + 1).min(t_len.saturating_sub(1)))), + + //_ => return None + //}) +//} diff --git a/tek/src/model.rs b/tek/src/model.rs index f664101d..3ff2e905 100644 --- a/tek/src/model.rs +++ b/tek/src/model.rs @@ -92,60 +92,60 @@ has_clock!(|self: App|&self.clock); has_clips!(|self: App|self.pool.as_ref().expect("no clip pool").clips); has_editor!(|self: App|self.editor.as_ref().expect("no editor")); -#[derive(Default)] pub struct Sequencer { - pub jack: Arc>, - pub compact: bool, - pub editor: MidiEditor, - pub midi_buf: Vec>>, - pub note_buf: Vec, - pub perf: PerfModel, - pub player: MidiPlayer, - pub pool: PoolModel, - pub selectors: bool, - pub size: Measure, - pub status: bool, - pub transport: bool, -} -has_size!(|self:Sequencer|&self.size); -has_clock!(|self:Sequencer|&self.player.clock); -has_clips!(|self:Sequencer|self.pool.clips); -has_editor!(|self:Sequencer|self.editor); -has_player!(|self:Sequencer|self.player); +//#[derive(Default)] pub struct Sequencer { + //pub jack: Arc>, + //pub compact: bool, + //pub editor: MidiEditor, + //pub midi_buf: Vec>>, + //pub note_buf: Vec, + //pub perf: PerfModel, + //pub player: MidiPlayer, + //pub pool: PoolModel, + //pub selectors: bool, + //pub size: Measure, + //pub status: bool, + //pub transport: bool, +//} +//has_size!(|self:Sequencer|&self.size); +//has_clock!(|self:Sequencer|&self.player.clock); +//has_clips!(|self:Sequencer|self.pool.clips); +//has_editor!(|self:Sequencer|self.editor); +//has_player!(|self:Sequencer|self.player); -#[derive(Default)] pub struct Groovebox { - pub jack: Arc>, - pub compact: bool, - pub editor: MidiEditor, - pub midi_buf: Vec>>, - pub note_buf: Vec, - pub perf: PerfModel, - pub player: MidiPlayer, - pub pool: PoolModel, - pub sampler: Sampler, - pub size: Measure, - pub status: bool, -} -has_clock!(|self: Groovebox|self.player.clock()); +//#[derive(Default)] pub struct Groovebox { + //pub jack: Arc>, + //pub compact: bool, + //pub editor: MidiEditor, + //pub midi_buf: Vec>>, + //pub note_buf: Vec, + //pub perf: PerfModel, + //pub player: MidiPlayer, + //pub pool: PoolModel, + //pub sampler: Sampler, + //pub size: Measure, + //pub status: bool, +//} +//has_clock!(|self: Groovebox|self.player.clock()); -#[derive(Default)] pub struct Arranger { - pub clock: Clock, - pub color: ItemPalette, - pub compact: bool, - pub editing: AtomicBool, - pub editor: MidiEditor, - pub jack: Arc>, - pub midi_buf: Vec>>, - pub midi_ins: Vec>, - pub midi_outs: Vec>, - pub note_buf: Vec, - pub perf: PerfModel, - pub pool: PoolModel, - pub scenes: Vec, - pub selected: ArrangerSelection, - pub size: Measure, - pub splits: [u16;2], - pub tracks: Vec, -} -has_clock!(|self: Arranger|&self.clock); -has_clips!(|self: Arranger|self.pool.clips); -has_editor!(|self: Arranger|self.editor); +//#[derive(Default)] pub struct Arranger { + //pub clock: Clock, + //pub color: ItemPalette, + //pub compact: bool, + //pub editing: AtomicBool, + //pub editor: MidiEditor, + //pub jack: Arc>, + //pub midi_buf: Vec>>, + //pub midi_ins: Vec>, + //pub midi_outs: Vec>, + //pub note_buf: Vec, + //pub perf: PerfModel, + //pub pool: PoolModel, + //pub scenes: Vec, + //pub selected: ArrangerSelection, + //pub size: Measure, + //pub splits: [u16;2], + //pub tracks: Vec, +//} +//has_clock!(|self: Arranger|&self.clock); +//has_clips!(|self: Arranger|self.pool.clips); +//has_editor!(|self: Arranger|self.editor); diff --git a/tek/src/select.rs b/tek/src/select.rs deleted file mode 100644 index c7b7e813..00000000 --- a/tek/src/select.rs +++ /dev/null @@ -1 +0,0 @@ -use crate::*; diff --git a/tek/src/view.rs b/tek/src/view.rs index 3c8cb9f5..afc20926 100644 --- a/tek/src/view.rs +++ b/tek/src/view.rs @@ -337,134 +337,134 @@ fn cell > (color: ItemPalette, field: T) -> impl Content self.size.of(EdnView::from_source(self, Self::EDN))); -impl EdnViewData for &Sequencer { - fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> { - use EdnItem::*; - match item { - Nil => Box::new(()), - Exp(items) => Box::new(EdnView::from_items(*self, items.as_slice())), - Sym(":editor") => (&self.editor).boxed(), - Sym(":pool") => self.pool_view().boxed(), - Sym(":status") => self.status_view().boxed(), - Sym(":toolbar") => self.toolbar_view().boxed(), - _ => panic!("no content for {item:?}") - } - } -} -impl Sequencer { - const EDN: &'static str = include_str!("../edn/sequencer.edn"); - fn toolbar_view (&self) -> impl Content + use<'_> { - Fill::x(Fixed::y(2, Align::x(ClockView::new(true, &self.player.clock)))) - } - fn status_view (&self) -> impl Content + use<'_> { - Bsp::e( - When(self.selectors, Bsp::e( - self.player.play_status(), - self.player.next_status(), - )), - Bsp::e( - self.editor.clip_status(), - self.editor.edit_status(), - ) - ) - } - fn pool_view (&self) -> impl Content + use<'_> { - let w = self.size.w(); - let clip_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 }; - let pool_w = if self.pool.visible { clip_w } else { 0 }; - let pool = Pull::y(1, Fill::y(Align::e(PoolView(self.pool.visible, &self.pool)))); - Fixed::x(pool_w, Align::e(Fill::y(PoolView(self.compact, &self.pool)))) - } - fn help () -> impl Content { - let single = |binding, command|row!(" ", col!( - Tui::fg(TuiTheme::yellow(), binding), - command - )); - let double = |(b1, c1), (b2, c2)|col!( - row!(" ", Tui::fg(TuiTheme::yellow(), b1), " ", c1,), - row!(" ", Tui::fg(TuiTheme::yellow(), b2), " ", c2,), - ); - Tui::fg_bg(TuiTheme::g(255), TuiTheme::g(50), row!( - single("SPACE", "play/pause"), - double(("▲▼▶◀", "cursor"), ("Ctrl", "scroll"), ), - double(("a", "append"), ("s", "set note"),), - double((",.", "length"), ("<>", "triplet"), ), - double(("[]", "clip"), ("{}", "order"), ), - double(("q", "enqueue"), ("e", "edit"), ), - double(("c", "color"), ("", ""),), - )) - } -} -/////////////////////////////////////////////////////////////////////////////////////////////////// -render!(TuiOut: (self: Groovebox) => self.size.of(EdnView::from_source(self, Self::EDN))); -impl EdnViewData for &Groovebox { - fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> { - use EdnItem::*; - match item { - Nil => Box::new(()), - Exp(items) => Box::new(EdnView::from_items(*self, items.as_slice())), - Sym(":editor") => (&self.editor).boxed(), - Sym(":pool") => self.pool().boxed(), - Sym(":status") => self.status().boxed(), - Sym(":toolbar") => self.toolbar().boxed(), - Sym(":sampler") => self.sampler().boxed(), - Sym(":sample") => self.sample().boxed(), - _ => panic!("no content for {item:?}") - } - } - fn get_unit (&self, item: EdnItem<&str>) -> u16 { - use EdnItem::*; - match item.to_str() { - ":sample-h" => if self.compact { 0 } else { 5 }, - ":samples-w" => if self.compact { 4 } else { 11 }, - ":samples-y" => if self.compact { 1 } else { 0 }, - ":pool-w" => if self.compact { 5 } else { - let w = self.size.w(); - if w > 60 { 20 } else if w > 40 { 15 } else { 10 } - }, - _ => 0 - } - } -} -impl Groovebox { - const EDN: &'static str = include_str!("../edn/groovebox.edn"); - fn toolbar (&self) -> impl Content + use<'_> { - Fill::x(Fixed::y(2, lay!( - Fill::x(Align::w(Meter("L/", self.sampler.input_meter[0]))), - Fill::x(Align::e(Meter("R/", self.sampler.input_meter[1]))), - Align::x(ClockView::new(true, &self.player.clock)), - ))) - } - fn status (&self) -> impl Content + use<'_> { - row!( - self.player.play_status(), - self.player.next_status(), - self.editor.clip_status(), - self.editor.edit_status(), - ) - } - fn sample (&self) -> impl Content + use<'_> { - let note_pt = self.editor.note_point(); - let sample_h = if self.compact { 0 } else { 5 }; - Max::y(sample_h, Fill::xy( - Bsp::a( - Fill::x(Align::w(Fixed::y(1, self.sampler.status(note_pt)))), - self.sampler.viewer(note_pt)))) - } - fn pool (&self) -> impl Content + use<'_> { - let w = self.size.w(); - let pool_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 }; - Fixed::x(if self.compact { 5 } else { pool_w }, - PoolView(self.compact, &self.pool)) - } - fn sampler (&self) -> impl Content + use<'_> { - let note_pt = self.editor.note_point(); - let sampler_w = if self.compact { 4 } else { 40 }; - let sampler_y = if self.compact { 1 } else { 0 }; - Fixed::x(sampler_w, Push::y(sampler_y, Fill::y(self.sampler.list(self.compact, &self.editor)))) - } -} +//render!(TuiOut: (self: Sequencer) => self.size.of(EdnView::from_source(self, Self::EDN))); +//impl EdnViewData for &Sequencer { + //fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> { + //use EdnItem::*; + //match item { + //Nil => Box::new(()), + //Exp(items) => Box::new(EdnView::from_items(*self, items.as_slice())), + //Sym(":editor") => (&self.editor).boxed(), + //Sym(":pool") => self.pool_view().boxed(), + //Sym(":status") => self.status_view().boxed(), + //Sym(":toolbar") => self.toolbar_view().boxed(), + //_ => panic!("no content for {item:?}") + //} + //} +//} +//impl Sequencer { + //const EDN: &'static str = include_str!("../edn/sequencer.edn"); + //fn toolbar_view (&self) -> impl Content + use<'_> { + //Fill::x(Fixed::y(2, Align::x(ClockView::new(true, &self.player.clock)))) + //} + //fn status_view (&self) -> impl Content + use<'_> { + //Bsp::e( + //When(self.selectors, Bsp::e( + //self.player.play_status(), + //self.player.next_status(), + //)), + //Bsp::e( + //self.editor.clip_status(), + //self.editor.edit_status(), + //) + //) + //} + //fn pool_view (&self) -> impl Content + use<'_> { + //let w = self.size.w(); + //let clip_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 }; + //let pool_w = if self.pool.visible { clip_w } else { 0 }; + //let pool = Pull::y(1, Fill::y(Align::e(PoolView(self.pool.visible, &self.pool)))); + //Fixed::x(pool_w, Align::e(Fill::y(PoolView(self.compact, &self.pool)))) + //} + //fn help () -> impl Content { + //let single = |binding, command|row!(" ", col!( + //Tui::fg(TuiTheme::yellow(), binding), + //command + //)); + //let double = |(b1, c1), (b2, c2)|col!( + //row!(" ", Tui::fg(TuiTheme::yellow(), b1), " ", c1,), + //row!(" ", Tui::fg(TuiTheme::yellow(), b2), " ", c2,), + //); + //Tui::fg_bg(TuiTheme::g(255), TuiTheme::g(50), row!( + //single("SPACE", "play/pause"), + //double(("▲▼▶◀", "cursor"), ("Ctrl", "scroll"), ), + //double(("a", "append"), ("s", "set note"),), + //double((",.", "length"), ("<>", "triplet"), ), + //double(("[]", "clip"), ("{}", "order"), ), + //double(("q", "enqueue"), ("e", "edit"), ), + //double(("c", "color"), ("", ""),), + //)) + //} +//} +///////////////////////////////////////////////////////////////////////////////////////////////////// +//render!(TuiOut: (self: Groovebox) => self.size.of(EdnView::from_source(self, Self::EDN))); +//impl EdnViewData for &Groovebox { + //fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> { + //use EdnItem::*; + //match item { + //Nil => Box::new(()), + //Exp(items) => Box::new(EdnView::from_items(*self, items.as_slice())), + //Sym(":editor") => (&self.editor).boxed(), + //Sym(":pool") => self.pool().boxed(), + //Sym(":status") => self.status().boxed(), + //Sym(":toolbar") => self.toolbar().boxed(), + //Sym(":sampler") => self.sampler().boxed(), + //Sym(":sample") => self.sample().boxed(), + //_ => panic!("no content for {item:?}") + //} + //} + //fn get_unit (&self, item: EdnItem<&str>) -> u16 { + //use EdnItem::*; + //match item.to_str() { + //":sample-h" => if self.compact { 0 } else { 5 }, + //":samples-w" => if self.compact { 4 } else { 11 }, + //":samples-y" => if self.compact { 1 } else { 0 }, + //":pool-w" => if self.compact { 5 } else { + //let w = self.size.w(); + //if w > 60 { 20 } else if w > 40 { 15 } else { 10 } + //}, + //_ => 0 + //} + //} +//} +//impl Groovebox { + //const EDN: &'static str = include_str!("../edn/groovebox.edn"); + //fn toolbar (&self) -> impl Content + use<'_> { + //Fill::x(Fixed::y(2, lay!( + //Fill::x(Align::w(Meter("L/", self.sampler.input_meter[0]))), + //Fill::x(Align::e(Meter("R/", self.sampler.input_meter[1]))), + //Align::x(ClockView::new(true, &self.player.clock)), + //))) + //} + //fn status (&self) -> impl Content + use<'_> { + //row!( + //self.player.play_status(), + //self.player.next_status(), + //self.editor.clip_status(), + //self.editor.edit_status(), + //) + //} + //fn sample (&self) -> impl Content + use<'_> { + //let note_pt = self.editor.note_point(); + //let sample_h = if self.compact { 0 } else { 5 }; + //Max::y(sample_h, Fill::xy( + //Bsp::a( + //Fill::x(Align::w(Fixed::y(1, self.sampler.status(note_pt)))), + //self.sampler.viewer(note_pt)))) + //} + //fn pool (&self) -> impl Content + use<'_> { + //let w = self.size.w(); + //let pool_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 }; + //Fixed::x(if self.compact { 5 } else { pool_w }, + //PoolView(self.compact, &self.pool)) + //} + //fn sampler (&self) -> impl Content + use<'_> { + //let note_pt = self.editor.note_point(); + //let sampler_w = if self.compact { 4 } else { 40 }; + //let sampler_y = if self.compact { 1 } else { 0 }; + //Fixed::x(sampler_w, Push::y(sampler_y, Fill::y(self.sampler.list(self.compact, &self.editor)))) + //} +//} ///// Status bar for sequencer app //#[derive(Clone)] @@ -577,417 +577,417 @@ impl Groovebox { ////} ////} /////////////////////////////////////////////////////////////////////////////////////////////////// -render!(TuiOut: (self: Arranger) => self.size.of(EdnView::from_source(self, Self::EDN))); -impl EdnViewData for &Arranger { - fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> { - use EdnItem::*; - let tracks_w = self.tracks_with_sizes().last().unwrap().3 as u16; - match item { - Nil => Box::new(()), - Exp(items) => Box::new(EdnView::from_items(*self, items.as_slice())), - Sym(":editor") => (&self.editor).boxed(), - Sym(":pool") => self.pool().boxed(), - Sym(":status") => self.status().boxed(), - Sym(":toolbar") => self.toolbar().boxed(), - Sym(":tracks") => self.track_row(tracks_w).boxed(), - Sym(":scenes") => self.scene_row(tracks_w).boxed(), - Sym(":inputs") => self.input_row(tracks_w).boxed(), - Sym(":outputs") => self.output_row(tracks_w).boxed(), - _ => panic!("no content for {item:?}") - } - } -} -impl Arranger { - const EDN: &'static str = include_str!("../edn/arranger.edn"); - pub const LEFT_SEP: char = '▎'; - pub const TRACK_MIN_WIDTH: usize = 9; +//render!(TuiOut: (self: Arranger) => self.size.of(EdnView::from_source(self, Self::EDN))); +//impl EdnViewData for &Arranger { + //fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> { + //use EdnItem::*; + //let tracks_w = self.tracks_with_sizes().last().unwrap().3 as u16; + //match item { + //Nil => Box::new(()), + //Exp(items) => Box::new(EdnView::from_items(*self, items.as_slice())), + //Sym(":editor") => (&self.editor).boxed(), + //Sym(":pool") => self.pool().boxed(), + //Sym(":status") => self.status().boxed(), + //Sym(":toolbar") => self.toolbar().boxed(), + //Sym(":tracks") => self.track_row(tracks_w).boxed(), + //Sym(":scenes") => self.scene_row(tracks_w).boxed(), + //Sym(":inputs") => self.input_row(tracks_w).boxed(), + //Sym(":outputs") => self.output_row(tracks_w).boxed(), + //_ => panic!("no content for {item:?}") + //} + //} +//} +//impl Arranger { + //const EDN: &'static str = include_str!("../edn/arranger.edn"); + //pub const LEFT_SEP: char = '▎'; + //pub const TRACK_MIN_WIDTH: usize = 9; - fn toolbar (&self) -> impl Content + use<'_> { - Fill::x(Fixed::y(2, Align::x(ClockView::new(true, &self.clock)))) - } - fn pool (&self) -> impl Content + use<'_> { - Align::e(Fixed::x(self.sidebar_w(), PoolView(self.compact, &self.pool))) - } - fn status (&self) -> impl Content + use<'_> { - Bsp::e(self.editor.clip_status(), self.editor.edit_status()) - } - fn is_editing (&self) -> bool { - !self.pool.visible - } - fn sidebar_w (&self) -> u16 { - let w = self.size.w(); - let w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 }; - let w = if self.pool.visible { w } else { 8 }; - w - } - fn editor_w (&self) -> usize { - //self.editor.note_len() / self.editor.note_zoom().get() - (5 + (self.editor.time_len().get() / self.editor.time_zoom().get())) - .min(self.size.w().saturating_sub(20)) - .max(16) - //self.editor.time_axis().get().max(16) - //50 - } - pub fn scenes_with_sizes (&self, h: usize) - -> impl Iterator - { - let mut y = 0; - let editing = self.is_editing(); - let (selected_track, selected_scene) = match self.selected { - ArrangerSelection::Clip(t, s) => (Some(t), Some(s)), - _ => (None, None) - }; - self.scenes.iter().enumerate().map(move|(s, scene)|{ - let active = editing && selected_track.is_some() && selected_scene == Some(s); - let height = if active { 15 } else { h }; - let data = (s, scene, y, y + height); - y += height; - data - }) - } - pub fn tracks_with_sizes (&self) - -> impl Iterator - { - tracks_with_sizes(self.tracks.iter(), match self.selected { - ArrangerSelection::Track(t) if self.is_editing() => Some(t), - ArrangerSelection::Clip(t, _) if self.is_editing() => Some(t), - _ => None - }, self.editor_w()) - } - - fn play_row (&self, tracks_w: u16) -> impl Content + '_ { - let h = 2; - Fixed::y(h, Bsp::e( - Fixed::xy(self.sidebar_w() as u16, h, self.play_header()), - Fill::x(Align::c(Fixed::xy(tracks_w, h, self.play_cells()))) - )) - } - fn play_header (&self) -> BoxThunk { - (||Tui::bold(true, Tui::fg(TuiTheme::g(128), "Playing")).boxed()).into() - } - fn play_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - (move||Align::x(Map::new(||self.tracks_with_sizes(), move|(_, track, x1, x2), i| { - //let color = track.color; - let color: ItemPalette = track.color.dark.into(); - let timebase = self.clock().timebase(); - let value = Tui::fg_bg(color.lightest.rgb, color.base.rgb, - if let Some((_, Some(clip))) = track.player.play_clip().as_ref() { - let length = clip.read().unwrap().length; - let elapsed = track.player.pulses_since_start().unwrap() as usize; - format!("+{:>}", timebase.format_beats_1_short((elapsed % length) as f64)) - } else { - String::new() - }); - let cell = Bsp::s(value, phat_hi(color.dark.rgb, color.darker.rgb)); - Tui::bg(color.base.rgb, map_east(x1 as u16, (x2 - x1) as u16, cell)) - })).boxed()).into() - } - - fn next_row (&self, tracks_w: u16) -> impl Content + '_ { - let h = 2; - Fixed::y(h, Bsp::e( - Fixed::xy(self.sidebar_w() as u16, h, self.next_header()), - Fill::x(Align::c(Fixed::xy(tracks_w, h, self.next_cells()))) - )) - } - fn next_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - (||Tui::bold(true, Tui::fg(TuiTheme::g(128), "Next")).boxed()).into() - } - fn next_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - (move||Align::x(Map::new(||self.tracks_with_sizes(), move|(_, track, x1, x2), i| { - let color: ItemPalette = track.color; - let color: ItemPalette = track.color.dark.into(); - let current = &self.clock().playhead; - let timebase = ¤t.timebase; - let cell = Self::cell(color, Tui::bold(true, { - let mut result = String::new(); - if let Some((t, _)) = track.player.next_clip().as_ref() { - let target = t.pulse.get(); - let current = current.pulse.get(); - if target > current { - result = format!("-{:>}", timebase.format_beats_0_short(target - current)) - } - } - result - })); - let cell = Tui::fg_bg(color.lightest.rgb, color.base.rgb, cell); - let cell = Bsp::s(cell, phat_hi(color.dark.rgb, color.darker.rgb)); - Tui::bg(color.base.rgb, map_east(x1 as u16, (x2 - x1) as u16, cell)) - })).boxed()).into() - } - /// beats until switchover - fn cell_until_next (track: &ArrangerTrack, current: &Arc) - -> Option> - { - let timebase = ¤t.timebase; - let mut result = String::new(); - if let Some((t, _)) = track.player.next_clip().as_ref() { - let target = t.pulse.get(); - let current = current.pulse.get(); - if target > current { - result = format!("-{:>}", timebase.format_beats_0_short(target - current)) - } - } - Some(result) - } - - fn track_row (&self, tracks_w: u16) -> impl Content + '_ { - let h = 3; - let border = |x|x;//Rugged(Style::default().fg(Color::Rgb(0,0,0)).bg(Color::Reset)).enclose2(x); - Fixed::y(h, Bsp::e( - Fixed::xy(self.sidebar_w() as u16, h, self.track_header()), - Fill::x(Align::c(Fixed::xy(tracks_w, h, border(self.track_cells())))) - )) - } - fn track_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - (||Tui::bold(true, Bsp::s( - row!( - Tui::fg(TuiTheme::g(128), "add "), - Tui::fg(TuiTheme::orange(), "t"), - Tui::fg(TuiTheme::g(128), "rack"), - ), - row!( - Tui::fg(TuiTheme::orange(), "a"), - Tui::fg(TuiTheme::g(128), "dd scene"), - ), - ).boxed())).into() - } - fn track_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - let iter = ||self.tracks_with_sizes(); - (move||Align::x(Map::new(iter, move|(_, track, x1, x2), i| { - let name = Push::x(1, &track.name); - let color = track.color; - let fg = color.lightest.rgb; - let bg = color.base.rgb; - let active = self.selected.track() == Some(i); - let bfg = if active { Color::Rgb(255,255,255) } else { Color::Rgb(0,0,0) }; - let border = Style::default().fg(bfg).bg(bg); - Tui::bg(bg, map_east(x1 as u16, (x2 - x1) as u16, - Outer(border).enclose(Tui::fg_bg(fg, bg, Tui::bold(true, Fill::x(Align::x(name))))) - )) - })).boxed()).into() - } - - fn input_row (&self, tracks_w: u16) -> impl Content + '_ { - let h = 2 + self.midi_ins[0].connect.len() as u16; - Fixed::y(h, Bsp::e( - Fixed::xy(self.sidebar_w() as u16, h, self.input_header()), - Fill::x(Align::c(Fixed::xy(tracks_w, h, self.input_cells()))) - )) - } - fn input_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - (||Bsp::s( - Tui::bold(true, row!( - Tui::fg(TuiTheme::g(128), "midi "), - Tui::fg(TuiTheme::orange(), "I"), - Tui::fg(TuiTheme::g(128), "ns"), - )), - Bsp::s( - Fill::x(Tui::bold(true, Tui::fg_bg(TuiTheme::g(224), TuiTheme::g(64), - Align::w(&self.midi_ins[0].name)))), - self.midi_ins.get(0) - .and_then(|midi_in|midi_in.connect.get(0)) - .map(|connect|Fill::x(Align::w( - Tui::bold(false, Tui::fg_bg(TuiTheme::g(224), TuiTheme::g(64), connect.info()))))) - ) - ).boxed()).into() - } - fn input_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - (move||Align::x(Map::new(||self.tracks_with_sizes(), move|(_, track, x1, x2), i| { - let w = (x2 - x1) as u16; - let color: ItemPalette = track.color.dark.into(); - map_east(x1 as u16, w, Fixed::x(w, Self::cell(color, Bsp::n( - Self::rec_mon(color.base.rgb, false, false), - phat_hi(color.base.rgb, color.dark.rgb) - )))) - })).boxed()).into() - } - fn rec_mon (bg: Color, rec: bool, mon: bool) -> impl Content { - row!( - Tui::fg_bg(if rec { Color::Red } else { bg }, bg, "▐"), - Tui::fg_bg(if rec { Color::White } else { Color::Rgb(0,0,0) }, bg, "REC"), - Tui::fg_bg(if rec { Color::White } else { bg }, bg, "▐"), - Tui::fg_bg(if mon { Color::White } else { Color::Rgb(0,0,0) }, bg, "MON"), - Tui::fg_bg(if mon { Color::White } else { bg }, bg, "▌"), - ) - } - - fn output_row (&self, tracks_w: u16) -> impl Content + '_ { - let h = 2 + self.midi_outs[0].connect.len() as u16; - Fixed::y(h, Bsp::e( - Fixed::xy(self.sidebar_w() as u16, h, self.output_header()), - Fill::x(Align::c(Fixed::xy(tracks_w, h, self.output_cells()))) - )) - } - fn output_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - (||Bsp::s( - Tui::bold(true, row!( - Tui::fg(TuiTheme::g(128), "midi "), - Tui::fg(TuiTheme::orange(), "O"), - Tui::fg(TuiTheme::g(128), "uts"), - )), - Bsp::s( - Fill::x(Tui::bold(true, Tui::fg_bg(TuiTheme::g(224), TuiTheme::g(64), - Align::w(&self.midi_outs[0].name)))), - self.midi_outs.get(0) - .and_then(|midi_out|midi_out.connect.get(0)) - .map(|connect|Fill::x(Align::w( - Tui::bold(false, Tui::fg_bg(TuiTheme::g(224), TuiTheme::g(64), connect.info()))))) - ), - ).boxed()).into() - } - fn output_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - (move||Align::x(Map::new(||self.tracks_with_sizes(), move|(_, track, x1, x2), i| { - let w = (x2 - x1) as u16; - let color: ItemPalette = track.color.dark.into(); - map_east(x1 as u16, w, Fixed::x(w, Self::cell(color, Bsp::n( - Self::mute_solo(color.base.rgb, false, false), - phat_hi(color.dark.rgb, color.darker.rgb) - )))) - })).boxed()).into() - } - fn mute_solo (bg: Color, mute: bool, solo: bool) -> impl Content { - row!( - Tui::fg_bg(if mute { Color::White } else { Color::Rgb(0,0,0) }, bg, "MUTE"), - Tui::fg_bg(if mute { Color::White } else { bg }, bg, "▐"), - Tui::fg_bg(if solo { Color::White } else { Color::Rgb(0,0,0) }, bg, "SOLO"), - ) - } - - fn scene_row (&self, tracks_w: u16) -> impl Content + '_ { - let h = (self.size.h() as u16).saturating_sub(8).max(8); - let border = |x|x;//Skinny(Style::default().fg(Color::Rgb(0,0,0)).bg(Color::Reset)).enclose2(x); - Bsp::e( - Tui::bg(Color::Reset, Fixed::xy(self.sidebar_w() as u16, h, self.scene_headers())), - Tui::bg(Color::Reset, Fill::x(Align::c(Fixed::xy(tracks_w, h, border(self.scene_cells()))))) - ) - } - fn scene_headers <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - (||{ - let last_color = Arc::new(RwLock::new(ItemPalette::from(Color::Rgb(0, 0, 0)))); - let selected = self.selected.scene(); - Fill::y(Align::c(Map::new(||self.scenes_with_sizes(2), move|(_, scene, y1, y2), i| { - let h = (y2 - y1) as u16; - let name = format!("🭬{}", &scene.name); - let color = scene.color; - let active = selected == Some(i); - let mid = if active { color.light } else { color.base }; - let top = Some(last_color.read().unwrap().base.rgb); - let cell = phat_sel_3( - active, - Tui::bold(true, name.clone()), - Tui::bold(true, name), - top, - mid.rgb, - Color::Rgb(0, 0, 0) - ); - *last_color.write().unwrap() = color; - map_south(y1 as u16, h + 1, Fixed::y(h + 1, cell)) - }))).boxed() - }).into() - } - fn scene_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - let editing = self.is_editing(); - let tracks = move||self.tracks_with_sizes(); - let scenes = ||self.scenes_with_sizes(2); - let selected_track = self.selected.track(); - let selected_scene = self.selected.scene(); - (move||Fill::y(Align::c(Map::new(tracks, move|(_, track, x1, x2), t| { - let w = (x2 - x1) as u16; - let color: ItemPalette = track.color.dark.into(); - let last_color = Arc::new(RwLock::new(ItemPalette::from(Color::Rgb(0, 0, 0)))); - let cells = Map::new(scenes, move|(_, scene, y1, y2), s| { - let h = (y2 - y1) as u16; - let color = scene.color; - let (name, fg, bg) = if let Some(c) = &scene.clips[t] { - let c = c.read().unwrap(); - (c.name.to_string(), c.color.lightest.rgb, c.color.base.rgb) - } else { - ("⏹ ".to_string(), TuiTheme::g(64), TuiTheme::g(32)) - }; - let last = last_color.read().unwrap().clone(); - let active = editing && selected_scene == Some(s) && selected_track == Some(t); - let editor = Thunk::new(||&self.editor); - let cell = Thunk::new(move||phat_sel_3( - selected_track == Some(t) && selected_scene == Some(s), - Tui::fg(fg, Push::x(1, Tui::bold(true, name.to_string()))), - Tui::fg(fg, Push::x(1, Tui::bold(true, name.to_string()))), - if selected_track == Some(t) && selected_scene.map(|s|s+1) == Some(s) { - None - } else { - Some(bg.into()) - }, - bg.into(), - bg.into(), - )); - let cell = Either(active, editor, cell); - *last_color.write().unwrap() = bg.into(); - map_south( - y1 as u16, - h + 1, - Fill::x(Fixed::y(h + 1, cell)) - ) - }); - let column = Fixed::x(w, Tui::bg(Color::Reset, Align::y(cells)).boxed()); - Fixed::x(w, map_east(x1 as u16, w, column)) - }))).boxed()).into() - } - fn cell_clip <'a> ( - scene: &'a ArrangerScene, index: usize, track: &'a ArrangerTrack, w: u16, h: u16 - ) -> impl Content + use<'a> { - scene.clips.get(index).map(|clip|clip.as_ref().map(|clip|{ - let clip = clip.read().unwrap(); - let mut bg = TuiTheme::border_bg(); - let name = clip.name.to_string(); - let max_w = name.len().min((w as usize).saturating_sub(2)); - let color = clip.color; - bg = color.dark.rgb; - if let Some((_, Some(ref playing))) = track.player.play_clip() { - if *playing.read().unwrap() == *clip { - bg = color.light.rgb - } - }; - Fixed::xy(w, h, &Tui::bg(bg, Push::x(1, Fixed::x(w, &name.as_str()[0..max_w])))); - })) - } - - fn track_column_separators <'a> (&'a self) -> impl Content + 'a { - let scenes_w = 16;//.max(SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16); - let fg = Color::Rgb(64,64,64); - Map::new(move||self.tracks_with_sizes(), move|(_n, _track, x1, x2), _i|{ - Push::x(scenes_w, map_east(x1 as u16, (x2 - x1) as u16, - Fixed::x((x2 - x1) as u16, Tui::fg(fg, RepeatV(&"·"))))) - }) - } - - pub fn track_widths (tracks: &[ArrangerTrack]) -> Vec<(usize, usize)> { - let mut widths = vec![]; - let mut total = 0; - for track in tracks.iter() { - let width = track.width; - widths.push((width, total)); - total += width; - } - widths.push((0, total)); - widths - } - - fn scene_row_sep <'a> (&'a self) -> impl Content + 'a { - let fg = Color::Rgb(255,255,255); - Map::new(move||self.scenes_with_sizes(1), |_, _|"") - //Map(||rows.iter(), |(_n, _scene, y1, _y2), _i| { - //let y = to.area().y() + (y / PPQ) as u16 + 1; - //if y >= to.buffer.area.height { break } - //for x in to.area().x()..to.area().x2().saturating_sub(2) { - ////if x < to.buffer.area.x && y < to.buffer.area.y { - //if let Some(cell) = to.buffer.cell_mut(ratatui::prelude::Position::from((x, y))) { - //cell.modifier = Modifier::UNDERLINED; - //cell.underline_color = fg; - //} - ////} - //} + //fn toolbar (&self) -> impl Content + use<'_> { + //Fill::x(Fixed::y(2, Align::x(ClockView::new(true, &self.clock)))) + //} + //fn pool (&self) -> impl Content + use<'_> { + //Align::e(Fixed::x(self.sidebar_w(), PoolView(self.compact, &self.pool))) + //} + //fn status (&self) -> impl Content + use<'_> { + //Bsp::e(self.editor.clip_status(), self.editor.edit_status()) + //} + //fn is_editing (&self) -> bool { + // !self.pool.visible + //} + //fn sidebar_w (&self) -> u16 { + //let w = self.size.w(); + //let w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 }; + //let w = if self.pool.visible { w } else { 8 }; + //w + //} + //fn editor_w (&self) -> usize { + ////self.editor.note_len() / self.editor.note_zoom().get() + //(5 + (self.editor.time_len().get() / self.editor.time_zoom().get())) + //.min(self.size.w().saturating_sub(20)) + //.max(16) + ////self.editor.time_axis().get().max(16) + ////50 + //} + //pub fn scenes_with_sizes (&self, h: usize) + //-> impl Iterator + //{ + //let mut y = 0; + //let editing = self.is_editing(); + //let (selected_track, selected_scene) = match self.selected { + //ArrangerSelection::Clip(t, s) => (Some(t), Some(s)), + //_ => (None, None) + //}; + //self.scenes.iter().enumerate().map(move|(s, scene)|{ + //let active = editing && selected_track.is_some() && selected_scene == Some(s); + //let height = if active { 15 } else { h }; + //let data = (s, scene, y, y + height); + //y += height; + //data //}) - } + //} + //pub fn tracks_with_sizes (&self) + //-> impl Iterator + //{ + //tracks_with_sizes(self.tracks.iter(), match self.selected { + //ArrangerSelection::Track(t) if self.is_editing() => Some(t), + //ArrangerSelection::Clip(t, _) if self.is_editing() => Some(t), + //_ => None + //}, self.editor_w()) + //} + + //fn play_row (&self, tracks_w: u16) -> impl Content + '_ { + //let h = 2; + //Fixed::y(h, Bsp::e( + //Fixed::xy(self.sidebar_w() as u16, h, self.play_header()), + //Fill::x(Align::c(Fixed::xy(tracks_w, h, self.play_cells()))) + //)) + //} + //fn play_header (&self) -> BoxThunk { + //(||Tui::bold(true, Tui::fg(TuiTheme::g(128), "Playing")).boxed()).into() + //} + //fn play_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + //(move||Align::x(Map::new(||self.tracks_with_sizes(), move|(_, track, x1, x2), i| { + ////let color = track.color; + //let color: ItemPalette = track.color.dark.into(); + //let timebase = self.clock().timebase(); + //let value = Tui::fg_bg(color.lightest.rgb, color.base.rgb, + //if let Some((_, Some(clip))) = track.player.play_clip().as_ref() { + //let length = clip.read().unwrap().length; + //let elapsed = track.player.pulses_since_start().unwrap() as usize; + //format!("+{:>}", timebase.format_beats_1_short((elapsed % length) as f64)) + //} else { + //String::new() + //}); + //let cell = Bsp::s(value, phat_hi(color.dark.rgb, color.darker.rgb)); + //Tui::bg(color.base.rgb, map_east(x1 as u16, (x2 - x1) as u16, cell)) + //})).boxed()).into() + //} + + //fn next_row (&self, tracks_w: u16) -> impl Content + '_ { + //let h = 2; + //Fixed::y(h, Bsp::e( + //Fixed::xy(self.sidebar_w() as u16, h, self.next_header()), + //Fill::x(Align::c(Fixed::xy(tracks_w, h, self.next_cells()))) + //)) + //} + //fn next_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + //(||Tui::bold(true, Tui::fg(TuiTheme::g(128), "Next")).boxed()).into() + //} + //fn next_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + //(move||Align::x(Map::new(||self.tracks_with_sizes(), move|(_, track, x1, x2), i| { + //let color: ItemPalette = track.color; + //let color: ItemPalette = track.color.dark.into(); + //let current = &self.clock().playhead; + //let timebase = ¤t.timebase; + //let cell = Self::cell(color, Tui::bold(true, { + //let mut result = String::new(); + //if let Some((t, _)) = track.player.next_clip().as_ref() { + //let target = t.pulse.get(); + //let current = current.pulse.get(); + //if target > current { + //result = format!("-{:>}", timebase.format_beats_0_short(target - current)) + //} + //} + //result + //})); + //let cell = Tui::fg_bg(color.lightest.rgb, color.base.rgb, cell); + //let cell = Bsp::s(cell, phat_hi(color.dark.rgb, color.darker.rgb)); + //Tui::bg(color.base.rgb, map_east(x1 as u16, (x2 - x1) as u16, cell)) + //})).boxed()).into() + //} + ///// beats until switchover + //fn cell_until_next (track: &ArrangerTrack, current: &Arc) + //-> Option> + //{ + //let timebase = ¤t.timebase; + //let mut result = String::new(); + //if let Some((t, _)) = track.player.next_clip().as_ref() { + //let target = t.pulse.get(); + //let current = current.pulse.get(); + //if target > current { + //result = format!("-{:>}", timebase.format_beats_0_short(target - current)) + //} + //} + //Some(result) + //} + + //fn track_row (&self, tracks_w: u16) -> impl Content + '_ { + //let h = 3; + //let border = |x|x;//Rugged(Style::default().fg(Color::Rgb(0,0,0)).bg(Color::Reset)).enclose2(x); + //Fixed::y(h, Bsp::e( + //Fixed::xy(self.sidebar_w() as u16, h, self.track_header()), + //Fill::x(Align::c(Fixed::xy(tracks_w, h, border(self.track_cells())))) + //)) + //} + //fn track_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + //(||Tui::bold(true, Bsp::s( + //row!( + //Tui::fg(TuiTheme::g(128), "add "), + //Tui::fg(TuiTheme::orange(), "t"), + //Tui::fg(TuiTheme::g(128), "rack"), + //), + //row!( + //Tui::fg(TuiTheme::orange(), "a"), + //Tui::fg(TuiTheme::g(128), "dd scene"), + //), + //).boxed())).into() + //} + //fn track_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + //let iter = ||self.tracks_with_sizes(); + //(move||Align::x(Map::new(iter, move|(_, track, x1, x2), i| { + //let name = Push::x(1, &track.name); + //let color = track.color; + //let fg = color.lightest.rgb; + //let bg = color.base.rgb; + //let active = self.selected.track() == Some(i); + //let bfg = if active { Color::Rgb(255,255,255) } else { Color::Rgb(0,0,0) }; + //let border = Style::default().fg(bfg).bg(bg); + //Tui::bg(bg, map_east(x1 as u16, (x2 - x1) as u16, + //Outer(border).enclose(Tui::fg_bg(fg, bg, Tui::bold(true, Fill::x(Align::x(name))))) + //)) + //})).boxed()).into() + //} + + //fn input_row (&self, tracks_w: u16) -> impl Content + '_ { + //let h = 2 + self.midi_ins[0].connect.len() as u16; + //Fixed::y(h, Bsp::e( + //Fixed::xy(self.sidebar_w() as u16, h, self.input_header()), + //Fill::x(Align::c(Fixed::xy(tracks_w, h, self.input_cells()))) + //)) + //} + //fn input_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + //(||Bsp::s( + //Tui::bold(true, row!( + //Tui::fg(TuiTheme::g(128), "midi "), + //Tui::fg(TuiTheme::orange(), "I"), + //Tui::fg(TuiTheme::g(128), "ns"), + //)), + //Bsp::s( + //Fill::x(Tui::bold(true, Tui::fg_bg(TuiTheme::g(224), TuiTheme::g(64), + //Align::w(&self.midi_ins[0].name)))), + //self.midi_ins.get(0) + //.and_then(|midi_in|midi_in.connect.get(0)) + //.map(|connect|Fill::x(Align::w( + //Tui::bold(false, Tui::fg_bg(TuiTheme::g(224), TuiTheme::g(64), connect.info()))))) + //) + //).boxed()).into() + //} + //fn input_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + //(move||Align::x(Map::new(||self.tracks_with_sizes(), move|(_, track, x1, x2), i| { + //let w = (x2 - x1) as u16; + //let color: ItemPalette = track.color.dark.into(); + //map_east(x1 as u16, w, Fixed::x(w, Self::cell(color, Bsp::n( + //Self::rec_mon(color.base.rgb, false, false), + //phat_hi(color.base.rgb, color.dark.rgb) + //)))) + //})).boxed()).into() + //} + //fn rec_mon (bg: Color, rec: bool, mon: bool) -> impl Content { + //row!( + //Tui::fg_bg(if rec { Color::Red } else { bg }, bg, "▐"), + //Tui::fg_bg(if rec { Color::White } else { Color::Rgb(0,0,0) }, bg, "REC"), + //Tui::fg_bg(if rec { Color::White } else { bg }, bg, "▐"), + //Tui::fg_bg(if mon { Color::White } else { Color::Rgb(0,0,0) }, bg, "MON"), + //Tui::fg_bg(if mon { Color::White } else { bg }, bg, "▌"), + //) + //} + + //fn output_row (&self, tracks_w: u16) -> impl Content + '_ { + //let h = 2 + self.midi_outs[0].connect.len() as u16; + //Fixed::y(h, Bsp::e( + //Fixed::xy(self.sidebar_w() as u16, h, self.output_header()), + //Fill::x(Align::c(Fixed::xy(tracks_w, h, self.output_cells()))) + //)) + //} + //fn output_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + //(||Bsp::s( + //Tui::bold(true, row!( + //Tui::fg(TuiTheme::g(128), "midi "), + //Tui::fg(TuiTheme::orange(), "O"), + //Tui::fg(TuiTheme::g(128), "uts"), + //)), + //Bsp::s( + //Fill::x(Tui::bold(true, Tui::fg_bg(TuiTheme::g(224), TuiTheme::g(64), + //Align::w(&self.midi_outs[0].name)))), + //self.midi_outs.get(0) + //.and_then(|midi_out|midi_out.connect.get(0)) + //.map(|connect|Fill::x(Align::w( + //Tui::bold(false, Tui::fg_bg(TuiTheme::g(224), TuiTheme::g(64), connect.info()))))) + //), + //).boxed()).into() + //} + //fn output_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + //(move||Align::x(Map::new(||self.tracks_with_sizes(), move|(_, track, x1, x2), i| { + //let w = (x2 - x1) as u16; + //let color: ItemPalette = track.color.dark.into(); + //map_east(x1 as u16, w, Fixed::x(w, Self::cell(color, Bsp::n( + //Self::mute_solo(color.base.rgb, false, false), + //phat_hi(color.dark.rgb, color.darker.rgb) + //)))) + //})).boxed()).into() + //} + //fn mute_solo (bg: Color, mute: bool, solo: bool) -> impl Content { + //row!( + //Tui::fg_bg(if mute { Color::White } else { Color::Rgb(0,0,0) }, bg, "MUTE"), + //Tui::fg_bg(if mute { Color::White } else { bg }, bg, "▐"), + //Tui::fg_bg(if solo { Color::White } else { Color::Rgb(0,0,0) }, bg, "SOLO"), + //) + //} + + //fn scene_row (&self, tracks_w: u16) -> impl Content + '_ { + //let h = (self.size.h() as u16).saturating_sub(8).max(8); + //let border = |x|x;//Skinny(Style::default().fg(Color::Rgb(0,0,0)).bg(Color::Reset)).enclose2(x); + //Bsp::e( + //Tui::bg(Color::Reset, Fixed::xy(self.sidebar_w() as u16, h, self.scene_headers())), + //Tui::bg(Color::Reset, Fill::x(Align::c(Fixed::xy(tracks_w, h, border(self.scene_cells()))))) + //) + //} + //fn scene_headers <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + //(||{ + //let last_color = Arc::new(RwLock::new(ItemPalette::from(Color::Rgb(0, 0, 0)))); + //let selected = self.selected.scene(); + //Fill::y(Align::c(Map::new(||self.scenes_with_sizes(2), move|(_, scene, y1, y2), i| { + //let h = (y2 - y1) as u16; + //let name = format!("🭬{}", &scene.name); + //let color = scene.color; + //let active = selected == Some(i); + //let mid = if active { color.light } else { color.base }; + //let top = Some(last_color.read().unwrap().base.rgb); + //let cell = phat_sel_3( + //active, + //Tui::bold(true, name.clone()), + //Tui::bold(true, name), + //top, + //mid.rgb, + //Color::Rgb(0, 0, 0) + //); + //*last_color.write().unwrap() = color; + //map_south(y1 as u16, h + 1, Fixed::y(h + 1, cell)) + //}))).boxed() + //}).into() + //} + //fn scene_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + //let editing = self.is_editing(); + //let tracks = move||self.tracks_with_sizes(); + //let scenes = ||self.scenes_with_sizes(2); + //let selected_track = self.selected.track(); + //let selected_scene = self.selected.scene(); + //(move||Fill::y(Align::c(Map::new(tracks, move|(_, track, x1, x2), t| { + //let w = (x2 - x1) as u16; + //let color: ItemPalette = track.color.dark.into(); + //let last_color = Arc::new(RwLock::new(ItemPalette::from(Color::Rgb(0, 0, 0)))); + //let cells = Map::new(scenes, move|(_, scene, y1, y2), s| { + //let h = (y2 - y1) as u16; + //let color = scene.color; + //let (name, fg, bg) = if let Some(c) = &scene.clips[t] { + //let c = c.read().unwrap(); + //(c.name.to_string(), c.color.lightest.rgb, c.color.base.rgb) + //} else { + //("⏹ ".to_string(), TuiTheme::g(64), TuiTheme::g(32)) + //}; + //let last = last_color.read().unwrap().clone(); + //let active = editing && selected_scene == Some(s) && selected_track == Some(t); + //let editor = Thunk::new(||&self.editor); + //let cell = Thunk::new(move||phat_sel_3( + //selected_track == Some(t) && selected_scene == Some(s), + //Tui::fg(fg, Push::x(1, Tui::bold(true, name.to_string()))), + //Tui::fg(fg, Push::x(1, Tui::bold(true, name.to_string()))), + //if selected_track == Some(t) && selected_scene.map(|s|s+1) == Some(s) { + //None + //} else { + //Some(bg.into()) + //}, + //bg.into(), + //bg.into(), + //)); + //let cell = Either(active, editor, cell); + //*last_color.write().unwrap() = bg.into(); + //map_south( + //y1 as u16, + //h + 1, + //Fill::x(Fixed::y(h + 1, cell)) + //) + //}); + //let column = Fixed::x(w, Tui::bg(Color::Reset, Align::y(cells)).boxed()); + //Fixed::x(w, map_east(x1 as u16, w, column)) + //}))).boxed()).into() + //} + //fn cell_clip <'a> ( + //scene: &'a ArrangerScene, index: usize, track: &'a ArrangerTrack, w: u16, h: u16 + //) -> impl Content + use<'a> { + //scene.clips.get(index).map(|clip|clip.as_ref().map(|clip|{ + //let clip = clip.read().unwrap(); + //let mut bg = TuiTheme::border_bg(); + //let name = clip.name.to_string(); + //let max_w = name.len().min((w as usize).saturating_sub(2)); + //let color = clip.color; + //bg = color.dark.rgb; + //if let Some((_, Some(ref playing))) = track.player.play_clip() { + //if *playing.read().unwrap() == *clip { + //bg = color.light.rgb + //} + //}; + //Fixed::xy(w, h, &Tui::bg(bg, Push::x(1, Fixed::x(w, &name.as_str()[0..max_w])))); + //})) + //} + + //fn track_column_separators <'a> (&'a self) -> impl Content + 'a { + //let scenes_w = 16;//.max(SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16); + //let fg = Color::Rgb(64,64,64); + //Map::new(move||self.tracks_with_sizes(), move|(_n, _track, x1, x2), _i|{ + //Push::x(scenes_w, map_east(x1 as u16, (x2 - x1) as u16, + //Fixed::x((x2 - x1) as u16, Tui::fg(fg, RepeatV(&"·"))))) + //}) + //} + + //pub fn track_widths (tracks: &[ArrangerTrack]) -> Vec<(usize, usize)> { + //let mut widths = vec![]; + //let mut total = 0; + //for track in tracks.iter() { + //let width = track.width; + //widths.push((width, total)); + //total += width; + //} + //widths.push((0, total)); + //widths + //} + + //fn scene_row_sep <'a> (&'a self) -> impl Content + 'a { + //let fg = Color::Rgb(255,255,255); + //Map::new(move||self.scenes_with_sizes(1), |_, _|"") + ////Map(||rows.iter(), |(_n, _scene, y1, _y2), _i| { + ////let y = to.area().y() + (y / PPQ) as u16 + 1; + ////if y >= to.buffer.area.height { break } + ////for x in to.area().x()..to.area().x2().saturating_sub(2) { + //////if x < to.buffer.area.x && y < to.buffer.area.y { + ////if let Some(cell) = to.buffer.cell_mut(ratatui::prelude::Position::from((x, y))) { + ////cell.modifier = Modifier::UNDERLINED; + ////cell.underline_color = fg; + ////} + //////} + ////} + ////}) + //} //pub fn scene_heights (scenes: &[ArrangerScene], factor: usize) -> Vec<(usize, usize)> { //let mut total = 0; @@ -1066,8 +1066,8 @@ impl Arranger { //}) //} - /// A 1-row cell. - fn cell > (color: ItemPalette, field: T) -> impl Content { - Tui::fg_bg(color.lightest.rgb, color.base.rgb, Fixed::y(1, field)) - } -} + ///// A 1-row cell. + //fn cell > (color: ItemPalette, field: T) -> impl Content { + //Tui::fg_bg(color.lightest.rgb, color.base.rgb, Fixed::y(1, field)) + //} +//}