mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-08-28 12:56:57 +02:00
This commit is contained in:
parent
def7a1b210
commit
9f2327f96c
12 changed files with 499 additions and 347 deletions
214
proc/src/lib.rs
214
proc/src/lib.rs
|
|
@ -167,69 +167,18 @@ attribute!(commands {
|
||||||
let mut keywords = quote! {};
|
let mut keywords = quote! {};
|
||||||
let mut expressions = quote! {};
|
let mut expressions = quote! {};
|
||||||
for (ident, (variant, inputs, keyword)) in items.iter() {
|
for (ident, (variant, inputs, keyword)) in items.iter() {
|
||||||
let mut typed = quote! {};
|
write_variant(
|
||||||
let mut params = quote! {};
|
&mut expressions, &mut keywords, &mut variants, &mut dispatch,
|
||||||
let mut has_args = false;
|
ident, command, variant, inputs, namespace, keyword,
|
||||||
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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let impls = match item {
|
let impls = match item {
|
||||||
Item::Impl(ItemImpl { generics, .. }) => {
|
Item::Impl(ItemImpl { generics, .. }) => write_for_struct(
|
||||||
let lts = Punctuated::<_, Comma>::from_iter(generics.lifetimes());
|
keywords, expressions, dispatch, state, command, generics,
|
||||||
let tys = Punctuated::<_, Comma>::from_iter(generics.type_params());
|
),
|
||||||
let cns = Punctuated::<_, Comma>::from_iter(generics.const_params());
|
Item::Trait { .. } => write_for_trait(
|
||||||
quote! {
|
keywords, expressions, dispatch, state, command,
|
||||||
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<Self> {
|
|
||||||
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<Self> {
|
|
||||||
match self { #dispatch _ => unreachable!() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => panic!("trait or inherent impl needed for #[commands]")
|
_ => panic!("trait or inherent impl needed for #[commands]")
|
||||||
};
|
};
|
||||||
append(out, quote! {
|
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<LitStr>,
|
||||||
|
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<Self> {
|
||||||
|
match self {
|
||||||
|
#disp
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_for_trait (
|
||||||
|
syms: TokenStream2,
|
||||||
|
exps: TokenStream2,
|
||||||
|
disp: TokenStream2,
|
||||||
|
state: &Path,
|
||||||
|
command: &Path,
|
||||||
|
) -> TokenStream2 {
|
||||||
|
quote! {
|
||||||
|
impl<T: #state> dizzle::Namespaced<T> for #command {
|
||||||
|
fn namespaced_symbol <L: Symbol> (state: &T, word: L) -> Perhaps<#command> {
|
||||||
|
if let Some(word) = word.word()? {
|
||||||
|
#syms
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
fn namespaced_expression <L: 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<T: #state> dizzle::Dispatch<T> for #command {
|
||||||
|
fn dispatch (self, state: &mut T) -> Perhaps<Self> {
|
||||||
|
match self {
|
||||||
|
#disp
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
attribute!(command {
|
attribute!(command {
|
||||||
|
|
@ -393,8 +481,8 @@ attribute!(keyword {
|
||||||
append(out, quote! {
|
append(out, quote! {
|
||||||
#item
|
#item
|
||||||
|
|
||||||
impl<'n> Namespace<'n, #ident> for #state {
|
impl Namespace<#ident> for #state {
|
||||||
symbols!('n |_state: #state| -> #ident { #body, });
|
def_namespace_symbols!(|_state: #state| -> #ident { #body, });
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,32 +73,32 @@ pub trait HasScene: AsRefOpt<Scene> + AsMutOpt<Scene> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: HasScene
|
impl<T: HasScene
|
||||||
+ for<'a> Namespace<'a, usize>
|
+ Namespace<usize>
|
||||||
+ for<'a> Namespace<'a, Arc<str>>
|
+ Namespace<Arc<str>>
|
||||||
+ for<'a> Namespace<'a, ItemTheme>
|
+ Namespace<ItemTheme>
|
||||||
> SceneController for T {}
|
> SceneController for T {}
|
||||||
|
|
||||||
#[tek_proc::commands(SceneCommand)]
|
#[tek_proc::commands(SceneCommand)]
|
||||||
pub trait SceneController: HasScene
|
pub trait SceneController: HasScene
|
||||||
+ for<'a> Namespace<'a, usize>
|
+ Namespace<usize>
|
||||||
+ for<'a> Namespace<'a, Arc<str>>
|
+ Namespace<Arc<str>>
|
||||||
+ for<'a> Namespace<'a, ItemTheme>
|
+ Namespace<ItemTheme>
|
||||||
{
|
{
|
||||||
#[command(SetSize = "scene/size")]
|
#[command(SetSize = "scene/size")]
|
||||||
fn scene_set_size (&mut self, size: usize) -> Perhaps<SceneCommand>
|
fn scene_set_size (&mut self, size: usize) -> Perhaps<SceneCommand>
|
||||||
where Self: for<'a> Namespace<'a, usize>
|
where Self: Namespace<usize>
|
||||||
{
|
{
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
#[command(SetZoom = "scene/zoom")]
|
#[command(SetZoom = "scene/zoom")]
|
||||||
fn scene_set_zoom (&mut self, size: usize) -> Perhaps<SceneCommand>
|
fn scene_set_zoom (&mut self, size: usize) -> Perhaps<SceneCommand>
|
||||||
where Self: for<'a> Namespace<'a, usize>
|
where Self: Namespace<usize>
|
||||||
{
|
{
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
#[command(SetName = "scene/name")]
|
#[command(SetName = "scene/name")]
|
||||||
fn scene_set_name (&mut self, name: Arc<str>) -> Perhaps<SceneCommand>
|
fn scene_set_name (&mut self, name: Arc<str>) -> Perhaps<SceneCommand>
|
||||||
where Self: for<'a> Namespace<'a, Arc<str>>
|
where Self: Namespace<Arc<str>>
|
||||||
{
|
{
|
||||||
Ok(self.scene_mut().map(|scene|swap_value(
|
Ok(self.scene_mut().map(|scene|swap_value(
|
||||||
&mut scene.name,
|
&mut scene.name,
|
||||||
|
|
@ -108,7 +108,7 @@ pub trait SceneController: HasScene
|
||||||
}
|
}
|
||||||
#[command(SetColor = "scene/color")]
|
#[command(SetColor = "scene/color")]
|
||||||
fn scene_set_color (&mut self, color: ItemTheme) -> Perhaps<SceneCommand>
|
fn scene_set_color (&mut self, color: ItemTheme) -> Perhaps<SceneCommand>
|
||||||
where Self: for<'a> Namespace<'a, ItemTheme>
|
where Self: Namespace<ItemTheme>
|
||||||
{
|
{
|
||||||
Ok(self.scene_mut().map(|scene|swap_value(
|
Ok(self.scene_mut().map(|scene|swap_value(
|
||||||
&mut scene.color,
|
&mut scene.color,
|
||||||
|
|
@ -172,16 +172,16 @@ pub trait HasScenes: AsRef<Vec<Scene>> + AsMut<Vec<Scene>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: HasScenes
|
impl<T: HasScenes
|
||||||
+ for<'a> Namespace<'a, usize>
|
+ Namespace<usize>
|
||||||
+ for<'a> Namespace<'a, Option<ItemTheme>>
|
+ Namespace<Option<ItemTheme>>
|
||||||
+ for<'a> Namespace<'a, Option<Arc<str>>>
|
+ Namespace<Option<Arc<str>>>
|
||||||
> ScenesController for T {}
|
> ScenesController for T {}
|
||||||
|
|
||||||
#[tek_proc::commands(ScenesCommand)]
|
#[tek_proc::commands(ScenesCommand)]
|
||||||
pub trait ScenesController: HasScenes
|
pub trait ScenesController: HasScenes
|
||||||
+ for<'a> Namespace<'a, usize>
|
+ Namespace<usize>
|
||||||
+ for<'a> Namespace<'a, Option<ItemTheme>>
|
+ Namespace<Option<ItemTheme>>
|
||||||
+ for<'a> Namespace<'a, Option<Arc<str>>>
|
+ Namespace<Option<Arc<str>>>
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,18 +85,18 @@ pub trait HasTrack: AsRefOpt<Track> + AsMutOpt<Track> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: HasTrack
|
impl<T: HasTrack
|
||||||
+ for<'a> Namespace<'a, usize>
|
+ Namespace<usize>
|
||||||
+ for<'a> Namespace<'a, Arc<str>>
|
+ Namespace<Arc<str>>
|
||||||
+ for<'a> Namespace<'a, ItemTheme>
|
+ Namespace<ItemTheme>
|
||||||
+ for<'a> Namespace<'a, Option<bool>>
|
+ Namespace<Option<bool>>
|
||||||
> TrackController for T {}
|
> TrackController for T {}
|
||||||
|
|
||||||
#[tek_proc::commands(TrackCommand)]
|
#[tek_proc::commands(TrackCommand)]
|
||||||
pub trait TrackController: HasTrack
|
pub trait TrackController: HasTrack
|
||||||
+ for<'a> Namespace<'a, usize>
|
+ Namespace<usize>
|
||||||
+ for<'a> Namespace<'a, Arc<str>>
|
+ Namespace<Arc<str>>
|
||||||
+ for<'a> Namespace<'a, ItemTheme>
|
+ Namespace<ItemTheme>
|
||||||
+ for<'a> Namespace<'a, Option<bool>>
|
+ Namespace<Option<bool>>
|
||||||
{
|
{
|
||||||
#[command(Stop = "track/stop")]
|
#[command(Stop = "track/stop")]
|
||||||
fn track_stop (&mut self) -> Perhaps<TrackCommand> {
|
fn track_stop (&mut self) -> Perhaps<TrackCommand> {
|
||||||
|
|
@ -259,24 +259,24 @@ pub trait HasTracks: AsRef<Vec<Track>> + AsMut<Vec<Track>> + HasClock + HasTrack
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: HasTracks
|
impl<T: HasTracks
|
||||||
+ for<'a> Namespace<'a, usize>
|
+ Namespace<usize>
|
||||||
+ for<'a> Namespace<'a, Arc<[Connect]>>
|
+ Namespace<Arc<[Connect]>>
|
||||||
+ for<'a> Namespace<'a, Option<usize>>
|
+ Namespace<Option<usize>>
|
||||||
+ for<'a> Namespace<'a, Option<Arc<str>>>
|
+ Namespace<Option<Arc<str>>>
|
||||||
+ for<'a> Namespace<'a, Option<ItemTheme>>
|
+ Namespace<Option<ItemTheme>>
|
||||||
+ for<'a> Namespace<'a, Option<Arc<[Connect]>>>
|
+ Namespace<Option<Arc<[Connect]>>>
|
||||||
+ for<'a> Namespace<'a, Option<Vec<Option<Arc<RwLock<MidiClip>>>>>>
|
+ Namespace<Option<Vec<Option<Arc<RwLock<MidiClip>>>>>>
|
||||||
> TracksController for T {}
|
> TracksController for T {}
|
||||||
|
|
||||||
#[tek_proc::commands(TracksCommand)]
|
#[tek_proc::commands(TracksCommand)]
|
||||||
pub trait TracksController: HasTracks
|
pub trait TracksController: HasTracks
|
||||||
+ for<'a> Namespace<'a, usize>
|
+ Namespace<usize>
|
||||||
+ for<'a> Namespace<'a, Arc<[Connect]>>
|
+ Namespace<Arc<[Connect]>>
|
||||||
+ for<'a> Namespace<'a, Option<usize>>
|
+ Namespace<Option<usize>>
|
||||||
+ for<'a> Namespace<'a, Option<Arc<str>>>
|
+ Namespace<Option<Arc<str>>>
|
||||||
+ for<'a> Namespace<'a, Option<ItemTheme>>
|
+ Namespace<Option<ItemTheme>>
|
||||||
+ for<'a> Namespace<'a, Option<Arc<[Connect]>>>
|
+ Namespace<Option<Arc<[Connect]>>>
|
||||||
+ for<'a> Namespace<'a, Option<Vec<Option<Arc<RwLock<MidiClip>>>>>>
|
+ Namespace<Option<Vec<Option<Arc<RwLock<MidiClip>>>>>>
|
||||||
{
|
{
|
||||||
|
|
||||||
#[command(Stop = "tracks/stop")]
|
#[command(Stop = "tracks/stop")]
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,9 @@ impl App {
|
||||||
|
|
||||||
#[tek_proc::commands(BrowseCommand = "browse")]
|
#[tek_proc::commands(BrowseCommand = "browse")]
|
||||||
pub trait BrowseController:
|
pub trait BrowseController:
|
||||||
for<'a> Namespace<'a, usize> +
|
Namespace<usize> +
|
||||||
for<'a> Namespace<'a, PathBuf> +
|
Namespace<PathBuf> +
|
||||||
for<'a> Namespace<'a, Arc<str>>
|
Namespace<Arc<str>>
|
||||||
{
|
{
|
||||||
/// Toggle visibility of browser
|
/// Toggle visibility of browser
|
||||||
#[command(Show = "show")]
|
#[command(Show = "show")]
|
||||||
|
|
|
||||||
|
|
@ -66,16 +66,16 @@ pub trait HasClock: AsRef<Clock> + AsMut<Clock> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: HasClock
|
impl<T: HasClock
|
||||||
+ for<'a> Namespace<'a, u32>
|
+ Namespace<u32>
|
||||||
+ for<'a> Namespace<'a, f64>
|
+ Namespace<f64>
|
||||||
+ for<'a> Namespace<'a, Option<u32>>
|
+ Namespace<Option<u32>>
|
||||||
> ClockController for T {}
|
> ClockController for T {}
|
||||||
|
|
||||||
#[tek_proc::commands(ClockCommand = "clock")]
|
#[tek_proc::commands(ClockCommand = "clock")]
|
||||||
pub trait ClockController: HasClock
|
pub trait ClockController: HasClock
|
||||||
+ for<'a> Namespace<'a, u32>
|
+ Namespace<u32>
|
||||||
+ for<'a> Namespace<'a, f64>
|
+ Namespace<f64>
|
||||||
+ for<'a> Namespace<'a, Option<u32>>
|
+ Namespace<Option<u32>>
|
||||||
{
|
{
|
||||||
#[command(SeekUsec = "usec")]
|
#[command(SeekUsec = "usec")]
|
||||||
fn seek_usec (&mut self, usec: f64) -> Perhaps<ClockCommand> {
|
fn seek_usec (&mut self, usec: f64) -> Perhaps<ClockCommand> {
|
||||||
|
|
@ -468,3 +468,58 @@ impl_time_unit!(Ppq);
|
||||||
impl_time_unit!(Pulse);
|
impl_time_unit!(Pulse);
|
||||||
impl_time_unit!(Bpm);
|
impl_time_unit!(Bpm);
|
||||||
impl_time_unit!(LaunchSync);
|
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<Tui> {
|
||||||
|
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<Tui> {
|
||||||
|
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<Tui> {
|
||||||
|
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)),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,22 +67,22 @@ pub trait HasEditor: AsRefOpt<MidiEditor> + AsMutOpt<MidiEditor> {
|
||||||
impl<T: AsRefOpt<MidiEditor>+AsMutOpt<MidiEditor>> HasEditor for T {}
|
impl<T: AsRefOpt<MidiEditor>+AsMutOpt<MidiEditor>> HasEditor for T {}
|
||||||
|
|
||||||
impl<T: HasEditor
|
impl<T: HasEditor
|
||||||
+ for<'a> Namespace<'a, u32>
|
+ Namespace<u32>
|
||||||
+ for<'a> Namespace<'a, f64>
|
+ Namespace<f64>
|
||||||
+ for<'a> Namespace<'a, bool>
|
+ Namespace<bool>
|
||||||
+ for<'a> Namespace<'a, usize>
|
+ Namespace<usize>
|
||||||
+ for<'a> Namespace<'a, Option<u32>>
|
+ Namespace<Option<u32>>
|
||||||
+ for<'a> Namespace<'a, Option<Arc<RwLock<MidiClip>>>>
|
+ Namespace<Option<Arc<RwLock<MidiClip>>>>
|
||||||
> MidiEditController for T {}
|
> MidiEditController for T {}
|
||||||
|
|
||||||
#[tek_proc::commands(MidiEditCommand = "edit")]
|
#[tek_proc::commands(MidiEditCommand = "edit")]
|
||||||
pub trait MidiEditController: HasEditor
|
pub trait MidiEditController: HasEditor
|
||||||
+ for<'a> Namespace<'a, u32>
|
+ Namespace<u32>
|
||||||
+ for<'a> Namespace<'a, f64>
|
+ Namespace<f64>
|
||||||
+ for<'a> Namespace<'a, bool>
|
+ Namespace<bool>
|
||||||
+ for<'a> Namespace<'a, usize>
|
+ Namespace<usize>
|
||||||
+ for<'a> Namespace<'a, Option<u32>>
|
+ Namespace<Option<u32>>
|
||||||
+ for<'a> Namespace<'a, Option<Arc<RwLock<MidiClip>>>>
|
+ Namespace<Option<Arc<RwLock<MidiClip>>>>
|
||||||
{
|
{
|
||||||
#[command(Show = "show")]
|
#[command(Show = "show")]
|
||||||
fn show (&mut self, clip: Option<Arc<RwLock<MidiClip>>>) -> Perhaps<MidiEditCommand> {
|
fn show (&mut self, clip: Option<Arc<RwLock<MidiClip>>>) -> Perhaps<MidiEditCommand> {
|
||||||
|
|
|
||||||
|
|
@ -70,28 +70,28 @@ pub trait HasPool: AsRef<Pool> + AsMut<Pool> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: HasPool
|
impl<T: HasPool
|
||||||
+ for<'a> Namespace<'a, bool>
|
+ Namespace<bool>
|
||||||
+ for<'a> Namespace<'a, usize>
|
+ Namespace<usize>
|
||||||
+ for<'a> Namespace<'a, Arc<str>>
|
+ Namespace<Arc<str>>
|
||||||
+ for<'a> Namespace<'a, PathBuf>
|
+ Namespace<PathBuf>
|
||||||
+ for<'a> Namespace<'a, MidiClip>
|
+ Namespace<MidiClip>
|
||||||
+ for<'a> Namespace<'a, ItemColor>
|
+ Namespace<ItemColor>
|
||||||
+ for<'a> Namespace<'a, PoolCommand>
|
+ Namespace<PoolCommand>
|
||||||
+ for<'a> Namespace<'a, PoolCommand>
|
+ Namespace<PoolCommand>
|
||||||
+ for<'a> Namespace<'a, BrowseCommand>
|
+ Namespace<BrowseCommand>
|
||||||
> PoolController for T {}
|
> PoolController for T {}
|
||||||
|
|
||||||
#[tek_proc::commands(PoolCommand = "pool")]
|
#[tek_proc::commands(PoolCommand = "pool")]
|
||||||
pub trait PoolController: HasPool
|
pub trait PoolController: HasPool
|
||||||
+ for<'a> Namespace<'a, bool>
|
+ Namespace<bool>
|
||||||
+ for<'a> Namespace<'a, usize>
|
+ Namespace<usize>
|
||||||
+ for<'a> Namespace<'a, Arc<str>>
|
+ Namespace<Arc<str>>
|
||||||
+ for<'a> Namespace<'a, PathBuf>
|
+ Namespace<PathBuf>
|
||||||
+ for<'a> Namespace<'a, MidiClip>
|
+ Namespace<MidiClip>
|
||||||
+ for<'a> Namespace<'a, ItemColor>
|
+ Namespace<ItemColor>
|
||||||
+ for<'a> Namespace<'a, PoolCommand>
|
+ Namespace<PoolCommand>
|
||||||
+ for<'a> Namespace<'a, PoolCommand>
|
+ Namespace<PoolCommand>
|
||||||
+ for<'a> Namespace<'a, BrowseCommand>
|
+ Namespace<BrowseCommand>
|
||||||
{
|
{
|
||||||
|
|
||||||
#[command(Show = "show")]
|
#[command(Show = "show")]
|
||||||
|
|
@ -139,7 +139,7 @@ pub trait PoolController: HasPool
|
||||||
for event in events.iter() {
|
for event in events.iter() {
|
||||||
clip.notes[event.0 as usize].push(event.2);
|
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
|
///// Export to file
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ impl<T: AsRef<Sampler> + AsMut<Sampler>> HasSampler for T {}
|
||||||
|
|
||||||
#[tek_proc::commands(SamplerCommand = "sampler")]
|
#[tek_proc::commands(SamplerCommand = "sampler")]
|
||||||
pub trait SamplerController: HasSampler
|
pub trait SamplerController: HasSampler
|
||||||
+ for<'a> Namespace<'a, usize>
|
+ Namespace<usize>
|
||||||
{
|
{
|
||||||
|
|
||||||
#[command(RecordToggle = "rec-toggle")]
|
#[command(RecordToggle = "rec-toggle")]
|
||||||
|
|
@ -30,10 +30,10 @@ pub trait SamplerController: HasSampler
|
||||||
{
|
{
|
||||||
let sampler = self.sampler_mut();
|
let sampler = self.sampler_mut();
|
||||||
let recording = sampler.recording.as_ref().map(|x|x.0);
|
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
|
// autoslice: continue recording at next slot
|
||||||
if recording != Some(slot) {
|
if recording != Some(slot) {
|
||||||
SamplerCommand::RecordBegin { slot }.act(self)
|
SamplerCommand::RecordBegin { slot }.dispatch(self)
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -246,14 +246,14 @@ pub trait HasMidiClip {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: HasMidiClip
|
impl<T: HasMidiClip
|
||||||
+ for<'a> Namespace<'a, Option<bool>>
|
+ Namespace<Option<bool>>
|
||||||
+ for<'a> Namespace<'a, Option<ItemTheme>>
|
+ Namespace<Option<ItemTheme>>
|
||||||
> MidiClipController for T {}
|
> MidiClipController for T {}
|
||||||
|
|
||||||
#[tek_proc::commands(MidiClipCommand)]
|
#[tek_proc::commands(MidiClipCommand)]
|
||||||
pub trait MidiClipController: HasMidiClip
|
pub trait MidiClipController: HasMidiClip
|
||||||
+ for<'a> Namespace<'a, Option<bool>>
|
+ Namespace<Option<bool>>
|
||||||
+ for<'a> Namespace<'a, Option<ItemTheme>>
|
+ Namespace<Option<ItemTheme>>
|
||||||
{
|
{
|
||||||
|
|
||||||
#[command(SetColor = "clip/color")]
|
#[command(SetColor = "clip/color")]
|
||||||
|
|
|
||||||
24
src/tek.edn
24
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)))
|
(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 :back (@escape (back)))
|
||||||
(keys :confirm (@enter confirm))
|
(keys :confirm (@enter :confirm))
|
||||||
(keys :axis/x (@left app/dec :x) (@right app/inc :x))
|
(keys :axis/x (@left dec :x) (@right inc :x))
|
||||||
(keys :axis/x2 (@shift/left app/dec :x2) (@shift/right app/inc :x2))
|
(keys :axis/x2 (@shift/left dec :x2) (@shift/right inc :x2))
|
||||||
(keys :axis/y (@up app/dec :y) (@down app/inc :y))
|
(keys :axis/y (@up (dec :y)) (@down (inc :y)))
|
||||||
(keys :axis/y2 (@shift/up app/dec :y2) (@shift/down app/inc :y2))
|
(keys :axis/y2 (@shift/up dec :y2) (@shift/down inc :y2))
|
||||||
(keys :axis/z (@minus app/dec :z) (@equal app/inc :z))
|
(keys :axis/z (@minus dec :z) (@equal inc :z))
|
||||||
(keys :axis/z2 (@underscore app/dec :z2) (@plus app/inc :z2))
|
(keys :axis/z2 (@underscore dec :z2) (@plus inc :z2))
|
||||||
(keys :axis/i (@comma app/dec :i) (@period app/inc :z))
|
(keys :axis/i (@comma dec :i) (@period inc :z))
|
||||||
(keys :axis/i2 (@lt app/dec :i2) (@gt app/inc :z2))
|
(keys :axis/i2 (@lt dec :i2) (@gt inc :z2))
|
||||||
(keys :axis/w (@openbracket app/dec :w) (@closebracket app/inc :w))
|
(keys :axis/w (@openbracket dec :w) (@closebracket inc :w))
|
||||||
(keys :axis/w2 (@openbrace app/dec :w2) (@closebrace app/inc :w2))
|
(keys :axis/w2 (@openbrace dec :w2) (@closebrace inc :w2))
|
||||||
(keys :focus)
|
(keys :focus)
|
||||||
(keys :editor (see :axis/i :axis/i2 :axis/y
|
(keys :editor (see :axis/i :axis/i2 :axis/y
|
||||||
:page :editor/view :editor/add :editor/del))
|
:page :editor/view :editor/add :editor/del))
|
||||||
|
|
|
||||||
367
src/tek.rs
367
src/tek.rs
|
|
@ -620,7 +620,7 @@ mod app {
|
||||||
/// ```
|
/// ```
|
||||||
/// use tek::{HasTracks, HasScenes, TracksView, ScenesView};
|
/// use tek::{HasTracks, HasScenes, TracksView, ScenesView};
|
||||||
/// let mut app = tek::App::default();
|
/// 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();
|
/// let _ = app.update_clock();
|
||||||
/// app.project.editor = Some(Default::default());
|
/// app.project.editor = Some(Default::default());
|
||||||
/// //let _: Vec<_> = app.project.inputs_with_sizes().collect();
|
/// //let _: Vec<_> = app.project.inputs_with_sizes().collect();
|
||||||
|
|
@ -639,21 +639,27 @@ mod app {
|
||||||
/// let _ = app.project.h_scenes();
|
/// let _ = app.project.h_scenes();
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
#[namespace(u8)]
|
#[namespace(Arc<[Connect]> App::get_arc_array_connect)]
|
||||||
#[namespace(isize)]
|
|
||||||
#[namespace(ItemTheme)]
|
|
||||||
#[namespace(Arc<str> App::get_arc_str)]
|
#[namespace(Arc<str> 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(Color App::get_color)]
|
||||||
#[namespace(Option<u7> App::get_opt_u7)]
|
|
||||||
#[namespace(Option<u16> App::get_opt_u16)]
|
|
||||||
#[namespace(Option<usize> App::get_opt_usize)]
|
|
||||||
#[namespace(Option<Arc<RwLock<MidiClip>>> App::get_clip)]
|
|
||||||
#[namespace(Dialog App::get_dialog)]
|
|
||||||
#[namespace(ControlAxis App::get_axis)]
|
#[namespace(ControlAxis App::get_axis)]
|
||||||
|
#[namespace(Dialog App::get_dialog)]
|
||||||
|
#[namespace(ItemTheme)]
|
||||||
|
#[namespace(Option<Arc<RwLock<MidiClip>>> App::get_clip)]
|
||||||
|
#[namespace(Option<Arc<[Connect]>> App::get_opt_arc_array_connect)]
|
||||||
|
#[namespace(Option<Arc<str>> App::get_opt_arc_str)]
|
||||||
|
#[namespace(Option<ItemTheme> App::get_opt_itemtheme)]
|
||||||
|
#[namespace(Option<Vec<Option<Arc<RwLock<MidiClip>>>>> App::get_opt_vec_opt_arc_rwlock_midiclip)]
|
||||||
|
#[namespace(Option<u16> App::get_opt_u16)]
|
||||||
|
#[namespace(Option<u7> App::get_opt_u7)]
|
||||||
|
#[namespace(Option<usize> App::get_opt_usize)]
|
||||||
|
#[namespace(Option<bool> 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 {
|
pub struct App {
|
||||||
/// Exit flag
|
/// Exit flag
|
||||||
pub exit: Exit,
|
pub exit: Exit,
|
||||||
|
|
@ -827,12 +833,34 @@ mod app {
|
||||||
|
|
||||||
fn get_axis (&self, src: impl Language) -> Perhaps<ControlAxis> {
|
fn get_axis (&self, src: impl Language) -> Perhaps<ControlAxis> {
|
||||||
Ok(src.word()?.map(|word|Ok(match word {
|
Ok(src.word()?.map(|word|Ok(match word {
|
||||||
"x" => ControlAxis::X,
|
":x" => ControlAxis::X,
|
||||||
"y" => ControlAxis::Y,
|
":y" => ControlAxis::Y,
|
||||||
_ => return Err(format!("unknown axis {word}"))
|
_ => return Err(format!("unknown axis {word}"))
|
||||||
})).transpose()?)
|
})).transpose()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_opt_itemtheme (&self, src: impl Language) -> Perhaps<Option<ItemTheme>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn get_opt_vec_opt_arc_rwlock_midiclip (&self, src: impl Language) -> Perhaps<Option<Vec<Option<Arc<RwLock<MidiClip>>>>>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn get_opt_arc_array_connect (&self, src: impl Language) -> Perhaps<Option<Arc<[Connect]>>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_arc_array_connect (&self, src: impl Language) -> Perhaps<Arc<[Connect]>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_opt_arc_str (&self, src: impl Language) -> Perhaps<Option<Arc<str>>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_opt_bool (&self, src: impl Language) -> Perhaps<Option<bool>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -883,52 +911,155 @@ mod bind {
|
||||||
pub Arc<Box<dyn Fn()->bool + Send + Sync>>
|
pub Arc<Box<dyn Fn()->bool + Send + Sync>>
|
||||||
);
|
);
|
||||||
|
|
||||||
tui_keys!(self: App, input {
|
struct AppDispatch;
|
||||||
let commands = collect_commands(self, input)?;
|
|
||||||
let results = execute_commands(self, commands)?;
|
|
||||||
self.history.extend(results.into_iter());
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
|
|
||||||
fn collect_commands (app: &App, input: &TuiEvent)
|
tui_keys!(self: App, input {
|
||||||
-> Usually<Vec<AppCommand>>
|
|
||||||
{
|
if let Some(mode) = self.mode.as_ref().and_then(
|
||||||
let mut commands = vec![];
|
|m|self.config.get_mode(m)
|
||||||
app.mode.as_ref().and_then(|m|app.config.get_mode(m)).map(|mode|{
|
).as_ref().map(Arc::clone) {
|
||||||
|
let binds = self.config.binds.clone();
|
||||||
for id in mode.keys.iter() {
|
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) {
|
&& let Some(bindings) = event_map.query(input) {
|
||||||
for binding in bindings {
|
for binding in bindings {
|
||||||
for command in binding.commands.iter() {
|
for command in binding.commands.iter() {
|
||||||
if let Some(command) = app.namespace(command)? as Option<AppCommand> {
|
for namespace in App::commands() {
|
||||||
commands.push(command)
|
if namespace(self, command)? {
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok::<_, Box<dyn Error>>(())
|
}
|
||||||
}).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<AppCommand>)
|
impl_from!(AppCommand: |x: AxisCommand| AppCommand::Axis(x));
|
||||||
-> Usually<Vec<(AppCommand, Option<AppCommand>)>>
|
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<Item = fn(&mut Self, &str) -> Usually<bool>> {
|
||||||
|
[
|
||||||
|
Self::command::<AxisCommand>,
|
||||||
|
//Self::command::<BrowseCommand>,
|
||||||
|
Self::command::<DialogCommand>,
|
||||||
|
//Self::command::<MidiClipCommand>,
|
||||||
|
//Self::command::<PoolCommand>,
|
||||||
|
Self::command::<SceneCommand>,
|
||||||
|
Self::command::<ScenesCommand>,
|
||||||
|
Self::command::<TrackCommand>,
|
||||||
|
Self::command::<TracksCommand>,
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn command <
|
||||||
|
T: Dispatch<Self> + Clone + Into<AppCommand>
|
||||||
|
> (state: &mut Self, src: &str) -> Usually<bool> where
|
||||||
|
Self: Namespace<T>,
|
||||||
{
|
{
|
||||||
let mut history = vec![];
|
if let Some(command) = Namespace::<T>::namespace(state, src)? {
|
||||||
for command in commands.into_iter() {
|
match command.clone().dispatch(state) {
|
||||||
let result = command.clone().act(app);
|
|
||||||
match result {
|
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
history.push((command, None));
|
state.history.push((command.into(), None));
|
||||||
return Err(err)
|
return Err(err)
|
||||||
},
|
},
|
||||||
Ok(undo) => {
|
Ok(undo) => {
|
||||||
history.push((command, undo));
|
state.history.push((command.into(), undo.map(Into::into)));
|
||||||
|
return Ok(true)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Ok(history)
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tek_proc::commands(DialogCommand)]
|
||||||
|
impl App {
|
||||||
|
/// Cancel current dialog
|
||||||
|
#[command(Cancel = "cancel")]
|
||||||
|
pub fn cancel (&mut self) -> Perhaps<DialogCommand> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
/// Confirm current dialog selection.
|
||||||
|
#[command(Confirm = "confirm")]
|
||||||
|
pub fn confirm (&mut self) -> Perhaps<DialogCommand> {
|
||||||
|
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<DialogCommand> {
|
||||||
|
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<DialogCommand> {
|
||||||
|
//todo!()
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tek_proc::commands(AxisCommand)]
|
||||||
|
impl App {
|
||||||
|
/// Increment a given data axis.
|
||||||
|
#[command(Inc = "inc")]
|
||||||
|
pub fn inc (&mut self, axis: ControlAxis) -> Perhaps<AxisCommand> {
|
||||||
|
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<AxisCommand> {
|
||||||
|
Ok(match (&self.dialog, axis) {
|
||||||
|
(Dialog::None, _) => None,
|
||||||
|
(Dialog::Menu(_, _), ControlAxis::Y) => {
|
||||||
|
DialogCommand::SetDialog { dialog: self.dialog.menu_prev() }.dispatch(self)?;
|
||||||
|
None
|
||||||
|
},
|
||||||
|
_ => todo!()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn load_bind (binds: &Binds, name: &impl AsRef<str>, body: &impl Language) -> Usually<()> {
|
pub(crate) fn load_bind (binds: &Binds, name: &impl AsRef<str>, body: &impl Language) -> Usually<()> {
|
||||||
|
|
@ -1012,75 +1143,6 @@ mod bind {
|
||||||
|
|
||||||
impl_debug!(Condition |self, w| { write!(w, "*") });
|
impl_debug!(Condition |self, w| { write!(w, "*") });
|
||||||
|
|
||||||
#[tek_proc::commands(AppCommand = "app")]
|
|
||||||
impl App {
|
|
||||||
|
|
||||||
#[command(Nop = "nop")]
|
|
||||||
pub fn nop (&mut self) -> Perhaps<AppCommand> {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[command(Cancel = "cancel")]
|
|
||||||
pub fn cancel (&mut self) -> Perhaps<AppCommand> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[command(Inc = "inc")]
|
|
||||||
pub fn inc (&mut self, axis: ControlAxis) -> Perhaps<AppCommand> {
|
|
||||||
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<AppCommand> {
|
|
||||||
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<AppCommand> {
|
|
||||||
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<AppCommand> {
|
|
||||||
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.
|
/// A control axis.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
|
@ -1293,6 +1355,18 @@ mod device {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn view_device (state: &App) -> impl Draw<Tui> {
|
||||||
|
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 arrange; pub use self::arrange::*;
|
||||||
pub mod browse; pub use self::browse::*;
|
pub mod browse; pub use self::browse::*;
|
||||||
pub mod pool; pub use self::pool::*;
|
pub mod pool; pub use self::pool::*;
|
||||||
|
|
@ -1346,8 +1420,10 @@ mod draw {
|
||||||
|
|
||||||
impl Keywords<Tui, XYWH<u16>> for App {
|
impl Keywords<Tui, XYWH<u16>> for App {
|
||||||
fn keywords () -> impl Iterator<Item = fn(&Self, &mut Tui, &str) -> Perhaps<XYWH<u16>>> {
|
fn keywords () -> impl Iterator<Item = fn(&Self, &mut Tui, &str) -> Perhaps<XYWH<u16>>> {
|
||||||
[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()
|
.into_iter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1521,73 +1597,6 @@ mod draw {
|
||||||
Ok(Some(to.area().into()))
|
Ok(Some(to.area().into()))
|
||||||
}).min_w(w).exact_h(h)
|
}).min_w(w).exact_h(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn view_device (state: &App) -> impl Draw<Tui> {
|
|
||||||
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<Tui> {
|
|
||||||
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<Tui> {
|
|
||||||
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<Tui> {
|
|
||||||
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 bg = tengri::ratatui::style::Color::Red;
|
||||||
/// let fg = tengri::ratatui::style::Color::Green;
|
/// let fg = tengri::ratatui::style::Color::Green;
|
||||||
|
|
|
||||||
2
tengri
2
tengri
|
|
@ -1 +1 @@
|
||||||
Subproject commit b7f4d55e1d67d3481ecee14f693ca3e0a9426a6c
|
Subproject commit 1f541407597c7866cf1450d03967ab4711d28d29
|
||||||
Loading…
Add table
Add a link
Reference in a new issue