Compare commits

...

3 commits

Author SHA1 Message Date
i do not exist
6771b24f79 draws again!
Some checks failed
/ build (push) Has been cancelled
2026-08-23 11:29:48 +03:00
i do not exist
f2443a4314 update readme 2026-08-23 10:04:29 +03:00
i do not exist
9e25564abb fix config loading 2026-08-23 06:18:04 +03:00
18 changed files with 218 additions and 215 deletions

View file

@ -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 [statically linked binaries](https://codeberg.org/unspeaker/tek/releases), and on the
[aur](https://codeberg.org/unspeaker/tek#arch-linux). [aur](https://codeberg.org/unspeaker/tek#arch-linux).
author is reachable via [**mastodon** `@unspeaker@mastodon.social`](https://mastodon.social/@unspeaker) author was reachable via [**mastodon** on `@unspeaker@mastodon.social`](https://mastodon.social/@unspeaker)
or [**matrix** `@unspeaker:matrix.org`](https://matrix.to/#/@unspeaker:matrix.org) or [**matrix** on `@unspeaker:matrix.org`](https://matrix.to/#/@unspeaker:matrix.org) but then
had a crashout and deleted those.
| | | | | |
|-|-| |-|-|

View file

@ -2,55 +2,88 @@ use crate::*;
use std::path::PathBuf; use std::path::PathBuf;
use notify_debouncer_full::notify::{RecursiveMode, PollWatcher, Config as NotifyConfig, Watcher}; use notify_debouncer_full::notify::{RecursiveMode, PollWatcher, Config as NotifyConfig, Watcher};
/// Write initial contents of configuration. impl AsRef<Config> for Config {
pub fn config_init (config: &Config) -> UsuallyRef<'static, ()> { fn as_ref (&self) -> &Config {
//println!("\r\ninit {}", quanta::Clock::new().raw()); self
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)?;
} }
Ok(if let Some(path) = config.find_file() { }
//println!("Loading {path:?}");
let src = std::fs::read_to_string(&path)?; /// Write initial contents of configuration.
src.as_str().each((), &mut move|ctx, item: &str|{ pub fn config_init <C: AsRef<Config>> (config: C) -> Usually<C> {
config_add(config, item); if config.as_ref().find_file().is_none() {
Ok(ctx) 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 { } 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_load <C: AsRef<Config>, L: Language> (config: C, src: L) -> Usually<C> {
pub fn config_add <'a> (config: &'a Config, dsl: &'a str) -> UsuallyRef<'a, &'a Config> { config.as_ref().clear();
dsl.each(config, &mut move|ctx: &'a Config, item: &'a str|if let Some(expr) = item.expr()? { config.as_ref().stamp.store(quanta::Clock::new().raw(), Relaxed);
//if let (Some(name), Some(body)) = (expr.tail()?.head()?, expr.tail()?.tail()?,) { src.each(config, |c, s|config_load_item(c, s))
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)), pub fn config_load_item <C: AsRef<Config>, L: Language> (config: C, src: L) -> Usually<C> {
Some("view") => expr.tail()?.map(|tail|load_view(&config.views, tail)), if let Some(expr) = src.expr()? {
_ => return Err(format!("Config::load: expected view/keys/mode expr, got: {item:?}").into()) config_load_kind(config, expr)
}.transpose()?;
//}
Ok(config)
} else { } else {
Err(format!("Config::add_one: tried to add empty item").into()) Err(format!("Config::add_one: tried to add empty item").into())
}) }
}
pub fn config_load_kind <C: AsRef<Config>, L: Language> (config: C, src: L) -> Usually<C> {
//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<Config>, poll: Option<Duration>
) -> 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. /// 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 name = expr.head()?.ok_or("mode: missing name")?;
let body = expr.tail()?.ok_or("mode: missing body")?; let body = expr.tail()?.ok_or("mode: missing body")?;
let mode = Mode::default(); let mode = Mode::default();
let mode = body.each(mode, move|mut submode: Mode, item: &str|{ let mode = body.each(mode, |c,s|mode_add(c,s))?;
mode_add(&mut submode, &item);
Ok(submode)
})?;
modes.0.write().unwrap().insert(name.into(), Arc::new(mode)); modes.0.write().unwrap().insert(name.into(), Arc::new(mode));
Ok(()) Ok(())
} }
@ -69,8 +102,8 @@ pub fn modes_add <'a> (modes: &Modes, expr: &'a str) -> UsuallyRef<'a, ()> {
/// let mut mode: tek::Mode<std::sync::Arc<str>> = Default::default(); /// let mut mode: tek::Mode<std::sync::Arc<str>> = Default::default();
/// mode.add("(name hello)").unwrap(); /// mode.add("(name hello)").unwrap();
/// ``` /// ```
pub fn mode_add <'a> (mode: &'a mut Mode, dsl: &'a str) -> PerhapsRef<'a, &'a mut Mode> { pub fn mode_add (mut mode: Mode, dsl: impl Language) -> Usually<Mode> {
Ok(Some(if let Ok(Some(expr)) = dsl.expr() && let Ok(Some(head)) = expr.head() { Ok(if let Ok(Some(expr)) = dsl.expr() && let Ok(Some(head)) = expr.head() {
//println!("Mode::add: {head} {:?}", expr.tail()); //println!("Mode::add: {head} {:?}", expr.tail());
let tail = expr.tail()?.map(|x|x.trim()).unwrap_or(""); let tail = expr.tail()?.map(|x|x.trim()).unwrap_or("");
match head { 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 name = tail.head()?.ok_or("submode: missing name")?;
let body = tail.tail()?.ok_or("submode: missing body")?; let body = tail.tail()?.ok_or("submode: missing body")?;
let submode = Mode::default(); let submode = Mode::default();
let submode = body.each(submode, move|mut submode: Mode, item: &str|{ let submode = body.each(submode, |c,s|mode_add(c,s))?;
mode_add(&mut submode, &item);
Ok(submode)
})?;
let modes = mode.modes.clone(); let modes = mode.modes.clone();
modes.0.write().unwrap().insert(name.into(), Arc::new(submode)); modes.0.write().unwrap().insert(name.into(), Arc::new(submode));
mode mode
}, },
"keys" => { "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()); mode.keys.push(expr.trim().into());
Ok(mode) Ok(mode)
})? })?
@ -102,11 +132,11 @@ pub fn mode_add <'a> (mode: &'a mut Mode, dsl: &'a str) -> PerhapsRef<'a, &'a mu
mode mode
} else { } else {
return Err(format!("Mode::add: unexpected: {dsl:?}").into()); return Err(format!("Mode::add: unexpected: {dsl:?}").into());
})) })
} }
/// Load custom view definition. /// 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 name = expr.head()?.ok_or("view: missing name")?;
let body = expr.tail()?.ok_or("view: missing body")?; let body = expr.tail()?.ok_or("view: missing body")?;
views.write().unwrap().insert( views.write().unwrap().insert(
@ -116,12 +146,12 @@ pub fn load_view <'a> (views: &Views, expr: &'a str,) -> UsuallyRef<'a, ()> {
Ok(()) 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 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(), { binds.write().unwrap().insert(name.into(), {
let mut map = Bind::new(); 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 // TODO
Ok(()) Ok(())
} else if let Ok(Some(_word)) = item.expr().head().word() { } else if let Ok(Some(_word)) = item.expr().head().word() {
@ -241,47 +271,14 @@ impl Config {
/// Create, initialize, and watch a new configuration. /// Create, initialize, and watch a new configuration.
pub fn watched <T: 'static> (callback: impl FnOnce(Arc<Config>)->T) -> Usually<T> { pub fn watched <T: 'static> (callback: impl FnOnce(Arc<Config>)->T) -> Usually<T> {
let config = Self::init_new(None)?; let config = Self::init_new(None)?;
Self::watch(config.clone(), None)?; config_watch(config.clone(), None)?;
let result = callback(config); let result = callback(config);
Ok(result) Ok(result)
} }
/// Create and initialize a new configuration. /// Create and initialize a new configuration.
pub fn init_new (_dirs: Option<BaseDirectories>) -> UsuallyRef<'static, Arc<Self>> { pub fn init_new (_dirs: Option<BaseDirectories>) -> Usually<Arc<Self>> {
let config = Arc::new(Self::new(None)); config_init(Arc::new(Self::new(None)))
config_init(&config)?;
Ok(config)
}
/// Watch a config's file for changes.
pub fn watch (config: Arc<Config>, poll: Option<Duration>) -> 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())
}
} }
/// Find the config file. /// Find the config file.
@ -316,6 +313,10 @@ impl Config {
*self.binds.write().unwrap() = Default::default(); *self.binds.write().unwrap() = Default::default();
} }
pub fn get_view (&self, name: impl AsRef<str>) -> Option<Arc<str>> {
self.views.read().unwrap().get(name.as_ref()).cloned()
}
} }
pub use self::view::*; pub use self::view::*;

View file

@ -70,7 +70,7 @@ impl<T: TracksView + ScenesView + Send + Sync> ClipsView for T {}
pub trait ClipsView: TracksView + ScenesView { pub trait ClipsView: TracksView + ScenesView {
/// Draw clips per scene /// Draw clips per scene
fn view_scenes_clips (&self) -> impl Draw<'_, Tui> { fn view_scenes_clips (&self) -> impl Draw<Tui> {
let select = self.selection(); let select = self.selection();
let editor = self.editor(); let editor = self.editor();
let size = self.clips_size(); let size = self.clips_size();
@ -105,7 +105,7 @@ pub trait ClipsView: TracksView + ScenesView {
fg_bg(o, b, "".full_wh()), fg_bg(o, b, "".full_wh()),
fg_bg(f, b, bold(true, name)).align_nw().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() ).full_wh()
).exact_wh(w, y) ).exact_wh(w, y)
})).full_h().exact_w(track.width as u16) })).full_h().exact_w(track.width as u16)

View file

@ -5,24 +5,24 @@ impl_has!(Vec<MidiOutput>: |self: Arrangement| self.midi_outs);
impl_has!(Vec<MidiInput>: |self: App|self.project.midi_ins); impl_has!(Vec<MidiInput>: |self: App|self.project.midi_ins);
impl_has!(Vec<MidiOutput>: |self: App|self.project.midi_outs); impl_has!(Vec<MidiOutput>: |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<Tui> {
track.map(move|track|view_ports_status(theme, "MIDI ins: ", &track.sequencer.midi_ins)) 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<Tui> {
track.map(move|track|view_ports_status(theme, "MIDI outs: ", &track.sequencer.midi_outs)) 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<Tui> {
track.map(move|track|view_ports_status(theme, "Audio ins: ", &track.audio_ins())) 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<Tui> {
track.map(move|track|view_ports_status(theme, "Audio outs:", &track.audio_outs())) 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]) 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<Tui> + use<'a, T>
{ {
let ins = ports.len() as u16; let ins = ports.len() as u16;
let frame = Outer(true, Style::default().fg(g(96))); 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>> ( pub fn view_io_ports <'a, T: PortsSizes<'a>> (
fg: Color, bg: Color, items: impl Fn()->T + Send + Sync + 'a fg: Color, bg: Color, items: impl Fn()->T + Send + Sync + 'a
) -> impl Draw<'a, Tui> + 'a { ) -> impl Draw<Tui> + 'a {
type Item<'a> = (usize, &'a Arc<str>, &'a [Connect], usize, usize); type Item<'a> = (usize, &'a Arc<str>, &'a [Connect], usize, usize);
iter(items, iter(items,
move|(_index, name, connections, y, y2): Item<'a>, _| south( 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: JackPort>(T); pub struct Junction<T: JackPort>(T);
impl<T: JackPort> View<Tui> for Junction<T> { impl<T: JackPort> Draw<Tui> for Junction<T> {
fn view (&self) -> impl Draw<'_, Tui> { fn draw (&self, to: &mut Tui) -> Drawn<u16> {
T::KIND T::KIND.draw(to)
} }
} }

View file

@ -203,7 +203,7 @@ pub trait ScenesView: HasEditor + HasSelection + HasSceneScroll + HasClipsSize +
fn w_side (&self) -> u16; fn w_side (&self) -> u16;
fn w_mid (&self) -> u16; fn w_mid (&self) -> u16;
fn view_scenes_names (&self) -> impl Draw<'_, Tui> { fn view_scenes_names (&self) -> impl Draw<Tui> {
let select = self.selection(); let select = self.selection();
let editor = self.editor(); let editor = self.editor();
let editing = self.is_editing(); let editing = self.is_editing();
@ -281,7 +281,7 @@ pub fn view_scene_name <'a> (
index: usize, index: usize,
scene: &Scene, scene: &Scene,
editing: bool editing: bool
) -> impl Draw<'a, 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 {

View file

@ -46,10 +46,10 @@ impl Track {
pub fn audio_outs (&self) -> &[AudioOutput] { pub fn audio_outs (&self) -> &[AudioOutput] {
self.devices.last().map(|x|x.audio_outs()).unwrap_or_default() 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<Tui> + 'a, U: TracksSizes<'a>> (
tracks: impl Fn() -> U + Send + Sync + 'a, tracks: impl Fn() -> U + Send + Sync + 'a,
callback: &'a (impl Fn(usize, &'a Track)->T + Send + Sync + 'a) callback: &'a (impl Fn(usize, &'a Track)->T + Send + Sync + 'a)
) -> impl Draw<'a, Tui> { ) -> impl Draw<Tui> {
iter_east(move||tracks().map(|(index, track, x1, x2): (usize, &Track, usize, usize)|{ iter_east(move||tracks().map(|(index, track, x1, x2): (usize, &Track, usize, usize)|{
fg_bg( fg_bg(
track.color.lightest.term, track.color.lightest.term,
@ -343,7 +343,7 @@ pub trait TracksView: HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTra
} }
/// Draw name of each track /// Draw name of each track
fn view_track_names (&self, theme: ItemTheme) -> impl Draw<'_, Tui> { fn view_track_names (&self, theme: ItemTheme) -> impl Draw<Tui> {
let selected = self.selection(); let selected = self.selection();
east( east(
south( south(
@ -371,32 +371,33 @@ pub trait TracksView: HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTra
button_2("T", "+", false), button_2("T", "+", false),
button_2("S", "+", false), button_2("S", "+", false),
), ),
bg(theme.darker.term, iter_east(||self.tracks_with_sizes().map(|(index, track, x1, _x2)|{ bg(theme.darker.term, iter_east(||self.tracks_with_sizes()
let b = if selected.track() == Some(index) { .map(|(index, track, _x1, _x2)|{
track.color.light.term let b = if selected.track() == Some(index) {
} else { track.color.light.term
track.color.base.term } else {
}; track.color.base.term
bg(b, south( };
east!( bg(b, south(
"·t", east!(
index, "·t",
" ", index,
fg(Rgb(255, 255, 255), bold(true, &track.name)) " ",
) fg(Rgb(255, 255, 255), bold(true, &track.name))
.align_nw() )
.full_w(), .align_nw()
"" .full_w(),
)) ""
.exact_w(track_width(index, track)) ))
.exact_h(2) .exact_w(track_width(index, track))
}))) .exact_h(2)
})))
) )
) )
} }
/// Draw outputs per track /// 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<Tui> {
view_track_row_section(theme, view_track_row_section(theme,
south(button_2("o", "utput", false).align_w().full_w(), south(button_2("o", "utput", false).align_w().full_w(),
draw(|to: &mut Tui|{ draw(|to: &mut Tui|{
@ -431,7 +432,7 @@ pub trait TracksView: HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTra
} }
/// Draw inputs per track /// 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<Tui> {
let mut height = 0u16; let mut height = 0u16;
for track in self.tracks().iter() { for track in self.tracks().iter() {
height = height.max(track.sequencer.midi_ins.len() as u16); height = height.max(track.sequencer.midi_ins.len() as u16);
@ -459,7 +460,7 @@ pub trait TracksView: HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTra
}).align_w())) }).align_w()))
} }
fn view_track_devices (&self, theme: ItemTheme) -> impl Draw<'_, Tui> { fn view_track_devices (&self, theme: ItemTheme) -> impl Draw<Tui> {
let height = self.tracks_devices_height(); let height = self.tracks_devices_height();
let btn1 = button_3("d", "evice", self.track().map(|t|t.devices.len()).unwrap_or(0), false); let btn1 = button_3("d", "evice", self.track().map(|t|t.devices.len()).unwrap_or(0), false);
let btn2 = button_2("D", "+", false); let btn2 = button_2("D", "+", false);
@ -488,7 +489,7 @@ pub trait TracksView: HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTra
h as u16 h as u16
} }
fn view_inputs (&self, _theme: ItemTheme) -> impl Draw<'_, Tui> + '_ { fn view_inputs (&self, _theme: ItemTheme) -> impl Draw<Tui> + '_ {
let title_1 = button_3("i", "nput ", self.midi_ins().len(), false).align_w().exact_wh(20, 1); 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); let title_2 = button_2("I", "+", false).exact_wh(4, 1);
east(title_1, west(title_2, draw(move|to: &mut Tui|{ east(title_1, west(title_2, draw(move|to: &mut Tui|{
@ -532,7 +533,7 @@ pub trait TracksView: HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTra
h as u16 h as u16
} }
fn view_outputs (&self, theme: ItemTheme) -> impl Draw<'_, Tui> { fn view_outputs (&self, theme: ItemTheme) -> impl Draw<Tui> {
let height = self.outputs_height(); let height = self.outputs_height();
let list = south( let list = south(
button_3( button_3(
@ -632,10 +633,10 @@ impl HasTrackScroll for App {
fn view_track_row_section <'a> ( fn view_track_row_section <'a> (
_theme: ItemTheme, _theme: ItemTheme,
button: impl Draw<'a, Tui>, button: impl Draw<Tui>,
button_add: impl Draw<'a, Tui>, button_add: impl Draw<Tui>,
content: impl Draw<'a, Tui>, content: impl Draw<Tui>,
) -> impl Draw<'a, Tui> { ) -> impl Draw<Tui> {
west( west(
button_add.align_nw().exact_w(4).full_h(), button_add.align_nw().exact_w(4).full_h(),
east( east(

View file

@ -118,7 +118,7 @@ impl Browse {
unreachable!() unreachable!()
}) })
} }
//fn tui (&self) -> impl Draw<'_, Tui> { //fn tui (&self) -> impl Draw<Tui> {
//iter_south_fixed(1, ||self.tui_entries(), |entry, _index|entry.origin_w().full_w()) //iter_south_fixed(1, ||self.tui_entries(), |entry, _index|entry.origin_w().full_w())
//} //}
//fn tui_entries (&self) -> EntriesIterator<'_, Tui> { //fn tui_entries (&self) -> EntriesIterator<'_, Tui> {
@ -133,7 +133,7 @@ impl Browse {
} }
//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();
@ -179,7 +179,7 @@ pub fn scan (dir: &PathBuf) -> Usually<(Vec<OsString>, Vec<OsString>)> {
Ok((subdirs, files)) Ok((subdirs, files))
} }
pub fn view_browse_title (state: &App) -> impl Draw<'_, Tui> { pub fn view_browse_title (state: &App) -> impl Draw<Tui> {
field_v(ItemTheme::default(), match state.dialog.browser_target().unwrap() { field_v(ItemTheme::default(), match state.dialog.browser_target().unwrap() {
BrowseTarget::SaveProject => "Save project:", BrowseTarget::SaveProject => "Save project:",
BrowseTarget::LoadProject => "Load project:", BrowseTarget::LoadProject => "Load project:",

View file

@ -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(true, x.as_ref(), x.as_ref(), x.as_ref());
/// let _ = tek::view_transport(false, 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<Tui> {
let theme = ItemTheme::G[96]; let theme = ItemTheme::G[96];
bg(Black, east!(above( bg(Black, east!(above(
button_play_pause(play, false).align_w(), 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(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()); /// 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<Tui> {
let theme = ItemTheme::G[96]; let theme = ItemTheme::G[96];
let sr = field_h(theme, "SR", sr); let sr = field_h(theme, "SR", sr);
let buf = field_h(theme, "Buf", buf); 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, true);
/// let _ = tek::button_play_pause(false, false); /// 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<Tui> {
bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) }, bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) },
either(compact, either(compact,
draw(move|to: &mut Tui|either(playing, draw(move|to: &mut Tui|either(playing,

View file

@ -1,7 +1,7 @@
use crate::{*, device::*}; use crate::{*, device::*};
pub fn draw_dialog <'a> (to: &mut Tui, mut frags: std::str::Split<&str>, state: &App) pub fn draw_dialog <'a> (to: &mut Tui, mut frags: std::str::Split<&str>, state: &App)
-> Drawn<'a, u16> -> Drawn<u16>
{ {
match frags.next() { match frags.next() {
Some("menu") => if let Dialog::Menu(selected, items) = &state.dialog { Some("menu") => if let Dialog::Menu(selected, items) = &state.dialog {

View file

@ -268,7 +268,7 @@ impl MidiEditor {
self.get_time_pos().overflowing_sub(1) self.get_time_pos().overflowing_sub(1)
.0.min(self.clip_length().saturating_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<Tui> + '_ {
let (_color, name, length, looped) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) { 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) (clip.color, clip.name.clone(), clip.length, clip.looped)
} else { (ItemTheme::G[64], String::new().into(), 0, false) }; } else { (ItemTheme::G[64], String::new().into(), 0, false) };
@ -281,7 +281,7 @@ impl MidiEditor {
.origin_w().full_w(), .origin_w().full_w(),
).exact_w(20) ).exact_w(20)
} }
pub fn edit_status (&self) -> impl Draw<'_, Tui> + '_ { pub fn edit_status (&self) -> impl Draw<Tui> + '_ {
let (_color, length) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) { let (_color, length) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) {
(clip.color, clip.length) (clip.color, clip.length)
} else { (ItemTheme::G[64], 0) }; } else { (ItemTheme::G[64], 0) };
@ -425,11 +425,11 @@ impl MidiViewer for MidiEditor {
fn set_clip (&mut self, p: Option<&Arc<RwLock<MidiClip>>>) { self.mode.set_clip(p) } fn set_clip (&mut self, p: Option<&Arc<RwLock<MidiClip>>>) { self.mode.set_clip(p) }
} }
impl View<Tui> for MidiEditor { impl Draw<Tui> for MidiEditor {
fn view (&self) -> impl Draw<'_, Tui> { fn draw (&self, to: &mut Tui) -> Drawn<u16> {
self.autoscroll(); self.autoscroll();
/*self.autozoom();*/ /*self.autozoom();*/
self.size.of(self.mode.view()) self.size.of(&self.mode).draw(to)
} }
} }

View file

@ -19,7 +19,7 @@ impl OctaveVertical {
} }
impl OctaveVertical { impl OctaveVertical {
pub fn tui (&self) -> impl Draw<'_, Tui> { pub fn tui (&self) -> impl Draw<Tui> {
east!( east!(
fg_bg(self.color(0), self.color(1), ""), fg_bg(self.color(0), self.color(1), ""),
fg_bg(self.color(2), self.color(3), ""), fg_bg(self.color(2), self.color(3), ""),

View file

@ -24,8 +24,8 @@ pub struct PianoHorizontal {
impl_has!(Sizer: |self: PianoHorizontal| self.size); impl_has!(Sizer: |self: PianoHorizontal| self.size);
impl View<Tui> for PianoHorizontal { impl Draw<Tui> for PianoHorizontal {
fn view (&self) -> impl Draw<'_, Tui> { fn draw (&self, to: &mut Tui) -> Drawn<u16> {
south( south(
east( east(
format!("{}x{}", self.size.w(), self.size.h()).exact_w(5), format!("{}x{}", self.size.w(), self.size.h()).exact_w(5),
@ -35,7 +35,7 @@ impl View<Tui> for PianoHorizontal {
self.keys(), self.keys(),
self.size.of(below(self.notes().full_wh(), self.cursor().full_wh())) 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<Tui> {
let time_start = self.get_time_start(); let time_start = self.get_time_start();
let note_lo = self.get_note_lo(); let note_lo = self.get_note_lo();
let note_hi = self.get_note_hi(); let note_hi = self.get_note_hi();
@ -168,7 +168,7 @@ impl PianoHorizontal {
}) })
} }
fn cursor (&self) -> impl Draw<'_, Tui> { fn cursor (&self) -> impl Draw<Tui> {
let note_hi = self.get_note_hi(); let note_hi = self.get_note_hi();
let note_lo = self.get_note_lo(); let note_lo = self.get_note_lo();
let note_pos = self.get_note_pos(); let note_pos = self.get_note_pos();
@ -202,7 +202,7 @@ impl PianoHorizontal {
}) })
} }
fn keys (&self) -> impl Draw<'_, Tui> { fn keys (&self) -> impl Draw<Tui> {
let state = self; let state = self;
let color = state.color; let color = state.color;
let note_lo = state.get_note_lo(); let note_lo = state.get_note_lo();
@ -229,7 +229,7 @@ impl PianoHorizontal {
}).exact_w(self.keys_width).full_h() }).exact_w(self.keys_width).full_h()
} }
fn timeline (&self) -> impl Draw<'_, Tui> + '_ { fn timeline (&self) -> impl Draw<Tui> + '_ {
draw(move|to: &mut Tui|{ draw(move|to: &mut Tui|{
let xywh = to.area().into(); let xywh = to.area().into();
let XYWH(x, y, w, _h) = xywh; let XYWH(x, y, w, _h) = xywh;

View file

@ -40,7 +40,7 @@ impl_draw!(|self: Log10Meter, to: Tui| {
Ok(Some(to.area().into())) Ok(Some(to.area().into()))
}); });
fn draw_meters (meters: &[f32]) -> impl Draw<'_, Tui> + use<'_> { fn draw_meters (meters: &[f32]) -> impl Draw<Tui> + use<'_> {
bg(Black, iter_east_fixed(1, ||meters.iter(), |value, _index|{ bg(Black, iter_east_fixed(1, ||meters.iter(), |value, _index|{
RmsMeter(*value).full_h() RmsMeter(*value).full_h()
}).exact_w(2)) }).exact_w(2))

View file

@ -330,7 +330,7 @@ fn draw_list_item (sample: &Option<Arc<RwLock<Sample>>>) -> String {
} }
} }
fn draw_viewer (sample: Option<&Arc<RwLock<Sample>>>) -> impl Draw<'_, Tui> + use<'_> { fn draw_viewer (sample: Option<&Arc<RwLock<Sample>>>) -> impl Draw<Tui> + use<'_> {
let min_db = -64.0; let min_db = -64.0;
draw(move|to: &mut Tui|{ draw(move|to: &mut Tui|{
let xywh = to.area().into(); let xywh = to.area().into();
@ -750,7 +750,7 @@ fn read_sample_data (_: &str) -> Usually<(usize, Vec<Vec<f32>>)> {
todo!(); todo!();
} }
pub fn view_sample_info (sample: Option<&Arc<RwLock<Sample>>>) -> impl Draw<'_, Tui> + use<'_> { pub fn view_sample_info (sample: Option<&Arc<RwLock<Sample>>>) -> impl Draw<Tui> + use<'_> {
when(sample.is_some(), draw(move|to: &mut Tui|{ when(sample.is_some(), draw(move|to: &mut Tui|{
let sample = sample.unwrap().read().unwrap(); let sample = sample.unwrap().read().unwrap();
let theme = sample.color; let theme = sample.color;
@ -765,7 +765,7 @@ pub fn view_sample_info (sample: Option<&Arc<RwLock<Sample>>>) -> impl Draw<'_,
})) }))
} }
pub fn view_sample_info_v (sample: Option<&Arc<RwLock<Sample>>>) -> impl Draw<'_, Tui> + use<'_> { pub fn view_sample_info_v (sample: Option<&Arc<RwLock<Sample>>>) -> impl Draw<Tui> + use<'_> {
let a = draw(move|to: &mut Tui|{ let a = draw(move|to: &mut Tui|{
let sample = sample.unwrap().read().unwrap(); let sample = sample.unwrap().read().unwrap();
let theme = sample.color; let theme = sample.color;
@ -788,7 +788,7 @@ pub fn view_sample_info_v (sample: Option<&Arc<RwLock<Sample>>>) -> impl Draw<'_
either(sample.is_some(), a, b) either(sample.is_some(), a, b)
} }
pub fn view_sample_status (sample: Option<&Arc<RwLock<Sample>>>) -> impl Draw<'_, Tui> { pub fn view_sample_status (sample: Option<&Arc<RwLock<Sample>>>) -> impl Draw<Tui> {
bold(true, fg(g(224), sample bold(true, fg(g(224), sample
.map(|sample|{ .map(|sample|{
let sample = sample.read().unwrap(); let sample = sample.read().unwrap();

View file

@ -113,7 +113,7 @@ pub trait HasPlayClip: HasClock {
*self.reset_mut() = true; *self.reset_mut() = true;
} }
fn play_status (&self) -> impl Draw<'_, Tui> { fn play_status (&self) -> impl Draw<Tui> {
let (name, color): (Arc<str>, ItemTheme) = if let Some((_, Some(clip))) = self.play_clip() { let (name, color): (Arc<str>, ItemTheme) = if let Some((_, Some(clip))) = self.play_clip() {
let MidiClip { ref name, color, .. } = *clip.read().unwrap(); let MidiClip { ref name, color, .. } = *clip.read().unwrap();
(name.clone(), color) (name.clone(), color)
@ -130,7 +130,7 @@ pub trait HasPlayClip: HasClock {
.into() .into()
} }
fn next_status (&self) -> impl Draw<'_, Tui> { fn next_status (&self) -> impl Draw<Tui> {
let mut time: Arc<str> = String::from("--.-.--").into(); let mut time: Arc<str> = String::from("--.-.--").into();
let mut name: Arc<str> = String::from("").into(); let mut name: Arc<str> = String::from("").into();
let mut color = ItemTheme::G[64]; let mut color = ItemTheme::G[64];

View file

@ -1,7 +1,4 @@
(view :logo (bsp/s (bg (rgb 100 70 40) (text ~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ )) (view :logo (text tek))
(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 :browse (bsp/s (view :browse (bsp/s
(padding 3 1 :browse-title) (padding 3 1 :browse-title)

View file

@ -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<Tui> {
let selected = state.dialog.device_kind().unwrap(); let selected = state.dialog.device_kind().unwrap();
south( south(
bold(true, "Add device"), bold(true, "Add device"),
@ -892,15 +892,13 @@ mod draw {
/// ///
/// If there is an error, the error is displayed. FIXME: overlay it /// If there is an error, the error is displayed. FIXME: overlay it
/// Then, every top-level form of the DSL description is rendered. /// Then, every top-level form of the DSL description is rendered.
impl View<Tui> for App { impl Draw<Tui> for App {
fn view (&self) -> impl Draw<'_, Tui> { fn draw (&self, to: &mut Tui) -> Drawn<u16> {
//self.perf.cycle(&mut |_|{ //self.perf.cycle(&mut |_|{
draw(|to: &mut Tui|{ self.draw_error(to)?;
self.draw_error(to)?; self.draw_modes(to)?;
self.draw_modes(to)?; //self.draw_debug(to)?;
//self.draw_debug(to)?; Ok(Some(to.area().into()))
Ok(Some(to.area().into()))
})
//}) //})
} }
} }
@ -913,11 +911,11 @@ mod draw {
Ok(()) 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)) { if let Some(mode) = self.mode.as_ref().and_then(|m|self.config.get_mode(m)) {
let mut error = false; let mut error = false;
for (index, dsl) in mode.view.iter().enumerate() { for (index, dsl) in mode.view.iter().enumerate() {
match self.draw_mode(to, dsl) { match self.interpret(to, dsl) {
Ok(None) => {}, Ok(None) => {},
Ok(Some(XYWH(.., w, h))) => { Ok(Some(XYWH(.., w, h))) => {
self.size.0.store(w as usize, Relaxed); self.size.0.store(w as usize, Relaxed);
@ -942,11 +940,7 @@ mod draw {
Ok(()) Ok(())
} }
fn draw_mode <'a, L: Language + 'a> (&'a self, to: &mut Tui, dsl: L) -> Drawn<'a, u16> { #[allow(unused)] fn draw_debug (&self, to: &mut Tui) -> Drawn<u16> {
self.interpret(to, dsl)
}
fn draw_debug (&self, to: &mut Tui) -> Drawn<u16> {
east( east(
format!("{}x{} ", self.size.0.load(Relaxed), self.size.1.load(Relaxed)), format!("{}x{} ", self.size.0.load(Relaxed), self.size.1.load(Relaxed)),
format!("{}% {} ", self.perf.percentage().unwrap_or_default(), self.perf.clock.raw() / 1000000000), format!("{}% {} ", self.perf.percentage().unwrap_or_default(), self.perf.clock.raw() / 1000000000),
@ -954,15 +948,29 @@ mod draw {
} }
} }
impl<'a> Interpret<'a, Tui, Option<XYWH<u16>>> for App { impl Interpret<Tui, Option<XYWH<u16>>> for App {
fn interpret <L: Language + 'a> (&'a self, to: &mut Tui, dsl: L) -> Drawn<'a, u16> { fn interpret <L: Language> (&self, to: &mut Tui, dsl: L) -> Drawn<u16> {
if let Some(expr) = dsl.expr()? { if let Ok(Some(expr)) = dsl.expr() {
interpret_keyword!(self, to, expr, [ ok_flat(expr.head()?.map(|head|{
kw_when, kw_either, kw_split, kw_align, kw_exact, kw_min, kw_max, kw_push, kw_full, match head.split('/').next() {
kw_tui_text, kw_tui_fg, kw_tui_bg Some("when") => kw_when(self, to, expr),
]); Some("either") => kw_either(self, to, expr),
Err(format!("interpret_expr: unexpected: {expr:?}").into()) Some("bsp") => kw_split(self, to, expr),
} else if let Some(word) = dsl.word()? { 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("/"); let mut frags = word.src()?.unwrap().split("/");
match frags.next() { match frags.next() {
//Some(":logo") => view_logo().draw(to), //Some(":logo") => view_logo().draw(to),
@ -993,15 +1001,10 @@ mod draw {
Some(":editor") => "TODO Editor".draw(to), Some(":editor") => "TODO Editor".draw(to),
Some(":transport") => view_transport(true, "", "", "").draw(to), Some(":transport") => view_transport(true, "", "", "").draw(to),
Some(":debug") => format!("[{:?}]", to.area()).exact_h(1).draw(to), Some(":debug") => format!("[{:?}]", to.area()).exact_h(1).draw(to),
Some(_) => { Some(_) => if let Some(lang) = self.config.get_view(word) {
let views = self.config.views.read().unwrap(); self.interpret(to, lang)
if let Some(lang) = views.get(word.src()?.unwrap()) { } else {
let lang = lang.clone(); fg(Color::Rgb(128, 32, 32), format!("undefined: {word:?}")).draw(to)
std::mem::drop(views);
self.draw_mode(to, lang)
} else {
fg(Color::Rgb(128, 32, 32), format!("undefined: {word:?}")).draw(to)
}
}, },
_ => unreachable!() _ => unreachable!()
} }
@ -1022,7 +1025,7 @@ mod draw {
} }
pub fn view_templates <'a> (_frags: std::str::Split<&str>, state: &'a App) pub fn view_templates <'a> (_frags: std::str::Split<&str>, state: &'a App)
-> impl Draw<'a, Tui> + use<'a> -> impl Draw<Tui> + use<'a>
{ {
let height = (state.config.modes.len() * 2) as u16; let height = (state.config.modes.len() * 2) as u16;
draw(move |to: &mut Tui|{ draw(move |to: &mut Tui|{
@ -1045,16 +1048,16 @@ mod draw {
} }
pub fn field_h <'a, T: Screen> ( pub fn field_h <'a, T: Screen> (
_theme: ItemTheme, _head: impl Draw<'a, T>, _body: impl Draw<'a, T> _theme: ItemTheme, _head: impl Draw<T>, _body: impl Draw<T>
) -> impl Draw<'a, T> { ) -> impl Draw<T> {
} }
pub fn field_v <'a, T: Screen> ( pub fn field_v <'a, T: Screen> (
_theme: ItemTheme, _head: impl Draw<'a, T>, _body: impl Draw<'a, T> _theme: ItemTheme, _head: impl Draw<T>, _body: impl Draw<T>
) -> impl Draw<'a, T> { ) -> impl Draw<T> {
} }
pub fn view_sessions <'a> () -> impl Draw<'a, Tui> { pub fn view_sessions <'a> () -> impl Draw<Tui> {
let h = 6; let h = 6;
let w = Some(30); let w = Some(30);
let f = Rgb(224, 192, 128); let f = Rgb(224, 192, 128);
@ -1074,8 +1077,8 @@ mod draw {
/// let fg = tengri::ratatui::style::Color::Green; /// let fg = tengri::ratatui::style::Color::Green;
/// let _ = tek::view_wrap(bg, fg, "and then blue, too!"); /// let _ = tek::view_wrap(bg, fg, "and then blue, too!");
/// ``` /// ```
pub fn view_wrap <'a> (bg: Color, fg: Color, content: impl Draw<'a, Tui>) pub fn view_wrap <'a> (bg: Color, fg: Color, content: impl Draw<Tui>)
-> impl Draw<'a, Tui> -> impl Draw<Tui>
{ {
let left = fg_bg(bg, Reset, y_repeat("").exact_w(1)); let left = fg_bg(bg, Reset, y_repeat("").exact_w(1));
let right = fg_bg(bg, Reset, y_repeat("").exact_w(1)); let right = fg_bg(bg, Reset, y_repeat("").exact_w(1));
@ -1087,7 +1090,7 @@ mod draw {
/// let _ = tek::view_meters(&[0.0, 0.0]); /// let _ = tek::view_meters(&[0.0, 0.0]);
/// ``` /// ```
pub fn view_meter <'a> (label: &'a str, value: f32) pub fn view_meter <'a> (label: &'a str, value: f32)
-> impl Draw<'a, Tui> -> impl Draw<Tui>
{ {
let f = field_h(ItemTheme::G[128], label, format!("{:>+9.3}", value)); let f = field_h(ItemTheme::G[128], label, format!("{:>+9.3}", value));
let w = if value >= 0.0 { 13 } let w = if value >= 0.0 { 13 }
@ -1110,14 +1113,14 @@ mod draw {
south!(f, bg(c, ()).exact_wh(w, 1)) 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<Tui> + use<'_> {
let left = format!("L/{:>+9.3}", values[0]); let left = format!("L/{:>+9.3}", values[0]);
let right = format!("R/{:>+9.3}", values[1]); let right = format!("R/{:>+9.3}", values[1]);
south(left, right) south(left, right)
} }
pub fn view_track_header <'a> (theme: ItemTheme, content: impl Draw<'a, Tui>) pub fn view_track_header <'a> (theme: ItemTheme, content: impl Draw<Tui>)
-> impl Draw<'a, Tui> -> impl Draw<Tui>
{ {
bg(theme.darker.term, content.align_e().full_w()).exact_w(12) bg(theme.darker.term, content.align_e().full_w()).exact_w(12)
} }
@ -1126,8 +1129,8 @@ mod draw {
/// let _ = tek::button_2("", "", true); /// let _ = tek::button_2("", "", true);
/// let _ = tek::button_2("", "", false); /// let _ = tek::button_2("", "", false);
/// ``` /// ```
pub fn button_2 <'a> (key: impl Draw<'a, Tui>, label: impl Draw<'a, Tui>, hide: bool) pub fn button_2 <'a> (key: impl Draw<Tui>, label: impl Draw<Tui>, hide: bool)
-> impl Draw<'a, Tui> -> impl Draw<Tui>
{ {
let c1 = tui_orange(); let c1 = tui_orange();
let c2 = tui_g(0); let c2 = tui_g(0);
@ -1142,11 +1145,11 @@ mod draw {
/// let _ = tek::button_3("", "", "", false); /// let _ = tek::button_3("", "", "", false);
/// ``` /// ```
pub fn button_3 <'a> ( pub fn button_3 <'a> (
key: impl Draw<'a, Tui>, key: impl Draw<Tui>,
label: impl Draw<'a, Tui>, label: impl Draw<Tui>,
value: impl Draw<'a, Tui>, value: impl Draw<Tui>,
editing: bool, editing: bool,
) -> impl Draw<'a, Tui> { ) -> impl Draw<Tui> {
let c1 = tui_orange(); let c1 = tui_orange();
let c2 = tui_g(0); let c2 = tui_g(0);
let c3 = tui_g(96); let c3 = tui_g(96);

2
tengri

@ -1 +1 @@
Subproject commit e69d4287e0ee1f751d2f096f7f85b43628cef259 Subproject commit 4172fa257776f5c6c7b406429b2244d630702458