From 3c8d9668fedb2a3fdce288c79efb3e7505261dd7 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Mon, 30 Sep 2024 09:41:43 +0300 Subject: [PATCH] fix (?) Inset; add arranger cursor description --- crates/tek_core/src/space.rs | 22 +- crates/tek_core/src/tui.rs | 8 +- crates/tek_sequencer/src/main_arranger.rs | 2 +- crates/tek_sequencer/src/sequencer.rs | 256 +++++++++++----------- 4 files changed, 137 insertions(+), 151 deletions(-) diff --git a/crates/tek_core/src/space.rs b/crates/tek_core/src/space.rs index 5163bca3..79cda5cb 100644 --- a/crates/tek_core/src/space.rs +++ b/crates/tek_core/src/space.rs @@ -101,6 +101,12 @@ pub trait Area: Copy { _ => todo!(), } } + #[inline] fn push_x (&self, x: N) -> [N;4] { [self.x() + x, self.y(), self.w(), self.h()] } + #[inline] fn push_y (&self, y: N) -> [N;4] { [self.x(), self.y() + y, self.w(), self.h()] } + #[inline] fn shrink_x (&self, x: N) -> [N;4] { [self.x(), self.y(), self.w() - x, self.h()] } + #[inline] fn shrink_y (&self, y: N) -> [N;4] { [self.x(), self.y(), self.w(), self.h() - y] } + #[inline] fn set_w (&self, w: N) -> [N;4] { [self.x(), self.y(), w, self.h()] } + #[inline] fn set_h (&self, h: N) -> [N;4] { [self.x(), self.y(), self.w(), h] } } impl Area for (N, N, N, N) { @@ -610,18 +616,14 @@ impl Outset { impl> Widget for Inset { type Engine = E; - fn layout (&self, to: E::Size) -> Perhaps { - match *self { - Self::X(x, ref inner) => Shrink::X(x + x, inner as &dyn Widget), - Self::Y(y, ref inner) => Shrink::Y(y + y, inner as &dyn Widget), - Self::XY(x, y, ref inner) => Shrink::XY(x + x, y + y, inner as &dyn Widget), - }.layout(to) - } fn render (&self, to: &mut E::Output) -> Usually<()> { match *self { - Self::X(x, ref inner) => Push::X(x, inner as &dyn Widget), - Self::Y(y, ref inner) => Push::Y(y, inner as &dyn Widget), - Self::XY(x, y, ref inner) => Push::XY(x, y, inner as &dyn Widget), + Self::X(x, ref inner) => + Push::X(x, Shrink::X(x, inner as &dyn Widget)), + Self::Y(y, ref inner) => + Push::Y(y, Shrink::Y(y, inner as &dyn Widget)), + Self::XY(x, y, ref inner) => + Push::XY(x, y, Shrink::XY(x, y, inner as &dyn Widget)), }.render(to) } } diff --git a/crates/tek_core/src/tui.rs b/crates/tek_core/src/tui.rs index 17c524b6..7bedad32 100644 --- a/crates/tek_core/src/tui.rs +++ b/crates/tek_core/src/tui.rs @@ -464,12 +464,8 @@ pub struct Bordered>(pub S, pub W); impl> Content for Bordered { type Engine = Tui; fn content (&self) -> impl Widget { - let style = self.0; - Layers::new(move|add|{ - add(&Inset::XY(1, 1, &self.1 as &dyn Widget))?; - add(&Border(style))?; - Ok(()) - }).fill_xy() + let content: &dyn Widget = &self.1; + lay! { content.inset_xy(1, 1), Border(self.0) }.fill_xy() } } diff --git a/crates/tek_sequencer/src/main_arranger.rs b/crates/tek_sequencer/src/main_arranger.rs index 7aa27e16..58af6dc1 100644 --- a/crates/tek_sequencer/src/main_arranger.rs +++ b/crates/tek_sequencer/src/main_arranger.rs @@ -81,7 +81,7 @@ impl Content for ArrangerStandalone { ) { let arranger = &self.arranger as &dyn Widget; let sequencer = sequencer as &dyn Widget; - add(&Split::new(direction, 40, arranger, sequencer.min_y(20))) + add(&Split::new(direction, 20, arranger, sequencer.min_y(20))) } else { add(&self.arranger) } diff --git a/crates/tek_sequencer/src/sequencer.rs b/crates/tek_sequencer/src/sequencer.rs index 63bf6af0..0873b719 100644 --- a/crates/tek_sequencer/src/sequencer.rs +++ b/crates/tek_sequencer/src/sequencer.rs @@ -306,9 +306,15 @@ impl Handle for Arranger { impl Content for Arranger { type Engine = Tui; fn content (&self) -> impl Widget { - Layers::new(move |add|match self.mode { - ArrangerViewMode::Horizontal => add(&HorizontalArranger(&self)), - ArrangerViewMode::Vertical(factor) => add(&VerticalArranger(&self, factor)) + Layers::new(move |add|{ + match self.mode { + ArrangerViewMode::Horizontal => add(&HorizontalArranger(&self)), + ArrangerViewMode::Vertical(factor) => add(&VerticalArranger(&self, factor)) + }?; + add(&Align::SE(self.selected.description( + &self.tracks, + &self.scenes, + ).as_str())) }) } } @@ -368,6 +374,41 @@ pub enum ArrangerFocus { /// Focus identification methods impl ArrangerFocus { + pub fn description ( + &self, + tracks: &Vec>, + scenes: &Vec, + ) -> String { + format!("Selected: {}", match self { + Self::Mix => format!("Everything"), + Self::Track(t) => if let Some(track) = tracks.get(*t) { + format!("T{t}: {}", &track.name.read().unwrap()) + } else { + format!("T??") + }, + Self::Scene(s) => if let Some(scene) = scenes.get(*s) { + format!("S{s}: {}", &scene.name.read().unwrap()) + } else { + format!("S??") + }, + Self::Clip(t, s) => if let (Some(track), Some(scene)) = ( + tracks.get(*t), + scenes.get(*s), + ) { + if let Some(Some(slot)) = scene.clips.get(*t) { + if let Some(clip) = track.phrases.get(*slot) { + format!("T{t} S{s} C{slot} ({})", &clip.read().unwrap().name.read().unwrap()) + } else { + format!("T{t} S{s}: Empty") + } + } else { + format!("T{t} S{s}: Empty") + } + } else { + format!("T{t} S{s}: Empty") + } + }) + } pub fn is_mix (&self) -> bool { match self { Self::Mix => true, _ => false } } @@ -505,7 +546,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> { }).fixed_xy(offset.saturating_sub(1), height))?; for (track, (w, _x)) in cols.iter().enumerate() { add(&Layers::new(move |add|{ - let mut color = COLOR_BG0; + let mut color = Color::Rgb(40, 50, 30); if let (Some(track), Some(Some(clip))) = ( tracks.get(track), scene.clips.get(track), @@ -531,10 +572,10 @@ impl<'a> Content for VerticalArranger<'a, Tui> { add(&col!(track_titles, track_clips))?; Ok(()) }) - .bg(Color::Rgb(35,40,25)) + .bg(Color::Rgb(35, 40, 25)) .border(Lozenge(Style::default() - .bg(Color::Rgb(40,50,30)) - .fg(Color::Rgb(70,80,50)))) + .bg(Color::Rgb(40, 50, 30)) + .fg(Color::Rgb(70, 80, 50)))) } } @@ -612,7 +653,7 @@ impl<'a> Widget for VerticalArrangerCursor<'a> { let area = match selected { ArrangerFocus::Mix => { if focused { - to.fill_bg(area, COLOR_BG0); + to.fill_bg(area, Color::Rgb(40, 50, 30)); } area }, @@ -632,21 +673,21 @@ impl<'a> Widget for VerticalArrangerCursor<'a> { }, }; if let Some([x, y, width, height]) = track_area { - to.fill_fg([x, y, 1, height], COLOR_BG5); - to.fill_fg([x + width, y, 1, height], COLOR_BG5); + to.fill_fg([x, y, 1, height], Color::Rgb(70, 80, 50)); + to.fill_fg([x + width, y, 1, height], Color::Rgb(70, 80, 50)); } if let Some([_, y, _, height]) = scene_area { - to.fill_ul([area.x(), y - 1, area.w(), 1], COLOR_BG5); - to.fill_ul([area.x(), y + height - 1, area.w(), 1], COLOR_BG5); + to.fill_ul([area.x(), y - 1, area.w(), 1], Color::Rgb(70, 80, 50)); + to.fill_ul([area.x(), y + height - 1, area.w(), 1], Color::Rgb(70, 80, 50)); } if focused { if let Some(clip_area) = clip_area { to.render_in(clip_area, &CORNERS)?; - to.fill_bg(clip_area, COLOR_BG0); + to.fill_bg(clip_area, Color::Rgb(40, 50, 30)); } else if let Some(track_area) = track_area { - to.fill_bg(track_area, COLOR_BG0); + to.fill_bg(track_area, Color::Rgb(40, 50, 30)); } else if let Some(scene_area) = scene_area { - to.fill_bg(scene_area, COLOR_BG0); + to.fill_bg(scene_area, Color::Rgb(40, 50, 30)); } } //Ok(Some(area)) @@ -666,7 +707,7 @@ impl<'a> Content for HorizontalArranger<'a, Tui> { let Arranger { tracks, focused, selected, scenes, .. } = self.0; let tracks = tracks.as_slice(); Layers::new(|add|{ - add(&focused.then_some(Background(COLOR_BG0)))?; + add(&focused.then_some(Background(Color::Rgb(40, 50, 30))))?; add(&Stack::right(|add|{ add(&TrackNameColumn(tracks, *selected))?; add(&TrackMonitorColumn(tracks))?; @@ -1330,38 +1371,17 @@ impl Content for Sequencer { type Engine = Tui; fn content (&self) -> impl Widget { let toolbar = col!( - col! { "Name" - , self.name.read().unwrap().as_str(), - }.min_xy(10, 4), - - col! { "Start: ", " 1.1.1" - , "End: ", " 2.1.1", - }.min_xy(10, 6), - - col! { "Loop [ ]" - , "From: ", " 1.1.1" - , "Length: ", " 1.0.0", - }.min_xy(10, 7), - - col! { "Notes: " - , "C#0-C#9 " - , "[ /2 ]" - , "[ x2 ]" - , "[ Rev ]" - , "[ Inv ]" - , "[ Dup ]" - }.min_xy(10, 9), + col! { "Name", self.name.read().unwrap().as_str(), }.min_xy(10, 4), + col! { "Start: ", " 1.1.1", "End: ", " 2.1.1", }.min_xy(10, 6), + col! { "Loop [ ]", "From: ", " 1.1.1", "Length: ", " 1.0.0", }.min_xy(10, 7), + col! { "Notes: ", "C#0-C#9 ", "[ /2 ]", "[ x2 ]" + , "[ Rev ]", "[ Inv ]", "[ Dup ]" }.min_xy(10, 9), ); let content = lay!( - // keys - CustomWidget::new(|to|Ok(Some([32,4])), |to: &mut TuiOutput|{ - let area = to.area(); - if area.h() < 2 { - return Ok(()) - } - let area = [area.x(), area.y(), 5, area.h() - 2]; - to.buffer_update(area, &|cell, x, y|{ + CustomWidget::new(|_|Ok(Some([32,4])), |to: &mut TuiOutput|{ + if to.area().h() < 2 { return Ok(()) } + to.buffer_update(to.area().set_w(5).shrink_y(2), &|cell, x, y|{ let y = y + self.note_axis.start as u16; if x < self.keys.area.width && y < self.keys.area.height { *cell = self.keys.get(x, y).clone() @@ -1369,66 +1389,66 @@ impl Content for Sequencer { }); Ok(()) }).fill_y(), - - self.phrase.as_ref().map(|phrase|SequenceTimer(&self, phrase.clone()).fill_x()), - - // notes - CustomWidget::new(|to|Ok(Some([32,4])), |to: &mut TuiOutput|{ - let area = to.area(); - if area.h() < 2 { - return Ok(())//Some(area)) + // playhead + CustomWidget::new(|_|Ok(Some([32,2])), |to: &mut TuiOutput|{ + if let Some(phrase) = self.phrase.as_ref() { + let time_0 = self.time_axis.start; + let time_z = self.time_axis.scale; + let now = self.now % phrase.read().unwrap().length; + let [x, y, width, _] = to.area(); + let x2 = x as usize + Sequencer::H_KEYS_OFFSET; + let x3 = x as usize + width as usize; + for x in x2..x3 { + let step = (time_0 + x2) * time_z; + let next_step = (time_0 + x2 + 1) * time_z; + let style = Sequencer::::style_timer_step(now, step as usize, next_step as usize); + to.blit(&"-", x as u16, y, Some(style)); + } } - let area = [ - area.x() + Sequencer::H_KEYS_OFFSET as u16, - area.y() + 1, - area.w().saturating_sub(Sequencer::H_KEYS_OFFSET as u16), - area.h().saturating_sub(2), - ]; + Ok(()) + }).fill_x(), + // notes + CustomWidget::new(|_|Ok(Some([32,4])), |to: &mut TuiOutput|{ + let offset = Sequencer::H_KEYS_OFFSET as u16; + if to.area().h() < 2 || to.area().w() < offset { return Ok(()) } + let area = to.area().push_x(offset).shrink_x(offset).shrink_y(2); to.buffer_update(area, &move |cell, x, y|{ + cell.set_bg(Color::Rgb(20, 20, 20)); let src_x = ((x as usize + self.time_axis.start) * self.time_axis.scale) as usize; let src_y = (y as usize + self.note_axis.start) as usize; if src_x < self.buffer.width && src_y < self.buffer.height - 1 { let src = self.buffer.get(src_x, self.buffer.height - src_y); - src.map(|src|{ - cell.set_symbol(src.symbol()); - cell.set_fg(src.fg); - }); + src.map(|src|{ cell.set_symbol(src.symbol()); cell.set_fg(src.fg); }); } }); - Ok(())//Some(area)) + Ok(()) }).fill_x(), - - // cursor - CustomWidget::new(|to|Ok(Some([1,1])), |to: &mut TuiOutput|{ + // note cursor + CustomWidget::new(|_|Ok(Some([1,1])), |to: &mut TuiOutput|{ let area = to.area(); if let (Some(time), Some(note)) = (self.time_axis.point, self.note_axis.point) { let x = area.x() + Sequencer::H_KEYS_OFFSET as u16 + time as u16; let y = area.y() + 1 + note as u16 / 2; let c = if note % 2 == 0 { "▀" } else { "▄" }; to.blit(&c, x, y, self.style_focus()); - Ok(()) - } else { - //Ok(Some([0,0,0,0])) - Ok(()) } - }), - - //zoom - CustomWidget::new(|to|Ok(Some([10,1])), |to: &mut TuiOutput|{ - let area = to.area(); - let quant = ppq_to_name(self.time_axis.scale); - let quant_x = area.x() + area.w() - 1 - quant.len() as u16; - let quant_y = area.y() + area.h() - 2; - to.blit(&quant, quant_x, quant_y, self.style_focus()); Ok(()) }), - + //zoom + CustomWidget::new(|_|Ok(Some([10,1])), |to: &mut TuiOutput|{ + let [x, y, w, h] = to.area.xywh(); + let quant = ppq_to_name(self.time_axis.scale); + let x = x + w - 1 - quant.len() as u16; + let y = y + h - 2; + to.blit(&quant, x, y, self.style_focus()); + Ok(()) + }), ); row!(toolbar, content).fill_x() - .bg(Color::Rgb(40,50,30)) + .bg(Color::Rgb(40, 50, 30)) .border(Lozenge(Style::default() - .bg(Color::Rgb(40,50,30)) - .fg(Color::Rgb(70,80,50)))) + .bg(Color::Rgb(40, 50, 30)) + .fg(Color::Rgb(70, 80, 50)))) } } @@ -1479,7 +1499,7 @@ impl Handle for Sequencer { fn nth_octave (index: u16) -> &'static str { match index { - 0 => "-1", + 0 => "-1", 1 => "0", 2 => "1", 3 => "2", @@ -1496,12 +1516,12 @@ fn nth_octave (index: u16) -> &'static str { fn key_colors (index: u16) -> (Color, Color) { match index % 6 { - 0 => (Color::White, Color::Black), - 1 => (Color::White, Color::Black), - 2 => (Color::White, Color::White), - 3 => (Color::Black, Color::White), - 4 => (Color::Black, Color::White), - 5 => (Color::Black, Color::White), + 0 => (Color::Rgb(255, 255, 255), Color::Rgb(0, 0, 0)), + 1 => (Color::Rgb(255, 255, 255), Color::Rgb(0, 0, 0)), + 2 => (Color::Rgb(255, 255, 255), Color::Rgb(255, 255, 255)), + 3 => (Color::Rgb(0, 0, 0), Color::Rgb(255, 255, 255)), + 4 => (Color::Rgb(0, 0, 0), Color::Rgb(255, 255, 255)), + 5 => (Color::Rgb(0, 0, 0), Color::Rgb(255, 255, 255)), _ => unreachable!() } } @@ -1620,34 +1640,6 @@ pub(crate) fn keys_vert () -> Buffer { ////////////////////////////////////////////////////////////////////////////////////////////////// -struct SequenceTimer<'a>(&'a Sequencer, Arc>); -impl<'a> Widget for SequenceTimer<'a> { - type Engine = Tui; - fn layout (&self, _to: [u16;2]) -> Perhaps<[u16;2]> { - Ok(Some([32,2])) - } - fn render (&self, to: &mut TuiOutput) -> Usually<()> { - let area = to.area(); - let phrase = self.1.read().unwrap(); - let (time0, time_z, now) = ( - self.0.time_axis.start, self.0.time_axis.scale, self.0.now % phrase.length - ); - let [x, _, width, _] = area; - let x2 = x as usize + Sequencer::H_KEYS_OFFSET; - let x3 = x as usize + width as usize; - for x in x2..x3 { - let step = (time0 + x2) * time_z; - let next_step = (time0 + x2 + 1) * time_z; - let style = Sequencer::::style_timer_step(now, step as usize, next_step as usize); - to.blit(&"-", x as u16, area.y(), Some(style)); - } - //return Ok(Some([area.x(), area.y(), area.w(), 1])) - Ok(()) - } -} - -////////////////////////////////////////////////////////////////////////////////////////////////// - /// A collection of phrases to play on each track. #[derive(Default)] pub struct Scene { @@ -2013,21 +2005,17 @@ impl Handle for TransportToolbar { impl Content for TransportToolbar { type Engine = Tui; fn content (&self) -> impl Widget { - Stack::right(|add|{ - let focus_wrap = |focused, component|Layers::new(move |add|{ - if focused { - add(&CORNERS)?; - add(&Background(COLOR_BG1))?; - } - add(component) - }); - add(&focus_wrap(self.focused && self.playing.focused, &self.playing))?; - add(&focus_wrap(self.focused && self.bpm.focused, &self.bpm))?; - add(&focus_wrap(self.focused && self.quant.focused, &self.quant))?; - add(&focus_wrap(self.focused && self.sync.focused, &self.sync))?; - add(&focus_wrap(self.focused && self.clock.focused, &self.clock))?; - Ok(()) - }) + let focus_wrap = |focused, component|Layers::new(move |add|{ + if focused { add(&CORNERS)?; add(&Background(COLOR_BG1))?; } + add(component) + }); + row! { + focus_wrap(self.focused && self.playing.focused, &self.playing), + focus_wrap(self.focused && self.bpm.focused, &self.bpm), + focus_wrap(self.focused && self.quant.focused, &self.quant), + focus_wrap(self.focused && self.sync.focused, &self.sync), + focus_wrap(self.focused && self.clock.focused, &self.clock), + }.fill_x().bg(Color::Rgb(25, 30, 20)) } }