wip: compiles and runs (not enabled yet)

This commit is contained in:
🪞👃🪞 2025-01-04 11:19:37 +01:00
parent ac3827b8f3
commit 98d2107e4e
15 changed files with 440 additions and 357 deletions

View file

@ -2,7 +2,6 @@ use crate::*;
use ClockCommand::{Play, Pause, SetBpm, SetQuant, SetSync};
use FocusCommand::{Next, Prev};
use KeyCode::{Enter, Left, Right, Char};
/// Transport clock app.
pub struct TransportTui {
pub jack: Arc<RwLock<JackConnection>>,
@ -10,48 +9,13 @@ pub struct TransportTui {
}
has_clock!(|self: TransportTui|&self.clock);
audio!(|self: TransportTui, client, scope|ClockAudio(self).process(client, scope));
handle!(<Tui>|self: TransportTui, input|ClockCommand::execute_with_state(self, input.event()));
keymap!(TRANSPORT_KEYS = |state: TransportTui, input: Event| ClockCommand {
key(Char(' ')) =>
if state.clock().is_stopped() { Play(None) } else { Pause(None) },
shift(key(Char(' '))) =>
if state.clock().is_stopped() { Play(Some(0)) } else { Pause(Some(0)) }
});
// TODO:
//keymap!(TRANSPORT_BPM_KEYS = |state: Clock, input: Event| ClockCommand {
//key(Char(',')) => SetBpm(state.bpm().get() - 1.0),
//key(Char('.')) => SetBpm(state.bpm().get() + 1.0),
//key(Char('<')) => SetBpm(state.bpm().get() - 0.001),
//key(Char('>')) => SetBpm(state.bpm().get() + 0.001),
//});
//keymap!(TRANSPORT_QUANT_KEYS = |state: Clock, input: Event| ClockCommand {
//key(Char(',')) => SetQuant(state.quant.prev()),
//key(Char('.')) => SetQuant(state.quant.next()),
//key(Char('<')) => SetQuant(state.quant.prev()),
//key(Char('>')) => SetQuant(state.quant.next()),
//});
//keymap!(TRANSPORT_SYNC_KEYS = |sync: Clock, input: Event | ClockCommand {
//key(Char(',')) => SetSync(state.sync.prev()),
//key(Char('.')) => SetSync(state.sync.next()),
//key(Char('<')) => SetSync(state.sync.prev()),
//key(Char('>')) => SetSync(state.sync.next()),
//});
//keymap!(TRANSPORT_SEEK_KEYS = |state: Clock, input: Event| ClockCommand {
//key(Char(',')) => todo!("transport seek bar"),
//key(Char('.')) => todo!("transport seek bar"),
//key(Char('<')) => todo!("transport seek beat"),
//key(Char('>')) => todo!("transport seek beat"),
//});
render!(Tui: (self: TransportTui) => TransportView {
compact: false,
clock: &self.clock
});
impl TransportTui {
pub fn new (jack: &Arc<RwLock<JackConnection>>) -> Usually<Self> {
Ok(Self {
jack: jack.clone(),
clock: Clock::from(jack),
})
Ok(Self { jack: jack.clone(), clock: Clock::from(jack) })
}
}
@ -136,3 +100,35 @@ render!(Tui: (self: OutputStats) => Either(self.compact,
Bsp::e(Tui::fg(TuiTheme::g(255), format!("{:.3}ms", self.latency)), " latency"),
)));
handle!(<Tui>|self: TransportTui, input|ClockCommand::execute_with_state(self, input.event()));
keymap!(TRANSPORT_KEYS = |state: TransportTui, input: Event| ClockCommand {
key(Char(' ')) =>
if state.clock().is_stopped() { Play(None) } else { Pause(None) },
shift(key(Char(' '))) =>
if state.clock().is_stopped() { Play(Some(0)) } else { Pause(Some(0)) }
});
// TODO:
//keymap!(TRANSPORT_BPM_KEYS = |state: Clock, input: Event| ClockCommand {
//key(Char(',')) => SetBpm(state.bpm().get() - 1.0),
//key(Char('.')) => SetBpm(state.bpm().get() + 1.0),
//key(Char('<')) => SetBpm(state.bpm().get() - 0.001),
//key(Char('>')) => SetBpm(state.bpm().get() + 0.001),
//});
//keymap!(TRANSPORT_QUANT_KEYS = |state: Clock, input: Event| ClockCommand {
//key(Char(',')) => SetQuant(state.quant.prev()),
//key(Char('.')) => SetQuant(state.quant.next()),
//key(Char('<')) => SetQuant(state.quant.prev()),
//key(Char('>')) => SetQuant(state.quant.next()),
//});
//keymap!(TRANSPORT_SYNC_KEYS = |sync: Clock, input: Event | ClockCommand {
//key(Char(',')) => SetSync(state.sync.prev()),
//key(Char('.')) => SetSync(state.sync.next()),
//key(Char('<')) => SetSync(state.sync.prev()),
//key(Char('>')) => SetSync(state.sync.next()),
//});
//keymap!(TRANSPORT_SEEK_KEYS = |state: Clock, input: Event| ClockCommand {
//key(Char(',')) => todo!("transport seek bar"),
//key(Char('.')) => todo!("transport seek bar"),
//key(Char('<')) => todo!("transport seek beat"),
//key(Char('>')) => todo!("transport seek beat"),
//});

View file

@ -1,6 +1,48 @@
use crate::*;
use super::*;
use std::marker::ConstParamTy;
use tek_engine::Render;
use Item::*;
impl EdnLayout<Tui> for Groovebox {
fn get_bool (&self, item: &Item<&str>) -> bool { todo!() }
fn get_unit (&self, item: &Item<&str>) -> u16 {
match item {
Sym(":sample-h") => if self.compact { 0 } else { 5 },
Sym(":samples-w") => if self.compact { 4 } else { 11 },
Sym(":samples-y") => if self.compact { 1 } else { 0 },
Sym(":pool-w") => if self.compact { 5 } else {
let w = self.size.w();
if w > 60 { 20 } else if w > 40 { 15 } else { 10 }
},
_ => 0
}
}
fn get_content (&self, item: &Item<&str>) -> Box<dyn Render<Tui> + '_> {
match item {
Sym(":input-meter-l") => Box::new(Meter("L/", self.sampler.input_meter[0])),
Sym(":input-meter-r") => Box::new(Meter("R/", self.sampler.input_meter[1])),
Sym(":transport") => Box::new(TransportView::new(true, &self.player.clock)),
Sym(":clip-play") => Box::new(ClipSelected::play_phrase(&self.player)),
Sym(":clip-next") => Box::new(ClipSelected::next_phrase(&self.player)),
Sym(":clip-edit") => Box::new(MidiEditClip(&self.editor)),
Sym(":edit-stat") => Box::new(MidiEditStatus(&self.editor)),
Sym(":pool-view") => Box::new(PoolView(self.compact, &self.pool)),
Sym(":midi-view") => Box::new(&self.editor),
Sym(":sample-view") => Box::new(SampleViewer::from_sampler(
&self.sampler, self.editor.note_point())),
Sym(":sample-stat") => Box::new(SamplerStatus(
&self.sampler, self.editor.note_point())),
Sym(":samples-view") => Box::new(SampleList::new(
self.compact, &self.sampler, &self.editor)),
_ => Box::new(())
}
}
}
render!(Tui: (self: Groovebox) => self.size.of(
Bsp::s(self.toolbar_view(),
Bsp::n(self.selector_view(),
@ -8,109 +50,120 @@ render!(Tui: (self: Groovebox) => self.size.of(
Bsp::n(self.status_view(),
Bsp::w(self.pool_view(), Fill::xy(Bsp::e(self.sampler_view(), &self.editor)))))))));
const GROOVEBOX_EDN: &'static str = include_str!("groovebox.edn");
impl Content<Tui> for Groovebox {
fn content (&self) -> impl Content<Tui> {
EdnView::parse(self.edn.as_slice())
impl Groovebox {
fn toolbar_view (&self) -> impl Content<Tui> + use<'_> {
Fill::x(Fixed::y(2, lay!(
Align::w(Meter("L/", self.sampler.input_meter[0])),
Align::e(Meter("R/", self.sampler.input_meter[1])),
Align::x(TransportView::new(true, &self.player.clock)),
)))
}
fn selector_view (&self) -> impl Content<Tui> + use<'_> {
row!(
ClipSelected::play_phrase(&self.player),
ClipSelected::next_phrase(&self.player),
MidiEditClip(&self.editor),
MidiEditStatus(&self.editor),
)
}
fn sample_view (&self) -> impl Content<Tui> + use<'_> {
let note_pt = self.editor.note_point();
let sample_h = if self.compact { 0 } else { 5 };
Max::y(sample_h, Fill::xy(
SampleViewer::from_sampler(&self.sampler, note_pt)))
}
fn status_view (&self) -> impl Content<Tui> + use<'_> {
let note_pt = self.editor.note_point();
Align::w(Fixed::y(1, SamplerStatus(&self.sampler, note_pt)))
}
fn pool_view (&self) -> impl Content<Tui> + use<'_> {
let w = self.size.w();
let pool_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 };
Fixed::x(if self.compact { 5 } else { pool_w },
PoolView(self.compact, &self.pool))
}
fn sampler_view (&self) -> impl Content<Tui> + use<'_> {
let sampler_w = if self.compact { 4 } else { 11 };
let sampler_y = if self.compact { 1 } else { 0 };
Fixed::x(sampler_w, Push::y(sampler_y, Fill::y(
SampleList::new(self.compact, &self.sampler, &self.editor))))
}
}
macro_rules! edn_context {
($Struct:ident |$l:lifetime, $state:ident| {
$($key:literal = $field:ident: $Type:ty => $expr:expr,)*
}) => {
//render!(Tui: (self: Groovebox) => self.size.of(
//Bsp::s(self.toolbar_view(),
//Bsp::n(self.selector_view(),
//Bsp::n(self.sample_view(),
//Bsp::n(self.status_view(),
//Bsp::w(self.pool_view(), Fill::xy(Bsp::e(self.sampler_view(), &self.editor)))))))));
#[derive(Default)]
pub struct EdnView<$l> { $($field: Option<$Type>),* }
//const GROOVEBOX_EDN: &'static str = include_str!("groovebox.edn");
impl<$l> EdnView<$l> {
pub fn parse <'e> (edn: &[Edn<'e>]) -> impl Fn(&$Struct) + use<'e> {
let imports = Self::imports_all(edn);
move |state| {
let mut context = EdnView::default();
for import in imports.iter() {
context.import(state, import)
}
}
}
fn imports_all <'e> (edn: &[Edn<'e>]) -> Vec<&'e str> {
let mut imports = vec![];
for edn in edn.iter() {
for import in Self::imports_one(edn) {
imports.push(import);
}
}
imports
}
fn imports_one <'e> (edn: &Edn<'e>) -> Vec<&'e str> {
match edn {
Edn::Symbol(import) => vec![import],
Edn::List(edn) => Self::imports_all(edn.as_slice()),
_ => vec![],
}
}
pub fn import (&mut self, $state: &$l$Struct, key: &str) {
match key {
$($key => self.$field = Some($expr),)*
_ => {}
}
}
}
}
}
edn_context!(Groovebox |'a, state| {
":input-meter-l" = input_meter_l: Meter<'a> =>
Meter("L/", state.sampler.input_meter[0]),
":input-meter-r" = input_meter_r: Meter<'a> =>
Meter("R/", state.sampler.input_meter[1]),
":transport" = transport: TransportView<'a> =>
TransportView::new(true, &state.player.clock),
":clip-play" = clip_play: ClipSelected =>
ClipSelected::play_phrase(&state.player),
":clip-next" = clip_next: ClipSelected =>
ClipSelected::next_phrase(&state.player),
":clip-edit" = clip_edit: MidiEditClip<'a> =>
MidiEditClip(&state.editor),
":edit-stat" = edit_stat: MidiEditStatus<'a> =>
MidiEditStatus(&state.editor),
":sample-h" = sample_h: u16 =>
if state.compact { 0 } else { 5 },
":sample-view" = sample_view: SampleViewer =>
SampleViewer::from_sampler(&state.sampler, state.editor.note_point()),
":sample-stat" = sample_stat: SamplerStatus<'a> =>
SamplerStatus(&state.sampler, state.editor.note_point()),
":pool-w" = pool_w: u16 => if state.compact { 5 } else {
let w = state.size.w();
if w > 60 { 20 } else if w > 40 { 15 } else { 10 } },
":pool-view" = pool_view: PoolView<'a> =>
PoolView(state.compact, &state.pool),
":samples-w" = samples_w: u16 =>
if state.compact { 4 } else { 11 },
":samples-y" = samples_y: u16 =>
if state.compact { 1 } else { 0 },
":samples-view" = samples_view: SampleList<'a> => SampleList::new(
state.compact, &state.sampler, &state.editor),
":midi-view" = midi_view: &'a MidiEditor =>
&state.editor,
});
//impl Groovebox {
//fn status_view (&self) -> impl Content<Tui> + use<'_> {
//let note_pt = self.editor.note_point();
//Align::w(Fixed::y(1, ))
//}
//fn pool_view (&self) -> impl Content<Tui> + use<'_> {
//let w = self.size.w();
//let pool_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 };
//Fixed::x(if self.compact { 5 } else { pool_w },
//)
//}
//fn sampler_view (&self) -> impl Content<Tui> + use<'_> {
//let sampler_w = if self.compact { 4 } else { 11 };
//let sampler_y = if self.compact { 1 } else { 0 };
//Fixed::x(sampler_w, Push::y(sampler_y, Fill::y(
//SampleList::new(self.compact, &self.sampler, &self.editor))))
//impl Content<Tui> for Groovebox {
//fn content (&self) -> impl Content<Tui> {
//EdnView::parse(self.edn.as_slice())
//}
//}
//macro_rules! edn_context {
//($Struct:ident |$l:lifetime, $state:ident| {
//$($key:literal = $field:ident: $Type:ty => $expr:expr,)*
//}) => {
//#[derive(Default)]
//pub struct EdnView<$l> { $($field: Option<$Type>),* }
//impl<$l> EdnView<$l> {
//pub fn parse <'e> (edn: &[Edn<'e>]) -> impl Fn(&$Struct) + use<'e> {
//let imports = Self::imports_all(edn);
//move |state| {
//let mut context = EdnView::default();
//for import in imports.iter() {
//context.import(state, import)
//}
//}
//}
//fn imports_all <'e> (edn: &[Edn<'e>]) -> Vec<&'e str> {
//let mut imports = vec![];
//for edn in edn.iter() {
//for import in Self::imports_one(edn) {
//imports.push(import);
//}
//}
//imports
//}
//fn imports_one <'e> (edn: &Edn<'e>) -> Vec<&'e str> {
//match edn {
//Edn::Symbol(import) => vec![import],
//Edn::List(edn) => Self::imports_all(edn.as_slice()),
//_ => vec![],
//}
//}
//pub fn import (&mut self, $state: &$l$Struct, key: &str) {
//match key {
//$($key => self.$field = Some($expr),)*
//_ => {}
//}
//}
//}
//}
//}
////impl Groovebox {
////fn status_view (&self) -> impl Content<Tui> + use<'_> {
////let note_pt = self.editor.note_point();
////Align::w(Fixed::y(1, ))
////}
////fn pool_view (&self) -> impl Content<Tui> + use<'_> {
////let w = self.size.w();
////let pool_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 };
////Fixed::x(if self.compact { 5 } else { pool_w },
////)
////}
////fn sampler_view (&self) -> impl Content<Tui> + use<'_> {
////let sampler_w = if self.compact { 4 } else { 11 };
////let sampler_y = if self.compact { 1 } else { 0 };
////Fixed::x(sampler_w, Push::y(sampler_y, Fill::y(
////SampleList::new(self.compact, &self.sampler, &self.editor))))
////}
////}

View file

@ -13,7 +13,7 @@ pub(crate) use ::tek_layout::{
tek_engine::{
Usually, Perhaps,
Engine, Size, Area,
Output, Content, Thunk, render,
Output, Content, Render, Thunk, render,
Input, Handle, handle,
kexp, kpat,
tui::{

View file

@ -21,11 +21,8 @@ pub trait TuiStyle {
impl TuiStyle for Tui {}
pub struct Bold<W: Content<Tui>>(pub bool, W);
impl<W: Content<Tui>> Content<Tui> for Bold<W> {
fn content (&self) -> impl Content<Tui> {
Some(&self.1)
}
fn content (&self) -> impl Render<Tui> { &self.1 }
fn render (&self, to: &mut TuiOut) {
to.fill_bold(to.area(), self.0);
self.1.render(to)
@ -33,11 +30,8 @@ impl<W: Content<Tui>> Content<Tui> for Bold<W> {
}
pub struct Foreground<W: Content<Tui>>(pub Color, W);
impl<W: Content<Tui>> Content<Tui> for Foreground<W> {
fn content (&self) -> impl Content<Tui> {
Some(&self.1)
}
fn content (&self) -> impl Render<Tui> { &self.1 }
fn render (&self, to: &mut TuiOut) {
to.fill_fg(to.area(), self.0);
self.1.render(to)
@ -45,11 +39,8 @@ impl<W: Content<Tui>> Content<Tui> for Foreground<W> {
}
pub struct Background<W: Content<Tui>>(pub Color, W);
impl<W: Content<Tui>> Content<Tui> for Background<W> {
fn content (&self) -> impl Content<Tui> {
Some(&self.1)
}
fn content (&self) -> impl Render<Tui> { &self.1 }
fn render (&self, to: &mut TuiOut) {
to.fill_bg(to.area(), self.0);
self.1.render(to)
@ -57,11 +48,8 @@ impl<W: Content<Tui>> Content<Tui> for Background<W> {
}
pub struct Styled<T: Content<Tui>>(pub Option<Style>, pub T);
impl Content<Tui> for Styled<&str> {
fn content (&self) -> impl Content<Tui> {
Some(&self.1)
}
fn content (&self) -> impl Render<Tui> { &self.1 }
fn render (&self, to: &mut TuiOut) {
// FIXME
let [x, y, ..] = to.area();
@ -71,16 +59,16 @@ impl Content<Tui> for Styled<&str> {
}
//pub trait TuiStyle: Content<Tui> + Sized {
//fn fg (self, color: Color) -> impl Content<Tui> {
//fn fg (self, color: Color) -> impl Render<Tui> {
//Layers::new(move |add|{ add(&Foreground(color))?; add(&self) })
//}
//fn bg (self, color: Color) -> impl Content<Tui> {
//fn bg (self, color: Color) -> impl Render<Tui> {
//Layers::new(move |add|{ add(&Background(color))?; add(&self) })
//}
//fn bold (self, on: bool) -> impl Content<Tui> {
//fn bold (self, on: bool) -> impl Render<Tui> {
//Layers::new(move |add|{ add(&Bold(on))?; add(&self) })
//}
//fn border <S: BorderStyle> (self, style: S) -> impl Content<Tui> {
//fn border <S: BorderStyle> (self, style: S) -> impl Render<Tui> {
//Bordered(style, self)
//}
//}
@ -91,7 +79,7 @@ impl Content<Tui> for Styled<&str> {
//}
//impl<S: BorderStyle, W: Content<Tui>> Content<Tui> for Bordered<S, W> {
//fn content (&self) -> impl Content<Tui> {
//fn content (&self) -> impl Render<Tui> {
//let content: &dyn Content<Tui> = &self.1;
//lay! { content.padding_xy(1, 1), Border(self.0) }.fill_xy()
//}