use crate::*; impl Arrangement { /// Width of display pub(crate) fn w (&self) -> u16 { self.size.w() as u16 } /// Width allocated for sidebar. 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 // FIXME //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() + self.h_devices() + 13 // FIXME ) } /// Height taken by all scenes. pub(crate) fn h_scenes (&self) -> u16 { let (selected_track, selected_scene) = match Has::>::get(self) { Some(Selection::Track(t)) => (Some(*t), None), Some(Selection::Scene(s)) => (None, Some(*s)), Some(Selection::TrackClip { track, scene }) => (Some(*track), Some(*scene)), _ => (None, None) }; self.scenes_with_sizes( self.is_editing, ArrangerView::H_SCENE, ArrangerView::H_EDITOR, selected_track, selected_scene ) .last() .map(|(_, _, _, y)|y as u16).unwrap_or(0) } /// Height taken by all inputs. pub(crate) fn h_inputs (&self) -> u16 { 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 { self.outputs_with_sizes() .last() .map(|(_, _, _, _, y)|y as u16).unwrap_or(0) } /// Height taken by visible device slots. pub(crate) fn h_devices (&self) -> u16 { 2 //1 + self.devices_with_sizes().last().map(|(_, _, _, _, y)|y as u16).unwrap_or(0) } } pub(crate) struct ArrangerView<'a> { pub arrangement: &'a Arrangement, pub is_editing: bool, pub width: u16, pub width_mid: u16, pub width_side: u16, pub inputs_count: usize, pub inputs_height: u16, pub outputs_count: usize, pub outputs_height: u16, pub scene_last: usize, pub scene_count: usize, pub scene_scroll: Fill>, pub scene_selected: Option, pub scenes_height: u16, pub track_scroll: Fill>, pub track_count: usize, pub track_selected: Option, pub tracks_height: u16, pub show_debug_info: bool, } impl<'a> ArrangerView<'a> { pub fn new ( arrangement: &'a Arrangement, editor: Option<&'a MidiEditor> ) -> Self { let selected = arrangement.selected; let h_tracks_area = arrangement.h_tracks_area(); let h_scenes_area = arrangement.h_scenes_area(); let h_scenes = arrangement.h_scenes(); Self { arrangement, is_editing: editor.is_some(), width: arrangement.w(), width_mid: arrangement.w_tracks_area(), width_side: arrangement.w_sidebar(), inputs_height: arrangement.h_inputs(), inputs_count: arrangement.midi_ins.len(), outputs_height: arrangement.h_outputs(), outputs_count: arrangement.midi_outs.len(), scenes_height: h_scenes_area, scene_selected: selected.map(|s|s.scene()).flatten(), scene_count: arrangement.scenes.len(), scene_last: arrangement.scenes.len().saturating_sub(1), scene_scroll: Fill::y(Fixed::x(1, ScrollbarV { offset: arrangement.scene_scroll, length: h_scenes_area as usize, total: h_scenes as usize, })), tracks_height: h_tracks_area, track_count: arrangement.tracks.len(), track_selected: selected.map(|s|s.track()).flatten(), track_scroll: Fill::x(Fixed::y(1, ScrollbarH { offset: arrangement.track_scroll, length: h_tracks_area as usize, total: h_scenes as usize, })), show_debug_info: false } } } impl<'a> Content for ArrangerView<'a> { fn content (&self) -> impl Render { let ins = |x|Bsp::n(self.inputs(), x); let tracks = |x|Bsp::s(self.tracks(), x); let devices = |x|Bsp::s(self.devices(), x); let outs = |x|Bsp::s(self.outputs(), x); let bg = |x|Tui::bg(Reset, x); //let track_scroll = |x|Bsp::s(&self.track_scroll, x); //let scene_scroll = |x|Bsp::e(&self.scene_scroll, x); outs(tracks(devices(ins(bg(self.scenes(None)))))) } } impl<'a> ArrangerView<'a> { /// Render input matrix. pub(crate) fn inputs (&'a self) -> impl Content + 'a { Tui::bg(Reset, Bsp::s( self.input_intos(), Bsp::s(self.input_routes(), self.input_ports()), )) } /// Render output matrix. pub(crate) fn outputs (&'a self) -> impl Content + 'a { Tui::bg(Reset, Align::n(Bsp::s( Bsp::s(self.output_ports(), self.output_conns()), Bsp::s(self.output_nexts(), self.output_froms()), ))) } /// Render track headers pub(crate) fn tracks (&'a self) -> impl Content + 'a { let Self { width_side, width_mid, track_count, track_selected, is_editing, .. } = self; Tryptich::center(3) .left(*width_side, button_3("t", "track", format!("{}", *track_count), *is_editing)) .right(*width_side, button_2("T", "add track", *is_editing)) .middle(*width_mid, per_track(*width_mid, ||self.tracks_with_sizes_scrolled(), |index, track|wrap( if *track_selected == Some(index) { track.color.light } else { track.color.base }.rgb, track.color.lightest.rgb, Tui::bold(true, Fill::xy(Align::nw(&track.name))) ))) } /// Render device switches. pub(crate) fn devices (&'a self) -> impl Content + 'a { let Self { width_side, width_mid, track_count, track_selected, is_editing, .. } = self; Tryptich::top(1) .left(*width_side, button_3("d", "devices", format!("{}", 0), *is_editing)) .right(*width_side, button_2("D", "add device", *is_editing)) .middle(*width_mid, per_track_top(*width_mid, ||self.tracks_with_sizes_scrolled(), move|index, track|{ let bg = if *track_selected == Some(index) { track.color.light } else { track.color.base }; let fg = Tui::g(224); track.devices.get(0).map(|device|wrap(bg.rgb, fg, device.name())) })) } }