wip: more flattening and view_arranger

This commit is contained in:
🪞👃🪞 2025-02-09 16:49:50 +01:00
parent e0f4ec9a15
commit 53f443f4bd
6 changed files with 94 additions and 81 deletions

View file

@ -1,5 +1,10 @@
use crate::*;
pub(crate) use std::fmt::Write;
pub(crate) use ::tek_tui::ratatui::prelude::Position;
/// Clear a pre-allocated buffer, then write into it.
#[macro_export] macro_rules! rewrite {
($buf:ident, $($rest:tt)*) => { |$buf,_,_|{$buf.clear();write!($buf, $($rest)*)} } }
/// Define a type alias for iterators of sized items (columns).
macro_rules! def_sizes_iter {
($Type:ident => $($Item:ty),+) => {
pub(crate) trait $Type<'a> =
@ -9,13 +14,13 @@ 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>);
pub(crate) trait ScenesColors<'a> = Iterator<Item=SceneWithColor<'a>>;
pub(crate) type SceneWithColor<'a> = (usize, &'a Scene, usize, usize, Option<ItemPalette>);
view!(TuiOut: |self: Tek| self.size.of(View(self, self.view)); {
":editor" => (&self.editor).boxed(),
":arranger" => self.view_arranger().boxed(),
":editor" => self.editor.as_ref().map(|e|Bsp::e(e.clip_status(), e.edit_status())).boxed(),
":inputs" => self.view_inputs().boxed(),
":outputs" => self.view_outputs().boxed(),
":pool" => self.view_pool().boxed(),
":sample" => ().boxed(),//self.view_sample(self.is_editing()).boxed(),
":sampler" => ().boxed(),//self.view_sampler(self.is_editing(), &self.editor).boxed(),
":scene-add" => self.view_scene_add().boxed(),
@ -23,6 +28,9 @@ view!(TuiOut: |self: Tek| self.size.of(View(self, self.view)); {
":transport" => self.view_transport().boxed(),
":status" => self.view_status().boxed(),
":tracks" => self.view_tracks().boxed(),
":pool" => self.pool.as_ref()
.map(|pool|Fixed::x(self.w_sidebar(), PoolView(self.is_editing(), pool)))
.boxed(),
});
provide_num!(u16: |self: Tek| {
":h-ins" => self.h_inputs(),
@ -34,21 +42,6 @@ provide_num!(u16: |self: Tek| {
":y-outs" => (self.size.h() as u16).saturating_sub(self.h_outputs() + 1),
":y-samples" => if self.is_editing() { 1 } else { 0 },
});
#[macro_export] macro_rules! rewrite {
($buf:ident, $($rest:tt)*) => { |$buf,_,_|{$buf.clear();write!($buf, $($rest)*)} }
}
impl Tek {
fn view_editor (&self) -> impl Content<TuiOut> + use<'_> {
self.editor.as_ref()
.map(|e|Bsp::e(e.clip_status(), e.edit_status()))
}
fn view_pool (&self) -> impl Content<TuiOut> + use<'_> {
self.pool.as_ref()
.map(|pool|Fixed::x(self.w_sidebar(), PoolView(self.is_editing(), pool)))
}
}
pub(crate) fn row <'a> (
w: u16,
h: u16,
@ -65,7 +58,6 @@ pub(crate) fn row <'a> (
),
))
}
pub(crate) fn row_top <'a> (
w: u16,
h: u16,
@ -82,7 +74,6 @@ pub(crate) fn row_top <'a> (
),
))
}
pub(crate) fn wrap (
bg: Color,
fg: Color,
@ -92,7 +83,6 @@ pub(crate) fn wrap (
Bsp::w(Tui::fg_bg(bg, Reset, ""),
Tui::fg_bg(fg, bg, content)))
}
pub(crate) fn button_2 <'a, K, L> (
key: K,
label: L,
@ -108,7 +98,6 @@ pub(crate) fn button_2 <'a, K, L> (
let label = When::new(!editing, Tui::fg_bg(Tui::g(255), Tui::g(96), label));
Tui::bold(true, Bsp::e(key, label))
}
pub(crate) fn button_3 <'a, K, L, V> (
key: K,
label: L,
@ -136,7 +125,6 @@ pub(crate) fn button_3 <'a, K, L, V> (
));
Tui::bold(true, Bsp::e(key, label))
}
fn heading <'a> (
key: &'a str,
label: &'a str,
@ -147,7 +135,6 @@ fn heading <'a> (
let count = format!("{count}");
Fill::xy(Align::w(Bsp::s(Fill::x(Align::w(button_3(key, label, count, editing))), content)))
}
#[cfg(test)] mod test {
use super::*;
#[test] fn test_view () {

View file

@ -1 +1,3 @@
(bsp/n (fixed/y 1 :transport) (bsp/s (fixed/y 1 :status) (fill/xy (bsp/a (fill/xy (align/e :pool)) :arranger))))
(bsp/n (fixed/y 1 :transport)
(bsp/s (fixed/y 1 :status)
(fill/xy (bsp/a (fill/xy (align/e :pool)) :arranger))))

View file

@ -1,19 +1,11 @@
use crate::*;
impl Tek {
/// Blit the currently visible section of the arranger to the output.
///
/// If the arranger is larger than the available display area,
/// the scrollbars determine the portion that will be shown.
pub fn view_arranger (&self) -> impl Content<TuiOut> + use<'_> {
() // TODO
}
/// Draw the full arranger to the arranger view buffer.
///
/// This should happen on changes to the arrangement view
/// other than scrolling. Scrolling should just determine
/// which part of the arranger buffer to blit to output.
/// This should happen on changes to the arrangement contents,
/// i.e. not on scroll. Scrolling just determines which
/// part of the arranger buffer to blit in [view_arranger]
pub fn redraw_arranger (&self) {
let width = self.w_tracks();
let height = self.h_scenes() + self.h_inputs() + self.h_outputs();
@ -24,6 +16,34 @@ impl Tek {
*self.arranger.write().unwrap() = output.buffer;
}
/// Blit the currently visible section of the arranger view buffer to the output.
///
/// If the arranger is larger than the available display area,
/// the scrollbars determine the portion that will be shown.
///
/// This function is called on every frame, but is relatively cheap
/// as the rendering logic of [redraw_arranger] is only invoked on
/// changes to the content.
pub fn view_arranger (&self) -> impl Content<TuiOut> + use<'_> {
ThunkRender::new(move|to: &mut TuiOut|{
let [x0, y0, w, h] = to.area().xywh();
let source = self.arranger.read().unwrap();
for (source_x, target_x) in (x0..x0+w).enumerate() {
for (source_y, target_y) in (y0..y0+h).enumerate() {
let source_pos = Position::from((source_x as u16, source_y as u16));
let target_pos = Position::from((target_x, target_y));
if let Some(target) = to.buffer.cell_mut(target_pos) {
if let Some(source) = source.cell(source_pos) {
*target = source.clone();
}
}
}
}
// TODO
()
})
}
/// Display the current scene scroll state.
fn scene_scrollbar (&self) -> impl Content<TuiOut> + use<'_> {
let offset = self.scene_scroll;
@ -89,11 +109,11 @@ impl Tek {
Fixed::y(self.h_tracks_area(), row(self.w_tracks_area(), h, s,
Map::new(
move||self.scenes_with_colors(editing, h_area),
move|(s, scene, y1, y2, prev): SceneColor, _|self.view_scene_name(
move|(s, scene, y1, y2, prev): SceneWithColor, _|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_area, t),
move|(s, scene, y1, y2, prev): SceneColor, _|self.view_scene_clip(
move|(s, scene, y1, y2, prev): SceneWithColor, _|self.view_scene_clip(
w, (1 + y2 - y1) as u16, y1 as u16,
scene, prev, s, t, editing, selected_track == Some(t), selected_scene))),
() )))))

View file

@ -34,7 +34,7 @@ impl Tek {
let theme = ItemPalette::G[96];
let fmtd = self.fmtd.read().unwrap();
Tui::bg(Black, row!(Bsp::a(
Fill::xy(Align::w(self.view_play_pause())),
Fill::xy(Align::w(button_play_pause(self.clock.is_rolling()))),
Fill::xy(Align::e(row!(
FieldH(theme, "BPM", fmtd.bpm.view.clone()),
FieldH(theme, "Beat", fmtd.beat.view.clone()),
@ -47,7 +47,9 @@ impl Tek {
let theme = ItemPalette::G[96];
let fmtd = self.fmtd.read().unwrap();
Tui::bg(Black, row!(Bsp::a(
Fill::xy(Align::w(FieldH(theme, "Selected", self.selected.describe(&self.tracks, &self.scenes)))),
Fill::xy(Align::w(
FieldH(theme, "Selected", self.selected.describe(&self.tracks, &self.scenes))
)),
Fill::xy(Align::e(row!(
FieldH(theme, "SR", fmtd.sr.view.clone()),
FieldH(theme, "Buf", fmtd.buf.view.clone()),
@ -55,17 +57,17 @@ impl Tek {
)))
)))
}
fn view_play_pause (&self) -> impl Content<TuiOut> + use<'_> {
let playing = self.clock.is_rolling();
let compact = true;//self.is_editing();
Tui::bg(
if playing{Rgb(0,128,0)}else{Rgb(128,64,0)},
Either::new(compact,
Thunk::new(move||Fixed::x(9, Either::new(playing,
Tui::fg(Rgb(0, 255, 0), " PLAYING "),
Tui::fg(Rgb(255, 128, 0), " STOPPED ")))),
Thunk::new(move||Fixed::x(5, Either::new(playing,
Tui::fg(Rgb(0, 255, 0), Bsp::s(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)),
Tui::fg(Rgb(255, 128, 0), Bsp::s(" ▗▄▖ ", " ▝▀▘ ",)))))))
}
}
fn button_play_pause (playing: bool) -> impl Content<TuiOut> {
let compact = true;//self.is_editing();
Tui::bg(
if playing{Rgb(0,128,0)}else{Rgb(128,64,0)},
Either::new(compact,
Thunk::new(move||Fixed::x(9, Either::new(playing,
Tui::fg(Rgb(0, 255, 0), " PLAYING "),
Tui::fg(Rgb(255, 128, 0), " STOPPED ")))),
Thunk::new(move||Fixed::x(5, Either::new(playing,
Tui::fg(Rgb(0, 255, 0), Bsp::s(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)),
Tui::fg(Rgb(255, 128, 0), Bsp::s(" ▗▄▖ ", " ▝▀▘ ",)))))))
}

View file

@ -4,7 +4,9 @@ use crate::*;
pub(crate) 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()) }
}
pub(crate) 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);

View file

@ -1,29 +1,29 @@
use crate::*;
impl Tek {
fn view_meter <'a> (&'a self, label: &'a str, value: f32) -> impl Content<TuiOut> + 'a {
col!(
FieldH(ItemPalette::G[128], label, format!("{:>+9.3}", value)),
Fixed::xy(if value >= 0.0 { 13 }
else if value >= -1.0 { 12 }
else if value >= -2.0 { 11 }
else if value >= -3.0 { 10 }
else if value >= -4.0 { 9 }
else if value >= -6.0 { 8 }
else if value >= -9.0 { 7 }
else if value >= -12.0 { 6 }
else if value >= -15.0 { 5 }
else if value >= -20.0 { 4 }
else if value >= -25.0 { 3 }
else if value >= -30.0 { 2 }
else if value >= -40.0 { 1 }
else { 0 }, 1, Tui::bg(if value >= 0.0 { Red }
else if value >= -3.0 { Yellow }
else { Green }, ())))
}
fn view_meters (&self, values: &[f32;2]) -> impl Content<TuiOut> + use<'_> {
Bsp::s(
format!("L/{:>+9.3}", values[0]),
format!("R/{:>+9.3}", values[1]),
)
}
}
fn view_meter <'a> (label: &'a str, value: f32) -> impl Content<TuiOut> + 'a {
col!(
FieldH(ItemPalette::G[128], label, format!("{:>+9.3}", value)),
Fixed::xy(if value >= 0.0 { 13 }
else if value >= -1.0 { 12 }
else if value >= -2.0 { 11 }
else if value >= -3.0 { 10 }
else if value >= -4.0 { 9 }
else if value >= -6.0 { 8 }
else if value >= -9.0 { 7 }
else if value >= -12.0 { 6 }
else if value >= -15.0 { 5 }
else if value >= -20.0 { 4 }
else if value >= -25.0 { 3 }
else if value >= -30.0 { 2 }
else if value >= -40.0 { 1 }
else { 0 }, 1, Tui::bg(if value >= 0.0 { Red }
else if value >= -3.0 { Yellow }
else { Green }, ())))
}
fn view_meters (values: &[f32;2]) -> impl Content<TuiOut> + use<'_> {
Bsp::s(
format!("L/{:>+9.3}", values[0]),
format!("R/{:>+9.3}", values[1]),
)
}