use crate::*; pub struct ArrangementEditor { /// Global JACK client pub jack: Arc>, /// Global timebase pub clock: Arc, /// Name of arranger pub name: Arc>, /// Collection of phrases. pub phrases: Arc>>, /// Collection of tracks. pub tracks: Vec, /// Collection of scenes. pub scenes: Vec, /// Currently selected element. pub selected: ArrangementEditorFocus, /// Display mode of arranger pub mode: ArrangementViewMode, /// Whether the arranger is currently focused pub focused: bool, /// Background color of arrangement pub color: ItemColor, /// Width and height of arrangement area at last render pub size: Measure, /// Whether this is currently in edit mode pub entered: bool, } /// Display mode of arranger #[derive(PartialEq)] pub enum ArrangementViewMode { /// Tracks are rows Horizontal, /// Tracks are columns Vertical(usize), } impl Content for ArrangementEditor { type Engine = Tui; fn content (&self) -> impl Widget { Layers::new(move |add|{ match self.mode { ArrangementViewMode::Horizontal => { add(&HorizontalArranger(&self)) }, ArrangementViewMode::Vertical(factor) => { add(&VerticalArranger(&self, factor)) }, }?; add(&self.size) }) } } #[derive(PartialEq, Clone, Copy)] /// Represents the current user selection in the arranger pub enum ArrangementEditorFocus { /// The whole mix is selected Mix, /// A track is selected. Track(usize), /// A scene is selected. Scene(usize), /// A clip (track × scene) is selected. Clip(usize, usize), } /// Focus identification methods impl ArrangementEditorFocus { pub fn description ( &self, tracks: &Vec, scenes: &Vec, ) -> String { format!("Selected: {}", match self { Self::Mix => format!("Everything"), Self::Track(t) => match tracks.get(*t) { Some(track) => format!("T{t}: {}", &track.name.read().unwrap()), None => format!("T??"), }, Self::Scene(s) => match scenes.get(*s) { Some(scene) => format!("S{s}: {}", &scene.name.read().unwrap()), None => format!("S??"), }, Self::Clip(t, s) => match (tracks.get(*t), scenes.get(*s)) { (Some(_), Some(scene)) => match scene.clip(*t) { Some(clip) => format!("T{t} S{s} C{}", &clip.read().unwrap().name), None => format!("T{t} S{s}: Empty") }, _ => format!("T{t} S{s}: Empty"), } }) } pub fn is_mix (&self) -> bool { match self { Self::Mix => true, _ => false } } pub fn is_track (&self) -> bool { match self { Self::Track(_) => true, _ => false } } pub fn is_scene (&self) -> bool { match self { Self::Scene(_) => true, _ => false } } pub fn is_clip (&self) -> bool { match self { Self::Clip(_, _) => true, _ => false } } pub fn track (&self) -> Option { match self { Self::Clip(t, _) => Some(*t), Self::Track(t) => Some(*t), _ => None } } pub fn track_next (&mut self, last_track: usize) { *self = match self { Self::Mix => Self::Track(0), Self::Track(t) => Self::Track(last_track.min(*t + 1)), Self::Scene(s) => Self::Clip(0, *s), Self::Clip(t, s) => Self::Clip(last_track.min(*t + 1), *s), } } pub fn track_prev (&mut self) { *self = match self { Self::Mix => Self::Mix, Self::Scene(s) => Self::Scene(*s), Self::Track(t) => if *t == 0 { Self::Mix } else { Self::Track(*t - 1) }, Self::Clip(t, s) => if *t == 0 { Self::Scene(*s) } else { Self::Clip(t.saturating_sub(1), *s) } } } pub fn scene (&self) -> Option { match self { Self::Clip(_, s) => Some(*s), Self::Scene(s) => Some(*s), _ => None } } pub fn scene_next (&mut self, last_scene: usize) { *self = match self { Self::Mix => Self::Scene(0), Self::Track(t) => Self::Clip(*t, 0), Self::Scene(s) => Self::Scene(last_scene.min(*s + 1)), Self::Clip(t, s) => Self::Clip(*t, last_scene.min(*s + 1)), } } pub fn scene_prev (&mut self) { *self = match self { Self::Mix => Self::Mix, Self::Track(t) => Self::Track(*t), Self::Scene(s) => if *s == 0 { Self::Mix } else { Self::Scene(*s - 1) }, Self::Clip(t, s) => if *s == 0 { Self::Track(*t) } else { Self::Clip(*t, s.saturating_sub(1)) } } } }