diff --git a/app/src/view.rs b/app/src/view.rs index 0eef3365..db9e9182 100644 --- a/app/src/view.rs +++ b/app/src/view.rs @@ -44,7 +44,7 @@ pub(crate) struct ArrangerView<'a> { is_editing: bool, - width_total: u16, + width: u16, width_mid: u16, width_side: u16, @@ -54,12 +54,14 @@ pub(crate) struct ArrangerView<'a> { outputs_count: usize, outputs_height: u16, - scene_last: u16, - scene_scroll: u16, + scene_last: usize, + scene_count: usize, + scene_scroll: Fill>, scene_selected: Option, scenes_height: u16, - track_scroll: u16, + track_scroll: Fill>, + track_count: usize, track_selected: Option, tracks_height: u16, } @@ -68,7 +70,9 @@ impl<'a> Content for ArrangerView<'a> { fn content (&self) -> impl Render { Bsp::s(self.inputs(), Bsp::s(self.tracks(), - Bsp::n(self.outputs(), self.scenes()))) + Bsp::n(self.outputs(), Tui::bg(Reset, Bsp::s(self.track_scroll, + Bsp::e(self.scene_scroll, + Fixed::y(self.scenes_height,self.scenes()))))))) } } @@ -78,7 +82,7 @@ impl<'a> ArrangerView<'a> { app, is_editing: app.is_editing(), - width_total: app.w(), + width: app.w(), width_mid: app.w_tracks_area(), width_side: app.w_sidebar(), @@ -90,6 +94,7 @@ impl<'a> ArrangerView<'a> { scenes_height: app.h_scenes(), scene_selected: app.selected().scene(), + scene_count: app.scenes.len(), scene_last: app.scenes.len().saturating_sub(1), scene_scroll: Fill::y(Fixed::x(1, ScrollbarV { offset: app.scene_scroll, @@ -98,8 +103,9 @@ impl<'a> ArrangerView<'a> { })), tracks_height: app.h_tracks_area(), + track_count: app.tracks.len(), track_selected: app.selected().track(), - track_scroll: Fill::y(Fixed::x(1, ScrollbarV { + track_scroll: Fill::y(Fixed::x(1, ScrollbarH { offset: app.scene_scroll, length: app.h_tracks_area() as usize, total: app.h_scenes() as usize, diff --git a/app/src/view/view_layout.rs b/app/src/view/view_layout.rs index d671933c..27d9b255 100644 --- a/app/src/view/view_layout.rs +++ b/app/src/view/view_layout.rs @@ -140,12 +140,12 @@ pub(crate) fn io_conns <'a, T: PortsSizes<'a>> ( Fill::x(Align::w(Tui::bold(false, wrap(bg, fg, Fill::x("")))))))))) } -pub(crate) fn per_track_top <'a, T: Content + 'a> ( +pub(crate) fn per_track_top <'a, T: Content + 'a, U: TracksSizes<'a>> ( width: u16, - tracks: impl TracksSizes<'a>, + tracks: impl Fn() -> U + Send + Sync + 'a, callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a ) -> impl Content + 'a { - Align::x(Tui::bg(Reset, Map::new(||tracks, move|(index, track, x1, x2), _|{ + Align::x(Tui::bg(Reset, Map::new(tracks, move|(index, track, x1, x2), _|{ let width = (x2 - x1) as u16; map_east(x1 as u16, width, Fixed::x(width, Tui::fg_bg( track.color.lightest.rgb, diff --git a/app/src/view/view_ports.rs b/app/src/view/view_ports.rs index 672e4fc9..98ebc468 100644 --- a/app/src/view/view_ports.rs +++ b/app/src/view/view_ports.rs @@ -3,210 +3,132 @@ use crate::*; impl<'a> ArrangerView<'a> { /// Render input matrix. pub(crate) fn inputs (&'a self) -> impl Content + 'a { - inputs_layout( - input_intos( + Bsp::s(Bsp::s(self.input_routes(), self.input_ports()), self.input_intos()) + } + + fn input_routes (&'a self) -> impl Content + 'a { + Tryptich::top(self.inputs_height) + .left(self.width_side, io_ports(Tui::g(224), Tui::g(32), ||self.inputs_with_sizes())) + .middle(self.width_mid, per_track_top( self.width_mid, - self.width_side, - self.tracks_with_sizes(), - ), - input_routes( - self.width_mid, - self.width_side, - self.tracks_with_sizes(), - self.inputs_height, - self.inputs_with_sizes(), - ), - input_ports( - self.width_mid, - self.width_side, - self.tracks_with_sizes(), - self.inputs_count, - self.track_selected, - self.is_editing - ) - ) + ||self.tracks_with_sizes(), + move|_, &Track { color, .. }|{ + io_conns(color.dark.rgb, color.darker.rgb, ||self.inputs_with_sizes()) + })) + } + + fn input_ports (&'a self) -> impl Content + 'a { + Tryptich::top(1) + .left(self.width_side, + button_3("i", "midi ins", format!("{}", self.outputs_count), self.is_editing)) + .right(self.width_side, + button_2("I", "add midi in", self.is_editing)) + .middle(self.width_mid, + per_track_top( + self.width_mid, + ||self.tracks_with_sizes(), + move|t, track|{ + let rec = track.player.recording; + let mon = track.player.monitoring; + let rec = if rec { White } else { track.color.darkest.rgb }; + let mon = if mon { White } else { track.color.darkest.rgb }; + let bg = if self.track_selected == Some(t) { + track.color.light.rgb + } else { + track.color.base.rgb + }; + //let bg2 = if t > 0 { track.color.base.rgb } else { Reset }; + wrap(bg, Tui::g(224), Tui::bold(true, Fill::x(Bsp::e( + Tui::fg_bg(rec, bg, "Rec "), + Tui::fg_bg(mon, bg, "Mon "), + )))) + })) + } + + fn input_intos (&'a self) -> impl Content + 'a { + Tryptich::top(2) + .left(self.width_side, + Bsp::s(Align::e("Input:"), Align::e("Into:"))) + .middle(self.width_mid, + per_track_top( + self.width_mid, + ||self.tracks_with_sizes(), + |_, _|{ + Tui::bg(Reset, Align::c(Bsp::s(OctaveVertical::default(), " ------ "))) + })) } /// Render output matrix. pub(crate) fn outputs (&'a self) -> impl Content + 'a { - outputs_layout( - output_nexts( - self.width_mid, - self.width_side, - self.tracks_sizes + Align::n(Bsp::s( + Bsp::s( + self.output_nexts(), + self.output_froms(), ), - output_froms( - self.width_mid, - self.width_side, - self.tracks_sizes - ), - output_conns( - self.width_mid, - self.width_side, - self.tracks_sizes, - self.outputs_height, - self.outputs_sizes - ), - output_ports( - self.width_mid, - self.width_side, - self.tracks_sizes, - self.outputs_count, - self.track_selected, - self.is_editing + Bsp::s( + self.output_ports(), + self.output_conns(), ) - ) + )) + } + + fn output_nexts (&'a self) -> impl Content + 'a { + Tryptich::top(2) + .left(self.width_side, Align::ne("From:")) + .middle(self.width_mid, per_track_top( + self.width_mid, + ||self.tracks_with_sizes(), + |_, _|{ + Tui::bg(Reset, Align::c(Bsp::s(" ------ ", OctaveVertical::default()))) + })) + } + fn output_froms (&'a self) -> impl Content + 'a { + Tryptich::top(2) + .left(self.width_side, Align::ne("Next:")) + .middle(self.width_mid, per_track_top( + self.width_mid, + ||self.tracks_with_sizes(), + |t, track|Either( + track.player.next_clip.is_some(), + Thunk::new(||Tui::bg(Reset, format!("{:?}", + track.player.next_clip.as_ref() + .map(|(moment, clip)|clip.as_ref() + .map(|clip|clip.read().unwrap().name.clone())) + .flatten().as_ref()))), + Thunk::new(||Tui::bg(Reset, " ------ ")) + ))) + } + fn output_ports (&'a self) -> impl Content + 'a { + Tryptich::top(1) + .left(self.width_side, + button_3("o", "midi outs", format!("{}", self.outputs_count), self.is_editing)) + .right(self.width_side, + button_2("O", "add midi out", self.is_editing)) + .middle(self.width_mid, + per_track_top( + self.width_mid, + ||self.tracks_with_sizes(), + move|i, t|{ + let mute = false; + let solo = false; + let mute = if mute { White } else { t.color.darkest.rgb }; + let solo = if solo { White } else { t.color.darkest.rgb }; + let bg_1 = if self.track_selected == Some(i) { t.color.light.rgb } else { t.color.base.rgb }; + let bg_2 = if i > 0 { t.color.base.rgb } else { Reset }; + let mute = Tui::fg_bg(mute, bg_1, "Play "); + let solo = Tui::fg_bg(solo, bg_1, "Solo "); + wrap(bg_1, Tui::g(224), Tui::bold(true, Fill::x(Bsp::e(mute, solo)))) + })) + } + fn output_conns (&'a self) -> impl Content + 'a { + Tryptich::top(self.outputs_height) + .left(self.width_side, + io_ports(Tui::g(224), Tui::g(32), ||self.outputs_with_sizes())) + .middle(self.width_mid, per_track_top( + self.width_mid, + ||self.tracks_with_sizes(), + |_, t|{ + io_conns(t.color.dark.rgb, t.color.darker.rgb, ||self.outputs_with_sizes()) + })) } } - -fn inputs_layout <'a> ( - intos: impl Content, - routes: impl Content, - ports: impl Content, -) -> impl Content + 'a { - Bsp::s(Bsp::s(routes, ports), intos) -} - -fn input_intos <'a> ( - w_mid: u16, - w_side: u16, - tracks: impl TracksSizes<'a>, -) -> impl Content + use<'a> { - Tryptich::top(2) - .left(w_side, Bsp::s(Align::e("Input:"), Align::e("Into:"))) - .middle(w_mid, per_track_top(w_mid, tracks, |_, _|Tui::bg(Reset, - Align::c(Bsp::s(OctaveVertical::default(), " ------ "))))) -} - -fn input_routes <'a> ( - w_mid: u16, - w_side: u16, - tracks: impl TracksSizes<'a>, - height: u16, - inputs: impl PortsSizes<'a> -) -> impl Content { - Tryptich::top(height) - .left(w_side, io_ports(Tui::g(224), Tui::g(32), inputs)) - .middle(w_mid, per_track_top(w_mid, tracks, move|_, &Track { color, .. }|{ - io_conns(color.dark.rgb, color.darker.rgb, inputs) - })) -} - -fn input_ports <'a> ( - w_mid: u16, - w_side: u16, - tracks: impl TracksSizes<'a>, - port_count: usize, - selected_track: Option, - editing: bool, -) -> impl Content + 'a { - Tryptich::top(1) - .left(w_side, button_3("i", "midi ins", format!("{port_count}"), editing)) - .right(w_side, button_2("I", "add midi in", editing)) - .middle(w_mid, per_track_top(w_mid, tracks, move|t, track|{ - let rec = track.player.recording; - let mon = track.player.monitoring; - let rec = if rec { White } else { track.color.darkest.rgb }; - let mon = if mon { White } else { track.color.darkest.rgb }; - let bg = if selected_track == Some(t) { - track.color.light.rgb - } else { - track.color.base.rgb - }; - //let bg2 = if t > 0 { track.color.base.rgb } else { Reset }; - wrap(bg, Tui::g(224), Tui::bold(true, Fill::x(Bsp::e( - Tui::fg_bg(rec, bg, "Rec "), - Tui::fg_bg(mon, bg, "Mon "), - )))) - })) -} - -fn outputs_layout <'a, T, U, V, W> ( - nexts: T, froms: U, conns: V, outputs: W, -) -> impl Content + use<'a, T, U, V, W> where - T: Content + 'a, - U: Content + 'a, - V: Content + 'a, - W: Content + 'a, -{ - Align::n(Bsp::s(Bsp::s(nexts, froms), Bsp::s(outputs, conns))) -} - -fn output_nexts <'a, T: TracksSizes<'a>, U: Fn()->T + Send + Sync + 'a> ( - w_mid: u16, w_side: u16, tracks: U -) -> impl Content + use<'a, T, U> { - Tryptich::top(2) - .left(w_side, Align::ne("From:")) - .middle(w_mid, per_track_top(w_mid, tracks, |_, _|Tui::bg(Reset, - Align::c(Bsp::s(" ------ ", OctaveVertical::default()))))) -} - -fn output_froms <'a, T: TracksSizes<'a>, U: Fn()->T + Send + Sync + 'a> ( - w_mid: u16, w_side: u16, tracks: U -) -> impl Content + use<'a, T, U> { - Tryptich::top(2) - .left(w_side, Align::ne("Next:")) - .middle(w_mid, per_track_top(w_mid, tracks, |t, track|Either( - track.player.next_clip.is_some(), - Thunk::new(||Tui::bg(Reset, format!("{:?}", - track.player.next_clip.as_ref() - .map(|(moment, clip)|clip.as_ref() - .map(|clip|clip.read().unwrap().name.clone())) - .flatten().as_ref()))), - Thunk::new(||Tui::bg(Reset, " ------ "))))) -} - -fn output_conns <'a, T, U, V, W> ( - w_mid: u16, w_side: u16, tracks: U, height: u16, outputs: W -) -> impl Content + use<'a, T, U, V, W> where - T: PortsSizes<'a>, U: Fn()->T + Send + Sync + 'a, - V: PortsSizes<'a>, W: Fn()->T + Send + Sync + 'a, -{ - Tryptich::top(height) - .left(w_side, io_ports(Tui::g(224), Tui::g(32), outputs)) - .middle(w_mid, per_track_top(w_mid, tracks, |_, t|{ - io_conns(t.color.dark.rgb, t.color.darker.rgb, outputs) - })) -} - -fn output_ports <'a, T, U> ( - w_mid: u16, - w_side: u16, - tracks_sizes: U, - port_count: usize, - selected_track: Option, - editing: bool, -) -> impl Content + use<'a, T, U> where - T: TracksSizes<'a>, U: Fn()->T + Send + Sync + 'a -{ - Tryptich::top(1) - .left(w_side, button_3("o", "midi outs", format!("{port_count}"), editing)) - .right(w_side, button_2("O", "add midi out", editing)) - .middle(w_mid, per_track_top(w_mid, tracks_sizes, move|i, t|{ - let mute = false; - let solo = false; - let mute = if mute { White } else { t.color.darkest.rgb }; - let solo = if solo { White } else { t.color.darkest.rgb }; - let bg_1 = if selected_track == Some(i) { t.color.light.rgb } else { t.color.base.rgb }; - let bg_2 = if i > 0 { t.color.base.rgb } else { Reset }; - let mute = Tui::fg_bg(mute, bg_1, "Play "); - let solo = Tui::fg_bg(solo, bg_1, "Solo "); - wrap(bg_1, Tui::g(224), Tui::bold(true, Fill::x(Bsp::e(mute, solo)))) - })) -} - -#[cfg(test)] #[test] fn test_inputs () { - let mut output = TuiOut::default(); - output.area[2] = 9; - output.area[3] = 9; - - Content::render(&inputs(), &mut output); -} - -#[cfg(test)] #[test] fn test_outputs () { - let mut output = TuiOut::default(); - output.area[2] = 9; - output.area[3] = 9; - - Content::render(&outputs(), &mut output); -} diff --git a/app/src/view/view_track.rs b/app/src/view/view_track.rs index 78508551..0f3ad74b 100644 --- a/app/src/view/view_track.rs +++ b/app/src/view/view_track.rs @@ -3,48 +3,59 @@ use crate::*; impl<'a> ArrangerView<'a> { /// Render track headers - pub(crate) fn tracks (&self) -> impl Content + 'a { + pub(crate) fn tracks (&'a self) -> impl Content + 'a { Tryptich::center(1) .left(self.width_side, - button_3("t", "track", self.track_count, self.is_editing)) + button_3("t", "track", format!("{}", self.track_count), self.is_editing)) .right(self.width_side, button_2("T", "add track", self.is_editing)) - .middle(self.width_middle, + .middle(self.width_mid, per_track( - self.width_middle, - self.tracks_sizes, + self.width_mid, + ||self.tracks_with_sizes(), |t, track|view_track_header(t, track, self.track_selected == Some(t)))) } /// Render scenes with clips - pub(crate) fn scenes (&self) -> impl Content + 'a { - Tui::bg(Reset, Bsp::s(self.track_scroll, - Bsp::e(self.scene_scroll, - Fixed::y(self.scenes_height, - Tryptich::center(self.scenes_height) - .left(self.width_side, - Map::new(self.scenes_with_scene_colors(), self.scene_name())) - .middle(self.width_mid, - per_track(self.width_mid, self.track_sizes(), self.scene_track())))))) + pub(crate) fn scenes (&'a self) -> impl Content + 'a { + Tryptich::center(self.scenes_height) + .left(self.width_side, Map::new( + ||self.scenes_with_scene_colors(), + move|(index, scene, y1, y2, previous): SceneWithColor, _|{ + let offset = y1 as u16; + let height = (1 + y2 - y1) as u16; + let is_last = self.scene_last == index; + view_scene_name( + self.width, height, offset, + index, scene, previous, is_last, self.scene_selected + ) + })) + .middle(self.width_mid, per_track( + self.width_mid, + ||self.tracks_with_sizes(), + move|track_index, track|Map::new( + ||self.scenes_with_track_colors(), + move|(scene_index, scene, y1, y2, prev_scene): SceneWithColor, _| + view_scene_clip( + self.width_mid, + (1 + y2 - y1) as u16, + y1 as u16, + scene, + prev_scene, + scene_index, + track_index, + self.is_editing, + self.track_selected == Some(track_index), + self.scene_selected, + self.scene_last == scene_index, + &self.app.editor + )))) } - fn scene_add (&self) -> impl Content + 'a { - let editing = self.is_editing(); - let data = (self.selected().scene().unwrap_or(0), self.scenes().len()); - self.fmtd.write().unwrap().scns.update(Some(data), rewrite!(buf, "({}/{})", data.0, data.1)); - button_3("S", "add scene", self.fmtd.read().unwrap().scns.view.clone(), editing) - } - - fn scene_name (&self) -> impl Content + 'a { - let width = self.width_full; - let scene_last = self.scene_last; - let scene_selected = self.scene_selected; - move|(index, scene, y1, y2, previous): SceneWithColor, _|{ - let offset = y1 as u16; - let height = (1 + y2 - y1) as u16; - let is_last = scene_last == index; - view_scene_name(width, height, offset, index, scene, previous, is_last, scene_selected) - } + fn scene_add (&'a self) -> impl Content + 'a { + let data = (self.scene_selected.unwrap_or(0), self.scene_count); + self.app.fmtd.write().unwrap().scns.update(Some(data), rewrite!(buf, "({}/{})", data.0, data.1)); + button_3("S", "add scene", self.app.fmtd.read().unwrap().scns.view.clone(), self.is_editing) } fn scenes_with_scene_colors (&'a self) -> impl ScenesColors<'a> { @@ -69,25 +80,6 @@ impl<'a> ArrangerView<'a> { self.app.fmtd.read().unwrap().trks.view.clone() } - fn scene_track (&self) -> impl Content + 'a { - move|track_index, track|Map::new( - self.scenes_with_track_colors(), - move|(scene_index, scene, y1, y2, prev_scene): SceneWithColor, _|view_scene_clip( - self.width_mid, - (1 + y2 - y1) as u16, - y1 as u16, - scene, - prev_scene, - scene_index, - track_index, - self.is_editing, - self.track_selected == Some(track_index), - self.scene_selected, - self.scene_last == scene_index, - self.app.editor - )) - } - } fn view_track_header <'a> ( @@ -128,7 +120,7 @@ pub(crate) fn view_scene_clip <'a> ( width: u16, height: u16, offset: u16, - scene: &Scene, + scene: &'a Scene, prev: Option, scene_index: usize, track_index: usize, @@ -136,7 +128,7 @@ pub(crate) fn view_scene_clip <'a> ( same_track: bool, scene_selected: Option, scene_is_last: bool, - editor: &Option, + editor: &'a Option, ) -> impl Content + use<'a> { let (name, fg, bg) = if let Some(clip) = &scene.clips[track_index] { let clip = clip.read().unwrap(); @@ -183,19 +175,3 @@ pub(crate) fn view_scene_cell <'a> ( ) } } - -#[cfg(test)] #[test] fn test_view_scene () { - let mut output = TuiOut::default(); - output.area[2] = 9; - output.area[3] = 9; - - Content::render(&view_scenes(), &mut output); -} - -#[cfg(test)] #[test] fn test_view_track () { - let mut output = TuiOut::default(); - output.area[2] = 9; - output.area[3] = 9; - - Content::render(&view_tracks(), &mut output); -}