mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-09-18 12:56:42 +02:00
Compare commits
3 commits
9f2327f96c
...
d9e17cd348
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9e17cd348 | ||
|
|
da2099c52e | ||
|
|
406aabde6e |
16 changed files with 947 additions and 793 deletions
|
|
@ -11,7 +11,8 @@ name = "tek"
|
||||||
path = "src/tek.rs"
|
path = "src/tek.rs"
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")']
|
[target.'cfg(target_os = "linux")']
|
||||||
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
linker = "clang"
|
||||||
|
rustflags = ["-Clink-arg=-fuse-ld=mold", "-Clink-arg=-Wl,--no-rosegment"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tek_proc = { path = "./proc" }
|
tek_proc = { path = "./proc" }
|
||||||
|
|
|
||||||
8
Justfile
8
Justfile
|
|
@ -1,7 +1,11 @@
|
||||||
#export RUSTFLAGS := "--cfg procmacro2_semver_exempt -Zmacro-backtrace -Clink-arg=-fuse-ld=mold"
|
#export RUSTFLAGS := "--cfg procmacro2_semver_exempt -Zmacro-backtrace -Clink-arg=-fuse-ld=mold"
|
||||||
export RUST_BACKTRACE := "1"
|
export RUST_BACKTRACE := "1"
|
||||||
|
|
||||||
default +ARGS="new":
|
[default]
|
||||||
|
list:
|
||||||
|
just -l
|
||||||
|
|
||||||
|
now +ARGS="new":
|
||||||
cargo run -- {{ARGS}}
|
cargo run -- {{ARGS}}
|
||||||
|
|
||||||
doc +ARGS="":
|
doc +ARGS="":
|
||||||
|
|
@ -44,7 +48,7 @@ run-init:
|
||||||
rm -rf ~/.config/tek && {{debug}}
|
rm -rf ~/.config/tek && {{debug}}
|
||||||
|
|
||||||
prof:
|
prof:
|
||||||
CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph --
|
CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph -- new
|
||||||
|
|
||||||
release := "reset && cargo run --release --"
|
release := "reset && cargo run --release --"
|
||||||
release:
|
release:
|
||||||
|
|
|
||||||
226
proc/src/lib.rs
226
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 {
|
||||||
|
|
@ -265,7 +353,7 @@ attribute!(command {
|
||||||
let mut variants_filtered = item.variants.clone();
|
let mut variants_filtered = item.variants.clone();
|
||||||
variants_filtered.clear();
|
variants_filtered.clear();
|
||||||
for variant in item.variants.iter_mut() {
|
for variant in item.variants.iter_mut() {
|
||||||
let Variant { attrs, ident, fields, discriminant } = variant;
|
let Variant { attrs, ident, fields, discriminant: _ } = variant;
|
||||||
let mut attrs_filtered = attrs.clone();
|
let mut attrs_filtered = attrs.clone();
|
||||||
attrs_filtered.clear();
|
attrs_filtered.clear();
|
||||||
for attr in attrs.iter() {
|
for attr in attrs.iter() {
|
||||||
|
|
@ -308,7 +396,7 @@ attribute!(command {
|
||||||
panic!()
|
panic!()
|
||||||
};
|
};
|
||||||
match fields {
|
match fields {
|
||||||
Fields::Named(fields) => todo!("named command fields"),
|
Fields::Named(_fields) => todo!("named command fields"),
|
||||||
Fields::Unnamed(fields) => {
|
Fields::Unnamed(fields) => {
|
||||||
let mut params = quote! {};
|
let mut params = quote! {};
|
||||||
let mut values = quote! {};
|
let mut values = quote! {};
|
||||||
|
|
@ -367,9 +455,11 @@ attribute!(keyword {
|
||||||
impl Parse for CustomAttributeItem {
|
impl Parse for CustomAttributeItem {
|
||||||
fn parse (input: ParseStream) -> Result<Self> {
|
fn parse (input: ParseStream) -> Result<Self> {
|
||||||
let mut item: ItemEnum = input.parse()?;
|
let mut item: ItemEnum = input.parse()?;
|
||||||
for Variant { attrs, ident, fields, discriminant } in item.variants.iter_mut() {
|
for Variant {
|
||||||
|
attrs, ident: _, fields: _, discriminant: _
|
||||||
|
} in item.variants.iter_mut() {
|
||||||
attrs.retain(|attr|if let syn::Meta::List(MetaList {
|
attrs.retain(|attr|if let syn::Meta::List(MetaList {
|
||||||
ref path, ref tokens, ..
|
ref path, tokens: ref _tokens, ..
|
||||||
}) = attr.meta && path == &Path::from(Ident::new(
|
}) = attr.meta && path == &Path::from(Ident::new(
|
||||||
"keyword", Span::call_site()
|
"keyword", Span::call_site()
|
||||||
)) {
|
)) {
|
||||||
|
|
@ -386,15 +476,15 @@ attribute!(keyword {
|
||||||
fn to_tokens (&self, out: &mut TokenStream2) {
|
fn to_tokens (&self, out: &mut TokenStream2) {
|
||||||
let Self(
|
let Self(
|
||||||
CustomAttributeMeta(state),
|
CustomAttributeMeta(state),
|
||||||
CustomAttributeItem(item, variants)
|
CustomAttributeItem(item, _variants)
|
||||||
) = self;
|
) = self;
|
||||||
let ident = &item.ident;
|
let ident = &item.ident;
|
||||||
let body = quote! {};
|
let body = quote! {};
|
||||||
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, });
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
pkgs.grcov
|
pkgs.grcov
|
||||||
pkgs.libclang
|
pkgs.libclang
|
||||||
pkgs.mold
|
pkgs.mold
|
||||||
|
pkgs.perf
|
||||||
pkgs.pkg-config
|
pkgs.pkg-config
|
||||||
pkgs.watchexec
|
pkgs.watchexec
|
||||||
];
|
];
|
||||||
|
|
|
||||||
39
src/deps.rs
Normal file
39
src/deps.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
pub extern crate atomic_float;
|
||||||
|
pub extern crate xdg;
|
||||||
|
pub extern crate tengri;
|
||||||
|
#[cfg(feature = "cli")]
|
||||||
|
pub(crate) use ::clap::{self, Parser, Subcommand};
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) use ::{
|
||||||
|
std::{
|
||||||
|
cmp::Ord,
|
||||||
|
collections::BTreeMap,
|
||||||
|
error::Error,
|
||||||
|
ffi::OsString,
|
||||||
|
fmt::{Write, Debug, Formatter},
|
||||||
|
fs::File,
|
||||||
|
ops::{Add, Sub, Mul, Div, Rem},
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::{Arc, RwLock, atomic::{AtomicBool, AtomicUsize, AtomicU64, Ordering::Relaxed}},
|
||||||
|
time::Duration,
|
||||||
|
thread::{spawn, JoinHandle},
|
||||||
|
},
|
||||||
|
xdg::{
|
||||||
|
BaseDirectories,
|
||||||
|
},
|
||||||
|
tengri::{
|
||||||
|
*,
|
||||||
|
dizzle::*,
|
||||||
|
midly::{
|
||||||
|
Smf, TrackEventKind, MidiMessage, Error as MidiError,
|
||||||
|
num::*,
|
||||||
|
live::*,
|
||||||
|
},
|
||||||
|
crossterm::event::{Event, KeyEvent},
|
||||||
|
ratatui::{
|
||||||
|
self,
|
||||||
|
prelude::{Rect, Style, Stylize, Buffer, Color::{self, *}},
|
||||||
|
widgets::{Widget, canvas::{Canvas, Line}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
use crate::*;
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// A scene consists of a set of clips to play together.
|
/// A scene consists of a set of clips to play together.
|
||||||
|
|
@ -73,32 +72,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 = "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 = "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 = "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,
|
||||||
|
|
@ -106,9 +105,9 @@ pub trait SceneController: HasScene
|
||||||
|name|SceneCommand::SetName { name }
|
|name|SceneCommand::SetName { name }
|
||||||
)).transpose()?.flatten())
|
)).transpose()?.flatten())
|
||||||
}
|
}
|
||||||
#[command(SetColor = "scene/color")]
|
#[command(SetColor = "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,17 +171,30 @@ pub trait HasScenes: AsRef<Vec<Scene>> + AsMut<Vec<Scene>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: HasScenes
|
impl<T: HasScenes
|
||||||
+ for<'a> Namespace<'a, usize>
|
+ HasTracks
|
||||||
+ for<'a> Namespace<'a, Option<ItemTheme>>
|
+ HasTrackScroll
|
||||||
+ for<'a> Namespace<'a, Option<Arc<str>>>
|
+ HasJack<'static>
|
||||||
|
+ Namespace<usize>
|
||||||
|
+ Namespace<Option<ItemTheme>>
|
||||||
|
+ 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>
|
+ HasTracks
|
||||||
+ for<'a> Namespace<'a, Option<ItemTheme>>
|
+ HasTrackScroll
|
||||||
+ for<'a> Namespace<'a, Option<Arc<str>>>
|
+ HasJack<'static>
|
||||||
|
+ Namespace<usize>
|
||||||
|
+ Namespace<Option<ItemTheme>>
|
||||||
|
+ Namespace<Option<Arc<str>>>
|
||||||
{
|
{
|
||||||
|
#[command(Add = "add")]
|
||||||
|
fn scenes_add (&mut self) -> Perhaps<ScenesCommand>
|
||||||
|
where Self: HasScenes + HasJack<'static>
|
||||||
|
{
|
||||||
|
let (_index, _) = self.scenes_add_one(None, None)?;
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -263,13 +275,13 @@ impl HasSceneScroll for App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn view_scene_name (
|
pub fn view_scene_name (
|
||||||
select: &Selection,
|
select: &Selection,
|
||||||
editor: Option<&MidiEditor>,
|
editor: Option<&MidiEditor>,
|
||||||
index: usize,
|
index: usize,
|
||||||
scene: &Scene,
|
scene: &Scene,
|
||||||
editing: bool
|
editing: bool
|
||||||
) -> impl Draw<Tui> {
|
) -> impl Draw<Tui> {
|
||||||
let h = if select.scene() == Some(index) && let Some(_editor) = editor {
|
let h = if select.scene() == Some(index) && let Some(_editor) = editor {
|
||||||
7
|
7
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -286,4 +298,4 @@ impl HasSceneScroll for App {
|
||||||
scene.color.base.term
|
scene.color.base.term
|
||||||
};
|
};
|
||||||
bg(c, south(a, b).align_nw()).exact_wh(20, h)
|
bg(c, south(a, b).align_nw()).exact_wh(20, h)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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> {
|
||||||
|
|
@ -104,19 +104,19 @@ pub trait TrackController: HasTrack
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
#[command(SetMute = "track/mute")]
|
#[command(SetMute = "track/mute")]
|
||||||
fn track_set_mute (&mut self, mute: Option<bool>) -> Perhaps<TrackCommand> {
|
fn track_set_mute (&mut self, _mute: Option<bool>) -> Perhaps<TrackCommand> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
#[command(SetSolo = "track/solo")]
|
#[command(SetSolo = "track/solo")]
|
||||||
fn track_set_solo (&mut self, solo: Option<bool>) -> Perhaps<TrackCommand> {
|
fn track_set_solo (&mut self, _solo: Option<bool>) -> Perhaps<TrackCommand> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
#[command(SetSize = "track/size")]
|
#[command(SetSize = "track/size")]
|
||||||
fn track_set_size (&mut self, size: usize) -> Perhaps<TrackCommand> {
|
fn track_set_size (&mut self, _size: usize) -> Perhaps<TrackCommand> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
#[command(SetZoom = "track/zoom")]
|
#[command(SetZoom = "track/zoom")]
|
||||||
fn track_set_zoom (&mut self, zoom: usize) -> Perhaps<TrackCommand> {
|
fn track_set_zoom (&mut self, _zoom: usize) -> Perhaps<TrackCommand> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
#[command(SetName = "track/name")]
|
#[command(SetName = "track/name")]
|
||||||
|
|
@ -259,27 +259,31 @@ pub trait HasTracks: AsRef<Vec<Track>> + AsMut<Vec<Track>> + HasClock + HasTrack
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: HasTracks
|
impl<T: HasTracks
|
||||||
+ for<'a> Namespace<'a, usize>
|
+ HasScenes
|
||||||
+ for<'a> Namespace<'a, Arc<[Connect]>>
|
+ HasJack<'static>
|
||||||
+ for<'a> Namespace<'a, Option<usize>>
|
+ Namespace<usize>
|
||||||
+ for<'a> Namespace<'a, Option<Arc<str>>>
|
+ Namespace<Arc<[Connect]>>
|
||||||
+ for<'a> Namespace<'a, Option<ItemTheme>>
|
+ Namespace<Option<usize>>
|
||||||
+ for<'a> Namespace<'a, Option<Arc<[Connect]>>>
|
+ Namespace<Option<Arc<str>>>
|
||||||
+ for<'a> Namespace<'a, Option<Vec<Option<Arc<RwLock<MidiClip>>>>>>
|
+ Namespace<Option<ItemTheme>>
|
||||||
|
+ Namespace<Option<Arc<[Connect]>>>
|
||||||
|
+ 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>
|
+ HasScenes
|
||||||
+ for<'a> Namespace<'a, Arc<[Connect]>>
|
+ HasJack<'static>
|
||||||
+ for<'a> Namespace<'a, Option<usize>>
|
+ Namespace<usize>
|
||||||
+ for<'a> Namespace<'a, Option<Arc<str>>>
|
+ Namespace<Arc<[Connect]>>
|
||||||
+ for<'a> Namespace<'a, Option<ItemTheme>>
|
+ Namespace<Option<usize>>
|
||||||
+ for<'a> Namespace<'a, Option<Arc<[Connect]>>>
|
+ Namespace<Option<Arc<str>>>
|
||||||
+ for<'a> Namespace<'a, Option<Vec<Option<Arc<RwLock<MidiClip>>>>>>
|
+ Namespace<Option<ItemTheme>>
|
||||||
|
+ Namespace<Option<Arc<[Connect]>>>
|
||||||
|
+ Namespace<Option<Vec<Option<Arc<RwLock<MidiClip>>>>>>
|
||||||
{
|
{
|
||||||
|
|
||||||
#[command(Stop = "tracks/stop")]
|
#[command(Stop = "stop")]
|
||||||
/// Stop all playing clips
|
/// Stop all playing clips
|
||||||
fn tracks_stop_all (&mut self) -> Perhaps<TracksCommand> {
|
fn tracks_stop_all (&mut self) -> Perhaps<TracksCommand> {
|
||||||
for track in self.tracks_mut().iter_mut() {
|
for track in self.tracks_mut().iter_mut() {
|
||||||
|
|
@ -288,7 +292,7 @@ pub trait TracksController: HasTracks
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command(Launch = "tracks/launch")]
|
#[command(Launch = "launch")]
|
||||||
/// Launch multiple clips
|
/// Launch multiple clips
|
||||||
fn tracks_launch (
|
fn tracks_launch (
|
||||||
&mut self, clips: Option<Vec<Option<Arc<RwLock<MidiClip>>>>>
|
&mut self, clips: Option<Vec<Option<Arc<RwLock<MidiClip>>>>>
|
||||||
|
|
@ -304,6 +308,14 @@ pub trait TracksController: HasTracks
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[command(Add = "add")]
|
||||||
|
fn tracks_add (&mut self) -> Perhaps<TracksCommand>
|
||||||
|
where Self: HasScenes + HasJack<'static>
|
||||||
|
{
|
||||||
|
let (_index, _) = self.tracks_add_one(None, None, [].into(), [].into())?;
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,17 @@ impl App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T:
|
||||||
|
Namespace<usize> +
|
||||||
|
Namespace<PathBuf> +
|
||||||
|
Namespace<Arc<str>>
|
||||||
|
> BrowseController for T {}
|
||||||
|
|
||||||
#[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")]
|
||||||
|
|
@ -54,13 +60,13 @@ pub trait BrowseController:
|
||||||
pub size: Sizer,
|
pub size: Sizer,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct EntriesIterator<'a, S: Screen> {
|
//pub(crate) struct EntriesIterator<'a, S: Screen> {
|
||||||
pub browser: &'a Browse,
|
//pub browser: &'a Browse,
|
||||||
pub offset: usize,
|
//pub offset: usize,
|
||||||
pub length: usize,
|
//pub length: usize,
|
||||||
pub index: usize,
|
//pub index: usize,
|
||||||
_screen: std::marker::PhantomData<S>
|
//_screen: std::marker::PhantomData<S>
|
||||||
}
|
//}
|
||||||
|
|
||||||
#[derive(Clone, Debug)] pub enum BrowseTarget {
|
#[derive(Clone, Debug)] pub enum BrowseTarget {
|
||||||
SaveProject,
|
SaveProject,
|
||||||
|
|
@ -112,40 +118,37 @@ impl Browse {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn _todo_stub_path_buf (&self) -> PathBuf { todo!() }
|
//fn tui (&self) -> impl Draw<Tui> {
|
||||||
fn _todo_stub_usize (&self) -> usize { todo!() }
|
//iter_south_fixed(1, ||self.tui_entries(), |entry, _index|entry.origin_w().full_w())
|
||||||
fn _todo_stub_arc_str (&self) -> Arc<str> { todo!() }
|
//}
|
||||||
fn tui (&self) -> impl Draw<Tui> {
|
//fn tui_entries (&self) -> EntriesIterator<'_, Tui> {
|
||||||
iter_south_fixed(1, ||self.tui_entries(), |entry, _index|entry.origin_w().full_w())
|
//EntriesIterator {
|
||||||
}
|
//offset: 0,
|
||||||
fn tui_entries (&self) -> EntriesIterator<'_, Tui> {
|
//index: 0,
|
||||||
EntriesIterator {
|
//length: self.dirs.len() + self.files.len(),
|
||||||
offset: 0,
|
//browser: self,
|
||||||
index: 0,
|
//_screen: Default::default(),
|
||||||
length: self.dirs.len() + self.files.len(),
|
//}
|
||||||
browser: self,
|
//}
|
||||||
_screen: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for EntriesIterator<'a, Tui> {
|
//impl<'a> Iterator for EntriesIterator<'a, Tui> {
|
||||||
type Item = impl Draw<Tui>;
|
//type Item = impl Draw<Tui>;
|
||||||
fn next (&mut self) -> Option<Self::Item> {
|
//fn next (&mut self) -> Option<Self::Item> {
|
||||||
let dirs = self.browser.dirs.len();
|
//let dirs = self.browser.dirs.len();
|
||||||
let files = self.browser.files.len();
|
//let files = self.browser.files.len();
|
||||||
let index = self.index;
|
//let index = self.index;
|
||||||
if self.index < dirs {
|
//if self.index < dirs {
|
||||||
self.index += 1;
|
//self.index += 1;
|
||||||
Some(bold(true, self.browser.dirs[index].1.as_str()))
|
//Some(bold(true, self.browser.dirs[index].1.as_str()))
|
||||||
} else if self.index < dirs + files {
|
//} else if self.index < dirs + files {
|
||||||
self.index += 1;
|
//self.index += 1;
|
||||||
Some(bold(false, self.browser.files[index - dirs].1.as_str()))
|
//Some(bold(false, self.browser.files[index - dirs].1.as_str()))
|
||||||
} else {
|
//} else {
|
||||||
None
|
//None
|
||||||
}
|
//}
|
||||||
}
|
//}
|
||||||
}
|
//}
|
||||||
|
|
||||||
impl PartialEq for BrowseTarget {
|
impl PartialEq for BrowseTarget {
|
||||||
fn eq (&self, other: &Self) -> bool {
|
fn eq (&self, other: &Self) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -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> {
|
||||||
|
|
@ -416,18 +416,6 @@ impl Clock {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clock {
|
|
||||||
fn _todo_provide_u32 (&self) -> u32 {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn _todo_provide_opt_u32 (&self) -> Option<u32> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn _todo_provide_f64 (&self) -> f64 {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_has!(Clock: |self: Track|self.sequencer.clock);
|
impl_has!(Clock: |self: Track|self.sequencer.clock);
|
||||||
impl_default!(Timebase: Self::new(48000f64, 150f64, DEFAULT_PPQ));
|
impl_default!(Timebase: Self::new(48000f64, 150f64, DEFAULT_PPQ));
|
||||||
|
|
||||||
|
|
@ -468,3 +456,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> {
|
||||||
|
|
@ -236,7 +236,6 @@ impl MidiEditor {
|
||||||
self.mode.redraw();
|
self.mode.redraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn _todo_opt_clip_stub (&self) -> Option<Arc<RwLock<MidiClip>>> { todo!() }
|
|
||||||
fn clip_length (&self) -> usize { self.clip().as_ref().map(|p|p.read().unwrap().length).unwrap_or(1) }
|
fn clip_length (&self) -> usize { self.clip().as_ref().map(|p|p.read().unwrap().length).unwrap_or(1) }
|
||||||
fn note_length (&self) -> usize { self.get_note_len() }
|
fn note_length (&self) -> usize { self.get_note_len() }
|
||||||
fn note_pos (&self) -> usize { self.get_note_pos() }
|
fn note_pos (&self) -> usize { self.get_note_pos() }
|
||||||
|
|
|
||||||
|
|
@ -70,28 +70,24 @@ 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<BrowseCommand>
|
||||||
+ for<'a> Namespace<'a, PoolCommand>
|
|
||||||
+ for<'a> Namespace<'a, 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<BrowseCommand>
|
||||||
+ for<'a> Namespace<'a, PoolCommand>
|
|
||||||
+ for<'a> Namespace<'a, BrowseCommand>
|
|
||||||
{
|
{
|
||||||
|
|
||||||
#[command(Show = "show")]
|
#[command(Show = "show")]
|
||||||
|
|
@ -139,7 +135,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
|
||||||
|
|
@ -226,7 +222,7 @@ pub trait PoolController: HasPool
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command(CropSet = "crop/set")]
|
#[command(CropSet = "crop/set")]
|
||||||
fn crop_set (&mut self, length: usize) -> Perhaps<PoolCommand> {
|
fn crop_set (&mut self, _length: usize) -> Perhaps<PoolCommand> {
|
||||||
if let Some(PoolMode::Length(clip, ref mut length, ref mut _focus))
|
if let Some(PoolMode::Length(clip, ref mut length, ref mut _focus))
|
||||||
= self.pool_mut().mode_mut().clone()
|
= self.pool_mut().mode_mut().clone()
|
||||||
{
|
{
|
||||||
|
|
@ -237,7 +233,7 @@ pub trait PoolController: HasPool
|
||||||
clip.write().unwrap().length = *length;
|
clip.write().unwrap().length = *length;
|
||||||
}
|
}
|
||||||
*self.pool_mut().mode_mut() = None;
|
*self.pool_mut().mode_mut() = None;
|
||||||
return Ok(old_length.map(|length|PoolCommand::CropSet { length }))
|
return Ok(old_length.map(|l|PoolCommand::CropSet { _length: l }))
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
@ -433,12 +429,6 @@ impl ClipLength {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pool {
|
impl Pool {
|
||||||
fn _todo_usize_ (&self) -> usize { todo!() }
|
|
||||||
fn _todo_bool_ (&self) -> bool { todo!() }
|
|
||||||
fn _todo_clip_ (&self) -> MidiClip { todo!() }
|
|
||||||
fn _todo_path_ (&self) -> PathBuf { todo!() }
|
|
||||||
fn _todo_color_ (&self) -> ItemColor { todo!() }
|
|
||||||
fn _todo_str_ (&self) -> Arc<str> { todo!() }
|
|
||||||
fn _clip_new (&self) -> MidiClip { self.new_clip() }
|
fn _clip_new (&self) -> MidiClip { self.new_clip() }
|
||||||
fn _clip_cloned (&self) -> MidiClip { self.cloned_clip() }
|
fn _clip_cloned (&self) -> MidiClip { self.cloned_clip() }
|
||||||
fn _clip_index_current (&self) -> usize { 0 }
|
fn _clip_index_current (&self) -> usize { 0 }
|
||||||
|
|
@ -449,44 +439,44 @@ impl Pool {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PoolView<'a> {
|
impl<'a> PoolView<'a> {
|
||||||
fn tui (&self) -> impl Draw<Tui> {
|
//fn tui (&self) -> impl Draw<Tui> {
|
||||||
let Self(pool) = self;
|
//let Self(pool) = self;
|
||||||
//let color = self.1.clip().map(|c|c.read().unwrap().color).unwrap_or_else(||g(32).into());
|
////let color = self.1.clip().map(|c|c.read().unwrap().color).unwrap_or_else(||g(32).into());
|
||||||
//let on_bg = |x|x;//below(Repeat(" "), bg(color.darkest.term, x));
|
////let on_bg = |x|x;//below(Repeat(" "), bg(color.darkest.term, x));
|
||||||
//let border = |x|x;//Outer(Style::default().fg(color.dark.term).bg(color.darkest.term)).enclose(x);
|
////let border = |x|x;//Outer(Style::default().fg(color.dark.term).bg(color.darkest.term)).enclose(x);
|
||||||
//let height = pool.clips.read().unwrap().len() as u16;
|
////let height = pool.clips.read().unwrap().len() as u16;
|
||||||
iter(
|
//iter(
|
||||||
||pool.clips().clone().into_iter(),
|
//||pool.clips().clone().into_iter(),
|
||||||
move|clip: Arc<RwLock<MidiClip>>, i: usize|{
|
//move|clip: Arc<RwLock<MidiClip>>, i: usize|{
|
||||||
let MidiClip { ref name, color, length, .. } = *clip.read().unwrap();
|
//let MidiClip { ref name, color, length, .. } = *clip.read().unwrap();
|
||||||
let item_height = 1;
|
//let item_height = 1;
|
||||||
let _item_offset = i as u16 * item_height;
|
//let _item_offset = i as u16 * item_height;
|
||||||
let selected = i == pool.clip_index();
|
//let selected = i == pool.clip_index();
|
||||||
let b = if selected { color.light.term } else { color.base.term };
|
//let b = if selected { color.light.term } else { color.base.term };
|
||||||
let f = color.lightest.term;
|
//let f = color.lightest.term;
|
||||||
let name = if false { format!(" {i:>3}") } else { format!(" {i:>3} {name}") };
|
//let name = if false { format!(" {i:>3}") } else { format!(" {i:>3} {name}") };
|
||||||
let length = if false { String::default() } else { format!("{length} ") };
|
//let length = if false { String::default() } else { format!("{length} ") };
|
||||||
bg(b, below!(
|
//bg(b, below!(
|
||||||
fg(f, bold(selected, name)).origin_w().full_w(),
|
//fg(f, bold(selected, name)).origin_w().full_w(),
|
||||||
fg(f, bold(selected, length)).origin_e().full_w(),
|
//fg(f, bold(selected, length)).origin_e().full_w(),
|
||||||
when(selected, bold(true, fg(g(255), "▶"))).origin_w().full_w(),
|
//when(selected, bold(true, fg(g(255), "▶"))).origin_w().full_w(),
|
||||||
when(selected, bold(true, fg(g(255), "◀"))).origin_e().full_w(),
|
//when(selected, bold(true, fg(g(255), "◀"))).origin_e().full_w(),
|
||||||
)).exact_h(1)
|
//)).exact_h(1)
|
||||||
}).origin_n().full_h().exact_w(20)
|
//}).origin_n().full_h().exact_w(20)
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClipLength {
|
impl ClipLength {
|
||||||
fn tui (&self) -> impl Draw<Tui> {
|
//fn tui (&self) -> impl Draw<Tui> {
|
||||||
use ClipLengthFocus::*;
|
//use ClipLengthFocus::*;
|
||||||
let bars = format!("{}", self.bars());
|
//let bars = format!("{}", self.bars());
|
||||||
let beats = format!("{}", self.beats());
|
//let beats = format!("{}", self.beats());
|
||||||
let ticks = format!("{:>02}", self.ticks());
|
//let ticks = format!("{:>02}", self.ticks());
|
||||||
match self.focus {
|
//match self.focus {
|
||||||
None => east!(" ", bars, ".", beats, ".", ticks),
|
//None => east!(" ", bars, ".", beats, ".", ticks),
|
||||||
Some(Bar) => east!("[", bars, "]", beats, ".", ticks),
|
//Some(Bar) => east!("[", bars, "]", beats, ".", ticks),
|
||||||
Some(Beat) => east!(" ", bars, "[", beats, "]", ticks),
|
//Some(Beat) => east!(" ", bars, "[", beats, "]", ticks),
|
||||||
Some(Tick) => east!(" ", bars, ".", beats, "[", ticks),
|
//Some(Tick) => east!(" ", bars, ".", beats, "[", ticks),
|
||||||
}
|
//}
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -245,19 +245,25 @@ pub trait HasMidiClip {
|
||||||
fn clip (&self) -> Option<Arc<RwLock<MidiClip>>>;
|
fn clip (&self) -> Option<Arc<RwLock<MidiClip>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HasMidiClip for App {
|
||||||
|
fn clip (&self) -> Option<Arc<RwLock<MidiClip>>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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")]
|
||||||
fn clip_set_color (&mut self, color: Option<ItemTheme>) -> Perhaps<MidiClipCommand> {
|
fn clip_set_color (&mut self, _color: Option<ItemTheme>) -> Perhaps<MidiClipCommand> {
|
||||||
//(SetColor [t: usize, s: usize, c: ItemTheme]
|
//(SetColor [t: usize, s: usize, c: ItemTheme]
|
||||||
//clip.clip_set_color(t, s, c).map(|o|Self::SetColor(t, s, o)))));
|
//clip.clip_set_color(t, s, c).map(|o|Self::SetColor(t, s, o)))));
|
||||||
//("color" [a: usize, b: usize] Some(Self::SetColor(a.unwrap(), b.unwrap(), ItemTheme::random())))
|
//("color" [a: usize, b: usize] Some(Self::SetColor(a.unwrap(), b.unwrap(), ItemTheme::random())))
|
||||||
|
|
@ -265,7 +271,7 @@ pub trait MidiClipController: HasMidiClip
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command(SetLoop = "clip/loop")]
|
#[command(SetLoop = "clip/loop")]
|
||||||
fn clip_toggle_loop (&mut self, looping: Option<bool>) -> Perhaps<MidiClipCommand> {
|
fn clip_toggle_loop (&mut self, _looping: Option<bool>) -> Perhaps<MidiClipCommand> {
|
||||||
//(SetLoop [t: usize, s: usize, l: bool] cmd_todo!("\n\rtodo: {self:?}"))
|
//(SetLoop [t: usize, s: usize, l: bool] cmd_todo!("\n\rtodo: {self:?}"))
|
||||||
//("loop" [a: usize, b: usize, c: bool] Some(Self::SetLoop(a.unwrap(), b.unwrap(), c.unwrap())))
|
//("loop" [a: usize, b: usize, c: bool] Some(Self::SetLoop(a.unwrap(), b.unwrap(), c.unwrap())))
|
||||||
todo!()
|
todo!()
|
||||||
|
|
@ -356,14 +362,6 @@ impl PartialEq for MidiClip {
|
||||||
|
|
||||||
impl Eq for MidiClip {}
|
impl Eq for MidiClip {}
|
||||||
|
|
||||||
impl MidiClip {
|
|
||||||
fn _todo_opt_bool_stub_ (&self) -> Option<bool> { todo!() }
|
|
||||||
fn _todo_bool_stub_ (&self) -> bool { todo!() }
|
|
||||||
fn _todo_usize_stub_ (&self) -> usize { todo!() }
|
|
||||||
fn _todo_arc_str_stub_ (&self) -> Arc<str> { todo!() }
|
|
||||||
fn _todo_item_theme_stub (&self) -> ItemTheme { todo!() }
|
|
||||||
fn _todo_opt_item_theme_stub (&self) -> Option<ItemTheme> { todo!() }
|
|
||||||
}
|
|
||||||
impl_has!(Sequencer: |self: Track| self.sequencer);
|
impl_has!(Sequencer: |self: Track| self.sequencer);
|
||||||
impl_has!(Clock: |self: Sequencer| self.clock);
|
impl_has!(Clock: |self: Sequencer| self.clock);
|
||||||
impl_has!(Vec<MidiInput>: |self: Sequencer| self.midi_ins);
|
impl_has!(Vec<MidiInput>: |self: Sequencer| self.midi_ins);
|
||||||
|
|
|
||||||
75
src/tek.edn
75
src/tek.edn
|
|
@ -18,10 +18,13 @@
|
||||||
(align/s (bsp/e :ports/out (bsp/e :transport :ports/in)))
|
(align/s (bsp/e :ports/out (bsp/e :transport :ports/in)))
|
||||||
(align/c (bsp/s (align/x (bg (g 36) :logo)) (bg (g 24) :dialog/menu))))))
|
(align/c (bsp/s (align/x (bg (g 36) :logo)) (bg (g 24) :dialog/menu))))))
|
||||||
|
|
||||||
|
(keys :back (@escape (back)))
|
||||||
|
(keys :confirm (@enter (dialog confirm)))
|
||||||
|
(keys :axis/y (@up (axis (dec :y)))
|
||||||
|
(@down (axis (inc :y))))
|
||||||
|
|
||||||
(mode :arranger (name Arranger) (info Launch grid.)
|
(mode :arranger (name Arranger) (info Launch grid.)
|
||||||
(keys (see :clock :color :launch :scenes :tracks :global)
|
(keys :clock :color :launch :scenes :tracks :global)
|
||||||
(@tab project/edit) (@shift/I project/input/add) (@shift/O project/output/add)
|
|
||||||
(@shift/D dialog/show :dialog/device))
|
|
||||||
(mode :editor (keys :editor))
|
(mode :editor (keys :editor))
|
||||||
(mode :dialog (keys :dialog))
|
(mode :dialog (keys :dialog))
|
||||||
(mode :message (keys :message))
|
(mode :message (keys :message))
|
||||||
|
|
@ -46,6 +49,27 @@
|
||||||
(bg (g 80) (bsp/s :scenes/names :editor))
|
(bg (g 80) (bsp/s :scenes/names :editor))
|
||||||
(bg (g 90) :scenes))))))))))))))
|
(bg (g 90) :scenes))))))))))))))
|
||||||
|
|
||||||
|
(keys :clock (@space clock/toggle 0)
|
||||||
|
(@shift/space clock/toggle 0))
|
||||||
|
|
||||||
|
(keys :color (@c color))
|
||||||
|
|
||||||
|
(keys :launch (@q launch))
|
||||||
|
|
||||||
|
(keys :scenes (@shift/S (scenes add))
|
||||||
|
(@s (select scene))
|
||||||
|
(@up (select scene-dec))
|
||||||
|
(@down (select scene-inc)))
|
||||||
|
|
||||||
|
(keys :tracks (@shift/T (tracks add))
|
||||||
|
(@t (select track))
|
||||||
|
(@left (select track-dec))
|
||||||
|
(@right (select track-inc)))
|
||||||
|
|
||||||
|
(keys :global (see :history :saveload)
|
||||||
|
(@f8 dialog :options)
|
||||||
|
(@f10 dialog :quit))
|
||||||
|
|
||||||
(view :ports/out
|
(view :ports/out
|
||||||
(bsp/s (align/w (text L-AUDIO-OUT))
|
(bsp/s (align/w (text L-AUDIO-OUT))
|
||||||
(bsp/e (text MIDI-OUT)
|
(bsp/e (text MIDI-OUT)
|
||||||
|
|
@ -86,18 +110,24 @@
|
||||||
|
|
||||||
(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 :axis/x (@left dec :x)
|
||||||
(keys :confirm (@enter confirm))
|
(@right inc :x))
|
||||||
(keys :axis/x (@left app/dec :x) (@right app/inc :x))
|
(keys :axis/x2 (@shift/left dec :x2)
|
||||||
(keys :axis/x2 (@shift/left app/dec :x2) (@shift/right app/inc :x2))
|
(@shift/right inc :x2))
|
||||||
(keys :axis/y (@up app/dec :y) (@down app/inc :y))
|
(keys :axis/y2 (@shift/up dec :y2)
|
||||||
(keys :axis/y2 (@shift/up app/dec :y2) (@shift/down app/inc :y2))
|
(@shift/down inc :y2))
|
||||||
(keys :axis/z (@minus app/dec :z) (@equal app/inc :z))
|
(keys :axis/z (@minus dec :z)
|
||||||
(keys :axis/z2 (@underscore app/dec :z2) (@plus app/inc :z2))
|
(@equal inc :z))
|
||||||
(keys :axis/i (@comma app/dec :i) (@period app/inc :z))
|
(keys :axis/z2 (@underscore dec :z2)
|
||||||
(keys :axis/i2 (@lt app/dec :i2) (@gt app/inc :z2))
|
(@plus inc :z2))
|
||||||
(keys :axis/w (@openbracket app/dec :w) (@closebracket app/inc :w))
|
(keys :axis/i (@comma dec :i)
|
||||||
(keys :axis/w2 (@openbrace app/dec :w2) (@closebrace app/inc :w2))
|
(@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 :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))
|
||||||
|
|
@ -117,19 +147,11 @@
|
||||||
(@down sampler/select :sample/below)
|
(@down sampler/select :sample/below)
|
||||||
(@left sampler/select :sample/to/left)
|
(@left sampler/select :sample/to/left)
|
||||||
(@right sampler/select :sample/to/right))
|
(@right sampler/select :sample/to/right))
|
||||||
(keys :tracks (@t select :select/track)
|
|
||||||
(@shift/T project/track/add)
|
|
||||||
(@left select :select/track/dec)
|
|
||||||
(@right select :select/track/inc))
|
|
||||||
(keys :track (see :color :launch :axis/z :axis/z2 :delete)
|
(keys :track (see :color :launch :axis/z :axis/z2 :delete)
|
||||||
(@r toggle :rec)
|
(@r toggle :rec)
|
||||||
(@m toggle :mon)
|
(@m toggle :mon)
|
||||||
(@p toggle :play)
|
(@p toggle :play)
|
||||||
(@P toggle :solo))
|
(@P toggle :solo))
|
||||||
(keys :scenes (@s select :select/scene)
|
|
||||||
(@shift/S project/scene/add)
|
|
||||||
(@up select :select/scene/dec)
|
|
||||||
(@down select :select/scene/inc))
|
|
||||||
(keys :scene (see :color :launch :axis/z :axis/z2 :delete))
|
(keys :scene (see :color :launch :axis/z :axis/z2 :delete))
|
||||||
(keys :help (@f1 dialog :help))
|
(keys :help (@f1 dialog :help))
|
||||||
(keys :page (@pgup page/up)
|
(keys :page (@pgup page/up)
|
||||||
|
|
@ -144,13 +166,6 @@
|
||||||
(@r redo 1))
|
(@r redo 1))
|
||||||
(keys :saveload (@f6 dialog :save)
|
(keys :saveload (@f6 dialog :save)
|
||||||
(@f9 dialog :load))
|
(@f9 dialog :load))
|
||||||
(keys :color (@c color))
|
|
||||||
(keys :launch (@q launch))
|
|
||||||
(keys :clock (@space clock/toggle 0)
|
|
||||||
(@shift/space clock/toggle 0))
|
|
||||||
(keys :global (see :history :saveload)
|
|
||||||
(@f8 dialog :options)
|
|
||||||
(@f10 dialog :quit))
|
|
||||||
(keys :clip (see :color :launch :axis/z :axis/z2 :delete)
|
(keys :clip (see :color :launch :axis/z :axis/z2 :delete)
|
||||||
(@l toggle :loop))
|
(@l toggle :loop))
|
||||||
(keys :sequencer (see :color :launch)
|
(keys :sequencer (see :color :launch)
|
||||||
|
|
|
||||||
723
src/tek.rs
723
src/tek.rs
|
|
@ -1,44 +1,6 @@
|
||||||
#![allow(clippy::unit_arg)]
|
#![allow(clippy::unit_arg)]
|
||||||
#![feature(impl_trait_in_assoc_type)] // Used by EntriesIterator; TODO remove
|
//#![feature(impl_trait_in_assoc_type)] // Used by EntriesIterator; TODO remove
|
||||||
pub extern crate atomic_float;
|
mod deps; pub use self::deps::*;
|
||||||
pub extern crate xdg;
|
|
||||||
pub extern crate tengri;
|
|
||||||
#[cfg(feature = "cli")]
|
|
||||||
pub(crate) use ::clap::{self, Parser, Subcommand};
|
|
||||||
#[allow(unused)]
|
|
||||||
pub(crate) use ::{
|
|
||||||
std::{
|
|
||||||
cmp::Ord,
|
|
||||||
collections::BTreeMap,
|
|
||||||
error::Error,
|
|
||||||
ffi::OsString,
|
|
||||||
fmt::{Write, Debug, Formatter},
|
|
||||||
fs::File,
|
|
||||||
ops::{Add, Sub, Mul, Div, Rem},
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
sync::{Arc, RwLock, atomic::{AtomicBool, AtomicUsize, AtomicU64, Ordering::Relaxed}},
|
|
||||||
time::Duration,
|
|
||||||
thread::{spawn, JoinHandle},
|
|
||||||
},
|
|
||||||
xdg::{
|
|
||||||
BaseDirectories,
|
|
||||||
},
|
|
||||||
tengri::{
|
|
||||||
*,
|
|
||||||
dizzle::*,
|
|
||||||
midly::{
|
|
||||||
Smf, TrackEventKind, MidiMessage, Error as MidiError,
|
|
||||||
num::*,
|
|
||||||
live::*,
|
|
||||||
},
|
|
||||||
crossterm::event::{Event, KeyEvent},
|
|
||||||
ratatui::{
|
|
||||||
self,
|
|
||||||
prelude::{Rect, Style, Stylize, Buffer, Color::{self, *}},
|
|
||||||
widgets::{Widget, canvas::{Canvas, Line}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Banner.
|
/// Banner.
|
||||||
pub(crate) const HEADER: &'static str = r#"
|
pub(crate) const HEADER: &'static str = r#"
|
||||||
|
|
@ -88,6 +50,27 @@ fn run_new_plain (config: Config) -> Usually<()> {
|
||||||
})?)
|
})?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//pub fn tui (
|
||||||
|
//app: Arc<RwLock<App>>,
|
||||||
|
//jack: Jack,
|
||||||
|
//sync_lead: &bool,
|
||||||
|
//sync_follow: &bool,
|
||||||
|
//) -> Usually<()> {
|
||||||
|
//// Run the [Tui] and [Jack] threads with the [App] state.
|
||||||
|
//Tui::run_main(&jack.run(move|jack|{
|
||||||
|
//// Between jack init and app's first cycle:
|
||||||
|
////jack.sync_lead(*sync_lead, |mut state|{
|
||||||
|
////let clock = app.write().unwrap().clock();
|
||||||
|
////clock.playhead.update_from_sample(state.position.frame() as f64);
|
||||||
|
////state.position.bbt = Some(clock.bbt());
|
||||||
|
////state.position
|
||||||
|
////})?;
|
||||||
|
////jack.sync_follow(*sync_follow)?;
|
||||||
|
//// FIXME: They don't work properly.
|
||||||
|
//Ok(app)
|
||||||
|
//})?)?
|
||||||
|
//}
|
||||||
|
|
||||||
#[cfg(feature = "cli")] pub mod cli {
|
#[cfg(feature = "cli")] pub mod cli {
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
|
@ -238,14 +221,8 @@ fn run_new_plain (config: Config) -> Usually<()> {
|
||||||
.chain(
|
.chain(
|
||||||
connect_audio_outs(&jack, &"R".to_string(), &right_to, None)?.into_iter()
|
connect_audio_outs(&jack, &"R".to_string(), &right_to, None)?.into_iter()
|
||||||
));
|
));
|
||||||
//&jack, Clock::new(&jack, *bpm)?, &lf, <, &rf, &rt, &mf, &mt, &mfr, &mtr)?;
|
|
||||||
proj.tracks_add_many(tracks.unwrap_or(0), None, [].into(), [].into())?;
|
proj.tracks_add_many(tracks.unwrap_or(0), None, [].into(), [].into())?;
|
||||||
proj.scenes_add_many(scenes.unwrap_or(0))?;
|
proj.scenes_add_many(scenes.unwrap_or(0))?;
|
||||||
//if matches!(self, Action::Status) {
|
|
||||||
//// Show status and exit
|
|
||||||
//tek_print_status(&proj);
|
|
||||||
//return Ok(())
|
|
||||||
//}
|
|
||||||
Ok(proj)
|
Ok(proj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -325,11 +302,14 @@ mod config {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
|
/// Default configuration directory.
|
||||||
const CONFIG_DIR: &'static str = "tek";
|
const CONFIG_DIR: &'static str = "tek";
|
||||||
|
/// Default configuration subdirectory.
|
||||||
const CONFIG_SUB: &'static str = "v0";
|
const CONFIG_SUB: &'static str = "v0";
|
||||||
|
/// Default configuration file name.
|
||||||
const CONFIG: &'static str = "tek.edn";
|
const CONFIG: &'static str = "tek.edn";
|
||||||
|
/// Default configuration contents.
|
||||||
const DEFAULTS: &'static str = include_str!("tek.edn");
|
const DEFAULTS: &'static str = include_str!("tek.edn");
|
||||||
|
|
||||||
/// Create a new app configuration from a set of XDG base directories,
|
/// Create a new app configuration from a set of XDG base directories,
|
||||||
pub fn new (dirs: Option<BaseDirectories>) -> Self {
|
pub fn new (dirs: Option<BaseDirectories>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -340,20 +320,20 @@ mod config {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Create, initialize, and watch a new configuration.
|
||||||
pub fn watched <T> (callback: impl FnOnce(Arc<Self>)->T) -> Usually<T> {
|
pub fn watched <T> (callback: impl FnOnce(Arc<Self>)->T) -> Usually<T> {
|
||||||
let config = Self::init_new(None)?;
|
let config = Self::init_new(None)?;
|
||||||
Self::watch(config.clone(), None)?;
|
Self::watch(config.clone(), None)?;
|
||||||
let result = callback(config);
|
let result = callback(config);
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
/// Create and initialize a new configuration.
|
||||||
pub fn init_new (_dirs: Option<BaseDirectories>) -> Usually<Arc<Self>> {
|
pub fn init_new (_dirs: Option<BaseDirectories>) -> Usually<Arc<Self>> {
|
||||||
let config = Arc::new(Self::new(None));
|
let config = Arc::new(Self::new(None));
|
||||||
config.init()?;
|
config.init()?;
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
/// Watch a config's file for changes.
|
||||||
pub fn watch (config: Arc<Self>, poll: Option<Duration>) -> Usually<()> {
|
pub fn watch (config: Arc<Self>, poll: Option<Duration>) -> Usually<()> {
|
||||||
let handler = {
|
let handler = {
|
||||||
let config = config.clone();
|
let config = config.clone();
|
||||||
|
|
@ -383,19 +363,18 @@ mod config {
|
||||||
Err(format!("no config path").into())
|
Err(format!("no config path").into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Find the config file.
|
||||||
fn find_file (&self) -> Option<PathBuf> {
|
fn find_file (&self) -> Option<PathBuf> {
|
||||||
self.dirs.find_config_file(Self::CONFIG)
|
self.dirs.find_config_file(Self::CONFIG)
|
||||||
}
|
}
|
||||||
|
/// Place config file in default location.
|
||||||
fn place_file (&self) -> Result<PathBuf, std::io::Error> {
|
fn place_file (&self) -> Result<PathBuf, std::io::Error> {
|
||||||
self.dirs.place_config_file(Self::CONFIG)
|
self.dirs.place_config_file(Self::CONFIG)
|
||||||
}
|
}
|
||||||
|
/// Get path to config file.
|
||||||
fn get_file (&self) -> Option<PathBuf> {
|
fn get_file (&self) -> Option<PathBuf> {
|
||||||
self.dirs.get_config_file(Self::CONFIG)
|
self.dirs.get_config_file(Self::CONFIG)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write initial contents of configuration.
|
/// Write initial contents of configuration.
|
||||||
pub fn init (&self) -> Usually<()> {
|
pub fn init (&self) -> Usually<()> {
|
||||||
//println!("\r\ninit {}", quanta::Clock::new().raw());
|
//println!("\r\ninit {}", quanta::Clock::new().raw());
|
||||||
|
|
@ -405,13 +384,9 @@ mod config {
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write initial contents of a configuration file.
|
/// Write initial contents of a configuration file.
|
||||||
pub fn load (
|
pub fn load <F: FnMut(&Self, &str)->Usually<()>> (
|
||||||
&self,
|
&self, path: &str, defaults: &str, mut each: F
|
||||||
path: &str,
|
|
||||||
defaults: &str,
|
|
||||||
mut each: impl FnMut(&Self, &str)->Usually<()>
|
|
||||||
) -> Usually<()> {
|
) -> Usually<()> {
|
||||||
self.stamp.store(quanta::Clock::new().raw(), Relaxed);
|
self.stamp.store(quanta::Clock::new().raw(), Relaxed);
|
||||||
if self.find_file().is_none() {
|
if self.find_file().is_none() {
|
||||||
|
|
@ -426,19 +401,18 @@ mod config {
|
||||||
return Err(format!("{path}: not found").into())
|
return Err(format!("{path}: not found").into())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add statements to configuration from [Dsl] source.
|
/// Add statements to configuration from [Dsl] source.
|
||||||
pub fn add (&self, dsl: impl Language) -> Usually<&Self> {
|
pub fn add (&self, dsl: impl Language) -> Usually<&Self> {
|
||||||
dsl.each(|item|self.add_one(item))?;
|
dsl.each(|item|self.add_one(item))?;
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
/// Make this configuration empty.
|
||||||
fn clear (&self) {
|
fn clear (&self) {
|
||||||
*self.modes.0.write().unwrap() = Default::default();
|
*self.modes.0.write().unwrap() = Default::default();
|
||||||
*self.views.write().unwrap() = Default::default();
|
*self.views.write().unwrap() = Default::default();
|
||||||
*self.binds.write().unwrap() = Default::default();
|
*self.binds.write().unwrap() = Default::default();
|
||||||
}
|
}
|
||||||
|
/// Add one entry to the configuration.
|
||||||
fn add_one (&self, item: impl Language) -> Usually<()> {
|
fn add_one (&self, item: impl Language) -> Usually<()> {
|
||||||
if let Some(expr) = item.expr()? {
|
if let Some(expr) = item.expr()? {
|
||||||
let head = expr.head()?;
|
let head = expr.head()?;
|
||||||
|
|
@ -458,74 +432,35 @@ mod config {
|
||||||
return Err(format!("Config::load: expected expr, got: {item:?}").into())
|
return Err(format!("Config::load: expected expr, got: {item:?}").into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Get a mode by name.
|
||||||
pub fn get_mode (&self, mode: impl AsRef<str>) -> Option<Arc<Mode<Arc<str>>>> {
|
pub fn get_mode (&self, mode: impl AsRef<str>) -> Option<Arc<Mode<Arc<str>>>> {
|
||||||
self.modes.get(mode)
|
self.modes.get(mode)
|
||||||
}
|
}
|
||||||
|
/// Print the configuration.
|
||||||
pub fn print (&self) {
|
pub fn print (&self) {
|
||||||
use ::ansi_term::Color::*;
|
print_config(self)
|
||||||
println!("{:?}", self.dirs);
|
|
||||||
for (k, v) in self.views.read().unwrap().iter() {
|
|
||||||
println!("{} {} {v}", Green.paint("VIEW"), Green.bold().paint(format!("{k:<16}")));
|
|
||||||
}
|
|
||||||
for (k, v) in self.binds.read().unwrap().iter() {
|
|
||||||
println!("{} {}", Green.paint("BIND"), Green.bold().paint(format!("{k:<16}")));
|
|
||||||
for (k, v) in v.0.iter() {
|
|
||||||
print!("{} ", &Yellow.paint(match &k.0 {
|
|
||||||
Event::Key(KeyEvent { modifiers, .. }) =>
|
|
||||||
format!("{:>16}", format!("{modifiers}")),
|
|
||||||
_ => unimplemented!()
|
|
||||||
}));
|
|
||||||
print!("{}", &Yellow.bold().paint(match &k.0 {
|
|
||||||
Event::Key(KeyEvent { code, .. }) =>
|
|
||||||
format!("{:<10}", format!("{code}")),
|
|
||||||
_ => unimplemented!()
|
|
||||||
}));
|
|
||||||
for v in v.iter() {
|
|
||||||
print!(" => {:?}", v.commands);
|
|
||||||
print!(" {}", v.condition.as_ref().map(|x|format!("{x:?}")).unwrap_or_default());
|
|
||||||
println!(" {}", v.description.as_ref().map(|x|x.as_ref()).unwrap_or_default());
|
|
||||||
//println!(" {:?}", v.source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.modes.for_each(|k, v|{
|
|
||||||
println!();
|
|
||||||
for v in v.name.iter() { print!("{}", Green.bold().paint(format!("{v} "))); }
|
|
||||||
for v in v.info.iter() { print!("\n{}", Green.paint(format!("{v}"))); }
|
|
||||||
print!("\n{} {}", Blue.paint("TOOL"), Green.bold().paint(format!("{k:<16}")));
|
|
||||||
print!("\n{}", Blue.paint("KEYS"));
|
|
||||||
for v in v.keys.iter() { print!("{}", Green.paint(format!(" {v}"))); }
|
|
||||||
println!();
|
|
||||||
v.modes.for_each(|k, v|{
|
|
||||||
print!("{} {} {:?}", Blue.paint("MODE"), Green.bold().paint(format!("{k:<16}")), v.name);
|
|
||||||
print!( " INFO={:?}", v.info);
|
|
||||||
print!( " VIEW={:?}", v.view);
|
|
||||||
println!(" KEYS={:?}", v.keys);
|
|
||||||
});
|
|
||||||
print!("{}", Blue.paint("VIEW"));
|
|
||||||
for v in v.view.iter() { print!("{}", Green.paint(format!(" {v}"))); }
|
|
||||||
println!();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Modes {
|
impl Modes {
|
||||||
|
/// Register a mode.
|
||||||
pub fn add (&self, name: &impl AsRef<str>, body: &impl Language) -> Usually<()> {
|
pub fn add (&self, name: &impl AsRef<str>, body: &impl Language) -> Usually<()> {
|
||||||
let mut mode = Mode::default();
|
let mut mode = Mode::default();
|
||||||
body.each(|item|mode.add(item))?;
|
body.each(|item|mode.add(item))?;
|
||||||
self.0.write().unwrap().insert(name.as_ref().into(), Arc::new(mode));
|
self.0.write().unwrap().insert(name.as_ref().into(), Arc::new(mode));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
/// Get a mode by name.
|
||||||
pub fn get (&self, name: impl AsRef<str>) -> Option<Arc<Mode<Arc<str>>>> {
|
pub fn get (&self, name: impl AsRef<str>) -> Option<Arc<Mode<Arc<str>>>> {
|
||||||
self.0.read().unwrap().get(name.as_ref()).cloned()
|
self.0.read().unwrap().get(name.as_ref()).cloned()
|
||||||
}
|
}
|
||||||
|
/// Run something for each mode.
|
||||||
pub fn for_each <T> (&self, mut ator: impl FnMut(&str, &Mode<Arc<str>>)->T) {
|
pub fn for_each <T> (&self, mut ator: impl FnMut(&str, &Mode<Arc<str>>)->T) {
|
||||||
for (k, v) in self.0.read().unwrap().iter() {
|
for (k, v) in self.0.read().unwrap().iter() {
|
||||||
let _ = ator(k.as_ref(), v.as_ref());
|
let _ = ator(k.as_ref(), v.as_ref());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Count modes.
|
||||||
pub fn len (&self) -> usize {
|
pub fn len (&self) -> usize {
|
||||||
self.0.read().unwrap().len()
|
self.0.read().unwrap().len()
|
||||||
}
|
}
|
||||||
|
|
@ -562,34 +497,24 @@ mod config {
|
||||||
} else {
|
} else {
|
||||||
return Err(format!("Mode::add: unexpected: {dsl:?}").into());
|
return Err(format!("Mode::add: unexpected: {dsl:?}").into());
|
||||||
})
|
})
|
||||||
|
|
||||||
//DslParse(dsl, ||Err(format!("Mode::add: unexpected: {dsl:?}").into()))
|
|
||||||
//.word(|word|self.add_view(word))
|
|
||||||
//.expr(|expr|expr.head(|head|{
|
|
||||||
////println!("Mode::add: {head} {:?}", expr.tail());
|
|
||||||
//let tail = expr.tail()?.map(|x|x.trim()).unwrap_or("");
|
|
||||||
//match head {
|
|
||||||
//"name" => self.add_name(tail),
|
|
||||||
//"info" => self.add_info(tail),
|
|
||||||
//"keys" => self.add_keys(tail)?,
|
|
||||||
//"mode" => self.add_mode(tail)?,
|
|
||||||
//_ => self.add_view(tail),
|
|
||||||
//};
|
|
||||||
//}))
|
|
||||||
}
|
}
|
||||||
|
/// Add a name to the mode.
|
||||||
fn add_name (&mut self, dsl: impl Language) -> Perhaps<()> {
|
fn add_name (&mut self, dsl: impl Language) -> Perhaps<()> {
|
||||||
Ok(dsl.src()?.map(|src|self.name.push(src.into())))
|
Ok(dsl.src()?.map(|src|self.name.push(src.into())))
|
||||||
}
|
}
|
||||||
|
/// Add a description to the mode.
|
||||||
fn add_info (&mut self, dsl: impl Language) -> Perhaps<()> {
|
fn add_info (&mut self, dsl: impl Language) -> Perhaps<()> {
|
||||||
Ok(dsl.src()?.map(|src|self.info.push(src.into())))
|
Ok(dsl.src()?.map(|src|self.info.push(src.into())))
|
||||||
}
|
}
|
||||||
|
/// Add a view definition to the mode.
|
||||||
fn add_view (&mut self, dsl: impl Language) -> Perhaps<()> {
|
fn add_view (&mut self, dsl: impl Language) -> Perhaps<()> {
|
||||||
Ok(dsl.src()?.map(|src|self.view.push(src.into())))
|
Ok(dsl.src()?.map(|src|self.view.push(src.into())))
|
||||||
}
|
}
|
||||||
|
/// Add a keyboard input bindin to the mode.
|
||||||
fn add_keys (&mut self, dsl: impl Language) -> Perhaps<()> {
|
fn add_keys (&mut self, dsl: impl Language) -> Perhaps<()> {
|
||||||
Ok(Some(dsl.each(|expr|{ self.keys.push(expr.trim().into()); Ok(()) })?))
|
Ok(Some(dsl.each(|expr|{ self.keys.push(expr.trim().into()); Ok(()) })?))
|
||||||
}
|
}
|
||||||
|
/// Add a submode to the mode.
|
||||||
fn add_mode (&mut self, dsl: impl Language) -> Perhaps<()> {
|
fn add_mode (&mut self, dsl: impl Language) -> Perhaps<()> {
|
||||||
Ok(Some(if let Some(id) = dsl.head()? {
|
Ok(Some(if let Some(id) = dsl.head()? {
|
||||||
self.modes.add(&id, &dsl.tail())?;
|
self.modes.add(&id, &dsl.tail())?;
|
||||||
|
|
@ -620,7 +545,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 +564,27 @@ mod app {
|
||||||
/// let _ = app.project.h_scenes();
|
/// let _ = app.project.h_scenes();
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
#[namespace(u8)]
|
#[namespace(MidiClip)]
|
||||||
#[namespace(isize)]
|
#[namespace(ItemColor)]
|
||||||
#[namespace(ItemTheme)]
|
#[namespace(ItemTheme)]
|
||||||
#[namespace(Arc<str> App::get_arc_str)]
|
#[namespace(isize)]
|
||||||
#[namespace(u16 App::get_u16)]
|
#[namespace(u8)]
|
||||||
#[namespace(usize App::get_usize)]
|
#[namespace(Arc<[Connect]> get_arc_array_connect)]
|
||||||
#[namespace(bool App::get_bool)]
|
#[namespace(Arc<str> get_arc_str)]
|
||||||
#[namespace(Selection App::get_selection)]
|
#[namespace(Color get_color)]
|
||||||
#[namespace(Color App::get_color)]
|
#[namespace(ControlAxis get_axis)]
|
||||||
#[namespace(Option<u7> App::get_opt_u7)]
|
#[namespace(Option<Arc<[Connect]>> get_opt_arc_array_connect)]
|
||||||
#[namespace(Option<u16> App::get_opt_u16)]
|
#[namespace(Option<Arc<str>> get_opt_arc_str)]
|
||||||
#[namespace(Option<usize> App::get_opt_usize)]
|
#[namespace(Option<ItemTheme> get_opt_itemtheme)]
|
||||||
#[namespace(Option<Arc<RwLock<MidiClip>>> App::get_clip)]
|
#[namespace(Option<Vec<Option<Arc<RwLock<MidiClip>>>>> get_opt_vec_opt_arc_rwlock_midiclip)]
|
||||||
|
#[namespace(Option<u16> get_opt_u16)]
|
||||||
|
#[namespace(Option<usize> get_opt_usize)]
|
||||||
|
#[namespace(Option<bool> get_opt_bool)]
|
||||||
|
#[namespace(bool get_bool)]
|
||||||
|
#[namespace(u16 get_u16)]
|
||||||
|
#[namespace(PathBuf get_path_buf)]
|
||||||
|
#[namespace(usize get_usize)]
|
||||||
#[namespace(Dialog App::get_dialog)]
|
#[namespace(Dialog App::get_dialog)]
|
||||||
#[namespace(ControlAxis App::get_axis)]
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
/// Exit flag
|
/// Exit flag
|
||||||
pub exit: Exit,
|
pub exit: Exit,
|
||||||
|
|
@ -692,10 +623,7 @@ mod app {
|
||||||
/// let tek = tek::App::new(None, proj, conf, "hello");
|
/// let tek = tek::App::new(None, proj, conf, "hello");
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new (
|
pub fn new (
|
||||||
exit: Option<Exit>,
|
exit: Option<Exit>, project: Arrangement, config: Arc<Config>, mode: impl AsRef<str>
|
||||||
project: Arrangement,
|
|
||||||
config: Arc<Config>,
|
|
||||||
mode: impl AsRef<str>
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
App {
|
App {
|
||||||
exit: exit.unwrap_or_default(),
|
exit: exit.unwrap_or_default(),
|
||||||
|
|
@ -708,64 +636,61 @@ mod app {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_arc_str (&self, src: impl Language) -> Perhaps<Arc<str>> {
|
|
||||||
Ok(src.src()?.map(|x|x.into()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_u16 (&self, src: impl Language) -> Perhaps<u16> {
|
fn get_arc_str (_: &App, src: impl Language) -> Perhaps<Arc<str>> {
|
||||||
|
Ok(src.src()?.map(|x|x.into()))
|
||||||
|
}
|
||||||
|
fn get_u16 (state: &App, src: impl Language) -> Perhaps<u16> {
|
||||||
Ok(Some(match src.word()? {
|
Ok(Some(match src.word()? {
|
||||||
Some(":w/sidebar") => self.project.w_sidebar(self.editor().is_some()),
|
Some(":w/sidebar") => state.project.w_sidebar(state.editor().is_some()),
|
||||||
Some(":h/sample-detail") => 6.max(self.size.h() as u16 * 3 / 9),
|
Some(":h/sample-detail") => 6.max(state.size.h() as u16 * 3 / 9),
|
||||||
_ => return try_to_u16(src)
|
_ => return try_to_u16(src)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
fn get_usize (state: &App, src: impl Language) -> Perhaps<usize> {
|
||||||
fn get_usize (&self, src: impl Language) -> Perhaps<usize> {
|
|
||||||
Ok(Some(match src.word()? {
|
Ok(Some(match src.word()? {
|
||||||
Some(":scene-count") => self.scenes().len(),
|
Some(":scene-count") => state.scenes().len(),
|
||||||
Some(":track-count") => self.tracks().len(),
|
Some(":track-count") => state.tracks().len(),
|
||||||
Some(":device-kind") => self.dialog.device_kind().unwrap_or(0),
|
Some(":device-kind") => state.dialog.device_kind().unwrap_or(0),
|
||||||
Some(":device-kind/next") => self.dialog.device_kind_next().unwrap_or(0),
|
Some(":device-kind/next") => state.dialog.device_kind_next().unwrap_or(0),
|
||||||
Some(":device-kind/prev") => self.dialog.device_kind_prev().unwrap_or(0),
|
Some(":device-kind/prev") => state.dialog.device_kind_prev().unwrap_or(0),
|
||||||
_ => return try_to_usize(src)
|
_ => return try_to_usize(src)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
fn get_bool (state: &App, src: impl Language) -> Perhaps<bool> {
|
||||||
fn get_bool (&self, src: impl Language) -> Perhaps<bool> {
|
|
||||||
src.word()?.map(|word|Ok(match word {
|
src.word()?.map(|word|Ok(match word {
|
||||||
"Y" => true,
|
"Y" => true,
|
||||||
"N" => false,
|
"N" => false,
|
||||||
":mode/editor" => self.project.editor.is_some(),
|
":mode/editor" => state.project.editor.is_some(),
|
||||||
":focused/dialog" => !matches!(self.dialog, Dialog::None),
|
":focused/dialog" => !matches!(state.dialog, Dialog::None),
|
||||||
":focused/message" => matches!(self.dialog, Dialog::Message(..)),
|
":focused/message" => matches!(state.dialog, Dialog::Message(..)),
|
||||||
":focused/add_device" => matches!(self.dialog, Dialog::Device(..)),
|
":focused/add_device" => matches!(state.dialog, Dialog::Device(..)),
|
||||||
":focused/browser" => self.dialog.browser().is_some(),
|
":focused/browser" => state.dialog.browser().is_some(),
|
||||||
":focused/pool/import" => matches!(self.pool.mode, Some(PoolMode::Import(..))),
|
":focused/pool/import" => matches!(state.pool.mode, Some(PoolMode::Import(..))),
|
||||||
":focused/pool/export" => matches!(self.pool.mode, Some(PoolMode::Export(..))),
|
":focused/pool/export" => matches!(state.pool.mode, Some(PoolMode::Export(..))),
|
||||||
":focused/pool/rename" => matches!(self.pool.mode, Some(PoolMode::Rename(..))),
|
":focused/pool/rename" => matches!(state.pool.mode, Some(PoolMode::Rename(..))),
|
||||||
":focused/pool/length" => matches!(self.pool.mode, Some(PoolMode::Length(..))),
|
":focused/pool/length" => matches!(state.pool.mode, Some(PoolMode::Length(..))),
|
||||||
":focused/clip" => !self.editor_focused() && matches!(self.selection(), Selection::TrackClip{..}),
|
":focused/clip" => !state.editor_focused() && matches!(state.selection(), Selection::TrackClip{..}),
|
||||||
":focused/track" => !self.editor_focused() && matches!(self.selection(), Selection::Track(..)),
|
":focused/track" => !state.editor_focused() && matches!(state.selection(), Selection::Track(..)),
|
||||||
":focused/scene" => !self.editor_focused() && matches!(self.selection(), Selection::Scene(..)),
|
":focused/scene" => !state.editor_focused() && matches!(state.selection(), Selection::Scene(..)),
|
||||||
":focused/mix" => !self.editor_focused() && matches!(self.selection(), Selection::Mix),
|
":focused/mix" => !state.editor_focused() && matches!(state.selection(), Selection::Mix),
|
||||||
_ => return Err(format!("not bool: {word}").into())
|
_ => return Err(format!("not bool: {word}").into())
|
||||||
})).transpose()
|
})).transpose()
|
||||||
}
|
}
|
||||||
|
#[allow(unused)]
|
||||||
fn get_selection (&self, src: impl Language) -> Perhaps<Selection> {
|
fn get_selection (state: &App, src: impl Language) -> Perhaps<Selection> {
|
||||||
src.word()?.map(|word|Ok(match word {
|
src.word()?.map(|word|Ok(match word {
|
||||||
":select/scene" => self.selection().select_scene(self.tracks().len()),
|
":select/scene" => state.selection().select_scene(state.tracks().len()),
|
||||||
":select/scene/next" => self.selection().select_scene_next(self.scenes().len()),
|
":select/scene/next" => state.selection().select_scene_next(state.scenes().len()),
|
||||||
":select/scene/prev" => self.selection().select_scene_prev(),
|
":select/scene/prev" => state.selection().select_scene_prev(),
|
||||||
":select/track" => self.selection().select_track(self.tracks().len()),
|
":select/track" => state.selection().select_track(state.tracks().len()),
|
||||||
":select/track/next" => self.selection().select_track_next(self.tracks().len()),
|
":select/track/next" => state.selection().select_track_next(state.tracks().len()),
|
||||||
":select/track/prev" => self.selection().select_track_prev(),
|
":select/track/prev" => state.selection().select_track_prev(),
|
||||||
_ => return Err(format!("not selection: {word}").into())
|
_ => return Err(format!("not selection: {word}").into())
|
||||||
})).transpose()
|
})).transpose()
|
||||||
}
|
}
|
||||||
|
fn get_color (_: &App, src: impl Language) -> Perhaps<Color> {
|
||||||
fn get_color (&self, src: impl Language) -> Perhaps<Color> {
|
|
||||||
if let Some(expr) = src.expr()? {
|
if let Some(expr) = src.expr()? {
|
||||||
match (expr.head()?, expr.tail()?) {
|
match (expr.head()?, expr.tail()?) {
|
||||||
(Some("g"), Some(tail)) => {
|
(Some("g"), Some(tail)) => {
|
||||||
|
|
@ -795,44 +720,60 @@ mod app {
|
||||||
return Err(format!("not a color: {:?}", src.src()?).into())
|
return Err(format!("not a color: {:?}", src.src()?).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[allow(unused)]
|
||||||
fn get_opt_u7 (&self, src: impl Language) -> Perhaps<Option<u7>> {
|
fn get_opt_u7 (state: &App, src: impl Language) -> Perhaps<Option<u7>> {
|
||||||
src.word()?.map(|word|Ok(match word {
|
src.word()?.map(|word|Ok(match word {
|
||||||
":editor/pitch" => Some((
|
":editor/pitch" => Some((
|
||||||
self.editor().as_ref().map(|e|e.get_note_pos()).unwrap() as u8
|
state.editor().as_ref().map(|e|e.get_note_pos()).unwrap() as u8
|
||||||
).into()),
|
).into()),
|
||||||
_ => return Err(format!("unknown midi note: {word}").into())
|
_ => return Err(format!("unknown midi note: {word}").into())
|
||||||
})).transpose()
|
})).transpose()
|
||||||
}
|
}
|
||||||
|
fn get_opt_usize (state: &App, src: impl Language) -> Perhaps<Option<usize>> {
|
||||||
fn get_opt_u16 (&self, _src: impl Language) -> Perhaps<Option<u16>> {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_opt_usize (&self, src: impl Language) -> Perhaps<Option<usize>> {
|
|
||||||
src.word()?.map(|word|Ok(match word {
|
src.word()?.map(|word|Ok(match word {
|
||||||
":selected/scene" => self.selection().scene(),
|
":selected/scene" => state.selection().scene(),
|
||||||
":selected/track" => self.selection().track(),
|
":selected/track" => state.selection().track(),
|
||||||
_ => return Err(format!("unknown opt<usize>: {word}").into())
|
_ => return Err(format!("unknown opt<usize>: {word}").into())
|
||||||
})).transpose()
|
})).transpose()
|
||||||
}
|
}
|
||||||
|
#[allow(unused)]
|
||||||
fn get_clip (&self, src: impl Language) -> Perhaps<Option<Arc<RwLock<MidiClip>>>> {
|
fn get_clip (state: &App, src: impl Language) -> Perhaps<Option<Arc<RwLock<MidiClip>>>> {
|
||||||
src.word()?.map(|word|Ok(match word {
|
src.word()?.map(|word|Ok(match word {
|
||||||
":selected/clip" if let Selection::TrackClip { track, scene } = self.selection() =>
|
":selected/clip" if let Selection::TrackClip { track, scene } = state.selection() =>
|
||||||
self.scenes()[*scene].clips[*track].clone(),
|
state.scenes()[*scene].clips[*track].clone(),
|
||||||
_ => return Err(format!("not a clip: {word}").into())
|
_ => return Err(format!("not a clip: {word}").into())
|
||||||
})).transpose()
|
})).transpose()
|
||||||
}
|
}
|
||||||
|
fn get_axis (_: &App, 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_u16 (_: &App, _src: impl Language) -> Perhaps<Option<u16>> {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
fn get_opt_itemtheme (_: &App, _: impl Language) -> Perhaps<Option<ItemTheme>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn get_opt_vec_opt_arc_rwlock_midiclip (_: &App, _: impl Language) -> Perhaps<Option<Vec<Option<Arc<RwLock<MidiClip>>>>>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn get_opt_arc_array_connect (_: &App, _: impl Language) -> Perhaps<Option<Arc<[Connect]>>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn get_arc_array_connect (_: &App, _: impl Language) -> Perhaps<Arc<[Connect]>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn get_opt_arc_str (_: &App, _: impl Language) -> Perhaps<Option<Arc<str>>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn get_opt_bool (_: &App, _: impl Language) -> Perhaps<Option<bool>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn get_path_buf (_: &App, _: impl Language) -> Perhaps<PathBuf> {
|
||||||
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -879,56 +820,158 @@ mod bind {
|
||||||
/// let condition = tek::Condition(std::sync::Arc::new(Box::new(||{true})));
|
/// let condition = tek::Condition(std::sync::Arc::new(Box::new(||{true})));
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Condition(
|
pub struct Condition(pub Arc<Box<dyn Fn()->bool + Send + Sync>>);
|
||||||
pub Arc<Box<dyn Fn()->bool + Send + Sync>>
|
|
||||||
);
|
|
||||||
|
|
||||||
tui_keys!(self: App, input {
|
tui_keys!(self: App, input {
|
||||||
let commands = collect_commands(self, input)?;
|
let name = self.mode.as_ref();
|
||||||
let results = execute_commands(self, commands)?;
|
let mode = name.and_then(|m|self.config.get_mode(m)).as_ref().map(Arc::clone);
|
||||||
self.history.extend(results.into_iter());
|
if let Some(mode) = mode {
|
||||||
Ok(())
|
let binds = self.config.binds.clone();
|
||||||
});
|
|
||||||
|
|
||||||
fn collect_commands (app: &App, input: &TuiEvent)
|
|
||||||
-> Usually<Vec<AppCommand>>
|
|
||||||
{
|
|
||||||
let mut commands = vec![];
|
|
||||||
app.mode.as_ref().and_then(|m|app.config.get_mode(m)).map(|mode|{
|
|
||||||
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> {
|
if Self::command::<AppCommand>(self, command)? {
|
||||||
commands.push(command)
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok::<_, Box<dyn Error>>(())
|
|
||||||
}).transpose()?;
|
|
||||||
Ok(commands)
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
fn execute_commands (app: &mut App, commands: Vec<AppCommand>)
|
impl App {
|
||||||
-> Usually<Vec<(AppCommand, Option<AppCommand>)>>
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_from!(AppCommand: |x: AxisCommand| AppCommand::Axis { command: x });
|
||||||
|
impl_from!(AppCommand: |x: BrowseCommand| AppCommand::Browse { command: x });
|
||||||
|
impl_from!(AppCommand: |x: DialogCommand| AppCommand::Dialog { command: x });
|
||||||
|
impl_from!(AppCommand: |x: MidiClipCommand| AppCommand::MidiClip { command: x });
|
||||||
|
impl_from!(AppCommand: |x: PoolCommand| AppCommand::Pool { command: x });
|
||||||
|
impl_from!(AppCommand: |x: SceneCommand| AppCommand::Scene { command: x });
|
||||||
|
impl_from!(AppCommand: |x: ScenesCommand| AppCommand::Scenes { command: x });
|
||||||
|
impl_from!(AppCommand: |x: TrackCommand| AppCommand::Track { command: x });
|
||||||
|
impl_from!(AppCommand: |x: TracksCommand| AppCommand::Tracks { command: x });
|
||||||
|
#[tek_proc::commands(AppCommand)]
|
||||||
|
impl App {
|
||||||
|
#[command(Axis = "axis")]
|
||||||
|
pub fn command_axis (&mut self, command: AxisCommand) -> Perhaps<AppCommand> {
|
||||||
|
Ok(command.dispatch(self)?.map(Into::into))
|
||||||
|
}
|
||||||
|
#[command(Browse = "browse")]
|
||||||
|
pub fn command_browse (&mut self, command: BrowseCommand) -> Perhaps<AppCommand> {
|
||||||
|
Ok(command.dispatch(self)?.map(Into::into))
|
||||||
|
}
|
||||||
|
#[command(Dialog = "dialog")]
|
||||||
|
pub fn command_dialog (&mut self, command: DialogCommand) -> Perhaps<AppCommand> {
|
||||||
|
Ok(command.dispatch(self)?.map(Into::into))
|
||||||
|
}
|
||||||
|
#[command(MidiClip = "clip")]
|
||||||
|
pub fn command_midi_clip (&mut self, command: MidiClipCommand) -> Perhaps<AppCommand> {
|
||||||
|
Ok(command.dispatch(self)?.map(Into::into))
|
||||||
|
}
|
||||||
|
#[command(Pool = "pool")]
|
||||||
|
pub fn command_pool (&mut self, command: PoolCommand) -> Perhaps<AppCommand> {
|
||||||
|
Ok(command.dispatch(self)?.map(Into::into))
|
||||||
|
}
|
||||||
|
#[command(Scene = "scene")]
|
||||||
|
pub fn command_scene (&mut self, command: SceneCommand) -> Perhaps<AppCommand> {
|
||||||
|
Ok(command.dispatch(self)?.map(Into::into))
|
||||||
|
}
|
||||||
|
#[command(Scenes = "scenes")]
|
||||||
|
pub fn command_scenes (&mut self, command: ScenesCommand) -> Perhaps<AppCommand> {
|
||||||
|
Ok(command.dispatch(self)?.map(Into::into))
|
||||||
|
}
|
||||||
|
#[command(Track = "track")]
|
||||||
|
pub fn command_track (&mut self, command: TrackCommand) -> Perhaps<AppCommand> {
|
||||||
|
Ok(command.dispatch(self)?.map(Into::into))
|
||||||
|
}
|
||||||
|
#[command(Tracks = "tracks")]
|
||||||
|
pub fn command_tracks (&mut self, command: TracksCommand) -> Perhaps<AppCommand> {
|
||||||
|
Ok(command.dispatch(self)?.map(Into::into))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 = "seg")]
|
||||||
|
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 }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 +1055,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 +1267,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::*;
|
||||||
|
|
@ -1308,27 +1294,6 @@ mod device {
|
||||||
#[cfg(feature = "plugin")] pub use self::plugin::*;
|
#[cfg(feature = "plugin")] pub use self::plugin::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
//pub fn tui (
|
|
||||||
//app: Arc<RwLock<App>>,
|
|
||||||
//jack: Jack,
|
|
||||||
//sync_lead: &bool,
|
|
||||||
//sync_follow: &bool,
|
|
||||||
//) -> Usually<()> {
|
|
||||||
//// Run the [Tui] and [Jack] threads with the [App] state.
|
|
||||||
//Tui::run_main(&jack.run(move|jack|{
|
|
||||||
//// Between jack init and app's first cycle:
|
|
||||||
////jack.sync_lead(*sync_lead, |mut state|{
|
|
||||||
////let clock = app.write().unwrap().clock();
|
|
||||||
////clock.playhead.update_from_sample(state.position.frame() as f64);
|
|
||||||
////state.position.bbt = Some(clock.bbt());
|
|
||||||
////state.position
|
|
||||||
////})?;
|
|
||||||
////jack.sync_follow(*sync_follow)?;
|
|
||||||
//// FIXME: They don't work properly.
|
|
||||||
//Ok(app)
|
|
||||||
//})?)?
|
|
||||||
//}
|
|
||||||
|
|
||||||
pub use self::draw::*;
|
pub use self::draw::*;
|
||||||
mod draw {
|
mod draw {
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
@ -1346,8 +1311,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 +1488,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;
|
||||||
|
|
@ -1670,6 +1570,53 @@ mod draw {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn print_config (config: &Config) {
|
||||||
|
use ::ansi_term::Color::*;
|
||||||
|
println!("{:?}", config.dirs);
|
||||||
|
for (k, v) in config.views.read().unwrap().iter() {
|
||||||
|
println!("{} {} {v}", Green.paint("VIEW"), Green.bold().paint(format!("{k:<16}")));
|
||||||
|
}
|
||||||
|
for (k, v) in config.binds.read().unwrap().iter() {
|
||||||
|
println!("{} {}", Green.paint("BIND"), Green.bold().paint(format!("{k:<16}")));
|
||||||
|
for (k, v) in v.0.iter() {
|
||||||
|
print!("{} ", &Yellow.paint(match &k.0 {
|
||||||
|
Event::Key(KeyEvent { modifiers, .. }) =>
|
||||||
|
format!("{:>16}", format!("{modifiers}")),
|
||||||
|
_ => unimplemented!()
|
||||||
|
}));
|
||||||
|
print!("{}", &Yellow.bold().paint(match &k.0 {
|
||||||
|
Event::Key(KeyEvent { code, .. }) =>
|
||||||
|
format!("{:<10}", format!("{code}")),
|
||||||
|
_ => unimplemented!()
|
||||||
|
}));
|
||||||
|
for v in v.iter() {
|
||||||
|
print!(" => {:?}", v.commands);
|
||||||
|
print!(" {}", v.condition.as_ref().map(|x|format!("{x:?}")).unwrap_or_default());
|
||||||
|
println!(" {}", v.description.as_ref().map(|x|x.as_ref()).unwrap_or_default());
|
||||||
|
//println!(" {:?}", v.source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config.modes.for_each(|k, v|{
|
||||||
|
println!();
|
||||||
|
for v in v.name.iter() { print!("{}", Green.bold().paint(format!("{v} "))); }
|
||||||
|
for v in v.info.iter() { print!("\n{}", Green.paint(format!("{v}"))); }
|
||||||
|
print!("\n{} {}", Blue.paint("TOOL"), Green.bold().paint(format!("{k:<16}")));
|
||||||
|
print!("\n{}", Blue.paint("KEYS"));
|
||||||
|
for v in v.keys.iter() { print!("{}", Green.paint(format!(" {v}"))); }
|
||||||
|
println!();
|
||||||
|
v.modes.for_each(|k, v|{
|
||||||
|
print!("{} {} {:?}", Blue.paint("MODE"), Green.bold().paint(format!("{k:<16}")), v.name);
|
||||||
|
print!( " INFO={:?}", v.info);
|
||||||
|
print!( " VIEW={:?}", v.view);
|
||||||
|
println!(" KEYS={:?}", v.keys);
|
||||||
|
});
|
||||||
|
print!("{}", Blue.paint("VIEW"));
|
||||||
|
for v in v.view.iter() { print!("{}", Green.paint(format!(" {v}"))); }
|
||||||
|
println!();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn print_status (project: &Arrangement) {
|
pub fn print_status (project: &Arrangement) {
|
||||||
println!("Name: {:?}", &project.name);
|
println!("Name: {:?}", &project.name);
|
||||||
println!("JACK: {:?}", &project.jack);
|
println!("JACK: {:?}", &project.jack);
|
||||||
|
|
|
||||||
2
tengri
2
tengri
|
|
@ -1 +1 @@
|
||||||
Subproject commit b7f4d55e1d67d3481ecee14f693ca3e0a9426a6c
|
Subproject commit 1f541407597c7866cf1450d03967ab4711d28d29
|
||||||
Loading…
Add table
Add a link
Reference in a new issue