trying to get new Bsp to work; update docs

This commit is contained in:
🪞👃🪞 2024-12-31 19:21:48 +01:00
parent c9b81edb45
commit 62ce1776c0
11 changed files with 301 additions and 157 deletions

65
engine/README.md Normal file
View file

@ -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<RwLock>` and `Arc<Atomic>`. the engine and
application instances are expected to be wrapped
in `Arc<RwLock>`; 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<E>`, 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<E>`, 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.

View file

@ -78,7 +78,7 @@ impl<N: Coordinate> Size<N> for [N;2] {
#[inline] fn y (&self) -> N { self[1] } #[inline] fn y (&self) -> N { self[1] }
} }
pub trait Area<N: Coordinate>: Copy { pub trait Area<N: Coordinate> {
fn x (&self) -> N; fn x (&self) -> N;
fn y (&self) -> N; fn y (&self) -> N;
fn w (&self) -> N; fn w (&self) -> N;
@ -133,6 +133,10 @@ pub trait Area<N: Coordinate>: Copy {
[self.x(), self.y(), self.w(), self.h().minus(y)] [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] { fn zero () -> [N;4] {
[N::zero(), N::zero(), N::zero(), N::zero()] [N::zero(), N::zero(), N::zero(), N::zero()]
} }

View file

@ -31,17 +31,39 @@ pub trait Content<E: Engine>: Send + Sync {
} }
impl<E: Engine> Content<E> for () { impl<E: Engine> Content<E> 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() [0.into(), 0.into(), 0.into(), 0.into()].into()
} }
fn render (&self, output: &mut E::Output) {} fn render (&self, _: &mut E::Output) {}
} }
impl<E: Engine, T: Content<E>> Content<E> for &T {} impl<E: Engine, T: Content<E>> Content<E> for &T {
fn content (&self) -> impl Content<E> {
(*self).content()
}
fn area (&self, area: E::Area) -> E::Area {
(*self).area(area)
}
fn render (&self, output: &mut E::Output) {
(*self).render(output)
}
}
impl<E: Engine, T: Content<E>> Content<E> for Option<T> {} impl<E: Engine, T: Content<E>> Content<E> for Option<T> {
fn content (&self) -> impl Content<E> {
impl<E: Engine, T: Content<E>> Content<E> for Vec<T> {} 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<E: Engine, T: Content<E>, F: Fn()->T + Send + Sync>(F, PhantomData<E>); pub struct Thunk<E: Engine, T: Content<E>, F: Fn()->T + Send + Sync>(F, PhantomData<E>);

View file

@ -255,10 +255,14 @@ impl Output<Tui> for TuiOutput {
#[inline] fn area (&self) -> [u16;4] { self.area } #[inline] fn area (&self) -> [u16;4] { self.area }
#[inline] fn area_mut (&mut self) -> &mut [u16;4] { &mut 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<Tui>) { #[inline] fn place (&mut self, area: [u16;4], content: &impl Content<Tui>) {
let last = self.area(); let last = self.area().xywh().clone();
*self.area_mut() = area; //panic!("a {last:?} {area:?} {:?}", self.area);
*self.area_mut() = area.xywh().clone();
//panic!("b {last:?} {area:?} {:?}", self.area);
content.render(self); content.render(self);
//panic!("c {last:?} {area:?} {:?}", self.area);
*self.area_mut() = last; *self.area_mut() = last;
//panic!("placed");
} }
} }
@ -330,12 +334,18 @@ pub fn half_block (lower: bool, upper: bool) -> Option<char> {
//impl<T: Content<Tui>> Render<Tui> for T {} //impl<T: Content<Tui>> Render<Tui> for T {}
impl Content<Tui> for &str { impl Content<Tui> 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) { fn render (&self, to: &mut TuiOutput) {
to.blit(self, to.area.x(), to.area.y(), None) to.blit(self, to.area.x(), to.area.y(), None)
} }
} }
impl Content<Tui> for String { impl Content<Tui> 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) { fn render (&self, to: &mut TuiOutput) {
to.blit(self, to.area.x(), to.area.y(), None) to.blit(self, to.area.x(), to.area.y(), None)
} }

4
layout/README.md Normal file
View file

@ -0,0 +1,4 @@
# `tek_layout`
this crate exposes several layout operators
which are generic over `tek_engine::Engine`.

View file

@ -89,28 +89,28 @@ impl Direction {
pub enum Bsp<E: Engine, X: Content<E>, Y: Content<E>> { pub enum Bsp<E: Engine, X: Content<E>, Y: Content<E>> {
/// X is north of Y /// X is north of Y
N(Option<f64>, Option<X>, Option<Y>), North(Option<f64>, Option<X>, Option<Y>),
/// X is south of Y /// X is south of Y
S(Option<f64>, Option<X>, Option<Y>), South(Option<f64>, Option<X>, Option<Y>),
/// X is east of Y /// X is east of Y
E(Option<f64>, Option<X>, Option<Y>), East(Option<f64>, Option<X>, Option<Y>),
/// X is west of Y /// X is west of Y
W(Option<f64>, Option<X>, Option<Y>), West(Option<f64>, Option<X>, Option<Y>),
/// X is above Y /// X is above Y
A(Option<X>, Option<Y>), Above(Option<X>, Option<Y>),
/// X is below Y /// X is below Y
B(Option<X>, Option<Y>), Below(Option<X>, Option<Y>),
/// Should be avoided. /// Should be avoided.
Null(PhantomData<E>), Null(PhantomData<E>),
} }
impl<E: Engine, X: Content<E>, Y: Content<E>> Bsp<E, X, Y> { impl<E: Engine, X: Content<E>, Y: Content<E>> Bsp<E, X, Y> {
pub fn n (x: X, y: Y) -> Self { Self::N(None, 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::S(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::E(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::W(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::A(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::B(Some(x), Some(y)) } pub fn b (x: X, y: Y) -> Self { Self::Below(Some(x), Some(y)) }
} }
impl<E: Engine, X: Content<E>, Y: Content<E>> Default for Bsp<E, X, Y> { impl<E: Engine, X: Content<E>, Y: Content<E>> Default for Bsp<E, X, Y> {
@ -123,17 +123,27 @@ impl<E: Engine, X: Content<E>, Y: Content<E>> Content<E> for Bsp<E, X, Y> {
fn area (&self, outer: E::Area) -> E::Area { fn area (&self, outer: E::Area) -> E::Area {
match self { match self {
Self::Null(_) => [0.into(), 0.into(), 0.into(), 0.into()].into(), 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 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() [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 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() [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 a = a.area(outer);
let b = b.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() [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<E: Engine, X: Content<E>, Y: Content<E>> Content<E> for Bsp<E, X, Y> {
} }
} }
fn render (&self, to: &mut E::Output) { fn render (&self, to: &mut E::Output) {
let n = E::Area::zero(); let area = to.area().clone();
let area = to.area();
match self { match self {
Self::S(p, a, b) => { Self::North(_, a, b) => {
let s_a = a.area(area); let area_a = a.area(area);
let _ = b.area(area); let area_b = b.area(North.split_fixed(area, area_a.y() + area_a.h()).1.into());
let h = s_a.h().into(); to.place(area_a, a);
to.place(to.area().clip_h(h).into(), a); to.place(area_b, b);
to.place(to.area().shrink_y(h).push_y(h).into(), b);
}, },
Self::E(p, a, b) => { Self::South(_, a, b) => {
let s_a = a.area(area); let area_a = a.area(area).clone();
let _ = b.area(area); let area_b = b.area(South.split_fixed(area, area_a.y() + area_a.h()).1.into()).clone();
let w = s_a.w().into(); to.place(area_a, a);
to.place(to.area().clip_w(w).into(), a); to.place(area_b, b);
to.place(to.area().push_x(w).shrink_x(w).into(), b);
}, },
Self::W(p, a, b) => { Self::East(_, a, b) => {
let s_a = a.area(area); let area_a = a.area(area);
let _ = b.area(area); let area_b = b.area(East.split_fixed(area, area_a.x() + area_a.w()).1.into());
let w = (to.area().w() - s_a.w()).into(); to.place(area_a, a);
to.place(to.area().push_x(w).into(), a); to.place(area_b, b);
to.place(to.area().shrink_x(w).into(), b);
}, },
Self::N(p, a, b) => { Self::West(_, a, b) => {
let s_a = a.area(area); let area_a = a.area(area);
let _ = b.area(area); let area_b = b.area(West.split_fixed(area, area_a.x() + area_a.w()).1.into());
let h = to.area().h() - s_a.h(); to.place(area_a, a);
to.place(to.area().push_y(h).into(), a); to.place(area_b, b);
to.place(to.area().shrink_y(h).into(), 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(_) => {}
} }
} }
} }

View file

@ -1,5 +1,6 @@
use crate::*; use crate::*;
use std::sync::{Arc, atomic::{AtomicUsize, Ordering::Relaxed}}; use std::sync::{Arc, atomic::{AtomicUsize, Ordering::Relaxed}};
use ratatui::prelude::{Style, Color};
// TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small // TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small
@ -79,3 +80,43 @@ impl<E: Engine> Measure<E> {
//} //}
//impl<E: Engine> ContentDebug<E> for E {} //impl<E: Engine> ContentDebug<E> for E {}
impl Render<Tui> for Measure<Tui> {
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<Tui> {
pub fn debug (&self) -> ShowMeasure {
ShowMeasure(&self)
}
}
render!(<Tui>|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<Tui>);
pub struct DebugOverlay<E: Engine, W: Render<E>>(PhantomData<E>, pub W);
impl<T: Render<Tui>> Render<Tui> for DebugOverlay<Tui, T> {
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())))
}
}

View file

@ -92,46 +92,63 @@ macro_rules! transform_xy_unit {
} }
} }
transform_xy_unit!(|self: Fixed, to|match self { transform_xy_unit!(|self: Fixed, area|{
Self::X(fw, _) => [to.x(), to.y(), *fw, to.h()], let area = self.content().area(area);
Self::Y(fh, _) => [to.x(), to.y(), to.w(), *fh], match self {
Self::XY(fw, fh, _) => [to.x(), to.y(), *fw, *fh], // tagn 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| transform_xy_unit!(|self: Shrink, area|{
[to.x(), to.y(), to.w().minus(self.dx()), to.h().minus(self.dy())]); let area = self.content().area(area);
[area.x(), area.y(), area.w().minus(self.dx()), area.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: Max, to|match self { transform_xy_unit!(|self: Expand, area|{
Self::X(mw, _) => [to.x(), to.y(), to.w().min(*mw), to.h()], let area = self.content().area(area);
Self::Y(mh, _) => [to.x(), to.y(), to.w(), to.h().min(*mh)], [area.x(), area.y(), area.w() + self.dx(), area.h() + self.dy()]
Self::XY(mw, mh, _) => [to.x(), to.y(), to.w().min(*mw), to.h().min(*mh)],
}); });
transform_xy_unit!(|self: Push, to| transform_xy_unit!(|self: Min, area|{
[to.x() + self.dx(), to.y() + self.dy(), to.w(), to.h()]); 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| transform_xy_unit!(|self: Max, area|{
[to.x().minus(self.dx()), to.y().minus(self.dy()), to.w(), to.h()]); 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 dx = self.dx();
let dy = self.dy(); 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 dx = self.dx();
let dy = self.dy(); 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); content_enum!(Align: Center, X, Y, NW, N, NE, E, SE, S, SW, W);
@ -173,10 +190,26 @@ fn align<E: Engine, T: Content<E>, N: Coordinate, R: Area<N> + From<[N;4]>> (ali
} }
impl<E: Engine, T: Content<E>> Content<E> for Align<E, T> { impl<E: Engine, T: Content<E>> Content<E> for Align<E, T> {
fn render (&self, to: &mut E::Output) { fn area (&self, outer: E::Area) -> E::Area {
let outer = to.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 content = self.content();
let inner = Content::area(&content, outer); 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()) { if let Some(aligned) = align(&self, outer.into(), inner.into()) {
to.place(aligned, &content) to.place(aligned, &content)
} }

View file

@ -1,42 +0,0 @@
use crate::*;
use ratatui::prelude::{Style, Color};
impl Render<Tui> for Measure<Tui> {
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<Tui> {
pub fn debug (&self) -> ShowMeasure {
ShowMeasure(&self)
}
}
render!(<Tui>|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<Tui>);
pub struct DebugOverlay<E: Engine, W: Render<E>>(PhantomData<E>, pub W);
impl<T: Render<Tui>> Render<Tui> for DebugOverlay<Tui, T> {
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())))
}
}

View file

@ -128,16 +128,16 @@ render!(Tui: (self: Groovebox) => {
PhraseSelector::play_phrase(&self.player), PhraseSelector::play_phrase(&self.player),
PhraseSelector::next_phrase(&self.player), PhraseSelector::next_phrase(&self.player),
))); )));
let pool = move|x|Bsp::w( let pool = PoolView(&self.pool);
Fixed::x(pool_w, Pull::y(1, Fill::y(Align::e(PoolView(&self.pool))))), let with_pool = move|x|Bsp::w(Fixed::x(pool_w, Pull::y(1, Fill::y(Align::e(pool)))), x);
x); with_pool(col!(transport, selector))
let sampler = move|x|Bsp::e( //selector
Fixed::x(sampler_w, Fill::xy(col!( //let sampler = move|x|Bsp::e(
Meters(self.sampler.input_meter.as_ref()), //Fixed::x(sampler_w, Fill::xy(col!(
GrooveboxSamples(self)))), //Meters(self.sampler.input_meter.as_ref()),
x); //GrooveboxSamples(self)))),
let status = EditStatus(&self.sampler, &self.editor, note_pt, pool(sampler(&self.editor))); //x);
lay!("kyp", "nymka") //let status = EditStatus(&self.sampler, &self.editor, note_pt, pool(sampler(&self.editor)));
//status //status
//Fill::xy(lay!( //Fill::xy(lay!(
//&self.size, //&self.size,

View file

@ -23,10 +23,10 @@ from_jack!(|jack|TransportTui Self {
has_clock!(|self: TransportTui|&self.clock); has_clock!(|self: TransportTui|&self.clock);
audio!(|self: TransportTui, client, scope|ClockAudio(self).process(client, scope)); audio!(|self: TransportTui, client, scope|ClockAudio(self).process(client, scope));
handle!(<Tui>|self: TransportTui, from|TransportCommand::execute_with_state(self, from)); handle!(<Tui>|self: TransportTui, from|TransportCommand::execute_with_state(self, from));
render!(Tui: (self: TransportTui) => Fixed::y(3, row!( render!(Tui: (self: TransportTui) => Align::x(Fixed::y(3, row!(
" ", Fixed::x(5, PlayPause(false)), Fixed::x(5, Fixed::y(3, PlayPause(false))),
" ", Shrink::x(1, TransportView::new(self, Some(self.color), true)), TransportView::new(self, Some(self.color), true),
))); ))));
impl std::fmt::Debug for TransportTui { impl std::fmt::Debug for TransportTui {
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
f.debug_struct("TransportTui") f.debug_struct("TransportTui")
@ -89,35 +89,25 @@ impl TransportView {
} }
render!(Tui: (self: TransportView) => { render!(Tui: (self: TransportView) => {
let color = self.color; let color = self.color;
let transport_field = move|label, value|row!( //let transport_field = move|label, value|row!(
Tui::fg_bg(color.lightest.rgb, color.base.rgb, Tui::bold(true, label)), //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.base.rgb, color.darkest.rgb, "▌"),
Tui::fg_bg(color.lightest.rgb, color.darkest.rgb, format!("{:>10}", value)), //Tui::fg_bg(color.lightest.rgb, color.darkest.rgb, format!("{:>10}", value)),
Tui::fg_bg(color.darkest.rgb, color.base.rgb, ""), //Tui::fg_bg(color.darkest.rgb, color.base.rgb, "▌"),
); //);
Fixed::y(3, Tui::bg(color.base.rgb, Fill::x(row!( Min::x(35, Fixed::y(3, Tui::bg(color.base.rgb, "kyp")))/*Bsp::e(
Thunk::new(move||col!( Fixed::x(17, col!(
transport_field(" Beat", self.beat.clone()), transport_field(" Beat", self.beat.clone()),
transport_field(" Time", format!("{:.1}s", self.current_second)), transport_field(" Time", format!("{:.1}s", self.current_second)),
transport_field(" BPM", self.bpm.clone()), transport_field(" BPM", self.bpm.clone()),
)), )),
Thunk::new(move||col!( Fixed::x(17, col!(
transport_field(" Rate", format!("{}", self.sr)), transport_field(" Rate", format!("{}", self.sr)),
transport_field(" Chunk", format!("{}", self.chunk)), transport_field(" Chunk", format!("{}", self.chunk)),
transport_field(" Lag", format!("{:.3}ms", self.latency)), 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); pub struct PlayPause(pub bool);
render!(Tui: (self: PlayPause) => Tui::bg( render!(Tui: (self: PlayPause) => Tui::bg(
if self.0{Color::Rgb(0,128,0)}else{Color::Rgb(128,64,0)}, if self.0{Color::Rgb(0,128,0)}else{Color::Rgb(128,64,0)},