refactor scene grid code, trying to fix initial alignment

This commit is contained in:
🪞👃🪞 2025-01-27 14:34:16 +01:00
parent df396f2f83
commit 36c1c9bebb
7 changed files with 159 additions and 143 deletions

View file

@ -18,6 +18,9 @@ pub trait HasJack {
fn port_by_name (&self, name: &str) -> Option<Port<Unowned>> {
self.with_client(|client|client.port_by_name(name))
}
fn port_by_id (&self, id: u32) -> Option<Port<Unowned>> {
self.with_client(|c|c.port_by_id(id))
}
fn register_port <PS: PortSpec + Default> (&self, name: impl AsRef<str>) -> Usually<Port<PS>> {
self.with_client(|client|Ok(client.register_port(name.as_ref(), PS::default())?))
}

View file

@ -90,6 +90,9 @@ pub trait JackPortAutoconnect: JackPort + for<'a>JackPortConnect<&'a Port<Unowne
fn ports (&self, re_name: Option<&str>, re_type: Option<&str>, flags: PortFlags) -> Vec<String> {
self.with_client(|c|c.ports(re_name, re_type, flags))
}
fn port_by_id (&self, id: u32) -> Option<Port<Unowned>> {
self.with_client(|c|c.port_by_id(id))
}
fn port_by_name (&self, name: impl AsRef<str>) -> Option<Port<Unowned>> {
self.with_client(|c|c.port_by_name(name.as_ref()))
}

View file

@ -73,26 +73,22 @@ audio!(
|self, event|{
use JackEvent::*;
match event {
SampleRate(sr) =>
{ self.clock.timebase.sr.set(sr as f64); },
PortRegistration(id, true) =>
{ /*println!("\rport add: {id}")*/ },
PortRegistration(id, false) =>
{ /*println!("\rport del: {id}")*/ },
PortsConnected(a, b, true) =>
{ /*println!("\rport conn: {a} {b}")*/ },
PortsConnected(a, b, false) =>
{ /*println!("\rport disc: {a} {b}")*/ },
ClientRegistration(id, true) =>
{},
ClientRegistration(id, false) =>
{},
ThreadInit =>
{},
XRun =>
{},
GraphReorder =>
{},
SampleRate(sr) => { self.clock.timebase.sr.set(sr as f64); },
PortRegistration(id, true) => {
//let port = self.jack().port_by_id(id);
//println!("\rport add: {id} {port:?}");
println!("\rport add: {id}");
},
PortRegistration(id, false) => {
/*println!("\rport del: {id}")*/
},
PortsConnected(a, b, true) => { /*println!("\rport conn: {a} {b}")*/ },
PortsConnected(a, b, false) => { /*println!("\rport disc: {a} {b}")*/ },
ClientRegistration(id, true) => {},
ClientRegistration(id, false) => {},
ThreadInit => {},
XRun => {},
GraphReorder => {},
_ => { panic!("{event:?}"); }
}
}

View file

@ -5,23 +5,19 @@ mod input; pub use self::input::*;
mod output; pub use self::output::*;
macro_rules! def_sizes_iter {
($Type:ident => $($Item:ty),+) => {
trait $Type<'a> = Iterator<Item=(usize, $(&'a $Item,)+ usize, usize)> + Send + Sync + 'a;
}
}
pub(crate) trait $Type<'a> =
Iterator<Item=(usize, $(&'a $Item,)+ usize, usize)> + Send + Sync + 'a;}}
def_sizes_iter!(ScenesSizes => Scene);
def_sizes_iter!(TracksSizes => Track);
def_sizes_iter!(InputsSizes => JackMidiIn);
def_sizes_iter!(OutputsSizes => JackMidiOut);
def_sizes_iter!(PortsSizes => Arc<str>, [PortConnect]);
pub(crate) trait ScenesColors<'a> = Iterator<Item=SceneColor<'a>>;
pub(crate) type SceneColor<'a> = (usize, &'a Scene, usize, usize, Option<ItemPalette>);
#[derive(Debug, Default)] struct ViewMemo<T, U> { value: T, view: Arc<RwLock<U>> }
impl<T: PartialEq, U> ViewMemo<T, U> {
fn new (value: T, view: U) -> Self {
Self {
value,
view: Arc::new(view.into())
}
}
fn new (value: T, view: U) -> Self { Self { value, view: Arc::new(view.into()) } }
fn update <R> (&mut self, newval: T, render: impl Fn(&mut U, &T, &T)->R) -> Option<R> {
if newval != self.value {
let result = render(&mut*self.view.write().unwrap(), &newval, &self.value);
@ -254,17 +250,37 @@ impl Tek {
}
/// COMPONENTS ////////////////////////////////////////////////////////////////////////////////
fn row <'a> (
&'a self, w: u16, h: u16, a: impl Content<TuiOut> + 'a, b: impl Content<TuiOut> + 'a
&'a self,
w: u16,
h: u16,
a: impl Content<TuiOut> + 'a,
b: impl Content<TuiOut> + 'a,
c: impl Content<TuiOut> + 'a,
) -> impl Content<TuiOut> + 'a {
Fixed::y(h, Bsp::a(
Fill::xy(Align::w(Fixed::x(self.w_sidebar() as u16, a))),
Fill::xy(Align::c(Fixed::x(w, Align::x(b))))))}
Fill::xy(Align::c(Fixed::x(w, Align::x(b)))),
Bsp::a(
Fill::xy(Align::w(Fixed::x(self.w_sidebar() as u16, a))),
Fill::xy(Align::e(Fixed::x(self.w_sidebar() as u16, c))),
),
))
}
fn row_top <'a> (
&'a self, w: u16, h: u16, a: impl Content<TuiOut> + 'a, b: impl Content<TuiOut> + 'a
&'a self,
w: u16,
h: u16,
a: impl Content<TuiOut> + 'a,
b: impl Content<TuiOut> + 'a,
c: impl Content<TuiOut> + 'a,
) -> impl Content<TuiOut> + 'a {
Fixed::y(h, Bsp::a(
Fill::xy(Align::nw(Fixed::xy(self.w_sidebar() as u16, h, a))),
Fill::xy(Align::n(Fixed::xy(w, h, Align::x(b))))))}
Fill::x(Align::n(Fixed::x(w, Align::x(Tui::bg(Green, b))))),
Bsp::a(
Fill::x(Align::nw(Fixed::x(self.w_sidebar() as u16, Tui::bg(Red, a)))),
Fill::x(Align::ne(Fixed::x(self.w_sidebar() as u16, Tui::bg(Yellow, c)))),
),
))
}
fn per_track <'a, T: Content<TuiOut> + 'a> (
&'a self, f: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
) -> impl Content<TuiOut> + 'a {

View file

@ -4,77 +4,90 @@ impl Tek {
let w = (self.size.w() as u16).saturating_sub(2 * self.w_sidebar());
let data = (self.selected.track().unwrap_or(0), self.tracks().len());
self.fmtd.write().unwrap().trks.update(Some(data), rewrite!(buf, "{}/{}", data.0, data.1));
self.row(w, 1, Align::w(Bsp::e(
self.button3("t", "track", self.fmtd.read().unwrap().trks.view.clone()),
self.button2("T", "add track"),
)), self.per_track(|t, track|{
let active = self.selected().track() == Some(t+1);
let name = &track.name;
let fg = track.color.lightest.rgb;
let bg = if active { track.color.light.rgb } else { track.color.base.rgb };
let bg2 = Reset;//if t > 0 { self.tracks()[t - 1].color.base.rgb } else { Reset };
let bfg = if active { Rgb(255,255,255) } else { Rgb(0,0,0) };
Self::wrap(bg, fg, Tui::bold(true, Fill::x(Align::nw(name))))
}))
}
self.row(w, 1, self.button3("t", "track", self.fmtd.read().unwrap().trks.view.clone()),
self.per_track(|t, track|self.view_track_header(t, track)),
self.button2("T", "add track")) }
fn view_track_header <'a> (
&self, t: usize, track: &'a Track
) -> impl Content<TuiOut> + use<'a> {
let active = self.selected().track() == Some(t+1);
let name = &track.name;
let fg = track.color.lightest.rgb;
let bg = if active { track.color.light.rgb } else { track.color.base.rgb };
let bg2 = Reset;//if t > 0 { self.tracks()[t - 1].color.base.rgb } else { Reset };
let bfg = if active { Rgb(255,255,255) } else { Rgb(0,0,0) };
Self::wrap(bg, fg, Tui::bold(true, Fill::x(Align::nw(name)))) }
pub fn view_scenes (&self) -> impl Content<TuiOut> + use<'_> {
let w = self.w_tracks_area();
let w_full = self.w();
let h = self.h_tracks_area();
let editing = self.is_editing();
let selected_track = self.selected().track();
let selected_scene = self.selected().scene();
self.row(w, h,
Align::y(Map::new(
move||self.scenes_sizes(editing, 2, 15).map_while(
move|(s, scene, y1, y2)|if y2 as u16 > h {
None
} else { Some((s, scene, y1, y2, if s == 0 {
None
} else {
Some(self.scenes[s-1].color)
})) }),
move|(_, scene, y1, y2, prev), s| {
let height = (1 + y2 - y1) as u16;
let bg = scene.color;
let fg = scene.color.lightest.rgb;
let name = Some(scene.name.clone());
let cell = self.clip_cell(true, s, &bg, prev, name, "", fg);
map_south(y1 as u16, height, Fixed::y(height, cell)) })).boxed(),
self.per_track(move|t, track|{
let same_track = selected_track == Some(t+1);
Map::new(
move||self.scenes_sizes(editing, 2, 15).map_while(
move|(s, scene, y1, y2)|if y2 as u16 > h {
None
} else { Some((s, scene, y1, y2, if s == 0 {
None
} else {
Some(self.scenes[s-1].clips[t].as_ref()
.map(|c|c.read().unwrap().color)
.unwrap_or(ItemPalette::G[32]))
})) }),
move|(_, scene, y1, y2, prev), s| {
let height = (1 + y2 - y1) as u16;
let (name, fg, bg) = if let Some(clip) = &scene.clips[t] {
let clip = clip.read().unwrap();
(Some(clip.name.clone()), clip.color.lightest.rgb, clip.color)
} else {
(None, Tui::g(96), ItemPalette::G[32])
};
let active = editing && same_track && selected_scene == Some(s+1);
let edit = |x|Bsp::b(x, When(active, &self.editor));
let cell = self.clip_cell(same_track, s, &bg, prev, name, "", fg);
map_south(y1 as u16, height, edit(Fixed::y(height, cell)))}) })) }
const TAB: &str = " Tab";
Tui::bg(Black, self.row_top(
self.w_tracks_area(),
h,
Map::new(
move||self.scenes_with_colors(editing, h),
move|(s, scene, y1, y2, prev): SceneColor, _|self.view_scene_name(
w_full, (1 + y2 - y1) as u16, y1 as u16, s, scene, prev)),
self.per_track(move|t, track|Map::new(
move||self.scenes_with_track_colors(editing, h, t),
move|(s, scene, y1, y2, prev): SceneColor, _|self.view_scene_track(
(1 + y2 - y1) as u16, y1 as u16,
scene, prev, s, t, editing, selected_track == Some(t+1), selected_scene))), ())) }
fn scenes_with_colors (&self, editing: bool, h: u16) -> impl ScenesColors<'_> {
self.scenes_sizes(editing, 2, 15).map_while(
move|(s, scene, y1, y2)|if y2 as u16 > h {
None
} else { Some((s, scene, y1, y2, if s == 0 {
None
} else {
Some(self.scenes[s-1].color)
})) }) }
fn view_scene_name (
&self, width: u16, height: u16, offset: u16,
s: usize, scene: &Scene, prev: Option<ItemPalette>,
) -> impl Content<TuiOut> + use<'_> {
let bg = scene.color;
let fg = scene.color.lightest.rgb;
let name = Some(scene.name.clone());
let cell = self.clip_cell(true, s, &bg, prev, name, "", fg);
Fixed::x(width, map_south(offset, height, Fixed::y(height, cell))) }
fn scenes_with_track_colors (&self, editing: bool, h: u16, t: usize) -> impl ScenesColors<'_> {
self.scenes_sizes(editing, 2, 15).map_while(
move|(s, scene, y1, y2)|if y2 as u16 > h {
None
} else { Some((s, scene, y1, y2, if s == 0 {
None
} else {
Some(self.scenes[s-1].clips[t].as_ref()
.map(|c|c.read().unwrap().color)
.unwrap_or(ItemPalette::G[32]))
})) }) }
fn view_scene_track (
&self, height: u16, offset: u16,
scene: &Scene, prev: Option<ItemPalette>, s: usize, t: usize,
editing: bool, same_track: bool, selected_scene: Option<usize>
) -> impl Content<TuiOut> + use<'_> {
let (name, fg, bg) = if let Some(clip) = &scene.clips[t] {
let clip = clip.read().unwrap();
(Some(clip.name.clone()), clip.color.lightest.rgb, clip.color)
} else {
(None, Tui::g(96), ItemPalette::G[32])
};
let active = editing && same_track && selected_scene == Some(s+1);
let edit = |x|Bsp::b(x, When(active, &self.editor));
let cell = self.clip_cell(same_track, s, &bg, prev, name, "", fg);
map_south(offset, height, edit(Fixed::y(height, cell))) }
fn clip_cell <'a> (
&self,
same_track: bool,
scene: usize,
color: &ItemPalette,
prev: Option<ItemPalette>,
name: Option<Arc<str>>,
icon: &'a str,
fg: Color,
scene: usize,
color: &ItemPalette,
prev: Option<ItemPalette>,
name: Option<Arc<str>>,
icon: &'a str,
fg: Color,
) -> impl Content<TuiOut> + use<'a> {
let selected_scene = self.selected().scene();
let selected = same_track && selected_scene == Some(scene+1);
@ -82,34 +95,23 @@ impl Tek {
let is_last = scene == self.scenes.len().saturating_sub(1);
let colors = Self::colors(color, prev, selected, neighbor, is_last);
let content = Fill::x(Align::w(Tui::fg(fg, Tui::bold(true, Bsp::e(icon, name)))));
Phat { width: 0, height: 0, selected, content, colors, }
}
Phat { width: 0, height: 0, selected, content, colors, } }
fn colors (
theme: &ItemPalette, prev: Option<ItemPalette>,
selected: bool, neighbor: bool, is_last: bool,
) -> [Color;4] {
let fg = theme.lightest.rgb;
let bg = if selected { theme.light } else { theme.base }.rgb;
let hi = Self::color_hi(prev, neighbor);
let lo = Self::color_lo(theme, is_last, selected);
[fg, bg, hi, lo] }
fn color_hi (prev: Option<ItemPalette>, neighbor: bool) -> Color {
prev.map(|prev|if neighbor { prev.light.rgb } else { prev.base.rgb }).unwrap_or(Reset) }
fn color_lo (theme: &ItemPalette, is_last: bool, selected: bool) -> Color {
if is_last { Reset } else if selected { theme.light.rgb } else { theme.base.rgb } }
const TAB: &str = " Tab";
pub fn view_scene_add (&self) -> impl Content<TuiOut> + use<'_> {
let data = (self.selected().scene().unwrap_or(0), self.scenes().len());
self.fmtd.write().unwrap().scns.update(Some(data), rewrite!(buf, "({}/{})", data.0, data.1));
self.button3("S", "add scene", self.fmtd.read().unwrap().scns.view.clone())
}
fn colors (
this: &ItemPalette,
prev: Option<ItemPalette>,
selected: bool,
neighbor: bool,
is_last: bool,
) -> [Color;4] {
let fg = this.lightest.rgb;
let bg = if selected { this.light } else { this.base }.rgb;
let hi = if neighbor {
prev.map(|prev|prev.light.rgb)
} else {
prev.map(|prev|prev.base.rgb)
}.unwrap_or(Reset);
let lo = if is_last {
Reset
} else if selected {
this.light.rgb
} else {
this.base.rgb
};
[fg, bg, hi, lo]
}
self.button3("S", "add scene", self.fmtd.read().unwrap().scns.view.clone()) }
}

View file

@ -9,12 +9,9 @@ impl Tek {
track.color.dark.rgb,
track.color.darker.rgb,
||self.inputs_sizes()
)));
)), ());
let ports = self.row_top(w, 1,
Fill::x(Align::w(Bsp::e(
self.button3("i", "midi ins", format!("{}", self.midi_ins.len())),
self.button2("I", "add midi in"),
))),
self.button3("i", "midi ins", format!("{}", self.midi_ins.len())),
self.per_track_top(move|t, track|{
let rec = track.player.recording;
let mon = track.player.monitoring;
@ -28,14 +25,16 @@ impl Tek {
let bg2 = if t > 0 { self.tracks()[t - 1].color.base.rgb } else { Reset };
Self::wrap(bg, fg, Tui::bold(true, Fill::x(Bsp::e(
Tui::fg_bg(rec, bg, "Rec "),
Tui::fg_bg(mon, bg, "Mon ")))))}));
Tui::fg_bg(mon, bg, "Mon ")))))
}),
self.button2("I", "add midi in"));
Bsp::s(
Bsp::s(routes, ports),
self.row_top(w, 2,
Bsp::s(Align::e("Input:"), Align::e("Into:")),
self.per_track_top(|_, _|Tui::bg(Reset, Align::c(Bsp::s(
OctaveVertical::default(),
" ------ ")))))
" ------ ")))), ())
)
}
}

View file

@ -3,16 +3,12 @@ impl Tek {
pub fn view_outputs (&self) -> impl Content<TuiOut> + use<'_> {
let w = self.w_tracks_area();
let fg = Tui::g(224);
let nexts = self.row_top(w, 1, Align::e("Next:"),
self.per_track_top(|_, _|Tui::bg(Reset, " ------ ")));
let froms = self.row_top(w, 2, Align::e("From:"),
self.per_track_top(|_, _|Tui::bg(Reset,
Align::c(Bsp::s(" ------ ", OctaveVertical::default(),)))));
let nexts = self.per_track_top(|_, _|Tui::bg(Reset, " ------ "));
let nexts = self.row_top(w, 1, Align::e("Next:"), nexts, ());
let froms = self.per_track_top(|_, _|Tui::bg(Reset, Align::c(Bsp::s(" ------ ", OctaveVertical::default(),))));
let froms = self.row_top(w, 2, Align::e("From:"), froms, ());
let ports = self.row_top(w, 1,
Fill::x(Align::w(Bsp::e(
self.button3("o", "midi outs", format!("{}", self.midi_outs.len())),
self.button2("O", "add midi out"),
))),
self.button3("o", "midi outs", format!("{}", self.midi_outs.len())),
self.per_track_top(move|t, track|{
let mute = false;
let solo = false;
@ -22,11 +18,12 @@ impl Tek {
let bg2 = if t > 0 { self.tracks()[t - 1].color.base.rgb } else { Reset };
Self::wrap(bg, fg, Tui::bold(true, Fill::x(Bsp::e(
Tui::fg_bg(mute, bg, "Play "),
Tui::fg_bg(solo, bg, "Solo ")))))}));
Tui::fg_bg(solo, bg, "Solo ")))))}),
self.button2("O", "add midi out"));
let routes = self.row_top(w, self.h_outputs() - 1,
self.io_ports(fg, Tui::g(32), ||self.outputs_sizes()),
self.per_track_top(move|t, track|self.io_connections(
track.color.dark.rgb, track.color.darker.rgb, ||self.outputs_sizes())));
track.color.dark.rgb, track.color.darker.rgb, ||self.outputs_sizes())), ());
Align::n(Bsp::s(Bsp::s(nexts, froms), Bsp::s(ports, routes)))
}