mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
clamp grid horizontally
This commit is contained in:
parent
81a74d79dc
commit
751e7d2160
2 changed files with 158 additions and 140 deletions
292
tek/src/lib.rs
292
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<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()
|
||||
|
|
|
|||
|
|
@ -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)))))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue