diff --git a/config/config_arranger.edn b/config/config_arranger.edn index 6249a06a..fb40e339 100644 --- a/config/config_arranger.edn +++ b/config/config_arranger.edn @@ -19,12 +19,9 @@ (view (bsp/a :view-dialog (bsp/s - (fixed/y 8 (bsp/e - (fixed/x 20 (fill/y (align/n (bsp/s :view-status-v - (bsp/s :view-audio-ins-status :view-audio-outs-status))))) - (fill/xy (align/n (bsp/s :view-arranger-track-names - (bsp/s :view-arranger-track-outputs - (bsp/s :view-arranger-track-devices :view-arranger-track-inputs))))))) - (fill/xy (bsp/e - (fixed/x 20 (align/nw :view-arranger-scenes-names)) - :view-arranger-scenes-clips))))) + :view-status-h2 + (bsp/n (fill/x (align/w :view-arranger-inputs)) + (bsp/n (fill/x (align/w :view-arranger-devices)) + (bsp/s (fill/x (align/w :view-arranger-outputs)) + (bsp/s (fill/x (align/w :view-arranger-tracks)) + :view-arranger-scenes))))))) diff --git a/crates/app/src/view.rs b/crates/app/src/view.rs index 8801a26f..fbb50f1b 100644 --- a/crates/app/src/view.rs +++ b/crates/app/src/view.rs @@ -14,12 +14,45 @@ impl App { pub fn view_nil (&self) -> impl Content + use<'_> { "nil" } + pub fn view_status_h2 (&self) -> impl Content + use<'_> { + self.update_clock(); + let theme = self.color; + let playing = self.clock().is_rolling(); + Fixed::y(2, Stack::east(move|add: &mut dyn FnMut(&dyn Render)|{ + add(&Fixed::x(5, Tui::bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) }, + Either::new(false, // TODO + Thunk::new(move||Fixed::x(9, Either::new(playing, + Tui::fg(Rgb(0, 255, 0), " PLAYING "), + Tui::fg(Rgb(255, 128, 0), " STOPPED "))) + ), + Thunk::new(move||Fixed::x(5, Either::new(playing, + Tui::fg(Rgb(0, 255, 0), Bsp::s(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)), + Tui::fg(Rgb(255, 128, 0), Bsp::s(" ▗▄▖ ", " ▝▀▘ ",)))) + ) + ) + ))); + { + let cache = self.view_cache.read().unwrap(); + add(&Align::w(Bsp::s( + FieldH(theme, "Beat", cache.beat.view.clone()), + FieldH(theme, "Time", cache.time.view.clone()), + ))); + add(&Align::w(Bsp::s( + Fill::x(Align::w(FieldH(theme, "BPM", cache.bpm.view.clone()))), + Fill::x(Align::w(FieldH(theme, "SR ", cache.sr.view.clone()))), + ))); + add(&FieldH(theme, "Buf", + Bsp::e(cache.buf.view.clone(), Bsp::e(" = ", cache.lat.view.clone())) + )); + } + })) + } pub fn view_status_v (&self) -> impl Content + use<'_> { self.update_clock(); let cache = self.view_cache.read().unwrap(); let theme = self.color; let playing = self.clock().is_rolling(); - Tui::bg(theme.darkest.rgb, Fixed::xy(20, 6, Outer(true, Style::default().fg(Tui::g(96))).enclose( + Tui::bg(theme.darker.rgb, Fixed::xy(20, 5, Outer(true, Style::default().fg(Tui::g(96))).enclose( col!( Fill::x(Align::w(Bsp::e( Align::w(Tui::bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) }, @@ -41,8 +74,7 @@ impl App { ))), Fill::x(Align::w(FieldH(theme, "BPM", cache.bpm.view.clone()))), Fill::x(Align::w(FieldH(theme, "SR ", cache.sr.view.clone()))), - Fill::x(Align::w(FieldH(theme, "Buf", cache.buf.view.clone()))), - Fill::x(Align::w(FieldH(theme, "Lat", cache.lat.view.clone()))), + Fill::x(Align::w(FieldH(theme, "Buf", Bsp::e(cache.buf.view.clone(), Bsp::e(" = ", cache.lat.view.clone()))))), )))) } pub fn view_status (&self) -> impl Content + use<'_> { @@ -76,11 +108,35 @@ impl App { pub fn view_audio_outs_status (&self) -> impl Content + use<'_> { self.project.view_audio_outs_status(self.color) } - pub fn view_arranger_scenes_names (&self) -> impl Content + use<'_> { - self.project.view_scenes_names() + pub fn view_arranger_scenes (&self) -> impl Content + use<'_> { + Bsp::e( + Fixed::x(20, Align::nw(self.project.view_scenes_names())), + Bsp::w(self.view_pool(), self.project.view_scenes_clips()), + ) } - pub fn view_arranger_scenes_clips (&self) -> impl Content + use<'_> { - self.project.view_scenes_clips() + pub fn view_arranger_inputs <'a> (&'a self) -> impl Content + use<'a> { + Fixed::y(3, Bsp::e( + Fixed::x(20, Tui::bg(self.color.darker.rgb, Fill::xy(""))), + self.project.view_track_inputs(self.color) + )) + } + pub fn view_arranger_outputs <'a> (&'a self) -> impl Content + use<'a> { + Fixed::y(3, Bsp::e( + Fixed::x(20, Tui::bg(self.color.darker.rgb, Fill::xy(""))), + self.project.view_track_outputs(self.color) + )) + } + pub fn view_arranger_devices <'a> (&'a self) -> impl Content + use<'a> { + Fixed::y(3, Bsp::e( + Fixed::x(20, Tui::bg(self.color.darker.rgb, Fill::xy(""))), + self.project.view_track_devices(self.color) + )) + } + pub fn view_arranger_tracks <'a> (&'a self) -> impl Content + use<'a> { + Fixed::y(2, Bsp::e( + Fixed::x(20, Tui::bg(self.color.darker.rgb, Fill::xy(""))), + self.project.view_track_names(self.color) + )) } pub fn view_arranger_track_names (&self) -> impl Content + use<'_> { self.project.view_track_names(self.color) @@ -103,8 +159,7 @@ impl App { Fixed::x(20, Tui::bg(self.color.darkest.rgb, col!(Tui::bold(true, "Devices"), "[d] Select", "[D] Add"))), Fixed::y(max_devices + 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), + ||self.project.tracks_with_sizes_scrolled(), move|(index, track, x1, x2): (usize, &'a Track, usize, usize), _| Push::x(x2 as u16, Fixed::xy(track.width as u16, max_devices + 1, Align::nw(Map::south(1, ||track.devices.iter(), @@ -248,17 +303,6 @@ impl ScenesView for App { } } -pub(crate) fn heading <'a> ( - key: &'a str, - label: &'a str, - count: usize, - content: impl Content + Send + Sync + 'a, - editing: bool, -) -> impl Content + 'a { - let count = format!("{count}"); - Fill::xy(Align::w(Bsp::s(Fill::x(Align::w(button_3(key, label, count, editing))), content))) -} - /// Clear a pre-allocated buffer, then write into it. #[macro_export] macro_rules! rewrite { ($buf:ident, $($rest:tt)*) => { |$buf,_,_|{ $buf.clear(); write!($buf, $($rest)*) } } diff --git a/crates/device/src/arranger/arranger_model.rs b/crates/device/src/arranger/arranger_model.rs index d0a4698e..a1717211 100644 --- a/crates/device/src/arranger/arranger_model.rs +++ b/crates/device/src/arranger/arranger_model.rs @@ -64,11 +64,6 @@ impl Arrangement { pub fn w_sidebar (&self, is_editing: bool) -> u16 { self.w() / if is_editing { 16 } else { 8 } as u16 } - /// Width taken by all tracks. - pub fn w_tracks (&self) -> u16 { - self.tracks_with_sizes(&self.selection(), None).last() - .map(|(_, _, _, x)|x as u16).unwrap_or(0) - } /// Width available to display tracks. pub fn w_tracks_area (&self, is_editing: bool) -> u16 { self.w().saturating_sub(self.w_sidebar(is_editing)) diff --git a/crates/device/src/arranger/arranger_tracks.rs b/crates/device/src/arranger/arranger_tracks.rs index 08d9a121..aeb7ed91 100644 --- a/crates/device/src/arranger/arranger_tracks.rs +++ b/crates/device/src/arranger/arranger_tracks.rs @@ -44,25 +44,6 @@ 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<'_> - { - let mut x = 0; - let active_track = if let Some(width) = editor_width { - selection.track() - } else { - None - }; - self.tracks().iter().enumerate().map(move |(index, track)|{ - let width = active_track - .and_then(|_|editor_width) - .unwrap_or(track.width.max(8)); - let data = (index, track, x, x + width); - x += width + Self::TRACK_SPACING; - data - }) - } /// Spacing between tracks. const TRACK_SPACING: usize = 0; } diff --git a/crates/device/src/arranger/arranger_view.rs b/crates/device/src/arranger/arranger_view.rs index 0488314b..4beee14d 100644 --- a/crates/device/src/arranger/arranger_view.rs +++ b/crates/device/src/arranger/arranger_view.rs @@ -9,8 +9,27 @@ pub trait TracksView: fn tracks_width_available (&self) -> u16 { (self.width() as u16).saturating_sub(40) } + /// Iterate over tracks with their corresponding sizes. + fn tracks_with_sizes (&self) -> impl TracksSizes<'_> { + let editor_width = self.editor().map(|e|e.width()); + let active_track = if let Some(width) = editor_width { + self.selection().track() + } else { + None + }; + let mut x = 0; + self.tracks().iter().enumerate().map(move |(index, track)|{ + let width = active_track + .and_then(|_|editor_width) + .unwrap_or(track.width.max(8)); + let data = (index, track, x, x + width); + x += width + Self::TRACK_SPACING; + data + }) + } fn tracks_with_sizes_scrolled <'t> (&'t self) -> impl TracksSizes<'t> { - self.tracks_with_sizes(&self.selection(), self.is_editing().then_some(20/*FIXME*/)) + self.tracks_with_sizes() + .skip(self.track_scroll()) .map_while(move|(t, track, x1, x2)| ((x2 as u16) < self.tracks_width_available()) .then_some((t, track, x1, x2))) @@ -23,10 +42,7 @@ pub trait TracksView: fn view_track_names (&self, theme: ItemTheme) -> impl Content { let content = Fixed::y(1, Align::w(Tui::bg(theme.darker.rgb, Align::w(Fill::x( Stack::east(move|add: &mut dyn FnMut(&dyn Render)|{ - for (index, track, x1, x2) in self - .tracks_with_sizes(&self.selection(), None) - .skip(self.track_scroll()) - { + for (index, track, x1, x2) in self.tracks_with_sizes() { add(&Fixed::x(track.width as u16, Tui::bg(if self.selection().track() == Some(index) { track.color.light.rgb @@ -53,11 +69,8 @@ pub trait TracksView: let content = Align::w(Fixed::y(1 + max_outputs*2, Tui::bg(theme.darker.rgb, Align::w(Fill::x( Stack::east(move|add: &mut dyn FnMut(&dyn Render)|{ - for (index, track, x1, x2) in self - .tracks_with_sizes(&self.selection(), None) - .skip(self.track_scroll()) - { - add(&Fixed::x(track.width as u16, Align::nw(Bsp::s( + for (index, track, x1, x2) in self.tracks_with_sizes() { + add(&Fixed::x(track.width as u16, Align::nw(Bsp::n( Tui::bg(if self.selection().track() == Some(index) { track.color.light.rgb } else { @@ -89,10 +102,7 @@ pub trait TracksView: )), Fixed::y(h, Tui::bg(theme.darker.rgb, Align::w(Fill::x(Stack::east( move|add: &mut dyn FnMut(&dyn Render)|{ - for (index, track, x1, x2) in self - .tracks_with_sizes(&self.selection(), None) - .skip(self.track_scroll()) - { + for (index, track, x1, x2) in self.tracks_with_sizes() { add(&Fixed::xy(track.width as u16, h + 1, Tui::bg(track.color.dark.rgb, Align::nw(Map::south(1, move||0..h, |_, index|format!("·d{index}: {}", "--------")))))); @@ -106,12 +116,9 @@ pub trait TracksView: } let content = Tui::bg(theme.darker.rgb, Align::w(Fill::x( Stack::east(move|add: &mut dyn FnMut(&dyn Render)|{ - for (index, track, x1, x2) in self - .tracks_with_sizes(&self.selection(), None) - .skip(self.track_scroll()) - { + for (index, track, x1, x2) in self.tracks_with_sizes() { add(&Fixed::xy(track.width as u16, h + 1, - Align::nw(Bsp::n( + Align::nw(Bsp::s( Tui::bg(track.color.base.rgb, Fill::x(Align::w(format!("·mon ·rec ·dub")))), Map::south(1, ||track.sequencer.midi_ins.iter(), @@ -176,10 +183,7 @@ pub trait ClipsView: TracksView + ScenesView + Send + Sync { -> impl Content + 'a { Fill::xy(Stack::::east(move|column: &mut dyn FnMut(&dyn Render)|{ - for (track_index, track, _, _) in self - .tracks_with_sizes(&self.selection(), None) - .skip(self.track_scroll()) - { + for (track_index, track, _, _) in self.tracks_with_sizes() { //column(&Fixed::x(5, Fill::xy(Tui::bg(Green, "kyp")))); column(&Fixed::x( if self.selection().track() == Some(track_index)