mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
down to 5 format macro calls in main view.rs
This commit is contained in:
parent
2ea6a7bd8b
commit
85507bf27e
2 changed files with 123 additions and 106 deletions
|
|
@ -34,7 +34,7 @@ use crate::*;
|
|||
pub keys_scene: SourceIter<'static>,
|
||||
pub keys_mix: SourceIter<'static>,
|
||||
|
||||
pub fmtd: ViewCache,
|
||||
pub(crate) fmtd: Arc<RwLock<ViewCache>>,
|
||||
}
|
||||
has_size!(<TuiOut>|self: Tek|&self.size);
|
||||
has_clock!(|self: Tek|self.clock);
|
||||
|
|
|
|||
227
tek/src/view.rs
227
tek/src/view.rs
|
|
@ -1,41 +1,58 @@
|
|||
use crate::*;
|
||||
use std::fmt::Write;
|
||||
#[derive(Debug)] pub(crate) struct ViewCache {
|
||||
beat: Arc<RwLock<String>>,
|
||||
time: Arc<RwLock<String>>,
|
||||
bpm: Arc<RwLock<String>>,
|
||||
sr: Arc<RwLock<String>>,
|
||||
buf: Arc<RwLock<String>>,
|
||||
lat: Arc<RwLock<String>>,
|
||||
sr: ViewMemo<Option<(bool, f64)>, String>,
|
||||
buf: ViewMemo<Option<f64>, String>,
|
||||
lat: ViewMemo<Option<f64>, String>,
|
||||
bpm: ViewMemo<Option<f64>, String>,
|
||||
beat: ViewMemo<Option<f64>, String>,
|
||||
time: ViewMemo<Option<f64>, String>,
|
||||
scns: ViewMemo<Option<(usize, usize)>, String>,
|
||||
trks: ViewMemo<Option<(usize, usize)>, String>,
|
||||
stop: Arc<str>,
|
||||
}
|
||||
impl Default for ViewCache {
|
||||
fn default () -> Self {
|
||||
Self {
|
||||
beat: Arc::new(RwLock::new(String::with_capacity(16))),
|
||||
time: Arc::new(RwLock::new(String::with_capacity(16))),
|
||||
bpm: Arc::new(RwLock::new(String::with_capacity(16))),
|
||||
sr: Arc::new(RwLock::new(String::with_capacity(16))),
|
||||
buf: Arc::new(RwLock::new(String::with_capacity(16))),
|
||||
lat: Arc::new(RwLock::new(String::with_capacity(16))),
|
||||
beat: ViewMemo::new(None, String::with_capacity(16)),
|
||||
time: ViewMemo::new(None, String::with_capacity(16)),
|
||||
bpm: ViewMemo::new(None, String::with_capacity(16)),
|
||||
sr: ViewMemo::new(None, String::with_capacity(16)),
|
||||
buf: ViewMemo::new(None, String::with_capacity(16)),
|
||||
lat: ViewMemo::new(None, String::with_capacity(16)),
|
||||
scns: ViewMemo::new(None, String::with_capacity(16)),
|
||||
trks: ViewMemo::new(None, String::with_capacity(16)),
|
||||
stop: "⏹".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Default)] struct ViewMemo<T, U> { value: T, view: Arc<RwLock<U>> }
|
||||
impl<T, U> ViewMemo<T, U> {
|
||||
fn new (value: T, view: U) -> Self { Self { value, view: Arc::new(view.into()) } }
|
||||
}
|
||||
impl<T: PartialEq, U> ViewMemo<T, U> {
|
||||
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);
|
||||
self.value = newval;
|
||||
return Some(result);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
view!(TuiOut: |self: Tek| self.size.of(View(self, self.view)); {
|
||||
":editor" => (&self.editor).boxed(),
|
||||
":pool" => self.view_pool().boxed(),
|
||||
":sample" => self.view_sample(self.is_editing()).boxed(),
|
||||
":sampler" => self.view_sampler(self.is_editing(), &self.editor).boxed(),
|
||||
":status" => self.view_editor().boxed(),
|
||||
":toolbar" => self.view_clock().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(),
|
||||
":editor" => (&self.editor).boxed(),
|
||||
":pool" => self.view_pool().boxed(),
|
||||
":sample" => self.view_sample(self.is_editing()).boxed(),
|
||||
":sampler" => self.view_sampler(self.is_editing(), &self.editor).boxed(),
|
||||
":status" => self.view_editor().boxed(),
|
||||
":toolbar" => self.view_clock().boxed(),
|
||||
":tracks" => self.view_tracks().boxed(),
|
||||
":track-add" => self.view_track_add().boxed(),
|
||||
":inputs" => self.view_inputs().boxed(),
|
||||
":outputs" => self.view_outputs().boxed(),
|
||||
":scenes" => self.view_scenes().boxed(),
|
||||
":scene-add" => self.view_scene_add().boxed(),
|
||||
});
|
||||
provide_num!(u16: |self: Tek| {
|
||||
":sidebar-w" => self.w_sidebar(),
|
||||
|
|
@ -44,8 +61,7 @@ provide_num!(u16: |self: Tek| {
|
|||
":samples-y" => if self.is_editing() { 1 } else { 0 },
|
||||
":pool-w" => if self.is_editing() { 5 } else {
|
||||
let w = self.size.w();
|
||||
if w > 60 { 20 } else if w > 40 { 15 } else { 10 }
|
||||
} });
|
||||
if w > 60 { 20 } else if w > 40 { 15 } else { 10 } } });
|
||||
macro_rules! per_track {
|
||||
(|$self:ident,$track:ident,$index:ident|$content:expr) => {{
|
||||
let tracks = ||$self.tracks_sizes($self.is_editing(), $self.editor_w());
|
||||
|
|
@ -53,76 +69,70 @@ macro_rules! per_track {
|
|||
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()
|
||||
}}
|
||||
}
|
||||
map_east(x1 as u16, width, Fixed::x(width, styled)) }))).into() }} }
|
||||
macro_rules! io_header {
|
||||
($self:ident, $key:expr, $label:expr, $count:expr, $content:expr) => {
|
||||
(move||{
|
||||
let button = $self.button($key, format!("{:10} ({})", $label, $count));
|
||||
Bsp::s(Fill::x(Align::w(button)), $content).boxed()
|
||||
}).into()
|
||||
}
|
||||
}
|
||||
Bsp::s(Fill::x(Align::w(button)), $content).boxed() }).into() } }
|
||||
macro_rules! rewrite {
|
||||
($buf:ident, $($rest:tt)*) => { |$buf,_,_|{$buf.clear();write!($buf, $($rest)*)} } }
|
||||
impl Tek {
|
||||
fn view_clock (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
Outer(false, Style::default().fg(Tui::g(0))).enclose(row!(
|
||||
self.view_engine_stats(), " ",
|
||||
self.view_play_pause(), " ",
|
||||
self.view_beat_stats(),
|
||||
))
|
||||
}
|
||||
fn view_beat_stats (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
let compact = self.size.w() > 80;
|
||||
let clock = self.clock();
|
||||
let delta = |start: &Moment|clock.global.usec.get() - start.usec.get();
|
||||
let mut fmtd_beat = self.fmtd.beat.write().unwrap();
|
||||
let mut fmtd_time = self.fmtd.time.write().unwrap();
|
||||
let mut fmtd_bpm = self.fmtd.bpm.write().unwrap();
|
||||
fmtd_beat.clear();
|
||||
fmtd_time.clear();
|
||||
fmtd_bpm.clear();
|
||||
if let Some(now) = clock.started.read().unwrap().as_ref().map(delta) {
|
||||
clock.timebase.format_beats_1_to(&mut*fmtd_beat, clock.timebase.usecs_to_pulse(now));
|
||||
write!(&mut fmtd_time, "{:.3}s", now/1000000.);
|
||||
write!(&mut fmtd_bpm, "{:.3}", clock.timebase.bpm.get());
|
||||
} else {
|
||||
write!(&mut fmtd_beat, "-.-.--");
|
||||
write!(&mut fmtd_time, "-.---s");
|
||||
write!(&mut fmtd_bpm, "---.---");
|
||||
}
|
||||
let theme = ItemPalette::G[128];
|
||||
Thunk::new(move||Either::new(compact,
|
||||
row!(FieldH(theme, "BPM", self.fmtd.bpm.clone()),
|
||||
FieldH(theme, "Beat", self.fmtd.beat.clone()),
|
||||
FieldH(theme, "Time", self.fmtd.time.clone())),
|
||||
row!(FieldV(theme, "BPM", self.fmtd.bpm.clone()),
|
||||
FieldV(theme, "Beat", self.fmtd.beat.clone()),
|
||||
FieldV(theme, "Time", self.fmtd.time.clone()))))
|
||||
}
|
||||
fn view_engine_stats (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
fn update_clock (&self) {
|
||||
let compact = self.size.w() > 80;
|
||||
let clock = self.clock();
|
||||
let rate = clock.timebase.sr.get();
|
||||
let chunk = clock.chunk.load(Relaxed);
|
||||
let mut fmtd_sr = self.fmtd.sr.write().unwrap();
|
||||
let mut fmtd_buf = self.fmtd.buf.write().unwrap();
|
||||
let mut fmtd_lat = self.fmtd.lat.write().unwrap();
|
||||
fmtd_sr.clear();
|
||||
write!(&mut fmtd_sr, "{}", if compact {format!("{:.1}kHz", rate / 1000.)} else {format!("{:.0}Hz", rate)});
|
||||
fmtd_buf.clear();
|
||||
write!(&mut fmtd_buf, "{chunk}");
|
||||
fmtd_lat.clear();
|
||||
write!(&mut fmtd_lat, "{:.1}ms", chunk as f64 / rate * 1000.);
|
||||
let theme = ItemPalette::G[128];
|
||||
Either::new(compact,
|
||||
row!(FieldH(theme, "SR", self.fmtd.sr.clone()),
|
||||
FieldH(theme, "Buf", self.fmtd.buf.clone()),
|
||||
FieldH(theme, "Lat", self.fmtd.lat.clone())),
|
||||
row!(FieldV(theme, "SR", self.fmtd.sr.clone()),
|
||||
FieldV(theme, "Buf", self.fmtd.buf.clone()),
|
||||
FieldV(theme, "Lat", self.fmtd.lat.clone())))
|
||||
let chunk = clock.chunk.load(Relaxed) as f64;
|
||||
let lat = chunk / rate * 1000.;
|
||||
let delta = |start: &Moment|clock.global.usec.get() - start.usec.get();
|
||||
let mut fmtd = self.fmtd.write().unwrap();
|
||||
fmtd.buf.update(Some(chunk), rewrite!(buf, "{chunk}"));
|
||||
fmtd.lat.update(Some(lat), rewrite!(buf, "{lat:.1}ms"));
|
||||
fmtd.sr.update(Some((compact, rate)), |buf,_,_|if compact {
|
||||
buf.clear(); write!(buf, "{:.1}kHz", rate / 1000.)
|
||||
} else {
|
||||
buf.clear(); write!(buf, "{:.0}Hz", rate)
|
||||
});
|
||||
if let Some(now) = clock.started.read().unwrap().as_ref().map(delta) {
|
||||
let pulse = clock.timebase.usecs_to_pulse(now);
|
||||
let time = now/1000000.;
|
||||
let bpm = clock.timebase.bpm.get();
|
||||
fmtd.beat.update(Some(pulse), |buf, _, _|clock.timebase.format_beats_1_to(buf, pulse));
|
||||
fmtd.time.update(Some(time), rewrite!(buf, "{:.3}s", time));
|
||||
fmtd.bpm.update(Some(bpm), rewrite!(buf, "{:.3}", bpm));
|
||||
} else {
|
||||
fmtd.beat.update(None, rewrite!(buf, "-.-.--"));
|
||||
fmtd.time.update(None, rewrite!(buf, "-.---s"));
|
||||
fmtd.bpm.update(None, rewrite!(buf, "---.---"));
|
||||
}
|
||||
}
|
||||
fn view_clock (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
self.update_clock();
|
||||
let compact = self.size.w() > 80;
|
||||
let theme = ItemPalette::G[96];
|
||||
Outer(false, Style::default().fg(Tui::g(0))).enclose(row!(
|
||||
Thunk::new(move||{
|
||||
let fmtd = self.fmtd.read().unwrap();
|
||||
Either::new(compact,
|
||||
row!(FieldH(theme, "SR", fmtd.sr.view.clone()),
|
||||
FieldH(theme, "Buf", fmtd.buf.view.clone()),
|
||||
FieldH(theme, "Lat", fmtd.lat.view.clone())),
|
||||
row!(FieldV(theme, "SR", fmtd.sr.view.clone()),
|
||||
FieldV(theme, "Buf", fmtd.buf.view.clone()),
|
||||
FieldV(theme, "Lat", fmtd.lat.view.clone()))) }),
|
||||
" ",
|
||||
self.view_play_pause(),
|
||||
" ",
|
||||
Thunk::new(move||{
|
||||
let fmtd = self.fmtd.read().unwrap();
|
||||
Either::new(compact,
|
||||
row!(FieldH(theme, "BPM", fmtd.bpm.view.clone()),
|
||||
FieldH(theme, "Beat", fmtd.beat.view.clone()),
|
||||
FieldH(theme, "Time", fmtd.time.view.clone())),
|
||||
row!(FieldV(theme, "BPM", fmtd.bpm.view.clone()),
|
||||
FieldV(theme, "Beat", fmtd.beat.view.clone()),
|
||||
FieldV(theme, "Time", fmtd.time.view.clone()))) }),
|
||||
))
|
||||
}
|
||||
fn view_meter <'a> (&'a self, label: &'a str, value: f32) -> impl Content<TuiOut> + 'a {
|
||||
col!(
|
||||
|
|
@ -169,11 +179,6 @@ impl Tek {
|
|||
fn view_pool (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
self.pool.as_ref().map(|pool|PoolView(self.is_editing(), 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_row <'a> (
|
||||
&'a self, w: u16, h: u16, a: impl Content<TuiOut> + 'a, b: impl Content<TuiOut> + 'a
|
||||
) -> impl Content<TuiOut> + 'a {
|
||||
|
|
@ -185,7 +190,7 @@ impl Tek {
|
|||
fn view_inputs (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
let fg = Tui::g(224);
|
||||
let bg = Tui::g(64);
|
||||
let h = 1 + self.midi_ins.len() as u16;
|
||||
let h = 1 + self.midi_ins.len() as u16;
|
||||
let header: ThunkBox<_> = io_header!(self, " I ", " midi ins", self.midi_ins.len(), self.midi_ins().get(0).map(
|
||||
move|input: &JackPort<MidiIn>|Bsp::s(
|
||||
Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(input.name.clone())))),
|
||||
|
|
@ -228,19 +233,16 @@ impl Tek {
|
|||
}
|
||||
fn view_tracks (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
let h = 1;
|
||||
let add_track = ||self.button(" C-t ", format!(" add track ({}/{})",
|
||||
self.selected.track().unwrap_or(0),
|
||||
self.tracks().len()));
|
||||
let header: ThunkBox<_> =
|
||||
(move||Tui::bg(Tui::g(32), Fill::x(Align::w(add_track()))).boxed()).into();
|
||||
(move||Tui::bg(Tui::g(32), Fill::x(Align::w(self.view_track_add()))).boxed()).into();
|
||||
let cells: ThunkBox<_> = per_track!(|self, track, t|{
|
||||
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 = 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) };
|
||||
let bs = Style::default().fg(bfg).bg(bg);
|
||||
let fg = track.color.lightest.rgb;
|
||||
let bg = if active { track.color.light.rgb } else { track.color.base.rgb };
|
||||
let bg2 = 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) };
|
||||
let bs = Style::default().fg(bfg).bg(bg);
|
||||
let cell = Bsp::e(
|
||||
Tui::fg_bg(bg, bg2, "▐"),
|
||||
Tui::fg_bg(fg, bg, Tui::bold(true, Fill::x(Align::nw(name)))));
|
||||
|
|
@ -248,6 +250,20 @@ impl Tek {
|
|||
});
|
||||
self.view_row(self.w(), 1, header, cells)
|
||||
}
|
||||
fn view_track_add (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
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));
|
||||
button(" C-t ", Bsp::e(" add track ",
|
||||
self.fmtd.read().unwrap().trks.view.clone()))
|
||||
}
|
||||
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));
|
||||
button(" C-a ", Bsp::e(" add scene ",
|
||||
self.fmtd.read().unwrap().scns.view.clone()))
|
||||
}
|
||||
fn view_scenes (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
let header: ThunkBox<_> = (move||{
|
||||
let last_color = Arc::new(RwLock::new(ItemPalette::G[0]));
|
||||
|
|
@ -352,7 +368,8 @@ impl Tek {
|
|||
let w = self.w();
|
||||
let d = 6 + self.midi_ins.len() + self.midi_outs.len();
|
||||
let h = self.size.h().saturating_sub(d) as u16;
|
||||
self.view_row(w, h, header, cells)}) }
|
||||
self.view_row(w, h, header, cells)})
|
||||
}
|
||||
fn w (&self) -> u16 {
|
||||
self.tracks_sizes(self.is_editing(), self.editor_w())
|
||||
.last()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue