diff --git a/crates/tek/src/cli/cli_arranger.rs b/crates/tek/src/cli/cli_arranger.rs index 4c11ea1f..10f8223c 100644 --- a/crates/tek/src/cli/cli_arranger.rs +++ b/crates/tek/src/cli/cli_arranger.rs @@ -29,18 +29,16 @@ impl ArrangerCli { let track_color_1 = ItemColor::random(); let track_color_2 = ItemColor::random(); for i in 0..self.tracks { - let _track = app.track_add( - None, - Some(track_color_1.mix(track_color_2, i as f32 / self.tracks as f32)) - )?; + let _track = app.track_add(None, Some( + track_color_1.mix(track_color_2, i as f32 / self.tracks as f32).into() + ))?; } let scene_color_1 = ItemColor::random(); let scene_color_2 = ItemColor::random(); for i in 0..self.scenes { - let _scene = app.scene_add( - None, - Some(scene_color_1.mix(scene_color_2, i as f32 / self.scenes as f32)) - )?; + let _scene = app.scene_add(None, Some( + scene_color_1.mix(scene_color_2, i as f32 / self.scenes as f32).into() + ))?; } Ok(app) })?)?; diff --git a/crates/tek/src/lib.rs b/crates/tek/src/lib.rs index 782eac2e..abe73129 100644 --- a/crates/tek/src/lib.rs +++ b/crates/tek/src/lib.rs @@ -1,4 +1,4 @@ -pub mod core; pub(crate) use self::core::*; +pub mod core; pub(crate) use self::core::*; pub mod time; pub(crate) use self::time::*; pub mod space; pub(crate) use self::space::*; pub mod tui; pub(crate) use self::tui::*; diff --git a/crates/tek/src/midi/midi_scene.rs b/crates/tek/src/midi/midi_scene.rs index 2d464290..fe0825dc 100644 --- a/crates/tek/src/midi/midi_scene.rs +++ b/crates/tek/src/midi/midi_scene.rs @@ -3,7 +3,7 @@ use crate::*; pub trait HasScenes { fn scenes (&self) -> &Vec; fn scenes_mut (&mut self) -> &mut Vec; - fn scene_add (&mut self, name: Option<&str>, color: Option) -> Usually<&mut S>; + fn scene_add (&mut self, name: Option<&str>, color: Option) -> Usually<&mut S>; fn scene_del (&mut self, index: usize) { self.scenes_mut().remove(index); } @@ -42,7 +42,7 @@ pub enum ArrangerSceneCommand { pub trait ArrangerSceneApi: Sized { fn name (&self) -> &Arc>; fn clips (&self) -> &Vec>>>; - fn color (&self) -> ItemColor; + fn color (&self) -> ItemPalette; fn ppqs (scenes: &[Self], factor: usize) -> Vec<(usize, usize)> { let mut total = 0; diff --git a/crates/tek/src/midi/midi_track.rs b/crates/tek/src/midi/midi_track.rs index 43229a71..0a4fc7e6 100644 --- a/crates/tek/src/midi/midi_track.rs +++ b/crates/tek/src/midi/midi_track.rs @@ -15,7 +15,7 @@ impl HasTracks for Vec { } pub trait ArrangerTracksApi: HasTracks { - fn track_add (&mut self, name: Option<&str>, color: Option)-> Usually<&mut T>; + fn track_add (&mut self, name: Option<&str>, color: Option)-> Usually<&mut T>; fn track_del (&mut self, index: usize); fn track_default_name (&self) -> String { format!("Track {}", self.tracks().len() + 1) @@ -41,7 +41,7 @@ pub trait ArrangerTrackApi: HasPlayer + Send + Sync + Sized { /// Preferred width of track column fn width_mut (&mut self) -> &mut usize; /// Identifying color of track - fn color (&self) -> ItemColor; + fn color (&self) -> ItemPalette; fn longest_name (tracks: &[Self]) -> usize { tracks.iter().map(|s|s.name().read().unwrap().len()).fold(0, usize::max) diff --git a/crates/tek/src/space/bsp.rs b/crates/tek/src/space/bsp.rs index 181f7fbd..47474f1c 100644 --- a/crates/tek/src/space/bsp.rs +++ b/crates/tek/src/space/bsp.rs @@ -97,3 +97,8 @@ impl, Y: Render> Render for Bsp { }) } } + +#[cfg(test)] +mod test { + use super::*; +} diff --git a/crates/tek/src/time.rs b/crates/tek/src/time.rs index 58c3c798..4c37e243 100644 --- a/crates/tek/src/time.rs +++ b/crates/tek/src/time.rs @@ -38,6 +38,7 @@ pub const NOTE_DURATIONS: [(usize, &str);26] = [ (3456, "9/1"), (6144, "16/1"), ]; + /// Returns the next shorter length pub fn prev_note_length (pulses: usize) -> usize { for i in 1..=16 { let length = NOTE_DURATIONS[16-i].0; if length < pulses { return length } } diff --git a/crates/tek/src/tui/app_arranger.rs b/crates/tek/src/tui/app_arranger.rs index 7c8920ce..867e786e 100644 --- a/crates/tek/src/tui/app_arranger.rs +++ b/crates/tek/src/tui/app_arranger.rs @@ -10,7 +10,7 @@ pub struct ArrangerTui { pub splits: [u16;2], pub selected: ArrangerSelection, pub mode: ArrangerMode, - pub color: ItemColor, + pub color: ItemPalette, pub entered: bool, pub size: Measure, pub cursor: (usize, usize), @@ -33,7 +33,7 @@ from_jack!(|jack| ArrangerTui Self { tracks: vec![], color: TuiTheme::bg().into(), history: vec![], - mode: ArrangerMode::Vertical(2), + mode: ArrangerMode::V(2), name: Arc::new(RwLock::new(String::new())), size: Measure::new(), cursor: (0, 0), @@ -46,28 +46,38 @@ from_jack!(|jack| ArrangerTui Self { perf: PerfModel::default(), focus: FocusState::Entered(ArrangerFocus::Transport(TransportFocus::PlayPause)), }); -has_clock!(|self:ArrangerTui|&self.clock); -has_phrases!(|self:ArrangerTui|self.phrases.phrases); -has_editor!(|self:ArrangerTui|self.editor); +has_clock!(|self: ArrangerTui|&self.clock); +has_phrases!(|self: ArrangerTui|self.phrases.phrases); +has_editor!(|self: ArrangerTui|self.editor); handle!(|self: ArrangerTui, input|ArrangerCommand::execute_with_state(self, input)); render!(|self: ArrangerTui|{ - let arranger_focused = self.arranger_focused(); - let transport_focused = match self.focus.inner() { - ArrangerFocus::Transport(_) => true, _ => false - }; - let transport = TransportView::from((self, None, transport_focused)); - let with_transport = move|x|col!([transport, x]); - let border = Lozenge(Style::default() - .bg(TuiTheme::border_bg()) - .fg(TuiTheme::border_fg(arranger_focused))); - let arranger = move||border.wrap(Tui::grow_y(1, lay!(|add|{ + let arranger = ||lay!(|add|{ + add(&Fill::wh(Lozenge(Style::default() + .bg(TuiTheme::border_bg()) + .fg(TuiTheme::border_fg(true)))))?; + add(&self.size)?; match self.mode { - ArrangerMode::Horizontal => add(&arranger_content_horizontal(self))?, - ArrangerMode::Vertical(factor) => add(&arranger_content_vertical(self, factor))? - }; - add(&self.size) - }))); + ArrangerMode::H => todo!("horizontal arranger"), + ArrangerMode::V(factor) => add(&lay!([ + Align::se(Fill::wh(Tui::pull_x(1, Tui::fg(TuiTheme::title_fg(true), + format!("{}x{}", self.size.w(), self.size.h())) + ))), + Tui::bg(self.color.darkest.rgb, lay!(![ + ArrangerVColumnSeparator::from(self), + ArrangerVRowSeparator::from((self, factor)), + col!(![ + ArrangerVHeader::from(self), + ArrangerVContent::from((self, factor)), + ]), + ArrangerVCursor::from((self, factor)), + ])), + ])), + } + }); let with_pool = |x|Split::right(false, self.splits[1], PhraseListView(&self.phrases), x); + let play = Fixed::wh(5, 2, PlayPause(self.clock.is_rolling())); + let transport = TransportView::from((self, None, true)); + let with_transport = |x|col!([row!(![&play, &transport]), &x]); with_transport(col!([Fixed::h(self.splits[0], arranger()), with_pool(&self.editor),])) }); audio!(|self: ArrangerTui, client, scope|{ @@ -112,7 +122,7 @@ audio!(|self: ArrangerTui, client, scope|{ Undo, Redo, Clear, - Color(ItemColor), + Color(ItemPalette), Clock(ClockCommand), Scene(ArrangerSceneCommand), Track(ArrangerTrackCommand), @@ -122,92 +132,9 @@ audio!(|self: ArrangerTui, client, scope|{ Phrases(PhrasesCommand), Editor(PhraseCommand), } -command!(|self:ArrangerCommand,state:ArrangerTui|{ - use ArrangerCommand::*; - match self { - Focus(cmd) => cmd.execute(state)?.map(Focus), - Scene(cmd) => cmd.execute(state)?.map(Scene), - Track(cmd) => cmd.execute(state)?.map(Track), - Clip(cmd) => cmd.execute(state)?.map(Clip), - Phrases(cmd) => cmd.execute(&mut state.phrases)?.map(Phrases), - Editor(cmd) => cmd.execute(&mut state.editor)?.map(Editor), - Clock(cmd) => cmd.execute(state)?.map(Clock), - Zoom(_) => { todo!(); }, - Select(selected) => { - *state.selected_mut() = selected; - None - }, - _ => { todo!() } - } -}); -command!(|self:ArrangerSceneCommand,_state:ArrangerTui|None); -command!(|self:ArrangerTrackCommand,_state:ArrangerTui|None); -command!(|self:ArrangerClipCommand, _state:ArrangerTui|None); -pub trait ArrangerControl: TransportControl { - fn selected (&self) -> ArrangerSelection; - fn selected_mut (&mut self) -> &mut ArrangerSelection; - fn activate (&mut self) -> Usually<()>; - fn selected_phrase (&self) -> Option>>; - fn toggle_loop (&mut self); - fn randomize_color (&mut self); -} -impl ArrangerControl for ArrangerTui { - fn selected (&self) -> ArrangerSelection { - self.selected - } - fn selected_mut (&mut self) -> &mut ArrangerSelection { - &mut self.selected - } - fn activate (&mut self) -> Usually<()> { - if let ArrangerSelection::Scene(s) = self.selected { - for (t, track) in self.tracks.iter_mut().enumerate() { - let phrase = self.scenes[s].clips[t].clone(); - if track.player.play_phrase.is_some() || phrase.is_some() { - track.player.enqueue_next(phrase.as_ref()); - } - } - if self.clock().is_stopped() { - self.clock().play_from(Some(0))?; - } - } else if let ArrangerSelection::Clip(t, s) = self.selected { - let phrase = self.scenes()[s].clips[t].clone(); - self.tracks_mut()[t].player.enqueue_next(phrase.as_ref()); - }; - Ok(()) - } - fn selected_phrase (&self) -> Option>> { - self.selected_scene()?.clips.get(self.selected.track()?)?.clone() - } - fn toggle_loop (&mut self) { - if let Some(phrase) = self.selected_phrase() { - phrase.write().unwrap().toggle_loop() - } - } - fn randomize_color (&mut self) { - match self.selected { - ArrangerSelection::Mix => { - self.color = ItemColor::random_dark() - }, - ArrangerSelection::Track(t) => { - self.tracks_mut()[t].color = ItemColor::random() - }, - ArrangerSelection::Scene(s) => { - self.scenes_mut()[s].color = ItemColor::random() - }, - ArrangerSelection::Clip(t, s) => { - if let Some(phrase) = &self.scenes_mut()[s].clips[t] { - phrase.write().unwrap().color = ItemPalette::random(); - } - } - } - } -} -impl InputToCommand for ArrangerCommand { - fn input_to_command (state: &ArrangerTui, input: &TuiInput) -> Option { - to_arranger_command(state, input) - .or_else(||to_focus_command(input).map(ArrangerCommand::Focus)) - } -} +input_to_command!(ArrangerCommand: |state:ArrangerTui,input| + to_arranger_command(state, input) + .or_else(||to_focus_command(input).map(ArrangerCommand::Focus))?); fn to_arranger_command (state: &ArrangerTui, input: &TuiInput) -> Option { use ArrangerCommand as Cmd; use KeyCode::Char; @@ -264,7 +191,7 @@ fn to_arranger_mix_command (input: &TuiInput) -> Option { key_pat!(Char('<')) => Cmd::Zoom(0), key_pat!(Char('>')) => Cmd::Zoom(0), key_pat!(Delete) => Cmd::Clear, - key_pat!(Char('c')) => Cmd::Color(ItemColor::random()), + key_pat!(Char('c')) => Cmd::Color(ItemPalette::random()), _ => return None }) } @@ -282,7 +209,7 @@ fn to_arranger_track_command (input: &TuiInput, t: usize) -> Option Cmd::Track(Track::Swap(t, t - 1)), key_pat!(Char('>')) => Cmd::Track(Track::Swap(t, t + 1)), key_pat!(Delete) => Cmd::Track(Track::Delete(t)), - //key_pat!(Char('c')) => Cmd::Track(Track::Color(t, ItemColor::random())), + //key_pat!(Char('c')) => Cmd::Track(Track::Color(t, ItemPalette::random())), _ => return None }) } @@ -301,7 +228,7 @@ fn to_arranger_scene_command (input: &TuiInput, s: usize) -> Option')) => Cmd::Scene(Scene::Swap(s, s + 1)), key_pat!(Enter) => Cmd::Scene(Scene::Play(s)), key_pat!(Delete) => Cmd::Scene(Scene::Delete(s)), - //key_pat!(Char('c')) => Cmd::Track(Scene::Color(s, ItemColor::random())), + //key_pat!(Char('c')) => Cmd::Track(Scene::Color(s, ItemPalette::random())), _ => return None }) } @@ -320,12 +247,92 @@ fn to_arranger_clip_command (input: &TuiInput, t: usize, s: usize) -> Option Cmd::Clip(Clip::Set(t, s, None)), key_pat!(Char('>')) => Cmd::Clip(Clip::Set(t, s, None)), key_pat!(Delete) => Cmd::Clip(Clip::Set(t, s, None)), - //key_pat!(Char('c')) => Cmd::Clip(Clip::Color(t, s, ItemColor::random())), + //key_pat!(Char('c')) => Cmd::Clip(Clip::Color(t, s, ItemPalette::random())), //key_pat!(Char('g')) => Cmd::Clip(Clip(Clip::Get(t, s))), //key_pat!(Char('s')) => Cmd::Clip(Clip(Clip::Set(t, s))), _ => return None }) } +command!(|self:ArrangerCommand,state:ArrangerTui|{ + use ArrangerCommand::*; + match self { + Focus(cmd) => cmd.execute(state)?.map(Focus), + Scene(cmd) => cmd.execute(state)?.map(Scene), + Track(cmd) => cmd.execute(state)?.map(Track), + Clip(cmd) => cmd.execute(state)?.map(Clip), + Phrases(cmd) => cmd.execute(&mut state.phrases)?.map(Phrases), + Editor(cmd) => cmd.execute(&mut state.editor)?.map(Editor), + Clock(cmd) => cmd.execute(state)?.map(Clock), + Zoom(_) => { todo!(); }, + Select(selected) => { + *state.selected_mut() = selected; + None + }, + _ => { todo!() } + } +}); +command!(|self:ArrangerSceneCommand,_state:ArrangerTui|None); +command!(|self:ArrangerTrackCommand,_state:ArrangerTui|None); +command!(|self:ArrangerClipCommand, _state:ArrangerTui|None); +pub trait ArrangerControl: TransportControl { + fn selected (&self) -> ArrangerSelection; + fn selected_mut (&mut self) -> &mut ArrangerSelection; + fn selected_phrase (&self) -> Option>>; + fn activate (&mut self) -> Usually<()>; + fn toggle_loop (&mut self); + fn randomize_color (&mut self); +} +impl ArrangerControl for ArrangerTui { + fn selected (&self) -> ArrangerSelection { + self.selected + } + fn selected_mut (&mut self) -> &mut ArrangerSelection { + &mut self.selected + } + fn activate (&mut self) -> Usually<()> { + if let ArrangerSelection::Scene(s) = self.selected { + for (t, track) in self.tracks.iter_mut().enumerate() { + let phrase = self.scenes[s].clips[t].clone(); + if track.player.play_phrase.is_some() || phrase.is_some() { + track.player.enqueue_next(phrase.as_ref()); + } + } + if self.clock().is_stopped() { + self.clock().play_from(Some(0))?; + } + } else if let ArrangerSelection::Clip(t, s) = self.selected { + let phrase = self.scenes()[s].clips[t].clone(); + self.tracks_mut()[t].player.enqueue_next(phrase.as_ref()); + }; + Ok(()) + } + fn selected_phrase (&self) -> Option>> { + self.selected_scene()?.clips.get(self.selected.track()?)?.clone() + } + fn toggle_loop (&mut self) { + if let Some(phrase) = self.selected_phrase() { + phrase.write().unwrap().toggle_loop() + } + } + fn randomize_color (&mut self) { + match self.selected { + ArrangerSelection::Mix => { + self.color = ItemPalette::random() + }, + ArrangerSelection::Track(t) => { + self.tracks_mut()[t].color = ItemPalette::random() + }, + ArrangerSelection::Scene(s) => { + self.scenes_mut()[s].color = ItemPalette::random() + }, + ArrangerSelection::Clip(t, s) => { + if let Some(phrase) = &self.scenes_mut()[s].clips[t] { + phrase.write().unwrap().color = ItemPalette::random(); + } + } + } + } +} impl TransportControl for ArrangerTui { fn transport_focused (&self) -> Option { match self.focus.inner() { @@ -368,32 +375,15 @@ impl From<&ArrangerTui> for Option { } impl_focus!(ArrangerTui ArrangerFocus [ - //&[ - //Menu, - //Menu, - //Menu, - //Menu, - //Menu, - //], &[ Transport(TransportFocus::PlayPause), Transport(TransportFocus::Bpm), Transport(TransportFocus::Sync), Transport(TransportFocus::Quant), Transport(TransportFocus::Clock), - ], &[ - Arranger, - Arranger, - Arranger, - Arranger, - Arranger, - ], &[ - Phrases, - Phrases, - PhraseEditor, - PhraseEditor, - PhraseEditor, ], + &[Arranger;5], + &[Phrases, Phrases, PhraseEditor, PhraseEditor, PhraseEditor], ]); /// Status bar for arranger app @@ -412,10 +402,10 @@ pub enum ArrangerStatus { /// Display mode of arranger #[derive(Clone, PartialEq)] pub enum ArrangerMode { - /// Tracks are rows - Horizontal, /// Tracks are columns - Vertical(usize), + V(usize), + /// Tracks are rows + H, } /// Arranger display mode can be cycled @@ -423,24 +413,15 @@ impl ArrangerMode { /// 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), + Self::H => Self::V(1), + Self::V(1) => Self::V(2), + Self::V(2) => Self::V(2), + Self::V(0) => Self::H, + Self::V(_) => Self::V(0), } } } -pub trait ArrangerViewState { - fn arranger_focused (&self) -> bool; -} -impl ArrangerViewState for ArrangerTui { - fn arranger_focused (&self) -> bool { - self.focused() == ArrangerFocus::Arranger - } -} - fn track_widths (tracks: &[ArrangerTrack]) -> Vec<(usize, usize)> { let mut widths = vec![]; let mut total = 0; @@ -457,32 +438,12 @@ fn any_size (_: E::Size) -> Perhaps{ Ok(Some([0.into(),0.into()].into())) } -pub fn arranger_content_vertical ( - view: &ArrangerTui, - factor: usize -) -> impl Render + use<'_> { - lay!([ - Align::se(Fill::wh(Tui::pull_x(1, Tui::fg(TuiTheme::title_fg(view.arranger_focused()), - format!("{}x{}", view.size.w(), view.size.h())) - ))), - Tui::bg(view.color.rgb, lay!(![ - ArrangerVerticalColumnSeparator::from(view), - ArrangerVerticalRowSeparator::from((view, factor)), - col!(![ - ArrangerVerticalHeader::from(view), - ArrangerVerticalContent::from((view, factor)), - ]), - ArrangerVerticalCursor::from((view, factor)), - ])), - ]) -} - -struct ArrangerVerticalColumnSeparator { +struct ArrangerVColumnSeparator { cols: Vec<(usize, usize)>, scenes_w: u16, sep_fg: Color, } -impl From<&ArrangerTui> for ArrangerVerticalColumnSeparator { +impl From<&ArrangerTui> for ArrangerVColumnSeparator { fn from (state: &ArrangerTui) -> Self { Self { cols: track_widths(state.tracks()), @@ -491,7 +452,7 @@ impl From<&ArrangerTui> for ArrangerVerticalColumnSeparator { } } } -render!(|self: ArrangerVerticalColumnSeparator|render(move|to: &mut TuiOutput|{ +render!(|self: ArrangerVColumnSeparator|render(move|to: &mut TuiOutput|{ let style = Some(Style::default().fg(self.sep_fg)); Ok(for x in self.cols.iter().map(|col|col.1) { let x = self.scenes_w + to.area().x() + x as u16; @@ -501,11 +462,11 @@ render!(|self: ArrangerVerticalColumnSeparator|render(move|to: &mut TuiOutp }) })); -struct ArrangerVerticalRowSeparator { +struct ArrangerVRowSeparator { rows: Vec<(usize, usize)>, sep_fg: Color, } -impl From<(&ArrangerTui, usize)> for ArrangerVerticalRowSeparator { +impl From<(&ArrangerTui, usize)> for ArrangerVRowSeparator { fn from ((state, factor): (&ArrangerTui, usize)) -> Self { Self { rows: ArrangerScene::ppqs(state.scenes(), factor), @@ -514,7 +475,7 @@ impl From<(&ArrangerTui, usize)> for ArrangerVerticalRowSeparator { } } -render!(|self: ArrangerVerticalRowSeparator|render(move|to: &mut TuiOutput|{ +render!(|self: ArrangerVRowSeparator|render(move|to: &mut TuiOutput|{ Ok(for y in self.rows.iter().map(|row|row.1) { let y = to.area().y() + (y / PPQ) as u16 + 1; if y >= to.buffer.area.height { break } @@ -528,7 +489,7 @@ render!(|self: ArrangerVerticalRowSeparator|render(move|to: &mut TuiOutput| }) })); -struct ArrangerVerticalCursor { +struct ArrangerVCursor { cols: Vec<(usize, usize)>, rows: Vec<(usize, usize)>, focused: bool, @@ -536,19 +497,19 @@ struct ArrangerVerticalCursor { scenes_w: u16, header_h: u16, } -impl From<(&ArrangerTui, usize)> for ArrangerVerticalCursor { +impl From<(&ArrangerTui, usize)> for ArrangerVCursor { fn from ((state, factor): (&ArrangerTui, usize)) -> Self { Self { cols: track_widths(state.tracks()), rows: ArrangerScene::ppqs(state.scenes(), factor), - focused: state.arranger_focused(), + focused: true, selected: state.selected, scenes_w: 3 + ArrangerScene::longest_name(state.scenes()) as u16, header_h: 3, } } } -render!(|self: ArrangerVerticalCursor|render(move|to: &mut TuiOutput|{ +render!(|self: ArrangerVCursor|render(move|to: &mut TuiOutput|{ let area = to.area(); let focused = self.focused; let selected = self.selected; @@ -603,7 +564,7 @@ render!(|self: ArrangerVerticalCursor|render(move|to: &mut TuiOutput|{ }) })); -struct ArrangerVerticalHeader<'a> { +struct ArrangerVHeader<'a> { tracks: &'a Vec, cols: Vec<(usize, usize)>, focused: bool, @@ -613,12 +574,12 @@ struct ArrangerVerticalHeader<'a> { timebase: &'a Arc, current: &'a Arc, } -impl<'a> From<&'a ArrangerTui> for ArrangerVerticalHeader<'a> { +impl<'a> From<&'a ArrangerTui> for ArrangerVHeader<'a> { fn from (state: &'a ArrangerTui) -> Self { Self { tracks: &state.tracks, cols: track_widths(state.tracks()), - focused: state.arranger_focused(), + focused: true, selected: state.selected, scenes_w: 3 + ArrangerScene::longest_name(state.scenes()) as u16, header_h: 3, @@ -627,13 +588,13 @@ impl<'a> From<&'a ArrangerTui> for ArrangerVerticalHeader<'a> { } } } -render!(|self: ArrangerVerticalHeader<'a>|row!( +render!(|self: ArrangerVHeader<'a>|row!( (track, w) in self.tracks.iter().zip(self.cols.iter().map(|col|col.0)) => { // name and width of track let name = track.name().read().unwrap(); let max_w = w.saturating_sub(1).min(name.len()).max(2); let name = format!("▎{}", &name[0..max_w]); - let name = Tui::bold(true, name); + let name = Tui::bold(true, Tui::fg(track.color.lightest.rgb, name)); // beats elapsed let elapsed = if let Some((_, Some(phrase))) = track.player.play_phrase().as_ref() { let length = phrase.read().unwrap().length; @@ -668,13 +629,13 @@ render!(|self: ArrangerVerticalHeader<'a>|row!( .transpose()? .unwrap_or("(none)".into())); Tui::push_x(self.scenes_w, - Tui::bg(track.color().rgb, + Tui::bg(track.color().base.rgb, Tui::min_xy(w as u16, self.header_h, col!([name, timer])))) } )); -struct ArrangerVerticalContent<'a> { +struct ArrangerVContent<'a> { size: &'a Measure, scenes: &'a Vec, tracks: &'a Vec, @@ -682,7 +643,7 @@ struct ArrangerVerticalContent<'a> { cols: Vec<(usize, usize)>, header_h: u16, } -impl<'a> From<(&'a ArrangerTui, usize)> for ArrangerVerticalContent<'a> { +impl<'a> From<(&'a ArrangerTui, usize)> for ArrangerVContent<'a> { fn from ((state, factor): (&'a ArrangerTui, usize)) -> Self { Self { size: &state.size, @@ -694,7 +655,7 @@ impl<'a> From<(&'a ArrangerTui, usize)> for ArrangerVerticalContent<'a> { } } } -render!(|self: ArrangerVerticalContent<'a>|Fixed::h( +render!(|self: ArrangerVContent<'a>|Fixed::h( (self.size.h() as u16).saturating_sub(self.header_h), col!((scene, pulses) in self.scenes.iter().zip(self.rows.iter().map(|row|row.0)) => { let height = 1.max((pulses / PPQ) as u16); @@ -736,14 +697,14 @@ impl HasScenes for ArrangerTui { fn scenes_mut (&mut self) -> &mut Vec { &mut self.scenes } - fn scene_add (&mut self, name: Option<&str>, color: Option) + fn scene_add (&mut self, name: Option<&str>, color: Option) -> Usually<&mut ArrangerScene> { let name = name.map_or_else(||self.scene_default_name(), |x|x.to_string()); let scene = ArrangerScene { name: Arc::new(name.into()), clips: vec![None;self.tracks().len()], - color: color.unwrap_or_else(||ItemColor::random()), + color: color.unwrap_or_else(||ItemPalette::random()), }; self.scenes_mut().push(scene); let index = self.scenes().len() - 1; @@ -763,7 +724,7 @@ impl HasScenes for ArrangerTui { /// Clips in scene, one per track pub(crate) clips: Vec>>>, /// Identifying color of scene - pub(crate) color: ItemColor, + pub(crate) color: ItemPalette, } impl ArrangerSceneApi for ArrangerScene { fn name (&self) -> &Arc> { @@ -772,7 +733,7 @@ impl ArrangerSceneApi for ArrangerScene { fn clips (&self) -> &Vec>>> { &self.clips } - fn color (&self) -> ItemColor { + fn color (&self) -> ItemPalette { self.color } } @@ -785,14 +746,14 @@ impl HasTracks for ArrangerTui { } } impl ArrangerTracksApi for ArrangerTui { - fn track_add (&mut self, name: Option<&str>, color: Option) + fn track_add (&mut self, name: Option<&str>, color: Option) -> Usually<&mut ArrangerTrack> { let name = name.map_or_else(||self.track_default_name(), |x|x.to_string()); let track = ArrangerTrack { width: name.len() + 2, name: Arc::new(name.into()), - color: color.unwrap_or_else(||ItemColor::random()), + color: color.unwrap_or_else(||ItemPalette::random()), player: PhrasePlayerModel::from(&self.clock), }; self.tracks_mut().push(track); @@ -813,7 +774,7 @@ impl ArrangerTracksApi for ArrangerTui { /// Preferred width of track column pub(crate) width: usize, /// Identifying color of track - pub(crate) color: ItemColor, + pub(crate) color: ItemPalette, /// MIDI player state pub(crate) player: PhrasePlayerModel, } @@ -833,7 +794,7 @@ impl ArrangerTrackApi for ArrangerTrack { &mut self.width } /// Identifying color of track - fn color (&self) -> ItemColor { + fn color (&self) -> ItemPalette { self.color } } @@ -930,7 +891,7 @@ pub fn arranger_content_horizontal ( ) -> impl Render + use<'_> { todo!() } - //let focused = view.arranger_focused(); + //let focused = true; //let _tracks = view.tracks(); //lay!( //focused.then_some(Background(TuiTheme::border_bg())), @@ -1137,13 +1098,13 @@ pub fn arranger_content_horizontal ( //AddTrack => { state.state.track_add(None, None)?; }, //ToggleLoop => { state.state.toggle_loop() }, //pub fn zoom_in (&mut self) { - //if let ArrangerEditorMode::Vertical(factor) = self.mode { - //self.mode = ArrangerEditorMode::Vertical(factor + 1) + //if let ArrangerEditorMode::V(factor) = self.mode { + //self.mode = ArrangerEditorMode::V(factor + 1) //} //} //pub fn zoom_out (&mut self) { - //if let ArrangerEditorMode::Vertical(factor) = self.mode { - //self.mode = ArrangerEditorMode::Vertical(factor.saturating_sub(1)) + //if let ArrangerEditorMode::V(factor) = self.mode { + //self.mode = ArrangerEditorMode::V(factor.saturating_sub(1)) //} //} //pub fn move_back (&mut self) {