diff --git a/config/profiles.edn b/config/profiles.edn deleted file mode 100644 index 918d4d60..00000000 --- a/config/profiles.edn +++ /dev/null @@ -1,80 +0,0 @@ -(module :transport - (name "Transport") - (info "A JACK transport controller.") - (keys :clock) - (keys :global) - :view/transport) - -(module :arranger - (name "Arranger") - (info "A grid of launchable clips arranged by track and scene.") - (mode :editor (keys :editor)) - (mode :dialog (keys :dialog)) - (mode :message (keys :message)) - (mode :device-add (keys :device-add)) - (mode :browser (keys :browser)) - (mode :rename (keys :pool-rename)) - (mode :length (keys :pool-length)) - (mode :clip (keys :clip)) - (mode :track (keys :track)) - (mode :scene (keys :scene)) - (mode :mix (keys :mix)) - (keys :clock) - (keys :arranger) - (keys :global) - :view/dialog - (bsp/w :view/meters/output - (bsp/e :view/meters/input - (stack/n (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 - (bsp/e :view/scenes/names :view/editor) - :view/scenes))))))) - -(module :groovebox - (name "Groovebox") - (info "A sequencer with built-in sampler.") - (mode :browser (keys :browser)) - (mode :rename (keys :pool-rename)) - (mode :length (keys :pool-length)) - (keys :clock) - (keys :editor) - (keys :sampler) - (keys :global) - :view/dialog - (bsp/w :view/meters/output - (bsp/e :view/meters/input - (bsp/w - (fill/y (align/n (stack/s :view/midi-ins/status - :view/midi-outs/status - :view/audio-ins/status - :view/audio-outs/status - :view/pool))) - (bsp/n (fixed/y :h-sample-detail (bsp/e (fill/y (fixed/x 20 (align/nw :view/sample-status))) - :view/sample-viewer)) - (bsp/e (fill/y (align/n (bsp/s :view/status/v :view/editor-status))) - (bsp/e :view/samples/keys - :view/editor))))))) - -(module :sampler - (name "Sampler") - (info "A sampling soundboard.") - (keys :sampler) - (keys :global) - :view/dialog - (bsp/s (fixed/y 1 :view/transport) - (bsp/n (fixed/y 1 :view/status) - (fill/xy :view/samples/grid)))) - -(module :sequencer - (name "Sequencer") - (info "A MIDI sequencer.") - (mode :browser (keys :browser)) - (mode :rename (keys :pool-rename)) - (mode :length (keys :pool-length)) - (keys :editor) - (keys :clock) - (keys :global) - :view/dialog - (bsp/s (fixed/y 1 :view/transport) (bsp/n (fixed/y 1 :view/status) - (fill/xy (bsp/a (fill/xy (align/e :view/pool)) :view/editor))))) diff --git a/config/templates.edn b/config/templates.edn new file mode 100644 index 00000000..c3efd64c --- /dev/null +++ b/config/templates.edn @@ -0,0 +1,74 @@ +(module :transport + (name "Transport") + (info "A JACK transport controller.") + (bind :keys/clock) + (bind :keys/global) + :view/transport) + +(module :arranger + (name "Arranger") + (info "A grid of launchable clips arranged by track and scene.") + (bind :keys/editor :focused/editor) + (bind :keys/dialog :focused/dialog) + (bind :keys/message :focused/message) + (bind :keys/device_add :focused/device-add) + (bind :keys/browser :focused/browser) + (bind :keys/rename :focused/pool-rename) + (bind :keys/length :focused/pool-length) + (bind :keys/clip :focused/clip) + (bind :keys/track :focused/track) + (bind :keys/scene :focused/scene) + (bind :keys/mix :focused/mix) + (bind :keys/clock) + (bind :keys/arranger) + (bind :keys/global) + :view/dialog + (bsp/w :view/meters/output + (bsp/e :view/meters/input + (stack/n (fixed/y 2 :view/status/h2) :view/tracks/inputs + (stack/s :view/tracks/devices :view/tracks/outputs :view/tracks/names + (fill/xy (either :focused/editor + (bsp/e :view/scenes/names :view/editor) + :view/scenes))))))) + +(module :groovebox + (name "Groovebox") + (info "A sequencer with built-in sampler.") + (bind :keys/browser :focused/browser) + (bind :keys/rename :focused/pool-rename) + (bind :keys/length :focused/pool-length) + (bind :keys/clock) + (bind :keys/editor) + (bind :keys/sampler) + (bind :keys/global) + :view/dialog + (bsp/w :view/meters/output (bsp/e :view/meters/input (bsp/w (fill/y (align/n (stack/s :view/midi-ins/status + :view/midi-outs/status + :view/audio-ins/status + :view/audio-outs/status + :view/pool))) + (bsp/n (fixed/y :h-sample-detail (bsp/e (fill/y (fixed/x 20 (align/nw :view/sample-status))) :view/sample-viewer)) + (bsp/e (fill/y (align/n (bsp/s :view/status/v :view/editor-status))) (bsp/e :view/samples/keys :view/editor))))))) + +(module :sampler + (name "Sampler") + (info "A sampling soundboard.") + (bind :keys/sampler) + (bind :keys/global) + :view/dialog + (bsp/s (fixed/y 1 :view/transport) + (bsp/n (fixed/y 1 :view/status) + (fill/xy :view/samples/grid)))) + +(module :sequencer + (name "Sequencer") + (info "A MIDI sequencer.") + (bind :keys/browser :focused/browser) + (bind :keys/rename :mode/pool-rename) + (bind :keys/length :mode/pool-length) + (bind :keys/editor) + (bind :keys/clock) + (bind :keys/global) + :view/dialog + (bsp/s (fixed/y 1 :view/transport) (bsp/n (fixed/y 1 :view/status) + (fill/xy (bsp/a (fill/xy (align/e :view/pool)) :view/editor))))) diff --git a/crates/app/src/config.rs b/crates/app/src/config.rs index 6fa417c6..9c99aba7 100644 --- a/crates/app/src/config.rs +++ b/crates/app/src/config.rs @@ -1,16 +1,17 @@ use crate::*; use xdg::BaseDirectories; -/// Configuration + +/// Configurations #[derive(Default, Debug)] pub struct Configurations { - pub dirs: BaseDirectories, - pub current: Option, - pub profiles: Arc, Profile>>>, - pub bindings: Arc, Arc>>>, + pub dirs: BaseDirectories, + pub modules: Arc, Arc>>>, + pub current: Option } -/// Profile + +/// Configuration #[derive(Default, Debug)] -pub struct Profile { +pub struct Configuration { /// Path of configuration entrypoint pub path: std::path::PathBuf, /// Name of configuration @@ -22,82 +23,56 @@ pub struct Profile { // Input keymap pub keys: EventMap, } -impl Profile { - fn from_dsl (dsl: impl Dsl) -> Usually { - 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) + +macro_rules! dsl_for_each (($dsl:expr => |$head:ident|$body:expr)=>{ + let mut dsl: Arc = $dsl.src().into(); + let mut $head: Option> = dsl.head()?.map(Into::into); + let mut tail: Option> = dsl.tail()?.map(Into::into); + loop { + if let Some($head) = $head { + $body; + } else { + break + } + if let Some(next) = tail { + $head = next.head()?.map(Into::into); + tail = next.tail()?.map(Into::into); + } else { + break + } } -} +}); + impl Configurations { - const PROFILES: &'static str = "profiles.edn"; - const BINDINGS: &'static str = "bindings.edn"; - const DEFAULT_PROFILES: &'static str = include_str!("../../../config/profiles.edn"); - const DEFAULT_BINDINGS: &'static str = include_str!("../../../config/bindings.edn"); + const DEFAULT_TEMPLATES: &'static str = include_str!("../../../config/templates.edn"); + const DEFAULT_BINDINGS: &'static str = include_str!("../../../config/bindings.edn"); pub fn init () -> Usually { let mut dirs = BaseDirectories::with_profile("tek", "v0"); let mut cfgs = Self { dirs, ..Default::default() }; - cfgs.init_file(Self::PROFILES, Self::DEFAULT_PROFILES)?; - cfgs.load_file(Self::PROFILES, |cfgs, dsl|{ - Ok(if dsl.exp().head().key() == Ok(Some("module")) { - let exp = dsl.exp()?; - let tail = exp.tail()?; - let head = tail.head()?; - if let Some(id) = head.sym()? { - cfgs.profiles.write().unwrap().insert( - id.into(), - Profile::from_dsl(tail.tail()?)? - ); - } - } else { - return Err("unexpected: {exp:?}".into()); - }) - })?; - //cfgs.init_file(Self::BINDINGS, Self::DEFAULT_BINDINGS)?; - //cfgs.load_file(Self::BINDINGS, |cfgs, dsl|Ok( - //if let Some(exp) = dsl.head()?.exp()? && exp.head()?.key()? == Some("module") { - //let name = exp.tail()?.head()?.unwrap_or_default().into(); - //println!("name = {name}"); - //let body = exp.tail()?.tail()?.unwrap_or_default().into(); - //println!("body = {body}"); - //cfgs.bindings.write().unwrap().insert(name, body); - //} else { - //return Err("unexpected: {exp:?}".into()); - //} - //))?; - println!("{cfgs:#?}"); + cfgs.init_file("templates.edn", Self::DEFAULT_TEMPLATES)?; + cfgs.load_file("templates.edn", |head|{ Ok(()) })?; + cfgs.init_file("bindings.edn", Self::DEFAULT_BINDINGS)?; + cfgs.load_file("bindings.end", |head|{ Ok(()) })?; Ok(cfgs) } fn init_file (&mut self, path: &str, val: &str) -> Usually<()> { - if self.dirs.find_config_file(path).is_none() { - std::fs::write(self.dirs.place_config_file("profiles.edn")?, Self::DEFAULT_PROFILES); + if self.dirs.find_config_file("templates.edn").is_none() { + std::fs::write(self.dirs.place_config_file("templates.edn")?, Self::DEFAULT_TEMPLATES); } Ok(()) } - fn load_file ( - &mut self, - path: &str, - mut each: impl FnMut(&mut Self, &str)->Usually<()> - ) -> Usually<()> { - Ok(if let Some(path) = self.dirs.find_config_file(path) { - let src = std::fs::read_to_string(&path)?; - src.as_str().each(move|item|each(self, item))?; + fn load_file (&mut self, path: &str, mut each: impl FnMut(&Arc)->Usually<()> ) -> Usually<()> { + Ok(if let Some(path) = self.dirs.find_config_file("templates.edn") { + dsl_for_each!(std::fs::read_to_string(path)?.as_str() => |dsl|each(&dsl)); } else { return Err(format!("{path}: not found").into()) }) } } -impl Profile { + +impl Configuration { fn load_template (&mut self, dsl: impl Dsl) -> Usually<&mut Self> { - dsl.src()?.unwrap_or_default().each(|item|Ok(match () { + dsl_for_each!(dsl => |dsl|match () { _ if let Some(exp) = dsl.exp()? => match exp.head()?.key()? { Some("name") => match exp.tail()?.text()? { Some(name) => self.name = Some(name.into()), @@ -112,13 +87,14 @@ impl Profile { _ => return Err(format!("missing keys definition").into()) }, Some("view") => match exp.tail()? { - Some(tail) => self.view = tail.src()?.unwrap_or_default().into(), + Some(tail) => self.view = tail.src().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> { diff --git a/crates/app/src/view.rs b/crates/app/src/view.rs index 23a1f0ab..e31fe771 100644 --- a/crates/app/src/view.rs +++ b/crates/app/src/view.rs @@ -38,19 +38,11 @@ impl App { "nil" } pub fn view_menu (&self) -> impl Content + use<'_> { - Bsp::s(Fill::x(Fixed::y(3, Tui::bg(Rgb(33,33,33), Tui::bold(true, "tek 0.3.0-rc0")))), - Bsp::n(Fill::x(Fixed::y(3, Tui::bg(Rgb(33,33,33), "+ new session"))), - Fill::xy(Stack::south(|add: &mut dyn FnMut(&dyn Render)|{ - for (index, (id, profile)) in self.configs.profiles.read().unwrap().iter().enumerate() { - add(&Fixed::y(3, Tui::bg(if index == 0 { Rgb(64,64,64) } else { Rgb(32,32,32) }, Bsp::s( - Fill::x(Bsp::a( - Fill::x(Align::w(Tui::fg(Rgb(224,192,128), &profile.name))), - Fill::x(Align::e(Tui::fg(Rgb(224,128,32), id))) - )), - Fill::x(Align::w(&profile.info)) - )))); - } - })))) + Stack::south(|add: &mut dyn FnMut(&dyn Render)|{ + add(&Tui::bold(true, "tek 0.3.0-rc0")); + add(&""); + add(&"+ new session"); + }) } pub fn view_dialog (&self) -> impl Content + use<'_> { self.dialog.as_ref().map(|dialog|Bsp::b("", diff --git a/deps/tengri b/deps/tengri index 104bb1c8..9e0b7be9 160000 --- a/deps/tengri +++ b/deps/tengri @@ -1 +1 @@ -Subproject commit 104bb1c8e76cacf249ccd340712ea7bd2d33b5f6 +Subproject commit 9e0b7be9a9c80b5df52854ab426bc60b794931ed