use crate::*; impl> + Send + Sync> HasScenes for T {} pub trait HasScenes: Has> + Send + Sync { fn scenes (&self) -> &Vec { Has::>::get(self) } fn scenes_mut (&mut self) -> &mut Vec { Has::>::get_mut(self) } fn scenes_with_sizes ( &self, editing: bool, height: usize, larger: usize, selected_track: Option, selected_scene: Option, ) -> impl ScenesSizes<'_> { 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 }) } /// Generate the default name for a new scene fn scene_default_name (&self) -> Arc { format!("Sc{:3>}", self.scenes().len() + 1).into() } fn scene_longest_name (&self) -> usize { self.scenes().iter().map(|s|s.name.len()).fold(0, usize::max) } } pub trait HasSceneScroll: HasScenes { fn scene_scroll (&self) -> usize; } impl HasSceneScroll for Arrangement { fn scene_scroll (&self) -> usize { self.scene_scroll } } impl<'a> HasSceneScroll for ArrangerView<'a> { fn scene_scroll (&self) -> usize { self.arrangement.scene_scroll() } } pub type SceneWith<'a, T: Send + Sync> = (usize, &'a Scene, usize, usize, T); pub trait ScenesView: HasSceneScroll + Send + Sync { /// Default scene height. const H_SCENE: usize = 2; /// Default editor height. const H_EDITOR: usize = 15; /// Render scenes with clips fn scenes_view <'a> (&'a self, editor: &'a Option) -> impl Content + 'a { Tryptich::center(self.scenes_height()) .left(self.width_side(), self.scenes_names()) .middle(self.width_mid(), self.scenes_clips(editor)) } fn is_editing (&self) -> bool; fn arrangement (&self) -> &Arrangement; fn scene_last (&self) -> usize; fn scene_selected (&self) -> Option; fn track_selected (&self) -> Option; fn scenes_height (&self) -> u16; fn width_side (&self) -> u16; fn width_mid (&self) -> u16; fn scenes_names (&self) -> impl Content { let h = self.scenes_with_prev_color().last().map(|(_,_,_,h,_)|h as u16).unwrap_or(0); Fixed::y(h, Map::new(move||self.scenes_with_prev_color(), move|(s, scene, y1, y2, previous): SceneWith<'_, Option>, _|{ let height = (1 + y2 - y1) as u16; let name = Some(scene.name.clone()); let content = Fill::x(Align::w(Tui::bold(true, Bsp::e(" ⯈ ", name)))); let selected = self.scene_selected() == Some(s); let neighbor = s > 0 && self.scene_selected() == Some(s - 1); let is_last = self.scene_last() == s; let theme = scene.color; let fg = theme.lightest.rgb; let bg = if selected { theme.light } else { theme.base }.rgb; let hi = if let Some(previous) = previous { if neighbor { previous.light.rgb } else { previous.base.rgb } } else { Reset }; let lo = if is_last { Reset } else if selected { theme.light.rgb } else { theme.base.rgb }; Fill::x(map_south(y1 as u16, height, Fixed::y(height, Phat { width: 0, height: 0, content, colors: [fg, bg, hi, lo] }))) })) } fn scenes_names_2 (&self, theme: ItemTheme) -> impl Content { let h = self.scenes().len() as u16 * 2; let bg = theme.darker.rgb; Fixed::y(h, Tui::bg(bg, Align::w(Fill::x(Map::new( ||self.scenes().iter().skip(self.scene_scroll()), move|scene: &Scene, index| Push::y(index as u16 * 2u16, Fixed::xy(20, 2, Tui::bg(scene.color.dark.rgb, Align::nw(Bsp::e( format!(" {index:2} "), Tui::fg(Rgb(255, 255, 255), Tui::bold(true, format!("{}", scene.name))))))))))))) } fn scenes_with_prev_color (&self) -> impl Iterator>> + Send + Sync { self.scenes_iter().map(|(s, scene, y1, y2)|(s, scene, y1, y2, (s>0).then(||self.arrangement().scenes()[s-1].color))) } fn per_track <'a, T: Content + 'a, U: TracksSizes<'a>> ( tracks: impl Fn() -> U + Send + Sync + 'a, callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a ) -> impl Content + 'a { Map::new(tracks, move|(index, track, x1, x2): (usize, &'a Track, usize, usize), _|{ Tui::fg_bg(track.color.lightest.rgb, track.color.darker.rgb, map_east(x1 as u16, (x2 - x1) as u16, callback(index, track))) }) } fn scenes_clips <'a> (&'a self, editor: &'a Option) -> impl Content + 'a { let h = self.scenes_with_prev_color().last().map(|(_,_,_,h,_)|h as u16).unwrap_or(0); Fixed::y(h, Self::per_track(||self.tracks_with_sizes_scrolled(), move|track_index, track|Map::new(move||self.scenes_with_clip(track_index), move|(s, scene, y1, y2, previous): SceneWith<'_, Option>, _|{ let (name, theme) = if let Some(clip) = &scene.clips[track_index] { let clip = clip.read().unwrap(); (Some(clip.name.clone()), clip.color) } else { (None, ItemTheme::G[32]) }; let content = Fill::x(Align::w(Tui::bold(true, Bsp::e(" ⏹ ", name)))); let same_track = self.track_selected() == Some(track_index); let selected = same_track && self.scene_selected() == Some(s); let neighbor = same_track && s > 0 && self.scene_selected() == Some(s - 1); let is_last = self.scene_last() == s; let fg = theme.lightest.rgb; let bg = if selected { theme.light } else { theme.base }.rgb; let hi = if let Some(previous) = previous { if neighbor { previous.light.rgb } else { previous.base.rgb } } else { Reset }; let lo = if is_last { Reset } else if selected { theme.light.rgb } else { theme.base.rgb }; let height = (1 + y2 - y1) as u16; map_south(y1 as u16, height, Bsp::b(Fixed::y(height, Phat { width: 0, height: 0, content, colors: [fg, bg, hi, lo] }), When( self.is_editing() && same_track && self.scene_selected() == Some(s), editor ))) }))) } fn scenes_clips_2 (&self, theme: ItemTheme) -> impl Content { let h = self.scenes().len() as u16 * 2; let bg = theme.darker.rgb; Fixed::y(h, Tui::bg(bg, Align::w(Fill::x(Map::new( ||self.scenes().iter().skip(self.scene_scroll()), move|scene: &Scene, index1| Push::y(index1 as u16 * 2u16, Fixed::xy(20, 2, Map::new( move||scene.clips.iter().skip(self.track_scroll()), move|clip: &Option>>, index2|{ let (theme, text) = if let Some(clip) = clip { let clip = clip.read().unwrap(); (clip.color, clip.name.clone()) } else { (scene.color, Default::default()) }; Push::x(index2 as u16 * 14, Tui::bg(theme.dark.rgb, Bsp::e( format!(" {index1:2} {index2:2} "), Tui::fg(Rgb(255, 255, 255), Tui::bold(true, format!("{}", text)))))) })))))))) } fn scenes_with_clip (&self, track_index: usize) -> impl Iterator>> + Send + Sync { self.scenes_iter().map(move|(s, scene, y1, y2)|(s, scene, y1, y2, (s>0).then(||self.arrangement().scenes()[s-1].clips[track_index].as_ref() .map(|c|c.read().unwrap().color) .unwrap_or(ItemTheme::G[32])))) } /// A scene with size and color. fn scenes_iter (&self) -> impl Iterator + Send + Sync { let selection = Has::::get(self.arrangement()); self.arrangement().scenes_with_sizes( self.is_editing(), Self::H_SCENE, Self::H_EDITOR, selection.track(), selection.scene(), ).map_while(|(s, scene, y1, y2)|(y2<=self.scenes_height() as usize) .then_some((s, scene, y1, y2))) } fn tracks_with_sizes_scrolled <'t> (&'t self) -> impl TracksSizes<'t> { self.arrangement() .tracks_with_sizes( &self.arrangement().selection(), self.is_editing().then_some(20/*FIXME*/) ) .map_while(move|(t, track, x1, x2)|{ (self.width_mid() > x2 as u16).then_some((t, track, x1, x2)) }) } } impl<'a> ScenesView for ArrangerView<'a> { fn arrangement (&self) -> &Arrangement { self.arrangement } fn scenes_height (&self) -> u16 { self.scenes_height } fn width_side (&self) -> u16 { self.width_side } fn width_mid (&self) -> u16 { self.width_mid } fn scene_selected (&self) -> Option { self.arrangement.selection.scene() } fn scene_last (&self) -> usize { self.scene_last } fn track_selected (&self) -> Option { self.arrangement.selection.track() } fn is_editing (&self) -> bool { self.is_editing } } impl AddScene for T {} pub trait AddScene: HasScenes + HasTracks { /// Add multiple scenes fn scenes_add (&mut self, n: usize) -> Usually<()> { let scene_color_1 = ItemColor::random(); let scene_color_2 = ItemColor::random(); for i in 0..n { let _ = self.scene_add(None, Some( scene_color_1.mix(scene_color_2, i as f32 / n as f32).into() ))?; } Ok(()) } /// Add a scene fn scene_add (&mut self, name: Option<&str>, color: Option) -> Usually<(usize, &mut Scene)> { let scene = Scene { name: name.map_or_else(||self.scene_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])) } }