From 9e25564abb6642566f27e2b33a8f50545ba96fcc Mon Sep 17 00:00:00 2001 From: i do not exist Date: Sun, 23 Aug 2026 06:12:01 +0300 Subject: [PATCH 1/3] fix config loading --- src/config.rs | 167 ++++++++++++++++++------------------ src/device/arrange/track.rs | 45 +++++----- src/tek.rs | 25 ++---- tengri | 2 +- 4 files changed, 116 insertions(+), 123 deletions(-) diff --git a/src/config.rs b/src/config.rs index 5a13144c..208a123b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,55 +2,88 @@ use crate::*; use std::path::PathBuf; use notify_debouncer_full::notify::{RecursiveMode, PollWatcher, Config as NotifyConfig, Watcher}; -/// Write initial contents of configuration. -pub fn config_init (config: &Config) -> UsuallyRef<'static, ()> { - //println!("\r\ninit {}", quanta::Clock::new().raw()); - config.clear(); - let path = Config::CONFIG; - let defaults = Config::DEFAULTS; - config.stamp.store(quanta::Clock::new().raw(), Relaxed); - if config.find_file().is_none() { - //println!("Creating {path:?}"); - std::fs::write(config.place_file()?, defaults)?; +impl AsRef for Config { + fn as_ref (&self) -> &Config { + self } - Ok(if let Some(path) = config.find_file() { - //println!("Loading {path:?}"); - let src = std::fs::read_to_string(&path)?; - src.as_str().each((), &mut move|ctx, item: &str|{ - config_add(config, item); - Ok(ctx) - }); +} + +/// Write initial contents of configuration. +pub fn config_init > (config: C) -> Usually { + if config.as_ref().find_file().is_none() { + std::fs::write(config.as_ref().place_file()?, Config::DEFAULTS)?; + } + Ok(if let Some(path) = config.as_ref().find_file() { + config_load(config, std::fs::read_to_string(&path)?)? } else { - return Err(format!("{path}: not found").into()) + return Err(format!("config_init: not found").into()) }) } -/// Add statements to configuration from [Dsl] source. -pub fn config_add <'a> (config: &'a Config, dsl: &'a str) -> UsuallyRef<'a, &'a Config> { - dsl.each(config, &mut move|ctx: &'a Config, item: &'a str|if let Some(expr) = item.expr()? { - //if let (Some(name), Some(body)) = (expr.tail()?.head()?, expr.tail()?.tail()?,) { - match expr.head()? { - Some("mode") => expr.tail()?.map(|tail|modes_add(&config.modes, tail)), - Some("keys") => expr.tail()?.map(|tail|load_bind(&config.binds, tail)), - Some("view") => expr.tail()?.map(|tail|load_view(&config.views, tail)), - _ => return Err(format!("Config::load: expected view/keys/mode expr, got: {item:?}").into()) - }.transpose()?; - //} - Ok(config) +pub fn config_load , L: Language> (config: C, src: L) -> Usually { + config.as_ref().clear(); + config.as_ref().stamp.store(quanta::Clock::new().raw(), Relaxed); + src.each(config, |c, s|config_load_item(c, s)) +} + +pub fn config_load_item , L: Language> (config: C, src: L) -> Usually { + if let Some(expr) = src.expr()? { + config_load_kind(config, expr) } else { Err(format!("Config::add_one: tried to add empty item").into()) - }) + } +} + +pub fn config_load_kind , L: Language> (config: C, src: L) -> Usually { + //if let Some(tail) = src.tail()? { + match src.head()? { + Some("mode") => modes_add(&config.as_ref().modes, src.tail()), + Some("keys") => load_bind(&config.as_ref().binds, src.tail()), + Some("view") => load_view(&config.as_ref().views, src.tail()), + _ => return Err(format!("Config::load: expected view/keys/mode expr, got: {src:?}").into()) + }?; + //} + Ok(config) +} + +/// Watch a config's file for changes. +pub fn config_watch ( + config: Arc, poll: Option +) -> Usually<()> { + let poll = poll.unwrap_or(Duration::from_millis(250)); + let opts = NotifyConfig::default().with_poll_interval(poll); + let mut watcher = ::notify_debouncer_full::notify::poll::PollWatcher::new({ + let config = config.clone(); + move|result|{ + match result { + Ok(_events) => if let Err(e) = config_init(config.as_ref()) { + *config.as_ref().error.write().unwrap() = Some(format!("{e:?}").into()); + panic!("{e:?}"); + } else { + //println!("config updated"); + }, + Err(errors) => { + panic!("{errors:?}"); + } + } + } + }, opts)?; + if let Some(path) = config.as_ref().get_file() { + //println!("watching: {path:?}"); + watcher.watch(&path, RecursiveMode::NonRecursive)?; + *config.as_ref().watch.write().unwrap() = Some(watcher); + Ok(()) + } else { + Err(format!("no config path").into()) + } } /// Register a mode. -pub fn modes_add <'a> (modes: &Modes, expr: &'a str) -> UsuallyRef<'a, ()> { +pub fn modes_add <'a> (modes: &Modes, expr: impl Language) -> UsuallyRef<'a, ()> { let name = expr.head()?.ok_or("mode: missing name")?; let body = expr.tail()?.ok_or("mode: missing body")?; let mode = Mode::default(); - let mode = body.each(mode, move|mut submode: Mode, item: &str|{ - mode_add(&mut submode, &item); - Ok(submode) - })?; + let mode = body.each(mode, |c,s|mode_add(c,s))?; modes.0.write().unwrap().insert(name.into(), Arc::new(mode)); Ok(()) } @@ -69,8 +102,8 @@ pub fn modes_add <'a> (modes: &Modes, expr: &'a str) -> UsuallyRef<'a, ()> { /// let mut mode: tek::Mode> = Default::default(); /// mode.add("(name hello)").unwrap(); /// ``` -pub fn mode_add <'a> (mode: &'a mut Mode, dsl: &'a str) -> PerhapsRef<'a, &'a mut Mode> { - Ok(Some(if let Ok(Some(expr)) = dsl.expr() && let Ok(Some(head)) = expr.head() { +pub fn mode_add (mut mode: Mode, dsl: impl Language) -> Usually { + Ok(if let Ok(Some(expr)) = dsl.expr() && let Ok(Some(head)) = expr.head() { //println!("Mode::add: {head} {:?}", expr.tail()); let tail = expr.tail()?.map(|x|x.trim()).unwrap_or(""); match head { @@ -78,16 +111,13 @@ pub fn mode_add <'a> (mode: &'a mut Mode, dsl: &'a str) -> PerhapsRef<'a, &'a mu let name = tail.head()?.ok_or("submode: missing name")?; let body = tail.tail()?.ok_or("submode: missing body")?; let submode = Mode::default(); - let submode = body.each(submode, move|mut submode: Mode, item: &str|{ - mode_add(&mut submode, &item); - Ok(submode) - })?; + let submode = body.each(submode, |c,s|mode_add(c,s))?; let modes = mode.modes.clone(); modes.0.write().unwrap().insert(name.into(), Arc::new(submode)); mode }, "keys" => { - dsl.each(mode, &mut |mode: &'a mut Mode, expr: &'a str|{ + dsl.each(mode, |mut mode: Mode, expr: &str|{ mode.keys.push(expr.trim().into()); Ok(mode) })? @@ -102,11 +132,11 @@ pub fn mode_add <'a> (mode: &'a mut Mode, dsl: &'a str) -> PerhapsRef<'a, &'a mu mode } else { return Err(format!("Mode::add: unexpected: {dsl:?}").into()); - })) + }) } /// Load custom view definition. -pub fn load_view <'a> (views: &Views, expr: &'a str,) -> UsuallyRef<'a, ()> { +pub fn load_view <'a> (views: &Views, expr: impl Language) -> UsuallyRef<'a, ()> { let name = expr.head()?.ok_or("view: missing name")?; let body = expr.tail()?.ok_or("view: missing body")?; views.write().unwrap().insert( @@ -116,12 +146,12 @@ pub fn load_view <'a> (views: &Views, expr: &'a str,) -> UsuallyRef<'a, ()> { Ok(()) } -pub fn load_bind <'a> (binds: &Binds, expr: &'a str) -> UsuallyRef<'a, ()> { +pub fn load_bind <'a> (binds: &Binds, expr: impl Language) -> UsuallyRef<'a, ()> { let name = expr.head()?.ok_or("bind: missing name")?; let body = expr.tail()?.ok_or("bind: missing body")?; binds.write().unwrap().insert(name.into(), { let mut map = Bind::new(); - body.each((), &mut |_, item: &str|if item.expr().head() == Ok(Some("see")) { + body.each((), |_, item: &str|if item.expr().head() == Ok(Some("see")) { // TODO Ok(()) } else if let Ok(Some(_word)) = item.expr().head().word() { @@ -241,47 +271,14 @@ impl Config { /// Create, initialize, and watch a new configuration. pub fn watched (callback: impl FnOnce(Arc)->T) -> Usually { let config = Self::init_new(None)?; - Self::watch(config.clone(), None)?; + config_watch(config.clone(), None)?; let result = callback(config); Ok(result) } /// Create and initialize a new configuration. - pub fn init_new (_dirs: Option) -> UsuallyRef<'static, Arc> { - let config = Arc::new(Self::new(None)); - config_init(&config)?; - Ok(config) - } - - /// Watch a config's file for changes. - pub fn watch (config: Arc, poll: Option) -> UsuallyRef<'static, ()> { - let handler = { - let config = config.clone(); - move |result|match result { - Ok(_events) => if let Err(e) = config_init(&config) { - *config.error.write().unwrap() = Some(format!("{e:?}").into()); - panic!("{e:?}"); - } else { - //println!("config updated"); - }, - Err(errors) => { - panic!("{errors:?}"); - } - } - }; - let mut watcher = ::notify_debouncer_full::notify::poll::PollWatcher::new( - handler, - NotifyConfig::default() - .with_poll_interval(poll.unwrap_or(Duration::from_millis(250))) - )?; - if let Some(path) = config.get_file() { - //println!("watching: {path:?}"); - watcher.watch(&path, RecursiveMode::NonRecursive)?; - *config.watch.write().unwrap() = Some(watcher); - Ok(()) - } else { - Err(format!("no config path").into()) - } + pub fn init_new (_dirs: Option) -> Usually> { + config_init(Arc::new(Self::new(None))) } /// Find the config file. @@ -316,6 +313,10 @@ impl Config { *self.binds.write().unwrap() = Default::default(); } + pub fn get_view (&self, name: impl AsRef) -> Option> { + self.views.read().unwrap().get(name.as_ref()).cloned() + } + } pub use self::view::*; diff --git a/src/device/arrange/track.rs b/src/device/arrange/track.rs index 895a9689..c6d9712d 100644 --- a/src/device/arrange/track.rs +++ b/src/device/arrange/track.rs @@ -371,32 +371,33 @@ pub trait TracksView: HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTra button_2("T", "+", false), button_2("S", "+", false), ), - bg(theme.darker.term, iter_east(||self.tracks_with_sizes().map(|(index, track, x1, _x2)|{ - let b = if selected.track() == Some(index) { - track.color.light.term - } else { - track.color.base.term - }; - bg(b, south( - east!( - "·t", - index, - " ", - fg(Rgb(255, 255, 255), bold(true, &track.name)) - ) - .align_nw() - .full_w(), - "" - )) - .exact_w(track_width(index, track)) - .exact_h(2) - }))) + bg(theme.darker.term, iter_east(||self.tracks_with_sizes() + .map(|(index, track, _x1, _x2)|{ + let b = if selected.track() == Some(index) { + track.color.light.term + } else { + track.color.base.term + }; + bg(b, south( + east!( + "·t", + index, + " ", + fg(Rgb(255, 255, 255), bold(true, &track.name)) + ) + .align_nw() + .full_w(), + "" + )) + .exact_w(track_width(index, track)) + .exact_h(2) + }))) ) ) } /// Draw outputs per track - fn view_track_outputs <'a> (&'a self, theme: ItemTheme, _h: u16) -> impl Draw<'_, Tui> { + fn view_track_outputs <'a> (&'a self, theme: ItemTheme, _h: u16) -> impl Draw<'a, Tui> { view_track_row_section(theme, south(button_2("o", "utput", false).align_w().full_w(), draw(|to: &mut Tui|{ @@ -431,7 +432,7 @@ pub trait TracksView: HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTra } /// Draw inputs per track - fn view_track_inputs <'a> (&'a self, theme: ItemTheme) -> impl Draw<'_, Tui> { + fn view_track_inputs <'a> (&'a self, theme: ItemTheme) -> impl Draw<'a, Tui> { let mut height = 0u16; for track in self.tracks().iter() { height = height.max(track.sequencer.midi_ins.len() as u16); diff --git a/src/tek.rs b/src/tek.rs index e401a3c3..38f1cc18 100644 --- a/src/tek.rs +++ b/src/tek.rs @@ -856,7 +856,7 @@ mod device { } } - pub fn view_device (state: &App) -> impl Draw<'_, Tui> { + pub fn view_device <'a> (state: &'a App) -> impl Draw<'a, Tui> { let selected = state.dialog.device_kind().unwrap(); south( bold(true, "Add device"), @@ -917,7 +917,7 @@ mod draw { if let Some(mode) = self.mode.as_ref().and_then(|m|self.config.get_mode(m)) { let mut error = false; for (index, dsl) in mode.view.iter().enumerate() { - match self.draw_mode(to, dsl) { + match self.interpret(to, dsl) { Ok(None) => {}, Ok(Some(XYWH(.., w, h))) => { self.size.0.store(w as usize, Relaxed); @@ -942,11 +942,7 @@ mod draw { Ok(()) } - fn draw_mode <'a, L: Language + 'a> (&'a self, to: &mut Tui, dsl: L) -> Drawn<'a, u16> { - self.interpret(to, dsl) - } - - fn draw_debug (&self, to: &mut Tui) -> Drawn { + #[allow(unused)] fn draw_debug (&self, to: &mut Tui) -> Drawn<'_, u16> { east( format!("{}x{} ", self.size.0.load(Relaxed), self.size.1.load(Relaxed)), format!("{}% {} ", self.perf.percentage().unwrap_or_default(), self.perf.clock.raw() / 1000000000), @@ -957,7 +953,7 @@ mod draw { impl<'a> Interpret<'a, Tui, Option>> for App { fn interpret (&'a self, to: &mut Tui, dsl: L) -> Drawn<'a, u16> { if let Some(expr) = dsl.expr()? { - interpret_keyword!(self, to, expr, [ + interpret_keyword!(self, to, &expr, [ 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 ]); @@ -993,15 +989,10 @@ mod draw { Some(":editor") => "TODO Editor".draw(to), Some(":transport") => view_transport(true, "", "", "").draw(to), Some(":debug") => format!("[{:?}]", to.area()).exact_h(1).draw(to), - Some(_) => { - let views = self.config.views.read().unwrap(); - if let Some(lang) = views.get(word.src()?.unwrap()) { - let lang = lang.clone(); - std::mem::drop(views); - self.draw_mode(to, lang) - } else { - fg(Color::Rgb(128, 32, 32), format!("undefined: {word:?}")).draw(to) - } + Some(_) => if let Some(lang) = self.config.get_view(word) { + self.interpret(to, lang) + } else { + fg(Color::Rgb(128, 32, 32), format!("undefined: {word:?}")).draw(to) }, _ => unreachable!() } diff --git a/tengri b/tengri index e69d4287..41ef6214 160000 --- a/tengri +++ b/tengri @@ -1 +1 @@ -Subproject commit e69d4287e0ee1f751d2f096f7f85b43628cef259 +Subproject commit 41ef62146bd74e3e69fa48eae5b8f1581acee149 From f2443a4314f02109119d1d591f6fa9a424b4dbb9 Mon Sep 17 00:00:00 2001 From: i do not exist Date: Sun, 23 Aug 2026 10:04:29 +0300 Subject: [PATCH 2/3] update readme --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index da256163..eedbc6f6 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,9 @@ it plays well with your midi controller, wav samples, and lv2 plugins. [statically linked binaries](https://codeberg.org/unspeaker/tek/releases), and on the [aur](https://codeberg.org/unspeaker/tek#arch-linux). -author is reachable via [**mastodon** `@unspeaker@mastodon.social`](https://mastodon.social/@unspeaker) -or [**matrix** `@unspeaker:matrix.org`](https://matrix.to/#/@unspeaker:matrix.org) +author was reachable via [**mastodon** on `@unspeaker@mastodon.social`](https://mastodon.social/@unspeaker) +or [**matrix** on `@unspeaker:matrix.org`](https://matrix.to/#/@unspeaker:matrix.org) but then +had a crashout and deleted those. | | | |-|-| From 6771b24f795fe89b91d5011d36b37d86345bbc36 Mon Sep 17 00:00:00 2001 From: i do not exist Date: Sun, 23 Aug 2026 11:29:48 +0300 Subject: [PATCH 3/3] draws again! --- src/config.rs | 2 +- src/device/arrange/clip.rs | 4 +- src/device/arrange/port.rs | 18 ++++---- src/device/arrange/scene.rs | 4 +- src/device/arrange/track.rs | 24 +++++----- src/device/browse.rs | 6 +-- src/device/clock.rs | 6 +-- src/device/dialog.rs | 2 +- src/device/editor.rs | 10 ++--- src/device/editor/octave.rs | 2 +- src/device/editor/piano.rs | 14 +++--- src/device/meter.rs | 2 +- src/device/sampler.rs | 8 ++-- src/device/sequence.rs | 4 +- src/tek.edn | 5 +-- src/tek.rs | 88 +++++++++++++++++++++---------------- tengri | 2 +- 17 files changed, 105 insertions(+), 96 deletions(-) diff --git a/src/config.rs b/src/config.rs index 208a123b..bd8d7dbb 100644 --- a/src/config.rs +++ b/src/config.rs @@ -148,7 +148,7 @@ pub fn load_view <'a> (views: &Views, expr: impl Language) -> UsuallyRef<'a, ()> pub fn load_bind <'a> (binds: &Binds, expr: impl Language) -> UsuallyRef<'a, ()> { let name = expr.head()?.ok_or("bind: missing name")?; - let body = expr.tail()?.ok_or("bind: missing body")?; + let body = expr.tail()?.unwrap_or(""); binds.write().unwrap().insert(name.into(), { let mut map = Bind::new(); body.each((), |_, item: &str|if item.expr().head() == Ok(Some("see")) { diff --git a/src/device/arrange/clip.rs b/src/device/arrange/clip.rs index f3ccdf86..aaed1248 100644 --- a/src/device/arrange/clip.rs +++ b/src/device/arrange/clip.rs @@ -70,7 +70,7 @@ impl ClipsView for T {} pub trait ClipsView: TracksView + ScenesView { /// Draw clips per scene - fn view_scenes_clips (&self) -> impl Draw<'_, Tui> { + fn view_scenes_clips (&self) -> impl Draw { let select = self.selection(); let editor = self.editor(); let size = self.clips_size(); @@ -105,7 +105,7 @@ pub trait ClipsView: TracksView + ScenesView { fg_bg(o, b, "".full_wh()), fg_bg(f, b, bold(true, name)).align_nw().full_wh(), ), - when(is_selected, editor.map(|e|e.view())).full_wh() + when(is_selected, editor).full_wh() ).full_wh() ).exact_wh(w, y) })).full_h().exact_w(track.width as u16) diff --git a/src/device/arrange/port.rs b/src/device/arrange/port.rs index 48b6a0c1..38ae3997 100644 --- a/src/device/arrange/port.rs +++ b/src/device/arrange/port.rs @@ -5,24 +5,24 @@ impl_has!(Vec: |self: Arrangement| self.midi_outs); impl_has!(Vec: |self: App|self.project.midi_ins); impl_has!(Vec: |self: App|self.project.midi_outs); -pub fn view_midi_ins_status (theme: ItemTheme, track: Option<&Track>) -> impl Draw<'_, Tui> { +pub fn view_midi_ins_status (theme: ItemTheme, track: Option<&Track>) -> impl Draw { track.map(move|track|view_ports_status(theme, "MIDI ins: ", &track.sequencer.midi_ins)) } -pub fn view_midi_outs_status (theme: ItemTheme, track: Option<&Track>) -> impl Draw<'_, Tui> { +pub fn view_midi_outs_status (theme: ItemTheme, track: Option<&Track>) -> impl Draw { track.map(move|track|view_ports_status(theme, "MIDI outs: ", &track.sequencer.midi_outs)) } -pub fn view_audio_ins_status (theme: ItemTheme, track: Option<&Track>) -> impl Draw<'_, Tui> { +pub fn view_audio_ins_status (theme: ItemTheme, track: Option<&Track>) -> impl Draw { track.map(move|track|view_ports_status(theme, "Audio ins: ", &track.audio_ins())) } -pub fn view_audio_outs_status (theme: ItemTheme, track: Option<&Track>) -> impl Draw<'_, Tui> { +pub fn view_audio_outs_status (theme: ItemTheme, track: Option<&Track>) -> impl Draw { track.map(move|track|view_ports_status(theme, "Audio outs:", &track.audio_outs())) } pub fn view_ports_status <'a, T: JackPort> (theme: ItemTheme, title: &'a str, ports: &'a [T]) - -> impl Draw<'a, Tui> + use<'a, T> + -> impl Draw + use<'a, T> { let ins = ports.len() as u16; let frame = Outer(true, Style::default().fg(g(96))); @@ -35,7 +35,7 @@ pub fn view_ports_status <'a, T: JackPort> (theme: ItemTheme, title: &'a str, po pub fn view_io_ports <'a, T: PortsSizes<'a>> ( fg: Color, bg: Color, items: impl Fn()->T + Send + Sync + 'a -) -> impl Draw<'a, Tui> + 'a { +) -> impl Draw + 'a { type Item<'a> = (usize, &'a Arc, &'a [Connect], usize, usize); iter(items, move|(_index, name, connections, y, y2): Item<'a>, _| south( @@ -48,9 +48,9 @@ pub fn view_io_ports <'a, T: PortsSizes<'a>> ( pub struct Junction(T); -impl View for Junction { - fn view (&self) -> impl Draw<'_, Tui> { - T::KIND +impl Draw for Junction { + fn draw (&self, to: &mut Tui) -> Drawn { + T::KIND.draw(to) } } diff --git a/src/device/arrange/scene.rs b/src/device/arrange/scene.rs index 99295d09..6de330c9 100644 --- a/src/device/arrange/scene.rs +++ b/src/device/arrange/scene.rs @@ -203,7 +203,7 @@ pub trait ScenesView: HasEditor + HasSelection + HasSceneScroll + HasClipsSize + fn w_side (&self) -> u16; fn w_mid (&self) -> u16; - fn view_scenes_names (&self) -> impl Draw<'_, Tui> { + fn view_scenes_names (&self) -> impl Draw { let select = self.selection(); let editor = self.editor(); let editing = self.is_editing(); @@ -281,7 +281,7 @@ pub fn view_scene_name <'a> ( index: usize, scene: &Scene, editing: bool -) -> impl Draw<'a, Tui> { +) -> impl Draw { let h = if select.scene() == Some(index) && let Some(_editor) = editor { 7 } else { diff --git a/src/device/arrange/track.rs b/src/device/arrange/track.rs index c6d9712d..fc694fce 100644 --- a/src/device/arrange/track.rs +++ b/src/device/arrange/track.rs @@ -46,10 +46,10 @@ impl Track { pub fn audio_outs (&self) -> &[AudioOutput] { self.devices.last().map(|x|x.audio_outs()).unwrap_or_default() } - pub fn per <'a, T: Draw<'a, Tui> + 'a, U: TracksSizes<'a>> ( + pub fn per <'a, T: Draw + 'a, U: TracksSizes<'a>> ( tracks: impl Fn() -> U + Send + Sync + 'a, callback: &'a (impl Fn(usize, &'a Track)->T + Send + Sync + 'a) - ) -> impl Draw<'a, Tui> { + ) -> impl Draw { iter_east(move||tracks().map(|(index, track, x1, x2): (usize, &Track, usize, usize)|{ fg_bg( track.color.lightest.term, @@ -343,7 +343,7 @@ pub trait TracksView: HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTra } /// Draw name of each track - fn view_track_names (&self, theme: ItemTheme) -> impl Draw<'_, Tui> { + fn view_track_names (&self, theme: ItemTheme) -> impl Draw { let selected = self.selection(); east( south( @@ -397,7 +397,7 @@ pub trait TracksView: HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTra } /// Draw outputs per track - fn view_track_outputs <'a> (&'a self, theme: ItemTheme, _h: u16) -> impl Draw<'a, Tui> { + fn view_track_outputs <'a> (&'a self, theme: ItemTheme, _h: u16) -> impl Draw { view_track_row_section(theme, south(button_2("o", "utput", false).align_w().full_w(), draw(|to: &mut Tui|{ @@ -432,7 +432,7 @@ pub trait TracksView: HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTra } /// Draw inputs per track - fn view_track_inputs <'a> (&'a self, theme: ItemTheme) -> impl Draw<'a, Tui> { + fn view_track_inputs <'a> (&'a self, theme: ItemTheme) -> impl Draw { let mut height = 0u16; for track in self.tracks().iter() { height = height.max(track.sequencer.midi_ins.len() as u16); @@ -460,7 +460,7 @@ pub trait TracksView: HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTra }).align_w())) } - fn view_track_devices (&self, theme: ItemTheme) -> impl Draw<'_, Tui> { + fn view_track_devices (&self, theme: ItemTheme) -> impl Draw { let height = self.tracks_devices_height(); let btn1 = button_3("d", "evice", self.track().map(|t|t.devices.len()).unwrap_or(0), false); let btn2 = button_2("D", "+", false); @@ -489,7 +489,7 @@ pub trait TracksView: HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTra h as u16 } - fn view_inputs (&self, _theme: ItemTheme) -> impl Draw<'_, Tui> + '_ { + fn view_inputs (&self, _theme: ItemTheme) -> impl Draw + '_ { let title_1 = button_3("i", "nput ", self.midi_ins().len(), false).align_w().exact_wh(20, 1); let title_2 = button_2("I", "+", false).exact_wh(4, 1); east(title_1, west(title_2, draw(move|to: &mut Tui|{ @@ -533,7 +533,7 @@ pub trait TracksView: HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTra h as u16 } - fn view_outputs (&self, theme: ItemTheme) -> impl Draw<'_, Tui> { + fn view_outputs (&self, theme: ItemTheme) -> impl Draw { let height = self.outputs_height(); let list = south( button_3( @@ -633,10 +633,10 @@ impl HasTrackScroll for App { fn view_track_row_section <'a> ( _theme: ItemTheme, - button: impl Draw<'a, Tui>, - button_add: impl Draw<'a, Tui>, - content: impl Draw<'a, Tui>, -) -> impl Draw<'a, Tui> { + button: impl Draw, + button_add: impl Draw, + content: impl Draw, +) -> impl Draw { west( button_add.align_nw().exact_w(4).full_h(), east( diff --git a/src/device/browse.rs b/src/device/browse.rs index 74269e57..be3bb3ec 100644 --- a/src/device/browse.rs +++ b/src/device/browse.rs @@ -118,7 +118,7 @@ impl Browse { unreachable!() }) } - //fn tui (&self) -> impl Draw<'_, Tui> { + //fn tui (&self) -> impl Draw { //iter_south_fixed(1, ||self.tui_entries(), |entry, _index|entry.origin_w().full_w()) //} //fn tui_entries (&self) -> EntriesIterator<'_, Tui> { @@ -133,7 +133,7 @@ impl Browse { } //impl<'a> Iterator for EntriesIterator<'a, Tui> { - //type Item = impl Draw<'_, Tui>; + //type Item = impl Draw; //fn next (&mut self) -> Option { //let dirs = self.browser.dirs.len(); //let files = self.browser.files.len(); @@ -179,7 +179,7 @@ pub fn scan (dir: &PathBuf) -> Usually<(Vec, Vec)> { Ok((subdirs, files)) } -pub fn view_browse_title (state: &App) -> impl Draw<'_, Tui> { +pub fn view_browse_title (state: &App) -> impl Draw { field_v(ItemTheme::default(), match state.dialog.browser_target().unwrap() { BrowseTarget::SaveProject => "Save project:", BrowseTarget::LoadProject => "Load project:", diff --git a/src/device/clock.rs b/src/device/clock.rs index 808ce214..b058e24a 100644 --- a/src/device/clock.rs +++ b/src/device/clock.rs @@ -462,7 +462,7 @@ impl_time_unit!(LaunchSync); /// 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 <'a> (play: bool, bpm: &str, beat: &str, time: &str) -> impl Draw<'a, Tui> { +pub fn view_transport <'a> (play: bool, bpm: &str, beat: &str, time: &str) -> impl Draw { let theme = ItemTheme::G[96]; bg(Black, east!(above( button_play_pause(play, false).align_w(), @@ -479,7 +479,7 @@ pub fn view_transport <'a> (play: bool, bpm: &str, beat: &str, time: &str) -> im /// 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 <'a> (sel: Option<&str>, sr: &str, buf: &str, lat: &str) -> impl Draw<'a, Tui> { +pub fn view_status <'a> (sel: Option<&str>, sr: &str, buf: &str, lat: &str) -> impl Draw { let theme = ItemTheme::G[96]; let sr = field_h(theme, "SR", sr); let buf = field_h(theme, "Buf", buf); @@ -496,7 +496,7 @@ pub fn view_status <'a> (sel: Option<&str>, sr: &str, buf: &str, lat: &str) -> i /// let _ = tek::button_play_pause(false, true); /// let _ = tek::button_play_pause(false, false); /// ``` -pub fn button_play_pause <'a> (playing: bool, compact: bool) -> impl Draw<'a, Tui> { +pub fn button_play_pause <'a> (playing: bool, compact: bool) -> impl Draw { bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) }, either(compact, draw(move|to: &mut Tui|either(playing, diff --git a/src/device/dialog.rs b/src/device/dialog.rs index cc8913fd..03941dcb 100644 --- a/src/device/dialog.rs +++ b/src/device/dialog.rs @@ -1,7 +1,7 @@ use crate::{*, device::*}; pub fn draw_dialog <'a> (to: &mut Tui, mut frags: std::str::Split<&str>, state: &App) - -> Drawn<'a, u16> + -> Drawn { match frags.next() { Some("menu") => if let Dialog::Menu(selected, items) = &state.dialog { diff --git a/src/device/editor.rs b/src/device/editor.rs index 3efdf235..b06107d4 100644 --- a/src/device/editor.rs +++ b/src/device/editor.rs @@ -268,7 +268,7 @@ impl MidiEditor { self.get_time_pos().overflowing_sub(1) .0.min(self.clip_length().saturating_sub(1)) } - pub fn clip_status (&self) -> impl Draw<'_, Tui> + '_ { + pub fn clip_status (&self) -> impl Draw + '_ { let (_color, name, length, looped) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) { (clip.color, clip.name.clone(), clip.length, clip.looped) } else { (ItemTheme::G[64], String::new().into(), 0, false) }; @@ -281,7 +281,7 @@ impl MidiEditor { .origin_w().full_w(), ).exact_w(20) } - pub fn edit_status (&self) -> impl Draw<'_, Tui> + '_ { + pub fn edit_status (&self) -> impl Draw + '_ { let (_color, length) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) { (clip.color, clip.length) } else { (ItemTheme::G[64], 0) }; @@ -425,11 +425,11 @@ impl MidiViewer for MidiEditor { fn set_clip (&mut self, p: Option<&Arc>>) { self.mode.set_clip(p) } } -impl View for MidiEditor { - fn view (&self) -> impl Draw<'_, Tui> { +impl Draw for MidiEditor { + fn draw (&self, to: &mut Tui) -> Drawn { self.autoscroll(); /*self.autozoom();*/ - self.size.of(self.mode.view()) + self.size.of(&self.mode).draw(to) } } diff --git a/src/device/editor/octave.rs b/src/device/editor/octave.rs index 46642120..21124e98 100644 --- a/src/device/editor/octave.rs +++ b/src/device/editor/octave.rs @@ -19,7 +19,7 @@ impl OctaveVertical { } impl OctaveVertical { - pub fn tui (&self) -> impl Draw<'_, Tui> { + pub fn tui (&self) -> impl Draw { east!( fg_bg(self.color(0), self.color(1), "▙"), fg_bg(self.color(2), self.color(3), "▙"), diff --git a/src/device/editor/piano.rs b/src/device/editor/piano.rs index b26a8f28..7a6ee3aa 100644 --- a/src/device/editor/piano.rs +++ b/src/device/editor/piano.rs @@ -24,8 +24,8 @@ pub struct PianoHorizontal { impl_has!(Sizer: |self: PianoHorizontal| self.size); -impl View for PianoHorizontal { - fn view (&self) -> impl Draw<'_, Tui> { +impl Draw for PianoHorizontal { + fn draw (&self, to: &mut Tui) -> Drawn { south( east( format!("{}x{}", self.size.w(), self.size.h()).exact_w(5), @@ -35,7 +35,7 @@ impl View for PianoHorizontal { self.keys(), self.size.of(below(self.notes().full_wh(), self.cursor().full_wh())) ), - ) + ).draw(to) } } @@ -132,7 +132,7 @@ impl PianoHorizontal { } } - fn notes (&self) -> impl Draw<'_, Tui> { + fn notes (&self) -> impl Draw { let time_start = self.get_time_start(); let note_lo = self.get_note_lo(); let note_hi = self.get_note_hi(); @@ -168,7 +168,7 @@ impl PianoHorizontal { }) } - fn cursor (&self) -> impl Draw<'_, Tui> { + fn cursor (&self) -> impl Draw { let note_hi = self.get_note_hi(); let note_lo = self.get_note_lo(); let note_pos = self.get_note_pos(); @@ -202,7 +202,7 @@ impl PianoHorizontal { }) } - fn keys (&self) -> impl Draw<'_, Tui> { + fn keys (&self) -> impl Draw { let state = self; let color = state.color; let note_lo = state.get_note_lo(); @@ -229,7 +229,7 @@ impl PianoHorizontal { }).exact_w(self.keys_width).full_h() } - fn timeline (&self) -> impl Draw<'_, Tui> + '_ { + fn timeline (&self) -> impl Draw + '_ { draw(move|to: &mut Tui|{ let xywh = to.area().into(); let XYWH(x, y, w, _h) = xywh; diff --git a/src/device/meter.rs b/src/device/meter.rs index 23e72649..52c17949 100644 --- a/src/device/meter.rs +++ b/src/device/meter.rs @@ -40,7 +40,7 @@ impl_draw!(|self: Log10Meter, to: Tui| { Ok(Some(to.area().into())) }); -fn draw_meters (meters: &[f32]) -> impl Draw<'_, Tui> + use<'_> { +fn draw_meters (meters: &[f32]) -> impl Draw + use<'_> { bg(Black, iter_east_fixed(1, ||meters.iter(), |value, _index|{ RmsMeter(*value).full_h() }).exact_w(2)) diff --git a/src/device/sampler.rs b/src/device/sampler.rs index 426e8a85..f9203341 100644 --- a/src/device/sampler.rs +++ b/src/device/sampler.rs @@ -330,7 +330,7 @@ fn draw_list_item (sample: &Option>>) -> String { } } -fn draw_viewer (sample: Option<&Arc>>) -> impl Draw<'_, Tui> + use<'_> { +fn draw_viewer (sample: Option<&Arc>>) -> impl Draw + use<'_> { let min_db = -64.0; draw(move|to: &mut Tui|{ let xywh = to.area().into(); @@ -750,7 +750,7 @@ fn read_sample_data (_: &str) -> Usually<(usize, Vec>)> { todo!(); } -pub fn view_sample_info (sample: Option<&Arc>>) -> impl Draw<'_, Tui> + use<'_> { +pub fn view_sample_info (sample: Option<&Arc>>) -> impl Draw + use<'_> { when(sample.is_some(), draw(move|to: &mut Tui|{ let sample = sample.unwrap().read().unwrap(); let theme = sample.color; @@ -765,7 +765,7 @@ pub fn view_sample_info (sample: Option<&Arc>>) -> impl Draw<'_, })) } -pub fn view_sample_info_v (sample: Option<&Arc>>) -> impl Draw<'_, Tui> + use<'_> { +pub fn view_sample_info_v (sample: Option<&Arc>>) -> impl Draw + use<'_> { let a = draw(move|to: &mut Tui|{ let sample = sample.unwrap().read().unwrap(); let theme = sample.color; @@ -788,7 +788,7 @@ pub fn view_sample_info_v (sample: Option<&Arc>>) -> impl Draw<'_ either(sample.is_some(), a, b) } -pub fn view_sample_status (sample: Option<&Arc>>) -> impl Draw<'_, Tui> { +pub fn view_sample_status (sample: Option<&Arc>>) -> impl Draw { bold(true, fg(g(224), sample .map(|sample|{ let sample = sample.read().unwrap(); diff --git a/src/device/sequence.rs b/src/device/sequence.rs index 302c8392..1ccc1d9d 100644 --- a/src/device/sequence.rs +++ b/src/device/sequence.rs @@ -113,7 +113,7 @@ pub trait HasPlayClip: HasClock { *self.reset_mut() = true; } - fn play_status (&self) -> impl Draw<'_, Tui> { + fn play_status (&self) -> impl Draw { let (name, color): (Arc, ItemTheme) = if let Some((_, Some(clip))) = self.play_clip() { let MidiClip { ref name, color, .. } = *clip.read().unwrap(); (name.clone(), color) @@ -130,7 +130,7 @@ pub trait HasPlayClip: HasClock { .into() } - fn next_status (&self) -> impl Draw<'_, Tui> { + fn next_status (&self) -> impl Draw { let mut time: Arc = String::from("--.-.--").into(); let mut name: Arc = String::from("").into(); let mut color = ItemTheme::G[64]; diff --git a/src/tek.edn b/src/tek.edn index 5330b32c..2698ee44 100644 --- a/src/tek.edn +++ b/src/tek.edn @@ -1,7 +1,4 @@ -(view :logo (bsp/s (bg (rgb 100 70 40) (text ~~ ╓─╥─╖ ╓──╖ ╥ ╖ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ )) - (bsp/s (bg (rgb 90 70 50) (text ~~~~ ║ ~ ╟─╌ ~╟─< ~~ heatwave is the new darkwave ~~ )) - (bsp/s (bg (rgb 80 70 60) (text ~~~~ ╨ ~ ╙──╜ ╨ ╜ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ )) - (align/x (text .)))))) +(view :logo (text tek)) (view :browse (bsp/s (padding 3 1 :browse-title) diff --git a/src/tek.rs b/src/tek.rs index 38f1cc18..185eed13 100644 --- a/src/tek.rs +++ b/src/tek.rs @@ -856,7 +856,7 @@ mod device { } } - pub fn view_device <'a> (state: &'a App) -> impl Draw<'a, Tui> { + pub fn view_device <'a> (state: &'a App) -> impl Draw { let selected = state.dialog.device_kind().unwrap(); south( bold(true, "Add device"), @@ -892,15 +892,13 @@ mod draw { /// /// If there is an error, the error is displayed. FIXME: overlay it /// Then, every top-level form of the DSL description is rendered. - impl View for App { - fn view (&self) -> impl Draw<'_, Tui> { + impl Draw for App { + fn draw (&self, to: &mut Tui) -> Drawn { //self.perf.cycle(&mut |_|{ - draw(|to: &mut Tui|{ - self.draw_error(to)?; - self.draw_modes(to)?; - //self.draw_debug(to)?; - Ok(Some(to.area().into())) - }) + self.draw_error(to)?; + self.draw_modes(to)?; + //self.draw_debug(to)?; + Ok(Some(to.area().into())) //}) } } @@ -913,7 +911,7 @@ mod draw { Ok(()) } - fn draw_modes <'a> (&'a self, to: &mut Tui) -> UsuallyRef<'a, ()> { + fn draw_modes (&self, to: &mut Tui) -> Usually<()> { if let Some(mode) = self.mode.as_ref().and_then(|m|self.config.get_mode(m)) { let mut error = false; for (index, dsl) in mode.view.iter().enumerate() { @@ -942,7 +940,7 @@ mod draw { Ok(()) } - #[allow(unused)] fn draw_debug (&self, to: &mut Tui) -> Drawn<'_, u16> { + #[allow(unused)] fn draw_debug (&self, to: &mut Tui) -> Drawn { east( format!("{}x{} ", self.size.0.load(Relaxed), self.size.1.load(Relaxed)), format!("{}% {} ", self.perf.percentage().unwrap_or_default(), self.perf.clock.raw() / 1000000000), @@ -950,15 +948,29 @@ mod draw { } } - impl<'a> Interpret<'a, Tui, Option>> for App { - fn interpret (&'a self, to: &mut Tui, dsl: L) -> Drawn<'a, u16> { - if let Some(expr) = dsl.expr()? { - interpret_keyword!(self, to, &expr, [ - 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 - ]); - Err(format!("interpret_expr: unexpected: {expr:?}").into()) - } else if let Some(word) = dsl.word()? { + impl Interpret>> for App { + fn interpret (&self, to: &mut Tui, dsl: L) -> Drawn { + if let Ok(Some(expr)) = dsl.expr() { + ok_flat(expr.head()?.map(|head|{ + match head.split('/').next() { + Some("when") => kw_when(self, to, expr), + Some("either") => kw_either(self, to, expr), + Some("bsp") => kw_split(self, to, expr), + Some("split") => kw_split(self, to, expr), + Some("align") => kw_align(self, to, expr), + Some("full") => kw_full(self, to, expr), + Some("exact") => kw_exact(self, to, expr), + Some("min") => kw_min(self, to, expr), + Some("max") => kw_max(self, to, expr), + Some("push") => kw_push(self, to, expr), + Some("pull") => kw_pull(self, to, expr), + Some("text") => kw_tui_text(self, to, expr), + Some("fg") => kw_tui_fg(self, to, expr), + Some("bg") => kw_tui_bg(self, to, expr), + _ => Err(format!("interpret_expr: unexpected: {expr:?}").into()) + } + })) + } else if let Ok(Some(word)) = dsl.word() { let mut frags = word.src()?.unwrap().split("/"); match frags.next() { //Some(":logo") => view_logo().draw(to), @@ -1013,7 +1025,7 @@ mod draw { } pub fn view_templates <'a> (_frags: std::str::Split<&str>, state: &'a App) - -> impl Draw<'a, Tui> + use<'a> + -> impl Draw + use<'a> { let height = (state.config.modes.len() * 2) as u16; draw(move |to: &mut Tui|{ @@ -1036,16 +1048,16 @@ mod draw { } pub fn field_h <'a, T: Screen> ( - _theme: ItemTheme, _head: impl Draw<'a, T>, _body: impl Draw<'a, T> - ) -> impl Draw<'a, T> { + _theme: ItemTheme, _head: impl Draw, _body: impl Draw + ) -> impl Draw { } pub fn field_v <'a, T: Screen> ( - _theme: ItemTheme, _head: impl Draw<'a, T>, _body: impl Draw<'a, T> - ) -> impl Draw<'a, T> { + _theme: ItemTheme, _head: impl Draw, _body: impl Draw + ) -> impl Draw { } - pub fn view_sessions <'a> () -> impl Draw<'a, Tui> { + pub fn view_sessions <'a> () -> impl Draw { let h = 6; let w = Some(30); let f = Rgb(224, 192, 128); @@ -1065,8 +1077,8 @@ mod draw { /// let fg = tengri::ratatui::style::Color::Green; /// let _ = tek::view_wrap(bg, fg, "and then blue, too!"); /// ``` - pub fn view_wrap <'a> (bg: Color, fg: Color, content: impl Draw<'a, Tui>) - -> impl Draw<'a, Tui> + pub fn view_wrap <'a> (bg: Color, fg: Color, content: impl Draw) + -> impl Draw { let left = fg_bg(bg, Reset, y_repeat("▐").exact_w(1)); let right = fg_bg(bg, Reset, y_repeat("▌").exact_w(1)); @@ -1078,7 +1090,7 @@ mod draw { /// let _ = tek::view_meters(&[0.0, 0.0]); /// ``` pub fn view_meter <'a> (label: &'a str, value: f32) - -> impl Draw<'a, Tui> + -> impl Draw { let f = field_h(ItemTheme::G[128], label, format!("{:>+9.3}", value)); let w = if value >= 0.0 { 13 } @@ -1101,14 +1113,14 @@ mod draw { south!(f, bg(c, ()).exact_wh(w, 1)) } - pub fn view_meters (values: &[f32;2]) -> impl Draw<'_, Tui> + use<'_> { + pub fn view_meters (values: &[f32;2]) -> impl Draw + use<'_> { let left = format!("L/{:>+9.3}", values[0]); let right = format!("R/{:>+9.3}", values[1]); south(left, right) } - pub fn view_track_header <'a> (theme: ItemTheme, content: impl Draw<'a, Tui>) - -> impl Draw<'a, Tui> + pub fn view_track_header <'a> (theme: ItemTheme, content: impl Draw) + -> impl Draw { bg(theme.darker.term, content.align_e().full_w()).exact_w(12) } @@ -1117,8 +1129,8 @@ mod draw { /// let _ = tek::button_2("", "", true); /// let _ = tek::button_2("", "", false); /// ``` - pub fn button_2 <'a> (key: impl Draw<'a, Tui>, label: impl Draw<'a, Tui>, hide: bool) - -> impl Draw<'a, Tui> + pub fn button_2 <'a> (key: impl Draw, label: impl Draw, hide: bool) + -> impl Draw { let c1 = tui_orange(); let c2 = tui_g(0); @@ -1133,11 +1145,11 @@ mod draw { /// let _ = tek::button_3("", "", "", false); /// ``` pub fn button_3 <'a> ( - key: impl Draw<'a, Tui>, - label: impl Draw<'a, Tui>, - value: impl Draw<'a, Tui>, + key: impl Draw, + label: impl Draw, + value: impl Draw, editing: bool, - ) -> impl Draw<'a, Tui> { + ) -> impl Draw { let c1 = tui_orange(); let c2 = tui_g(0); let c3 = tui_g(96); diff --git a/tengri b/tengri index 41ef6214..4172fa25 160000 --- a/tengri +++ b/tengri @@ -1 +1 @@ -Subproject commit 41ef62146bd74e3e69fa48eae5b8f1581acee149 +Subproject commit 4172fa257776f5c6c7b406429b2244d630702458