diff --git a/crates/tek_api/src/arrange.rs b/crates/tek_api/src/arrange.rs index 4a338a17..b180783c 100644 --- a/crates/tek_api/src/arrange.rs +++ b/crates/tek_api/src/arrange.rs @@ -42,9 +42,66 @@ impl Arrangement { pub fn is_stopped (&self) -> bool { *self.clock.playing.read().unwrap() == Some(TransportState::Stopped) } + pub fn track_default_name (&self) -> String { + format!("Track {}", self.tracks.len() + 1) + } + pub fn scene_default_name (&self) -> String { + format!("Scene {}", self.scenes.len() + 1) + } + pub fn track_add ( + &mut self, name: Option<&str>, color: Option + ) -> Usually<&mut ArrangementTrack> { + let name = name.map_or_else(||self.track_default_name(), |x|x.to_string()); + self.tracks.push(ArrangementTrack { + width: name.len() + 2, + color: color.unwrap_or_else(||ItemColor::random()), + player: MIDIPlayer::new(&self.jack, &self.clock, name.as_str())?, + name: Arc::new(name.into()), + }); + let index = self.tracks.len() - 1; + Ok(&mut self.tracks[index]) + } + pub fn scene_add ( + &mut self, name: Option<&str>, color: Option + ) -> Usually<&mut ArrangementScene> { + let name = name.map_or_else(||self.scene_default_name(), |x|x.to_string()); + self.scenes.push(ArrangementScene { + name: Arc::new(name.into()), + clips: vec![None;self.tracks.len()], + color: color.unwrap_or_else(||ItemColor::random()), + }); + let index = self.scenes.len() - 1; + Ok(&mut self.scenes[index]) + } + pub fn track_del (&mut self, index: usize) { + self.tracks.remove(index); + for scene in self.scenes.iter_mut() { + scene.clips.remove(index); + } + } + pub fn scene_del (&mut self, index: usize) { + self.scenes.remove(index); + } } impl ArrangementScene { + pub fn ppqs (scenes: &[Self], factor: usize) -> Vec<(usize, usize)> { + let mut total = 0; + if factor == 0 { + scenes.iter().map(|scene|{ + let pulses = scene.pulses().max(PPQ); + total = total + pulses; + (pulses, total - pulses) + }).collect() + } else { + (0..=scenes.len()).map(|i|{ + (factor*PPQ, factor*PPQ*i) + }).collect() + } + } + pub fn longest_name (scenes: &[Self]) -> usize { + scenes.iter().map(|s|s.name.read().unwrap().len()).fold(0, usize::max) + } /// Returns the pulse length of the longest phrase in the scene pub fn pulses (&self) -> usize { self.clips.iter().fold(0, |a, p|{ @@ -100,23 +157,6 @@ impl ArrangementScene { //clips, //}) //} - pub fn ppqs (scenes: &[Self], factor: usize) -> Vec<(usize, usize)> { - let mut total = 0; - if factor == 0 { - scenes.iter().map(|scene|{ - let pulses = scene.pulses().max(PPQ); - total = total + pulses; - (pulses, total - pulses) - }).collect() - } else { - (0..=scenes.len()).map(|i|{ - (factor*PPQ, factor*PPQ*i) - }).collect() - } - } - pub fn longest_name (scenes: &[Self]) -> usize { - scenes.iter().map(|s|s.name.read().unwrap().len()).fold(0, usize::max) - } } impl ArrangementTrack { diff --git a/crates/tek_core/src/focus.rs b/crates/tek_core/src/focus.rs index e4cbf21e..0325d97b 100644 --- a/crates/tek_core/src/focus.rs +++ b/crates/tek_core/src/focus.rs @@ -30,7 +30,7 @@ impl Command for FocusCommand { } pub trait FocusGrid { - type Item: Copy + PartialEq; + type Item: Copy + PartialEq + Debug; fn layout (&self) -> &[&[Self::Item]]; fn cursor (&self) -> (usize, usize); fn cursor_mut (&mut self) -> &mut (usize, usize); diff --git a/crates/tek_tui/src/tui_app_foc.rs b/crates/tek_tui/src/tui_app_foc.rs index eba5e4e1..915afd52 100644 --- a/crates/tek_tui/src/tui_app_foc.rs +++ b/crates/tek_tui/src/tui_app_foc.rs @@ -6,8 +6,8 @@ pub enum AppContainerCommand { App(T) } -#[derive(Debug, Copy, Clone)] -pub enum AppContainerFocus { +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum AppContainerFocus { Menu, Content(F), } @@ -50,7 +50,7 @@ where fn layout (&self) -> &[&[Self::Item]] { &[ &[AppContainerFocus::Menu], - &[AppContainerFocus::Content], + &[AppContainerFocus::Content(())], ] } fn update_focus (&mut self) { diff --git a/crates/tek_tui/src/tui_arrangement.rs b/crates/tek_tui/src/tui_arrangement.rs index 4d8dc56c..ce1f7736 100644 --- a/crates/tek_tui/src/tui_arrangement.rs +++ b/crates/tek_tui/src/tui_arrangement.rs @@ -25,6 +25,20 @@ pub enum ArrangementEditorMode { Vertical(usize), } +/// Arranger display mode can be cycled +impl ArrangementEditorMode { + /// Cycle arranger display mode + pub fn to_next (&mut self) { + *self = match self { + Self::Horizontal => Self::Vertical(1), + Self::Vertical(1) => Self::Vertical(2), + Self::Vertical(2) => Self::Vertical(2), + Self::Vertical(0) => Self::Horizontal, + Self::Vertical(_) => Self::Vertical(0), + } + } +} + impl Audio for ArrangementEditor { #[inline] fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control { self.state.process(client, scope) @@ -59,3 +73,88 @@ impl ArrangementEditor { } } } + +impl ArrangementEditor { + pub fn track (&self) -> Option<&ArrangementTrack> { + self.selected.track().map(|t|self.state.tracks.get(t)).flatten() + } + pub fn track_mut (&mut self) -> Option<&mut ArrangementTrack> { + self.selected.track().map(|t|self.state.tracks.get_mut(t)).flatten() + } + pub fn scene (&self) -> Option<&ArrangementScene> { + self.selected.scene().map(|s|self.state.scenes.get(s)).flatten() + } + pub fn scene_mut (&mut self) -> Option<&mut ArrangementScene> { + self.selected.scene().map(|s|self.state.scenes.get_mut(s)).flatten() + } + pub fn phrase (&self) -> Option>> { + self.scene()?.clips.get(self.selected.track()?)?.clone() + } + pub fn track_del (&mut self) { + if let Some(index) = self.selected.track() { self.state.track_del(index); } + } + pub fn scene_del (&mut self) { + if let Some(index) = self.selected.scene() { self.state.scene_del(index); } + } + pub fn track_widths (&self) -> Vec<(usize, usize)> { + let mut widths = vec![]; + let mut total = 0; + for track in self.tracks.iter() { + let width = track.width; + widths.push((width, total)); + total += width; + } + widths.push((0, total)); + widths + } + pub fn phrase_del (&mut self) { + let track_index = self.selected.track(); + let scene_index = self.selected.scene(); + track_index + .and_then(|index|self.tracks.get_mut(index).map(|track|(index, track))) + .map(|(track_index, _)|scene_index + .and_then(|index|self.scenes.get_mut(index)) + .map(|scene|scene.clips[track_index] = None)); + } + pub fn phrase_put (&mut self) { + if let ArrangementEditorFocus::Clip(track, scene) = self.selected { + self.scenes[scene].clips[track] = Some(self.phrases.read().unwrap().phrase().clone()); + } + } + pub fn phrase_get (&mut self) { + if let ArrangementEditorFocus::Clip(track, scene) = self.selected { + if let Some(phrase) = &self.scenes[scene].clips[track] { + let mut phrases = self.phrases.write().unwrap(); + if let Some(index) = phrases.index_of(&*phrase.read().unwrap()) { + phrases.phrase = index; + } + } + } + } + pub fn phrase_next (&mut self) { + if let ArrangementEditorFocus::Clip(track, scene) = self.selected { + if let Some(ref mut phrase) = self.scenes[scene].clips[track] { + let phrases = self.phrases.read().unwrap(); + let index = phrases.index_of(&*phrase.read().unwrap()); + if let Some(index) = index { + if index < phrases.phrases.len().saturating_sub(1) { + *phrase = phrases.phrases[index + 1].clone(); + } + } + } + } + } + pub fn phrase_prev (&mut self) { + if let ArrangementEditorFocus::Clip(track, scene) = self.selected { + if let Some(ref mut phrase) = self.scenes[scene].clips[track] { + let phrases = self.phrases.read().unwrap(); + let index = phrases.index_of(&*phrase.read().unwrap()); + if let Some(index) = index { + if index > 0 { + *phrase = phrases.phrases[index - 1].clone(); + } + } + } + } + } +} diff --git a/crates/tek_tui/src/tui_arranger.rs b/crates/tek_tui/src/tui_arranger.rs index 1949d3f4..de26ca87 100644 --- a/crates/tek_tui/src/tui_arranger.rs +++ b/crates/tek_tui/src/tui_arranger.rs @@ -46,36 +46,30 @@ impl Audio for ArrangerView { impl Content for ArrangerView { type Engine = Tui; fn content (&self) -> impl Widget { - let focused = self.arrangement.focused; - let border_bg = TuiTheme::border_bg(); - let border_fg = TuiTheme::border_fg(focused); - let title_fg = TuiTheme::title_fg(focused); - let border = Lozenge(Style::default().bg(border_bg).fg(border_fg)); - let entered = if self.arrangement.entered { "■" } else { " " }; - Split::down( + Split::up( 1, - row!(menu in self.menu.menus.iter() => { - row!(" ", menu.title.as_str(), " ") - }), - Split::up( - 1, - widget(&self.status), - Split::up( - 1, - widget(&self.transport), - Split::down( - self.arrangement_split, - lay!( - widget(&self.arrangement).grow_y(1).border(border), - widget(&self.arrangement.size), - widget(&format!("[{}] Arrangement", entered)).fg(title_fg).push_x(1), - ), - Split::right( - self.phrases_split, - self.phrases.clone(), - widget(&self.editor), - ) - ) + widget(&self.sequencer.transport), + Split::down( + self.split, + lay!( + widget(&self.arrangement) + .grow_y(1) + .border(Lozenge(Style::default() + .bg(TuiTheme::border_bg()) + .fg(TuiTheme::border_fg(self.arrangement.focused)))), + widget(&self.arrangement.size), + widget(&format!("[{}] Arrangement", if self.arrangement.entered { + "■" + } else { + " " + })) + .fg(TuiTheme::title_fg(self.arrangement.focused)) + .push_x(1), + ), + Split::right( + self.sequencer.split, + widget(&self.sequencer.phrases), + widget(&self.sequencer.editor), ) ) ) @@ -190,7 +184,7 @@ impl ArrangerView { } pub fn increment (&mut self) { match self.arrangement.selected { - ArrangementEditorFocus::Track(_) => self.track_width_inc(), + ArrangementEditorFocus::Track(_) => self.track_mut().map(|t|t.width_inc()), ArrangementEditorFocus::Scene(_) => self.scene_next(), ArrangementEditorFocus::Clip(_, _) => self.phrase_next(), ArrangementEditorFocus::Mix => self.zoom_in(), @@ -198,7 +192,7 @@ impl ArrangerView { } pub fn decrement (&mut self) { match self.arrangement.selected { - ArrangementEditorFocus::Track(_) => self.track_width_dec(), + ArrangementEditorFocus::Track(_) => self.track_mut().map(|t|t.width_dec()), ArrangementEditorFocus::Scene(_) => self.scene_prev(), ArrangementEditorFocus::Clip(_, _) => self.phrase_prev(), ArrangementEditorFocus::Mix => self.zoom_out(), @@ -302,161 +296,3 @@ impl ArrangerView { } } } - -/// Methods for tracks in arrangement -impl Arrangement { - pub fn track (&self) -> Option<&ArrangementTrack> { - self.selected.track().map(|t|self.tracks.get(t)).flatten() - } - pub fn track_mut (&mut self) -> Option<&mut ArrangementTrack> { - self.selected.track().map(|t|self.tracks.get_mut(t)).flatten() - } - pub fn track_width_inc (&mut self) { self.track_mut().map(|t|t.width_inc()); } - pub fn track_width_dec (&mut self) { self.track_mut().map(|t|t.width_dec()); } - pub fn track_next (&mut self) { self.selected.track_next(self.tracks.len() - 1) } - pub fn track_prev (&mut self) { self.selected.track_prev() } - pub fn track_add ( - &mut self, name: Option<&str>, color: Option - ) -> Usually<&mut ArrangementTrack> { - self.tracks.push(name.map_or_else( - || ArrangementTrack::new( - &self.jack, &self.clock, &self.track_default_name(), color - ), - |name| ArrangementTrack::new( - &self.jack, &self.clock, name, color - ), - )?); - let index = self.tracks.len() - 1; - Ok(&mut self.tracks[index]) - } - pub fn track_del (&mut self) { - if let Some(index) = self.selected.track() { - self.tracks.remove(index); - for scene in self.scenes.iter_mut() { - scene.clips.remove(index); - } - } - } - pub fn track_default_name (&self) -> String { - format!("Track {}", self.tracks.len() + 1) - } - pub fn track_widths (&self) -> Vec<(usize, usize)> { - let mut widths = vec![]; - let mut total = 0; - for track in self.tracks.iter() { - let width = track.width; - widths.push((width, total)); - total += width; - } - widths.push((0, total)); - widths - } -} -/// Methods for scenes in arrangement -impl Arrangement { - pub fn scene (&self) -> Option<&ArrangementScene> { - self.selected.scene().map(|s|self.scenes.get(s)).flatten() - } - pub fn scene_mut (&mut self) -> Option<&mut ArrangementScene> { - self.selected.scene().map(|s|self.scenes.get_mut(s)).flatten() - } - pub fn scene_next (&mut self) { - self.selected.scene_next(self.scenes.len() - 1) - } - pub fn scene_prev (&mut self) { - self.selected.scene_prev() - } - pub fn scene_add ( - &mut self, name: Option<&str>, color: Option - ) -> Usually<&mut ArrangementScene> { - let clips = vec![None;self.tracks.len()]; - let name = name.map(|x|x.to_string()).unwrap_or_else(||self.scene_default_name()); - self.scenes.push(Scene::new(name, clips, color)); - let index = self.scenes.len() - 1; - Ok(&mut self.scenes[index]) - } - pub fn scene_del (&mut self) { - if let Some(index) = self.selected.scene() { - self.scenes.remove(index); - } - } - pub fn scene_default_name (&self) -> String { - format!("Scene {}", self.scenes.len() + 1) - } -} -/// Methods for phrases in arrangement -impl Arrangement { - pub fn sequencer (&self) -> Option<&ArrangementTrack> { - self.selected.track().map(|track|self.tracks.get(track)).flatten() - } - pub fn sequencer_mut (&mut self) -> Option<&mut ArrangementTrack> { - self.selected.track().map(|track|self.tracks.get_mut(track)).flatten() - } - pub fn phrase (&self) -> Option>> { - self.scene()?.clips.get(self.selected.track()?)?.clone() - } - pub fn phrase_del (&mut self) { - let track_index = self.selected.track(); - let scene_index = self.selected.scene(); - track_index - .and_then(|index|self.tracks.get_mut(index).map(|track|(index, track))) - .map(|(track_index, _)|scene_index - .and_then(|index|self.scenes.get_mut(index)) - .map(|scene|scene.clips[track_index] = None)); - } - pub fn phrase_put (&mut self) { - if let ArrangementEditorFocus::Clip(track, scene) = self.selected { - self.scenes[scene].clips[track] = Some(self.phrases.read().unwrap().phrase().clone()); - } - } - pub fn phrase_get (&mut self) { - if let ArrangementEditorFocus::Clip(track, scene) = self.selected { - if let Some(phrase) = &self.scenes[scene].clips[track] { - let mut phrases = self.phrases.write().unwrap(); - if let Some(index) = phrases.index_of(&*phrase.read().unwrap()) { - phrases.phrase = index; - } - } - } - } - pub fn phrase_next (&mut self) { - if let ArrangementEditorFocus::Clip(track, scene) = self.selected { - if let Some(ref mut phrase) = self.scenes[scene].clips[track] { - let phrases = self.phrases.read().unwrap(); - let index = phrases.index_of(&*phrase.read().unwrap()); - if let Some(index) = index { - if index < phrases.phrases.len().saturating_sub(1) { - *phrase = phrases.phrases[index + 1].clone(); - } - } - } - } - } - pub fn phrase_prev (&mut self) { - if let ArrangementEditorFocus::Clip(track, scene) = self.selected { - if let Some(ref mut phrase) = self.scenes[scene].clips[track] { - let phrases = self.phrases.read().unwrap(); - let index = phrases.index_of(&*phrase.read().unwrap()); - if let Some(index) = index { - if index > 0 { - *phrase = phrases.phrases[index - 1].clone(); - } - } - } - } - } -} - -/// Arranger display mode can be cycled -impl ArrangementEditorMode { - /// Cycle arranger display mode - pub fn to_next (&mut self) { - *self = match self { - Self::Horizontal => Self::Vertical(1), - Self::Vertical(1) => Self::Vertical(2), - Self::Vertical(2) => Self::Vertical(2), - Self::Vertical(0) => Self::Horizontal, - Self::Vertical(_) => Self::Vertical(0), - } - } -} diff --git a/crates/tek_tui/src/tui_arranger_ver.rs b/crates/tek_tui/src/tui_arranger_ver.rs index 0b600c12..455ea80c 100644 --- a/crates/tek_tui/src/tui_arranger_ver.rs +++ b/crates/tek_tui/src/tui_arranger_ver.rs @@ -10,12 +10,12 @@ impl<'a> Content for VerticalArranger<'a, Tui> { let tracks = state.tracks.as_ref() as &[ArrangementTrack]; let scenes = state.scenes.as_ref(); let cols = state.track_widths(); - let rows = Scene::ppqs(scenes, *factor); + let rows = ArrangementScene::ppqs(scenes, *factor); let bg = state.color; - let clip_bg = Arranger::::border_bg(); - let sep_fg = Arranger::::separator_fg(false); + let clip_bg = TuiTheme::border_bg(); + let sep_fg = TuiTheme::separator_fg(false); let header_h = 3u16;//5u16; - let scenes_w = 3 + Scene::longest_name(scenes) as u16; // x of 1st track + let scenes_w = 3 + ArrangementScene::longest_name(scenes) as u16; // x of 1st track let clock = &self.0.clock; let arrangement = Layers::new(move |add|{ let rows: &[(usize, usize)] = rows.as_ref(); @@ -155,17 +155,23 @@ impl<'a> Content for VerticalArranger<'a, Tui> { let mut scene_area: Option<[u16;4]> = None; let mut clip_area: Option<[u16;4]> = None; let area = match selected { - ArrangementFocus::Mix => area, - ArrangementFocus::Track(t) => { track_area = Some(get_track_area(t)); area }, - ArrangementFocus::Scene(s) => { scene_area = Some(get_scene_area(s)); area }, - ArrangementFocus::Clip(t, s) => { + ArrangementEditorFocus::Mix => area, + ArrangementEditorFocus::Track(t) => { + track_area = Some(get_track_area(t)); + area + }, + ArrangementEditorFocus::Scene(s) => { + scene_area = Some(get_scene_area(s)); + area + }, + ArrangementEditorFocus::Clip(t, s) => { track_area = Some(get_track_area(t)); scene_area = Some(get_scene_area(s)); clip_area = Some(get_clip_area(t, s)); area }, }; - let bg = Arranger::::border_bg(); + let bg = TuiTheme::border_bg(); if let Some([x, y, width, height]) = track_area { to.fill_fg([x, y, 1, height], bg); to.fill_fg([x + width, y, 1, height], bg); @@ -182,7 +188,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> { }) })) }).bg(bg.rgb); - let color = Arranger::::title_fg(self.0.focused); + let color = TuiTheme::title_fg(self.0.focused); let size = format!("{}x{}", self.0.size.w(), self.0.size.h()); let lower_right = TuiStyle::fg(size, color).pull_x(1).align_se().fill_xy(); lay!(arrangement, lower_right)