use crate::*; impl> HasSelection for T {} pub trait HasSelection: Has { fn selection (&self) -> &Selection { self.get() } fn selection_mut (&mut self) -> &mut Selection { self.get_mut() } } impl Has> for Arrangement { fn get (&self) -> &Option { Has::>::get(self) .and_then(|selection|selection.track()) .and_then(|index|Has::>::get(self).get(index)) } fn get_mut (&mut self) -> &mut Option { Has::>::get(self) .and_then(|selection|selection.track()) .and_then(|index|Has::>::get_mut(self).get_mut(index)) } } impl Has> for Arrangement { fn get (&self) -> &Option { Has::>::get(self) .and_then(|selection|selection.track()) .and_then(|index|Has::>::get(self).get(index)) } fn get_mut (&mut self) -> &mut Option { Has::>::get(self) .and_then(|selection|selection.track()) .and_then(|index|Has::>::get_mut(self).get_mut(index)) } } /// Represents the current user selection in the arranger #[derive(PartialEq, Clone, Copy, Debug, Default)] pub enum Selection { /// The whole mix is selected #[default] Mix, /// A MIDI input is selected. Input(usize), /// A MIDI output is selected. Output(usize), /// A scene is selected. Scene(usize), /// A track is selected. Track(usize), /// A clip (track × scene) is selected. TrackClip { track: usize, scene: usize }, /// A track's MIDI input connection is selected. TrackInput { track: usize, port: usize }, /// A track's MIDI output connection is selected. TrackOutput { track: usize, port: usize }, /// A track device slot is selected. TrackDevice { track: usize, device: usize }, } /// Focus identification methods impl Selection { pub fn is_mix (&self) -> bool { matches!(self, Self::Mix) } pub fn is_track (&self) -> bool { matches!(self, Self::Track(_)) } pub fn is_scene (&self) -> bool { matches!(self, Self::Scene(_)) } pub fn is_clip (&self) -> bool { matches!(self, Self::TrackClip {..}) } pub fn track (&self) -> Option { use Selection::*; match self { Track(track) | TrackClip { track, .. } | TrackInput { track, .. } | TrackOutput { track, .. } | TrackDevice { track, .. } => Some(*track), _ => None } } pub fn track_header (&self, track_count: usize) -> Self { use Selection::*; match self { Mix => Track(0), Scene(_) => Mix, Track(t) => Track((t + 1) % track_count), TrackClip { track, .. } => Track(*track), _ => todo!(), } } pub fn track_next (&self, len: usize) -> Self { use Selection::*; match self { Mix => Track(0), Scene(s) => TrackClip { track: 0, scene: *s }, Track(t) => if t + 1 < len { Track(t + 1) } else { Mix }, TrackClip {track, scene} => if track + 1 < len { TrackClip { track: track + 1, scene: *scene } } else { Scene(*scene) }, _ => todo!() } } pub fn track_prev (&self) -> Self { use Selection::*; match self { Mix => Mix, Scene(s) => Scene(*s), Track(0) => Mix, Track(t) => Track(t - 1), TrackClip { track: 0, scene } => Scene(*scene), TrackClip { track: t, scene } => TrackClip { track: t - 1, scene: *scene }, _ => todo!() } } pub fn scene (&self) -> Option { use Selection::*; match self { Scene(scene) | TrackClip { scene, .. } => Some(*scene), _ => None } } pub fn scene_next (&self, len: usize) -> Self { use Selection::*; match self { Mix => Scene(0), Track(t) => TrackClip { track: *t, scene: 0 }, Scene(s) => if s + 1 < len { Scene(s + 1) } else { Mix }, TrackClip { track, scene } => if scene + 1 < len { TrackClip { track: *track, scene: scene + 1 } } else { Track(*track) }, _ => todo!() } } pub fn scene_prev (&self) -> Self { use Selection::*; match self { Mix | Scene(0) => Mix, Scene(s) => Scene(s - 1), Track(t) => Track(*t), TrackClip { track, scene: 0 } => Track(*track), TrackClip { track, scene } => TrackClip { track: *track, scene: scene - 1 }, _ => todo!() } } pub fn describe (&self, tracks: &[Track], scenes: &[Scene]) -> Arc { use Selection::*; format!("{}", match self { Mix => "Everything".to_string(), Scene(s) => scenes.get(*s) .map(|scene|format!("S{s}: {}", &scene.name)) .unwrap_or_else(||"S??".into()), Track(t) => tracks.get(*t) .map(|track|format!("T{t}: {}", &track.name)) .unwrap_or_else(||"T??".into()), TrackClip { track, scene } => match (tracks.get(*track), scenes.get(*scene)) { (Some(_), Some(s)) => match s.clip(*track) { Some(clip) => format!("T{track} S{scene} C{}", &clip.read().unwrap().name), None => format!("T{track} S{scene}: Empty") }, _ => format!("T{track} S{scene}: Empty"), }, _ => todo!() }).into() } } impl Arrangement { }