use super::*; /// A scene consists of a set of clips to play together. /// /// ``` /// let scene: tek::Scene = Default::default(); /// let _ = scene.pulses(); /// let _ = scene.is_playing(&[]); /// ``` #[derive(Debug, Default)] pub struct Scene { /// Name of scene pub name: Arc, /// Identifying color of scene pub color: ItemTheme, /// Clips in scene, one per track pub clips: Vec>>>, } impl Scene { pub const DEFAULT_HEIGHT: usize = 2; /// Get currently playing clip, if any pub fn clip (&self, index: usize) -> Option<&Arc>> { if let Some(Some(clip)) = self.clips.get(index) { Some(clip) } else { None } } /// Get pulse length of the longest clip in the scene pub fn pulses (&self) -> usize { self.clips.iter().fold(0, |a, p|{ a.max(p.as_ref().map(|q|q.try_read().unwrap().length).unwrap_or(0)) }) } /// True if all clips in scene are currently playing on given tracks. pub fn is_playing (&self, tracks: &[Track]) -> bool { self.clips.iter().any(|clip|clip.is_some()) && self.clips.iter().enumerate() .all(|(track_index, clip)|match clip { Some(c) => tracks .get(track_index) .map(|track|{ if let Some((_, Some(clip))) = track.sequencer().play_clip() { *clip.try_read().unwrap() == *c.try_read().unwrap() } else { false } }) .unwrap_or(false), None => true }) } } #[cfg(all(feature = "select"))] impl_as_ref_opt!(Scene: |self: App| self.project.as_ref_opt()); #[cfg(all(feature = "select"))] impl_as_mut_opt!(Scene: |self: App| self.project.as_mut_opt()); #[cfg(all(feature = "select"))] impl_as_ref_opt!(Scene: |self: Arrangement| self.selected_scene()); #[cfg(all(feature = "select"))] impl_as_mut_opt!(Scene: |self: Arrangement| self.selected_scene_mut()); impl + AsMutOpt + Send + Sync> HasScene for T {} pub trait HasScene: AsRefOpt + AsMutOpt { fn scene (&self) -> Option<&Scene> { self.as_ref_opt() } fn scene_mut (&mut self) -> Option<&mut Scene> { self.as_mut_opt() } } impl + Namespace> + Namespace > SceneController for T {} #[tek_proc::commands(SceneCommand)] pub trait SceneController: HasScene + Namespace + Namespace> + Namespace { #[command(SetSize = "size")] fn scene_set_size (&mut self, _size: usize) -> Perhaps where Self: Namespace { todo!() } #[command(SetZoom = "zoom")] fn scene_set_zoom (&mut self, _size: usize) -> Perhaps where Self: Namespace { todo!() } #[command(SetName = "name")] fn scene_set_name (&mut self, name: Arc) -> Perhaps where Self: Namespace> { Ok(self.scene_mut().map(|scene|swap_value( &mut scene.name, &name, |name|SceneCommand::SetName { name } )).transpose()?.flatten()) } #[command(SetColor = "color")] fn scene_set_color (&mut self, color: ItemTheme) -> Perhaps where Self: Namespace { Ok(self.scene_mut().map(|scene|swap_value( &mut scene.color, &color, |color|SceneCommand::SetColor { color } )).transpose()?.flatten()) } } pub type SceneWith<'a, T> = (usize, &'a Scene, usize, usize, T); def_sizes_iter!(ScenesSizes => Scene); impl_has!(Vec: |self: Arrangement| self.scenes); impl_as_ref!(Vec: |self: App| self.project.as_ref()); impl_as_mut!(Vec: |self: App| self.project.as_mut()); impl> + AsMut>> HasScenes for T {} pub trait HasScenes: AsRef> + AsMut> { fn scenes (&self) -> &Vec { self.as_ref() } fn scenes_mut (&mut self) -> &mut Vec { self.as_mut() } /// Generate the default name for a new scene fn scenes_default_name (&self) -> Arc { format!("s{:3>}", self.scenes().len() + 1).into() } fn scenes_longest_name (&self) -> usize { self.scenes().iter().map(|s|s.name.len()).fold(0, usize::max) } /// Add multiple scenes fn scenes_add_many (&mut self, n: usize) -> Usually<()> where Self: HasTracks { let scene_color_1 = ItemColor::random(); let scene_color_2 = ItemColor::random(); for i in 0..n { let _ = self.scenes_add_one(None, Some( scene_color_1.mix(scene_color_2, i as f32 / n as f32).into() ))?; } Ok(()) } /// Add a scene fn scenes_add_one (&mut self, name: Option>, color: Option) -> Usually<(usize, &mut Scene)> where Self: HasTracks { let scene = Scene { name: name.map_or_else(||self.scenes_default_name(), |x|x.to_string().into()), clips: vec![None;self.tracks().len()], color: color.unwrap_or_else(ItemTheme::random), }; self.scenes_mut().push(scene); let index = self.scenes().len() - 1; Ok((index, &mut self.scenes_mut()[index])) } } impl + Namespace + Namespace> + Namespace>> > ScenesController for T {} #[tek_proc::commands(ScenesCommand)] pub trait ScenesController: HasScenes + HasTracks + HasTrackScroll + HasJack<'static> + Namespace + Namespace> + Namespace>> { #[command(Add = "add")] fn scenes_add (&mut self) -> Perhaps where Self: HasScenes + HasJack<'static> { let (_index, _) = self.scenes_add_one(None, None)?; Ok(None) } // TODO } pub trait ScenesView: HasEditor + HasSelection + HasSceneScroll + HasClipsSize + Send + Sync { fn h_scenes (&self) -> u16; fn w_side (&self) -> u16; fn w_mid (&self) -> u16; fn view_scenes_names (&self) -> impl Draw { let select = self.selection(); let editor = self.editor(); let editing = self.is_editing(); draw(move |to: &mut Tui|{ for (index, scene, ..) in self.scenes_with_sizes() { view_scene_name(select, editor, index, scene, editing).draw(to)?; } Ok(Some(XYWH(1, 1, 1, 1))) }) .exact_w(20) } fn scenes_with_sizes (&self) -> impl ScenesSizes<'_> { let mut y = 0; self.scenes().iter().enumerate().skip(self.scene_scroll()).map_while(move|(s, scene)|{ let height = if self.selection().scene() == Some(s) && self.editor().is_some() { 8 } else { Scene::DEFAULT_HEIGHT }; if y + height <= self.clips_size().h() as usize { let data = (s, scene, y, y + height); y += height; Some(data) } else { None } }) } } impl ScenesView for App { fn w_mid (&self) -> u16 { (self.size.w() as u16).saturating_sub(self.w_side()) } fn w_side (&self) -> u16 { 20 } fn h_scenes (&self) -> u16 { (self.size.h() as u16).saturating_sub(20) } } impl ScenesView for Arrangement { fn h_scenes (&self) -> u16 { (self.size.h() as u16).saturating_sub(20) } fn w_side (&self) -> u16 { (self.size.w() as u16 * 2 / 10).max(20) } fn w_mid (&self) -> u16 { (self.size.w() as u16).saturating_sub(2 * self.w_side()).max(40) } } pub trait HasSceneScroll: HasScenes { fn scene_scroll (&self) -> usize; } impl HasSceneScroll for Arrangement { fn scene_scroll (&self) -> usize { self.scene_scroll } } impl HasSceneScroll for App { fn scene_scroll (&self) -> usize { self.project.scene_scroll() } } pub fn view_scene_name <'a> ( select: &Selection, editor: Option<&'a MidiEditor>, index: usize, scene: &Scene, editing: bool ) -> impl Draw { let h = if select.scene() == Some(index) && let Some(_editor) = editor { 7 } else { Scene::DEFAULT_HEIGHT as u16 }; let a = east(format!("·s{index:02} "), fg(g(255), bold(true, &scene.name))).align_w().full_w(); let b = when(select.scene() == Some(index) && editing, south( editor.as_ref().map(|e|e.clip_status()), editor.as_ref().map(|e|e.edit_status())).align_nw().full_wh()); let c = if select.scene() == Some(index) { scene.color.light.term } else { scene.color.base.term }; bg(c, south(a, b).align_nw()).exact_wh(20, h) }