mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-08 04:36:45 +01:00
wip: more flattening and view_arranger
This commit is contained in:
parent
e0f4ec9a15
commit
53f443f4bd
6 changed files with 94 additions and 81 deletions
|
|
@ -1,5 +1,10 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
pub(crate) use std::fmt::Write;
|
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 {
|
macro_rules! def_sizes_iter {
|
||||||
($Type:ident => $($Item:ty),+) => {
|
($Type:ident => $($Item:ty),+) => {
|
||||||
pub(crate) trait $Type<'a> =
|
pub(crate) trait $Type<'a> =
|
||||||
|
|
@ -9,13 +14,13 @@ 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) trait ScenesColors<'a> = Iterator<Item=SceneWithColor<'a>>;
|
||||||
pub(crate) type SceneColor<'a> = (usize, &'a Scene, usize, usize, Option<ItemPalette>);
|
pub(crate) type SceneWithColor<'a> = (usize, &'a Scene, usize, usize, Option<ItemPalette>);
|
||||||
view!(TuiOut: |self: Tek| self.size.of(View(self, self.view)); {
|
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(),
|
":inputs" => self.view_inputs().boxed(),
|
||||||
":outputs" => self.view_outputs().boxed(),
|
":outputs" => self.view_outputs().boxed(),
|
||||||
":pool" => self.view_pool().boxed(),
|
|
||||||
":sample" => ().boxed(),//self.view_sample(self.is_editing()).boxed(),
|
":sample" => ().boxed(),//self.view_sample(self.is_editing()).boxed(),
|
||||||
":sampler" => ().boxed(),//self.view_sampler(self.is_editing(), &self.editor).boxed(),
|
":sampler" => ().boxed(),//self.view_sampler(self.is_editing(), &self.editor).boxed(),
|
||||||
":scene-add" => self.view_scene_add().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(),
|
":transport" => self.view_transport().boxed(),
|
||||||
":status" => self.view_status().boxed(),
|
":status" => self.view_status().boxed(),
|
||||||
":tracks" => self.view_tracks().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| {
|
provide_num!(u16: |self: Tek| {
|
||||||
":h-ins" => self.h_inputs(),
|
":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-outs" => (self.size.h() as u16).saturating_sub(self.h_outputs() + 1),
|
||||||
":y-samples" => if self.is_editing() { 1 } else { 0 },
|
":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> (
|
pub(crate) fn row <'a> (
|
||||||
w: u16,
|
w: u16,
|
||||||
h: u16,
|
h: u16,
|
||||||
|
|
@ -65,7 +58,6 @@ pub(crate) fn row <'a> (
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn row_top <'a> (
|
pub(crate) fn row_top <'a> (
|
||||||
w: u16,
|
w: u16,
|
||||||
h: u16,
|
h: u16,
|
||||||
|
|
@ -82,7 +74,6 @@ pub(crate) fn row_top <'a> (
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn wrap (
|
pub(crate) fn wrap (
|
||||||
bg: Color,
|
bg: Color,
|
||||||
fg: Color,
|
fg: Color,
|
||||||
|
|
@ -92,7 +83,6 @@ pub(crate) fn wrap (
|
||||||
Bsp::w(Tui::fg_bg(bg, Reset, "▌"),
|
Bsp::w(Tui::fg_bg(bg, Reset, "▌"),
|
||||||
Tui::fg_bg(fg, bg, content)))
|
Tui::fg_bg(fg, bg, content)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn button_2 <'a, K, L> (
|
pub(crate) fn button_2 <'a, K, L> (
|
||||||
key: K,
|
key: K,
|
||||||
label: L,
|
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));
|
let label = When::new(!editing, Tui::fg_bg(Tui::g(255), Tui::g(96), label));
|
||||||
Tui::bold(true, Bsp::e(key, label))
|
Tui::bold(true, Bsp::e(key, label))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn button_3 <'a, K, L, V> (
|
pub(crate) fn button_3 <'a, K, L, V> (
|
||||||
key: K,
|
key: K,
|
||||||
label: L,
|
label: L,
|
||||||
|
|
@ -136,7 +125,6 @@ pub(crate) fn button_3 <'a, K, L, V> (
|
||||||
));
|
));
|
||||||
Tui::bold(true, Bsp::e(key, label))
|
Tui::bold(true, Bsp::e(key, label))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn heading <'a> (
|
fn heading <'a> (
|
||||||
key: &'a str,
|
key: &'a str,
|
||||||
label: &'a str,
|
label: &'a str,
|
||||||
|
|
@ -147,7 +135,6 @@ fn heading <'a> (
|
||||||
let count = format!("{count}");
|
let count = format!("{count}");
|
||||||
Fill::xy(Align::w(Bsp::s(Fill::x(Align::w(button_3(key, label, count, editing))), content)))
|
Fill::xy(Align::w(Bsp::s(Fill::x(Align::w(button_3(key, label, count, editing))), content)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)] mod test {
|
#[cfg(test)] mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
#[test] fn test_view () {
|
#[test] fn test_view () {
|
||||||
|
|
|
||||||
|
|
@ -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))))
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,11 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
impl Tek {
|
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.
|
/// Draw the full arranger to the arranger view buffer.
|
||||||
///
|
///
|
||||||
/// This should happen on changes to the arrangement view
|
/// This should happen on changes to the arrangement contents,
|
||||||
/// other than scrolling. Scrolling should just determine
|
/// i.e. not on scroll. Scrolling just determines which
|
||||||
/// which part of the arranger buffer to blit to output.
|
/// part of the arranger buffer to blit in [view_arranger]
|
||||||
pub fn redraw_arranger (&self) {
|
pub fn redraw_arranger (&self) {
|
||||||
let width = self.w_tracks();
|
let width = self.w_tracks();
|
||||||
let height = self.h_scenes() + self.h_inputs() + self.h_outputs();
|
let height = self.h_scenes() + self.h_inputs() + self.h_outputs();
|
||||||
|
|
@ -24,6 +16,34 @@ impl Tek {
|
||||||
*self.arranger.write().unwrap() = output.buffer;
|
*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.
|
/// Display the current scene scroll state.
|
||||||
fn scene_scrollbar (&self) -> impl Content<TuiOut> + use<'_> {
|
fn scene_scrollbar (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
let offset = self.scene_scroll;
|
let offset = self.scene_scroll;
|
||||||
|
|
@ -89,11 +109,11 @@ impl Tek {
|
||||||
Fixed::y(self.h_tracks_area(), row(self.w_tracks_area(), h, s,
|
Fixed::y(self.h_tracks_area(), row(self.w_tracks_area(), h, s,
|
||||||
Map::new(
|
Map::new(
|
||||||
move||self.scenes_with_colors(editing, h_area),
|
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)),
|
w_full, (1 + y2 - y1) as u16, y1 as u16, s, scene, prev)),
|
||||||
self.per_track(move|t, track|Map::new(
|
self.per_track(move|t, track|Map::new(
|
||||||
move||self.scenes_with_track_colors(editing, h_area, t),
|
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,
|
w, (1 + y2 - y1) as u16, y1 as u16,
|
||||||
scene, prev, s, t, editing, selected_track == Some(t), selected_scene))),
|
scene, prev, s, t, editing, selected_track == Some(t), selected_scene))),
|
||||||
() )))))
|
() )))))
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ impl Tek {
|
||||||
let theme = ItemPalette::G[96];
|
let theme = ItemPalette::G[96];
|
||||||
let fmtd = self.fmtd.read().unwrap();
|
let fmtd = self.fmtd.read().unwrap();
|
||||||
Tui::bg(Black, row!(Bsp::a(
|
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!(
|
Fill::xy(Align::e(row!(
|
||||||
FieldH(theme, "BPM", fmtd.bpm.view.clone()),
|
FieldH(theme, "BPM", fmtd.bpm.view.clone()),
|
||||||
FieldH(theme, "Beat", fmtd.beat.view.clone()),
|
FieldH(theme, "Beat", fmtd.beat.view.clone()),
|
||||||
|
|
@ -47,7 +47,9 @@ impl Tek {
|
||||||
let theme = ItemPalette::G[96];
|
let theme = ItemPalette::G[96];
|
||||||
let fmtd = self.fmtd.read().unwrap();
|
let fmtd = self.fmtd.read().unwrap();
|
||||||
Tui::bg(Black, row!(Bsp::a(
|
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!(
|
Fill::xy(Align::e(row!(
|
||||||
FieldH(theme, "SR", fmtd.sr.view.clone()),
|
FieldH(theme, "SR", fmtd.sr.view.clone()),
|
||||||
FieldH(theme, "Buf", fmtd.buf.view.clone()),
|
FieldH(theme, "Buf", fmtd.buf.view.clone()),
|
||||||
|
|
@ -55,8 +57,9 @@ impl Tek {
|
||||||
)))
|
)))
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
fn view_play_pause (&self) -> impl Content<TuiOut> + use<'_> {
|
}
|
||||||
let playing = self.clock.is_rolling();
|
|
||||||
|
fn button_play_pause (playing: bool) -> impl Content<TuiOut> {
|
||||||
let compact = true;//self.is_editing();
|
let compact = true;//self.is_editing();
|
||||||
Tui::bg(
|
Tui::bg(
|
||||||
if playing{Rgb(0,128,0)}else{Rgb(128,64,0)},
|
if playing{Rgb(0,128,0)}else{Rgb(128,64,0)},
|
||||||
|
|
@ -67,5 +70,4 @@ impl Tek {
|
||||||
Thunk::new(move||Fixed::x(5, Either::new(playing,
|
Thunk::new(move||Fixed::x(5, Either::new(playing,
|
||||||
Tui::fg(Rgb(0, 255, 0), Bsp::s(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)),
|
Tui::fg(Rgb(0, 255, 0), Bsp::s(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)),
|
||||||
Tui::fg(Rgb(255, 128, 0), Bsp::s(" ▗▄▖ ", " ▝▀▘ ",)))))))
|
Tui::fg(Rgb(255, 128, 0), Bsp::s(" ▗▄▖ ", " ▝▀▘ ",)))))))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@ use crate::*;
|
||||||
pub(crate) view: Arc<RwLock<U>>
|
pub(crate) 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 { 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> {
|
pub(crate) 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);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
impl Tek {
|
impl Tek {
|
||||||
fn view_meter <'a> (&'a self, label: &'a str, value: f32) -> impl Content<TuiOut> + 'a {
|
}
|
||||||
|
fn view_meter <'a> (label: &'a str, value: f32) -> impl Content<TuiOut> + 'a {
|
||||||
col!(
|
col!(
|
||||||
FieldH(ItemPalette::G[128], label, format!("{:>+9.3}", value)),
|
FieldH(ItemPalette::G[128], label, format!("{:>+9.3}", value)),
|
||||||
Fixed::xy(if value >= 0.0 { 13 }
|
Fixed::xy(if value >= 0.0 { 13 }
|
||||||
|
|
@ -19,11 +20,10 @@ impl Tek {
|
||||||
else { 0 }, 1, Tui::bg(if value >= 0.0 { Red }
|
else { 0 }, 1, Tui::bg(if value >= 0.0 { Red }
|
||||||
else if value >= -3.0 { Yellow }
|
else if value >= -3.0 { Yellow }
|
||||||
else { Green }, ())))
|
else { Green }, ())))
|
||||||
}
|
}
|
||||||
fn view_meters (&self, values: &[f32;2]) -> impl Content<TuiOut> + use<'_> {
|
fn view_meters (values: &[f32;2]) -> impl Content<TuiOut> + use<'_> {
|
||||||
Bsp::s(
|
Bsp::s(
|
||||||
format!("L/{:>+9.3}", values[0]),
|
format!("L/{:>+9.3}", values[0]),
|
||||||
format!("R/{:>+9.3}", values[1]),
|
format!("R/{:>+9.3}", values[1]),
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue