use crate::*; mod view_clock; pub use self::view_clock::*; mod view_color; pub use self::view_color::*; mod view_memo; pub use self::view_memo::*; mod view_meter; pub use self::view_meter::*; mod view_track; pub use self::view_track::*; mod view_ports; pub use self::view_ports::*; mod view_layout; pub use self::view_layout::*; pub(crate) use std::fmt::Write; pub(crate) use ::tengri::tui::ratatui::prelude::Position; pub(crate) trait ScenesColors<'a> = Iterator>; pub(crate) type SceneWithColor<'a> = (usize, &'a Scene, usize, usize, Option); pub(crate) struct ArrangerView<'a> { app: &'a Tek, is_editing: bool, width: u16, width_mid: u16, width_side: u16, inputs_count: usize, inputs_height: u16, outputs_count: usize, outputs_height: u16, scene_last: usize, scene_count: usize, scene_scroll: Fill>, scene_selected: Option, scenes_height: u16, track_scroll: Fill>, track_count: usize, track_selected: Option, tracks_height: u16, show_debug_info: bool, } impl<'a> Content for ArrangerView<'a> { fn content (&self) -> impl Render { let ins = |x|Bsp::s(self.inputs(), x); let tracks = |x|Bsp::s(self.tracks(), x); let outs = |x|Bsp::n(self.outputs(), x); let bg = |x|Tui::bg(Color::Reset, x); //let track_scroll = |x|Bsp::s(&self.track_scroll, x); //let scene_scroll = |x|Bsp::e(&self.scene_scroll, x); ins(tracks(outs(bg(self.scenes())))) } } impl<'a> ArrangerView<'a> { pub fn new (app: &'a Tek) -> Self { Self { app, is_editing: app.is_editing(), width: app.w(), width_mid: app.w_tracks_area(), width_side: app.w_sidebar(), inputs_height: app.h_inputs().saturating_sub(1), inputs_count: app.midi_ins.len(), outputs_height: app.h_outputs().saturating_sub(1), outputs_count: app.midi_outs.len(), scenes_height: app.h_scenes_area(), 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, length: app.h_scenes_area() as usize, total: app.h_scenes() as usize, })), tracks_height: app.h_tracks_area(), track_count: app.tracks.len(), track_selected: app.selected().track(), track_scroll: Fill::x(Fixed::y(1, ScrollbarH { offset: app.track_scroll, length: app.h_tracks_area() as usize, total: app.h_scenes() as usize, })), show_debug_info: false } } pub(crate) fn tracks_with_sizes_scrolled (&'a self) -> impl TracksSizes<'a> { let width = self.width_mid; self.app.tracks_with_sizes().map_while(move|(t, track, x1, x2)|{ (width > x2 as u16).then_some((t, track, x1, x2)) }) } pub(crate) fn scenes_with_scene_colors (&self) -> impl ScenesColors<'_> { self.app.scenes_with_sizes(self.is_editing, Tek::H_SCENE, Tek::H_EDITOR).map_while( move|(s, scene, y1, y2)|if y2 as u16 > self.scenes_height { None } else { Some((s, scene, y1, y2, if s == 0 { None } else { Some(self.app.scenes()[s-1].color) })) }) } pub(crate) fn scenes_with_track_colors (&self) -> impl ScenesColors<'_> { self.app.scenes_with_sizes(self.is_editing, Tek::H_SCENE, Tek::H_EDITOR).map_while( move|(s, scene, y1, y2)|if y2 as u16 > self.scenes_height { None } else { Some((s, scene, y1, y2, if s == 0 { None } else { Some(self.app.scenes[s-1].clips[self.track_selected.unwrap_or(0)].as_ref() .map(|c|c.read().unwrap().color) .unwrap_or(ItemPalette::G[32])) })) } ) } } impl Tek { /// Spacing between tracks. pub(crate) const TRACK_SPACING: usize = 0; /// Default scene height. pub(crate) const H_SCENE: usize = 2; /// Default editor height. pub(crate) const H_EDITOR: usize = 15; /// Width of display pub(crate) fn w (&self) -> u16 { self.size.w() as u16 } pub(crate) fn w_sidebar (&self) -> u16 { self.w() / if self.is_editing() { 16 } else { 8 } as u16 } /// Width taken by all tracks. pub(crate) fn w_tracks (&self) -> u16 { self.tracks_with_sizes().last().map(|(_, _, _, x)|x as u16).unwrap_or(0) } /// Width available to display tracks. pub(crate) fn w_tracks_area (&self) -> u16 { self.w().saturating_sub(2 * self.w_sidebar()) } /// Height of display pub(crate) fn h (&self) -> u16 { self.size.h() as u16 } /// Height available to display track headers. pub(crate) fn h_tracks_area (&self) -> u16 { 5 //self.h().saturating_sub(self.h_inputs() + self.h_outputs()) } /// Height available to display tracks. pub(crate) fn h_scenes_area (&self) -> u16 { //15 self.h().saturating_sub(self.h_inputs() + self.h_outputs() + 11) } /// Height taken by all inputs. pub(crate) fn h_inputs (&self) -> u16 { 1 + self.inputs_with_sizes().last().map(|(_, _, _, _, y)|y as u16).unwrap_or(0) } /// Height taken by all outputs. pub(crate) fn h_outputs (&self) -> u16 { 1 + self.outputs_with_sizes().last().map(|(_, _, _, _, y)|y as u16).unwrap_or(0) } /// Height taken by all scenes. pub(crate) fn h_scenes (&self) -> u16 { self.scenes_with_sizes(self.is_editing(), Self::H_SCENE, Self::H_EDITOR).last() .map(|(_, _, _, y)|y as u16).unwrap_or(0) } pub(crate) fn inputs_with_sizes (&self) -> impl PortsSizes<'_> { let mut y = 0; self.midi_ins.iter().enumerate().map(move|(i, input)|{ let height = 1 + input.conn().len(); let data = (i, input.name(), input.conn(), y, y + height); y += height; data }) } pub(crate) fn outputs_with_sizes (&self) -> impl PortsSizes<'_> { let mut y = 0; self.midi_outs.iter().enumerate().map(move|(i, output)|{ let height = 1 + output.conn().len(); let data = (i, output.name(), output.conn(), y, y + height); y += height; data }) } pub(crate) fn tracks_with_sizes (&self) -> impl TracksSizes<'_> { let mut x = 0; let editing = self.is_editing(); let active = match self.selected() { Selection::Track(t) if editing => Some(t), Selection::Clip(t, _) if editing => Some(t), _ => None }; let bigger = self.editor_w(); self.tracks().iter().enumerate().map(move |(index, track)|{ let width = if Some(index) == active.copied() { bigger } else { track.width.max(8) }; let data = (index, track, x, x + width); x += width + Tek::TRACK_SPACING; data }) } pub(crate) fn scenes_with_sizes (&self, editing: bool, height: usize, larger: usize) -> impl ScenesSizes<'_> { let (selected_track, selected_scene) = match self.selected() { Selection::Track(t) => (Some(*t), None), Selection::Scene(s) => (None, Some(*s)), Selection::Clip(t, s) => (Some(*t), Some(*s)), _ => (None, None) }; 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 }) } } /// Define a type alias for iterators of sized items (columns). macro_rules! def_sizes_iter { ($Type:ident => $($Item:ty),+) => { pub(crate) trait $Type<'a> = Iterator + Send + Sync + 'a;}} def_sizes_iter!(ScenesSizes => Scene); def_sizes_iter!(TracksSizes => Track); def_sizes_iter!(InputsSizes => JackMidiIn); def_sizes_iter!(OutputsSizes => JackMidiOut); def_sizes_iter!(PortsSizes => Arc, [PortConnect]); #[cfg(test)] #[test] fn test_view_iter () { let mut tek = Tek::default(); tek.editor = Some(Default::default()); let _: Vec<_> = tek.inputs_with_sizes().collect(); let _: Vec<_> = tek.outputs_with_sizes().collect(); let _: Vec<_> = tek.tracks_with_sizes().collect(); let _: Vec<_> = tek.scenes_with_sizes(true, 10, 10).collect(); //let _: Vec<_> = tek.scenes_with_colors(true, 10).collect(); //let _: Vec<_> = tek.scenes_with_track_colors(true, 10, 10).collect(); } #[cfg(test)] #[test] fn test_view_sizes () { let app = Tek::default(); let _ = app.w(); let _ = app.w_sidebar(); let _ = app.w_tracks_area(); let _ = app.h(); let _ = app.h_tracks_area(); let _ = app.h_inputs(); let _ = app.h_outputs(); let _ = app.h_scenes(); }