From d3d1670af25a3efc27f9d0f0b1a13588be8dfcbd Mon Sep 17 00:00:00 2001 From: unspeaker Date: Fri, 10 Jan 2025 20:50:09 +0100 Subject: [PATCH] somewhat more like it --- tek/src/arranger.rs | 603 +++++++++++++++++++++++++++++- tek/src/arranger/arranger_tui.rs | 604 ------------------------------- 2 files changed, 602 insertions(+), 605 deletions(-) delete mode 100644 tek/src/arranger/arranger_tui.rs diff --git a/tek/src/arranger.rs b/tek/src/arranger.rs index 9a37b36b..384e7cec 100644 --- a/tek/src/arranger.rs +++ b/tek/src/arranger.rs @@ -4,7 +4,6 @@ mod arranger_command; pub use self::arranger_command::*; mod arranger_scene; pub use self::arranger_scene::*; mod arranger_select; pub use self::arranger_select::*; mod arranger_track; pub use self::arranger_track::*; -mod arranger_tui; pub use self::arranger_tui::*; mod arranger_h; /// Root view for standalone `tek_arranger` @@ -108,3 +107,605 @@ impl Arranger { } } } +pub(crate) const HEADER_H: u16 = 0; // 5 +pub(crate) const SCENES_W_OFFSET: u16 = 0; +render!(TuiOut: (self: Arranger) => { + let scenes_w = 16; + let row = move|h, header, cells|Fixed::y(h, Bsp::e( + Fixed::x(scenes_w, header), + Fixed::y(h, Fill::x(Align::x(cells))) + )); + //let row = move|h, header, cells|Align::w(Bsp::e( + //Align::w(Fixed::xy(scenes_w, h, header)), + //Fixed::xy(self.tracks.len() as u16*12, h, cells) + //)); + let h = self.size.h() as u16; + let toolbar = |x|Bsp::s(self.toolbar_view(), x); + let pool = |x|Bsp::w(self.pool_view(), x); + let editing = |x|Bsp::n(Bsp::e(self.editor.clip_status(), self.editor.edit_status()), x); + let outputs = |x|Bsp::s(row(2, self.output_row_header(), self.output_row_cells()), Fill::y(x)); + let playing = |x|Bsp::s(row(2, self.elapsed_row_header(), self.elapsed_row_cells()), Fill::y(x)); + let next = |x|Bsp::s(row(2, self.next_row_header(), self.next_row_cells()), Fill::y(x)); + let tracks = |x|Bsp::s(row(3, self.track_row_header(), self.track_row_cells()), Fill::y(x)); + let scenes = |x|Bsp::s(row(h.saturating_sub(13), self.scene_row_headers(), self.scene_row_cells()), x); + let inputs = |x|Bsp::n(row(2, self.input_row_header(), self.input_row_cells()), Fill::y(x)); + self.size.of(toolbar(pool(editing(inputs(outputs(playing(next(tracks(scenes(Fill::xy(""))))))))))) + //let enclosed = |x|Outer(Style::default().fg(Color::Rgb(72,72,72))).enclose(x); + //.max(SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16); + //Bsp::s(arrrrrr, enclosed(&self.editor)) +}); +impl Arranger { + pub const LEFT_SEP: char = '▎'; + pub const TRACK_MIN_WIDTH: usize = 4; + + pub fn scenes_with_heights (&self, h: usize) -> impl Iterator { + let mut y = 0; + let editing = self.editing; + let (selected_track, selected_scene) = match self.selected { + ArrangerSelection::Clip(t, s) => (Some(t), Some(s)), + _ => (None, None) + }; + self.scenes.iter().enumerate().map(move|(s, scene)|{ + let active = editing && selected_track.is_some() && selected_scene == Some(s); + let height = if active { 15 } else { h }; + let data = (s, scene, y, y + height); + y += height; + data + }) + } + pub fn tracks_with_widths (&self) + -> impl Iterator + { + let active = match self.selected { + ArrangerSelection::Track(t) => Some(t), + ArrangerSelection::Clip(t, _) => Some(t), + _ => None + }; + Self::tracks_with_widths_static(self.tracks.as_slice(), active) + } + fn tracks_with_widths_static (tracks: &[ArrangerTrack], active: Option) + -> impl Iterator + { + let mut x = 0; + tracks.iter().enumerate().map(move |(index, track)|{ + let width = if Some(index) == active { 40 } else { track.width }; + let data = (index, track, x, x + width); + x += width; + data + }) + } + + fn output_row_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + let fg = TuiTheme::g(192); + let bg = TuiTheme::g(48); + (move||Tui::bold(true, Tui::fg_bg(fg, bg, "[ ] Out 1: NI")).boxed()).into() + } + 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!(" M S "), phat_hi(color.dark.rgb, color.darker.rgb)); + map_east(x1 as u16, w, Fixed::x(w, Self::cell(color, cell))) + })).boxed()).into() + } + + fn elapsed_row_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + (||Tui::bold(true, Tui::fg(TuiTheme::g(128), "Playing")).boxed()).into() + } + fn elapsed_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(); + let color: ItemPalette = track.color().dark.into(); + let timebase = self.clock().timebase(); + let value = Tui::fg_bg(color.lightest.rgb, color.base.rgb, + if let Some((_, Some(clip))) = track.player.play_clip().as_ref() { + let length = clip.read().unwrap().length; + let elapsed = track.player.pulses_since_start().unwrap() as usize; + format!("+{:>}", timebase.format_beats_1_short((elapsed % length) as f64)) + } else { + String::new() + }); + let cell = Bsp::s(value, 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 next_row_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + (||Tui::bold(true, Tui::fg(TuiTheme::g(128), "Next")).boxed()).into() + } + fn next_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: ItemPalette = track.color(); + let color: ItemPalette = track.color().dark.into(); + let cell = Self::cell_until_next(track, &self.clock().playhead); + let cell = Self::cell(color, Tui::bold(true, cell)); + let cell = Tui::fg_bg(color.lightest.rgb, color.base.rgb, cell); + let cell = Bsp::s(cell, 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> { + let iter = ||self.tracks_with_widths(); + (move||Fixed::y(2, Map::new(iter, move|(_, track, x1, x2), i| { + let color = track.color(); + let name = Push::x(1, &track.name); + Tui::bg(color.base.rgb, map_east(x1 as u16, (x2 - x1) as u16, + Tui::fg_bg(color.lightest.rgb, color.base.rgb, + phat_cell(color, color.darkest.rgb.into(), + Tui::bold(true, name))))) })).boxed()).into() + } + + fn input_row_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + (||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> { + (move||Fixed::y(2, Map::new(||self.tracks_with_widths(), move|(_, track, x1, x2), i| { + let w = (x2 - x1) as u16; + let cell = Bsp::s("[Rec]", "[Mon]"); + let color: ItemPalette = track.color().dark.into(); + map_east(x1 as u16, w, Fixed::x(w, Self::cell(color, cell))) + })).boxed()).into() + } + + fn scene_row_headers <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + (||{ + 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)))); + let selected_scene = match self.selected { + ArrangerSelection::Scene(s) => Some(s), + _ => None + }; + 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 name = format!("🭬{}", &scene.name); + let color = scene.color(); + let cell = phat_sel_3( + selected_scene == Some(i), + Push::x(1, Tui::bold(true, name.clone())), + Push::x(1, Tui::bold(true, name)), + if selected_scene.map(|s|s + 1) == Some(i) { + None + } else { + Some(last_color.read().unwrap().base.rgb) + }, + if selected_scene == Some(i) { + color.light.rgb + } else { + color.base.rgb + }, + Color::Rgb(0, 0, 0) + ); + *last_color.write().unwrap() = color; + map_south(y1 as u16, h + 1, Fixed::y(h + 1, cell)) + } + ))).boxed() + }).into() + } + fn scene_row_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { + let editing = self.editing; + let (selected_track, selected_scene) = match self.selected { + ArrangerSelection::Clip(t, s) => (Some(t), Some(s)), + _ => (None, None) + }; + let tracks = move||self.tracks_with_widths(); + let scenes = ||self.scenes_with_heights(2); + (move||Fixed::y(2, Map::new(tracks, move|(_, track, x1, x2), t| { + let w = (x2 - x1) as u16; + let cell = Bsp::s("[Rec]", "[Mon]"); + let color: ItemPalette = track.color().dark.into(); + let last_color = Arc::new(RwLock::new(ItemPalette::from(Color::Rgb(0, 0, 0)))); + let cells = Map::new(scenes, move|(_, scene, y1, y2), s| { + let h = (y2 - y1) as u16; + let color = scene.color(); + let name = "⏹ "; + let last = last_color.read().unwrap().clone(); + //let cell = phat_sel_3( + //selected_track == Some(i) && selected_scene == Some(j), + //Tui::fg(TuiTheme::g(64), Push::x(1, name)), + //Tui::fg(TuiTheme::g(64), Push::x(1, name)), + //if selected_track == Some(i) && selected_scene.map(|s|s+1) == Some(j) { + //None + //} else { + //Some(TuiTheme::g(32).into()) + //}, + //TuiTheme::g(32).into(), + //TuiTheme::g(32).into(), + //); + let active = editing && selected_track == Some(t) && selected_scene == Some(s); + let editor = Thunk::new(||&self.editor); + let cell = Thunk::new(move||phat_sel_3( + selected_track == Some(t) && selected_scene == Some(s), + Tui::fg(TuiTheme::g(64), Push::x(1, Tui::bold(true, name.to_string()))), + Tui::fg(TuiTheme::g(64), Push::x(1, Tui::bold(true, name.to_string()))), + if selected_track == Some(t) && selected_scene.map(|s|s+1) == Some(s) { + None + } else { + Some(TuiTheme::g(32).into()) + }, + TuiTheme::g(32).into(), + TuiTheme::g(32).into(), + )); + let cell = Either(active, editor, cell); + map_south( + y1 as u16, + h + 1, + Fill::x(cell) + ) + }); + map_east( + x1 as u16, + w, + Fixed::x(w, Tui::bg(Color::Rgb(0,0,0), Fill::y(cells)).boxed()) + ) + })).boxed()).into() + } + + fn track_column_separators <'a> (&'a self) -> impl Content + 'a { + 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, + Fixed::x((x2 - x1) as u16, Tui::fg(fg, RepeatV(&"·"))))) + }) + } + /// name and width of track + fn cell_name (track: &ArrangerTrack, _w: usize) -> impl Content { + Tui::bold(true, Tui::fg(track.color.lightest.rgb, track.name().clone())) + } + /// beats until switchover + fn cell_until_next (track: &ArrangerTrack, current: &Arc) + -> Option> + { + let timebase = ¤t.timebase; + let mut result = String::new(); + if let Some((t, _)) = track.player.next_clip().as_ref() { + let target = t.pulse.get(); + let current = current.pulse.get(); + if target > current { + result = format!("-{:>}", timebase.format_beats_0_short(target - current)) + } + } + Some(result) + } + + fn scene_rows (&self) -> impl Content + use<'_> { + 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(); + let cell = Self::cell(color, scene.name.clone()); + let cell = Fixed::y(h, Fixed::x(scenes_w, cell)); + map_south(y1 as u16, 1, cell) + }) + } + pub fn scene_heights (scenes: &[ArrangerScene], factor: usize) -> Vec<(usize, usize)> { + let mut total = 0; + if factor == 0 { + scenes.iter().map(|scene|{ + let pulses = scene.pulses().max(PPQ); + total += pulses; + (pulses, total - pulses) + }).collect() + } else { + (0..=scenes.len()).map(|i|{ + (factor*PPQ, factor*PPQ*i) + }).collect() + } + } + + fn cell_scene <'a> ( + tracks: &'a [ArrangerTrack], scene: &'a ArrangerScene, pulses: usize + ) -> impl Content + use<'a> { + let height = 1.max((pulses / PPQ) as u16); + let playing = scene.is_playing(tracks); + let icon = Tui::bg( + scene.color.base.rgb, if playing { "▶ " } else { " " } + ); + let name = Tui::fg_bg(scene.color.lightest.rgb, scene.color.base.rgb, + Expand::x(1, Tui::bold(true, scene.name.clone())) + ); + let clips = Map::new(||Arranger::tracks_with_widths_static(tracks, None), move|(index, track, x1, x2), _| + Push::x((x2 - x1) as u16, Self::cell_clip(scene, index, track, (x2 - x1) as u16, height)) + ); + Fixed::y(height, Bsp::e(icon, Bsp::e(name, clips))) + } + + fn cell_clip <'a> ( + scene: &'a ArrangerScene, index: usize, track: &'a ArrangerTrack, w: u16, h: u16 + ) -> impl Content + use<'a> { + scene.clips.get(index).map(|clip|clip.as_ref().map(|clip|{ + let clip = clip.read().unwrap(); + let mut bg = TuiTheme::border_bg(); + let name = clip.name.to_string(); + let max_w = name.len().min((w as usize).saturating_sub(2)); + let color = clip.color; + bg = color.dark.rgb; + if let Some((_, Some(ref playing))) = track.player.play_clip() { + if *playing.read().unwrap() == *clip { + bg = color.light.rgb + } + }; + Fixed::xy(w, h, &Tui::bg(bg, Push::x(1, Fixed::x(w, &name.as_str()[0..max_w])))); + })) + } + fn toolbar_view (&self) -> impl Content + use<'_> { + Fill::x(Fixed::y(2, Align::x(TransportView::new(true, &self.clock)))) + } + fn pool_view (&self) -> impl Content + use<'_> { + let w = self.size.w(); + let clip_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 }; + let pool_w = if self.pool.visible { clip_w } else { 0 }; + let pool = Pull::y(1, Fill::y(Align::e(PoolView(self.pool.visible, &self.pool)))); + Fixed::x(pool_w, Align::e(Fill::y(PoolView(self.compact, &self.pool)))) + } + + pub fn track_widths (tracks: &[ArrangerTrack]) -> Vec<(usize, usize)> { + let mut widths = vec![]; + let mut total = 0; + for track in tracks.iter() { + let width = track.width; + widths.push((width, total)); + total += width; + } + widths.push((0, total)); + widths + } + + fn scene_row_sep <'a> (&'a self) -> impl Content + 'a { + let fg = Color::Rgb(255,255,255); + Map::new(move||self.scenes_with_heights(1), |_, _|"") + //Map(||rows.iter(), |(_n, _scene, y1, _y2), _i| { + //let y = to.area().y() + (y / PPQ) as u16 + 1; + //if y >= to.buffer.area.height { break } + //for x in to.area().x()..to.area().x2().saturating_sub(2) { + ////if x < to.buffer.area.x && y < to.buffer.area.y { + //if let Some(cell) = to.buffer.cell_mut(ratatui::prelude::Position::from((x, y))) { + //cell.modifier = Modifier::UNDERLINED; + //cell.underline_color = fg; + //} + ////} + //} + //}) + } + + fn cursor (&self) -> impl Content + '_ { + let color = self.color; + let bg = color.lighter.rgb;//Color::Rgb(0, 255, 0); + let selected = self.selected(); + let cols = Arranger::track_widths(&self.tracks); + let rows = Arranger::scene_heights(&self.scenes, 1); + 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), + bg: None, + underline_color: None, + add_modifier: Modifier::empty(), + sub_modifier: Modifier::DIM + }); + RenderThunk::new(move|to: &mut TuiOut|{ + let area = to.area(); + let [x, y, w, h] = area.xywh(); + let mut track_area: Option<[u16;4]> = match selected { + ArrangerSelection::Track(t) | ArrangerSelection::Clip(t, _) => Some([ + x + scenes_w + cols[t].1 as u16, y, + cols[t].0 as u16, h, + ]), + _ => None + }; + let mut scene_area: Option<[u16;4]> = match selected { + ArrangerSelection::Scene(s) | ArrangerSelection::Clip(_, s) => Some([ + x, y + HEADER_H + (rows[s].1 / PPQ) as u16, + w, (rows[s].0 / PPQ) as u16 + ]), + _ => None + }; + let mut clip_area: Option<[u16;4]> = match selected { + ArrangerSelection::Clip(t, s) => Some([ + (scenes_w + x + cols[t].1 as u16).saturating_sub(1), + HEADER_H + y + (rows[s].1/PPQ) as u16, + cols[t].0 as u16 + 2, + (rows[s].0 / PPQ) as u16 + ]), + _ => None + }; + if let Some([x, y, width, height]) = track_area { + to.fill_fg([x, y, 1, height], bg); + to.fill_fg([x + width, y, 1, height], bg); + } + if let Some([_, y, _, height]) = scene_area { + to.fill_ul([x, y - 1, w, 1], bg); + to.fill_ul([x, y + height - 1, w, 1], bg); + } + if focused { + to.place(if let Some(clip_area) = clip_area { + clip_area + } else if let Some(track_area) = track_area { + track_area.clip_h(HEADER_H) + } else if let Some(scene_area) = scene_area { + scene_area.clip_w(scenes_w) + } else { + area.clip_w(scenes_w).clip_h(HEADER_H) + }, &reticle) + }; + }) + } + + /// 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(&"▄"))) +} +/// A phat line +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(phat_lo(color.base.rgb, last.base.rgb), + Bsp::n(phat_hi(color.base.rgb, last.base.rgb), + Fixed::y(1, Fill::x(Tui::fg_bg(color.lightest.rgb, color.base.rgb, field))), + ) + ) +} +fn phat_cell_3 > ( + field: T, top: Color, middle: Color, bottom: Color +) -> impl Content { + Bsp::s(phat_lo(middle, top), + Bsp::n(phat_hi(middle, bottom), + Fill::y(Fill::x(Tui::bg(middle, field))), + ) + ) +} +fn phat_sel_3 > ( + selected: bool, field_1: T, field_2: T, top: Option, middle: Color, bottom: Color +) -> impl Content { + let border = Style::default().fg(Color::Rgb(255,255,255)).bg(middle); + Either(selected, + Tui::bg(middle, Outer(border).enclose( Align::w(Bsp::s("", Bsp::s(field_1, ""))))), + Bsp::s( + Fixed::y(1, top.map(|top|phat_lo(middle, top))), + Bsp::n(phat_hi(middle, bottom), + Fixed::y(1, Fill::x(Tui::bg(middle, field_2))), + ) + )) +} + +//pub struct ArrangerVCursor { + //cols: Vec<(usize, usize)>, + //rows: Vec<(usize, usize)>, + //color: ItemPalette, + //reticle: Reticle, + //selected: ArrangerSelection, + //scenes_w: u16, +//} + +//from!(|args:(&Arranger, usize)|ArrangerVCursor = Self { + //cols: Arranger::track_widths(&args.0.tracks), + //rows: Arranger::scene_heights(&args.0.scenes, args.1), + //selected: args.0.selected(), + //scenes_w: SCENES_W_OFFSET + ArrangerScene::longest_name(&args.0.scenes) as u16, + //color: args.0.color, + //reticle: Reticle(Style { + //fg: Some(args.0.color.lighter.rgb), + //bg: None, + //underline_color: None, + //add_modifier: Modifier::empty(), + //sub_modifier: Modifier::DIM + //}), +//}); +//impl Content for ArrangerVCursor { + //fn render (&self, to: &mut TuiOut) { + //let area = to.area(); + //let focused = true; + //let selected = self.selected; + //let get_track_area = |t: usize| [ + //self.scenes_w + area.x() + self.cols[t].1 as u16, area.y(), + //self.cols[t].0 as u16, area.h(), + //]; + //let get_scene_area = |s: usize| [ + //area.x(), HEADER_H + area.y() + (self.rows[s].1 / PPQ) as u16, + //area.w(), (self.rows[s].0 / PPQ) as u16 + //]; + //let get_clip_area = |t: usize, s: usize| [ + //(self.scenes_w + area.x() + self.cols[t].1 as u16).saturating_sub(1), + //HEADER_H + area.y() + (self.rows[s].1/PPQ) as u16, + //self.cols[t].0 as u16 + 2, + //(self.rows[s].0 / PPQ) as u16 + //]; + //let mut track_area: Option<[u16;4]> = None; + //let mut scene_area: Option<[u16;4]> = None; + //let mut clip_area: Option<[u16;4]> = None; + //let area = match selected { + //ArrangerSelection::Mix => area, + //ArrangerSelection::Track(t) => { + //track_area = Some(get_track_area(t)); + //area + //}, + //ArrangerSelection::Scene(s) => { + //scene_area = Some(get_scene_area(s)); + //area + //}, + //ArrangerSelection::Clip(t, s) => { + //track_area = Some(get_track_area(t)); + //scene_area = Some(get_scene_area(s)); + //clip_area = Some(get_clip_area(t, s)); + //area + //}, + //}; + //let bg = self.color.lighter.rgb;//Color::Rgb(0, 255, 0); + //if let Some([x, y, width, height]) = track_area { + //to.fill_fg([x, y, 1, height], bg); + //to.fill_fg([x + width, y, 1, height], bg); + //} + //if let Some([_, y, _, height]) = scene_area { + //to.fill_ul([area.x(), y - 1, area.w(), 1], bg); + //to.fill_ul([area.x(), y + height - 1, area.w(), 1], bg); + //} + //if focused { + //to.place(if let Some(clip_area) = clip_area { + //clip_area + //} else if let Some(track_area) = track_area { + //track_area.clip_h(HEADER_H) + //} else if let Some(scene_area) = scene_area { + //scene_area.clip_w(self.scenes_w) + //} else { + //area.clip_w(self.scenes_w).clip_h(HEADER_H) + //}, &self.reticle) + //}; + //} +//} +//impl Arranger { + //fn render_mode (state: &Self) -> impl Content + use<'_> { + //match state.mode { + //ArrangerMode::H => todo!("horizontal arranger"), + //ArrangerMode::V(factor) => Self::render_mode_v(state, factor), + //} + //} +//} +//render!(TuiOut: (self: Arranger) => { + //let pool_w = if self.pool.visible { self.splits[1] } else { 0 }; + //let color = self.color; + //let layout = Bsp::a(Fill::xy(ArrangerStatus::from(self)), + //Bsp::n(Fixed::x(pool_w, PoolView(self.pool.visible, &self.pool)), + //Bsp::n(TransportView::new(true, &self.clock), + //Bsp::s(Fixed::y(1, MidiEditStatus(&self.editor)), + //Bsp::n(Fill::x(Fixed::y(20, + //Bsp::a(Fill::xy(Tui::bg(color.darkest.rgb, "background")), + //Bsp::a( + //Fill::xy(Outer(Style::default().fg(color.dark.rgb).bg(color.darkest.rgb))), + //Self::render_mode(self))))), Fill::y(&"fixme: self.editor")))))); + //self.size.of(layout) +//}); + //Align::n(Fill::xy(lay!( + //Align::n(Fill::xy(Tui::bg(self.color.darkest.rgb, " "))), + //Align::n(Fill::xy(ArrangerVRowSep::from((self, 1)))), + //Align::n(Fill::xy(ArrangerVColSep::from(self))), + //Align::n(Fill::xy(ArrangerVClips::new(self, 1))), + //Align::n(Fill::xy(ArrangerVCursor::from((self, 1)))))))))))))))); + //Align::n(Fill::xy(":"))))))))))))); + //"todo:")))))))); + //Bsp::s( + //Align::n(Fixed::y(1, Fill::x(ArrangerVIns::from(self)))), + //Bsp::s( + //Fixed::y(20, Align::n(ArrangerVClips::new(self, 1))), + //Fill::x(Fixed::y(1, ArrangerVOuts::from(self))))))))))))); + //Bsp::s( + //Bsp::s( + //Bsp::s( + //Fill::xy(ArrangerVClips::new(self, 1)), + //Fill::x(ArrangerVOuts::from(self))))) + diff --git a/tek/src/arranger/arranger_tui.rs b/tek/src/arranger/arranger_tui.rs deleted file mode 100644 index e600a7fa..00000000 --- a/tek/src/arranger/arranger_tui.rs +++ /dev/null @@ -1,604 +0,0 @@ -use crate::*; -pub(crate) const HEADER_H: u16 = 0; // 5 -pub(crate) const SCENES_W_OFFSET: u16 = 0; -render!(TuiOut: (self: Arranger) => { - let scenes_w = 16; - let row = move|h, header, cells|Align::w(Bsp::e( - Align::w(Fixed::xy(scenes_w, h, header)), - Fixed::xy(self.tracks.len() as u16*12, h, cells) - )); - let toolbar = |x|Bsp::s(self.toolbar_view(), x); - let pool = |x|Bsp::w(self.pool_view(), x); - let editing = |x|Bsp::n(Bsp::e(self.editor.clip_status(), self.editor.edit_status()), x); - let outputs = |x|Bsp::s(row(2, self.output_row_header(), self.output_row_cells()), Fill::y(x)); - let playing = |x|Bsp::s(row(2, self.elapsed_row_header(), self.elapsed_row_cells()), Fill::y(x)); - let next = |x|Bsp::s(row(2, self.next_row_header(), self.next_row_cells()), Fill::y(x)); - let tracks = |x|Bsp::s(row(3, self.track_row_header(), self.track_row_cells()), Fill::y(x)); - let scenes = |x|Bsp::s(row((self.size.h() as u16).saturating_sub(13), - self.scene_row_headers(), - self.scene_row_cells()), x); - let inputs = |x|Bsp::n( - row(2, self.input_row_header(), self.input_row_cells()), - Fill::y(x)); - self.size.of( - toolbar(pool(editing(inputs(outputs(playing(next(tracks(scenes(Fill::xy(""))))))))))) - - //let enclosed = |x|Outer(Style::default().fg(Color::Rgb(72,72,72))).enclose(x); - //.max(SCENES_W_OFFSET + ArrangerScene::longest_name(&self.scenes) as u16); - //Bsp::s(arrrrrr, enclosed(&self.editor)) -}); -impl Arranger { - pub const LEFT_SEP: char = '▎'; - pub const TRACK_MIN_WIDTH: usize = 4; - - pub fn scenes_with_heights (&self, h: usize) -> impl Iterator { - let mut y = 0; - let editing = self.editing; - let (selected_track, selected_scene) = match self.selected { - ArrangerSelection::Clip(t, s) => (Some(t), Some(s)), - _ => (None, None) - }; - self.scenes.iter().enumerate().map(move|(s, scene)|{ - let active = editing && selected_track.is_some() && selected_scene == Some(s); - let height = if active { 15 } else { h }; - let data = (s, scene, y, y + height); - y += height; - data - }) - } - pub fn tracks_with_widths (&self) - -> impl Iterator - { - let active = match self.selected { - ArrangerSelection::Track(t) => Some(t), - ArrangerSelection::Clip(t, _) => Some(t), - _ => None - }; - Self::tracks_with_widths_static(self.tracks.as_slice(), active) - } - fn tracks_with_widths_static (tracks: &[ArrangerTrack], active: Option) - -> impl Iterator - { - let mut x = 0; - tracks.iter().enumerate().map(move |(index, track)|{ - let width = if Some(index) == active { 40 } else { track.width }; - let data = (index, track, x, x + width); - x += width; - data - }) - } - - fn output_row_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - let fg = TuiTheme::g(192); - let bg = TuiTheme::g(48); - (move||Tui::bold(true, Tui::fg_bg(fg, bg, "[ ] Out 1: NI")).boxed()).into() - } - 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!(" M S "), phat_hi(color.dark.rgb, color.darker.rgb)); - map_east(x1 as u16, w, Fixed::x(w, Self::cell(color, cell))) - })).boxed()).into() - } - - fn elapsed_row_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - (||Tui::bold(true, Tui::fg(TuiTheme::g(128), "Playing")).boxed()).into() - } - fn elapsed_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(); - let color: ItemPalette = track.color().dark.into(); - let timebase = self.clock().timebase(); - let value = Tui::fg_bg(color.lightest.rgb, color.base.rgb, - if let Some((_, Some(clip))) = track.player.play_clip().as_ref() { - let length = clip.read().unwrap().length; - let elapsed = track.player.pulses_since_start().unwrap() as usize; - format!("+{:>}", timebase.format_beats_1_short((elapsed % length) as f64)) - } else { - String::new() - }); - let cell = Bsp::s(value, 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 next_row_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - (||Tui::bold(true, Tui::fg(TuiTheme::g(128), "Next")).boxed()).into() - } - fn next_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: ItemPalette = track.color(); - let color: ItemPalette = track.color().dark.into(); - let cell = Self::cell_until_next(track, &self.clock().playhead); - let cell = Self::cell(color, Tui::bold(true, cell)); - let cell = Tui::fg_bg(color.lightest.rgb, color.base.rgb, cell); - let cell = Bsp::s(cell, 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> { - let iter = ||self.tracks_with_widths(); - (move||Fixed::y(2, Map::new(iter, move|(_, track, x1, x2), i| { - let color = track.color(); - let name = Push::x(1, &track.name); - Tui::bg(color.base.rgb, map_east(x1 as u16, (x2 - x1) as u16, - Tui::fg_bg(color.lightest.rgb, color.base.rgb, - phat_cell(color, color.darkest.rgb.into(), - Tui::bold(true, name))))) })).boxed()).into() - } - - fn input_row_header <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - (||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> { - (move||Fixed::y(2, Map::new(||self.tracks_with_widths(), move|(_, track, x1, x2), i| { - let w = (x2 - x1) as u16; - let cell = Bsp::s("[Rec]", "[Mon]"); - let color: ItemPalette = track.color().dark.into(); - map_east(x1 as u16, w, Fixed::x(w, Self::cell(color, cell))) - })).boxed()).into() - } - - fn scene_row_headers <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - (||{ - 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)))); - let selected_scene = match self.selected { - ArrangerSelection::Scene(s) => Some(s), - _ => None - }; - 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 name = format!("🭬{}", &scene.name); - let color = scene.color(); - let cell = phat_sel_3( - selected_scene == Some(i), - Push::x(1, Tui::bold(true, name.clone())), - Push::x(1, Tui::bold(true, name)), - if selected_scene.map(|s|s + 1) == Some(i) { - None - } else { - Some(last_color.read().unwrap().base.rgb) - }, - if selected_scene == Some(i) { - color.light.rgb - } else { - color.base.rgb - }, - Color::Rgb(0, 0, 0) - ); - *last_color.write().unwrap() = color; - map_south(y1 as u16, h + 1, Fixed::y(h + 1, cell)) - } - ))).boxed() - }).into() - } - fn scene_row_cells <'a> (&'a self) -> BoxThunk<'a, TuiOut> { - let editing = self.editing; - let (selected_track, selected_scene) = match self.selected { - ArrangerSelection::Clip(t, s) => (Some(t), Some(s)), - _ => (None, None) - }; - let tracks = move||self.tracks_with_widths(); - let scenes = ||self.scenes_with_heights(2); - (move||Fixed::y(2, Map::new(tracks, move|(_, track, x1, x2), t| { - let w = (x2 - x1) as u16; - let cell = Bsp::s("[Rec]", "[Mon]"); - let color: ItemPalette = track.color().dark.into(); - let last_color = Arc::new(RwLock::new(ItemPalette::from(Color::Rgb(0, 0, 0)))); - let cells = Map::new(scenes, move|(_, scene, y1, y2), s| { - let h = (y2 - y1) as u16; - let color = scene.color(); - let name = "⏹ "; - let last = last_color.read().unwrap().clone(); - //let cell = phat_sel_3( - //selected_track == Some(i) && selected_scene == Some(j), - //Tui::fg(TuiTheme::g(64), Push::x(1, name)), - //Tui::fg(TuiTheme::g(64), Push::x(1, name)), - //if selected_track == Some(i) && selected_scene.map(|s|s+1) == Some(j) { - //None - //} else { - //Some(TuiTheme::g(32).into()) - //}, - //TuiTheme::g(32).into(), - //TuiTheme::g(32).into(), - //); - let active = editing && selected_track == Some(t) && selected_scene == Some(s); - let editor = Thunk::new(||&self.editor); - let cell = Thunk::new(move||phat_sel_3( - selected_track == Some(t) && selected_scene == Some(s), - Tui::fg(TuiTheme::g(64), Push::x(1, Tui::bold(true, name.to_string()))), - Tui::fg(TuiTheme::g(64), Push::x(1, Tui::bold(true, name.to_string()))), - if selected_track == Some(t) && selected_scene.map(|s|s+1) == Some(s) { - None - } else { - Some(TuiTheme::g(32).into()) - }, - TuiTheme::g(32).into(), - TuiTheme::g(32).into(), - )); - let cell = Either(active, editor, cell); - map_south( - y1 as u16, - h + 1, - Fill::x(cell) - ) - }); - map_east( - x1 as u16, - w, - Fixed::x(w, Tui::bg(Color::Rgb(0,0,0), Fill::y(cells)).boxed()) - ) - })).boxed()).into() - } - - fn track_column_separators <'a> (&'a self) -> impl Content + 'a { - 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, - Fixed::x((x2 - x1) as u16, Tui::fg(fg, RepeatV(&"·"))))) - }) - } - /// name and width of track - fn cell_name (track: &ArrangerTrack, _w: usize) -> impl Content { - Tui::bold(true, Tui::fg(track.color.lightest.rgb, track.name().clone())) - } - /// beats until switchover - fn cell_until_next (track: &ArrangerTrack, current: &Arc) - -> Option> - { - let timebase = ¤t.timebase; - let mut result = String::new(); - if let Some((t, _)) = track.player.next_clip().as_ref() { - let target = t.pulse.get(); - let current = current.pulse.get(); - if target > current { - result = format!("-{:>}", timebase.format_beats_0_short(target - current)) - } - } - Some(result) - } - - fn scene_rows (&self) -> impl Content + use<'_> { - 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(); - let cell = Self::cell(color, scene.name.clone()); - let cell = Fixed::y(h, Fixed::x(scenes_w, cell)); - map_south(y1 as u16, 1, cell) - }) - } - pub fn scene_heights (scenes: &[ArrangerScene], factor: usize) -> Vec<(usize, usize)> { - let mut total = 0; - if factor == 0 { - scenes.iter().map(|scene|{ - let pulses = scene.pulses().max(PPQ); - total += pulses; - (pulses, total - pulses) - }).collect() - } else { - (0..=scenes.len()).map(|i|{ - (factor*PPQ, factor*PPQ*i) - }).collect() - } - } - - fn cell_scene <'a> ( - tracks: &'a [ArrangerTrack], scene: &'a ArrangerScene, pulses: usize - ) -> impl Content + use<'a> { - let height = 1.max((pulses / PPQ) as u16); - let playing = scene.is_playing(tracks); - let icon = Tui::bg( - scene.color.base.rgb, if playing { "▶ " } else { " " } - ); - let name = Tui::fg_bg(scene.color.lightest.rgb, scene.color.base.rgb, - Expand::x(1, Tui::bold(true, scene.name.clone())) - ); - let clips = Map::new(||Arranger::tracks_with_widths_static(tracks, None), move|(index, track, x1, x2), _| - Push::x((x2 - x1) as u16, Self::cell_clip(scene, index, track, (x2 - x1) as u16, height)) - ); - Fixed::y(height, Bsp::e(icon, Bsp::e(name, clips))) - } - - fn cell_clip <'a> ( - scene: &'a ArrangerScene, index: usize, track: &'a ArrangerTrack, w: u16, h: u16 - ) -> impl Content + use<'a> { - scene.clips.get(index).map(|clip|clip.as_ref().map(|clip|{ - let clip = clip.read().unwrap(); - let mut bg = TuiTheme::border_bg(); - let name = clip.name.to_string(); - let max_w = name.len().min((w as usize).saturating_sub(2)); - let color = clip.color; - bg = color.dark.rgb; - if let Some((_, Some(ref playing))) = track.player.play_clip() { - if *playing.read().unwrap() == *clip { - bg = color.light.rgb - } - }; - Fixed::xy(w, h, &Tui::bg(bg, Push::x(1, Fixed::x(w, &name.as_str()[0..max_w])))); - })) - } - fn toolbar_view (&self) -> impl Content + use<'_> { - Fill::x(Fixed::y(2, Align::x(TransportView::new(true, &self.clock)))) - } - fn pool_view (&self) -> impl Content + use<'_> { - let w = self.size.w(); - let clip_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 }; - let pool_w = if self.pool.visible { clip_w } else { 0 }; - let pool = Pull::y(1, Fill::y(Align::e(PoolView(self.pool.visible, &self.pool)))); - Fixed::x(pool_w, Align::e(Fill::y(PoolView(self.compact, &self.pool)))) - } - - pub fn track_widths (tracks: &[ArrangerTrack]) -> Vec<(usize, usize)> { - let mut widths = vec![]; - let mut total = 0; - for track in tracks.iter() { - let width = track.width; - widths.push((width, total)); - total += width; - } - widths.push((0, total)); - widths - } - - fn scene_row_sep <'a> (&'a self) -> impl Content + 'a { - let fg = Color::Rgb(255,255,255); - Map::new(move||self.scenes_with_heights(1), |_, _|"") - //Map(||rows.iter(), |(_n, _scene, y1, _y2), _i| { - //let y = to.area().y() + (y / PPQ) as u16 + 1; - //if y >= to.buffer.area.height { break } - //for x in to.area().x()..to.area().x2().saturating_sub(2) { - ////if x < to.buffer.area.x && y < to.buffer.area.y { - //if let Some(cell) = to.buffer.cell_mut(ratatui::prelude::Position::from((x, y))) { - //cell.modifier = Modifier::UNDERLINED; - //cell.underline_color = fg; - //} - ////} - //} - //}) - } - - fn cursor (&self) -> impl Content + '_ { - let color = self.color; - let bg = color.lighter.rgb;//Color::Rgb(0, 255, 0); - let selected = self.selected(); - let cols = Arranger::track_widths(&self.tracks); - let rows = Arranger::scene_heights(&self.scenes, 1); - 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), - bg: None, - underline_color: None, - add_modifier: Modifier::empty(), - sub_modifier: Modifier::DIM - }); - RenderThunk::new(move|to: &mut TuiOut|{ - let area = to.area(); - let [x, y, w, h] = area.xywh(); - let mut track_area: Option<[u16;4]> = match selected { - ArrangerSelection::Track(t) | ArrangerSelection::Clip(t, _) => Some([ - x + scenes_w + cols[t].1 as u16, y, - cols[t].0 as u16, h, - ]), - _ => None - }; - let mut scene_area: Option<[u16;4]> = match selected { - ArrangerSelection::Scene(s) | ArrangerSelection::Clip(_, s) => Some([ - x, y + HEADER_H + (rows[s].1 / PPQ) as u16, - w, (rows[s].0 / PPQ) as u16 - ]), - _ => None - }; - let mut clip_area: Option<[u16;4]> = match selected { - ArrangerSelection::Clip(t, s) => Some([ - (scenes_w + x + cols[t].1 as u16).saturating_sub(1), - HEADER_H + y + (rows[s].1/PPQ) as u16, - cols[t].0 as u16 + 2, - (rows[s].0 / PPQ) as u16 - ]), - _ => None - }; - if let Some([x, y, width, height]) = track_area { - to.fill_fg([x, y, 1, height], bg); - to.fill_fg([x + width, y, 1, height], bg); - } - if let Some([_, y, _, height]) = scene_area { - to.fill_ul([x, y - 1, w, 1], bg); - to.fill_ul([x, y + height - 1, w, 1], bg); - } - if focused { - to.place(if let Some(clip_area) = clip_area { - clip_area - } else if let Some(track_area) = track_area { - track_area.clip_h(HEADER_H) - } else if let Some(scene_area) = scene_area { - scene_area.clip_w(scenes_w) - } else { - area.clip_w(scenes_w).clip_h(HEADER_H) - }, &reticle) - }; - }) - } - - /// 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(&"▄"))) -} -/// A phat line -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(phat_lo(color.base.rgb, last.base.rgb), - Bsp::n(phat_hi(color.base.rgb, last.base.rgb), - Fixed::y(1, Fill::x(Tui::fg_bg(color.lightest.rgb, color.base.rgb, field))), - ) - ) -} -fn phat_cell_3 > ( - field: T, top: Color, middle: Color, bottom: Color -) -> impl Content { - Bsp::s(phat_lo(middle, top), - Bsp::n(phat_hi(middle, bottom), - Fill::y(Fill::x(Tui::bg(middle, field))), - ) - ) -} -fn phat_sel_3 > ( - selected: bool, field_1: T, field_2: T, top: Option, middle: Color, bottom: Color -) -> impl Content { - let border = Style::default().fg(Color::Rgb(255,255,255)).bg(middle); - Either(selected, - Tui::bg(middle, Outer(border).enclose( Align::w(Bsp::s("", Bsp::s(field_1, ""))))), - Bsp::s( - Fixed::y(1, top.map(|top|phat_lo(middle, top))), - Bsp::n(phat_hi(middle, bottom), - Fixed::y(1, Fill::x(Tui::bg(middle, field_2))), - ) - )) -} - -//pub struct ArrangerVCursor { - //cols: Vec<(usize, usize)>, - //rows: Vec<(usize, usize)>, - //color: ItemPalette, - //reticle: Reticle, - //selected: ArrangerSelection, - //scenes_w: u16, -//} - -//from!(|args:(&Arranger, usize)|ArrangerVCursor = Self { - //cols: Arranger::track_widths(&args.0.tracks), - //rows: Arranger::scene_heights(&args.0.scenes, args.1), - //selected: args.0.selected(), - //scenes_w: SCENES_W_OFFSET + ArrangerScene::longest_name(&args.0.scenes) as u16, - //color: args.0.color, - //reticle: Reticle(Style { - //fg: Some(args.0.color.lighter.rgb), - //bg: None, - //underline_color: None, - //add_modifier: Modifier::empty(), - //sub_modifier: Modifier::DIM - //}), -//}); -//impl Content for ArrangerVCursor { - //fn render (&self, to: &mut TuiOut) { - //let area = to.area(); - //let focused = true; - //let selected = self.selected; - //let get_track_area = |t: usize| [ - //self.scenes_w + area.x() + self.cols[t].1 as u16, area.y(), - //self.cols[t].0 as u16, area.h(), - //]; - //let get_scene_area = |s: usize| [ - //area.x(), HEADER_H + area.y() + (self.rows[s].1 / PPQ) as u16, - //area.w(), (self.rows[s].0 / PPQ) as u16 - //]; - //let get_clip_area = |t: usize, s: usize| [ - //(self.scenes_w + area.x() + self.cols[t].1 as u16).saturating_sub(1), - //HEADER_H + area.y() + (self.rows[s].1/PPQ) as u16, - //self.cols[t].0 as u16 + 2, - //(self.rows[s].0 / PPQ) as u16 - //]; - //let mut track_area: Option<[u16;4]> = None; - //let mut scene_area: Option<[u16;4]> = None; - //let mut clip_area: Option<[u16;4]> = None; - //let area = match selected { - //ArrangerSelection::Mix => area, - //ArrangerSelection::Track(t) => { - //track_area = Some(get_track_area(t)); - //area - //}, - //ArrangerSelection::Scene(s) => { - //scene_area = Some(get_scene_area(s)); - //area - //}, - //ArrangerSelection::Clip(t, s) => { - //track_area = Some(get_track_area(t)); - //scene_area = Some(get_scene_area(s)); - //clip_area = Some(get_clip_area(t, s)); - //area - //}, - //}; - //let bg = self.color.lighter.rgb;//Color::Rgb(0, 255, 0); - //if let Some([x, y, width, height]) = track_area { - //to.fill_fg([x, y, 1, height], bg); - //to.fill_fg([x + width, y, 1, height], bg); - //} - //if let Some([_, y, _, height]) = scene_area { - //to.fill_ul([area.x(), y - 1, area.w(), 1], bg); - //to.fill_ul([area.x(), y + height - 1, area.w(), 1], bg); - //} - //if focused { - //to.place(if let Some(clip_area) = clip_area { - //clip_area - //} else if let Some(track_area) = track_area { - //track_area.clip_h(HEADER_H) - //} else if let Some(scene_area) = scene_area { - //scene_area.clip_w(self.scenes_w) - //} else { - //area.clip_w(self.scenes_w).clip_h(HEADER_H) - //}, &self.reticle) - //}; - //} -//} -//impl Arranger { - //fn render_mode (state: &Self) -> impl Content + use<'_> { - //match state.mode { - //ArrangerMode::H => todo!("horizontal arranger"), - //ArrangerMode::V(factor) => Self::render_mode_v(state, factor), - //} - //} -//} -//render!(TuiOut: (self: Arranger) => { - //let pool_w = if self.pool.visible { self.splits[1] } else { 0 }; - //let color = self.color; - //let layout = Bsp::a(Fill::xy(ArrangerStatus::from(self)), - //Bsp::n(Fixed::x(pool_w, PoolView(self.pool.visible, &self.pool)), - //Bsp::n(TransportView::new(true, &self.clock), - //Bsp::s(Fixed::y(1, MidiEditStatus(&self.editor)), - //Bsp::n(Fill::x(Fixed::y(20, - //Bsp::a(Fill::xy(Tui::bg(color.darkest.rgb, "background")), - //Bsp::a( - //Fill::xy(Outer(Style::default().fg(color.dark.rgb).bg(color.darkest.rgb))), - //Self::render_mode(self))))), Fill::y(&"fixme: self.editor")))))); - //self.size.of(layout) -//}); - //Align::n(Fill::xy(lay!( - //Align::n(Fill::xy(Tui::bg(self.color.darkest.rgb, " "))), - //Align::n(Fill::xy(ArrangerVRowSep::from((self, 1)))), - //Align::n(Fill::xy(ArrangerVColSep::from(self))), - //Align::n(Fill::xy(ArrangerVClips::new(self, 1))), - //Align::n(Fill::xy(ArrangerVCursor::from((self, 1)))))))))))))))); - //Align::n(Fill::xy(":"))))))))))))); - //"todo:")))))))); - //Bsp::s( - //Align::n(Fixed::y(1, Fill::x(ArrangerVIns::from(self)))), - //Bsp::s( - //Fixed::y(20, Align::n(ArrangerVClips::new(self, 1))), - //Fill::x(Fixed::y(1, ArrangerVOuts::from(self))))))))))))); - //Bsp::s( - //Bsp::s( - //Bsp::s( - //Fill::xy(ArrangerVClips::new(self, 1)), - //Fill::x(ArrangerVOuts::from(self))))) -