diff --git a/proc/src/lib.rs b/proc/src/lib.rs index fcd4ff7e..4725ca5f 100644 --- a/proc/src/lib.rs +++ b/proc/src/lib.rs @@ -167,69 +167,18 @@ attribute!(commands { let mut keywords = quote! {}; let mut expressions = quote! {}; for (ident, (variant, inputs, keyword)) in items.iter() { - let mut typed = quote! {}; - let mut params = quote! {}; - let mut has_args = false; - for arg in inputs.iter() { - if let FnArg::Typed(PatType { pat, ty, .. }) = arg { - has_args = true; - append(&mut typed, quote! { #pat: #ty, }); - append(&mut params, quote! { #pat, }); - } - } - append(&mut variants, if has_args { - quote! { #variant { #typed }, } - } else { - quote! { #variant, } - }); - append(&mut dispatch, if has_args { - quote! { #command::#variant { #params } => state.#ident(#params), } - } else { - quote! { #command::#variant => state.#ident(), } - }); - let keyword = if let Some(namespace) = namespace { - format!("{}/{}", namespace.value(), keyword.value()) - } else { - keyword.value() - }; - if has_args { - append(&mut expressions, quote! { - #keyword (#typed) => { #command::#variant { #params } }, - }); - } else { - append(&mut keywords, quote! { - #keyword => #command::#variant, - }); - } + write_variant( + &mut expressions, &mut keywords, &mut variants, &mut dispatch, + ident, command, variant, inputs, namespace, keyword, + ) } let impls = match item { - Item::Impl(ItemImpl { generics, .. }) => { - let lts = Punctuated::<_, Comma>::from_iter(generics.lifetimes()); - let tys = Punctuated::<_, Comma>::from_iter(generics.type_params()); - let cns = Punctuated::<_, Comma>::from_iter(generics.const_params()); - quote! { - impl<'n, #tys> dizzle::Namespace<'n, #command> for #state { - symbols!('n |state: Self| -> #command { #keywords }); - expressions!('n |state: Self| -> #command { #expressions }); - } - impl #generics #command { - pub fn act (self, state: &mut #state) -> Perhaps { - match self { #dispatch _ => unreachable!() } - } - } - } - }, - Item::Trait { .. } => quote! { - impl<'n, T: #state + 'n> dizzle::Namespaced<'n, T> for #command { - symbols!('n |state: T| -> Self { #keywords }); - expressions!('n |state: T| -> Self { #expressions }); - } - impl #command { - pub fn act <'n, T: #state + 'n> (self, state: &mut T) -> Perhaps { - match self { #dispatch _ => unreachable!() } - } - } - }, + Item::Impl(ItemImpl { generics, .. }) => write_for_struct( + keywords, expressions, dispatch, state, command, generics, + ), + Item::Trait { .. } => write_for_trait( + keywords, expressions, dispatch, state, command, + ), _ => panic!("trait or inherent impl needed for #[commands]") }; append(out, quote! { @@ -240,6 +189,145 @@ attribute!(commands { }) } } + + fn write_variant ( + exps: &mut TokenStream2, + syms: &mut TokenStream2, + vars: &mut TokenStream2, + disp: &mut TokenStream2, + ident: &Ident, + command: &Path, + variant: &Ident, + inputs: &[FnArg], + namespace: &Option, + keyword: &LitStr, + ) { + let keyword = if let Some(namespace) = namespace { + format!("{}/{}", namespace.value(), keyword.value()) + } else { + keyword.value() + }; + let mut typed = quote! {}; + let mut params = quote! {}; + let mut obtain = quote! {}; + let mut has_args = false; + for arg in inputs.iter() { + if let FnArg::Typed(PatType { pat, ty, .. }) = arg { + has_args = true; + append(&mut typed, quote! { #pat: #ty, }); + append(&mut params, quote! { #pat, }); + append(&mut obtain, quote! { let #pat: #ty = { + let head = tail.head()?.unwrap_or_default(); + let tail = tail.tail()?.unwrap_or_default(); + match dizzle::Namespace::<#ty>::namespace(state, &head)? { + Some(arg) => arg, + None => return Err(format!("{}: arg \"{}\" ({}) got: {head} {tail}", + #keyword, stringify!(#pat), stringify!(#ty), + ).into()) + } + };}); + } + } + if has_args { + append(vars, quote! { #variant { #typed }, }); + append(disp, quote! { #command::#variant { #params } => state.#ident(#params), }); + append(exps, quote! { + let tail_base = tail; + if head.src()? == Some(#keyword) { + let tail = tail_base; + #obtain + return Ok(Some(#command::#variant { #params })) + } + }); + } else { + append(vars, quote! { #variant, }); + append(disp, quote! { #command::#variant => state.#ident(), }); + append(syms, quote! { + if word == #keyword { return Ok(Some(#command::#variant)); } + }); + } + } + + fn write_for_struct ( + syms: TokenStream2, + exps: TokenStream2, + disp: TokenStream2, + state: &Path, + command: &Path, + generics: &syn::Generics, + ) -> TokenStream2 { + //let lts = Punctuated::<_, Comma>::from_iter(generics.lifetimes()); + //let cns = Punctuated::<_, Comma>::from_iter(generics.const_params()); + let tys = Punctuated::<_, Comma>::from_iter(generics.type_params()); + quote! { + impl<#tys> dizzle::Namespace<#command> for #state { + //def_namespace_symbols!('n |state: Self| -> #command { + //#syms + //}); + //def_namespace_exps!('n |state: Self| -> #command { + //#exps + //}); + fn namespace_symbol (&self, word: impl Symbol) -> Perhaps<#command> { + if let Some(word) = word.word()? { + #syms + } + Ok(None) + } + fn namespace_expression (&self, expr: impl Expression) -> Perhaps<#command> { + let state = self; + if let Some(expr) = expr.expr()? { + let head = expr.head()?; + let tail = expr.tail()?; + #exps + } + Ok(None) + } + } + impl #generics dizzle::Dispatch<#state> for #command { + fn dispatch (self, state: &mut #state) -> Perhaps { + match self { + #disp + _ => unreachable!() + } + } + } + } + } + + fn write_for_trait ( + syms: TokenStream2, + exps: TokenStream2, + disp: TokenStream2, + state: &Path, + command: &Path, + ) -> TokenStream2 { + quote! { + impl dizzle::Namespaced for #command { + fn namespaced_symbol (state: &T, word: L) -> Perhaps<#command> { + if let Some(word) = word.word()? { + #syms + } + Ok(None) + } + fn namespaced_expression (state: &T, expr: L) -> Perhaps<#command> { + if let Some(expr) = expr.expr()? { + let head = expr.head()?; + let tail = expr.tail()?; + #exps + } + Ok(None) + } + } + impl dizzle::Dispatch for #command { + fn dispatch (self, state: &mut T) -> Perhaps { + match self { + #disp + _ => unreachable!() + } + } + } + } + } }); attribute!(command { @@ -393,8 +481,8 @@ attribute!(keyword { append(out, quote! { #item - impl<'n> Namespace<'n, #ident> for #state { - symbols!('n |_state: #state| -> #ident { #body, }); + impl Namespace<#ident> for #state { + def_namespace_symbols!(|_state: #state| -> #ident { #body, }); } }) } diff --git a/src/device/arrange/scene.rs b/src/device/arrange/scene.rs index eae2e854..ce45629e 100644 --- a/src/device/arrange/scene.rs +++ b/src/device/arrange/scene.rs @@ -73,32 +73,32 @@ pub trait HasScene: AsRefOpt + AsMutOpt { } impl Namespace<'a, usize> - + for<'a> Namespace<'a, Arc> - + for<'a> Namespace<'a, ItemTheme> + + Namespace + + Namespace> + + Namespace > SceneController for T {} #[tek_proc::commands(SceneCommand)] pub trait SceneController: HasScene - + for<'a> Namespace<'a, usize> - + for<'a> Namespace<'a, Arc> - + for<'a> Namespace<'a, ItemTheme> + + Namespace + + Namespace> + + Namespace { #[command(SetSize = "scene/size")] fn scene_set_size (&mut self, size: usize) -> Perhaps - where Self: for<'a> Namespace<'a, usize> + where Self: Namespace { todo!() } #[command(SetZoom = "scene/zoom")] fn scene_set_zoom (&mut self, size: usize) -> Perhaps - where Self: for<'a> Namespace<'a, usize> + where Self: Namespace { todo!() } #[command(SetName = "scene/name")] fn scene_set_name (&mut self, name: Arc) -> Perhaps - where Self: for<'a> Namespace<'a, Arc> + where Self: Namespace> { Ok(self.scene_mut().map(|scene|swap_value( &mut scene.name, @@ -108,7 +108,7 @@ pub trait SceneController: HasScene } #[command(SetColor = "scene/color")] fn scene_set_color (&mut self, color: ItemTheme) -> Perhaps - where Self: for<'a> Namespace<'a, ItemTheme> + where Self: Namespace { Ok(self.scene_mut().map(|scene|swap_value( &mut scene.color, @@ -172,16 +172,16 @@ pub trait HasScenes: AsRef> + AsMut> { } impl Namespace<'a, usize> - + for<'a> Namespace<'a, Option> - + for<'a> Namespace<'a, Option>> + + Namespace + + Namespace> + + Namespace>> > ScenesController for T {} #[tek_proc::commands(ScenesCommand)] pub trait ScenesController: HasScenes - + for<'a> Namespace<'a, usize> - + for<'a> Namespace<'a, Option> - + for<'a> Namespace<'a, Option>> + + Namespace + + Namespace> + + Namespace>> { // TODO } diff --git a/src/device/arrange/track.rs b/src/device/arrange/track.rs index 2aa64c92..cc10823a 100644 --- a/src/device/arrange/track.rs +++ b/src/device/arrange/track.rs @@ -85,18 +85,18 @@ pub trait HasTrack: AsRefOpt + AsMutOpt { } impl Namespace<'a, usize> - + for<'a> Namespace<'a, Arc> - + for<'a> Namespace<'a, ItemTheme> - + for<'a> Namespace<'a, Option> + + Namespace + + Namespace> + + Namespace + + Namespace> > TrackController for T {} #[tek_proc::commands(TrackCommand)] pub trait TrackController: HasTrack - + for<'a> Namespace<'a, usize> - + for<'a> Namespace<'a, Arc> - + for<'a> Namespace<'a, ItemTheme> - + for<'a> Namespace<'a, Option> + + Namespace + + Namespace> + + Namespace + + Namespace> { #[command(Stop = "track/stop")] fn track_stop (&mut self) -> Perhaps { @@ -259,24 +259,24 @@ pub trait HasTracks: AsRef> + AsMut> + HasClock + HasTrack } impl Namespace<'a, usize> - + for<'a> Namespace<'a, Arc<[Connect]>> - + for<'a> Namespace<'a, Option> - + for<'a> Namespace<'a, Option>> - + for<'a> Namespace<'a, Option> - + for<'a> Namespace<'a, Option>> - + for<'a> Namespace<'a, Option>>>>> + + Namespace + + Namespace> + + Namespace> + + Namespace>> + + Namespace> + + Namespace>> + + Namespace>>>>> > TracksController for T {} #[tek_proc::commands(TracksCommand)] pub trait TracksController: HasTracks - + for<'a> Namespace<'a, usize> - + for<'a> Namespace<'a, Arc<[Connect]>> - + for<'a> Namespace<'a, Option> - + for<'a> Namespace<'a, Option>> - + for<'a> Namespace<'a, Option> - + for<'a> Namespace<'a, Option>> - + for<'a> Namespace<'a, Option>>>>> + + Namespace + + Namespace> + + Namespace> + + Namespace>> + + Namespace> + + Namespace>> + + Namespace>>>>> { #[command(Stop = "tracks/stop")] diff --git a/src/device/browse.rs b/src/device/browse.rs index f4498964..ca0aa331 100644 --- a/src/device/browse.rs +++ b/src/device/browse.rs @@ -13,9 +13,9 @@ impl App { #[tek_proc::commands(BrowseCommand = "browse")] pub trait BrowseController: - for<'a> Namespace<'a, usize> + - for<'a> Namespace<'a, PathBuf> + - for<'a> Namespace<'a, Arc> + Namespace + + Namespace + + Namespace> { /// Toggle visibility of browser #[command(Show = "show")] diff --git a/src/device/clock.rs b/src/device/clock.rs index a8e78b56..e37779ea 100644 --- a/src/device/clock.rs +++ b/src/device/clock.rs @@ -66,16 +66,16 @@ pub trait HasClock: AsRef + AsMut { } impl Namespace<'a, u32> - + for<'a> Namespace<'a, f64> - + for<'a> Namespace<'a, Option> + + Namespace + + Namespace + + Namespace> > ClockController for T {} #[tek_proc::commands(ClockCommand = "clock")] pub trait ClockController: HasClock - + for<'a> Namespace<'a, u32> - + for<'a> Namespace<'a, f64> - + for<'a> Namespace<'a, Option> + + Namespace + + Namespace + + Namespace> { #[command(SeekUsec = "usec")] fn seek_usec (&mut self, usec: f64) -> Perhaps { @@ -468,3 +468,58 @@ impl_time_unit!(Ppq); impl_time_unit!(Pulse); impl_time_unit!(Bpm); impl_time_unit!(LaunchSync); + +/// ``` +/// let x = ""; +/// let _ = tek::view_transport(true, x.as_ref(), x.as_ref(), x.as_ref()); +/// let _ = tek::view_transport(false, x.as_ref(), x.as_ref(), x.as_ref()); +/// ``` +pub fn view_transport (play: bool, bpm: &str, beat: &str, time: &str) -> impl Draw { + let theme = ItemTheme::G[96]; + bg(Black, east!(above( + button_play_pause(play, false).align_w(), + east!( + field_h(theme, "BPM", bpm), + field_h(theme, "Beat", beat), + field_h(theme, "Time", time), + ).align_e().full_wh() + ))) +} + +/// ``` +/// let x = ""; +/// let _ = tek::view_status(None, x.as_ref(), x.as_ref(), x.as_ref()); +/// let _ = tek::view_status(Some("".into()), x.as_ref(), x.as_ref(), x.as_ref()); +/// ``` +pub fn view_status (sel: Option<&str>, sr: &str, buf: &str, lat: &str) -> impl Draw { + let theme = ItemTheme::G[96]; + let sr = field_h(theme, "SR", sr); + let buf = field_h(theme, "Buf", buf); + let lat = field_h(theme, "Lat", lat); + bg(Black, east!(above( + sel.map(|sel|field_h(theme, "Selected", sel)).align_w().full_wh(), + east!(sr, buf, lat).align_e().full_wh(), + ))) +} + +/// ``` +/// let _ = tek::button_play_pause(true, true); +/// let _ = tek::button_play_pause(true, false); +/// let _ = tek::button_play_pause(false, true); +/// let _ = tek::button_play_pause(false, false); +/// ``` +pub fn button_play_pause (playing: bool, compact: bool) -> impl Draw { + bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) }, + either(compact, + draw(move|to: &mut Tui|either(playing, + fg(Rgb(0, 255, 0), " PLAYING "), + fg(Rgb(255, 128, 0), " STOPPED "), + ).exact_w(9).draw(to)), + draw(move|to: &mut Tui|either(playing, + fg(Rgb(0, 255, 0), south(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)), + fg(Rgb(255, 128, 0), south(" ▗▄▖ ", " ▝▀▘ ",)), + ).exact_w(5).draw(to)), + ) + ) +} + diff --git a/src/device/editor.rs b/src/device/editor.rs index 9cee7a03..c8dbf86a 100644 --- a/src/device/editor.rs +++ b/src/device/editor.rs @@ -67,22 +67,22 @@ pub trait HasEditor: AsRefOpt + AsMutOpt { impl+AsMutOpt> HasEditor for T {} impl Namespace<'a, u32> - + for<'a> Namespace<'a, f64> - + for<'a> Namespace<'a, bool> - + for<'a> Namespace<'a, usize> - + for<'a> Namespace<'a, Option> - + for<'a> Namespace<'a, Option>>> + + Namespace + + Namespace + + Namespace + + Namespace + + Namespace> + + Namespace>>> > MidiEditController for T {} #[tek_proc::commands(MidiEditCommand = "edit")] pub trait MidiEditController: HasEditor - + for<'a> Namespace<'a, u32> - + for<'a> Namespace<'a, f64> - + for<'a> Namespace<'a, bool> - + for<'a> Namespace<'a, usize> - + for<'a> Namespace<'a, Option> - + for<'a> Namespace<'a, Option>>> + + Namespace + + Namespace + + Namespace + + Namespace + + Namespace> + + Namespace>>> { #[command(Show = "show")] fn show (&mut self, clip: Option>>) -> Perhaps { diff --git a/src/device/pool.rs b/src/device/pool.rs index 2ae3c95b..8964e8c8 100644 --- a/src/device/pool.rs +++ b/src/device/pool.rs @@ -70,28 +70,28 @@ pub trait HasPool: AsRef + AsMut { } impl Namespace<'a, bool> - + for<'a> Namespace<'a, usize> - + for<'a> Namespace<'a, Arc> - + for<'a> Namespace<'a, PathBuf> - + for<'a> Namespace<'a, MidiClip> - + for<'a> Namespace<'a, ItemColor> - + for<'a> Namespace<'a, PoolCommand> - + for<'a> Namespace<'a, PoolCommand> - + for<'a> Namespace<'a, BrowseCommand> + + Namespace + + Namespace + + Namespace> + + Namespace + + Namespace + + Namespace + + Namespace + + Namespace + + Namespace > PoolController for T {} #[tek_proc::commands(PoolCommand = "pool")] pub trait PoolController: HasPool - + for<'a> Namespace<'a, bool> - + for<'a> Namespace<'a, usize> - + for<'a> Namespace<'a, Arc> - + for<'a> Namespace<'a, PathBuf> - + for<'a> Namespace<'a, MidiClip> - + for<'a> Namespace<'a, ItemColor> - + for<'a> Namespace<'a, PoolCommand> - + for<'a> Namespace<'a, PoolCommand> - + for<'a> Namespace<'a, BrowseCommand> + + Namespace + + Namespace + + Namespace> + + Namespace + + Namespace + + Namespace + + Namespace + + Namespace + + Namespace { #[command(Show = "show")] @@ -139,7 +139,7 @@ pub trait PoolController: HasPool for event in events.iter() { clip.notes[event.0 as usize].push(event.2); } - Ok(PoolCommand::Add { index, clip }.act(self)?) + Ok(PoolCommand::Add { index, clip }.dispatch(self)?) } ///// Export to file diff --git a/src/device/sampler.rs b/src/device/sampler.rs index b2808ac7..f9203341 100644 --- a/src/device/sampler.rs +++ b/src/device/sampler.rs @@ -21,7 +21,7 @@ impl + AsMut> HasSampler for T {} #[tek_proc::commands(SamplerCommand = "sampler")] pub trait SamplerController: HasSampler - + for<'a> Namespace<'a, usize> + + Namespace { #[command(RecordToggle = "rec-toggle")] @@ -30,10 +30,10 @@ pub trait SamplerController: HasSampler { let sampler = self.sampler_mut(); let recording = sampler.recording.as_ref().map(|x|x.0); - let _ = SamplerCommand::RecordFinish.act(self)?; + let _ = SamplerCommand::RecordFinish.dispatch(self)?; // autoslice: continue recording at next slot if recording != Some(slot) { - SamplerCommand::RecordBegin { slot }.act(self) + SamplerCommand::RecordBegin { slot }.dispatch(self) } else { Ok(None) } diff --git a/src/device/sequence.rs b/src/device/sequence.rs index cb60604f..b4ceadb6 100644 --- a/src/device/sequence.rs +++ b/src/device/sequence.rs @@ -246,14 +246,14 @@ pub trait HasMidiClip { } impl Namespace<'a, Option> - + for<'a> Namespace<'a, Option> + + Namespace> + + Namespace> > MidiClipController for T {} #[tek_proc::commands(MidiClipCommand)] pub trait MidiClipController: HasMidiClip - + for<'a> Namespace<'a, Option> - + for<'a> Namespace<'a, Option> + + Namespace> + + Namespace> { #[command(SetColor = "clip/color")] diff --git a/src/tek.edn b/src/tek.edn index 198f95b9..970f29af 100644 --- a/src/tek.edn +++ b/src/tek.edn @@ -86,18 +86,18 @@ (view :groove/sequence (bsp/e (fill/y (align/n (bsp/s :status/v :editor-status))) (bsp/e :samples/keys :editor))) -(keys :back (@escape back)) -(keys :confirm (@enter confirm)) -(keys :axis/x (@left app/dec :x) (@right app/inc :x)) -(keys :axis/x2 (@shift/left app/dec :x2) (@shift/right app/inc :x2)) -(keys :axis/y (@up app/dec :y) (@down app/inc :y)) -(keys :axis/y2 (@shift/up app/dec :y2) (@shift/down app/inc :y2)) -(keys :axis/z (@minus app/dec :z) (@equal app/inc :z)) -(keys :axis/z2 (@underscore app/dec :z2) (@plus app/inc :z2)) -(keys :axis/i (@comma app/dec :i) (@period app/inc :z)) -(keys :axis/i2 (@lt app/dec :i2) (@gt app/inc :z2)) -(keys :axis/w (@openbracket app/dec :w) (@closebracket app/inc :w)) -(keys :axis/w2 (@openbrace app/dec :w2) (@closebrace app/inc :w2)) +(keys :back (@escape (back))) +(keys :confirm (@enter :confirm)) +(keys :axis/x (@left dec :x) (@right inc :x)) +(keys :axis/x2 (@shift/left dec :x2) (@shift/right inc :x2)) +(keys :axis/y (@up (dec :y)) (@down (inc :y))) +(keys :axis/y2 (@shift/up dec :y2) (@shift/down inc :y2)) +(keys :axis/z (@minus dec :z) (@equal inc :z)) +(keys :axis/z2 (@underscore dec :z2) (@plus inc :z2)) +(keys :axis/i (@comma dec :i) (@period inc :z)) +(keys :axis/i2 (@lt dec :i2) (@gt inc :z2)) +(keys :axis/w (@openbracket dec :w) (@closebracket inc :w)) +(keys :axis/w2 (@openbrace dec :w2) (@closebrace inc :w2)) (keys :focus) (keys :editor (see :axis/i :axis/i2 :axis/y :page :editor/view :editor/add :editor/del)) diff --git a/src/tek.rs b/src/tek.rs index fd2fb982..0e25d92b 100644 --- a/src/tek.rs +++ b/src/tek.rs @@ -620,7 +620,7 @@ mod app { /// ``` /// use tek::{HasTracks, HasScenes, TracksView, ScenesView}; /// let mut app = tek::App::default(); - /// let _ = app.scene_add(None, None).unwrap(); + /// let _ = app.scenes_add_one(None, None).unwrap(); /// let _ = app.update_clock(); /// app.project.editor = Some(Default::default()); /// //let _: Vec<_> = app.project.inputs_with_sizes().collect(); @@ -639,21 +639,27 @@ mod app { /// let _ = app.project.h_scenes(); /// ``` #[derive(Default, Debug)] - #[namespace(u8)] - #[namespace(isize)] - #[namespace(ItemTheme)] + #[namespace(Arc<[Connect]> App::get_arc_array_connect)] #[namespace(Arc App::get_arc_str)] - #[namespace(u16 App::get_u16)] - #[namespace(usize App::get_usize)] - #[namespace(bool App::get_bool)] - #[namespace(Selection App::get_selection)] #[namespace(Color App::get_color)] - #[namespace(Option App::get_opt_u7)] - #[namespace(Option App::get_opt_u16)] - #[namespace(Option App::get_opt_usize)] - #[namespace(Option>> App::get_clip)] - #[namespace(Dialog App::get_dialog)] #[namespace(ControlAxis App::get_axis)] + #[namespace(Dialog App::get_dialog)] + #[namespace(ItemTheme)] + #[namespace(Option>> App::get_clip)] + #[namespace(Option> App::get_opt_arc_array_connect)] + #[namespace(Option> App::get_opt_arc_str)] + #[namespace(Option App::get_opt_itemtheme)] + #[namespace(Option>>>> App::get_opt_vec_opt_arc_rwlock_midiclip)] + #[namespace(Option App::get_opt_u16)] + #[namespace(Option App::get_opt_u7)] + #[namespace(Option App::get_opt_usize)] + #[namespace(Option App::get_opt_bool)] + #[namespace(Selection App::get_selection)] + #[namespace(bool App::get_bool)] + #[namespace(u16 App::get_u16)] + #[namespace(isize)] + #[namespace(u8)] + #[namespace(usize App::get_usize)] pub struct App { /// Exit flag pub exit: Exit, @@ -827,12 +833,34 @@ mod app { fn get_axis (&self, src: impl Language) -> Perhaps { Ok(src.word()?.map(|word|Ok(match word { - "x" => ControlAxis::X, - "y" => ControlAxis::Y, + ":x" => ControlAxis::X, + ":y" => ControlAxis::Y, _ => return Err(format!("unknown axis {word}")) })).transpose()?) } + fn get_opt_itemtheme (&self, src: impl Language) -> Perhaps> { + todo!() + } + fn get_opt_vec_opt_arc_rwlock_midiclip (&self, src: impl Language) -> Perhaps>>>>> { + todo!() + } + fn get_opt_arc_array_connect (&self, src: impl Language) -> Perhaps>> { + todo!() + } + + fn get_arc_array_connect (&self, src: impl Language) -> Perhaps> { + todo!() + } + + fn get_opt_arc_str (&self, src: impl Language) -> Perhaps>> { + todo!() + } + + fn get_opt_bool (&self, src: impl Language) -> Perhaps> { + todo!() + } + } } @@ -883,52 +911,155 @@ mod bind { pub Arcbool + Send + Sync>> ); - tui_keys!(self: App, input { - let commands = collect_commands(self, input)?; - let results = execute_commands(self, commands)?; - self.history.extend(results.into_iter()); - Ok(()) - }); + struct AppDispatch; - fn collect_commands (app: &App, input: &TuiEvent) - -> Usually> - { - let mut commands = vec![]; - app.mode.as_ref().and_then(|m|app.config.get_mode(m)).map(|mode|{ + tui_keys!(self: App, input { + + if let Some(mode) = self.mode.as_ref().and_then( + |m|self.config.get_mode(m) + ).as_ref().map(Arc::clone) { + let binds = self.config.binds.clone(); for id in mode.keys.iter() { - if let Some(event_map) = app.config.binds.clone().read().unwrap().get(id.as_ref()) + if let Some(event_map) = binds.read().unwrap().get(id.as_ref()) && let Some(bindings) = event_map.query(input) { for binding in bindings { for command in binding.commands.iter() { - if let Some(command) = app.namespace(command)? as Option { - commands.push(command) + for namespace in App::commands() { + if namespace(self, command)? { + break + } } } } } } - Ok::<_, Box>(()) - }).transpose()?; - Ok(commands) + } + Ok(()) + }); + + #[derive(Clone, Debug)] + pub enum AppCommand { + Axis(AxisCommand), + Browse(BrowseCommand), + Dialog(DialogCommand), + MidiClip(MidiClipCommand), + Pool(PoolCommand), + Scene(SceneCommand), + Scenes(ScenesCommand), + Track(TrackCommand), + Tracks(TracksCommand), } - fn execute_commands (app: &mut App, commands: Vec) - -> Usually)>> - { - let mut history = vec![]; - for command in commands.into_iter() { - let result = command.clone().act(app); - match result { - Err(err) => { - history.push((command, None)); - return Err(err) - }, - Ok(undo) => { - history.push((command, undo)); - } - }; + impl_from!(AppCommand: |x: AxisCommand| AppCommand::Axis(x)); + impl_from!(AppCommand: |x: BrowseCommand| AppCommand::Browse(x)); + impl_from!(AppCommand: |x: DialogCommand| AppCommand::Dialog(x)); + impl_from!(AppCommand: |x: MidiClipCommand| AppCommand::MidiClip(x)); + impl_from!(AppCommand: |x: PoolCommand| AppCommand::Pool(x)); + impl_from!(AppCommand: |x: SceneCommand| AppCommand::Scene(x)); + impl_from!(AppCommand: |x: ScenesCommand| AppCommand::Scenes(x)); + impl_from!(AppCommand: |x: TrackCommand| AppCommand::Track(x)); + impl_from!(AppCommand: |x: TracksCommand| AppCommand::Tracks(x)); + + impl App { + fn commands () -> impl Iterator Usually> { + [ + Self::command::, + //Self::command::, + Self::command::, + //Self::command::, + //Self::command::, + Self::command::, + Self::command::, + Self::command::, + Self::command::, + ] + .into_iter() + } + + fn command < + T: Dispatch + Clone + Into + > (state: &mut Self, src: &str) -> Usually where + Self: Namespace, + { + if let Some(command) = Namespace::::namespace(state, src)? { + match command.clone().dispatch(state) { + Err(err) => { + state.history.push((command.into(), None)); + return Err(err) + }, + Ok(undo) => { + state.history.push((command.into(), undo.map(Into::into))); + return Ok(true) + } + }; + } + Ok(false) + } + } + + #[tek_proc::commands(DialogCommand)] + impl App { + /// Cancel current dialog + #[command(Cancel = "cancel")] + pub fn cancel (&mut self) -> Perhaps { + todo!() + } + /// Confirm current dialog selection. + #[command(Confirm = "confirm")] + pub fn confirm (&mut self) -> Perhaps { + Ok(match &self.dialog { + Dialog::Menu(index, items) => { + let callback = items.0[*index].1.clone(); + callback(self)?; + None + }, + _ => todo!(), + }) + } + /// Swap currently active modal dialog. + /// + /// ``` + /// let _ = tek::App::default().set_dialog(tek::Dialog::welcome()); + /// ``` + #[command(SetDialog = "dialog")] + pub fn set_dialog (&mut self, dialog: Dialog) -> Perhaps { + let mut dialog = dialog.clone(); + std::mem::swap(&mut self.dialog, &mut dialog); + Ok(Some(DialogCommand::SetDialog { dialog })) + } + + //#[command(Tracks = "tracks")] + //pub fn command_tracks (&mut self, command: TracksCommand) -> Perhaps { + //todo!() + //} + } + + #[tek_proc::commands(AxisCommand)] + impl App { + /// Increment a given data axis. + #[command(Inc = "inc")] + pub fn inc (&mut self, axis: ControlAxis) -> Perhaps { + Ok(match (&self.dialog, axis) { + (Dialog::None, _) => todo!(), + (Dialog::Menu(_, _), ControlAxis::Y) => { + DialogCommand::SetDialog { dialog: self.dialog.menu_next() }.dispatch(self)?; + None + }, + _ => todo!() + }) + } + /// Decrement a given data axis. + #[command(Dec = "dec")] + pub fn dec (&mut self, axis: ControlAxis) -> Perhaps { + Ok(match (&self.dialog, axis) { + (Dialog::None, _) => None, + (Dialog::Menu(_, _), ControlAxis::Y) => { + DialogCommand::SetDialog { dialog: self.dialog.menu_prev() }.dispatch(self)?; + None + }, + _ => todo!() + }) } - Ok(history) } pub(crate) fn load_bind (binds: &Binds, name: &impl AsRef, body: &impl Language) -> Usually<()> { @@ -1012,75 +1143,6 @@ mod bind { impl_debug!(Condition |self, w| { write!(w, "*") }); - #[tek_proc::commands(AppCommand = "app")] - impl App { - - #[command(Nop = "nop")] - pub fn nop (&mut self) -> Perhaps { - Ok(None) - } - - #[command(Cancel = "cancel")] - pub fn cancel (&mut self) -> Perhaps { - todo!() - } - - #[command(Inc = "inc")] - pub fn inc (&mut self, axis: ControlAxis) -> Perhaps { - Ok(match (&self.dialog, axis) { - (Dialog::None, _) => todo!(), - (Dialog::Menu(_, _), ControlAxis::Y) => - AppCommand::SetDialog { dialog: self.dialog.menu_next() }.act(self)?, - _ => todo!() - }) - } - - #[command(Dec = "dec")] - pub fn dec (&mut self, axis: ControlAxis) -> Perhaps { - Ok(match (&self.dialog, axis) { - (Dialog::None, _) => None, - (Dialog::Menu(_, _), ControlAxis::Y) => - AppCommand::SetDialog { dialog: self.dialog.menu_prev() }.act(self)?, - _ => todo!() - }) - } - - #[command(Confirm = "confirm")] - pub fn confirm (&mut self) -> Perhaps { - Ok(match &self.dialog { - Dialog::Menu(index, items) => { - let callback = items.0[*index].1.clone(); - callback(self)?; - None - }, - _ => todo!(), - }) - } - - /// Swap currently active modal dialog. - /// - /// ``` - /// let _ = tek::App::default().set_dialog(tek::Dialog::welcome()); - /// ``` - #[command(SetDialog = "dialog")] - pub fn set_dialog (&mut self, dialog: Dialog) -> Perhaps { - let mut dialog = dialog.clone(); - std::mem::swap(&mut self.dialog, &mut dialog); - Ok(Some(AppCommand::SetDialog { dialog })) - } - } - - //impl<'a> Namespace<'a, AppCommand> for App { - //symbols!('a |app| -> AppCommand { - //"x/inc" => AppCommand::Inc { axis: ControlAxis::X }, - //"x/dec" => AppCommand::Dec { axis: ControlAxis::X }, - //"y/inc" => AppCommand::Inc { axis: ControlAxis::Y }, - //"y/dec" => AppCommand::Dec { axis: ControlAxis::Y }, - //"confirm" => AppCommand::Confirm, - //"cancel" => AppCommand::Cancel, - //}); - //} - /// A control axis. /// /// ``` @@ -1293,6 +1355,18 @@ mod device { } } + pub fn view_device (state: &App) -> impl Draw { + let selected = state.dialog.device_kind().unwrap(); + south( + bold(true, "Add device"), + iter_south(move||device_kinds().iter().enumerate().map(move|(i, _label)|{ + let b = if i == selected { Rgb(64,128,32) } else { Rgb(0,0,0) }; + let l = if i == selected { "[ " } else { " " }; + let r = if i == selected { " ]" } else { " " }; + bg(b, east(l, west(r, "FIXME device name"))).full_w() + }))) + } + pub mod arrange; pub use self::arrange::*; pub mod browse; pub use self::browse::*; pub mod pool; pub use self::pool::*; @@ -1346,8 +1420,10 @@ mod draw { impl Keywords> for App { fn keywords () -> impl Iterator Perhaps>> { - [kw_when, kw_either, kw_split, kw_align, kw_exact, kw_min, kw_max, kw_push, kw_full, - kw_tui_text, kw_tui_fg, kw_tui_bg] + [ + kw_when, kw_either, kw_split, kw_align, kw_exact, kw_min, kw_max, kw_push, kw_full, + kw_tui_text, kw_tui_fg, kw_tui_bg + ] .into_iter() } } @@ -1521,73 +1597,6 @@ mod draw { Ok(Some(to.area().into())) }).min_w(w).exact_h(h) } - - pub fn view_device (state: &App) -> impl Draw { - let selected = state.dialog.device_kind().unwrap(); - south( - bold(true, "Add device"), - iter_south(move||device_kinds().iter().enumerate().map(move|(i, _label)|{ - let b = if i == selected { Rgb(64,128,32) } else { Rgb(0,0,0) }; - let l = if i == selected { "[ " } else { " " }; - let r = if i == selected { " ]" } else { " " }; - bg(b, east(l, west(r, "FIXME device name"))).full_w() - }))) - } - - /// ``` - /// let x = ""; - /// let _ = tek::view_transport(true, x.as_ref(), x.as_ref(), x.as_ref()); - /// let _ = tek::view_transport(false, x.as_ref(), x.as_ref(), x.as_ref()); - /// ``` - pub fn view_transport (play: bool, bpm: &str, beat: &str, time: &str) -> impl Draw { - let theme = ItemTheme::G[96]; - bg(Black, east!(above( - button_play_pause(play, false).align_w(), - east!( - field_h(theme, "BPM", bpm), - field_h(theme, "Beat", beat), - field_h(theme, "Time", time), - ).align_e().full_wh() - ))) - } - - /// ``` - /// let x = ""; - /// let _ = tek::view_status(None, x.as_ref(), x.as_ref(), x.as_ref()); - /// let _ = tek::view_status(Some("".into()), x.as_ref(), x.as_ref(), x.as_ref()); - /// ``` - pub fn view_status (sel: Option<&str>, sr: &str, buf: &str, lat: &str) -> impl Draw { - let theme = ItemTheme::G[96]; - let sr = field_h(theme, "SR", sr); - let buf = field_h(theme, "Buf", buf); - let lat = field_h(theme, "Lat", lat); - bg(Black, east!(above( - sel.map(|sel|field_h(theme, "Selected", sel)).align_w().full_wh(), - east!(sr, buf, lat).align_e().full_wh(), - ))) - } - - /// ``` - /// let _ = tek::button_play_pause(true, true); - /// let _ = tek::button_play_pause(true, false); - /// let _ = tek::button_play_pause(false, true); - /// let _ = tek::button_play_pause(false, false); - /// ``` - pub fn button_play_pause (playing: bool, compact: bool) -> impl Draw { - bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) }, - either(compact, - draw(move|to: &mut Tui|either(playing, - fg(Rgb(0, 255, 0), " PLAYING "), - fg(Rgb(255, 128, 0), " STOPPED "), - ).exact_w(9).draw(to)), - draw(move|to: &mut Tui|either(playing, - fg(Rgb(0, 255, 0), south(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)), - fg(Rgb(255, 128, 0), south(" ▗▄▖ ", " ▝▀▘ ",)), - ).exact_w(5).draw(to)), - ) - ) - } - /// ``` /// let bg = tengri::ratatui::style::Color::Red; /// let fg = tengri::ratatui::style::Color::Green; diff --git a/tengri b/tengri index b7f4d55e..1f541407 160000 --- a/tengri +++ b/tengri @@ -1 +1 @@ -Subproject commit b7f4d55e1d67d3481ecee14f693ca3e0a9426a6c +Subproject commit 1f541407597c7866cf1450d03967ab4711d28d29