use crate::*; /// Arranger. /// /// ``` /// let arranger = tek::Arrangement::default(); /// ``` #[derive(Default, Debug)] pub struct Arrangement { /// JACK client handle. pub jack: Jack<'static>, /// Project name. pub name: Arc, /// Base color. pub color: ItemTheme, /// FIXME a render of the project arrangement, redrawn on update. /// TODO rename to "render_cache" or smth pub arranger: Arc>, /// Display size pub size: Sizer, /// Display size of clips area pub size_inner: Sizer, /// Selected UI element pub selection: Selection, /// Source of time #[cfg(feature = "clock")] pub clock: Clock, /// Allows one MIDI clip to be edited #[cfg(feature = "editor")] pub editor: Option, /// List of global midi inputs #[cfg(feature = "port")] pub midi_ins: Vec, /// List of global midi outputs #[cfg(feature = "port")] pub midi_outs: Vec, /// List of global audio inputs #[cfg(feature = "port")] pub audio_ins: Vec, /// List of global audio outputs #[cfg(feature = "port")] pub audio_outs: Vec, /// Last track number (to avoid duplicate port names) #[cfg(feature = "track")] pub track_last: usize, /// List of tracks #[cfg(feature = "track")] pub tracks: Vec, /// Scroll offset of tracks #[cfg(feature = "track")] pub track_scroll: usize, /// List of scenes #[cfg(feature = "scene")] pub scenes: Vec, /// Scroll offset of scenes #[cfg(feature = "scene")] pub scene_scroll: usize, } impl Arrangement { /// Create a new arrangement. pub fn new ( jack: &Jack<'static>, name: Arc, clock: Clock, tracks: impl Iterator, scenes: impl Iterator, midi_ins: impl Iterator, midi_outs: impl Iterator, audio_ins: impl Iterator, audio_outs: impl Iterator, ) -> Self { Self { jack: jack.clone(), color: ItemTheme::random(), selection: Selection::TrackClip { track: 0, scene: 0 }, tracks: tracks.collect(), scenes: scenes.collect(), midi_ins: midi_ins.collect(), midi_outs: midi_outs.collect(), audio_ins: audio_ins.collect(), audio_outs: audio_outs.collect(), size_inner: Sizer(Arc::new(40.into()), Arc::new(25.into())), clock, name, ..Default::default() } } /// Width of display pub fn w (&self) -> u16 { self.size.w() as u16 } /// Width allocated for sidebar. pub fn w_sidebar (&self, is_editing: bool) -> u16 { self.w() / if is_editing { 16 } else { 8 } as u16 } /// Width available to display tracks. pub fn w_tracks_area (&self, is_editing: bool) -> u16 { self.w().saturating_sub(self.w_sidebar(is_editing)) } /// Height of display pub fn h (&self) -> u16 { self.size.h() as u16 } /// Height taken by visible device slots. pub fn h_devices (&self) -> u16 { 2 //1 + self.devices_with_sizes().last().map(|(_, _, _, _, y)|y as u16).unwrap_or(0) } } /// Get the first sampler of the active track #[cfg(feature = "sampler")] impl Arrangement { pub fn sampler (&self) -> Option<&Sampler> { self.selected_track()?.sampler(0) } /// Get the first sampler of the active track #[cfg(feature = "sampler")] pub fn sampler_mut (&mut self) -> Option<&mut Sampler> { self.selected_track_mut()?.sampler_mut(0) } } impl HasJack<'static> for Arrangement { fn jack (&self) -> &Jack<'static> { &self.jack } } impl_has!(Jack<'static>: |self: Arrangement| self.jack); impl_has!(Sizer: |self: Arrangement| self.size); impl_has!(Clock: |self: Arrangement| self.clock); impl_has!(Selection: |self: Arrangement| self.selection); impl_as_ref_opt!(MidiEditor: |self: Arrangement| self.editor.as_ref()); impl_as_mut_opt!(MidiEditor: |self: Arrangement| self.editor.as_mut()); /// Define a type alias for iterators of sized items (columns). macro_rules! def_sizes_iter { ($Type:ident => $($Item:ty),+) => { pub trait $Type<'a>: Iterator + Send + Sync + 'a {} impl<'a, T: Iterator + Send + Sync + 'a> $Type<'a> for T {} } } mod clip; pub use self::clip::*; mod scene; pub use self::scene::*; mod select; pub use self::select::*; mod port; pub use self::port::*; mod track; pub use self::track::*; pub(self) fn swap_value ( target: &mut T, value: &T, returned: impl Fn(T)->U ) -> Perhaps { if *target == *value { Ok(None) } else { let mut value = value.clone(); std::mem::swap(target, &mut value); Ok(Some(returned(value))) } } pub(self) fn toggle_bool ( target: &mut bool, value: &Option, returned: impl Fn(Option)->U ) -> Perhaps { let mut value = value.unwrap_or(!*target); if value == *target { Ok(None) } else { std::mem::swap(target, &mut value); Ok(Some(returned(Some(value)))) } } //pub fn per_track <'a, T: Draw<'_, Tui> + 'a, U: TracksSizes<'a>> ( //tracks: impl Fn() -> U + Send + Sync + 'a, //callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a //) -> impl Draw<'_, Tui> + 'a { //per_track_top(tracks, move|index, track|callback(index, track).full_h().align_y()) //} //pub fn per_track_top <'a, T: Draw<'_, Tui> + 'a, U: TracksSizes<'a>> ( //tracks: impl Fn() -> U + Send + Sync + 'a, //callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a //) -> impl Draw<'_, Tui> + 'a { //bg(Reset, iter_east(||tracks() //.map(move|(index, track, x1, x2): (usize, &'a Track, usize, usize)|{ //fg_bg( //track.color.lightest.term, //track.color.base.term, //callback(index, track) //).exact_w((x2 - x1) as u16) //})).align_x()) //}