more updates to space and transport

This commit is contained in:
🪞👃🪞 2024-12-30 14:31:00 +01:00
parent 9fa858f226
commit 304ce35cbb
6 changed files with 139 additions and 140 deletions

View file

@ -100,8 +100,8 @@ impl ArrangerTui {
}
}
render!(<Tui>|self: ArrangerTui|{
let play = Fixed::wh(5, 2, PlayPause(self.clock.is_rolling()));
let transport = TransportView::from((self, Some(ItemPalette::from(TuiTheme::g(96))), true));
let play = PlayPause(self.clock.is_rolling());
let transport = TransportView::new(self, Some(ItemPalette::from(TuiTheme::g(96))), true);
let with_transport = |x|col!([row!(![&play, &transport]), &x]);
let pool_size = if self.phrases.visible { self.splits[1] } else { 0 };
let with_pool = |x|Split::w(false, pool_size, PoolView(&self.phrases), x);

View file

@ -117,15 +117,16 @@ render!(<Tui>|self:Groovebox|{
let pool_w = if self.pool.visible { phrase_w } else { 0 };
let sampler_w = 11;
let note_pt = self.editor.note_point();
let color = self.player.play_phrase().as_ref()
.and_then(|(_,p)|p.as_ref().map(|p|p.read().unwrap().color))
.clone();
Fill::wh(lay!([
&self.size,
Fill::wh(Align::s(Fixed::h(2, GrooveboxStatus::from(self)))),
Shrink::y(2, col!([
Fixed::h(2, row!([
Fixed::wh(5, 2, PlayPause(self.clock().is_rolling())),
Fixed::h(2, TransportView::from((self, self.player.play_phrase().as_ref().map(|(_,p)|
p.as_ref().map(|p|p.read().unwrap().color)
).flatten().clone(), true))),
Fixed::h(3, row!([
PlayPause(self.clock().is_rolling()),
TransportView::new(self, color, true),
])),
Push::x(sampler_w, Fixed::h(1, row!([
PhraseSelector::play_phrase(&self.player),

View file

@ -57,8 +57,8 @@ render!(<Tui>|self: SequencerTui|{
).flatten().clone();
let toolbar = Cond::when(self.transport, row!([
Fixed::wh(5, 2, PlayPause(self.clock.is_rolling())),
Fixed::h(2, TransportView::from((self, color, true))),
PlayPause(self.clock.is_rolling()),
TransportView::new(self, color, true),
]));
let play_queue = Cond::when(self.selectors, row!([

View file

@ -11,6 +11,11 @@ mod scroll; pub use self::scroll::*;
mod size; pub use self::size::*;
mod split; pub use self::split::*;
/// A cardinal direction.
#[derive(Copy, Clone, PartialEq)]
pub enum Direction { North, South, West, East, }
use self::Direction::*;
/// Has static methods for conditional rendering,
/// in unary and binary forms.
pub struct Cond;
@ -94,6 +99,36 @@ pub trait Size<N: Coordinate> {
}
}
pub trait HasSize<E: Engine> {
fn size (&self) -> &Measure<E>;
}
#[macro_export] macro_rules! has_size {
(<$E:ty>|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
impl $(<$($L),*$($T $(: $U)?),*>)? HasSize<$E> for $Struct $(<$($L),*$($T),*>)? {
fn size (&$self) -> &Measure<$E> { $cb }
}
}
}
/// A widget that tracks its render width and height
#[derive(Default)]
pub struct Measure<E: Engine> {
_engine: PhantomData<E>,
pub x: Arc<AtomicUsize>,
pub y: Arc<AtomicUsize>,
}
pub struct ShowMeasure<'a>(&'a Measure<Tui>);
pub trait LayoutDebug<E: Engine> {
fn debug <W: Render<E>> (other: W) -> DebugOverlay<E, W> {
DebugOverlay(Default::default(), other)
}
}
pub struct DebugOverlay<E: Engine, W: Render<E>>(PhantomData<E>, pub W);
pub trait Area<N: Coordinate>: Copy {
fn x (&self) -> N;
fn y (&self) -> N;
@ -319,14 +354,27 @@ pub enum Margin<E: Engine, T: Render<E>> {
by_axis!(+Margin);
/// A cardinal direction.
#[derive(Copy, Clone, PartialEq)]
pub enum Direction { North, South, West, East, }
/// A binary split with fixed proportion
pub struct Split<E: Engine, A: Render<E>, B: Render<E>>(
pub bool, pub Direction, pub E::Unit, A, B, PhantomData<E>
);
pub struct Split<E, A, B>(pub bool, pub Direction, pub E::Unit, A, B, PhantomData<E>)
where E: Engine, A: Render<E>, B: Render<E>;
impl<E: Engine, A: Render<E>, B: Render<E>> Split<E, A, B> {
#[inline] pub fn new (flip: bool, direction: Direction, proportion: E::Unit, a: A, b: B) -> Self {
Self(flip, direction, proportion, a, b, Default::default())
}
#[inline] pub fn n (flip: bool, proportion: E::Unit, a: A, b: B) -> Self {
Self::new(flip, North, proportion, a, b)
}
#[inline] pub fn s (flip: bool, proportion: E::Unit, a: A, b: B) -> Self {
Self::new(flip, South, proportion, a, b)
}
#[inline] pub fn e (flip: bool, proportion: E::Unit, a: A, b: B) -> Self {
Self::new(flip, West, proportion, a, b)
}
#[inline] pub fn w (flip: bool, proportion: E::Unit, a: A, b: B) -> Self {
Self::new(flip, East, proportion, a, b)
}
}
pub enum Bsp<E: Engine, X: Render<E>, Y: Render<E>> {
/// X is north of Y
@ -345,11 +393,39 @@ pub enum Bsp<E: Engine, X: Render<E>, Y: Render<E>> {
Null(PhantomData<E>),
}
impl<E: Engine, X: Render<E>, Y: Render<E>> Bsp<E, X, Y> {
pub fn new (x: X) -> Self { Self::A(Some(x), None) }
pub fn n (x: X, y: Y) -> Self { Self::N(Some(x), Some(y)) }
pub fn s (x: X, y: Y) -> Self { Self::S(Some(x), Some(y)) }
pub fn e (x: X, y: Y) -> Self { Self::E(Some(x), Some(y)) }
pub fn w (x: X, y: Y) -> Self { Self::W(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 struct Stack<
E: Engine,
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)->Usually<()>)->Usually<()>
>(pub F, pub Direction, PhantomData<E>);
impl<
E: Engine,
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)->Usually<()>)->Usually<()>
> Stack<E, F> {
#[inline] pub fn new (direction: Direction, build: F) -> Self {
Self(build, direction, Default::default())
}
#[inline] pub fn right (build: F) -> Self {
Self::new(East, build)
}
#[inline] pub fn down (build: F) -> Self {
Self::new(South, build)
}
#[inline] pub fn up (build: F) -> Self {
Self::new(North, build)
}
}
#[macro_export] macro_rules! col {
([$($expr:expr),* $(,)?]) => {
Stack::down(move|add|{ $(add(&$expr)?;)* Ok(()) })
@ -395,41 +471,11 @@ pub struct Stack<
};
}
pub trait HasSize<E: Engine> {
fn size (&self) -> &Measure<E>;
}
#[macro_export] macro_rules! has_size {
(<$E:ty>|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
impl $(<$($L),*$($T $(: $U)?),*>)? HasSize<$E> for $Struct $(<$($L),*$($T),*>)? {
fn size (&$self) -> &Measure<$E> { $cb }
}
}
}
pub trait LayoutDebug<E: Engine> {
fn debug <W: Render<E>> (other: W) -> DebugOverlay<E, W> {
DebugOverlay(Default::default(), other)
}
}
pub struct DebugOverlay<E: Engine, W: Render<E>>(PhantomData<E>, pub W);
/// A widget that tracks its render width and height
#[derive(Default)]
pub struct Measure<E: Engine> {
_engine: PhantomData<E>,
pub x: Arc<AtomicUsize>,
pub y: Arc<AtomicUsize>,
}
pub struct ShowMeasure<'a>(&'a Measure<Tui>);
/// A scrollable area.
pub struct Scroll<
pub struct Scroll<E, F>(pub F, pub Direction, pub u64, PhantomData<E>)
where
E: Engine,
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)->Usually<()>)->Usually<()>
>(pub F, pub Direction, pub u64, PhantomData<E>);
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)->Usually<()>)->Usually<()>;
/// Override X and Y coordinates, aligning to corner, side, or center of area
pub enum Align<E: Engine, T: Render<E>> {

View file

@ -27,24 +27,6 @@ impl Direction {
}
}
impl<E: Engine, A: Render<E>, B: Render<E>> Split<E, A, B> {
pub fn new (flip: bool, direction: Direction, proportion: E::Unit, a: A, b: B) -> Self {
Self(flip, direction, proportion, a, b, Default::default())
}
pub fn n (flip: bool, proportion: E::Unit, a: A, b: B) -> Self {
Self(flip, North, proportion, a, b, Default::default())
}
pub fn s (flip: bool, proportion: E::Unit, a: A, b: B) -> Self {
Self(flip, South, proportion, a, b, Default::default())
}
pub fn e (flip: bool, proportion: E::Unit, a: A, b: B) -> Self {
Self(flip, West, proportion, a, b, Default::default())
}
pub fn w (flip: bool, proportion: E::Unit, a: A, b: B) -> Self {
Self(flip, East, proportion, a, b, Default::default())
}
}
impl<E: Engine, A: Render<E>, B: Render<E>> Render<E> for Split<E, A, B> {
fn min_size (&self, to: E::Size) -> Perhaps<E::Size> {
Ok(Some(to))
@ -61,16 +43,6 @@ impl<E: Engine, A: Render<E>, B: Render<E>> Render<E> for Split<E, A, B> {
}
}
impl<E: Engine, X: Render<E>, Y: Render<E>> Bsp<E, X, Y> {
pub fn new (x: X) -> Self { Self::A(Some(x), None) }
pub fn n (x: X, y: Y) -> Self { Self::N(Some(x), Some(y)) }
pub fn s (x: X, y: Y) -> Self { Self::S(Some(x), Some(y)) }
pub fn e (x: X, y: Y) -> Self { Self::E(Some(x), Some(y)) }
pub fn w (x: X, y: Y) -> Self { Self::W(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)) }
}
impl<E: Engine, X: Render<E>, Y: Render<E>> Default for Bsp<E, X, Y> {
fn default () -> Self {
Self::Null(Default::default())
@ -142,24 +114,6 @@ impl<E: Engine, X: Render<E>, Y: Render<E>> Render<E> for Bsp<E, X, Y> {
}
}
impl<
E: Engine,
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)->Usually<()>)->Usually<()>
> Stack<E, F> {
#[inline] pub fn new (direction: Direction, build: F) -> Self {
Self(build, direction, Default::default())
}
#[inline] pub fn right (build: F) -> Self {
Self::new(East, build)
}
#[inline] pub fn down (build: F) -> Self {
Self::new(South, build)
}
#[inline] pub fn up (build: F) -> Self {
Self::new(North, build)
}
}
impl<E: Engine, F> Render<E> for Stack<E, F>
where
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)->Usually<()>)->Usually<()>

View file

@ -18,10 +18,13 @@ from_jack!(|jack|TransportTui Self {
cursor: (0, 0),
focus: TransportFocus::PlayPause
});
has_clock!(|self:TransportTui|&self.clock);
audio!(|self:TransportTui,client,scope|ClockAudio(self).process(client, scope));
handle!(<Tui>|self:TransportTui,from|TransportCommand::execute_with_state(self, from));
render!(<Tui>|self: TransportTui|TransportView::from((self, None, true)));
has_clock!(|self: TransportTui|&self.clock);
audio!(|self: TransportTui, client, scope|ClockAudio(self).process(client, scope));
handle!(<Tui>|self: TransportTui, from|TransportCommand::execute_with_state(self, from));
render!(<Tui>|self: TransportTui|row!([
Fixed::wh(5, 3, PlayPause(false)),
Fixed::h(3, TransportView::new(self, None, 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")
@ -46,8 +49,8 @@ pub struct TransportView {
current_sample: f64,
current_second: f64,
}
impl<T: HasClock> From<(&T, Option<ItemPalette>, bool)> for TransportView {
fn from ((state, color, focused): (&T, Option<ItemPalette>, bool)) -> Self {
impl TransportView {
pub fn new (state: &impl HasClock, color: Option<ItemPalette>, focused: bool) -> Self {
let clock = state.clock();
let rate = clock.timebase.sr.get();
let chunk = clock.chunk.load(Relaxed);
@ -58,61 +61,56 @@ impl<T: HasClock> From<(&T, Option<ItemPalette>, bool)> for TransportView {
let chunk = format!("{chunk}");
let latency = format!("{latency}");
let color = color.unwrap_or(ItemPalette::from(TuiTheme::g(32)));
if let Some(started) = clock.started.read().unwrap().as_ref() {
let (
global_sample, global_second, current_sample, current_second, beat
) = if let Some(started) = clock.started.read().unwrap().as_ref() {
let current_sample = (clock.global.sample.get() - started.sample.get())/1000.;
let current_usec = clock.global.usec.get() - started.usec.get();
let current_second = current_usec/1000000.;
Self {
color, focused, sr, bpm, ppq, chunk, latency,
started: true,
global_sample: format!("{:.0}k", started.sample.get()/1000.),
global_second: format!("{:.1}s", started.usec.get()/1000.),
current_sample,
current_second,
beat: clock.timebase.format_beats_0(
clock.timebase.usecs_to_pulse(current_usec)
),
}
let global_sample = format!("{:.0}k", started.sample.get()/1000.);
let global_second = format!("{:.1}s", started.usec.get()/1000.);
let beat = clock.timebase.format_beats_1(clock.timebase.usecs_to_pulse(current_usec));
(global_sample, global_second, current_sample, current_second, beat)
} else {
Self {
color, focused, sr, bpm, ppq, chunk, latency,
started: false,
global_sample: format!("{:.0}k", clock.global.sample.get()/1000.),
global_second: format!("{:.1}s", clock.global.usec.get()/1000000.),
current_sample: 0.0,
current_second: 0.0,
beat: format!("000.0.00")
}
let global_sample = format!("{:.0}k", clock.global.sample.get()/1000.);
let global_second = format!("{:.1}s", clock.global.usec.get()/1000000.);
let current_sample = 0.0;
let current_second = 0.0;
let beat = format!("000.0.00");
(global_sample, global_second, current_sample, current_second, beat)
};
Self {
color, focused, sr, bpm, ppq, chunk, latency, started: false,
global_sample, global_second, current_sample, current_second, beat
}
}
}
render!(<Tui>|self: TransportView|{
let color = self.color;
struct Field<'a>(&'a str, &'a str, &'a ItemPalette);
render!(<Tui>|self: Field<'a>|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, ""),
]));
Tui::bg(color.base.rgb, Fill::w(row!([
Fixed::h(3, Tui::bg(color.base.rgb, Fill::w(row!([
//PlayPause(self.started), " ",
col!([
Field(" Beat", self.beat.as_str(), &color),
Field(" Time", format!("{:.1}s", self.current_second).as_str(), &color),
TransportField(" Beat", self.beat.as_str(), &color),
TransportField(" Time", format!("{:.1}s", self.current_second).as_str(), &color),
TransportField(" BPM", self.bpm.as_str(), &color),
]),
col!([
TransportField(" Rate", format!("{}", self.sr).as_str(), &color),
TransportField(" Chunk", format!("{}", self.chunk).as_str(), &color),
TransportField(" Lag", format!("{:.3}ms", self.latency).as_str(), &color),
]),
col!([
Field(" BPM", self.bpm.as_str(), &color),
//Field(" Smpl", format!("{:.1}k", self.current_sample).as_str(), &color),
Field(" Rate", format!("{}", self.sr).as_str(), &color),
//Field(" CPU%", format!("{:.1}ms", self.perf).as_str(), &color),
]),
col!([
Field(" Chunk", format!("{}", self.chunk).as_str(), &color),
Field(" Lag", format!("{:.3}ms", self.latency).as_str(), &color),
]),
])))
]))))
});
struct TransportField<'a>(&'a str, &'a str, &'a ItemPalette);
render!(<Tui>|self: TransportField<'a>|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)},