clamp grid horizontally

This commit is contained in:
🪞👃🪞 2025-01-21 01:48:21 +01:00
parent 81a74d79dc
commit 751e7d2160
2 changed files with 158 additions and 140 deletions

View file

@ -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<RwLock<MidiClip>>: |self: Tek| {});
@ -483,9 +483,39 @@ impl Tek {
fn view_pool (&self) -> impl Content<TuiOut> + use<'_> {
self.pool.as_ref().map(|pool|PoolView(self.is_editing(), pool))
}
fn pool (&self) -> impl Content<TuiOut> + 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<TuiOut> + use<'_> {
button(" C-a ", format!(" add scene ({}/{})",
self.selected().scene().unwrap_or(0),
self.scenes().len()))
}
fn view_scenes (&self) -> impl Content<TuiOut> + 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<TuiOut> + use<'_> {
let h = 1;
self.view_row(self.w(), 1, self.track_header(), self.track_cells())
}
fn view_inputs (&self) -> impl Content<TuiOut> + 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<TuiOut> + 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<TuiOut> + 'a, b: impl Content<TuiOut> + 'a
) -> impl Content<TuiOut> + '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<TuiOut> + 'a, b: impl Content<TuiOut> + 'a
) -> impl Content<TuiOut> + '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<Arc<RwLock<MidiClip>>> {
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<TuiOut> + 'a, label: impl Content<TuiOut> + 'a
@ -965,6 +988,17 @@ impl HasTracks for Tek {
fn tracks (&self) -> &Vec<Track> { &self.tracks }
fn tracks_mut (&mut self) -> &mut Vec<Track> { &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<JackPort<MidiIn>>;
fn midi_outs (&self) -> &Vec<JackPort<MidiOut>>;
@ -990,7 +1024,7 @@ trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync {
})
}
fn track_next_name (&self) -> Arc<str> {
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 <T: Content<TuiOut>> (theme: ItemPalette, field: T) -> impl Content<TuiOut> {
Tui::fg_bg(theme.lightest.rgb, theme.base.rgb, Fixed::y(1, field))
}
fn rec_mon (bg: Color, rec: bool, mon: bool) -> impl Content<TuiOut> {
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<TuiOut> {
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 <T: Content<TuiOut>> (color: ItemPalette, field: T) -> impl Content<TuiOut> {
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()

View file

@ -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)))))