mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-08 12:46:42 +01:00
load up to modes
the stacked modal music editor. lol
This commit is contained in:
parent
b991a49ad7
commit
4d4c470a81
7 changed files with 220 additions and 205 deletions
175
config/binds.edn
175
config/binds.edn
|
|
@ -1,75 +1,96 @@
|
||||||
(module :global
|
(def :keys/cancel
|
||||||
(@esc dialog/hide)
|
(@escape cancel))
|
||||||
(@f1 dialog/show :dialog/help)
|
|
||||||
(@f6 dialog/show :dialog/save)
|
|
||||||
(@f8 dialog/show :dialog/options)
|
|
||||||
(@f9 dialog/show :dialog/load)
|
|
||||||
(@f10 dialog/show :dialog/quit)
|
|
||||||
(@u history/undo 1)
|
|
||||||
(@r history/redo 1))
|
|
||||||
|
|
||||||
(module :clock
|
(def :keys/delete
|
||||||
(@space clock/toggle 0)
|
(@delete delete))
|
||||||
(@shift/space clock/toggle 0))
|
|
||||||
|
|
||||||
(module :arranger
|
(def :keys/history
|
||||||
(@c color)
|
(@u undo 1) (@r redo 1))
|
||||||
(@q launch)
|
|
||||||
(@tab project/edit)
|
|
||||||
(@enter project/edit)
|
|
||||||
(@escape project/home)
|
|
||||||
(@shift/I project/input/add)
|
|
||||||
(@shift/O project/output/add)
|
|
||||||
(@shift/S project/scene/add)
|
|
||||||
(@shift/T project/track/add)
|
|
||||||
(@shift/D dialog/show :dialog/device)
|
|
||||||
(@up select :select/scene/prev)
|
|
||||||
(@down select :select/scene/next)
|
|
||||||
(@left select :select/track/prev)
|
|
||||||
(@right select :select/track/next)
|
|
||||||
(@t select :select/track)
|
|
||||||
(@s select :select/scene))
|
|
||||||
|
|
||||||
(module :track
|
(def :keys/nextprev
|
||||||
(@delete track/delete :track)
|
(@comma prev) (@period next))
|
||||||
(@q track/launch :track)
|
|
||||||
(@c track/color :track)
|
|
||||||
(@comma track/prev)
|
|
||||||
(@period track/next)
|
|
||||||
(@lt track/swap/prev)
|
|
||||||
(@gt track/swap/next)
|
|
||||||
(@r track/rec)
|
|
||||||
(@m track/mon)
|
|
||||||
(@p track/play)
|
|
||||||
(@P track/solo))
|
|
||||||
|
|
||||||
(module :scene
|
(def :keys/swap
|
||||||
(@delete scene/delete :scene)
|
(@lt swap/prev) (@gt swap/next))
|
||||||
(@q scene/launch :scene)
|
|
||||||
(@c scene/color :scene)
|
|
||||||
(@comma scene/prev)
|
|
||||||
(@period scene/next)
|
|
||||||
(@lt scene/swap/prev)
|
|
||||||
(@gt scene/swap/next))
|
|
||||||
|
|
||||||
(module :clip
|
(def :keys/list
|
||||||
(@g clip/get)
|
(@up list/prev) (@down list/next) (@enter list/select) (@escape list/cancel))
|
||||||
(@p clip/put)
|
|
||||||
(@delete clip/del)
|
(def :keys/clock
|
||||||
(@comma clip/prev)
|
(@space clock/toggle 0) (@shift/space clock/toggle 0))
|
||||||
(@period clip/next)
|
|
||||||
(@lt clip/swap/prev)
|
(def :keys/color
|
||||||
(@gt clip/swap/next)
|
(@c color))
|
||||||
(@l clip/loop/toggle))
|
|
||||||
|
(def :keys/launch
|
||||||
|
(@q launch))
|
||||||
|
|
||||||
|
(def :keys/filter
|
||||||
|
(see :keys/list :keys/input))
|
||||||
|
|
||||||
|
(def :keys/input
|
||||||
|
(see :keys/cancel :keys/delete)
|
||||||
|
(@left cursor/prev) (@right cursor/next) (@backspace delete/prev) (:char input))
|
||||||
|
|
||||||
|
(def :keys/global
|
||||||
|
(see :keys/cancel :keys/history)
|
||||||
|
(@f1 dialog :help) (@f8 dialog :options) (@f10 dialog :quit)
|
||||||
|
(@f6 dialog :save) (@f9 dialog :load))
|
||||||
|
|
||||||
|
(def :keys/arranger
|
||||||
|
(see :keys/color :keys/launch :keys/arranger/direction :keys/arranger/cycle)
|
||||||
|
(@tab project/edit) (@enter project/edit)
|
||||||
|
(@shift/I project/input/add) (@shift/O project/output/add)
|
||||||
|
(@shift/S project/scene/add) (@shift/T project/track/add)
|
||||||
|
(@shift/D dialog/show :dialog/device))
|
||||||
|
|
||||||
|
(def :keys/arranger/cycle
|
||||||
|
(@t select :select/track) (@s select :select/scene))
|
||||||
|
|
||||||
|
(def :keys/arranger/direction
|
||||||
|
(@up select :select/scene/prev) (@down select :select/scene/next)
|
||||||
|
(@left select :select/track/prev) (@right select :select/track/next))
|
||||||
|
|
||||||
|
(def :keys/scene (see :keys/color :keys/launch :keys/nextprev :keys/swap :keys/delete))
|
||||||
|
|
||||||
|
(def :keys/track (see :keys/color :keys/launch :keys/nextprev :keys/swap :keys/delete)
|
||||||
|
(@r toggle :rec) (@m toggle :mon) (@p toggle :play) (@P toggle :solo))
|
||||||
|
|
||||||
|
(def :keys/clip (see :keys/color :keys/launch :keys/nextprev :keys/swap :keys/delete)
|
||||||
|
(@l toggle :loop))
|
||||||
|
|
||||||
|
(def :keys/sampler (see :keys/sampler/directions :keys/sampler/record :keys/sampler/play))
|
||||||
|
|
||||||
|
(def :keys/sampler/play
|
||||||
|
(@p sampler/play/sample :sample/selected)
|
||||||
|
(@P sampler/stop/sample :sample/selected))
|
||||||
|
|
||||||
|
(def :keys/sampler/record
|
||||||
|
(@r sampler/record/toggle :sample/selected)
|
||||||
|
(@shift/R sampler/record/cancel))
|
||||||
|
|
||||||
|
(def :keys/sampler/import-export
|
||||||
|
(@shift/f6 dialog :dialog/export/sample)
|
||||||
|
(@shift/f9 dialog :dialog/import/sample))
|
||||||
|
|
||||||
|
(def :keys/sampler/directions
|
||||||
|
(@up sampler/select :sample/above)
|
||||||
|
(@down sampler/select :sample/below)
|
||||||
|
(@left sampler/select :sample/to/left)
|
||||||
|
(@right sampler/select :sample/to/right))
|
||||||
|
|
||||||
|
(def :keys/sequencer (see :keys/color :keys/launch)
|
||||||
|
(@shift/I input/add)
|
||||||
|
(@shift/O output/add))
|
||||||
|
|
||||||
(module :browser
|
(module :browser
|
||||||
(@escape browser/cancel)
|
(@escape browser/cancel)
|
||||||
(@return browser/confirm)
|
(@return browser/confirm)
|
||||||
(@up browser/cursor/set :browser/cursor/prev)
|
(@up browser/cursor/set :browser/cursor/prev)
|
||||||
(@down browser/cursor/set :browser/cursor/next)
|
(@down browser/cursor/set :browser/cursor/next)
|
||||||
(@right browser/address/set :browser/address/selected)
|
(@right browser/address/set :browser/address/selected)
|
||||||
(@left browser/address/set :browser/address/parent)
|
(@left browser/address/set :browser/address/parent)
|
||||||
(:char browser/filter/append :char)
|
(:char browser/filter/append :char)
|
||||||
(@backspace browser/filter/delete :last))
|
(@backspace browser/filter/delete :last))
|
||||||
|
|
||||||
(module :device/add
|
(module :device/add
|
||||||
|
|
@ -112,10 +133,6 @@
|
||||||
(@return set :length)
|
(@return set :length)
|
||||||
(@escape cancel))
|
(@escape cancel))
|
||||||
|
|
||||||
(module :message
|
|
||||||
(@esc message/dismiss)
|
|
||||||
(@enter message/dismiss))
|
|
||||||
|
|
||||||
(module :pool
|
(module :pool
|
||||||
(@n rename/begin)
|
(@n rename/begin)
|
||||||
(@t length/begin)
|
(@t length/begin)
|
||||||
|
|
@ -145,23 +162,3 @@
|
||||||
(@backspace delete :last)
|
(@backspace delete :last)
|
||||||
(@return confirm)
|
(@return confirm)
|
||||||
(@escape cancel))
|
(@escape cancel))
|
||||||
|
|
||||||
(module :sampler
|
|
||||||
(@up sampler/select :sample/above)
|
|
||||||
(@down sampler/select :sample/below)
|
|
||||||
(@left sampler/select :sample/to/left)
|
|
||||||
(@right sampler/select :sample/to/right)
|
|
||||||
|
|
||||||
(@r sampler/record/toggle :sample/selected)
|
|
||||||
(@shift/R sampler/record/cancel)
|
|
||||||
(@p sampler/play/sample :sample/selected)
|
|
||||||
(@P sampler/stop/sample :sample/selected)
|
|
||||||
|
|
||||||
(@shift/f6 dialog :dialog/export/sample)
|
|
||||||
(@shift/f9 dialog :dialog/import/sample))
|
|
||||||
|
|
||||||
(module :sequencer
|
|
||||||
(@c color)
|
|
||||||
(@q launch)
|
|
||||||
(@shift/I input/add)
|
|
||||||
(@shift/O output/add))
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
(module :transport
|
(mode :transport
|
||||||
(name "Transport")
|
(name Transport)
|
||||||
(info "A JACK transport controller.")
|
(info A JACK transport controller.)
|
||||||
(keys :clock)
|
(keys :clock :global)
|
||||||
(keys :global)
|
|
||||||
:view/transport)
|
:view/transport)
|
||||||
|
|
||||||
(module :arranger
|
(mode :arranger
|
||||||
(name "Arranger")
|
(name Arranger)
|
||||||
(info "A grid of launchable clips arranged by track and scene.")
|
(info A grid of launchable clips arranged by track and scene.)
|
||||||
(mode :editor (keys :editor))
|
(mode :editor (keys :editor))
|
||||||
(mode :dialog (keys :dialog))
|
(mode :dialog (keys :dialog))
|
||||||
(mode :message (keys :message))
|
(mode :message (keys :message))
|
||||||
|
|
@ -19,27 +18,27 @@
|
||||||
(mode :track (keys :track))
|
(mode :track (keys :track))
|
||||||
(mode :scene (keys :scene))
|
(mode :scene (keys :scene))
|
||||||
(mode :mix (keys :mix))
|
(mode :mix (keys :mix))
|
||||||
(keys :clock)
|
(keys :clock :arranger :global)
|
||||||
(keys :arranger)
|
|
||||||
(keys :global)
|
|
||||||
(bsp/w :view/meters/output
|
(bsp/w :view/meters/output
|
||||||
(bsp/e :view/meters/input
|
(bsp/e :view/meters/input
|
||||||
(stack/n (fixed/y 2 :view/status/h2) :view/tracks/inputs
|
(stack/n
|
||||||
(stack/s :view/tracks/devices :view/tracks/outputs :view/tracks/names
|
(fixed/y 2 :view/status/h2)
|
||||||
|
:view/tracks/inputs
|
||||||
|
(stack/s
|
||||||
|
:view/tracks/devices
|
||||||
|
:view/tracks/outputs
|
||||||
|
:view/tracks/names
|
||||||
(fill/xy (either :mode/editor
|
(fill/xy (either :mode/editor
|
||||||
(bsp/e :view/scenes/names :view/editor)
|
(bsp/e :view/scenes/names :view/editor)
|
||||||
:view/scenes)))))))
|
:view/scenes)))))))
|
||||||
|
|
||||||
(module :groovebox
|
(mode :groovebox
|
||||||
(name "Groovebox")
|
(name Groovebox)
|
||||||
(info "A sequencer with built-in sampler.")
|
(info A sequencer with built-in sampler.)
|
||||||
(mode :browser (keys :browser))
|
(mode :browser (keys :browser))
|
||||||
(mode :rename (keys :pool-rename))
|
(mode :rename (keys :pool-rename))
|
||||||
(mode :length (keys :pool-length))
|
(mode :length (keys :pool-length))
|
||||||
(keys :clock)
|
(keys :clock :editor :sampler :global)
|
||||||
(keys :editor)
|
|
||||||
(keys :sampler)
|
|
||||||
(keys :global)
|
|
||||||
(bsp/w :view/meters/output
|
(bsp/w :view/meters/output
|
||||||
(bsp/e :view/meters/input
|
(bsp/e :view/meters/input
|
||||||
(bsp/w
|
(bsp/w
|
||||||
|
|
@ -54,24 +53,21 @@
|
||||||
(bsp/e :view/samples/keys
|
(bsp/e :view/samples/keys
|
||||||
:view/editor)))))))
|
:view/editor)))))))
|
||||||
|
|
||||||
(module :sampler
|
(mode :sampler
|
||||||
(name "Sampler")
|
(name Sampler)
|
||||||
(info "A sampling soundboard.")
|
(info A sampling soundboard.)
|
||||||
(keys :sampler)
|
(keys :sampler :global)
|
||||||
(keys :global)
|
|
||||||
(bsp/s (fixed/y 1 :view/transport)
|
(bsp/s (fixed/y 1 :view/transport)
|
||||||
(bsp/n (fixed/y 1 :view/status)
|
(bsp/n (fixed/y 1 :view/status)
|
||||||
(fill/xy :view/samples/grid))))
|
(fill/xy :view/samples/grid))))
|
||||||
|
|
||||||
(module :sequencer
|
(mode :sequencer
|
||||||
(name "Sequencer")
|
(name Sequencer)
|
||||||
(info "A MIDI sequencer.")
|
(info A MIDI sequencer.)
|
||||||
(mode :browser (keys :browser))
|
(mode :browser (keys :browser))
|
||||||
(mode :rename (keys :pool-rename))
|
(mode :rename (keys :pool-rename))
|
||||||
(mode :length (keys :pool-length))
|
(mode :length (keys :pool-length))
|
||||||
(keys :editor)
|
(keys :editor :clock :global)
|
||||||
(keys :clock)
|
|
||||||
(keys :global)
|
|
||||||
(bsp/s (fixed/y 1 :view/transport)
|
(bsp/s (fixed/y 1 :view/transport)
|
||||||
(bsp/n (fixed/y 1 :view/status)
|
(bsp/n (fixed/y 1 :view/status)
|
||||||
(fill/xy (bsp/a (fill/xy (align/e :view/pool))
|
(fill/xy (bsp/a (fill/xy (align/e :view/pool))
|
||||||
|
|
|
||||||
|
|
@ -69,16 +69,36 @@ pub struct App {
|
||||||
/// Base color.
|
/// Base color.
|
||||||
pub color: ItemTheme,
|
pub color: ItemTheme,
|
||||||
}
|
}
|
||||||
type ModuleMap<T> = Arc<RwLock<BTreeMap<Arc<str>, T>>>;
|
|
||||||
/// Configuration
|
/// Configuration
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
/// XDG basedirs
|
/// XDG basedirs
|
||||||
pub dirs: BaseDirectories,
|
pub dirs: BaseDirectories,
|
||||||
/// Available view profiles
|
/// Available view profiles
|
||||||
pub views: ModuleMap<Profile>,
|
pub views: Arc<RwLock<BTreeMap<Arc<str>, Profile>>>,
|
||||||
/// Available input bindings
|
/// Available input bindings
|
||||||
pub binds: ModuleMap<EventMap<Option<TuiEvent>, Arc<str>>>,
|
pub binds: Arc<RwLock<BTreeMap<Arc<str>, EventMap<Option<TuiEvent>, Arc<str>>>>>,
|
||||||
|
}
|
||||||
|
/// Profile
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct Profile {
|
||||||
|
/// Path of configuration entrypoint
|
||||||
|
pub path: PathBuf,
|
||||||
|
/// Default mode
|
||||||
|
pub mode: Mode,
|
||||||
|
/// Optional modes
|
||||||
|
pub modes: BTreeMap<Arc<str>, Mode>,
|
||||||
|
}
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct Mode {
|
||||||
|
/// Name of configuration
|
||||||
|
pub name: Vec<Arc<str>>,
|
||||||
|
/// Description of configuration
|
||||||
|
pub info: Vec<Arc<str>>,
|
||||||
|
/// View definition
|
||||||
|
pub view: Vec<Arc<str>>,
|
||||||
|
// Input keymap
|
||||||
|
pub keys: Vec<Arc<str>>,
|
||||||
}
|
}
|
||||||
impl Config {
|
impl Config {
|
||||||
const VIEWS: &'static str = "views.edn";
|
const VIEWS: &'static str = "views.edn";
|
||||||
|
|
@ -88,16 +108,14 @@ impl Config {
|
||||||
pub fn init () -> Usually<Self> {
|
pub fn init () -> Usually<Self> {
|
||||||
let mut cfgs: Self = Default::default();
|
let mut cfgs: Self = Default::default();
|
||||||
cfgs.dirs = BaseDirectories::with_profile("tek", "v0");
|
cfgs.dirs = BaseDirectories::with_profile("tek", "v0");
|
||||||
cfgs.load(Self::VIEWS, Self::DEFAULT_VIEWS, |cfgs, dsl|cfgs.load_view(dsl))?;
|
|
||||||
cfgs.load(Self::BINDS, Self::DEFAULT_BINDS, |cfgs, dsl|cfgs.load_bind(dsl))?;
|
cfgs.load(Self::BINDS, Self::DEFAULT_BINDS, |cfgs, dsl|cfgs.load_bind(dsl))?;
|
||||||
println!("{cfgs:#?}");
|
cfgs.load(Self::VIEWS, Self::DEFAULT_VIEWS, |cfgs, dsl|cfgs.load_view(dsl))?;
|
||||||
Ok(cfgs)
|
Ok(cfgs)
|
||||||
}
|
}
|
||||||
pub fn load (
|
pub fn load (
|
||||||
&mut self, path: &str, defaults: &str, mut each: impl FnMut(&mut Self, &str)->Usually<()>
|
&mut self, path: &str, defaults: &str, mut each: impl FnMut(&mut Self, &str)->Usually<()>
|
||||||
) -> Usually<()> {
|
) -> Usually<()> {
|
||||||
if self.dirs.find_config_file(path).is_none() {
|
if self.dirs.find_config_file(path).is_none() {
|
||||||
println!("init: {path}");
|
|
||||||
std::fs::write(self.dirs.place_config_file(path)?, defaults);
|
std::fs::write(self.dirs.place_config_file(path)?, defaults);
|
||||||
}
|
}
|
||||||
Ok(if let Some(path) = self.dirs.find_config_file(path) {
|
Ok(if let Some(path) = self.dirs.find_config_file(path) {
|
||||||
|
|
@ -107,15 +125,10 @@ impl Config {
|
||||||
return Err(format!("{path}: not found").into())
|
return Err(format!("{path}: not found").into())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn load_view <D: Dsl> (&mut self, dsl: D) -> Usually<()> {
|
|
||||||
load_mod(dsl, |id, tail|Ok(self.views.write().unwrap().insert(id.into(),
|
|
||||||
Profile::from_dsl(tail)?)))
|
|
||||||
}
|
|
||||||
pub fn load_bind <D: Dsl> (&mut self, dsl: D) -> Usually<()> {
|
pub fn load_bind <D: Dsl> (&mut self, dsl: D) -> Usually<()> {
|
||||||
load_mod(dsl, |id, tail|Ok(self.binds.write().unwrap().insert(id.into(), {
|
load_modules(dsl, |id, tail|Ok(self.binds.write().unwrap().insert(id.into(), {
|
||||||
let mut map = EventMap::new();
|
let mut map = EventMap::new();
|
||||||
tail.each(|item|{
|
tail.each(|item|{
|
||||||
println!("{item:?}");
|
|
||||||
map.add(TuiEvent::from_dsl(item.exp()?.head()?)?, Binding {
|
map.add(TuiEvent::from_dsl(item.exp()?.head()?)?, Binding {
|
||||||
command: item.exp()?.tail()?.unwrap_or_default().into(),
|
command: item.exp()?.tail()?.unwrap_or_default().into(),
|
||||||
condition: None,
|
condition: None,
|
||||||
|
|
@ -127,8 +140,59 @@ impl Config {
|
||||||
map
|
map
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
pub fn load_view <D: Dsl> (&mut self, dsl: D) -> Usually<()> {
|
||||||
|
load_modules(&dsl, |id, tail|Ok(self.views.write().unwrap().insert(id.into(), {
|
||||||
|
let mut profile = Profile::default();
|
||||||
|
tail.each(|item|Ok(if let Ok(Some(exp)) = item.exp() {
|
||||||
|
match exp.head()? {
|
||||||
|
Some("name") => profile.mode.name.push(
|
||||||
|
exp.tail()?.map(|x|x.trim()).unwrap_or("").into()
|
||||||
|
),
|
||||||
|
Some("info") => profile.mode.info.push(
|
||||||
|
exp.tail()?.map(|x|x.trim()).unwrap_or("").into()
|
||||||
|
),
|
||||||
|
Some("keys") => if let Some(tail) = exp.tail()? {
|
||||||
|
tail.each(|keys|Ok(profile.mode.keys.push(keys.trim().into())))?;
|
||||||
|
} else {
|
||||||
|
return Err(format!("load_view: empty keys: {exp}").into())
|
||||||
|
},
|
||||||
|
Some("mode") => if let (Some(name), Some(tail)) = (
|
||||||
|
exp.tail()?.head()?, exp.tail()?.tail()?,
|
||||||
|
) {
|
||||||
|
let mut mode: Mode = Default::default();
|
||||||
|
tail.each(|item|Ok(if let Ok(Some(exp)) = item.exp() {
|
||||||
|
match exp.head()? {
|
||||||
|
Some("keys") => if let Some(tail) = exp.tail()? {
|
||||||
|
tail.each(|keys|Ok(mode.keys.push(keys.trim().into())))?;
|
||||||
|
} else {
|
||||||
|
return Err(format!("load_view: empty keys: {exp}").into())
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
return Err(format!("load_view: unexpected in mode {name}: {item:?}").into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Ok(Some(sym)) = item.sym() {
|
||||||
|
// TODO
|
||||||
|
} else {
|
||||||
|
return Err(format!("load_view: unexpected in mode {name}: {item:?}").into())
|
||||||
|
}))?;
|
||||||
|
profile.modes.insert(name.trim().into(), mode);
|
||||||
|
} else {
|
||||||
|
return Err(format!("load_view: empty mode: {exp}").into())
|
||||||
|
},
|
||||||
|
Some(_) => profile.mode.view.push(exp.into()),
|
||||||
|
None => return Err(format!("load_view: empty: {exp}").into())
|
||||||
|
}
|
||||||
|
} else if let Ok(Some(sym)) = item.sym() {
|
||||||
|
profile.mode.view.push(sym.into());
|
||||||
|
} else {
|
||||||
|
return Err(format!("load_view: unexpected: {dsl:?}").into())
|
||||||
|
}))?;
|
||||||
|
profile
|
||||||
|
})))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fn load_mod <D: Dsl, U> (dsl: D, cb: impl Fn(&str, &str)->Usually<U>) -> Usually<()> {
|
fn load_modules <D: Dsl, U> (dsl: D, cb: impl Fn(&str, &str)->Usually<U>) -> Usually<()> {
|
||||||
//dsl!(dsl|module :id ..body|dsl!(body|@bind #info? ..commands|)
|
//dsl!(dsl|module :id ..body|dsl!(body|@bind #info? ..commands|)
|
||||||
if let Some(exp) = dsl.exp()?
|
if let Some(exp) = dsl.exp()?
|
||||||
&& Some("module") == exp.head().key()?
|
&& Some("module") == exp.head().key()?
|
||||||
|
|
@ -142,66 +206,6 @@ fn load_mod <D: Dsl, U> (dsl: D, cb: impl Fn(&str, &str)->Usually<U>) -> Usually
|
||||||
return Err("unexpected: {exp:?}".into());
|
return Err("unexpected: {exp:?}".into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Profile
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct Profile {
|
|
||||||
/// Path of configuration entrypoint
|
|
||||||
pub path: PathBuf,
|
|
||||||
/// Name of configuration
|
|
||||||
pub name: Option<Arc<str>>,
|
|
||||||
/// Description of configuration
|
|
||||||
pub info: Option<Arc<str>>,
|
|
||||||
/// View definition
|
|
||||||
pub view: Arc<str>,
|
|
||||||
// Input keymap
|
|
||||||
pub keys: EventMap<TuiEvent, AppCommand>,
|
|
||||||
}
|
|
||||||
impl Profile {
|
|
||||||
fn from_dsl (dsl: impl Dsl) -> Usually<Self> {
|
|
||||||
let mut profile = Self { ..Default::default() };
|
|
||||||
dsl.each(|dsl|{
|
|
||||||
let head = dsl.head();
|
|
||||||
let exp = dsl.exp();
|
|
||||||
Ok(if exp.head().key() == Ok(Some("name")) {
|
|
||||||
profile.name = Some(exp.tail()?.unwrap_or_default().into());
|
|
||||||
} else if exp.head().key() == Ok(Some("info")) {
|
|
||||||
profile.info = Some(exp.tail()?.unwrap_or_default().into());
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
Ok(profile)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Profile {
|
|
||||||
fn load_template (&mut self, dsl: impl Dsl) -> Usually<&mut Self> {
|
|
||||||
//dsl.src()?.unwrap_or_default().each(|item|Ok(match () {
|
|
||||||
//_ if let Some(exp) = dsl.exp()? => match exp.head()?.key()? {
|
|
||||||
//Some("name") => match exp.tail()?.text()? {
|
|
||||||
//Some(name) => self.name = Some(name.into()),
|
|
||||||
//_ => return Err(format!("missing name definition").into())
|
|
||||||
//},
|
|
||||||
//Some("info") => match exp.tail()?.text()? {
|
|
||||||
//Some(info) => self.info = Some(info.into()),
|
|
||||||
//_ => return Err(format!("missing info definition").into())
|
|
||||||
//},
|
|
||||||
//Some("bind") => match exp.tail()? {
|
|
||||||
//Some(keys) => self.keys = EventMap::from_dsl(&mut &keys)?,
|
|
||||||
//_ => return Err(format!("missing keys definition").into())
|
|
||||||
//},
|
|
||||||
//Some("view") => match exp.tail()? {
|
|
||||||
//Some(tail) => self.view = tail.src()?.unwrap_or_default().into(),
|
|
||||||
//_ => return Err(format!("missing view definition").into())
|
|
||||||
//},
|
|
||||||
//dsl => return Err(format!("unexpected: {dsl:?}").into())
|
|
||||||
//},
|
|
||||||
//_ => return Err(format!("unexpected: {dsl:?}").into())
|
|
||||||
//}));
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
fn load_binding (&mut self, dsl: impl Dsl) -> Usually<&mut Self> {
|
|
||||||
todo!();
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Various possible dialog modes.
|
/// Various possible dialog modes.
|
||||||
impl App {
|
impl App {
|
||||||
pub fn update_clock (&self) {
|
pub fn update_clock (&self) {
|
||||||
|
|
@ -230,12 +234,6 @@ maybe_has!(Scene: |self: App| { MaybeHas::<Scene>::get(&self.project) };
|
||||||
impl HasClipsSize for App { fn clips_size (&self) -> &Measure<TuiOut> { &self.project.inner_size } }
|
impl HasClipsSize for App { fn clips_size (&self) -> &Measure<TuiOut> { &self.project.inner_size } }
|
||||||
impl HasTrackScroll for App { fn track_scroll (&self) -> usize { self.project.track_scroll() } }
|
impl HasTrackScroll for App { fn track_scroll (&self) -> usize { self.project.track_scroll() } }
|
||||||
impl HasSceneScroll for App { fn scene_scroll (&self) -> usize { self.project.scene_scroll() } }
|
impl HasSceneScroll for App { fn scene_scroll (&self) -> usize { self.project.scene_scroll() } }
|
||||||
fn unquote (x: &str) -> &str {
|
|
||||||
let mut chars = x.chars();
|
|
||||||
chars.next();
|
|
||||||
//chars.next_back();
|
|
||||||
chars.as_str()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub enum Dialog {
|
pub enum Dialog {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,14 @@ use crate::*;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AppCommand {}
|
pub enum AppCommand {}
|
||||||
handle!(TuiIn:|self: App, input|{
|
handle!(TuiIn:|self: App, input|{
|
||||||
panic!("{input:?}");
|
panic!("wat: {:?}", self.profile);
|
||||||
|
for keys in self.profile.mode.keys.iter() {
|
||||||
|
panic!("{keys} {:?}", self.config.binds.read().unwrap());
|
||||||
|
if let Some(binding) = self.config.binds.read().unwrap().get(keys.as_ref()) {
|
||||||
|
panic!("{binding:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
//Ok(if let Some(binding) = self.profile.as_ref()
|
//Ok(if let Some(binding) = self.profile.as_ref()
|
||||||
//.map(|c|c.keys.dispatch(input.event())).flatten()
|
//.map(|c|c.keys.dispatch(input.event())).flatten()
|
||||||
//{
|
//{
|
||||||
|
|
@ -95,12 +102,14 @@ dsl_exp!(|app: App| -> AppCommand {
|
||||||
["clock" / command: ClockCommand] => todo!(),
|
["clock" / command: ClockCommand] => todo!(),
|
||||||
["sampler" / command: SamplerCommand] => todo!(),
|
["sampler" / command: SamplerCommand] => todo!(),
|
||||||
["pool" / command: PoolCommand] => todo!(),
|
["pool" / command: PoolCommand] => todo!(),
|
||||||
|
["pool" / editor: MidiEditCommand] => todo!(),
|
||||||
});
|
});
|
||||||
dsl_exp!(|app: App| -> DialogCommand {});
|
dsl_exp!(|app: App| -> DialogCommand {});
|
||||||
dsl_exp!(|app: App| -> ArrangementCommand {});
|
dsl_exp!(|app: App| -> ArrangementCommand {});
|
||||||
dsl_exp!(|app: App| -> ClockCommand {});
|
dsl_exp!(|app: App| -> ClockCommand {});
|
||||||
dsl_exp!(|app: App| -> SamplerCommand {});
|
dsl_exp!(|app: App| -> SamplerCommand {});
|
||||||
dsl_exp!(|app: App| -> PoolCommand {});
|
dsl_exp!(|app: App| -> PoolCommand {});
|
||||||
|
dsl_exp!(|app: App| -> MidiEditCommand {});
|
||||||
//dsl_bind!(AppCommand: App {
|
//dsl_bind!(AppCommand: App {
|
||||||
//enqueue = |app, clip: Option<Arc<RwLock<MidiClip>>>| { todo!() };
|
//enqueue = |app, clip: Option<Arc<RwLock<MidiClip>>>| { todo!() };
|
||||||
//history = |app, delta: isize| { todo!() };
|
//history = |app, delta: isize| { todo!() };
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,23 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
/// Namespace. Currently linearly searched.
|
||||||
pub struct DslNs<'t, T: 't>(pub &'t [(&'t str, T)]);
|
pub struct DslNs<'t, T: 't>(pub &'t [(&'t str, T)]);
|
||||||
|
|
||||||
pub trait DslSymNs<'t, T: 't>: 't { const NS: DslNs<'t, fn (&'t Self)->T>; }
|
/// Namespace where keys are symbols.
|
||||||
|
pub trait DslSymNs<'t, T: 't>: 't {
|
||||||
|
const NS: DslNs<'t, fn (&'t Self)->T>;
|
||||||
|
fn from_sym <D: Dsl> (&'t self, dsl: D) -> Usually<T> {
|
||||||
|
if let Some(dsl) = dsl.sym()? {
|
||||||
|
for (sym, get) in Self::NS.0 {
|
||||||
|
if dsl == *sym {
|
||||||
|
return Ok(get(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Err(format!("not found: sym: {dsl:?}").into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export] macro_rules! dsl_sym (
|
#[macro_export] macro_rules! dsl_sym (
|
||||||
(|$state:ident:$State:ty| -> $type:ty {$($lit:literal => $exp:expr),* $(,)?})=>{
|
(|$state:ident:$State:ty| -> $type:ty {$($lit:literal => $exp:expr),* $(,)?})=>{
|
||||||
impl<'t> DslSymNs<'t, $type> for $State {
|
impl<'t> DslSymNs<'t, $type> for $State {
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@ pub const VIEW: DslNs<'static, DslCb> = DslNs(&[
|
||||||
Stack::south(move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
|
Stack::south(move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
|
||||||
for (index, (id, profile)) in views.read().unwrap().iter().enumerate() {
|
for (index, (id, profile)) in views.read().unwrap().iter().enumerate() {
|
||||||
let bg = if index == 0 { Rgb(64,64,64) } else { Rgb(32,32,32) };
|
let bg = if index == 0 { Rgb(64,64,64) } else { Rgb(32,32,32) };
|
||||||
let name = profile.name.as_ref().map(|x|unquote(unquote(x.as_ref())));
|
let name = profile.mode.name.get(0).map(|x|x.as_ref()).unwrap_or("<no name>");
|
||||||
let info = profile.info.as_ref().map(|x|unquote(unquote(x.as_ref())));
|
let info = profile.mode.info.get(0).map(|x|x.as_ref()).unwrap_or("<no info>");
|
||||||
add(&Fixed::y(3, Tui::bg(bg, Bsp::s(
|
add(&Fixed::y(3, Tui::bg(bg, Bsp::s(
|
||||||
Fill::x(Bsp::a(
|
Fill::x(Bsp::a(
|
||||||
Fill::x(Align::w(Tui::fg(Rgb(224,192,128), name))),
|
Fill::x(Align::w(Tui::fg(Rgb(224,192,128), name))),
|
||||||
|
|
|
||||||
2
deps/tengri
vendored
2
deps/tengri
vendored
|
|
@ -1 +1 @@
|
||||||
Subproject commit 24ac52d8079140b2b9f363d1568597bb5600a166
|
Subproject commit e839096cf33f72c000d60b73958e1d6a0ec82be5
|
||||||
Loading…
Add table
Add a link
Reference in a new issue