From 30f43eac79de1af6edcb6dacf948ac007f318376 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sat, 25 Jan 2025 22:37:13 +0100 Subject: [PATCH] refactor model into submodules again --- tek/src/model.rs | 157 +--------------------------------------- tek/src/model/scene.rs | 58 +++++++++++++++ tek/src/model/select.rs | 51 +++++++++++++ tek/src/model/track.rs | 49 +++++++++++++ tek/src/view.rs | 47 ++++++------ 5 files changed, 183 insertions(+), 179 deletions(-) create mode 100644 tek/src/model/scene.rs create mode 100644 tek/src/model/select.rs create mode 100644 tek/src/model/track.rs diff --git a/tek/src/model.rs b/tek/src/model.rs index 70e0282e..b26ee351 100644 --- a/tek/src/model.rs +++ b/tek/src/model.rs @@ -1,4 +1,7 @@ use crate::*; +mod scene; pub use self::scene::*; +mod select; pub use self::select::*; +mod track; pub use self::track::*; #[derive(Default, Debug)] pub struct Tek { /// Must not be dropped for the duration of the process pub jack: Jack, @@ -182,162 +185,8 @@ impl Tek { Tui::fg_bg(theme.lightest.rgb, theme.base.rgb, Fixed::y(1, field)) } } -/// Represents the current user selection in the arranger -#[derive(PartialEq, Clone, Copy, Debug, Default)] pub enum Selection { - /// The whole mix is selected - #[default] Mix, - /// A track is selected. - Track(usize), - /// A scene is selected. - Scene(usize), - /// A clip (track × scene) is selected. - Clip(usize, usize), -} -/// Focus identification methods -impl Selection { - fn is_mix (&self) -> bool { matches!(self, Self::Mix) } - fn is_track (&self) -> bool { matches!(self, Self::Track(_)) } - fn is_scene (&self) -> bool { matches!(self, Self::Scene(_)) } - fn is_clip (&self) -> bool { matches!(self, Self::Clip(_, _)) } - pub fn track (&self) -> Option { - use Selection::*; - match self { Clip(t, _) => Some(*t), Track(t) => Some(*t), _ => None } - } - pub fn scene (&self) -> Option { - use Selection::*; - match self { Clip(_, s) => Some(*s), Scene(s) => Some(*s), _ => None } - } - fn description (&self, tracks: &[Track], scenes: &[Scene]) -> Arc { - format!("Selected: {}", match self { - Self::Mix => "Everything".to_string(), - Self::Track(t) => tracks.get(*t).map(|track|format!("T{t}: {}", &track.name)) - .unwrap_or_else(||"T??".into()), - Self::Scene(s) => scenes.get(*s).map(|scene|format!("S{s}: {}", &scene.name)) - .unwrap_or_else(||"S??".into()), - Self::Clip(t, s) => match (tracks.get(*t), scenes.get(*s)) { - (Some(_), Some(scene)) => match scene.clip(*t) { - Some(clip) => format!("T{t} S{s} C{}", &clip.read().unwrap().name), - None => format!("T{t} S{s}: Empty") - }, - _ => format!("T{t} S{s}: Empty"), - } - }).into() - } -} -impl HasSelection for Tek { - fn selected (&self) -> &Selection { &self.selected } - fn selected_mut (&mut self) -> &mut Selection { &mut self.selected } -} -pub trait HasSelection { - fn selected (&self) -> &Selection; - fn selected_mut (&mut self) -> &mut Selection; -} -#[derive(Debug, Default)] pub struct Track { - /// Name of track - pub name: Arc, - /// Preferred width of track column - pub width: usize, - /// Identifying color of track - pub color: ItemPalette, - /// MIDI player state - pub player: MidiPlayer, - /// Device chain - pub devices: Vec>, - /// Inputs of 1st device - pub audio_ins: Vec, - /// Outputs of last device - pub audio_outs: Vec, -} -has_clock!(|self: Track|self.player.clock); -has_player!(|self: Track|self.player); -impl Track { - const MIN_WIDTH: usize = 9; - fn width_inc (&mut self) { self.width += 1; } - fn width_dec (&mut self) { if self.width > Track::MIN_WIDTH { self.width -= 1; } } -} -impl HasTracks for Tek { - fn midi_ins (&self) -> &Vec { &self.midi_ins } - fn midi_outs (&self) -> &Vec { &self.midi_outs } - fn tracks (&self) -> &Vec { &self.tracks } - fn tracks_mut (&mut self) -> &mut Vec { &mut self.tracks } -} -pub trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync { - fn midi_ins (&self) -> &Vec; - fn midi_outs (&self) -> &Vec; - fn tracks (&self) -> &Vec; - fn tracks_mut (&mut self) -> &mut Vec; - fn track_longest (&self) -> usize { - self.tracks().iter().map(|s|s.name.len()).fold(0, usize::max) - } - fn track_next_name (&self) -> Arc { - format!("Track{:02}", self.tracks().len() + 1).into() - } - fn track (&self) -> Option<&Track> { - self.selected().track().and_then(|s|self.tracks().get(s)) - } - fn track_mut (&mut self) -> Option<&mut Track> { - self.selected().track().and_then(|s|self.tracks_mut().get_mut(s)) - } -} pub trait Device: Send + Sync + std::fmt::Debug { fn boxed <'a> (self) -> Box where Self: Sized + 'a { Box::new(self) } } impl Device for Sampler {} impl Device for Plugin {} -#[derive(Debug, Default)] pub struct Scene { - /// Name of scene - pub name: Arc, - /// Clips in scene, one per track - pub clips: Vec>>>, - /// Identifying color of scene - pub color: ItemPalette, -} -impl Scene { - /// Returns the pulse length of the longest clip in the scene - fn pulses (&self) -> usize { - 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 clips in the scene are - /// currently playing on the given collection of tracks. - fn is_playing (&self, tracks: &[Track]) -> bool { - self.clips.iter().any(|clip|clip.is_some()) && self.clips.iter().enumerate() - .all(|(track_index, clip)|match clip { - Some(c) => tracks - .get(track_index) - .map(|track|{ - if let Some((_, Some(clip))) = track.player().play_clip() { - *clip.read().unwrap() == *c.read().unwrap() - } else { - false - } - }) - .unwrap_or(false), - None => true - }) - } - fn clip (&self, index: usize) -> Option<&Arc>> { - match self.clips.get(index) { Some(Some(clip)) => Some(clip), _ => None } - } -} -impl HasScenes for Tek { - fn scenes (&self) -> &Vec { &self.scenes } - fn scenes_mut (&mut self) -> &mut Vec { &mut self.scenes } -} -pub trait HasScenes: HasSelection + HasEditor + Send + Sync { - fn scenes (&self) -> &Vec; - fn scenes_mut (&mut self) -> &mut Vec; - fn scene_longest (&self) -> usize { - self.scenes().iter().map(|s|s.name.len()).fold(0, usize::max) - } - fn scene (&self) -> Option<&Scene> { - self.selected().scene().and_then(|s|self.scenes().get(s)) - } - fn scene_mut (&mut self) -> Option<&mut Scene> { - self.selected().scene().and_then(|s|self.scenes_mut().get_mut(s)) - } - fn scene_del (&mut self, index: usize) { - self.selected().scene().and_then(|s|Some(self.scenes_mut().remove(index))); - } -} diff --git a/tek/src/model/scene.rs b/tek/src/model/scene.rs new file mode 100644 index 00000000..c8bf0e9c --- /dev/null +++ b/tek/src/model/scene.rs @@ -0,0 +1,58 @@ +use crate::*; +pub trait HasScenes: HasSelection + HasEditor + Send + Sync { + fn scenes (&self) -> &Vec; + fn scenes_mut (&mut self) -> &mut Vec; + fn scene_longest (&self) -> usize { + self.scenes().iter().map(|s|s.name.len()).fold(0, usize::max) + } + fn scene (&self) -> Option<&Scene> { + self.selected().scene().and_then(|s|self.scenes().get(s)) + } + fn scene_mut (&mut self) -> Option<&mut Scene> { + self.selected().scene().and_then(|s|self.scenes_mut().get_mut(s)) + } + fn scene_del (&mut self, index: usize) { + self.selected().scene().and_then(|s|Some(self.scenes_mut().remove(index))); + } +} +#[derive(Debug, Default)] pub struct Scene { + /// Name of scene + pub name: Arc, + /// Clips in scene, one per track + pub clips: Vec>>>, + /// Identifying color of scene + pub color: ItemPalette, +} +impl Scene { + /// Returns the pulse length of the longest clip in the scene + fn pulses (&self) -> usize { + 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 clips in the scene are + /// currently playing on the given collection of tracks. + fn is_playing (&self, tracks: &[Track]) -> bool { + self.clips.iter().any(|clip|clip.is_some()) && self.clips.iter().enumerate() + .all(|(track_index, clip)|match clip { + Some(c) => tracks + .get(track_index) + .map(|track|{ + if let Some((_, Some(clip))) = track.player().play_clip() { + *clip.read().unwrap() == *c.read().unwrap() + } else { + false + } + }) + .unwrap_or(false), + None => true + }) + } + pub fn clip (&self, index: usize) -> Option<&Arc>> { + match self.clips.get(index) { Some(Some(clip)) => Some(clip), _ => None } + } +} +impl HasScenes for Tek { + fn scenes (&self) -> &Vec { &self.scenes } + fn scenes_mut (&mut self) -> &mut Vec { &mut self.scenes } +} diff --git a/tek/src/model/select.rs b/tek/src/model/select.rs new file mode 100644 index 00000000..25a298fa --- /dev/null +++ b/tek/src/model/select.rs @@ -0,0 +1,51 @@ +use crate::*; +pub trait HasSelection { + fn selected (&self) -> &Selection; + fn selected_mut (&mut self) -> &mut Selection; +} +/// Represents the current user selection in the arranger +#[derive(PartialEq, Clone, Copy, Debug, Default)] pub enum Selection { + /// The whole mix is selected + #[default] Mix, + /// A track is selected. + Track(usize), + /// A scene is selected. + Scene(usize), + /// A clip (track × scene) is selected. + Clip(usize, usize), +} +/// Focus identification methods +impl Selection { + fn is_mix (&self) -> bool { matches!(self, Self::Mix) } + fn is_track (&self) -> bool { matches!(self, Self::Track(_)) } + fn is_scene (&self) -> bool { matches!(self, Self::Scene(_)) } + fn is_clip (&self) -> bool { matches!(self, Self::Clip(_, _)) } + pub fn track (&self) -> Option { + use Selection::*; + match self { Clip(t, _) => Some(*t), Track(t) => Some(*t), _ => None } + } + pub fn scene (&self) -> Option { + use Selection::*; + match self { Clip(_, s) => Some(*s), Scene(s) => Some(*s), _ => None } + } + fn description (&self, tracks: &[Track], scenes: &[Scene]) -> Arc { + format!("Selected: {}", match self { + Self::Mix => "Everything".to_string(), + Self::Track(t) => tracks.get(*t).map(|track|format!("T{t}: {}", &track.name)) + .unwrap_or_else(||"T??".into()), + Self::Scene(s) => scenes.get(*s).map(|scene|format!("S{s}: {}", &scene.name)) + .unwrap_or_else(||"S??".into()), + Self::Clip(t, s) => match (tracks.get(*t), scenes.get(*s)) { + (Some(_), Some(scene)) => match scene.clip(*t) { + Some(clip) => format!("T{t} S{s} C{}", &clip.read().unwrap().name), + None => format!("T{t} S{s}: Empty") + }, + _ => format!("T{t} S{s}: Empty"), + } + }).into() + } +} +impl HasSelection for Tek { + fn selected (&self) -> &Selection { &self.selected } + fn selected_mut (&mut self) -> &mut Selection { &mut self.selected } +} diff --git a/tek/src/model/track.rs b/tek/src/model/track.rs new file mode 100644 index 00000000..235e8d84 --- /dev/null +++ b/tek/src/model/track.rs @@ -0,0 +1,49 @@ +use crate::*; +pub trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync { + fn midi_ins (&self) -> &Vec; + fn midi_outs (&self) -> &Vec; + fn tracks (&self) -> &Vec; + fn tracks_mut (&mut self) -> &mut Vec; + fn track_longest (&self) -> usize { + self.tracks().iter().map(|s|s.name.len()).fold(0, usize::max) + } + const WIDTH_OFFSET: usize = 1; + fn track_next_name (&self) -> Arc { + format!("Track{:02}", self.tracks().len() + 1).into() + } + fn track (&self) -> Option<&Track> { + self.selected().track().and_then(|s|self.tracks().get(s)) + } + fn track_mut (&mut self) -> Option<&mut Track> { + self.selected().track().and_then(|s|self.tracks_mut().get_mut(s)) + } +} +#[derive(Debug, Default)] pub struct Track { + /// Name of track + pub name: Arc, + /// Preferred width of track column + pub width: usize, + /// Identifying color of track + pub color: ItemPalette, + /// MIDI player state + pub player: MidiPlayer, + /// Device chain + pub devices: Vec>, + /// Inputs of 1st device + pub audio_ins: Vec, + /// Outputs of last device + pub audio_outs: Vec, +} +has_clock!(|self: Track|self.player.clock); +has_player!(|self: Track|self.player); +impl Track { + const MIN_WIDTH: usize = 9; + fn width_inc (&mut self) { self.width += 1; } + fn width_dec (&mut self) { if self.width > Track::MIN_WIDTH { self.width -= 1; } } +} +impl HasTracks for Tek { + fn midi_ins (&self) -> &Vec { &self.midi_ins } + fn midi_outs (&self) -> &Vec { &self.midi_outs } + fn tracks (&self) -> &Vec { &self.tracks } + fn tracks_mut (&mut self) -> &mut Vec { &mut self.tracks } +} diff --git a/tek/src/view.rs b/tek/src/view.rs index cb6ce88e..cb8f9cc0 100644 --- a/tek/src/view.rs +++ b/tek/src/view.rs @@ -67,7 +67,6 @@ view!(TuiOut: |self: Tek| self.size.of(View(self, self.view)); { ":scenes" => self.view_scenes().boxed(), ":status" => self.view_editor().boxed(), ":toolbar" => self.view_clock().boxed(), - ":track-add" => self.view_track_add().boxed(), ":tracks" => self.view_tracks().boxed(), }); provide_num!(u16: |self: Tek| { @@ -291,12 +290,6 @@ impl Tek { let content = Fixed::y(1, f(index, track)); let styled = Tui::fg_bg(track.color.lightest.rgb, track.color.base.rgb, content); map_east(x1 as u16, width, Fixed::x(width, styled)) }) } - fn io_heading <'a, T: PortsSizes<'a>> ( - &'a self, key: &'a str, label: &'a str, count: usize, - fg: Color, bg: Color, iter: impl Fn()->T + Send + Sync + 'a, - ) -> impl Content + 'a { - self.heading(key, label, count, self.io_ports(fg, bg, iter)) - } fn io_ports <'a, T: PortsSizes<'a>> ( &'a self, fg: Color, bg: Color, iter: impl Fn()->T + Send + Sync + 'a ) -> impl Content + 'a { @@ -305,7 +298,7 @@ impl Tek { Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(Bsp::e(" 󰣲 ", name))))), Map::new(||connections.iter(), move|connect, index|map_south(index as u16, 1, Fill::x(Align::w(Tui::bold(false, Tui::fg_bg(fg, bg, - &connect.info[..(self.w_sidebar() as usize).min(connect.info.len())])))))))))} + &connect.info)))))))))} fn io_connections <'a, T: PortsSizes<'a>> ( &'a self, fg: Color, bg: Color, iter: impl Fn()->T + Send + Sync + 'a ) -> impl Content + 'a { @@ -351,10 +344,15 @@ impl Tek { // TRACKS ///////////////////////////////////////////////////////////////////////////////////// fn view_tracks (&self) -> impl Content + use<'_> { - let height = 3; - let heading: ThunkBox<_> = - (move||Tui::bg(Tui::g(32), Fill::x(Align::w(self.view_track_add()))).boxed()).into(); - let content = self.per_track(|t, track|{ + self.row(self.w(), 3, Tui::bg(Tui::g(32), Fill::x(Align::w({ + let data = (self.selected.track().unwrap_or(0), self.tracks().len()); + self.fmtd.write().unwrap().trks.update(Some(data), + rewrite!(buf, "({}/{})", data.0, data.1)); + Bsp::e( + self.button3(" t", "track", self.fmtd.read().unwrap().trks.view.clone()), + self.button2(" T", "add track"), + ) + }))), self.per_track(|t, track|{ let active = self.selected().track() == Some(t+1); let name = &track.name; let fg = track.color.lightest.rgb; @@ -362,14 +360,7 @@ impl Tek { let bg2 = Reset;//if t > 0 { self.tracks()[t - 1].color.base.rgb } else { Reset }; let bfg = if active { Rgb(255,255,255) } else { Rgb(0,0,0) }; Self::wrap(bg, fg, Tui::bold(true, Fill::x(Align::nw(name)))) - }); - self.row(self.w(), height, heading, content) - } - fn view_track_add (&self) -> impl Content + use<'_> { - let data = (self.selected.track().unwrap_or(0), self.tracks().len()); - self.fmtd.write().unwrap().trks.update(Some(data), - rewrite!(buf, "({}/{})", data.0, data.1)); - self.button3(" T", "track", self.fmtd.read().unwrap().trks.view.clone()) + })) } // ACTIVE CLIPS /////////////////////////////////////////////////////////////////////////////// @@ -392,13 +383,16 @@ impl Tek { // INPUTS ///////////////////////////////////////////////////////////////////////////////////// fn view_inputs (&self) -> impl Content + use<'_> { let fg = Tui::g(224); - let key = " I"; + let key = " i"; let label = "midi ins"; let count = self.midi_ins.len(); Bsp::s( Bsp::s( self.row_top(self.w(), 1, - Fill::x(Align::w(self.button3(key, label, format!("{count}")))), + Fill::x(Align::w(Bsp::e( + self.button3(" i", "midi ins", format!("{count}")), + self.button2(" I", "add midi in"), + ))), self.per_track_top(move|t, track|{ let rec = track.player.recording; let mon = track.player.monitoring; @@ -412,7 +406,7 @@ impl Tek { let bg2 = if t > 0 { self.tracks()[t - 1].color.base.rgb } else { Reset }; Self::wrap(bg, fg, Tui::bold(true, Fill::x(Bsp::e( Tui::fg_bg(rec, bg, "Rec "), - Tui::fg_bg(rec, bg, "Mon ")))))})), + Tui::fg_bg(mon, bg, "Mon ")))))})), self.row_top(self.w(), self.h_inputs() - 1, self.io_ports(fg, Tui::g(32), ||self.inputs_sizes()), self.per_track_top(move|t, track|self.io_connections( @@ -428,7 +422,7 @@ impl Tek { // OUTPUTS //////////////////////////////////////////////////////////////////////////////////// fn view_outputs (&self) -> impl Content + use<'_> { let fg = Tui::g(224); - let key = " O"; + let key = " o"; let label = "midi outs"; let count = self.midi_outs.len(); Align::n(Bsp::s( @@ -438,7 +432,10 @@ impl Tek { ), Bsp::s( self.row_top(self.w(), 1, - Fill::x(Align::w(self.button3(key, label, format!("{count}")))), + Fill::x(Align::w(Bsp::e( + self.button3(" o", "midi outs", format!("{count}")), + self.button2(" O", "add midi out"), + ))), self.per_track_top(move|t, track|{ let mute = false; let solo = false;