From d17d20e7db16d80a5521679ebc3426bde17ba999 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Wed, 1 Jan 2025 17:00:28 +0100 Subject: [PATCH] wip: fixing Map, centering --- engine/src/engine.rs | 8 ++- engine/src/output.rs | 9 ++-- layout/src/align.rs | 14 ++--- layout/src/lib.rs | 10 ++-- layout/src/ops.rs | 29 ++++++++-- src/arranger/arranger_v/v_clips.rs | 4 +- src/arranger/arranger_v/v_head.rs | 2 +- src/groovebox.rs | 5 +- src/pool.rs | 85 ++++++++++++++++-------------- src/transport.rs | 16 +++--- 10 files changed, 104 insertions(+), 78 deletions(-) diff --git a/engine/src/engine.rs b/engine/src/engine.rs index efc2398e..713c802f 100644 --- a/engine/src/engine.rs +++ b/engine/src/engine.rs @@ -90,6 +90,9 @@ pub trait Area { Ok(self) } } + #[inline] fn xy (&self) -> [N;2] { + [self.x(), self.y()] + } #[inline] fn wh (&self) -> [N;2] { [self.w(), self.h()] } @@ -121,7 +124,10 @@ pub trait Area { [self.x(), self.x2(), self.y(), self.y2()] } #[inline] fn center (&self) -> [N;2] { - [self.x() + self.w() / 2.into(), self.y() + self.h() / 2.into()] + [self.x() + self.w()/2.into(), self.y() + self.h()/2.into()] + } + #[inline] fn centered (&self) -> [N;2] { + [self.x().minus(self.w()/2.into()), self.y().minus(self.h()/2.into())] } fn zero () -> [N;4] { [N::zero(), N::zero(), N::zero(), N::zero()] diff --git a/engine/src/output.rs b/engine/src/output.rs index 78a46ce8..4becba8b 100644 --- a/engine/src/output.rs +++ b/engine/src/output.rs @@ -30,16 +30,13 @@ pub trait Content: Send + Sync { } } -/// The platonic ideal item of content: emptiness at dead center. +/// The platonic ideal unit of [Content]: total emptiness at dead center. impl Content for () { fn layout (&self, area: E::Area) -> E::Area { - let [x, y, w, h] = area.xywh(); - let x = x + w / 2.into(); - let y = y + h / 2.into(); + let [x, y] = area.center(); [x, y, 0.into(), 0.into()].into() } - fn render (&self, _: &mut E::Output) { - } + fn render (&self, _: &mut E::Output) {} } impl> Content for &T { diff --git a/layout/src/align.rs b/layout/src/align.rs index d63ca324..01b50f8a 100644 --- a/layout/src/align.rs +++ b/layout/src/align.rs @@ -29,13 +29,13 @@ impl> Content for Align { } pub fn align_areas(alignment: Alignment, on: [N;4], it: [N;4]) -> [N;4] { - let [cfx, cfy] = on.center(); - let [cmx, cmy] = it.center(); - let center = |cf, cm, m: N|if cf >= cm { m + (cf - cm) } else { m.minus(cm - cf) }; - let center_x = center(cfx, cmx, it.x()); - let center_y = center(cfy, cmy, it.y()); - let east_x = on.x() + on.w().minus(it.w()); - let south_y = on.y() + on.h().minus(it.h()); + let [cfx, cfy, ..] = on.center(); + let [cmx, cmy, ..] = it.center(); + let center = |cf, cm, m: N|if cf >= cm { m + (cf - cm) } else { m.minus(cm - cf) }; + let center_x = center(cfx, cmx, it.x()); + let center_y = center(cfy, cmy, it.y()); + let east_x = on.x() + on.w().minus(it.w()); + let south_y = on.y() + on.h().minus(it.h()); let [x, y] = match alignment { Alignment::Center => [center_x, center_y,], diff --git a/layout/src/lib.rs b/layout/src/lib.rs index a8e27425..5f4455be 100644 --- a/layout/src/lib.rs +++ b/layout/src/lib.rs @@ -13,11 +13,13 @@ pub(crate) use ::tek_engine::*; pub(crate) use std::marker::PhantomData; #[cfg(test)] #[test] fn test_layout () -> Usually<()> { + use crate::tui::Tui; let area: [u16;4] = [10, 10, 20, 20]; let unit = (); - //assert_eq!(().layout(area), [15, 15, 0, 0]); // should be independent over E, isn't - assert_eq!(Fill::x(()).layout(area), [10, 15, 20, 0]); - assert_eq!(Fill::y(()).layout(area), [15, 10, 0, 20]); - assert_eq!(Fill::xy(()).layout(area), area); + // should be independent over E, isn't + assert_eq!(Content::::layout(&unit, area), [15, 15, 0, 0]); + assert_eq!(Fill::::x(unit).layout(area), [10, 15, 20, 0]); + assert_eq!(Fill::::y(unit).layout(area), [15, 10, 0, 20]); + assert_eq!(Fill::::xy(unit).layout(area), area); Ok(()) } diff --git a/layout/src/ops.rs b/layout/src/ops.rs index 6d093b58..5e9bea67 100644 --- a/layout/src/ops.rs +++ b/layout/src/ops.rs @@ -21,13 +21,14 @@ pub trait Layout { { Opt(option, cb, Default::default()) } - fn map (iterator: I, callback: F) -> Map where + fn map (iterator: J, callback: F) -> Map where E: Engine, I: Iterator + Send + Sync, + J: Fn() -> I + Send + Sync, R: Content, F: Fn(T, usize)->R + Send + Sync { - Map(Default::default(), RwLock::new(iterator), callback) + Map(Default::default(), iterator, callback) } //pub fn reduce (iterator: I, callback: F) -> Reduce where //E: Engine, @@ -77,21 +78,39 @@ impl, B: Content> Content for Either { } } -pub struct Map(PhantomData, RwLock, F) where +pub struct Map(PhantomData, J, F) where E: Engine, I: Iterator + Send + Sync, + J: Fn()->I + Send + Sync, R: Content, F: Fn(T, usize)->R + Send + Sync; -impl Content for Map where +impl Content for Map where E: Engine, I: Iterator + Send + Sync, + J: Fn()->I + Send + Sync, R: Content, F: Fn(T, usize)->R + Send + Sync { + fn layout (&self, area: E::Area) -> E::Area { + let mut index = 0; + let mut max_w = 0; + let mut max_h = 0; + for item in (self.1)() { + let [x, y, w, h] = (self.2)(item, index).layout(area).xywh(); + max_w = max_w.max((x + w).into()); + max_h = max_h.max((y + h).into()); + index += 1; + } + align_areas( + Alignment::Center, + area.xywh(), + [0.into(), 0.into(), max_w.into(), max_h.into()] + ).into() + } fn render (&self, to: &mut E::Output) { let mut index = 0; - for item in &mut*self.1.write().unwrap() { + for item in (self.1)() { (self.2)(item, index).render(to); index += 1; } diff --git a/src/arranger/arranger_v/v_clips.rs b/src/arranger/arranger_v/v_clips.rs index a3216224..97520739 100644 --- a/src/arranger/arranger_v/v_clips.rs +++ b/src/arranger/arranger_v/v_clips.rs @@ -19,7 +19,7 @@ impl<'a> ArrangerVClips<'a> { } impl<'a> Content for ArrangerVClips<'a> { fn content (&self) -> impl Content { - let iter = self.scenes.iter().zip(self.rows.iter().map(|row|row.0)); + let iter = ||self.scenes.iter().zip(self.rows.iter().map(|row|row.0)); let col = Tui::map(iter, |(scene, pulses), i|Self::format_scene(self.tracks, scene, pulses)); Fill::xy(col) } @@ -37,7 +37,7 @@ impl<'a> ArrangerVClips<'a> { let name = Tui::fg_bg(scene.color.lightest.rgb, scene.color.base.rgb, Expand::x(1, Tui::bold(true, scene.name.read().unwrap().clone())) ); - let clips = Tui::map(ArrangerTrack::with_widths(tracks), move|(index, track, x1, x2), _| + let clips = Tui::map(||ArrangerTrack::with_widths(tracks), move|(index, track, x1, x2), _| Push::x((x2 - x1) as u16, Self::format_clip(scene, index, track, (x2 - x1) as u16, height)) ); Fixed::y(height, row!(icon, name, clips)) diff --git a/src/arranger/arranger_v/v_head.rs b/src/arranger/arranger_v/v_head.rs index 49146fb0..0a9a68a3 100644 --- a/src/arranger/arranger_v/v_head.rs +++ b/src/arranger/arranger_v/v_head.rs @@ -20,7 +20,7 @@ render!(Tui: (self: ArrangerVHead<'a>) => { row!(Tui::fg(color.light.rgb, "▎"), Tui::fg(color.lightest.rgb, field)) } Some(Push::x(self.scenes_w, - Tui::map(ArrangerTrack::with_widths(self.tracks), |(_, track, x1, x2), i| { + Tui::map(||ArrangerTrack::with_widths(self.tracks), |(_, track, x1, x2), i| { let (w, h) = (ArrangerTrack::MIN_WIDTH.max(x2 - x1), HEADER_H); let color = track.color(); let input = Self::format_input(track); diff --git a/src/groovebox.rs b/src/groovebox.rs index ff1f1bc8..c5e348ca 100644 --- a/src/groovebox.rs +++ b/src/groovebox.rs @@ -128,7 +128,7 @@ render!(Tui: (self: Groovebox) => { PhraseSelector::play_phrase(&self.player), PhraseSelector::next_phrase(&self.player), ))); - "tabula rasa" + PoolView(&self.pool) //let pool = PoolView(&self.pool); //let with_pool = move|x|Bsp::w(Fixed::x(pool_w, Pull::y(1, Fill::y(Align::e(pool)))), x); //with_pool(col!(transport, selector)) @@ -182,7 +182,8 @@ render!(Tui: (self: GrooveboxSamples<'a>) => { let note_lo = self.0.editor.note_lo().load(Relaxed); let note_pt = self.0.editor.note_point(); let note_hi = self.0.editor.note_hi(); - Fill::xy(Tui::map((note_lo..=note_hi).rev(), move|note, i| { + let range = move||(note_lo..=note_hi).rev(); + Fill::xy(Tui::map(range, move|note, i| { let mut bg = if note == note_pt { TuiTheme::g(64) } else { Color::Reset }; let mut fg = TuiTheme::g(160); if let Some((index, _)) = self.0.sampler.recording { diff --git a/src/pool.rs b/src/pool.rs index 146cae07..f7982dbf 100644 --- a/src/pool.rs +++ b/src/pool.rs @@ -209,49 +209,54 @@ pub struct PoolView<'a>(pub(crate) &'a PoolModel); // TODO: Display phrases always in order of appearance render!(Tui: (self: PoolView<'a>) => { let PoolModel { phrases, mode, .. } = self.0; - let bg = TuiTheme::g(32); + let bg = TuiTheme::g(32); let title_color = TuiTheme::ti1(); - let upper_left = "Pool:"; + let upper_left = "Pool:"; let upper_right = format!("({})", phrases.len()); - let color = self.0.phrase().read().unwrap().color; - let border = Fill::xy(Outer(Style::default().fg(color.base.rgb).bg(bg))); - let enclose = |x|lay!(border, Padding::xy(0, 1, Tui::bg(bg, x))); - let content = Tui::either( - self.0.file_picker().is_some(), + let color = self.0.phrase().read().unwrap().color; + let with_files = |x|Tui::either(self.0.file_picker().is_some(), Thunk::new(||self.0.file_picker().unwrap()), - Thunk::new(||Tui::map(phrases.iter(), |phrase, i|{ - let MidiClip { ref name, color, length, .. } = *phrase.read().unwrap(); - let mut length = PhraseLength::new(length, None); - if let Some(PoolMode::Length(phrase, new_length, focus)) = self.0.mode { - if i == phrase { - length.pulses = new_length; - length.focus = Some(focus); - } - } - let clip = Tui::bg(color.base.rgb, Fill::x(col!( - Fill::x(lay!( - Fill::x(Align::w(format!(" {i}"))), - Fill::x(Align::e(Pull::x(1, length.clone()))), - )), - Tui::bold(true, { - let mut row2 = format!(" {name}"); - if let Some(PoolMode::Rename(phrase, _)) = self.0.mode { - if i == phrase { - row2 = format!("{row2}▄"); - } - }; - row2 - }), - ))); - Push::y(i as u16 * 2, lay!(clip, Tui::when(i == self.0.phrase_index(), CORNERS))) - }))); - enclose(lay!( - //add(&Lozenge(Style::default().bg(border_bg).fg(border_color)))?; - content, - Fill::x(Align::nw(Push::x(1, Tui::fg(title_color, upper_left.to_string())))), - Fill::x(Align::ne(Pull::x(1, Tui::fg(title_color, upper_right.to_string())))), - self.0.size.clone() - )) + Thunk::new(x)); + let content = with_files(||Tui::map(||phrases.iter(), |clip, i|{ + let MidiClip { ref name, color, length, .. } = *clip.read().unwrap(); + //let mut length = PhraseLength::new(length, None); + //if let Some(PoolMode::Length(clip, new_length, focus)) = self.0.mode { + //if i == clip { + //length.pulses = new_length; + //length.focus = Some(focus); + //} + //} + Push::y(1 + i as u16 * 2, Fill::x(Fixed::y(2, Tui::bg(color.base.rgb, + format!(" {i} {name} {length} ")))))/*, + name.clone()))))Bsp::s( + Fill::x(Bsp::a( + Align::w(format!(" {i}")), + Align::e(Pull::x(1, length)), + )), + Tui::bold(true, { + let mut row2 = format!(" {name}"); + if let Some(PoolMode::Rename(clip, _)) = self.0.mode { + if i == clip { + row2 = format!("{row2}▄"); + } + }; + row2 + }), + ))))//lay!(clip, Tui::when(i == self.0.clip_index(), CORNERS)))*/ + })); + content + //let border = Outer(Style::default().fg(color.base.rgb).bg(color.dark.rgb)); + //let enclose = |x|lay!( + //Fill::xy(border), + //Padding::xy(0, 1, Tui::bg(bg, x)) + //); + //enclose(lay!( + ////add(&Lozenge(Style::default().bg(border_bg).fg(border_color)))?; + //Fill::xy(content), + //Fill::x(Align::nw(Push::x(1, Tui::fg(title_color, upper_left.to_string())))), + //Fill::x(Align::ne(Pull::x(1, Tui::fg(title_color, upper_right.to_string())))), + //self.0.size.clone() + //)) }); command!(|self: FileBrowserCommand, state: PoolModel|{ use PoolMode::*; diff --git a/src/transport.rs b/src/transport.rs index 20254ca8..c67c9ea4 100644 --- a/src/transport.rs +++ b/src/transport.rs @@ -23,10 +23,11 @@ from_jack!(|jack|TransportTui Self { has_clock!(|self: TransportTui|&self.clock); audio!(|self: TransportTui, client, scope|ClockAudio(self).process(client, scope)); handle!(|self: TransportTui, from|TransportCommand::execute_with_state(self, from)); -render!(Tui: (self: TransportTui) => Align::x(Fixed::y(3, row!( +render!(Tui: (self: TransportTui) => PlayPause(false)); +/*Align::x(Fixed::y(3, row!( Fixed::x(5, Fixed::y(3, PlayPause(false))), TransportView::new(self, Some(self.color), true), -)))); +))));*/ impl std::fmt::Debug for TransportTui { fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { f.debug_struct("TransportTui") @@ -111,14 +112,9 @@ render!(Tui: (self: TransportView) => { pub struct PlayPause(pub bool); render!(Tui: (self: PlayPause) => Tui::bg( if self.0{Color::Rgb(0,128,0)}else{Color::Rgb(128,64,0)}, - Fixed::x(5, Tui::either(self.0, Tui::fg(Color::Rgb(0, 255, 0), col!( - " 🭍🭑🬽 ", - " 🭞🭜🭘 ", - )), Tui::fg(Color::Rgb(255, 128, 0), col!( - " ▗▄▖ ", - " ▝▀▘ ", - )))) -)); + Fixed::x(5, Tui::either(self.0, + Tui::fg(Color::Rgb(0, 255, 0), Bsp::s(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)), + Tui::fg(Color::Rgb(255, 128, 0), Bsp::s(" ▗▄▖ ", " ▝▀▘ ",)))))); impl HasFocus for TransportTui { type Item = TransportFocus; fn focused (&self) -> Self::Item {