mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
wip: start replacing EdnViewData with EdnProvide
This commit is contained in:
parent
fc06fb863b
commit
1ff35baea9
8 changed files with 151 additions and 113 deletions
|
|
@ -2,21 +2,51 @@ use crate::*;
|
|||
use EdnItem::*;
|
||||
|
||||
#[macro_export] macro_rules! edn_command {
|
||||
($Command:ty : |$state:ident:$State:ty| {
|
||||
$(($key:literal [$($arg:ident : $type:ty),*] $command:expr))*
|
||||
}) => {
|
||||
($Command:ty : |$state:ident:$State:ty| { $((
|
||||
// identifier
|
||||
$key:literal [
|
||||
// named parameters
|
||||
$(
|
||||
// argument name
|
||||
$arg:ident
|
||||
// if type is not provided defaults to EdnItem
|
||||
$(
|
||||
// type:name separator
|
||||
:
|
||||
// argument type
|
||||
$type:ty
|
||||
)?
|
||||
),*
|
||||
// rest of parameters
|
||||
$(, ..$rest:ident)?
|
||||
]
|
||||
// bound command:
|
||||
$command:expr
|
||||
))* }) => {
|
||||
impl EdnCommand<$State> for $Command {
|
||||
fn from_edn <'a> ($state: &$State, head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self {
|
||||
match (head, tail) {
|
||||
$((EdnItem::Key($key), [$($arg),*]) => {
|
||||
$(let $arg: $type = EdnProvide::<$type>::get_or_fail($state, $arg);)*
|
||||
$command
|
||||
},)*
|
||||
_ => panic!("no such command")
|
||||
}
|
||||
$(if let (EdnItem::Key($key), [ // if the identifier matches
|
||||
// bind argument ids
|
||||
$($arg),*
|
||||
// bind rest parameters
|
||||
$(, $rest @ ..)?
|
||||
]) = (head, tail) {
|
||||
$(
|
||||
$(let $arg: Option<$type> = EdnProvide::<$type>::get($state, $arg);)?
|
||||
)*
|
||||
//$(edn_command!(@bind $state => $arg $(?)? : $type);)*
|
||||
return $command
|
||||
})*
|
||||
panic!("no such command")
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
(@bind $state:ident =>$arg:ident ? : $type:ty) => {
|
||||
let $arg: Option<$type> = EdnProvide::<$type>::get($state, $arg);
|
||||
};
|
||||
(@bind $state:ident => $arg:ident : $type:ty) => {
|
||||
let $arg: $type = EdnProvide::<$type>::get_or_fail($state, $arg);
|
||||
};
|
||||
}
|
||||
|
||||
/// Turns an EDN symbol sequence into a command enum variant.
|
||||
|
|
|
|||
|
|
@ -23,3 +23,12 @@ pub trait EdnProvide<U> {
|
|||
self.get(edn).expect("no value")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EdnProvide<U>, U> EdnProvide<U> for &T {
|
||||
fn get <S: AsRef<str>> (&self, edn: &EdnItem<S>) -> Option<U> {
|
||||
(*self).get(edn)
|
||||
}
|
||||
fn get_or_fail <S: AsRef<str>> (&self, edn: &EdnItem<S>) -> U {
|
||||
(*self).get_or_fail(edn)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,9 +12,13 @@ edn_provide!(usize: |self: MidiEditor|{
|
|||
":time-zoom" => self.time_zoom().get(),
|
||||
});
|
||||
edn_command!(MidiEditCommand: |state: MidiEditor| {
|
||||
("note/put" [a: bool] Self::PutNote)
|
||||
("note/del" [a: bool] Self::PutNote)
|
||||
("note/dur" [a: usize] Self::SetNoteCursor(a))
|
||||
("note/put" [a: bool] Self::PutNote)
|
||||
("note/del" [a: bool] Self::PutNote)
|
||||
("note/pos" [a: usize] Self::SetNoteCursor(a.expect("no note cursor")))
|
||||
("note/len" [a: usize] Self::SetNoteLength(a.expect("no note length")))
|
||||
("time/pos" [a: usize] Self::SetTimeCursor(a.expect("no time cursor")))
|
||||
("time/zoom" [a: usize] Self::SetTimeZoom(a.expect("no time zoom")))
|
||||
("time/lock" [a: bool] Self::SetTimeLock(a.expect("no time lock")))
|
||||
});
|
||||
//impl EdnCommand<MidiEditor> for MidiEditCommand {
|
||||
//fn from_edn <'a> (state: &MidiEditor, head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self {
|
||||
|
|
|
|||
|
|
@ -78,10 +78,12 @@ fn match_exp <'a, E: Output + 'a, State: EdnViewData<E>> (
|
|||
) -> Box<dyn Render<E> + 'a> {
|
||||
match (head, tail) {
|
||||
|
||||
(Key("when"), [c, a]) => When(s.get_bool(c.to_ref()),
|
||||
(Key("when"), [c, a]) => When(
|
||||
s.get_bool(c.to_ref()),
|
||||
s.get_content(a.to_ref())).boxed(),
|
||||
|
||||
(Key("either"),[c, a, b]) => Either(s.get_bool(c.to_ref()),
|
||||
(Key("either"),[c, a, b]) => Either(
|
||||
s.get_bool(c.to_ref()),
|
||||
s.get_content(a.to_ref()),
|
||||
s.get_content(b.to_ref())).boxed(),
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use crate::*;
|
||||
use EdnItem::*;
|
||||
use ClockCommand::{Play, Pause};
|
||||
use self::ArrangerCommand as Cmd;
|
||||
pub const TRACK_MIN_WIDTH: usize = 9;
|
||||
command!(|self: ClipCommand, state: App|match self { _ => todo!("clip command") });
|
||||
command!(|self: SceneCommand, state: App|match self { _ => todo!("scene command") });
|
||||
|
|
|
|||
|
|
@ -2,24 +2,16 @@ use crate::*;
|
|||
use EdnItem::*;
|
||||
use ClockCommand::{Play, Pause};
|
||||
use KeyCode::{Tab, Char};
|
||||
use SequencerCommand as SeqCmd;
|
||||
use GrooveboxCommand as GrvCmd;
|
||||
use ArrangerCommand as ArrCmd;
|
||||
use SamplerCommand as SmplCmd;
|
||||
use MidiEditCommand as EditCmd;
|
||||
use PoolClipCommand as PoolCmd;
|
||||
|
||||
handle!(TuiIn: |self: App, input| Ok(None));
|
||||
//handle!(TuiIn: |self: Sequencer, input|SequencerCommand::execute_with_state(self, input.event()));
|
||||
//handle!(TuiIn: |self: Groovebox, input|GrooveboxCommand::execute_with_state(self, input.event()));
|
||||
//handle!(TuiIn: |self: Arranger, input|ArrangerCommand::execute_with_state(self, input.event()));
|
||||
|
||||
#[derive(Clone, Debug)] pub enum AppCommand {
|
||||
Clear,
|
||||
Clip(ClipCommand),
|
||||
Clock(ClockCommand),
|
||||
Color(ItemPalette),
|
||||
Compact(bool),
|
||||
Compact(Option<bool>),
|
||||
Editor(MidiEditCommand),
|
||||
Enqueue(Option<Arc<RwLock<MidiClip>>>),
|
||||
History(isize),
|
||||
|
|
@ -29,46 +21,29 @@ handle!(TuiIn: |self: App, input| Ok(None));
|
|||
Select(ArrangerSelection),
|
||||
StopAll,
|
||||
Track(TrackCommand),
|
||||
Zoom(usize),
|
||||
}
|
||||
impl EdnCommand<App> for AppCommand {
|
||||
fn from_edn <'a> (state: &App, head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self {
|
||||
match (head, tail) {
|
||||
(Key("clear"), []) => Self::Clear,
|
||||
(Key("stop-all"), []) => Self::StopAll,
|
||||
|
||||
(Key("color"), [a]) => Self::Color(ItemPalette::default()),
|
||||
(Key("compact"), [a]) => Self::Compact(true),
|
||||
(Key("enqueue"), [a]) => Self::Enqueue(None),
|
||||
(Key("history"), [a]) => Self::History(0),
|
||||
(Key("select"), [a]) => Self::Select(ArrangerSelection::Mix),
|
||||
(Key("zoom"), [a]) => Self::Zoom(0),
|
||||
|
||||
(Key("clock"), [a, b @ ..]) => Self::Clock(
|
||||
ClockCommand::from_edn(state, &a.to_ref(), b)),
|
||||
(Key("track"), [a, b @ ..]) => Self::Track(
|
||||
TrackCommand::from_edn(state, &a.to_ref(), b)),
|
||||
(Key("scene"), [a, b @ ..]) => Self::Scene(
|
||||
SceneCommand::from_edn(state, &a.to_ref(), b)),
|
||||
(Key("clip"), [a, b @ ..]) => Self::Clip(
|
||||
ClipCommand::from_edn(state, &a.to_ref(), b)),
|
||||
|
||||
(Key("pool"), [a, b @ ..]) if let Some(pool) = state.pool.as_ref() =>
|
||||
Self::Pool(PoolCommand::from_edn(pool, &a.to_ref(), b)),
|
||||
(Key("editor"), [a, b @ ..]) if let Some(editor) = state.editor.as_ref() =>
|
||||
Self::Editor(MidiEditCommand::from_edn(editor, &a.to_ref(), b)),
|
||||
(Key("sampler"), [a, b @ ..]) if let Some(sampler) = state.sampler.as_ref() =>
|
||||
Self::Sampler(SamplerCommand::from_edn(sampler, &a.to_ref(), b)),
|
||||
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
Zoom(Option<usize>),
|
||||
}
|
||||
edn_command!(AppCommand: |state: App| {
|
||||
("clear" [] Self::Clear)
|
||||
("stop-all" [] Self::StopAll)
|
||||
("compact" [c: bool ] Self::Compact(c))
|
||||
("color" [c: Color] Self::Color(c.map(ItemPalette::from).unwrap_or_default()))
|
||||
("history" [d: isize] Self::History(d.unwrap_or(0)))
|
||||
("zoom" [z: usize] Self::Zoom(z))
|
||||
("clock" [a, ..b] Self::Clock(ClockCommand::from_edn(state, &a.to_ref(), b)))
|
||||
("track" [a, ..b] Self::Track(TrackCommand::from_edn(state, &a.to_ref(), b)))
|
||||
("scene" [a, ..b] Self::Scene(SceneCommand::from_edn(state, &a.to_ref(), b)))
|
||||
("clip" [a, ..b] Self::Clip(ClipCommand::from_edn(state, &a.to_ref(), b)))
|
||||
("pool" [a, ..b] Self::Pool(PoolCommand::from_edn(state.pool.as_ref().expect("no pool"), &a.to_ref(), b)))
|
||||
("editor" [a, ..b] Self::Editor(MidiEditCommand::from_edn(state.editor.as_ref().expect("no editor"), &a.to_ref(), b)))
|
||||
("sampler" [a, ..b] Self::Sampler(SamplerCommand::from_edn(state.sampler.as_ref().expect("no sampler"), &a.to_ref(), b)))
|
||||
("select" [s: ArrangerSelection] Self::Select(s.expect("no selection")))
|
||||
("enqueue" [c: Arc<RwLock<MidiClip>>] Self::Enqueue(c))
|
||||
});
|
||||
command!(|self: AppCommand, state: App|match self {
|
||||
Self::Clear => { todo!() },
|
||||
Self::Zoom(_) => { todo!(); },
|
||||
Self::History(delta) => { todo!("undo/redo") },
|
||||
|
||||
Self::Select(s) => { state.selected = s; None },
|
||||
Self::Clock(cmd) => cmd.delegate(state, Self::Clock)?,
|
||||
Self::Scene(cmd) => match cmd {
|
||||
|
|
@ -111,7 +86,6 @@ command!(|self: AppCommand, state: App|match self {
|
|||
},
|
||||
_ => None
|
||||
}.map(Self::Clip),
|
||||
|
||||
Self::Editor(cmd) =>
|
||||
state.editor.as_mut().map(|editor|cmd.delegate(editor, Self::Editor)).transpose()?.flatten(),
|
||||
Self::Sampler(cmd) =>
|
||||
|
|
@ -127,7 +101,6 @@ command!(|self: AppCommand, state: App|match self {
|
|||
state.color = palette;
|
||||
Some(Self::Color(old))
|
||||
},
|
||||
|
||||
Self::Pool(cmd) => if let Some(pool) = state.pool.as_mut() {
|
||||
let undo = cmd.clone().delegate(pool, Self::Pool)?;
|
||||
if let Some(editor) = state.editor.as_mut() {
|
||||
|
|
@ -143,45 +116,60 @@ command!(|self: AppCommand, state: App|match self {
|
|||
} else {
|
||||
None
|
||||
},
|
||||
|
||||
Self::Compact(compact) => if state.compact != compact {
|
||||
state.compact = compact;
|
||||
Some(Self::Compact(!compact))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
Self::Compact(compact) => match compact {
|
||||
Some(compact) => {
|
||||
if state.compact != compact {
|
||||
state.compact = compact;
|
||||
Some(Self::Compact(Some(!compact)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
None => {
|
||||
state.compact = !state.compact;
|
||||
Some(Self::Compact(Some(!state.compact)))
|
||||
}
|
||||
}
|
||||
});
|
||||
#[derive(Clone, Debug)] pub enum SequencerCommand {
|
||||
Compact(bool),
|
||||
History(isize),
|
||||
Clock(ClockCommand),
|
||||
Pool(PoolCommand),
|
||||
Editor(MidiEditCommand),
|
||||
Enqueue(Option<Arc<RwLock<MidiClip>>>),
|
||||
}
|
||||
#[derive(Clone, Debug)] pub enum GrooveboxCommand {
|
||||
Compact(bool),
|
||||
History(isize),
|
||||
Clock(ClockCommand),
|
||||
Pool(PoolCommand),
|
||||
Editor(MidiEditCommand),
|
||||
Enqueue(Option<Arc<RwLock<MidiClip>>>),
|
||||
Sampler(SamplerCommand),
|
||||
}
|
||||
#[derive(Clone, Debug)] pub enum ArrangerCommand {
|
||||
History(isize),
|
||||
Color(ItemPalette),
|
||||
Clock(ClockCommand),
|
||||
Scene(SceneCommand),
|
||||
Track(TrackCommand),
|
||||
Clip(ClipCommand),
|
||||
Select(ArrangerSelection),
|
||||
Zoom(usize),
|
||||
Pool(PoolCommand),
|
||||
Editor(MidiEditCommand),
|
||||
StopAll,
|
||||
Clear,
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//handle!(TuiIn: |self: Sequencer, input|SequencerCommand::execute_with_state(self, input.event()));
|
||||
//handle!(TuiIn: |self: Groovebox, input|GrooveboxCommand::execute_with_state(self, input.event()));
|
||||
//handle!(TuiIn: |self: Arranger, input|ArrangerCommand::execute_with_state(self, input.event()));
|
||||
//use SequencerCommand as SeqCmd;
|
||||
//use GrooveboxCommand as GrvCmd;
|
||||
//use ArrangerCommand as ArrCmd;
|
||||
//#[derive(Clone, Debug)] pub enum SequencerCommand {
|
||||
//Compact(bool),
|
||||
//History(isize),
|
||||
//Clock(ClockCommand),
|
||||
//Pool(PoolCommand),
|
||||
//Editor(MidiEditCommand),
|
||||
//Enqueue(Option<Arc<RwLock<MidiClip>>>),
|
||||
//}
|
||||
//#[derive(Clone, Debug)] pub enum GrooveboxCommand {
|
||||
//Compact(bool),
|
||||
//History(isize),
|
||||
//Clock(ClockCommand),
|
||||
//Pool(PoolCommand),
|
||||
//Editor(MidiEditCommand),
|
||||
//Enqueue(Option<Arc<RwLock<MidiClip>>>),
|
||||
//Sampler(SamplerCommand),
|
||||
//}
|
||||
//#[derive(Clone, Debug)] pub enum ArrangerCommand {
|
||||
//History(isize),
|
||||
//Color(ItemPalette),
|
||||
//Clock(ClockCommand),
|
||||
//Scene(SceneCommand),
|
||||
//Track(TrackCommand),
|
||||
//Clip(ClipCommand),
|
||||
//Select(ArrangerSelection),
|
||||
//Zoom(usize),
|
||||
//Pool(PoolCommand),
|
||||
//Editor(MidiEditCommand),
|
||||
//StopAll,
|
||||
//Clear,
|
||||
//}
|
||||
|
||||
//command!(|self: SequencerCommand, state: Sequencer|match self {
|
||||
//Self::Clock(cmd) => cmd.delegate(state, Self::Clock)?,
|
||||
|
|
|
|||
|
|
@ -94,6 +94,21 @@ has_size!(<TuiOut>|self: App|&self.size);
|
|||
has_clock!(|self: App|&self.clock);
|
||||
has_clips!(|self: App|self.pool.as_ref().expect("no clip pool").clips);
|
||||
has_editor!(|self: App|self.editor.as_ref().expect("no editor"));
|
||||
edn_provide!(u16: |self: App|{
|
||||
":sample-h" => if self.compact() { 0 } else { 5 },
|
||||
":samples-w" => if self.compact() { 4 } else { 11 },
|
||||
":samples-y" => if self.compact() { 1 } else { 0 },
|
||||
":pool-w" => if self.compact() { 5 } else {
|
||||
let w = self.size.w();
|
||||
if w > 60 { 20 } else if w > 40 { 15 } else { 10 }
|
||||
}
|
||||
});
|
||||
edn_provide!(Color: |self: App| { _ => return None });
|
||||
edn_provide!(usize: |self: App| { _ => return None });
|
||||
edn_provide!(isize: |self: App| { _ => return None });
|
||||
edn_provide!(bool: |self: App| { _ => return None });
|
||||
edn_provide!(ArrangerSelection: |self: App| { _ => return None });
|
||||
edn_provide!(Arc<RwLock<MidiClip>>: |self: App| { _ => return None });
|
||||
|
||||
//#[derive(Default)] pub struct Sequencer {
|
||||
//pub jack: Arc<RwLock<JackConnection>>,
|
||||
|
|
|
|||
|
|
@ -21,21 +21,11 @@ impl EdnViewData<TuiOut> for &App {
|
|||
}
|
||||
}
|
||||
fn get_unit (&self, item: EdnItem<&str>) -> u16 {
|
||||
use EdnItem::*;
|
||||
match item.to_str() {
|
||||
":sample-h" => if self.compact() { 0 } else { 5 },
|
||||
":samples-w" => if self.compact() { 4 } else { 11 },
|
||||
":samples-y" => if self.compact() { 1 } else { 0 },
|
||||
":pool-w" => if self.compact() { 5 } else {
|
||||
let w = self.size.w();
|
||||
if w > 60 { 20 } else if w > 40 { 15 } else { 10 }
|
||||
},
|
||||
_ => 0
|
||||
}
|
||||
EdnProvide::<u16>::get_or_fail(self, &item)
|
||||
}
|
||||
}
|
||||
impl App {
|
||||
fn compact (&self) -> bool { false }
|
||||
pub fn compact (&self) -> bool { false }
|
||||
fn toolbar (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
Fill::x(Fixed::y(2, Align::x(ClockView::new(true, &self.clock))))
|
||||
}
|
||||
|
|
@ -337,6 +327,7 @@ fn cell <T: Content<TuiOut>> (color: ItemPalette, field: T) -> impl Content<TuiO
|
|||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//render!(TuiOut: (self: Sequencer) => self.size.of(EdnView::from_source(self, Self::EDN)));
|
||||
//impl EdnViewData<TuiOut> for &Sequencer {
|
||||
//fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue