diff --git a/crates/app/src/view.rs b/crates/app/src/view.rs index 42153fd8..180e4f4c 100644 --- a/crates/app/src/view.rs +++ b/crates/app/src/view.rs @@ -88,112 +88,17 @@ impl App { pub fn view_arranger_scenes_clips (&self) -> impl Content + use<'_> { self.scenes_clips(&self.editor) } - pub fn view_arranger_scene_names <'a> (&'a self) -> impl Content + use<'a> { - let h = self.project.scenes.len() as u16 * 2; - let bg = self.color.darker.rgb; - Fixed::y(h, Tui::bg(bg, Align::w(Fill::x(Map::new( - ||self.project.scenes.iter().skip(self.project.scene_scroll), - move|scene: &Scene, index| - Push::y(index as u16 * 2u16, Fixed::xy(20, 2, - Tui::bg(scene.color.dark.rgb, Align::nw(Bsp::e( - format!(" {index:2} "), - Tui::fg(Rgb(255, 255, 255), - Tui::bold(true, format!("{}", scene.name))))))))))))) - } - pub fn view_arranger_scene_clips <'a> (&'a self) -> impl Content + use<'a> { - let h = self.project.scenes.len() as u16 * 2; - let bg = self.color.darker.rgb; - Fixed::y(h, Tui::bg(bg, Align::w(Fill::x(Map::new( - ||self.project.scenes.iter().skip(self.project.scene_scroll), - move|scene: &'a Scene, index1| - Push::y(index1 as u16 * 2u16, Fixed::xy(20, 2, - Map::new( - move||scene.clips.iter().skip(self.project.track_scroll), - move|clip: &'a Option>>, index2|{ - let (theme, text) = if let Some(clip) = clip { - let clip = clip.read().unwrap(); - (clip.color, clip.name.clone()) - } else { - (scene.color, Default::default()) - }; - Push::x(index2 as u16 * 14, Tui::bg(theme.dark.rgb, Bsp::e( - format!(" {index1:2} {index2:2} "), - Tui::fg(Rgb(255, 255, 255), - Tui::bold(true, format!("{}", text)))))) - })))))))) - } pub fn view_arranger_track_names (&self) -> impl Content + use<'_> { - let mut max_outputs = 0u16; - for track in self.project.tracks.iter() { - max_outputs = max_outputs.max(track.sequencer.midi_outs.len() as u16); - } - Bsp::w( - Fixed::x(20, Tui::bg(self.color.darkest.rgb, - col!(Tui::bold(true, "[t]rack"), "[T] Add"))), - Align::w(Fixed::y(max_outputs + 1, Tui::bg(self.color.darker.rgb, Align::w(Fill::x(Map::new( - ||self.project.tracks_with_sizes(&self.project.selection, None) - .skip(self.project.track_scroll), - move|(index, track, x1, x2): (usize, &Track, usize, usize), _| - Push::x(index as u16 * 14, Fixed::xy(track.width as u16, max_outputs + 1, - Tui::bg(track.color.dark.rgb, Align::nw(Bsp::s( - Tui::fg(Rgb(255, 255, 255), Tui::bold(true, format!("{}", track.name))), - format!("{index} {x1} {x2}"))))))))))))) + self.project.view_track_names(self.color) } pub fn view_arranger_track_outputs <'a> (&'a self) -> impl Content + use<'a> { - let mut max_outputs = 0u16; - for track in self.project.tracks.iter() { - max_outputs = max_outputs.max(track.sequencer.midi_outs.len() as u16); - } - Bsp::w( - Fixed::x(20, Tui::bg(self.color.darkest.rgb, - col!(Tui::bold(true, "[o]utput"), "[O] Add"))), - Align::w(Fixed::y(max_outputs + 1, Tui::bg(self.color.darker.rgb, Align::w(Fill::x(Map::new( - ||self.project.tracks_with_sizes(&self.project.selection, None) - .skip(self.project.track_scroll), - move|(index, track, x1, x2): (usize, &'a Track, usize, usize), _| - Push::x(x2 as u16, Tui::bg(track.color.dark.rgb, Fixed::xy( - track.width as u16, - max_outputs + 1, - Align::nw(Bsp::s( - format!("[mut] [sol]"), - Map::south(1, ||track.sequencer.midi_outs.iter(), - |port, index|Tui::fg(Rgb(255, 255, 255), - format!("{index}: {}", port.name()))))))))))))))) + self.project.view_track_outputs(self.color) } pub fn view_arranger_track_inputs <'a> (&'a self) -> impl Content + use<'a> { - let mut max_inputs = 0u16; - for track in self.project.tracks.iter() { - max_inputs = max_inputs.max(track.sequencer.midi_ins.len() as u16); - } - Bsp::w( - Fixed::x(20, Tui::bg(self.color.darkest.rgb, - col!(Tui::bold(true, "[i]nputs"), "[I] Add"))), - Fill::x(Align::w(Fixed::y(max_inputs + 1, Tui::bg(self.color.darker.rgb, Align::w(Fill::x(Map::new( - ||self.project.tracks_with_sizes(&self.project.selection, None) - .skip(self.project.track_scroll), - move|(index, track, x1, x2): (usize, &'a Track, usize, usize), _| - Push::x(x2 as u16, Fixed::xy(track.width as u16, max_inputs + 1, - Tui::bg(track.color.dark.rgb, Align::nw(Bsp::s( - format!("[rec] [mon]"), - Map::south(1, ||track.sequencer.midi_ins.iter(), - |port, index|Tui::fg(Rgb(255, 255, 255), - format!("{index}: {}", port.name())))))))))))))))) + self.project.view_track_inputs(self.color) } pub fn view_arranger_track_devices <'a> (&'a self) -> impl Content + use<'a> { - let mut max_devices = 2u16; - for track in self.project.tracks.iter() { - max_devices = max_devices.max(track.devices.len() as u16); - } - Bsp::w( - Fixed::x(20, Tui::bg(self.color.darkest.rgb, - col!(Tui::bold(true, "[d]evice"), "[D] Add"))), - Fixed::y(max_devices, Tui::bg(self.color.darker.rgb, Align::w(Fill::x(Map::new( - ||self.project.tracks_with_sizes(&self.project.selection, None) - .skip(self.project.track_scroll), - move|(index, track, x1, x2): (usize, &'a Track, usize, usize), _| - Push::x(x2 as u16, Fixed::xy(track.width as u16, max_devices + 1, - Tui::bg(track.color.dark.rgb, Align::nw(Map::south(1, move||0..max_devices, - |_, index|format!("{index}: {}", "--------")))))))))))) + self.project.view_track_devices(self.color) } pub fn view_arranger_track_scenes <'a> (&'a self) -> impl Content + use<'a> { let mut max_devices = 0u16; diff --git a/crates/device/src/arranger/arranger_scenes.rs b/crates/device/src/arranger/arranger_scenes.rs index f14d686f..c61e5b1d 100644 --- a/crates/device/src/arranger/arranger_scenes.rs +++ b/crates/device/src/arranger/arranger_scenes.rs @@ -1,8 +1,43 @@ use crate::*; +impl> + Send + Sync> HasScenes for T {} + +pub trait HasScenes: Has> + Send + Sync { + fn scenes (&self) -> &Vec { + Has::>::get(self) + } + fn scenes_mut (&mut self) -> &mut Vec { + Has::>::get_mut(self) + } + fn scenes_with_sizes ( + &self, + editing: bool, + height: usize, + larger: usize, + selected_track: Option, + selected_scene: Option, + ) -> impl ScenesSizes<'_> { + let mut y = 0; + self.scenes().iter().enumerate().map(move|(s, scene)|{ + let active = editing && selected_track.is_some() && selected_scene == Some(s); + let height = if active { larger } else { height }; + let data = (s, scene, y, y + height); + y += height; + data + }) + } + /// Generate the default name for a new scene + fn scene_default_name (&self) -> Arc { + format!("Sc{:3>}", self.scenes().len() + 1).into() + } + fn scene_longest_name (&self) -> usize { + self.scenes().iter().map(|s|s.name.len()).fold(0, usize::max) + } +} + pub type SceneWith<'a, T: Send + Sync> = (usize, &'a Scene, usize, usize, T); -pub trait ArrangerSceneRows: Send + Sync { +pub trait ScenesView: Send + Sync { /// Default scene height. const H_SCENE: usize = 2; /// Default editor height. @@ -49,6 +84,18 @@ pub trait ArrangerSceneRows: Send + Sync { }))) })) } + fn scenes_names_2 (&self, theme: ItemTheme) -> impl Content { + let h = self.scenes().len() as u16 * 2; + let bg = theme.darker.rgb; + Fixed::y(h, Tui::bg(bg, Align::w(Fill::x(Map::new( + ||self.scenes().iter().skip(self.project.scene_scroll), + move|scene: &Scene, index| + Push::y(index as u16 * 2u16, Fixed::xy(20, 2, + Tui::bg(scene.color.dark.rgb, Align::nw(Bsp::e( + format!(" {index:2} "), + Tui::fg(Rgb(255, 255, 255), + Tui::bold(true, format!("{}", scene.name))))))))))))) + } fn scenes_with_prev_color (&self) -> impl Iterator>> + Send + Sync { self.scenes_iter().map(|(s, scene, y1, y2)|(s, scene, y1, y2, (s>0).then(||self.arrangement().scenes()[s-1].color))) @@ -102,6 +149,28 @@ pub trait ArrangerSceneRows: Send + Sync { ))) }))) } + fn scenes_clips_2 <'a> (&'a self, theme: ItemTheme) -> impl Content + use<'a> { + let h = self.scenes().len() as u16 * 2; + let bg = theme.darker.rgb; + Fixed::y(h, Tui::bg(bg, Align::w(Fill::x(Map::new( + ||self.scenes().iter().skip(self.project.scene_scroll), + move|scene: &'a Scene, index1| + Push::y(index1 as u16 * 2u16, Fixed::xy(20, 2, + Map::new( + move||scene.clips.iter().skip(self.project.track_scroll), + move|clip: &'a Option>>, index2|{ + let (theme, text) = if let Some(clip) = clip { + let clip = clip.read().unwrap(); + (clip.color, clip.name.clone()) + } else { + (scene.color, Default::default()) + }; + Push::x(index2 as u16 * 14, Tui::bg(theme.dark.rgb, Bsp::e( + format!(" {index1:2} {index2:2} "), + Tui::fg(Rgb(255, 255, 255), + Tui::bold(true, format!("{}", text)))))) + })))))))) + } fn scenes_with_clip (&self, track_index: usize) -> impl Iterator>> + Send + Sync { self.scenes_iter().map(move|(s, scene, y1, y2)|(s, scene, y1, y2, (s>0).then(||self.arrangement().scenes()[s-1].clips[track_index].as_ref() @@ -130,7 +199,7 @@ pub trait ArrangerSceneRows: Send + Sync { } } -impl<'a> ArrangerSceneRows for ArrangerView<'a> { +impl<'a> ScenesView for ArrangerView<'a> { fn arrangement (&self) -> &Arrangement { self.arrangement } @@ -157,41 +226,6 @@ impl<'a> ArrangerSceneRows for ArrangerView<'a> { } } -impl> + Send + Sync> HasScenes for T {} - -pub trait HasScenes: Has> + Send + Sync { - fn scenes (&self) -> &Vec { - Has::>::get(self) - } - fn scenes_mut (&mut self) -> &mut Vec { - Has::>::get_mut(self) - } - fn scenes_with_sizes ( - &self, - editing: bool, - height: usize, - larger: usize, - selected_track: Option, - selected_scene: Option, - ) -> impl ScenesSizes<'_> { - let mut y = 0; - self.scenes().iter().enumerate().map(move|(s, scene)|{ - let active = editing && selected_track.is_some() && selected_scene == Some(s); - let height = if active { larger } else { height }; - let data = (s, scene, y, y + height); - y += height; - data - }) - } - /// Generate the default name for a new scene - fn scene_default_name (&self) -> Arc { - format!("Sc{:3>}", self.scenes().len() + 1).into() - } - fn scene_longest_name (&self) -> usize { - self.scenes().iter().map(|s|s.name.len()).fold(0, usize::max) - } -} - impl AddScene for T {} pub trait AddScene: HasScenes + HasTracks { diff --git a/crates/device/src/arranger/arranger_tracks.rs b/crates/device/src/arranger/arranger_tracks.rs index 471c0b88..7a2d4097 100644 --- a/crates/device/src/arranger/arranger_tracks.rs +++ b/crates/device/src/arranger/arranger_tracks.rs @@ -45,11 +45,9 @@ pub trait HasTracks: Has> + Send + Sync { } } /// Iterate over tracks with their corresponding sizes. - fn tracks_with_sizes ( - &self, - selection: &Selection, - editor_width: Option - ) -> impl TracksSizes<'_> { + fn tracks_with_sizes (&self, selection: &Selection, editor_width: Option) + -> impl TracksSizes<'_> + { let mut x = 0; let active_track = if let Some(width) = editor_width { selection.track() @@ -69,6 +67,92 @@ pub trait HasTracks: Has> + Send + Sync { const TRACK_SPACING: usize = 0; } +pub trait HasTracksScrolled: HasTracks { + fn tracks_scroll (&self) -> usize; +} +impl HasTracksScrolled for Arrangement { + fn tracks_scroll (&self) -> usize { self.tracks_scroll } +} +impl<'a> HasTracksScrolled for ArrangerView<'a> { + fn tracks_scroll (&self) -> usize { self.arrangement.tracks_scroll() } +} + +impl TracksView for T {} + +pub trait TracksView: HasTracks + HasSelection { + fn view_track_names (&self, theme: ItemTheme) -> impl Content + use<'_> { + let mut max_outputs = 0u16; + for track in self.tracks().iter() { + max_outputs = max_outputs.max(track.sequencer.midi_outs.len() as u16); + } + Bsp::w( + Fixed::x(20, Tui::bg(theme.darkest.rgb, + col!(Tui::bold(true, "[t]rack"), "[T] Add"))), + Align::w(Fixed::y(max_outputs + 1, Tui::bg(theme.darker.rgb, + Align::w(Fill::x(Map::new( + ||self.tracks_with_sizes(&self.project.selection, None).skip(self.track_scroll()), + move|(index, track, x1, x2): (usize, &Track, usize, usize), _| + Push::x(index as u16 * 14, Fixed::xy(track.width as u16, max_outputs + 1, + Tui::bg(track.color.dark.rgb, Align::nw(Bsp::s(Tui::fg( + Rgb(255, 255, 255), + Tui::bold(true, format!("{}", track.name)) + ), format!("{index} {x1} {x2}"))))))))))))) + } + fn view_track_outputs <'a> (&'a self, theme: ItemTheme) -> impl Content + use<'a> { + let mut max_outputs = 0u16; + for track in self.tracks().iter() { + max_outputs = max_outputs.max(track.sequencer.midi_outs.len() as u16); + } + Bsp::w( + Fixed::x(20, Tui::bg(theme.darkest.rgb, + col!(Tui::bold(true, "[o]utput"), "[O] Add"))), + Align::w(Fixed::y(max_outputs + 1, Tui::bg(theme.darker.rgb, Align::w(Fill::x(Map::new( + ||self.tracks_with_sizes(&self.project.selection, None).skip(self.track_scroll()), + move|(index, track, x1, x2): (usize, &'a Track, usize, usize), _| + Push::x(x2 as u16, Tui::bg(track.color.dark.rgb, Fixed::xy( + track.width as u16, + max_outputs + 1, + Align::nw(Bsp::s( + format!("[mut] [sol]"), + Map::south(1, ||track.sequencer.midi_outs.iter(), + |port, index|Tui::fg(Rgb(255, 255, 255), + format!("{index}: {}", port.name()))))))))))))))) + } + fn view_track_inputs <'a> (&'a self, theme: ItemTheme) -> impl Content + use<'a> { + let mut max_inputs = 0u16; + for track in self.tracks().iter() { + max_inputs = max_inputs.max(track.sequencer.midi_ins.len() as u16); + } + Bsp::w( + Fixed::x(20, Tui::bg(theme.darkest.rgb, + col!(Tui::bold(true, "[i]nputs"), "[I] Add"))), + Fill::x(Align::w(Fixed::y(max_inputs + 1, Tui::bg(theme.darker.rgb, Align::w(Fill::x(Map::new( + ||self.tracks_with_sizes(&self.project.selection, None).skip(self.track_scroll()), + move|(index, track, x1, x2): (usize, &'a Track, usize, usize), _| + Push::x(x2 as u16, Fixed::xy(track.width as u16, max_inputs + 1, + Tui::bg(track.color.dark.rgb, Align::nw(Bsp::s( + format!("[rec] [mon]"), + Map::south(1, ||track.sequencer.midi_ins.iter(), + |port, index|Tui::fg(Rgb(255, 255, 255), + format!("{index}: {}", port.name())))))))))))))))) + } + fn view_track_devices <'a> (&'a self, theme: ItemTheme) -> impl Content + use<'a> { + let mut max_devices = 2u16; + for track in self.tracks().iter() { + max_devices = max_devices.max(track.devices.len() as u16); + } + Bsp::w( + Fixed::x(20, Tui::bg(theme.darkest.rgb, + col!(Tui::bold(true, "[d]evice"), "[D] Add"))), + Fixed::y(max_devices, Tui::bg(theme.darker.rgb, Align::w(Fill::x(Map::new( + ||self.tracks_with_sizes(&self.project.selection, None).skip(self.track_scroll()), + move|(index, track, x1, x2): (usize, &'a Track, usize, usize), _| + Push::x(x2 as u16, Fixed::xy(track.width as u16, max_devices + 1, + Tui::bg(track.color.dark.rgb, Align::nw(Map::south(1, move||0..max_devices, + |_, index|format!("{index}: {}", "--------")))))))))))) + } +} + impl Arrangement { /// Add multiple tracks pub fn tracks_add ( diff --git a/crates/device/src/sampler/sampler_view.rs b/crates/device/src/sampler/sampler_view.rs index bfb344ff..7b21233d 100644 --- a/crates/device/src/sampler/sampler_view.rs +++ b/crates/device/src/sampler/sampler_view.rs @@ -119,13 +119,13 @@ impl Sampler { } pub fn view_sample_status (&self, note_pt: usize) -> impl Content + use<'_> { - Fixed::x(20, Outer(true, Style::default().fg(Tui::g(96))).enclose(draw_info_v(if let Some((_, sample)) = &self.recording { + Fixed::x(20, draw_info_v(if let Some((_, sample)) = &self.recording { Some(sample) } else if let Some(sample) = &self.mapped[note_pt] { Some(sample) } else { None - }))) + })) } pub fn view_status (&self, index: usize) -> impl Content {