diff --git a/src/arranger/arranger_tui.rs b/src/arranger/arranger_tui.rs index 4d9e019a..98a353fa 100644 --- a/src/arranger/arranger_tui.rs +++ b/src/arranger/arranger_tui.rs @@ -2,81 +2,69 @@ use crate::*; pub(crate) const HEADER_H: u16 = 0; // 5 pub(crate) const SCENES_W_OFFSET: u16 = 0; render!(TuiOut: (self: Arranger) => { - let scenes = &self.scenes; - let scenes_w = 16.max(SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16); - let scene_heights = Arranger::scene_heights(scenes, 1); - let border_style_1 = Style::default().fg(Color::Rgb(48,48,48)); - let border_style_2 = Style::default().fg(Color::Rgb(72,72,72)); - let toolbar = |x|Bsp::s(self.toolbar_view(), x); - let status = |x|Bsp::n(self.status_view(), x); - let pool = |x|Bsp::w(self.pool_view(), x); - let editing = |x|Bsp::n(self.editor_status_view(), x); - let editor = Fixed::y(20, Outer(border_style_2).enclose(&self.editor)); - let arrrrrr = Fixed::y(20, Map::new( - move||self.grid_rows(), - move|(height, header, cells), index|map_south( - index as u16, height, Fill::x(Align::w(Bsp::e( - Fixed::x(scenes_w, header), - cells)))))); + let toolbar = |x|Bsp::s(self.toolbar_view(), x); + let status = |x|Bsp::n(self.status_view(), x); + let pool = |x|Bsp::w(self.pool_view(), x); + let editing = |x|Bsp::n(self.editor_status_view(), x); + let enclosed = |x|Outer(Style::default().fg(Color::Rgb(72,72,72))).enclose(x); + let editor = Fixed::y(20, enclosed(&self.editor)); + let scenes_w = 16;//.max(SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16); + let arrrrrr = Fixed::y(32, Map::new( + move||[ + (0, 2, self.output_row_header(), self.output_row_cells()), + (2, 2, self.elapsed_row_header(), self.elapsed_row_cells()), + (4, 2, self.next_row_header(), self.next_row_cells()), + (6, 3, self.track_row_header(), self.track_row_cells()), + (9, 20, self.scene_row_headers(), self.scene_row_cells()), + (29, 2, self.input_row_header(), self.input_row_cells()), + ].into_iter(), + move|(y, h, header, cells), index|map_south(y, h, Fill::x(Align::w(Bsp::e( + Fixed::xy(scenes_w, h, header), + Fixed::xy(self.tracks.len() as u16*6, h, cells) + )))))); + self.size.of(toolbar(status(pool(editing(Bsp::n(editor, arrrrrr)))))) + }); -pub struct ArrangerVClips<'a> { - size: &'a Measure, - scenes: &'a Vec, - tracks: &'a Vec, - rows: Vec<(usize, usize)>, -} -impl<'a> ArrangerVClips<'a> { - pub fn new (state: &'a Arranger, zoom: usize) -> Self { - Self { - size: &state.size, - tracks: &state.tracks, - scenes: &state.scenes, - rows: Arranger::scene_heights(&state.scenes, zoom), - } - } -} impl Arranger { pub const LEFT_SEP: char = '▎'; pub const TRACK_MIN_WIDTH: usize = 4; - fn grid_rows <'a> (&'a self) -> impl Iterator, BoxThunk<'a, TuiOut>)> + 'a { - [ - (2, self.output_row_header(), self.output_row_cells()), - (2, self.elapsed_row_header(), self.elapsed_row_cells()), - (2, self.next_row_header(), self.next_row_cells()), - (2, self.track_row_header(), self.track_row_cells()), - (2, self.input_row_header(), self.input_row_cells()), - (2 * (self.scenes.len() as u16 + 1), self.scene_row_headers(), self.scene_row_cells()), - ].into_iter() - } - /// 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 phat line + fn phat_lo (fg: Color, bg: Color) -> impl Content { + Fixed::y(1, Tui::fg_bg(fg, bg, RepeatH(&"▄"))) + } + + fn phat_hi (fg: Color, bg: Color) -> impl Content { + Fixed::y(1, Tui::fg_bg(fg, bg, RepeatH(&"▀"))) + } + /// A cell that is 3-row on its own, but stacks, giving (N+1)*2 rows per N cells. fn phat_cell > (color: ItemPalette, last: ItemPalette, field: T) -> impl Content { Bsp::s( - Fixed::y(1, Tui::fg_bg(color.base.rgb, last.base.rgb, RepeatH(&"▄"))), - Bsp::s( + Self::phat_lo(color.base.rgb, last.base.rgb), + Bsp::n( + Self::phat_hi(color.base.rgb, last.base.rgb), Fixed::y(1, Fill::x(Tui::fg_bg(color.lightest.rgb, color.base.rgb, field))), - Fixed::y(1, Tui::fg_bg(color.base.rgb, Color::Reset, RepeatH(&"▀"))), ) ) } - fn track_row_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - (||Tui::bold(true, Tui::fg(TuiTheme::g(128), "Track")).boxed()).into() + fn output_row_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + (||Tui::bold(true, Tui::fg_bg(TuiTheme::g(0), TuiTheme::g(200), "[ ] Out 1: NI")).boxed()).into() } - fn track_row_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - let scenes_w = SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16; - (move||Fixed::y(1, Map::new(||self.tracks_with_widths(), move|(_, track, x1, x2), i| { - let color = track.color(); - Push::x(scenes_w, Tui::bg(color.base.rgb, map_east(x1 as u16, (x2 - x1) as u16, - Tui::fg_bg(color.lightest.rgb, color.base.rgb, - Self::cell(color, Tui::bold(true, format!("{}", track.name.read().unwrap()))))))) + fn output_row_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + //let scenes_w = 16;//.max(SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16); + (move||Fixed::y(2, Map::new(||self.tracks_with_widths(), move|(_, track, x1, x2), i| { + let w = (x2 - x1) as u16; + let color: ItemPalette = track.color().dark.into(); + let cell = Bsp::s(format!("MutSol"), Self::phat_hi(color.dark.rgb, color.darker.rgb)); + map_east(x1 as u16, w, Fixed::x(w, Self::cell(color, cell))) })).boxed()).into() } @@ -84,11 +72,22 @@ impl Arranger { (||Tui::bold(true, Tui::fg(TuiTheme::g(128), "Playing")).boxed()).into() } fn elapsed_row_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - let scenes_w = SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16; - (move||Fixed::y(1, Map::new(||self.tracks_with_widths(), move|(_, track, x1, x2), i| { - let color = track.color(); - Push::x(scenes_w, Tui::bg(color.base.rgb, map_east(x1 as u16, (x2 - x1) as u16, - Tui::fg_bg(color.lightest.rgb, color.base.rgb, Self::cell_elapsed(track, self.clock().timebase()))))) + (move||Fixed::y(2, Map::new(||self.tracks_with_widths(), move|(_, track, x1, x2), i| { + let color = track.color(); + let color: ItemPalette = track.color().dark.into(); + let timebase = self.clock().timebase(); + let elapsed = { + let mut result = String::new(); + if let Some((_, Some(phrase))) = track.player.play_phrase().as_ref() { + let length = phrase.read().unwrap().length; + let elapsed = track.player.pulses_since_start().unwrap() as usize; + result = format!("+{:>}", timebase.format_beats_1_short((elapsed % length) as f64)) + } + result + }; + let value = Tui::fg_bg(color.lightest.rgb, color.base.rgb, elapsed); + let cell = Bsp::s(value, Self::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() } @@ -96,11 +95,25 @@ impl Arranger { (||Tui::bold(true, Tui::fg(TuiTheme::g(128), "Next")).boxed()).into() } fn next_row_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - let scenes_w = SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16; - (move||Fixed::y(1, Map::new(||self.tracks_with_widths(), move|(_, track, x1, x2), i| { + (move||Fixed::y(2, Map::new(||self.tracks_with_widths(), move|(_, track, x1, x2), i| { let color: ItemPalette = track.color(); - Push::x(scenes_w, Tui::bg(color.base.rgb, map_east(x1 as u16, (x2 - x1) as u16, - Tui::fg_bg(color.lightest.rgb, color.base.rgb, Self::cell(color, Tui::bold(true, Self::cell_until_next(track, &self.clock().playhead))))))) + let color: ItemPalette = track.color().dark.into(); + let until_next = Self::cell(color, Tui::bold(true, Self::cell_until_next(track, &self.clock().playhead))); + let value = Tui::fg_bg(color.lightest.rgb, color.base.rgb, until_next); + let cell = Bsp::s(value, Self::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 track_row_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + (||Tui::bold(true, Tui::fg(TuiTheme::g(128), "Track")).boxed()).into() + } + fn track_row_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + (move||Fixed::y(2, Map::new(||self.tracks_with_widths(), move|(_, track, x1, x2), i| { + let color = track.color(); + Tui::bg(color.base.rgb, map_east(x1 as u16, (x2 - x1) as u16, + Tui::fg_bg(color.lightest.rgb, color.base.rgb, + Self::phat_cell(color, color.darkest.rgb.into(), Tui::bold(true, format!("{}", track.name.read().unwrap())))))) })).boxed()).into() } @@ -108,47 +121,25 @@ impl Arranger { (||Fill::x(Tui::bold(true, Tui::fg_bg(TuiTheme::g(0), TuiTheme::g(200), "[ ] In 1: Korg"))).boxed()).into() } fn input_row_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - let scenes_w = SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16; - (move||Fixed::y(1, Map::new(||self.tracks_with_widths(), move|(_, track, x1, x2), i| { + (move||Fixed::y(2, Map::new(||self.tracks_with_widths(), move|(_, track, x1, x2), i| { let w = (x2 - x1) as u16; - let cell = Self::cell_input(track).ok(); + let cell = Bsp::s("[Rec]", "[Mon]"); let color: ItemPalette = track.color().dark.into(); - Push::x(scenes_w, map_east(x1 as u16, w, Fixed::x(w, Fixed::y(1, Self::cell(color, cell))))) + map_east(x1 as u16, w, Fixed::x(w, Self::cell(color, cell))) })).boxed()).into() } - fn cell_input (track: &ArrangerTrack) -> Usually> { - Ok(format!("RecMon"))/*, track.player.midi_ins().first().map(|port|port.short_name()) - .transpose()?.unwrap_or("?".into())))*/ - } - - fn output_row_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - (||Fill::x(Tui::bold(true, Tui::fg_bg(TuiTheme::g(0), TuiTheme::g(200), "[ ] Out 1: NI"))).boxed()).into() - } - fn output_row_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - let scenes_w = SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16; - (move||Fixed::y(1, Map::new(||self.tracks_with_widths(), move|(_, track, x1, x2), i| { - let w = (x2 - x1) as u16; - let cell = Self::cell_output(track).ok(); - let color: ItemPalette = track.color().dark.into(); - Push::x(scenes_w, map_east(x1 as u16, w, Fixed::x(w, Fixed::y(1, Self::cell(color, cell))))) - })).boxed()).into() - } - /// output port - fn cell_output (track: &ArrangerTrack) -> Usually> { - Ok(format!("MutSol"))/*, track.player.midi_outs().first().map(|port|port.short_name()) - .transpose()?.unwrap_or("?".into())))*/ - } fn scene_row_headers <'a> (&'a self) -> BoxThunk<'a, TuiOut> { (||{ - let scenes_w = SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16; + let scenes_w = 16;//.max(SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16); let last_color = Arc::new(RwLock::new(ItemPalette::from(Color::Rgb(0, 0, 0)))); Tui::bg(Color::Rgb(0,0,0), Fill::y(Map::new( ||self.scenes_with_heights(2), move|(_, scene, y1, y2), i| { let h = (y2 - y1) as u16; let color = scene.color(); - let cell = Self::phat_cell(color, *last_color.read().unwrap(), scene.name.read().unwrap().clone()); + let name = format!(" {}", scene.name.read().unwrap()); + let cell = Self::phat_cell(color, *last_color.read().unwrap(), name); *last_color.write().unwrap() = color; map_south(y1 as u16, 2, Fill::x(cell)) } @@ -156,7 +147,7 @@ impl Arranger { }).into() } fn scene_row_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - (||"".boxed()).into() + (||Tui::bg(Color::Rgb(20,40,60), "").boxed()).into() } pub fn tracks_with_widths (&self) @@ -175,7 +166,7 @@ impl Arranger { }) } fn track_column_separators <'a> (&'a self) -> impl Content + 'a { - let scenes_w = SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16; + 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_widths(), move|(_n, _track, x1, x2), _i|{ Push::x(scenes_w, map_east(x1 as u16, (x2 - x1) as u16, @@ -187,16 +178,6 @@ impl Arranger { let name = track.name().read().unwrap().clone(); Tui::bold(true, Tui::fg(track.color.lightest.rgb, name)) } - /// beats elapsed - fn cell_elapsed (track: &ArrangerTrack, timebase: &Arc) -> impl Content { - let mut result = String::new(); - if let Some((_, Some(phrase))) = track.player.play_phrase().as_ref() { - let length = phrase.read().unwrap().length; - let elapsed = track.player.pulses_since_start().unwrap() as usize; - result = format!("+{:>}", timebase.format_beats_1_short((elapsed % length) as f64)) - } - result - } /// beats until switchover fn cell_until_next (track: &ArrangerTrack, current: &Arc) -> Option> @@ -222,7 +203,7 @@ impl Arranger { }) } fn scene_rows (&self) -> impl Content + use<'_> { - let scenes_w = SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16; + let scenes_w = 16.max(SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16); Map::new(||self.scenes_with_heights(1), move|(_, scene, y1, y2), i| { let h = (y2 - y1) as u16; let color = scene.color(); @@ -340,7 +321,7 @@ impl Arranger { let selected = self.selected(); let cols = Arranger::track_widths(&self.tracks); let rows = Arranger::scene_heights(&self.scenes, 1); - let scenes_w = SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16; + let scenes_w = 16.max(SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16); let focused = true; let reticle = Reticle(Style { fg: Some(self.color.lighter.rgb),