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>> { fn port_by_name (&self, name: &str) -> Option<Port<Unowned>> {
self.with_client(|client|client.port_by_name(name)) 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>> { 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())?)) 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> { 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)) 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>> { fn port_by_name (&self, name: impl AsRef<str>) -> Option<Port<Unowned>> {
self.with_client(|c|c.port_by_name(name.as_ref())) self.with_client(|c|c.port_by_name(name.as_ref()))
} }

View file

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

View file

@ -5,23 +5,19 @@ mod input; pub use self::input::*;
mod output; pub use self::output::*; mod output; pub use self::output::*;
macro_rules! def_sizes_iter { macro_rules! def_sizes_iter {
($Type:ident => $($Item:ty),+) => { ($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!(ScenesSizes => Scene);
def_sizes_iter!(TracksSizes => Track); def_sizes_iter!(TracksSizes => Track);
def_sizes_iter!(InputsSizes => JackMidiIn); def_sizes_iter!(InputsSizes => JackMidiIn);
def_sizes_iter!(OutputsSizes => JackMidiOut); def_sizes_iter!(OutputsSizes => JackMidiOut);
def_sizes_iter!(PortsSizes => Arc<str>, [PortConnect]); 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>> } #[derive(Debug, Default)] struct ViewMemo<T, U> { value: T, view: Arc<RwLock<U>> }
impl<T: PartialEq, U> ViewMemo<T, U> { impl<T: PartialEq, U> ViewMemo<T, U> {
fn new (value: T, view: U) -> Self { fn new (value: T, view: U) -> Self { Self { value, view: Arc::new(view.into()) } }
Self {
value,
view: Arc::new(view.into())
}
}
fn update <R> (&mut self, newval: T, render: impl Fn(&mut U, &T, &T)->R) -> Option<R> { fn update <R> (&mut self, newval: T, render: impl Fn(&mut U, &T, &T)->R) -> Option<R> {
if newval != self.value { if newval != self.value {
let result = render(&mut*self.view.write().unwrap(), &newval, &self.value); let result = render(&mut*self.view.write().unwrap(), &newval, &self.value);
@ -254,17 +250,37 @@ impl Tek {
} }
/// COMPONENTS //////////////////////////////////////////////////////////////////////////////// /// COMPONENTS ////////////////////////////////////////////////////////////////////////////////
fn row <'a> ( 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 { ) -> impl Content<TuiOut> + 'a {
Fixed::y(h, Bsp::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> ( 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 { ) -> impl Content<TuiOut> + 'a {
Fixed::y(h, Bsp::a( Fixed::y(h, Bsp::a(
Fill::xy(Align::nw(Fixed::xy(self.w_sidebar() as u16, h, a))), Fill::x(Align::n(Fixed::x(w, Align::x(Tui::bg(Green, b))))),
Fill::xy(Align::n(Fixed::xy(w, h, Align::x(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> ( fn per_track <'a, T: Content<TuiOut> + 'a> (
&'a self, f: impl Fn(usize, &'a Track)->T + Send + Sync + 'a &'a self, f: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
) -> impl Content<TuiOut> + '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 w = (self.size.w() as u16).saturating_sub(2 * self.w_sidebar());
let data = (self.selected.track().unwrap_or(0), self.tracks().len()); 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.fmtd.write().unwrap().trks.update(Some(data), rewrite!(buf, "{}/{}", data.0, data.1));
self.row(w, 1, Align::w(Bsp::e( self.row(w, 1, self.button3("t", "track", self.fmtd.read().unwrap().trks.view.clone()),
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"), self.button2("T", "add track")) }
)), self.per_track(|t, track|{ fn view_track_header <'a> (
let active = self.selected().track() == Some(t+1); &self, t: usize, track: &'a Track
let name = &track.name; ) -> impl Content<TuiOut> + use<'a> {
let fg = track.color.lightest.rgb; let active = self.selected().track() == Some(t+1);
let bg = if active { track.color.light.rgb } else { track.color.base.rgb }; let name = &track.name;
let bg2 = Reset;//if t > 0 { self.tracks()[t - 1].color.base.rgb } else { Reset }; let fg = track.color.lightest.rgb;
let bfg = if active { Rgb(255,255,255) } else { Rgb(0,0,0) }; let bg = if active { track.color.light.rgb } else { track.color.base.rgb };
Self::wrap(bg, fg, Tui::bold(true, Fill::x(Align::nw(name)))) 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<'_> { 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 h = self.h_tracks_area();
let editing = self.is_editing(); let editing = self.is_editing();
let selected_track = self.selected().track(); let selected_track = self.selected().track();
let selected_scene = self.selected().scene(); let selected_scene = self.selected().scene();
self.row(w, h, Tui::bg(Black, self.row_top(
Align::y(Map::new( self.w_tracks_area(),
move||self.scenes_sizes(editing, 2, 15).map_while( h,
move|(s, scene, y1, y2)|if y2 as u16 > h { Map::new(
None move||self.scenes_with_colors(editing, h),
} else { Some((s, scene, y1, y2, if s == 0 { move|(s, scene, y1, y2, prev): SceneColor, _|self.view_scene_name(
None w_full, (1 + y2 - y1) as u16, y1 as u16, s, scene, prev)),
} else { self.per_track(move|t, track|Map::new(
Some(self.scenes[s-1].color) move||self.scenes_with_track_colors(editing, h, t),
})) }), move|(s, scene, y1, y2, prev): SceneColor, _|self.view_scene_track(
move|(_, scene, y1, y2, prev), s| { (1 + y2 - y1) as u16, y1 as u16,
let height = (1 + y2 - y1) as u16; scene, prev, s, t, editing, selected_track == Some(t+1), selected_scene))), ())) }
let bg = scene.color; fn scenes_with_colors (&self, editing: bool, h: u16) -> impl ScenesColors<'_> {
let fg = scene.color.lightest.rgb; self.scenes_sizes(editing, 2, 15).map_while(
let name = Some(scene.name.clone()); move|(s, scene, y1, y2)|if y2 as u16 > h {
let cell = self.clip_cell(true, s, &bg, prev, name, "", fg); None
map_south(y1 as u16, height, Fixed::y(height, cell)) })).boxed(), } else { Some((s, scene, y1, y2, if s == 0 {
self.per_track(move|t, track|{ None
let same_track = selected_track == Some(t+1); } else {
Map::new( Some(self.scenes[s-1].color)
move||self.scenes_sizes(editing, 2, 15).map_while( })) }) }
move|(s, scene, y1, y2)|if y2 as u16 > h { fn view_scene_name (
None &self, width: u16, height: u16, offset: u16,
} else { Some((s, scene, y1, y2, if s == 0 { s: usize, scene: &Scene, prev: Option<ItemPalette>,
None ) -> impl Content<TuiOut> + use<'_> {
} else { let bg = scene.color;
Some(self.scenes[s-1].clips[t].as_ref() let fg = scene.color.lightest.rgb;
.map(|c|c.read().unwrap().color) let name = Some(scene.name.clone());
.unwrap_or(ItemPalette::G[32])) let cell = self.clip_cell(true, s, &bg, prev, name, "", fg);
})) }), Fixed::x(width, map_south(offset, height, Fixed::y(height, cell))) }
move|(_, scene, y1, y2, prev), s| { fn scenes_with_track_colors (&self, editing: bool, h: u16, t: usize) -> impl ScenesColors<'_> {
let height = (1 + y2 - y1) as u16; self.scenes_sizes(editing, 2, 15).map_while(
let (name, fg, bg) = if let Some(clip) = &scene.clips[t] { move|(s, scene, y1, y2)|if y2 as u16 > h {
let clip = clip.read().unwrap(); None
(Some(clip.name.clone()), clip.color.lightest.rgb, clip.color) } else { Some((s, scene, y1, y2, if s == 0 {
} else { None
(None, Tui::g(96), ItemPalette::G[32]) } else {
}; Some(self.scenes[s-1].clips[t].as_ref()
let active = editing && same_track && selected_scene == Some(s+1); .map(|c|c.read().unwrap().color)
let edit = |x|Bsp::b(x, When(active, &self.editor)); .unwrap_or(ItemPalette::G[32]))
let cell = self.clip_cell(same_track, s, &bg, prev, name, "", fg); })) }) }
map_south(y1 as u16, height, edit(Fixed::y(height, cell)))}) })) } fn view_scene_track (
const TAB: &str = " Tab"; &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> ( fn clip_cell <'a> (
&self, &self,
same_track: bool, same_track: bool,
scene: usize, scene: usize,
color: &ItemPalette, color: &ItemPalette,
prev: Option<ItemPalette>, prev: Option<ItemPalette>,
name: Option<Arc<str>>, name: Option<Arc<str>>,
icon: &'a str, icon: &'a str,
fg: Color, fg: Color,
) -> impl Content<TuiOut> + use<'a> { ) -> impl Content<TuiOut> + use<'a> {
let selected_scene = self.selected().scene(); let selected_scene = self.selected().scene();
let selected = same_track && selected_scene == Some(scene+1); 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 is_last = scene == self.scenes.len().saturating_sub(1);
let colors = Self::colors(color, prev, selected, neighbor, is_last); 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))))); 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<'_> { pub fn view_scene_add (&self) -> impl Content<TuiOut> + use<'_> {
let data = (self.selected().scene().unwrap_or(0), self.scenes().len()); 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.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()) 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]
}
} }

View file

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

View file

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