mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
more updates to space and transport
This commit is contained in:
parent
9fa858f226
commit
304ce35cbb
6 changed files with 139 additions and 140 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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!([
|
||||
|
|
|
|||
126
src/space.rs
126
src/space.rs
|
|
@ -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>> {
|
||||
|
|
|
|||
|
|
@ -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<()>
|
||||
|
|
|
|||
|
|
@ -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)},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue