From 751e7d2160d27da031014f68c2a04904123b5aba Mon Sep 17 00:00:00 2001 From: unspeaker Date: Tue, 21 Jan 2025 01:48:21 +0100 Subject: [PATCH] clamp grid horizontally --- tek/src/lib.rs | 292 ++++++++++++++++++++------------------ tek/src/view_arranger.edn | 6 +- 2 files changed, 158 insertions(+), 140 deletions(-) diff --git a/tek/src/lib.rs b/tek/src/lib.rs index e95142b6..937e6c44 100644 --- a/tek/src/lib.rs +++ b/tek/src/lib.rs @@ -177,13 +177,6 @@ has_editor!(|self: Tek|{ }; editor_h = 15; is_editing = self.editing.load(Relaxed); }); -provide_num!(usize: |self: Tek| { - ":scene" => self.selected.scene().unwrap_or(0), - ":scene-next" => (self.selected.scene().unwrap_or(0) + 1).min(self.scenes.len()), - ":scene-prev" => self.selected.scene().unwrap_or(0).saturating_sub(1), - ":track" => self.selected.track().unwrap_or(0), - ":track-next" => (self.selected.track().unwrap_or(0) + 1).min(self.tracks.len()), - ":track-prev" => self.selected.track().unwrap_or(0).saturating_sub(1) }); view!(TuiOut: |self: Tek| self.size.of(View(self, self.view)); { ":editor" => (&self.editor).boxed(), ":pool" => self.view_pool().boxed(), @@ -191,14 +184,21 @@ view!(TuiOut: |self: Tek| self.size.of(View(self, self.view)); { ":sampler" => self.view_sampler(self.is_editing(), &self.editor).boxed(), ":status" => self.view_editor().boxed(), ":toolbar" => self.view_clock().boxed(), - ":tracks" => self.view_row(self.w(), 2, self.track_header(), self.track_cells()).boxed(), - ":inputs" => self.view_row(self.w(), 2, self.input_header(), self.input_cells()).boxed(), - ":outputs" => self.view_row(self.w(), 2, self.output_header(), self.output_cells()).boxed(), - ":scenes" => Outer(false, Style::default().fg(Tui::g(0))).enclose_bg(self.view_row( - self.w(), self.size.h().saturating_sub(8) as u16, - self.scene_header(), self.clip_columns() - )).boxed() + ":tracks" => self.view_tracks().boxed(), + ":inputs" => self.view_inputs().boxed(), + ":outputs" => self.view_outputs().boxed(), + ":scenes" => self.view_scenes().boxed(), + ":scene-add" => Fill::x(Align::x(Fixed::x(23, button(" C-a ", format!(" add scene ({}/{})", + self.selected().scene().unwrap_or(0), + self.scenes().len()))))).boxed(), }); +provide_num!(usize: |self: Tek| { + ":scene" => self.selected.scene().unwrap_or(0), + ":scene-next" => (self.selected.scene().unwrap_or(0) + 1).min(self.scenes.len()), + ":scene-prev" => self.selected.scene().unwrap_or(0).saturating_sub(1), + ":track" => self.selected.track().unwrap_or(0), + ":track-next" => (self.selected.track().unwrap_or(0) + 1).min(self.tracks.len()), + ":track-prev" => self.selected.track().unwrap_or(0).saturating_sub(1) }); provide!(Color: |self: Tek| {}); provide!(Selection: |self: Tek| {}); provide!(Arc>: |self: Tek| {}); @@ -483,9 +483,39 @@ impl Tek { fn view_pool (&self) -> impl Content + use<'_> { self.pool.as_ref().map(|pool|PoolView(self.is_editing(), pool)) } - fn pool (&self) -> impl Content + use<'_> { - let by_pool = |pool|Align::e(Fixed::x(self.sidebar_w(), PoolView(self.is_editing(), pool))); - self.pool.as_ref().map(by_pool) + fn view_scene_add (&self) -> impl Content + use<'_> { + button(" C-a ", format!(" add scene ({}/{})", + self.selected().scene().unwrap_or(0), + self.scenes().len())) + } + fn view_scenes (&self) -> impl Content + use<'_> { + Outer(false, Style::default().fg(Tui::g(0))).enclose_bg({ + let w = self.w(); + let h = self.size.h().saturating_sub(6 + self.midi_ins.len() + self.midi_outs.len()) as u16; + self.view_row(w, h, self.scene_header(), self.clip_columns()) + }) + } + fn view_tracks (&self) -> impl Content + use<'_> { + let h = 1; + self.view_row(self.w(), 1, self.track_header(), self.track_cells()) + } + fn view_inputs (&self) -> impl Content + use<'_> { + let h = 1 + self.midi_ins.len() as u16; + self.view_row(self.w(), h, self.input_header(), self.input_cells()) + } + fn view_outputs (&self) -> impl Content + use<'_> { + let h = 1 + self.midi_outs.len(); + self.view_row(self.w(), h as u16, + self.output_header(), + self.output_cells()) + } + fn view_row <'a> ( + &'a self, w: u16, h: u16, a: impl Content + 'a, b: impl Content + 'a + ) -> impl Content + 'a { + Fixed::y(h, Bsp::e( + Fixed::x(self.sidebar_w() as u16, a), + Fill::x(Align::c(Fixed::xy(w, h, b))) + )) } fn w (&self) -> u16 { self.tracks_sizes(self.is_editing(), self.editor_w()) @@ -499,14 +529,6 @@ impl Tek { let w = if self.is_editing() { 8 } else { w }; w } - fn view_row <'a> ( - &'a self, w: u16, h: u16, a: impl Content + 'a, b: impl Content + 'a - ) -> impl Content + 'a { - Fixed::y(h, Bsp::e( - Fixed::x(self.sidebar_w() as u16, a), - Fill::x(Align::c(Fixed::xy(w, h, b))) - )) - } fn clip (&self) -> Option>> { self.scene()?.clips.get(self.selected().track()?)?.clone() } @@ -528,67 +550,74 @@ impl Tek { let selected_track = self.selected().track(); let selected_scene = self.selected().scene(); let border = |x|Outer(false, Style::default().fg(Tui::g(0))).enclose(x); + let area = self.size.w().saturating_sub(self.sidebar_w() as usize * 2); (move||Align::c(Map::new(tracks, { + let last_color = Arc::new(RwLock::new(ItemPalette::default())); move|(_, track, x1, x2), t| { - let last_color = Arc::new(RwLock::new(ItemPalette::default())); + let last_color = last_color.clone(); + let same_track = selected_track == Some(t+1); let w = (x2 - x1) as u16; - map_east(x1 as u16, w, border(Map::new(scenes, move|(_, scene, y1, y2), s| { - let same_track = selected_track == Some(t+1); - let selected = same_track && Some(s+1) == selected_scene; - let neighbor = same_track && Some(s) == selected_scene; - let active = editing && selected; + map_east(x1 as u16, w, border(Map::new(scenes, move|(_, scene, y1, y2), s|{ let last_color = last_color.clone(); - let mut fg = Tui::g(64); - let mut bg = ItemPalette::G[32]; - if let Some(clip) = &scene.clips[t] { - let clip = clip.read().unwrap(); - fg = clip.color.lightest.rgb; - bg = clip.color - }; + Either(x2 >= area, (), Thunk::new(move||{ + let last_color = last_color.clone(); + let mut fg = Tui::g(64); + let mut bg = ItemPalette::G[32]; + if let Some(clip) = &scene.clips[t] { + let clip = clip.read().unwrap(); + fg = clip.color.lightest.rgb; + bg = clip.color + }; - //let top = if neighbor { None } else { Some(last_color.read().unwrap().base.rgb) }; - let top = if s == 0 { - Some(Reset) - } else if neighbor { - Some(last_color.read().unwrap().light.rgb) - } else { - Some(last_color.read().unwrap().base.rgb) - }; - let mid = if selected { bg.light } else { bg.base }.rgb; - let low = Some(Reset); - let h = (1 + y2 - y1) as u16; - *last_color.write().unwrap() = bg; - let tab = " Tab "; - let name = if active { - self.editor.as_ref() - .map(|e|e.clip().as_ref().map(|c|c.clone())) - .flatten() - .map(|c|c.read().unwrap().name.clone()) - .unwrap_or_else(||"".into()) - } else { - "edit".into() - }; - let label = move||{ - let clip = scene.clips[t].clone(); - let icon = " ⏹ "; - let name = clip.map(|c|c.read().unwrap().name.clone()); - Align::nw(Tui::fg(fg, Bsp::e(icon, Bsp::e(Tui::bold(true, name), " ")))) - }; - map_south(y1 as u16, h, Push::y(1, Fixed::y(h, Either::new(active, - Thunk::new(move||Bsp::a( - Fill::xy(Align::nw(button(tab, label()))), - &self.editor)), - Thunk::new(move||Bsp::a( - When::new(selected, Fill::y(Align::n(button(tab, "edit")))), - phat_sel_3( - selected, - Fill::xy(label()), - Fill::xy(label()), - top, mid, low - ) - )), - )))) + // weird offsetting: + let selected = same_track && selected_scene == Some(s+1); + let neighbor = same_track && selected_scene == Some(s); + let active = editing && selected; + //let top = if neighbor { None } else { Some(last_color.read().unwrap().base.rgb) }; + let top = if s == 0 { + Some(Reset) + } else if neighbor { + Some(last_color.read().unwrap().light.rgb) + } else { + Some(last_color.read().unwrap().base.rgb) + }; + let mid = if selected { bg.light } else { bg.base }.rgb; + let low = Some(Reset); + let h = (1 + y2 - y1) as u16; + *last_color.write().unwrap() = bg; + let tab = " Tab "; + let name = if active { + self.editor.as_ref() + .map(|e|e.clip().as_ref().map(|c|c.clone())) + .flatten() + .map(|c|c.read().unwrap().name.clone()) + .unwrap_or_else(||"".into()) + } else { + "edit".into() + }; + let label = move||{ + let clip = scene.clips[t].clone(); + let icon = " ⏹ "; + let name = clip.map(|c|c.read().unwrap().name.clone()); + Align::nw(Tui::fg(fg, Bsp::e(icon, Bsp::e(Tui::bold(true, name), " ")))) + }; + map_south(y1 as u16, h, Push::y(1, Fixed::y(h, Either::new(active, + Thunk::new(move||Bsp::a( + Fill::xy(Align::nw(button(tab, label()))), + &self.editor)), + Thunk::new(move||Bsp::a( + When::new(selected, Fill::y(Align::n(button(tab, "edit")))), + phat_sel_3( + selected, + Fill::xy(label()), + Fill::xy(label()), + top, mid, low + ) + )), + )))) + + })) }))).boxed() } })).boxed()).into() @@ -637,16 +666,10 @@ impl Tek { ))).boxed()).into() } fn track_header <'a> (&'a self) -> ThunkBox<'a, TuiOut> { - let add_scene = ||self.button(" C-a ", format!(" add scene ({}/{})", - self.selected.scene().unwrap_or(0), - self.scenes().len())); let add_track = ||self.button(" C-t ", format!(" add track ({}/{})", self.selected.track().unwrap_or(0), self.tracks().len())); - (move||Tui::bg(Tui::g(32), Bsp::s( - Fill::x(Align::w(add_scene())), - Fill::x(Align::w(add_track())), - )).boxed()).into() + (move||Tui::bg(Tui::g(32), Fill::x(Align::w(add_track()))).boxed()).into() } fn button <'a> ( &'a self, key: impl Content + 'a, label: impl Content + 'a @@ -965,6 +988,17 @@ impl HasTracks for Tek { fn tracks (&self) -> &Vec { &self.tracks } fn tracks_mut (&mut self) -> &mut Vec { &mut self.tracks } } +macro_rules! per_track { + (|$self:ident,$track:ident|$content:expr) => {{ + let tracks = ||$self.tracks_sizes($self.is_editing(), $self.editor_w()); + Box::new(move||Align::x(Map::new(tracks, move|(_, $track, x1, x2), i| { + let width = (x2 - x1) as u16; + let content = Fixed::y(1, $content); + let styled = Tui::fg_bg($track.color.lightest.rgb, $track.color.base.rgb, content); + map_east(x1 as u16, width, Fixed::x(width, styled)) + }))).into() + }} +} trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync { fn midi_ins (&self) -> &Vec>; fn midi_outs (&self) -> &Vec>; @@ -990,7 +1024,7 @@ trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync { }) } fn track_next_name (&self) -> Arc { - format!("Trk{:02}", self.tracks().len() + 1).into() + format!("Track{:02}", self.tracks().len() + 1).into() } fn track (&self) -> Option<&Track> { self.selected().track().and_then(|s|self.tracks().get(s)) @@ -1001,52 +1035,37 @@ trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync { fn track_cells <'a> (&'a self) -> ThunkBox<'a, TuiOut> { let iter = ||self.tracks_sizes(self.is_editing(), self.editor_w()); (move||Align::x(Map::new(iter, move|(_, track, x1, x2), i| { - let name = &track.name; - let color = track.color; - let fg = color.lightest.rgb; - let bg = color.base.rgb; - let active = self.selected().track() == Some(i + 1); - let bfg = if active { Rgb(255,255,255) } else { Rgb(0,0,0) }; - let border = Style::default().fg(bfg).bg(bg); + let active = self.selected().track() == Some(i+1); + let name = &track.name; + let color = track.color; + let fg = color.lightest.rgb; + let bg = if active { color.light.rgb } else { color.base.rgb }; + let bfg = if active { Rgb(255,255,255) } else { Rgb(0,0,0) }; + let border = Style::default().fg(bfg).bg(bg); let content = Tui::fg_bg(fg, bg, Tui::bold(true, Fill::x(Align::nw(Bsp::e(" ", name))))); Tui::bg(bg, map_east(x1 as u16, (x2 - x1) as u16, Outer(false, border).enclose(content))) })).boxed()).into() } - fn input_cells <'a> (&'a self) -> ThunkBox<'a, TuiOut> { - let tracks = ||self.tracks_sizes(self.is_editing(), self.editor_w()); - (move||Align::x(Map::new(tracks, move|(_, track, x1, x2), i| { - let w = (x2 - x1) as u16; - let color: ItemPalette = track.color; - map_east(x1 as u16, w, Fixed::x(w, Self::cell(color, - Self::rec_mon(color.base.rgb, false, false)))) - })).boxed()).into() + fn cell > (theme: ItemPalette, field: T) -> impl Content { + Tui::fg_bg(theme.lightest.rgb, theme.base.rgb, Fixed::y(1, field)) } - fn rec_mon (bg: Color, rec: bool, mon: bool) -> impl Content { - row!( - Tui::fg_bg(if rec { Red } else { bg }, bg, "▐"), - Tui::fg_bg(if rec { White } else { Rgb(0,0,0) }, bg, "REC"), - Tui::fg_bg(if rec { White } else { bg }, bg, "▐"), - Tui::fg_bg(if mon { White } else { Rgb(0,0,0) }, bg, "MON"), - Tui::fg_bg(if mon { White } else { bg }, bg, "▌"), - ) + fn input_cells <'a> (&'a self) -> ThunkBox<'a, TuiOut> { + let rec = false; + let mon = false; + per_track!(|self, track|row!( + Tui::fg_bg(if rec { White } else { track.color.light.rgb }, track.color.dark.rgb, "Rcrd"), + Tui::fg_bg(if rec { White } else { track.color.dark.rgb }, track.color.dark.rgb, "▐"), + Tui::fg_bg(if mon { White } else { track.color.light.rgb }, track.color.dark.rgb, "Mntr"), + )) } fn output_cells <'a> (&'a self) -> ThunkBox<'a, TuiOut> { - let tracks = ||self.tracks_sizes(self.is_editing(), self.editor_w()); - (move||Align::x(Map::new(tracks, move|(_, track, x1, x2), i| { - let w = (x2 - x1) as u16; - let color: ItemPalette = track.color; - map_east(x1 as u16, w, Fixed::x(w, Self::cell(color, Self::mute_solo(color.base.rgb, false, false)))) - })).boxed()).into() - } - fn mute_solo (bg: Color, mute: bool, solo: bool) -> impl Content { - row!( - Tui::fg_bg(if mute { White } else { Rgb(0,0,0) }, bg, "MUT"), - Tui::fg_bg(if mute { White } else { bg }, bg, "▐"), - Tui::fg_bg(if solo { White } else { Rgb(0,0,0) }, bg, "SOL"), - ) - } - fn cell > (color: ItemPalette, field: T) -> impl Content { - Tui::fg_bg(color.lightest.rgb, color.base.rgb, Fixed::y(1, field)) + let mute = false; + let solo = false; + per_track!(|self, track|row!( + Tui::fg_bg(if mute { White } else { track.color.light.rgb }, track.color.dark.rgb, "Mute"), + Tui::fg_bg(if mute { White } else { track.color.dark.rgb }, track.color.dark.rgb, "▐"), + Tui::fg_bg(if solo { White } else { track.color.light.rgb }, track.color.dark.rgb, "Solo"), + )) } } pub trait Device: Send + Sync + std::fmt::Debug {} @@ -1169,23 +1188,20 @@ trait HasScenes: HasSelection + HasEditor + Send + Sync { fn scene_header <'a> (&'a self) -> ThunkBox<'a, TuiOut> { (move||{ let last_color = Arc::new(RwLock::new(ItemPalette::G[0])); - let selected = self.selected().scene(); - let iter = ||self.scenes_sizes(self.is_editing(), 2, 15); + let iter = ||self.scenes_sizes(self.is_editing(), 2, 15); Map::new(iter, move|(_, scene, y1, y2), i| { - let color = scene.color; - let top = if i == 0 { Some(Reset) } else if selected == Some(i+0) { None } else { Some(last_color.read().unwrap().base.rgb) }; - let mid = if selected == Some(i+1) { color.light } else { color.base }.rgb; - let low = Some(Reset); let cell = phat_sel_3( - selected == Some(i), + self.selected().scene() == Some(i), Tui::bold(true, Bsp::e("🭬", &scene.name)), Tui::bold(true, Bsp::e("🭬", &scene.name)), - top, - mid, - low + if i == 0 { Some(Reset) } + else if self.selected().scene() == Some(i) { None } + else { Some(last_color.read().unwrap().base.rgb) }, + if self.selected().scene() == Some(i+1) { scene.color.light } else { scene.color.base }.rgb, + Some(Reset) ); let h = (1 + y2 - y1) as u16; - *last_color.write().unwrap() = color; + *last_color.write().unwrap() = scene.color; map_south(y1 as u16, h, Push::y(1, Fixed::y(h, Outer(false, Style::default().fg(Tui::g(0))).enclose(cell)))) }).boxed() diff --git a/tek/src/view_arranger.edn b/tek/src/view_arranger.edn index f1976c4e..af4b588c 100644 --- a/tek/src/view_arranger.edn +++ b/tek/src/view_arranger.edn @@ -1,3 +1,5 @@ (bsp/s (max/y 1 :toolbar) - (fill/x (align/c (bsp/w (fixed/x :pool-w :pool) - (bsp/n :outputs (bsp/n :inputs (bsp/n :tracks :scenes))))))) + (fill/x (align/c (bsp/w (fixed/x :pool-w :pool) + (bsp/n + (bsp/s :scene-add (bsp/s :tracks (bsp/n :inputs :outputs))) + :scenes)))))