diff --git a/src/conf/lang.rs b/src/conf/lang.rs index b214a984..ab61ea39 100644 --- a/src/conf/lang.rs +++ b/src/conf/lang.rs @@ -505,14 +505,15 @@ pub fn tek_vec_arc_str <'a> (_: &App, _: impl Language) -> Perhaps> } /// Switch to another set of contextual modes. #[command(Mode = "mode")] pub fn cmd_mode (&mut self, modes: Vec>) -> Perhaps { - *self.mode_stack().write() = modes.into(); + *self.stack().write() = modes.into_iter().map(Into::into).collect(); Ok(None) } /// Enter a contextual mode. #[command(Enter = "enter")] pub fn enter_mode (&mut self, mode: Arc) -> Perhaps { - let mut modes = self.mode_stack().write(); - Ok(if modes.last() != Some(&mode) { - modes.push(mode); + let mut modes = self.stack().write(); + Ok(if modes.last().map(|mode|mode.name.as_ref()) != Some(mode.as_ref()) { + let data = Default::default(); + modes.push(Frame::new(mode, data)); Some(AppCmd::Leave) } else { None @@ -520,37 +521,35 @@ pub fn tek_vec_arc_str <'a> (_: &App, _: impl Language) -> Perhaps> } /// Leave a contextual mode. #[command(Leave = "leave")] fn leave_mode (&mut self) -> Perhaps { - let mut modes = self.mode_stack().write(); + let mut modes = self.stack().write(); if modes.len() > 1 { let mode = modes.pop(); - Ok(mode.map(|mode|AppCmd::Enter { mode })) + Ok(mode.map(|mode|AppCmd::Enter { mode: mode.name })) } else { Ok(None) } } /// Increment the given data axis. #[command(Inc = "inc")] pub fn inc (&mut self, axis: Arc) -> Perhaps { - panic!("inc {axis:?}"); - //Ok(match (&self.dialog, axis) { - //(Dialog::None, _) => todo!(), - //(Dialog::Menu(_, _), ControlAxis::Y) => { - //AppCmd::Dialog { dialog: self.dialog.menu_next() }.dispatch(self)?; - //None - //}, - //_ => todo!() - //}) + if let Some(mode) = self.stack().write().last_mut() { + if mode.data.contains_key(&axis) { + *mode.data.get_mut(&axis).unwrap() += 1; + } else { + return err!("invalid axis {axis} for mode {}", &mode.name) + } + } + Ok(None) } /// Decrement the given data axis. #[command(Dec = "dec")] pub fn dec (&mut self, axis: Arc) -> Perhaps { - panic!("dec {axis:?}"); - //Ok(match (&self.dialog, axis) { - //(Dialog::None, _) => None, - //(Dialog::Menu(_, _), ControlAxis::Y) => { - //AppCmd::Dialog { dialog: self.dialog.menu_prev() }.dispatch(self)?; - //None - //}, - //_ => todo!() - //}) + if let Some(mode) = self.stack().write().last_mut() { + if mode.data.contains_key(&axis) { + *mode.data.get_mut(&axis).unwrap() -= 1; + } else { + return err!("invalid axis {axis} for mode {}", &mode.name) + } + } + Ok(None) } /// Cancel current selection. #[command(Cancel = "cancel")] pub fn cmd_cancel (&mut self) -> Perhaps { diff --git a/src/conf/mode.rs b/src/conf/mode.rs index 7662dd81..0f5199b2 100644 --- a/src/conf/mode.rs +++ b/src/conf/mode.rs @@ -1,20 +1,5 @@ use crate::*; -/// Has a stack of currently active interaction modes. -pub trait HasModeStack { - /// Stack of active modes. - fn mode_stack (&self) -> &RwLock>>; - - fn mode_stack_push (&self, mode: impl AsRef) { - self.mode_stack().write().push(mode.as_ref().into()) - } -} -impl HasModeStack for App { - fn mode_stack (&self) -> &RwLock>> { - &self.stack - } -} - /// Collection of UI modes. /// /// ``` @@ -37,13 +22,11 @@ pub trait HasModes { /// /// ``` /// # use ::{std::sync::Arc, tek::*}; - /// let config: Config = Default::default(); - /// + /// let config = Config::default(); /// config.modes_add(stringify!(Menu /// (name Menu) (info Mode selector.) (axis Y Index) (bind Confirm) /// (view (bg (g 30) (bar/s 7 Logo (full/xy Dialog)))) /// )).unwrap(); - /// /// let _: Arc = config.modes_get("Menu").unwrap(); /// ``` fn modes_add <'a> (&self, expr: impl Language) -> UsuallyRef<'a, ()> { @@ -104,6 +87,7 @@ pub struct Mode { pub view: Vec>>, pub keys: Vec>, pub axis: BTreeMap, Arc>, + pub data: BTreeMap, usize>, pub modes: Arc, Arc>>>, } @@ -128,6 +112,7 @@ impl Mode { "self" => self.add_mode(tail)?, "bind" => self.add_bind(tail)?, "axis" => self.add_axis(tail)?, + "data" => self.add_data(tail)?, "name" => { self.name.push(tail.into()); self }, "info" => { self.info.push(tail.into()); self }, "view" => { self.view.push(View::new(tail)?.into()); self }, @@ -167,13 +152,33 @@ impl Mode { param = Some(src.into()); Ok(2) } else { - err!("expected (mode (axis [Name] [command] [command])), got: {tail}") + err!("mode: expected (axis Name Param), got: {tail}") })?; if let Some(axis) = axis && let Some(param) = param { self.axis.insert(axis.into(), param.into()); Ok(self) } else { - err!("mode: expected (axis X Param), got: {tail}") + err!("mode: expected (axis Name Param), got: {tail}") + } + } + + fn add_data (&mut self, tail: &str) -> Usually<&mut Self> { + let mut param: Option> = None; + let mut value: Option = None; + tail.each(0, |index: usize, src: &str|if index == 0 { + param = Some(src.into()); + Ok(1) + } else if index == 1 { + value = Some(src.parse::()?); + Ok(2) + } else { + err!("mode: expected (data Param Value), got: {tail}") + })?; + if let Some(param) = param && let Some(value) = value { + self.data.insert(param.into(), value.into()); + Ok(self) + } else { + err!("mode: expected (data Param Value), got: {tail}") } } diff --git a/src/conf/stack.rs b/src/conf/stack.rs new file mode 100644 index 00000000..07b6eb4d --- /dev/null +++ b/src/conf/stack.rs @@ -0,0 +1,38 @@ +use crate::*; + +/// Has a stack of currently active interaction modes. +pub trait HasStack { + /// Stack of active modes. + fn stack (&self) -> &RwLock>; + + fn stack_push (&self, mode: impl AsRef) { + self.stack().write().push(mode.as_ref().into()) + } +} + +impl HasStack for App { + fn stack (&self) -> &RwLock> { + &self.stack + } +} + +#[derive(Default, Debug)] +pub struct Frame { + pub name: Arc, + pub data: BTreeMap, usize>, +} + +impl Frame { + pub fn new (name: impl AsRef, data: Option, usize>>) -> Self { + Self { + name: name.as_ref().into(), + data: data.unwrap_or_default(), + } + } +} + +impl> From for Frame { + fn from (name: T) -> Self { + Self::new(name, Default::default()) + } +} diff --git a/src/conf/view.rs b/src/conf/view.rs index 0ba487b6..21b25ffc 100644 --- a/src/conf/view.rs +++ b/src/conf/view.rs @@ -66,7 +66,7 @@ pub fn draw_error (to: &mut Tui, error: &RwLock>>) -> Usually<() } pub fn view_debug ( - stack: &Vec>, + stack: &Vec, perf: &PerfModel, size: &Sizer, ) -> impl Draw { @@ -81,7 +81,7 @@ pub fn view_debug ( size.1.load(Relaxed)).align_w(), ).align_se(), //() - format!("{:?}", stack).align_sw(), + format!("{:?}", stack.iter().map(|mode|mode.name.as_ref()).collect::>()).align_sw(), ) } @@ -104,12 +104,12 @@ pub fn view_status <'a> (sel: Option<&str>, sr: &str, buf: &str, lat: &str) -> i pub fn draw_status_bar (to: &mut Tui, state: &App) -> Drawn { bar(Split::South, 1u16, dim(true, "Mode"), - bold(true, state.mode_stack().read().get(0)) + bold(true, state.stack().read().get(0).map(|mode|mode.name.as_ref())) ).push_x(1).draw(to) } -pub fn draw_mode_stack (to: &mut Tui, state: &App) -> Usually<()> { - for name in state.mode_stack().read().iter() { +pub fn draw_stack (to: &mut Tui, state: &App) -> Usually<()> { + for Frame { name, data } in state.stack().read().iter() { let mut error = false; //if state.watch.config.modes_count() > 0 { //panic!("{name:?} {:?}", state.watch.config.modes); @@ -181,10 +181,10 @@ pub fn view_config (app: &App) -> impl Draw { let area = to.area(); let XYWH(x0, y0, w, h) = area; let mut y = 0; - for key in app.stack.read().iter().rev() { - if let Some(mode) = app.modes_get(key) { + for frame in app.stack.read().iter().rev() { + if let Some(mode) = app.modes_get(&frame.name) { to.clip(XYWH(x0 + 1, y0 + y, w, 1), &|to|Split::East.bar(16, - key, + &frame.name, mode.info.get(0)).draw(to))?; next_line!(h, y, 1); for (ctrl, data) in mode.axis.iter() { @@ -197,7 +197,7 @@ pub fn view_config (app: &App) -> impl Draw { to.clip(XYWH(x0 + 3, y0 + y, w, 1), &|to|bind.draw(to))?; next_line!(h, y, 1); if let Some(bind) = app.binds_find(bind) { - for (key, binding) in bind.0.iter() { + for (key, _binding) in bind.0.iter() { to.clip(XYWH(x0 + 5, y0 + y, 40, 1), &|to|Split::East.bar(40, format!("{}", key), //binding.description.get(0) "").draw(to))?; @@ -672,14 +672,14 @@ fn iter_scenes_with_sizes <'s, T> (state: &'s T) -> impl ScenesSizes<'s> where T }) } -pub fn view_modes <'a, S: HasModeStack> (state: &'a S) -> impl Draw + 'a { +pub fn view_modes <'a, S: HasStack> (state: &'a S) -> impl Draw + 'a { draw(move|to: &mut Tui|{ let XYWH(x, y, w, h) = to.area(); let mut x_next = x; let mut w_used = 0u16; - for item in state.mode_stack().read().iter() { + for frame in state.stack().read().iter() { let area = XYWH(x_next, y, w.minus(w_used), h); - if let Some(used) = to.draw(area, item)? { + if let Some(used) = to.draw(area, &frame.name)? { let used_w = used.w() + 1; x_next = x_next.plus(used_w); w_used = w_used.plus(used_w); diff --git a/src/tek.edn b/src/tek.edn index 0d26225d..b33b2de4 100644 --- a/src/tek.edn +++ b/src/tek.edn @@ -11,22 +11,26 @@ (axis IFine @lt @gt ) (axis WFine @openbrace @closebrace ) -(mode Menu - (name Main Menu) - (info Welcome to Tek!) - (axis Y Index) +(mode Menu (name Main Menu) (info Welcome to Tek!) + (axis Y Index) (data Index 1) (bind Confirm) (view (bar/s 2 Transport (bar/n 2 StatusBar (align/c (bar/s 2 (align/x Logo) Menu)))))) (mode Help - (axis Y Index) - (axis P Page) + (axis Y Index) (data Index 0) + (axis P Page) (data Page 0) (view (bg (g 32) (fg (g 64) (full/xy (align/c (bg (g 48) (fg (g 224) (bar/s 2 (text Help yourself! ) (align/c Config)))))))))) +(mode Options + (axis Y Index) (data Index 0) + (axis P Page) (data Page 0) + (view (bg (g 32) (fg (g 64) (full/xy + (bg (g 48) (fg (g 224) (align/c (expand/wh 2 2 (text Options )))))))))) + (mode History - (axis Y Index) - (axis P Page) + (axis Y Index) (data Index 0) + (axis P Page) (data Page 0) (view (bg (g 32) (fg (g 64) (full/xy (bg (g 48) (fg (g 224) (align/c (bar/s 1 (text Project history ) HistoryList))))))))) @@ -37,35 +41,30 @@ (bsp/e (push/x 1 (text Yes)) (align/ne (pull/x 2 (text No)))))))))))))) (mode Save - (axis X Input) - (axis Y Index) + (axis X Input) (data Input 0) + (axis Y Index) (data Index 0) (view (bg (g 32) (fg (g 64) (full/xy (bg (g 48) (fg (g 224) (align/c (bar/s 1 (expand/wh 2 2 (text Save to: )) (text )))))))))) (mode Load - (axis X Input) - (axis Y Index) + (axis X Input) (data Input 0) + (axis Y Index) (data Index 0) (view (bg (g 32) (fg (g 64) (full/xy (bg (g 48) (fg (g 224) (align/c (bar/s 1 (expand/wh 2 2 (text Load from: )) (text )))))))))) (mode Rename - (axis X Input) - (axis Y Index) + (axis X Input) (data Input 0) + (axis Y Index) (data Index 0) (view (bg (g 32) (fg (g 64) (full/xy (bg (g 48) (fg (g 224) (align/c (expand/wh 2 2 (text Rename item )))))))))) -(mode Options - (axis Y Index) - (view (bg (g 32) (fg (g 64) (full/xy - (bg (g 48) (fg (g 224) (align/c (expand/wh 2 2 (text Options )))))))))) - (mode Transport (name Transport) (info JACK transport controller.) (bind Clock Global) (view Transport)) (mode Arranger (name Arranger) (info Launch grid.) - (axis X Track) - (axis Y Scene) + (axis X Track) (data Track 1) + (axis Y Scene) (data Scene 1) (bind Global Clock Tracks Scenes Clip Launch Color History) (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) @@ -78,8 +77,8 @@ (bar/w 20 (text) (bar/e 16 ScenesNames ScenesClips)))))) (mode Edit (info MIDI editor.) - (axis X Time) - (axis Y Pitch)) + (axis X Time) (data Time 0) + (axis Y Pitch) (data Pitch 36)) (mode Sequencer (info MIDI sequencer.) (bind Global Clock I IFine Y Page EditorView EditorAdd EditorDel) diff --git a/src/tek.rs b/src/tek.rs index e4ee1797..a105100a 100644 --- a/src/tek.rs +++ b/src/tek.rs @@ -102,10 +102,10 @@ pub struct App { pub size: Sizer, /// Performance counter pub perf: PerfModel, - /// Available view modes and input bindings + /// Configuration watcher pub watch: Arc, /// Currently active modes - pub stack: Arc>>>, + pub stack: Arc>>, /// Undo history pub history: Vec<(AppCmd, Option)>, /// Dialog overlay @@ -132,7 +132,7 @@ impl App { exit: Option, project: Project, watch: impl Into>>, - modes: impl Into>>>, + modes: impl Into>>, ) -> Self { App { exit: exit.unwrap_or_default(), @@ -150,11 +150,12 @@ impl App { pub use self::conf::*; mod conf { use crate::*; - mod axis; pub use self::axis::*; - mod bind; pub use self::bind::*; - mod lang; pub use self::lang::*; - mod mode; pub use self::mode::*; - mod view; pub use self::view::*; + mod axis; pub use self::axis::*; + mod bind; pub use self::bind::*; + mod lang; pub use self::lang::*; + mod mode; pub use self::mode::*; + mod stack; pub use self::stack::*; + mod view; pub use self::view::*; pub trait HasError { fn error (&self) -> &RwLock>; @@ -408,8 +409,12 @@ fn tek_jack_event (state: &mut App, event: JackEvent) { PortRegistration(_id, false) => { /*println!("\rport del: {id}")*/ }, - PortsConnected(_a, _b, true) => { /*println!("\rport conn: {a} {b}")*/ }, - PortsConnected(_a, _b, false) => { /*println!("\rport disc: {a} {b}")*/ }, + PortsConnected(_a, _b, true) => { + /*println!("\rport conn: {a} {b}")*/ + }, + PortsConnected(_a, _b, false) => { + /*println!("\rport disc: {a} {b}")*/ + }, ClientRegistration(_id, true) => {}, ClientRegistration(_id, false) => {}, ThreadInit => {}, @@ -863,7 +868,7 @@ impl Draw for App { #[cfg(feature = "prof2")] profiling::scope!("App::draw"); self.perf.cycle(&mut |_|{ draw_error(to, self.error.as_ref())?; - draw_mode_stack(to, &self)?; + draw_stack(to, &self)?; view_debug(&*self.stack.read(), &self.perf, &self.size).draw(to)?; #[cfg(feature = "prof2")] profiling::finish_frame!(); Ok(Some(to.area().into())) @@ -871,7 +876,7 @@ impl Draw for App { } } -pub trait DispatchBind: HasBinds + HasModeStack + HasModes + Namespace { +pub trait DispatchBind: HasBinds + HasStack + HasModes + Namespace { fn dispatch_bind (&mut self, input: &TuiEvent) -> Usually<()> { if let Some(bindings) = self.dispatch_bind_match(input) { return self.dispatch_bind_exec(bindings) @@ -897,7 +902,7 @@ pub trait DispatchBind: HasBinds + HasModeStack + HasModes + Namespace /// assert!(app.dispatch_bind_match(&"@up".into()).is_none()); /// /// // Push mode onto stack: - /// app.mode_stack_push("Test"); + /// app.stack_push("Test"); /// assert!(app.dispatch_bind_match(&"@left".into()).is_none()); /// assert!(app.dispatch_bind_match(&"@enter".into()).is_some()); /// assert!(app.dispatch_bind_match(&"@escape".into()).is_some()); @@ -905,7 +910,7 @@ pub trait DispatchBind: HasBinds + HasModeStack + HasModes + Namespace /// assert!(app.dispatch_bind_match(&"@up".into()).is_some()); /// ``` fn dispatch_bind_match (&self, input: &TuiEvent) -> Option>]>> { - for name in self.mode_stack().read().iter().rev() { + for Frame { name, data } in self.stack().read().iter().rev() { if let Some(mode) = self.modes_get(name).as_ref().map(Arc::clone) { if let Some(binds) = self.dispatch_bind_match_axis(mode.as_ref(), input) { return Some(binds) @@ -924,7 +929,7 @@ pub trait DispatchBind: HasBinds + HasModeStack + HasModes + Namespace /// )).unwrap(); /// assert!(app.dispatch_bind_match(&"@down".into()).is_none()); /// assert!(app.dispatch_bind_match(&"@up".into()).is_none()); - /// app.mode_stack_push("Test"); + /// app.stack_push("Test"); /// assert!(app.dispatch_bind_match(&"@down".into()).is_some()); /// assert!(app.dispatch_bind_match(&"@up".into()).is_some()); /// ``` @@ -950,7 +955,7 @@ pub trait DispatchBind: HasBinds + HasModeStack + HasModes + Namespace /// )).unwrap(); /// assert!(app.dispatch_bind_match(&"@enter".into()).is_none()); /// assert!(app.dispatch_bind_match(&"@escape".into()).is_none()); - /// app.mode_stack_push("Test"); + /// app.stack_push("Test"); /// assert!(app.dispatch_bind_match(&"@enter".into()).is_some()); /// assert!(app.dispatch_bind_match(&"@escape".into()).is_some()); /// ``` @@ -958,11 +963,11 @@ pub trait DispatchBind: HasBinds + HasModeStack + HasModes + Namespace -> Option>]>> { for id in mode.keys.iter() { - println!("{input:?} {id:?}"); if let Some(event_map) = self.binds_find(id) && let Some(bindings) = event_map.query(input) { return Some(bindings.into()) } + if let Ok(Some(expr)) = id.expr() && let Ok(Some(head)) = expr.head() && *input == head.into()