From 62ce1776c009fceee0949af9d0c1a94ff92827b7 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Tue, 31 Dec 2024 19:21:48 +0100 Subject: [PATCH] trying to get new Bsp to work; update docs --- engine/README.md | 65 +++++++++++++++++++++++++ engine/src/engine.rs | 6 ++- engine/src/output.rs | 34 ++++++++++--- engine/src/tui.rs | 14 +++++- layout/README.md | 4 ++ layout/src/direction.rs | 105 +++++++++++++++++++++++----------------- layout/src/measure.rs | 41 ++++++++++++++++ layout/src/transform.rs | 89 +++++++++++++++++++++++----------- layout/src/tui.rs | 42 ---------------- src/groovebox.rs | 20 ++++---- src/transport.rs | 38 ++++++--------- 11 files changed, 301 insertions(+), 157 deletions(-) create mode 100644 engine/README.md create mode 100644 layout/README.md delete mode 100644 layout/src/tui.rs diff --git a/engine/README.md b/engine/README.md new file mode 100644 index 00000000..0ac4a79c --- /dev/null +++ b/engine/README.md @@ -0,0 +1,65 @@ +# `tek_engine` + +this crate provides the `Engine` trait, +which defines an application's lifecycle. + +currently, there is one kind of engine implemented, `Tui`. +it uses `ratatui` to present an interactive user interface +in text mode. + +at launch, the `Tui` engine spawns two threads, +a **render thread** and an **input thread**. (the +application may spawn further threads, such as a +**jack thread**.) + +all threads communicate using shared ownership, +`Arc` and `Arc`. the engine and +application instances are expected to be wrapped +in `Arc`; internally, those synchronization +mechanisms may be used liberally. + +## rendering + +the **render thread** continually invokes the +`Content::render` method of the application +to redraw the display. it does this efficiently +by using ratatui's double buffering. + +thus, for a type to be a valid application for engine `E`, +it must implement the trait `Content`, which allows +it to display content to the engine's output. + +the most important thing about the `Content` trait is that +it composes: +* you can implement `Content::content` to build + `Content`s out of other `Content`s +* and/or `Content::area` for custom positioning and sizing, +* and/or `Content::render` for custom rendering + within the given `Content`'s area. + +the manner of output is determined by the +`Engine::Output` type, a mutable pointer to which +is passed to the render method, e.g. in the case of +the `Tui` engine: `fn render(&self, output: &mut TuiOutput)` + +you can use `TuiOutput::blit` and `TuiOutput::place` +to draw at specified coordinates of the display, and/or +directly modify the underlying `ratatui::Buffer` at +`output.buffer` + +rendering is intended to work with read-only access +to the application state. if you really need to update +values during rendering, use interior mutability. + +## input handling + +the **input thread** polls for keyboard events +and passes them onto the application's `Handle::handle` method. + +thus, for a type to be a valid application for engine `E`, +it must implement the trait `Handle`, which allows it +to respond to user input. + +this thread has write access to the application state, +and is responsible for mutating it in response to +user activity. diff --git a/engine/src/engine.rs b/engine/src/engine.rs index 497876fd..b81fe05a 100644 --- a/engine/src/engine.rs +++ b/engine/src/engine.rs @@ -78,7 +78,7 @@ impl Size for [N;2] { #[inline] fn y (&self) -> N { self[1] } } -pub trait Area: Copy { +pub trait Area { fn x (&self) -> N; fn y (&self) -> N; fn w (&self) -> N; @@ -133,6 +133,10 @@ pub trait Area: Copy { [self.x(), self.y(), self.w(), self.h().minus(y)] } + #[inline] fn center (&self) -> [N;2] { + [self.x() + self.w() / 2.into(), self.y() + self.h() / 2.into()] + } + fn zero () -> [N;4] { [N::zero(), N::zero(), N::zero(), N::zero()] } diff --git a/engine/src/output.rs b/engine/src/output.rs index e94c4ea6..4559a747 100644 --- a/engine/src/output.rs +++ b/engine/src/output.rs @@ -31,17 +31,39 @@ pub trait Content: Send + Sync { } impl Content for () { - fn area (&self, area: E::Area) -> E::Area { + fn area (&self, _: E::Area) -> E::Area { [0.into(), 0.into(), 0.into(), 0.into()].into() } - fn render (&self, output: &mut E::Output) {} + fn render (&self, _: &mut E::Output) {} } -impl> Content for &T {} +impl> Content for &T { + fn content (&self) -> impl Content { + (*self).content() + } + fn area (&self, area: E::Area) -> E::Area { + (*self).area(area) + } + fn render (&self, output: &mut E::Output) { + (*self).render(output) + } +} -impl> Content for Option {} - -impl> Content for Vec {} +impl> Content for Option { + fn content (&self) -> impl Content { + self.as_ref() + .map(|content|content.content()) + } + fn area (&self, area: E::Area) -> E::Area { + self.as_ref() + .map(|content|content.area(area)) + .unwrap_or([0.into(), 0.into(), 0.into(), 0.into(),].into()) + } + fn render (&self, output: &mut E::Output) { + self.as_ref() + .map(|content|content.render(output)); + } +} pub struct Thunk, F: Fn()->T + Send + Sync>(F, PhantomData); diff --git a/engine/src/tui.rs b/engine/src/tui.rs index ae3e89bb..ff09a43e 100644 --- a/engine/src/tui.rs +++ b/engine/src/tui.rs @@ -255,10 +255,14 @@ impl Output for TuiOutput { #[inline] fn area (&self) -> [u16;4] { self.area } #[inline] fn area_mut (&mut self) -> &mut [u16;4] { &mut self.area } #[inline] fn place (&mut self, area: [u16;4], content: &impl Content) { - let last = self.area(); - *self.area_mut() = area; + let last = self.area().xywh().clone(); + //panic!("a {last:?} {area:?} {:?}", self.area); + *self.area_mut() = area.xywh().clone(); + //panic!("b {last:?} {area:?} {:?}", self.area); content.render(self); + //panic!("c {last:?} {area:?} {:?}", self.area); *self.area_mut() = last; + //panic!("placed"); } } @@ -330,12 +334,18 @@ pub fn half_block (lower: bool, upper: bool) -> Option { //impl> Render for T {} impl Content for &str { + fn area (&self, to: [u16;4]) -> [u16;4] { + [to[0], to[1], self.chars().count() as u16, 1] + } fn render (&self, to: &mut TuiOutput) { to.blit(self, to.area.x(), to.area.y(), None) } } impl Content for String { + fn area (&self, to: [u16;4]) -> [u16;4] { + [to[0], to[1], self.chars().count() as u16, 1] + } fn render (&self, to: &mut TuiOutput) { to.blit(self, to.area.x(), to.area.y(), None) } diff --git a/layout/README.md b/layout/README.md new file mode 100644 index 00000000..1e76cb54 --- /dev/null +++ b/layout/README.md @@ -0,0 +1,4 @@ +# `tek_layout` + +this crate exposes several layout operators +which are generic over `tek_engine::Engine`. diff --git a/layout/src/direction.rs b/layout/src/direction.rs index 2ea19120..13609213 100644 --- a/layout/src/direction.rs +++ b/layout/src/direction.rs @@ -89,28 +89,28 @@ impl Direction { pub enum Bsp, Y: Content> { /// X is north of Y - N(Option, Option, Option), + North(Option, Option, Option), /// X is south of Y - S(Option, Option, Option), + South(Option, Option, Option), /// X is east of Y - E(Option, Option, Option), + East(Option, Option, Option), /// X is west of Y - W(Option, Option, Option), + West(Option, Option, Option), /// X is above Y - A(Option, Option), + Above(Option, Option), /// X is below Y - B(Option, Option), + Below(Option, Option), /// Should be avoided. Null(PhantomData), } impl, Y: Content> Bsp { - pub fn n (x: X, y: Y) -> Self { Self::N(None, Some(x), Some(y)) } - pub fn s (x: X, y: Y) -> Self { Self::S(None, Some(x), Some(y)) } - pub fn e (x: X, y: Y) -> Self { Self::E(None, Some(x), Some(y)) } - pub fn w (x: X, y: Y) -> Self { Self::W(None, Some(x), Some(y)) } - pub fn a (x: X, y: Y) -> Self { Self::A(Some(x), Some(y)) } - pub fn b (x: X, y: Y) -> Self { Self::B(Some(x), Some(y)) } + pub fn n (x: X, y: Y) -> Self { Self::North(None, Some(x), Some(y)) } + pub fn s (x: X, y: Y) -> Self { Self::South(None, Some(x), Some(y)) } + pub fn e (x: X, y: Y) -> Self { Self::East(None, Some(x), Some(y)) } + pub fn w (x: X, y: Y) -> Self { Self::West(None, Some(x), Some(y)) } + pub fn a (x: X, y: Y) -> Self { Self::Above(Some(x), Some(y)) } + pub fn b (x: X, y: Y) -> Self { Self::Below(Some(x), Some(y)) } } impl, Y: Content> Default for Bsp { @@ -123,17 +123,27 @@ impl, Y: Content> Content for Bsp { fn area (&self, outer: E::Area) -> E::Area { match self { Self::Null(_) => [0.into(), 0.into(), 0.into(), 0.into()].into(), - Self::N(_, a, b) | Self::S(_, a, b) => { + Self::North(_, a, b) => { let a = a.area(outer); - let b = b.area(outer); + let b = b.area(North.split_fixed(outer, a.y() + a.h()).1.into()); + [a.x().min(b.x()), a.y().min(b.y()), a.w().max(b.w()), a.h() + b.h()].into() + } + Self::South(_, a, b) => { + let a = a.area(outer); + let b = b.area(South.split_fixed(outer, a.y() + a.h()).1.into()); [a.x().min(b.x()), a.y().min(b.y()), a.w().max(b.w()), a.h() + b.h()].into() }, - Self::E(_, a, b) | Self::W(_, a, b) => { + Self::East(_, a, b) => { let a = a.area(outer); - let b = b.area(outer); + let b = b.area(East.split_fixed(outer, a.x() + a.w()).1.into()); [a.x().min(b.x()), a.y().min(b.y()), a.w() + b.w(), a.h().max(b.h())].into() }, - Self::A(a, b) | Self::B(a, b) => { + Self::West(_, a, b) => { + let a = a.area(outer); + let b = b.area(West.split_fixed(outer, a.x() + a.w()).1.into()); + [a.x().min(b.x()), a.y().min(b.y()), a.w() + b.w(), a.h().max(b.h())].into() + }, + Self::Above(a, b) | Self::Below(a, b) => { let a = a.area(outer); let b = b.area(outer); [a.x().min(b.x()), a.y().min(b.y()), a.w().max(b.w()), a.h().max(b.h())].into() @@ -141,38 +151,45 @@ impl, Y: Content> Content for Bsp { } } fn render (&self, to: &mut E::Output) { - let n = E::Area::zero(); - let area = to.area(); + let area = to.area().clone(); match self { - Self::S(p, a, b) => { - let s_a = a.area(area); - let _ = b.area(area); - let h = s_a.h().into(); - to.place(to.area().clip_h(h).into(), a); - to.place(to.area().shrink_y(h).push_y(h).into(), b); + Self::North(_, a, b) => { + let area_a = a.area(area); + let area_b = b.area(North.split_fixed(area, area_a.y() + area_a.h()).1.into()); + to.place(area_a, a); + to.place(area_b, b); }, - Self::E(p, a, b) => { - let s_a = a.area(area); - let _ = b.area(area); - let w = s_a.w().into(); - to.place(to.area().clip_w(w).into(), a); - to.place(to.area().push_x(w).shrink_x(w).into(), b); + Self::South(_, a, b) => { + let area_a = a.area(area).clone(); + let area_b = b.area(South.split_fixed(area, area_a.y() + area_a.h()).1.into()).clone(); + to.place(area_a, a); + to.place(area_b, b); }, - Self::W(p, a, b) => { - let s_a = a.area(area); - let _ = b.area(area); - let w = (to.area().w() - s_a.w()).into(); - to.place(to.area().push_x(w).into(), a); - to.place(to.area().shrink_x(w).into(), b); + Self::East(_, a, b) => { + let area_a = a.area(area); + let area_b = b.area(East.split_fixed(area, area_a.x() + area_a.w()).1.into()); + to.place(area_a, a); + to.place(area_b, b); }, - Self::N(p, a, b) => { - let s_a = a.area(area); - let _ = b.area(area); - let h = to.area().h() - s_a.h(); - to.place(to.area().push_y(h).into(), a); - to.place(to.area().shrink_y(h).into(), b); + Self::West(_, a, b) => { + let area_a = a.area(area); + let area_b = b.area(West.split_fixed(area, area_a.x() + area_a.w()).1.into()); + to.place(area_a, a); + to.place(area_b, b); }, - _ => todo!() + Self::Above(a, b) => { + let area_a = a.area(area); + let area_b = b.area(area); + to.place(area_b, b); + to.place(area_a, a); + }, + Self::Below(a, b) => { + let area_a = a.area(area); + let area_b = b.area(area); + to.place(area_a, a); + to.place(area_b, b); + }, + Self::Null(_) => {} } } } diff --git a/layout/src/measure.rs b/layout/src/measure.rs index cccdd1da..afb5785b 100644 --- a/layout/src/measure.rs +++ b/layout/src/measure.rs @@ -1,5 +1,6 @@ use crate::*; use std::sync::{Arc, atomic::{AtomicUsize, Ordering::Relaxed}}; +use ratatui::prelude::{Style, Color}; // TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small @@ -79,3 +80,43 @@ impl Measure { //} //impl ContentDebug for E {} + +impl Render for Measure { + fn min_size (&self, _: [u16;2]) -> Perhaps<[u16;2]> { + Ok(Some([0u16.into(), 0u16.into()].into())) + } + fn render (&self, to: &mut TuiOutput) -> Usually<()> { + self.set_w(to.area().w()); + self.set_h(to.area().h()); + Ok(()) + } +} + +impl Measure { + pub fn debug (&self) -> ShowMeasure { + ShowMeasure(&self) + } +} + +render!(|self: ShowMeasure<'a>|render(|to: &mut TuiOutput|Ok({ + let w = self.0.w(); + let h = self.0.h(); + to.blit(&format!(" {w} x {h} "), to.area.x(), to.area.y(), Some( + Style::default().bold().italic().bg(Color::Rgb(255, 0, 255)).fg(Color::Rgb(0,0,0)) + )) +}))); + +pub struct ShowMeasure<'a>(&'a Measure); + +pub struct DebugOverlay>(PhantomData, pub W); + +impl> Render for DebugOverlay { + fn min_size (&self, to: [u16;2]) -> Perhaps<[u16;2]> { + self.1.min_size(to) + } + fn render (&self, to: &mut TuiOutput) -> Usually<()> { + let [x, y, w, h] = to.area(); + self.1.render(to)?; + Ok(to.blit(&format!("{w}x{h}+{x}+{y}"), x, y, Some(Style::default().green()))) + } +} diff --git a/layout/src/transform.rs b/layout/src/transform.rs index 20d5029a..aa73e021 100644 --- a/layout/src/transform.rs +++ b/layout/src/transform.rs @@ -92,46 +92,63 @@ macro_rules! transform_xy_unit { } } -transform_xy_unit!(|self: Fixed, to|match self { - Self::X(fw, _) => [to.x(), to.y(), *fw, to.h()], - Self::Y(fh, _) => [to.x(), to.y(), to.w(), *fh], - Self::XY(fw, fh, _) => [to.x(), to.y(), *fw, *fh], // tagn +transform_xy_unit!(|self: Fixed, area|{ + let area = self.content().area(area); + match self { + Self::X(fw, _) => [area.x(), area.y(), *fw, area.h()], + Self::Y(fh, _) => [area.x(), area.y(), area.w(), *fh], + Self::XY(fw, fh, _) => [area.x(), area.y(), *fw, *fh], // tagn + } }); -transform_xy_unit!(|self: Shrink, to| - [to.x(), to.y(), to.w().minus(self.dx()), to.h().minus(self.dy())]); - -transform_xy_unit!(|self: Expand, to| - [to.x(), to.y(), to.w() + self.dx(), to.h() + self.dy()]); - -transform_xy_unit!(|self: Min, to|match self { - Self::X(mw, _) => [to.x(), to.y(), to.w().max(*mw), to.h()], - Self::Y(mh, _) => [to.x(), to.y(), to.w(), to.h().max(*mh)], - Self::XY(mw, mh, _) => [to.x(), to.y(), to.w().max(*mw), to.h().max(*mh)] +transform_xy_unit!(|self: Shrink, area|{ + let area = self.content().area(area); + [area.x(), area.y(), area.w().minus(self.dx()), area.h().minus(self.dy())] }); -transform_xy_unit!(|self: Max, to|match self { - Self::X(mw, _) => [to.x(), to.y(), to.w().min(*mw), to.h()], - Self::Y(mh, _) => [to.x(), to.y(), to.w(), to.h().min(*mh)], - Self::XY(mw, mh, _) => [to.x(), to.y(), to.w().min(*mw), to.h().min(*mh)], +transform_xy_unit!(|self: Expand, area|{ + let area = self.content().area(area); + [area.x(), area.y(), area.w() + self.dx(), area.h() + self.dy()] }); -transform_xy_unit!(|self: Push, to| - [to.x() + self.dx(), to.y() + self.dy(), to.w(), to.h()]); +transform_xy_unit!(|self: Min, area|{ + let area = self.content().area(area); + match self { + Self::X(mw, _) => [area.x(), area.y(), area.w().max(*mw), area.h()], + Self::Y(mh, _) => [area.x(), area.y(), area.w(), area.h().max(*mh)], + Self::XY(mw, mh, _) => [area.x(), area.y(), area.w().max(*mw), area.h().max(*mh)] + }}); -transform_xy_unit!(|self: Pull, to| - [to.x().minus(self.dx()), to.y().minus(self.dy()), to.w(), to.h()]); +transform_xy_unit!(|self: Max, area|{ + let area = self.content().area(area); + match self { + Self::X(mw, _) => [area.x(), area.y(), area.w().min(*mw), area.h()], + Self::Y(mh, _) => [area.x(), area.y(), area.w(), area.h().min(*mh)], + Self::XY(mw, mh, _) => [area.x(), area.y(), area.w().min(*mw), area.h().min(*mh)], + }}); -transform_xy_unit!(|self: Margin, to|{ +transform_xy_unit!(|self: Push, area|{ + let area = self.content().area(area); + [area.x() + self.dx(), area.y() + self.dy(), area.w(), area.h()] +}); + +transform_xy_unit!(|self: Pull, area|{ + let area = self.content().area(area); + [area.x().minus(self.dx()), area.y().minus(self.dy()), area.w(), area.h()] +}); + +transform_xy_unit!(|self: Margin, area|{ + let area = self.content().area(area); let dx = self.dx(); let dy = self.dy(); - [to.x().minus(dx), to.y().minus(dy), to.w() + dy + dy, to.h() + dy + dy] + [area.x().minus(dx), area.y().minus(dy), area.w() + dy + dy, area.h() + dy + dy] }); -transform_xy_unit!(|self: Padding, to|{ +transform_xy_unit!(|self: Padding, area|{ + let area = self.content().area(area); let dx = self.dx(); let dy = self.dy(); - [to.x() + dx, to.y() + dy, to.w().minus(dy + dy), to.h().minus(dy + dy), ] + [area.x() + dx, area.y() + dy, area.w().minus(dy + dy), area.h().minus(dy + dy), ] }); content_enum!(Align: Center, X, Y, NW, N, NE, E, SE, S, SW, W); @@ -173,10 +190,26 @@ fn align, N: Coordinate, R: Area + From<[N;4]>> (ali } impl> Content for Align { - fn render (&self, to: &mut E::Output) { - let outer = to.area(); + fn area (&self, outer: E::Area) -> E::Area { + let inner = Content::area(&self.content(), outer); + let (oc, ic) = (outer.center(), inner.center()); + match self { + Self::Center(_) => [0, 0, 0, 0], + Self::X(_) => [0, 0, 0, 0], + Self::Y(_) => [0, 0, 0, 0], + _ => [0, 0, 0, 0] + }.into() + } + fn render (&self, render: &mut E::Output) { + let outer = render.area(); let content = self.content(); let inner = Content::area(&content, outer); + let aligned = match self { + Self::Center(_) => { + let oc = outer.center(); + let ic = inner.center(); + } + } if let Some(aligned) = align(&self, outer.into(), inner.into()) { to.place(aligned, &content) } diff --git a/layout/src/tui.rs b/layout/src/tui.rs deleted file mode 100644 index 7064b922..00000000 --- a/layout/src/tui.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::*; -use ratatui::prelude::{Style, Color}; - -impl Render for Measure { - fn min_size (&self, _: [u16;2]) -> Perhaps<[u16;2]> { - Ok(Some([0u16.into(), 0u16.into()].into())) - } - fn render (&self, to: &mut TuiOutput) -> Usually<()> { - self.set_w(to.area().w()); - self.set_h(to.area().h()); - Ok(()) - } -} - -impl Measure { - pub fn debug (&self) -> ShowMeasure { - ShowMeasure(&self) - } -} - -render!(|self: ShowMeasure<'a>|render(|to: &mut TuiOutput|Ok({ - let w = self.0.w(); - let h = self.0.h(); - to.blit(&format!(" {w} x {h} "), to.area.x(), to.area.y(), Some( - Style::default().bold().italic().bg(Color::Rgb(255, 0, 255)).fg(Color::Rgb(0,0,0)) - )) -}))); - -pub struct ShowMeasure<'a>(&'a Measure); - -pub struct DebugOverlay>(PhantomData, pub W); - -impl> Render for DebugOverlay { - fn min_size (&self, to: [u16;2]) -> Perhaps<[u16;2]> { - self.1.min_size(to) - } - fn render (&self, to: &mut TuiOutput) -> Usually<()> { - let [x, y, w, h] = to.area(); - self.1.render(to)?; - Ok(to.blit(&format!("{w}x{h}+{x}+{y}"), x, y, Some(Style::default().green()))) - } -} diff --git a/src/groovebox.rs b/src/groovebox.rs index 904a2b85..81d7138d 100644 --- a/src/groovebox.rs +++ b/src/groovebox.rs @@ -128,16 +128,16 @@ render!(Tui: (self: Groovebox) => { PhraseSelector::play_phrase(&self.player), PhraseSelector::next_phrase(&self.player), ))); - let pool = move|x|Bsp::w( - Fixed::x(pool_w, Pull::y(1, Fill::y(Align::e(PoolView(&self.pool))))), - x); - let sampler = move|x|Bsp::e( - Fixed::x(sampler_w, Fill::xy(col!( - Meters(self.sampler.input_meter.as_ref()), - GrooveboxSamples(self)))), - x); - let status = EditStatus(&self.sampler, &self.editor, note_pt, pool(sampler(&self.editor))); - lay!("kyp", "nymka") + let pool = PoolView(&self.pool); + let with_pool = move|x|Bsp::w(Fixed::x(pool_w, Pull::y(1, Fill::y(Align::e(pool)))), x); + with_pool(col!(transport, selector)) + //selector + //let sampler = move|x|Bsp::e( + //Fixed::x(sampler_w, Fill::xy(col!( + //Meters(self.sampler.input_meter.as_ref()), + //GrooveboxSamples(self)))), + //x); + //let status = EditStatus(&self.sampler, &self.editor, note_pt, pool(sampler(&self.editor))); //status //Fill::xy(lay!( //&self.size, diff --git a/src/transport.rs b/src/transport.rs index 7fa382d4..31261647 100644 --- a/src/transport.rs +++ b/src/transport.rs @@ -23,10 +23,10 @@ from_jack!(|jack|TransportTui Self { has_clock!(|self: TransportTui|&self.clock); audio!(|self: TransportTui, client, scope|ClockAudio(self).process(client, scope)); handle!(|self: TransportTui, from|TransportCommand::execute_with_state(self, from)); -render!(Tui: (self: TransportTui) => Fixed::y(3, row!( - " ", Fixed::x(5, PlayPause(false)), - " ", Shrink::x(1, TransportView::new(self, Some(self.color), true)), -))); +render!(Tui: (self: TransportTui) => Align::x(Fixed::y(3, row!( + Fixed::x(5, Fixed::y(3, PlayPause(false))), + TransportView::new(self, Some(self.color), true), +)))); impl std::fmt::Debug for TransportTui { fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { f.debug_struct("TransportTui") @@ -89,35 +89,25 @@ impl TransportView { } render!(Tui: (self: TransportView) => { let color = self.color; - let transport_field = move|label, value|row!( - Tui::fg_bg(color.lightest.rgb, color.base.rgb, Tui::bold(true, label)), - Tui::fg_bg(color.base.rgb, color.darkest.rgb, "▌"), - Tui::fg_bg(color.lightest.rgb, color.darkest.rgb, format!("{:>10}", value)), - Tui::fg_bg(color.darkest.rgb, color.base.rgb, "▌"), - ); - Fixed::y(3, Tui::bg(color.base.rgb, Fill::x(row!( - Thunk::new(move||col!( + //let transport_field = move|label, value|row!( + //Tui::fg_bg(color.lightest.rgb, color.base.rgb, Tui::bold(true, label)), + //Tui::fg_bg(color.base.rgb, color.darkest.rgb, "▌"), + //Tui::fg_bg(color.lightest.rgb, color.darkest.rgb, format!("{:>10}", value)), + //Tui::fg_bg(color.darkest.rgb, color.base.rgb, "▌"), + //); + Min::x(35, Fixed::y(3, Tui::bg(color.base.rgb, "kyp")))/*Bsp::e( + Fixed::x(17, col!( transport_field(" Beat", self.beat.clone()), transport_field(" Time", format!("{:.1}s", self.current_second)), transport_field(" BPM", self.bpm.clone()), )), - Thunk::new(move||col!( + Fixed::x(17, col!( transport_field(" Rate", format!("{}", self.sr)), transport_field(" Chunk", format!("{}", self.chunk)), transport_field(" Lag", format!("{:.3}ms", self.latency)), )), - col!( - //Field(" CPU%", format!("{:.1}ms", self.perf).as_str(), &color), - ), - )))) + )))*/ }); -struct TransportField(&'static str, String, ItemPalette); -render!(Tui: (self: TransportField) => row!( - Tui::fg_bg(self.2.lightest.rgb, self.2.base.rgb, Tui::bold(true, self.0)), - Tui::fg_bg(self.2.base.rgb, self.2.darkest.rgb, "▌"), - Tui::fg_bg(self.2.lightest.rgb, self.2.darkest.rgb, format!("{:>10}", self.1)), - Tui::fg_bg(self.2.darkest.rgb, self.2.base.rgb, "▌"), -)); pub struct PlayPause(pub bool); render!(Tui: (self: PlayPause) => Tui::bg( if self.0{Color::Rgb(0,128,0)}else{Color::Rgb(128,64,0)},