wip: axis bindings

This commit is contained in:
i do not exist 2026-09-24 19:11:04 +03:00
parent 767f917302
commit 057d09cfdf
9 changed files with 205 additions and 95 deletions

View file

@ -21,14 +21,11 @@ pub trait EditorModel: HasEditor {
/// use std::sync::Arc; /// use std::sync::Arc;
/// use parking_lot::RwLock; /// use parking_lot::RwLock;
/// use tek::{MidiClip, MidiEditor, PianoHorizontal}; /// use tek::{MidiClip, MidiEditor, PianoHorizontal};
/// let clip = MidiClip::stop_all();
/// let mode = PianoHorizontal::default(); /// let mode = PianoHorizontal::default();
/// let mut editor = MidiEditor { mode, size: Default::default() }; /// let editor = MidiEditor::new();
/// /// editor.mode.clip.set(Some(MidiClip::stop_all()));
/// let _ = editor.put_note(true); /// let _ = editor.put_note(true);
/// let _ = editor.put_note(false); /// let _ = editor.put_note(false);
/// let _ = editor.clip_status();
/// let _ = editor.edit_status();
/// ``` /// ```
pub struct MidiEditor { pub struct MidiEditor {
/// View mode and state of editor /// View mode and state of editor
@ -74,7 +71,7 @@ impl MidiEditor {
} }
/// Put note at current position /// Put note at current position
pub fn put_note (&mut self, advance: bool) { pub fn put_note (&self, advance: bool) {
let mut redraw = false; let mut redraw = false;
if let Some(clip) = &*self.mode.clip.read() { if let Some(clip) = &*self.mode.clip.read() {
let note_start = self.get_time_pos(); let note_start = self.get_time_pos();

View file

@ -1,45 +1,73 @@
(mode Menu (name Main Menu) (info Welcome to Tek!) (keys Y Confirm) (axis X @left @right )
(axis Y @up @down )
(axis Z @minus @equal )
(axis I @comma @period )
(axis W @openbracket @closebracket)
(axis XFine @shift/left @shift/right )
(axis YFine @shift/up @shift/down )
(axis ZFine @underscore @plus )
(axis IFine @lt @gt )
(axis WFine @openbrace @closebrace )
(mode Menu
(name Main Menu)
(info Welcome to Tek!)
(axis Y Index)
(bind Confirm)
(view (bar/s 2 Transport (bar/n 2 StatusBar (align/c (bar/s 2 (align/x Logo) Menu)))))) (view (bar/s 2 Transport (bar/n 2 StatusBar (align/c (bar/s 2 (align/x Logo) Menu))))))
(mode Help (mode Help
(view (bg (g 32) (fg (g 64) (fill/xy (axis Y Index)
(bg (g 48) (fg (g 224) (align/c (expand/wh 2 2 (text Nobody helps! )))))))))) (axis P Page)
(view (bg (g 32) (fg (g 64) (fill/xy (bg (g 48) (fg (g 224)
(align/c (bar/s 2 (expand/wh 2 2 (text Nobody helps! )) Config)))))))))
(mode Quit (mode Quit
(keys (@y Quit)) (bind (@n leave) (@y quit))
(view (bg (g 32) (fg (g 64) (fill/xy (view (bg (g 32) (fg (g 64) (fill/xy (bg (g 48) (fg (g 224) (align/c (expand/wh 2 2 (text Are you sure? ))))))))))
(bg (g 48) (fg (g 224) (align/c (expand/wh 2 2 (text Are you sure? ))))))))))
(mode Save (mode Save
(axis X Cursor)
(axis Y Index)
(view (bg (g 32) (fg (g 64) (fill/xy (view (bg (g 32) (fg (g 64) (fill/xy
(bg (g 48) (fg (g 224) (align/c (bar/s 1 (bg (g 48) (fg (g 224) (align/c (bar/s 1
(expand/wh 2 2 (text Save to: )) (expand/wh 2 2 (text Save to: ))
(text <file browser here>)))))))))) (text <file browser here>))))))))))
(mode History
(axis Y Index)
(view (bg (g 32) (fg (g 64) (fill/xy
(bg (g 48) (fg (g 224) (align/c (bar/s 1
(expand/wh 2 2 (text Undo/redo history ))
HistoryList)))))))))
(mode Load (mode Load
(axis X Cursor)
(axis Y Index)
(view (bg (g 32) (fg (g 64) (fill/xy (view (bg (g 32) (fg (g 64) (fill/xy
(bg (g 48) (fg (g 224) (align/c (bar/s 1 (bg (g 48) (fg (g 224) (align/c (bar/s 1
(expand/wh 2 2 (text Load from: )) (expand/wh 2 2 (text Load from: ))
(text <file browser here>)))))))))) (text <file browser here>))))))))))
(mode History
(view (bg (g 32) (fg (g 64) (fill/xy
(bg (g 48) (fg (g 224) (align/c (expand/wh 2 2 (text Undo/redo history ))))))))))
(mode Rename (mode Rename
(axis X Cursor)
(axis Y Index)
(view (bg (g 32) (fg (g 64) (fill/xy (view (bg (g 32) (fg (g 64) (fill/xy
(bg (g 48) (fg (g 224) (align/c (expand/wh 2 2 (text Rename item )))))))))) (bg (g 48) (fg (g 224) (align/c (expand/wh 2 2 (text Rename item ))))))))))
(mode Options (mode Options
(axis Y Index)
(view (bg (g 32) (fg (g 64) (fill/xy (view (bg (g 32) (fg (g 64) (fill/xy
(bg (g 48) (fg (g 224) (align/c (expand/wh 2 2 (text Options )))))))))) (bg (g 48) (fg (g 224) (align/c (expand/wh 2 2 (text Options ))))))))))
(mode Transport (name Transport) (info JACK transport controller.) (mode Transport (name Transport) (info JACK transport controller.)
(keys Clock Global) (bind Clock Global)
(view Transport)) (view Transport))
(mode Arranger (name Arranger) (info Launch grid.) (mode Arranger (name Arranger) (info Launch grid.)
(keys Global Clock Tracks Scenes Clip Launch Color History) (bind Global Clock Tracks Scenes Clip Launch Color History)
(axis X Track)
(axis Y Clip)
(view (bar/n 2 StatusBar (bar/s 8 (bar/e 16 (align/nw Transport) (view (bar/n 2 StatusBar (bar/s 8 (bar/e 16 (align/nw Transport)
(bar/w 20 (bar/s 2 (bar/w 4 TracksOutputsAdd TracksOutputsCount) (bar/w 20 (bar/s 2 (bar/w 4 TracksOutputsAdd TracksOutputsCount)
(bar/s 2 (bar/w 4 TracksDevicesAdd TracksDevicesCount) (bar/s 2 (bar/w 4 TracksDevicesAdd TracksDevicesCount)
@ -50,19 +78,24 @@
(bar/s 2 TracksNamesCells TracksInputsCells)))))) (bar/s 2 TracksNamesCells TracksInputsCells))))))
(bar/w 20 (text) (bar/e 16 ScenesNames ScenesClips)))))) (bar/w 20 (text) (bar/e 16 ScenesNames ScenesClips))))))
(mode Edit (mode Edit (info MIDI editor.)
(keys X Y)) (axis X Time)
(axis Y Pitch))
(mode Sequencer (info MIDI sequencer.) (mode Sequencer (info MIDI sequencer.)
(keys Global Clock I IFine Y Page EditorView EditorAdd EditorDel) (bind Global Clock I IFine Y Page EditorView EditorAdd EditorDel)
(view (bsp/s (exact/h 1 Transport) (bsp/n (exact/h 1 StatusBar) (fill/xy (bsp/a (fill/xy (align/e Pool)) Editor)))))) (view (bsp/s (exact/h 1 Transport)
(bsp/n (exact/h 1 StatusBar)
(fill/xy (bsp/a (fill/xy (align/e Pool)) Editor))))))
(mode Sampler (info Sample player.) (mode Sampler (info Sample player.)
(keys SamplerDirs SamplerRec SamplerPlay) (bind SamplerDirs SamplerRec SamplerPlay)
(view (bsp/s (exact/h 1 Transport) (bsp/n (exact/h 1 StatusBar) (fill/xy SampleGrid))))) (view (bsp/s (exact/h 1 Transport)
(bsp/n (exact/h 1 StatusBar)
(fill/xy SampleGrid)))))
(mode Groovebox (info Sequencer with sampler.) (mode Groovebox (info Sequencer with sampler.)
(keys Global Clock Sampler I IFine Y Page EditorView EditorAdd EditorDel) (bind Global Clock Sampler I IFine Y Page EditorView EditorAdd EditorDel)
(view (bsp/w MetersOut (bsp/e MetersIn (bsp/w GrooveMeta GrooveEditor))))) (view (bsp/w MetersOut (bsp/e MetersIn (bsp/w GrooveMeta GrooveEditor)))))
(view Logo (bg (g 255) (pad/xy 1 0 (bg (g 0) (fg (g 255) (text tek! 0.4.0 )))))) (view Logo (bg (g 255) (pad/xy 1 0 (bg (g 0) (fg (g 255) (text tek! 0.4.0 ))))))
@ -78,7 +111,7 @@
(bar/e 24 (bg (g 42) (full/x (pad/x 1 (bar/s 1 (text Mode) ModeList)))) (bar/e 24 (bg (g 42) (full/x (pad/x 1 (bar/s 1 (text Mode) ModeList))))
(bg (g 34) (full/x (pad/x 1 (bar/s 1 (text History) HistoryCount))))))) (bg (g 34) (full/x (pad/x 1 (bar/s 1 (text History) HistoryCount)))))))
(keys Global (@escape leave) (bind Global (@escape leave)
(@f1 (enter Help)) (@f1 (enter Help))
(@f2 (enter Rename)) (@f2 (enter Rename))
(@f3 (project new)) (@f3 (project new))
@ -89,43 +122,33 @@
(@f10 (enter Quit)) (@f10 (enter Quit))
(@f12 (enter Devtools))) (@f12 (enter Devtools)))
(keys Confirm (@enter confirm)) (bind Confirm (@enter confirm))
(keys Page (@pgup page/up) (@pgdn page/down)) (bind Page (@pgup page/up) (@pgdn page/down))
(keys Delete (@delete delete) (@backspace delete/back)) (bind Delete (@delete delete) (@backspace delete/back))
(keys History (@u (undo 1)) (@r (redo 1))) (bind History (@u (undo 1)) (@r (redo 1)))
(keys X (@left (axis (dec X))) (@right (axis (inc X))))
(keys Y (@up (axis (dec Y))) (@down (axis (inc Y))))
(keys Z (@minus (axis (dec Z))) (@equal (axis (inc Z))))
(keys I (@comma (axis (dec I))) (@period (axis (inc Z))))
(keys W (@openbracket (axis (dec W))) (@closebracket (axis (inc W))))
(keys XFine (@shift/left (axis (dec XFine))) (@shift/right (axis (inc XFine))))
(keys YFine (@shift/up (axis (dec YFine))) (@shift/down (axis (inc YFine))))
(keys ZFine (@underscore (axis (dec ZFine))) (@plus (axis (inc ZFine))))
(keys IFine (@lt (axis (dec IFine))) (@gt (axis (inc ZFine))))
(keys WFine (@openbrace (axis (dec WFine))) (@closebrace (axis (inc WFine))))
(keys Clock (@space (clock/toggle 0)) (@shift/space (clock/toggle 0))) (bind Clock (@space (clock/toggle 0)) (@shift/space (clock/toggle 0)))
(keys Scenes (@shift/S (scenes add)) (@s (scenes select)) (@up (scenes prev)) (@down (scenes next))) (bind Scenes (@shift/S (scenes add)) (@s (scenes select)))
(keys Tracks (@shift/T (tracks add)) (@t (tracks select)) (@left (tracks prev)) (@right (tracks next))) (bind Tracks (@shift/T (tracks add)) (@t (tracks select)))
(keys Clip (@l (toggle :loop)) (@tab (clip upsert) (edit toggle))) (bind Clip (@l (toggle :loop)) (@tab (clip upsert) (edit toggle)))
(keys Color (@c (color))) (bind Color (@c (color)))
(keys Launch (@q (launch))) (bind Launch (@q (launch)))
(keys EditorView (see X XFine Z ZFine) (@z toggle :lock)) (bind EditorView (see X XFine Z ZFine) (@z toggle :lock))
(keys EditorAdd (@a editor/append :true) (@enter editor/append :false)) (bind EditorAdd (@a editor/append :true) (@enter editor/append :false))
(keys EditorDel (@del editor/delete/note) (@shift/del editor/delete/note)) (bind EditorDel (@del editor/delete/note) (@shift/del editor/delete/note))
(keys Sequencer (see Color Launch) (@shift/I input/add) (@shift/O output/add)) (bind Sequencer (see Color Launch) (@shift/I input/add) (@shift/O output/add))
(keys SamplerRec (@r sampler/record/toggle SelectedSample) (@shift/R sampler/record/back)) (bind SamplerRec (@r sampler/record/toggle SelectedSample) (@shift/R sampler/record/back))
(keys SamplerPlay (@p sampler/play/sample SelectedSample) (@P sampler/stop/sample SelectedSample)) (bind SamplerPlay (@p sampler/play/sample SelectedSample) (@P sampler/stop/sample SelectedSample))
(keys SamplerImpEx (@shift/f6 dialog :export/sample) (@shift/f9 dialog Import/sample)) (bind SamplerImpEx (@shift/f6 dialog :export/sample) (@shift/f9 dialog Import/sample))
(keys SamplerDirs (@up sampler/select :sample/above) (@down sampler/select :sample/below) (@left sampler/select :sample/to/left) (@right sampler/select :sample/to/right)) (bind SamplerDirs (@up sampler/select :sample/above) (@down sampler/select :sample/below) (@left sampler/select :sample/to/left) (@right sampler/select :sample/to/right))
(keys Track (see Color Launch Z ZFine Delete) (@r toggle :rec) (@m toggle :mon) (@p toggle :play) (@P toggle :solo)) (bind Track (see Color Launch Z ZFine Delete) (@r toggle :rec) (@m toggle :mon) (@p toggle :play) (@P toggle :solo))
(keys Scene (see Color Launch Z ZFine Delete)) (bind Scene (see Color Launch Z ZFine Delete))
(keys Input (see X Delete) (:char input)) (bind Input (see X Delete) (:char input))
(keys List (see Y Confirm)) (bind List (see Y Confirm))
(keys Length (see X Y Confirm)) (bind Length (see X Y Confirm))
(keys Browse (see List Input)) (bind Browse (see List Input))
(keys Pool (see Y W ZFine Color Delete) (@n rename/begin) (@t length/begin) (@m import/begin) (@x export/begin) (@shift/A clip/add :after :newClip) (@shift/D clip/add :after :clonedClip)) (bind Pool (see Y W ZFine Color Delete) (@n rename/begin) (@t length/begin) (@m import/begin) (@x export/begin) (@shift/A clip/add :after :newClip) (@shift/D clip/add :after :clonedClip))

View file

@ -95,7 +95,7 @@ fn run_new_plain (config: Config) -> Usually<()> {
/// Total application state. /// Total application state.
/// ///
/// ``` /// ```
/// use tek::{HasTracks, HasScenes, TracksView, ScenesView}; /// use tek::{HasTracks, HasScenes, TracksView, ScenesView, ScenesControl};
/// let mut app = tek::App::default(); /// let mut app = tek::App::default();
/// let _ = app.scenes_add_one(None, None).unwrap(); /// let _ = app.scenes_add_one(None, None).unwrap();
/// let _ = app.update_clock(); /// let _ = app.update_clock();

View file

@ -2,16 +2,20 @@ use crate::*;
/// Collection of input bindings. /// Collection of input bindings.
#[derive(Default, Clone, Debug)] #[derive(Default, Clone, Debug)]
pub struct Binds(pub Arc<RwLock<BTreeMap< pub struct Binds(pub Arc<RwLock<BTreeMap<Arc<str>, Arc<Bind<TuiEvent, Arc<str>>>>>>);
Arc<str>,
Arc<Bind<TuiEvent, Arc<str>>>
>>>);
impl Binds { impl Binds {
pub fn add <'a> (&self, expr: impl Language) -> UsuallyRef<'a, ()> { pub fn add_keys <'a> (&self, 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()?.unwrap_or(""); let body = expr.tail()?.unwrap_or("");
self.0.try_write().unwrap().insert(name.into(), Bind::load(body)?.into()); self.0.try_write().unwrap().insert(name.into(), Bind::load_keys(body)?.into());
Ok(())
}
pub fn add_axis <'a> (&self, expr: impl Language) -> UsuallyRef<'a, ()> {
let name = expr.head()?.ok_or("axis: missing name")?;
let body = expr.tail()?.unwrap_or("");
self.0.try_write().unwrap().insert(name.into(), Bind::load_axis(name, body)?.into());
Ok(()) Ok(())
} }
@ -36,11 +40,10 @@ impl Binds {
/// ///
/// ``` /// ```
/// use ::{std::sync::Arc, tek::{Bind, tengri::TuiEvent}}; /// use ::{std::sync::Arc, tek::{Bind, tengri::TuiEvent}};
/// let bind = Bind::<TuiEvent, Arc<str>>::load(&stringify!( /// let src = stringify!((@x (nop)) (@y (nop) (nop)));
/// (@x (nop)) /// let bind = Bind::<TuiEvent, Arc<str>>::load_keys(&src).unwrap();
/// (@y (nop) (nop)))).unwrap();
/// assert_eq!(bind.query(&'x'.into()).map(|x|x.len()), Some(1)); /// assert_eq!(bind.query(&'x'.into()).map(|x|x.len()), Some(1));
/// //assert_eq!(bind.query(&'y'.into()).map(|x|x.len()), Some(2)); /// assert_eq!(bind.query(&'y'.into()).map(|x|x.len()), Some(2));
/// ``` /// ```
#[derive(Debug)] #[derive(Debug)]
pub struct Bind<E, C>( pub struct Bind<E, C>(
@ -64,14 +67,6 @@ pub struct Bind<E, C>(
pub source: Option<Arc<PathBuf>>, pub source: Option<Arc<PathBuf>>,
} }
/// Condition that must evaluate to true in order to enable an input layer.
///
/// ```
/// let condition = tek::Condition(std::sync::Arc::new(Box::new(||{true})));
/// ```
#[derive(Clone)]
pub struct Condition(pub Arc<Box<dyn Fn()->bool + Send + Sync>>);
/// Default is always empty map regardless if `E` and `C` implement [Default]. /// Default is always empty map regardless if `E` and `C` implement [Default].
impl<E, C> Default for Bind<E, C> { impl<E, C> Default for Bind<E, C> {
fn default () -> Self { Self(Default::default()) } fn default () -> Self { Self(Default::default()) }
@ -89,14 +84,28 @@ impl<C: Default> Default for Binding<C> {
} }
impl Bind<TuiEvent, Arc<str>> { impl Bind<TuiEvent, Arc<str>> {
pub fn load (body: impl Language) -> Usually<Self> { pub fn load_keys (body: impl Language) -> Usually<Self> {
let mut bind: Bind<TuiEvent, Arc<str>> = Bind::new(); let mut bind: Bind<TuiEvent, Arc<str>> = Bind::new();
body.each((), |_, item: &str|if matches!(item.expr().head(), Ok(Some("see"))) { body.each((), |_, item: &str|if matches!(item.expr().head(), Ok(Some("see"))) {
bind.add_ref(item) bind.add_ref(item)
} else if let Ok(Some(_word)) = item.expr().head().word() { } else if let Ok(Some(_word)) = item.expr().head().word() {
bind.add_def(item) bind.add_def(item)
} else { } else {
return err!("Binds::add: unexpected: {item:?}") return err!("Bind::load_keys: unexpected: {item:?}")
})?;
Ok(bind)
}
pub fn load_axis (name: &str, body: impl Language) -> Usually<Self> {
let mut bind: Bind<TuiEvent, Arc<str>> = Bind::new();
body.each(0, |index, key: &str|if index == 0 {
bind.add_def(&format!("({key} (axis (inc {name})))"))?;
Ok(1)
} else if index == 1 {
bind.add_def(&format!("({key} (axis (dec {name})))"))?;
Ok(2)
} else {
err!("top-level (axis) takes 2 keysyms: inc and dec")
})?; })?;
Ok(bind) Ok(bind)
} }
@ -129,7 +138,6 @@ impl Bind<TuiEvent, Arc<str>> {
} }
impl<E: Clone + Ord, C> Bind<E, C> { impl<E: Clone + Ord, C> Bind<E, C> {
/// Create a new event map /// Create a new event map
pub fn new () -> Self { pub fn new () -> Self {
Default::default() Default::default()
@ -159,7 +167,17 @@ impl<E: Clone + Ord, C> Bind<E, C> {
} }
} }
impl_debug!(Condition |self, w| { write!(w, "*") }); /// Condition that must evaluate to true in order to enable an input binding.
///
/// ```
/// let condition = tek::Condition(std::sync::Arc::new(Box::new(||{true})));
/// ```
#[derive(Clone)]
pub struct Condition(pub Arc<Box<dyn Fn()->bool + Send + Sync>>);
impl_debug!(Condition |self, w| {
write!(w, "*")
});
//take!(DeviceCmd|state: Project, iter|state.selected_device().as_ref() //take!(DeviceCmd|state: Project, iter|state.selected_device().as_ref()
//.map(|t|Take::take(t, iter)).transpose().map(|x|x.flatten())); //.map(|t|Take::take(t, iter)).transpose().map(|x|x.flatten()));

View file

@ -7,11 +7,11 @@ use notify_debouncer_full::notify::{RecursiveMode, PollWatcher, Config as Notify
/// ``` /// ```
/// use ::{std::sync::Arc, tek::{Config, Modes, Mode}}; /// use ::{std::sync::Arc, tek::{Config, Modes, Mode}};
/// let config: Config = Default::default(); /// let config: Config = Default::default();
/// let mode = stringify!(mode :menu (name Menu) (info Mode selector.) (keys Y Confirm) /// let mode = stringify!(mode Menu (name Menu) (info Mode selector.) (keys Y Confirm)
/// (view (bg (g 0) (bsp/s PortsOut (bsp/n PortsIn /// (view (bg (g 0) (bsp/s PortsOut (bsp/n PortsIn
/// (bg (g 30) (bsp/s (exact/y 7 Logo) (fill Dialog)))))))); /// (bg (g 30) (bsp/s (exact/y 7 Logo) (fill Dialog))))))));
/// Modes::add(&config.modes, mode).unwrap(); /// Modes::add(&config.modes, mode).unwrap();
/// let mode: Arc<Mode> = config.get_mode(":menu").unwrap(); /// let mode: Arc<Mode> = config.get_mode("Menu").unwrap();
/// ``` /// ```
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct Config { pub struct Config {
@ -56,9 +56,10 @@ impl Config {
pub fn load_item <C: AsRef<Config>, L: Language> (config: C, src: L) -> Usually<C> { pub fn load_item <C: AsRef<Config>, L: Language> (config: C, src: L) -> Usually<C> {
if let Some(expr) = src.expr()? { if let Some(expr) = src.expr()? {
match expr.head()? { match expr.head()? {
Some("mode") => Modes::add(&config.as_ref().modes, expr.tail()), Some("mode") => config.as_ref().modes.add(expr.tail()),
Some("keys") => config.as_ref().binds.add(expr.tail()), Some("view") => config.as_ref().views.add(expr.tail()),
Some("view") => Views::add(&config.as_ref().views, expr.tail()), Some("bind") => config.as_ref().binds.add_keys(expr.tail()),
Some("axis") => config.as_ref().binds.add_axis(expr.tail()),
_ => return Err(format!("Config::load: expected view/keys/mode expr, got: {src:?}").into()) _ => return Err(format!("Config::load: expected view/keys/mode expr, got: {src:?}").into())
}?; }?;
Ok(config) Ok(config)

View file

@ -738,6 +738,7 @@ impl<T> BrowseControl for T where T:
#[tek_proc::commands(AxisCmd)] impl App { #[tek_proc::commands(AxisCmd)] impl App {
/// Increment the given data axis. /// Increment the given data axis.
#[command(Inc = "inc")] pub fn inc (&mut self, axis: ControlAxis) -> Perhaps<AxisCmd> { #[command(Inc = "inc")] pub fn inc (&mut self, axis: ControlAxis) -> Perhaps<AxisCmd> {
panic!("inc {axis:?}");
Ok(match (&self.dialog, axis) { Ok(match (&self.dialog, axis) {
(Dialog::None, _) => todo!(), (Dialog::None, _) => todo!(),
(Dialog::Menu(_, _), ControlAxis::Y) => { (Dialog::Menu(_, _), ControlAxis::Y) => {
@ -749,6 +750,7 @@ impl<T> BrowseControl for T where T:
} }
/// Decrement the given data axis. /// Decrement the given data axis.
#[command(Dec = "dec")] pub fn dec (&mut self, axis: ControlAxis) -> Perhaps<AxisCmd> { #[command(Dec = "dec")] pub fn dec (&mut self, axis: ControlAxis) -> Perhaps<AxisCmd> {
panic!("dec {axis:?}");
Ok(match (&self.dialog, axis) { Ok(match (&self.dialog, axis) {
(Dialog::None, _) => None, (Dialog::None, _) => None,
(Dialog::Menu(_, _), ControlAxis::Y) => { (Dialog::Menu(_, _), ControlAxis::Y) => {

View file

@ -58,7 +58,6 @@ pub struct Mode {
} }
impl Mode { impl Mode {
/// Add a definition to the mode. /// Add a definition to the mode.
/// ///
/// Supported definitions: /// Supported definitions:
@ -86,12 +85,29 @@ impl Mode {
modes.0.write().insert(name.into(), Arc::new(submode)); modes.0.write().insert(name.into(), Arc::new(submode));
mode mode
}, },
"keys" => { "bind" => {
tail.each(mode, |mut mode: T, expr: &str|{ tail.each(mode, |mut mode: T, expr: &str|{
mode.as_mut().keys.push(expr.trim().into()); mode.as_mut().keys.push(expr.trim().into());
Ok(mode) Ok(mode)
})? })?
}, },
"axis" => {
let mut ctrl: Option<Arc<str>> = None;
let mut data: Option<Arc<str>> = None;
tail.each(0, |index: usize, src: &str|if index == 0 {
ctrl = Some(src.into());
Ok(1)
} else if index == 1 {
data = Some(src.into());
Ok(2)
} else {
err!("(mode (axis)) takes 2 keysyms, control and data")
})?;
// TODO: get inc/dec keys from axis?
//mode.as_mut().keys.push(format!("({inc} (inc ({axis})))").into());
//mode.as_mut().keys.push(format!("({dec} (dec ({axis})))").into());
mode
},
"name" => { mode.as_mut().name.push(tail.into()); mode }, "name" => { mode.as_mut().name.push(tail.into()); mode },
"info" => { mode.as_mut().info.push(tail.into()); mode }, "info" => { mode.as_mut().info.push(tail.into()); mode },
"view" => { mode.as_mut().view.push(View::new(tail)?.into()); mode }, "view" => { mode.as_mut().view.push(View::new(tail)?.into()); mode },
@ -104,7 +120,6 @@ impl Mode {
return Err(format!("Mode::add: unexpected: {dsl:?}").into()); return Err(format!("Mode::add: unexpected: {dsl:?}").into());
}) })
} }
} }
impl AsMut<Mode> for Mode { impl AsMut<Mode> for Mode {

View file

@ -86,8 +86,8 @@ pub struct Views(pub Arc<RwLock<BTreeMap<
impl Views { impl Views {
/// Load custom view definition. /// Load custom view definition.
pub fn add <'a> (views: &Views, expr: impl Language) -> UsuallyRef<'a, ()> { pub fn add <'a> (&self, expr: impl Language) -> UsuallyRef<'a, ()> {
views.0.write().insert( self.0.write().insert(
expr.head()?.ok_or("view: missing name")?.into(), expr.head()?.ok_or("view: missing name")?.into(),
View::new(expr.tail()?.ok_or("view: missing body")?)?.into() View::new(expr.tail()?.ok_or("view: missing body")?)?.into()
); );
@ -373,6 +373,7 @@ impl View<Tui, App> {
Some("Beat") => view!(), Some("Beat") => view!(),
Some("Bpm") => view!(), Some("Bpm") => view!(),
Some("BrowseTitle") => view!(|app|view_browse_title(app)), Some("BrowseTitle") => view!(|app|view_browse_title(app)),
Some("Config") => view!(|app|view_config(app)),
Some("Debug") => Box::new(move|_, to|format!("[{:?}]", to.area()).exact_h(1).draw(to)), Some("Debug") => Box::new(move|_, to|format!("[{:?}]", to.area()).exact_h(1).draw(to)),
Some("Device") => view!(|app|view_device(app)), Some("Device") => view!(|app|view_device(app)),
Some("Menu") => view!(|app|view_menu(app)), Some("Menu") => view!(|app|view_menu(app)),
@ -381,6 +382,7 @@ impl View<Tui, App> {
Some("MetersOut") => view!(|___|bg(Rgb(30, 30, 30), "Output Meters".align_s().full_h())), Some("MetersOut") => view!(|___|bg(Rgb(30, 30, 30), "Output Meters".align_s().full_h())),
Some("ModeList") => view!(|app|view_modes(app)), Some("ModeList") => view!(|app|view_modes(app)),
Some("HistoryCount") => view!(|app|app.history.len()), Some("HistoryCount") => view!(|app|app.history.len()),
Some("HistoryList") => view!(|app|view_history_list(app)),
Some("ScenesClips") => view!(|app|view_scenes_clips(app)), Some("ScenesClips") => view!(|app|view_scenes_clips(app)),
Some("ScenesNames") => view!(|app|app.view_scenes_names()), Some("ScenesNames") => view!(|app|app.view_scenes_names()),
Some("Sessions") => view!(|___|view_sessions()), Some("Sessions") => view!(|___|view_sessions()),
@ -426,6 +428,58 @@ pub fn view_menu (app: &App) -> impl Draw<Tui> {
} }
} }
pub fn view_history_list (app: &App) -> impl Draw<Tui> {
above(
Outer(true, Style::default().fg(Color::Rgb(0, 192, 0))),
draw(|to: &mut Tui|{
let area = to.area();
let XYWH(x0, mut y0, w, h) = area;
for (done, undo) in app.history.iter().rev() {
to.clip(XYWH(x0 + 1, y0, w, 1), &|to|format!("{done:?}").draw(to))?;
y0 += 1;
}
Ok(Some(area))
})
)
}
pub fn view_config (app: &App) -> impl Draw<Tui> {
above(
Outer(true, Style::default().fg(Color::Rgb(0, 192, 0))),
draw(|to: &mut Tui|{
let area = to.area();
let XYWH(x0, mut y0, w, h) = area;
for key in app.modes.read().iter().rev() {
if let Some(mode) = app.config.modes.get(key) {
to.clip(XYWH(x0 + 1, y0, w, 1), &|to|Split::East.bar(16, key, mode.info.get(0)).draw(to))?;
y0 += 1;
for bind in mode.keys.iter() {
to.clip(XYWH(x0 + 3, y0, w, 1), &|to|bind.draw(to))?;
y0 += 1;
if let Some(bind) = app.config.binds.find(bind) {
for (key, binding) in bind.0.iter() {
to.clip(XYWH(x0 + 5, y0, 40, 1),
&|to|Split::East.bar(20, format!("{:?}", key.0), "").draw(to))?;
y0 += 1;
}
}
}
y0 += 1;
}
}
//for (index, (key, mode)) in app.config.modes.0.read().iter().enumerate() {
//to.clip(XYWH(x0, y0 + index as u16, w, 1), &|to|Split::East.bar(16,
//key, mode.info.get(0),
//).draw(to))?;
//}
Ok(Some(area))
})
)
}
pub fn view_config_mode (mode: &Mode) -> impl Draw<Tui> {
}
/// ``` /// ```
/// let _ = tek::button_play_pause(true, true); /// let _ = tek::button_play_pause(true, true);
/// let _ = tek::button_play_pause(true, false); /// let _ = tek::button_play_pause(true, false);

2
tengri

@ -1 +1 @@
Subproject commit 1e8b18134d8ad234e5d0142d706f21f1366668d0 Subproject commit 50c0ed1c66c9db97b83b95eb23a7b0bf00c5a760