diff --git a/crates/app/app.rs b/crates/app/app.rs index b371a796..9731d67e 100644 --- a/crates/app/app.rs +++ b/crates/app/app.rs @@ -41,11 +41,6 @@ use std::fmt::Write; use ::tengri::tui::ratatui::prelude::Position; use xdg::BaseDirectories; mod app_dsl; pub use self::app_dsl::*; -macro_rules!dsl_sym( - (|$state:ident:$State:ty| -> $type:ty {$($lit:literal => $exp:expr),* $(,)?})=>{ - impl<'t> DslSymNs<'t, $type> for $State { - const NS: DslNs<'t, fn (&'t $State)->$type> = - DslNs(&[$(($lit, |$state: &$State|$exp)),*]); } }); mod app_view; pub use self::app_view::*; mod app_ctrl; pub use self::app_ctrl::*; mod app_jack; pub use self::app_jack::*; diff --git a/crates/app/app_ctrl.rs b/crates/app/app_ctrl.rs index b24dc862..a1240359 100644 --- a/crates/app/app_ctrl.rs +++ b/crates/app/app_ctrl.rs @@ -1,4 +1,6 @@ use crate::*; +#[derive(Debug)] +pub enum AppCommand {} handle!(TuiIn:|self: App, input|{ panic!("{input:?}"); //Ok(if let Some(binding) = self.profile.as_ref() @@ -12,7 +14,7 @@ handle!(TuiIn:|self: App, input|{ //} else { //None //}) -}) +}); dsl_sym!(|app: App| -> isize { ":_isize_stub" => -1 }); @@ -70,91 +72,108 @@ dsl_sym!(|app: App| -> Selection { }); dsl_sym!(|app: App| -> Option { ":editor/pitch" => Some((app.editor().as_ref().map(|e|e.get_note_pos()).unwrap() as u8).into()) -}) +}); dsl_sym!(|app: App| -> Option { ":selected/scene" => app.selection().scene(), ":selected/track" => app.selection().track(), }); dsl_sym!(|app: App| -> Option>> { ":selected/clip" => if let Selection::TrackClip { track, scene } = app.selection() { - app.scenes()[*scene].clips[*track].clone(), + app.scenes()[*scene].clips[*track].clone() } else { None } }); -dsl_bind!(AppCommand: App { - enqueue = |app, clip: Option>>| { todo!() }; - history = |app, delta: isize| { todo!() }; - zoom = |app, zoom: usize| { todo!() }; - stop_all = |app| { app.tracks_stop_all(); Ok(None) }; - //dialog = |app, command: DialogCommand| - //Ok(command.delegate(&mut app.dialog, |c|Self::Dialog{command: c})?); - project = |app, command: ArrangementCommand| - Ok(command.delegate(&mut app.project, |c|Self::Project{command: c})?); - clock = |app, command: ClockCommand| - Ok(command.execute(app.clock_mut())?.map(|c|Self::Clock{command: c})); - sampler = |app, command: SamplerCommand| - Ok(app.project.sampler_mut().map(|s|command.delegate(s, |command|Self::Sampler{command})) - .transpose()?.flatten()); - pool = |app, command: PoolCommand| { - let undo = command.clone().delegate(&mut app.pool, |command|AppCommand::Pool{command})?; - // update linked editor after pool action - match command { - // autoselect: automatically load selected clip in editor - PoolCommand::Select { .. } | - // autocolor: update color in all places simultaneously - PoolCommand::Clip { command: PoolClipCommand::SetColor { .. } } => { - let clip = app.pool.clip().clone(); - app.editor_mut().map(|editor|editor.set_clip(clip.as_ref())) - }, - _ => None - }; - Ok(undo) - }; - select = |app, selection: Selection| { - *app.project.selection_mut() = selection; - //todo! - //if let Some(ref mut editor) = app.editor_mut() { - //editor.set_clip(match selection { - //Selection::TrackClip { track, scene } if let Some(Some(Some(clip))) = app - //.project - //.scenes.get(scene) - //.map(|s|s.clips.get(track)) - //=> - //Some(clip), - //_ => - //None - //}); - //} - Ok(None) - //("select" [t: usize, s: usize] Some(match (t.expect("no track"), s.expect("no scene")) { - //(0, 0) => Self::Select(Selection::Mix), - //(t, 0) => Self::Select(Selection::Track(t)), - //(0, s) => Self::Select(Selection::Scene(s)), - //(t, s) => Self::Select(Selection::TrackClip { track: t, scene: s }) }))) - // autoedit: load focused clip in editor. - }; - //fn color (app: &mut App, theme: ItemTheme) -> Perhaps { - //Ok(app.set_color(Some(theme)).map(|theme|Self::Color{theme})) - //} - //fn launch (app: &mut App) -> Perhaps { - //app.project.launch(); - //Ok(None) - //} - toggle_editor = |app, value: bool|{ app.toggle_editor(Some(value)); Ok(None) }; - editor = |app, command: MidiEditCommand| Ok(if let Some(editor) = app.editor_mut() { - let undo = command.clone().delegate(editor, |command|AppCommand::Editor{command})?; - // update linked sampler after editor action - app.project.sampler_mut().map(|sampler|match command { - // autoselect: automatically select sample in sampler - MidiEditCommand::SetNotePos { pos } => { sampler.set_note_pos(pos); }, - _ => {} - }); - undo - } else { - None - }); +dsl_exp!(|app: App| -> AppCommand { + ["stop-all"] => app.project.stop_all(), + ["enqueue", clip?: Option>>] => todo!(), + ["history", delta: isize] => todo!(), + ["zoom", zoom: usize] => todo!(), + ["select", selection: Selection] => todo!(), + ["dialog" / command: DialogCommand] => todo!(), + ["project" / command: ArrangementCommand] => todo!(), + ["clock" / command: ClockCommand] => todo!(), + ["sampler" / command: SamplerCommand] => todo!(), + ["pool" / command: PoolCommand] => todo!(), }); +dsl_exp!(|app: App| -> DialogCommand {}); +dsl_exp!(|app: App| -> ArrangementCommand {}); +dsl_exp!(|app: App| -> ClockCommand {}); +dsl_exp!(|app: App| -> SamplerCommand {}); +dsl_exp!(|app: App| -> PoolCommand {}); +//dsl_bind!(AppCommand: App { + //enqueue = |app, clip: Option>>| { todo!() }; + //history = |app, delta: isize| { todo!() }; + //zoom = |app, zoom: usize| { todo!() }; + //stop_all = |app| { app.tracks_stop_all(); Ok(None) }; + ////dialog = |app, command: DialogCommand| + ////Ok(command.delegate(&mut app.dialog, |c|Self::Dialog{command: c})?); + //project = |app, command: ArrangementCommand| + //Ok(command.delegate(&mut app.project, |c|Self::Project{command: c})?); + //clock = |app, command: ClockCommand| + //Ok(command.execute(app.clock_mut())?.map(|c|Self::Clock{command: c})); + //sampler = |app, command: SamplerCommand| + //Ok(app.project.sampler_mut().map(|s|command.delegate(s, |command|Self::Sampler{command})) + //.transpose()?.flatten()); + //pool = |app, command: PoolCommand| { + //let undo = command.clone().delegate(&mut app.pool, |command|AppCommand::Pool{command})?; + //// update linked editor after pool action + //match command { + //// autoselect: automatically load selected clip in editor + //PoolCommand::Select { .. } | + //// autocolor: update color in all places simultaneously + //PoolCommand::Clip { command: PoolClipCommand::SetColor { .. } } => { + //let clip = app.pool.clip().clone(); + //app.editor_mut().map(|editor|editor.set_clip(clip.as_ref())) + //}, + //_ => None + //}; + //Ok(undo) + //}; + //select = |app, selection: Selection| { + //*app.project.selection_mut() = selection; + ////todo! + ////if let Some(ref mut editor) = app.editor_mut() { + ////editor.set_clip(match selection { + ////Selection::TrackClip { track, scene } if let Some(Some(Some(clip))) = app + ////.project + ////.scenes.get(scene) + ////.map(|s|s.clips.get(track)) + ////=> + ////Some(clip), + ////_ => + ////None + ////}); + ////} + //Ok(None) + ////("select" [t: usize, s: usize] Some(match (t.expect("no track"), s.expect("no scene")) { + ////(0, 0) => Self::Select(Selection::Mix), + ////(t, 0) => Self::Select(Selection::Track(t)), + ////(0, s) => Self::Select(Selection::Scene(s)), + ////(t, s) => Self::Select(Selection::TrackClip { track: t, scene: s }) }))) + //// autoedit: load focused clip in editor. + //}; + ////fn color (app: &mut App, theme: ItemTheme) -> Perhaps { + ////Ok(app.set_color(Some(theme)).map(|theme|Self::Color{theme})) + ////} + ////fn launch (app: &mut App) -> Perhaps { + ////app.project.launch(); + ////Ok(None) + ////} + //toggle_editor = |app, value: bool|{ app.toggle_editor(Some(value)); Ok(None) }; + //editor = |app, command: MidiEditCommand| Ok(if let Some(editor) = app.editor_mut() { + //let undo = command.clone().delegate(editor, |command|AppCommand::Editor{command})?; + //// update linked sampler after editor action + //app.project.sampler_mut().map(|sampler|match command { + //// autoselect: automatically select sample in sampler + //MidiEditCommand::SetNotePos { pos } => { sampler.set_note_pos(pos); }, + //_ => {} + //}); + //undo + //} else { + //None + //}); +//}); //take!(ClockCommand |state: App, iter|Take::take(state.clock(), iter)); //take!(MidiEditCommand |state: App, iter|Ok(state.editor().map(|x|Take::take(x, iter)).transpose()?.flatten())); //take!(PoolCommand |state: App, iter|Take::take(&state.pool, iter)); diff --git a/crates/app/app_dsl.rs b/crates/app/app_dsl.rs index 21a1be6d..745da9a6 100644 --- a/crates/app/app_dsl.rs +++ b/crates/app/app_dsl.rs @@ -1,19 +1,24 @@ use crate::*; -macro_rules!dsl_sym( +pub struct DslNs<'t, T: 't>(pub &'t [(&'t str, T)]); + +pub trait DslSymNs<'t, T: 't>: 't { const NS: DslNs<'t, fn (&'t Self)->T>; } +#[macro_export] macro_rules! dsl_sym ( (|$state:ident:$State:ty| -> $type:ty {$($lit:literal => $exp:expr),* $(,)?})=>{ impl<'t> DslSymNs<'t, $type> for $State { const NS: DslNs<'t, fn (&'t $State)->$type> = DslNs(&[$(($lit, |$state: &$State|$exp)),*]); } }); -pub trait DslSymNs<'t, T: 't>: 't { - const NS: DslNs<'t, fn (&'t Self)->T>; -} - -pub struct DslNs<'t, T: 't>(pub &'t [(&'t str, T)]); +pub trait DslExpNs<'t, T: 't>: 't { const NS: DslNs<'t, fn (&'t Self, &str)->T>; } +#[macro_export] macro_rules! dsl_exp ( + (|$state:ident:$State:ty|->$type:ty { $( + [$key:literal $(/ $sub:ident: $Sub:ty)? $(, $arg:ident $(?)? :$argtype:ty)*] => $body:expr + ),* $(,)? }) => { + impl<'t> DslExpNs<'t, $type> for $State { + const NS: DslNs<'t, fn (&'t $State, &str)->$type> = + DslNs(&[]); } }); pub type DslCb = fn (&App) -> Box>; - impl<'t, D: Dsl> std::ops::Index for DslNs<'t, DslCb> { type Output = DslCb; fn index (&self, index: D) -> &Self::Output {