From 559d2fc4a15de40d196e72445dabdc7fb3f26e59 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sun, 24 Aug 2025 01:44:18 +0300 Subject: [PATCH] menu works lol --- crates/app/app.rs | 28 +++++++++++++++++-- crates/app/app_bind.rs | 59 ++++++++++++++++++++++++++++------------- crates/app/app_data.rs | 1 - crates/app/app_view.rs | 24 ++++++++++++++--- crates/cli/tek.rs | 2 +- crates/config/config.rs | 37 +++++++++++--------------- tek.edn | 46 +++++++++++++++----------------- 7 files changed, 125 insertions(+), 72 deletions(-) diff --git a/crates/app/app.rs b/crates/app/app.rs index f399769f..ad4e0a47 100644 --- a/crates/app/app.rs +++ b/crates/app/app.rs @@ -31,12 +31,18 @@ pub struct App { /// Contains the currently edited musical arrangement pub project: Arrangement, } +#[derive(Debug, Clone, Default, PartialEq)] +pub struct Axis { + min: usize, + max: usize, + step: usize, +} /// Various possible dialog modes. #[derive(Debug, Clone, Default, PartialEq)] pub enum Dialog { #[default] None, Help(usize), - Menu(usize, usize), + Menu(usize, Arc<[Arc]>), Device(usize), Message(Arc), Browse(BrowseTarget, Arc), @@ -62,6 +68,25 @@ impl HasClipsSize for App { fn clips_size (&self) -> &Measure { &self. impl HasTrackScroll for App { fn track_scroll (&self) -> usize { self.project.track_scroll() } } impl HasSceneScroll for App { fn scene_scroll (&self) -> usize { self.project.scene_scroll() } } impl Dialog { + pub fn welcome () -> Self { + Self::Menu(0, [ + "Continue session".into(), + "Load old session".into(), + "Begin new session".into(), + ].into()) + } + pub fn menu_next (&self) -> Self { + match self { + Self::Menu(index, items) => Self::Menu(wrap_inc(*index, items.len()), items.clone()), + _ => Self::None + } + } + pub fn menu_prev (&self) -> Self { + match self { + Self::Menu(index, items) => Self::Menu(wrap_dec(*index, items.len()), items.clone()), + _ => Self::None + } + } pub fn menu_selected (&self) -> Option { if let Self::Menu(selected, _) = self { Some(*selected) } else { None } } @@ -166,7 +191,6 @@ impl App { } } - fn wrap_dialog (dialog: impl Content) -> impl Content { Fixed::xy(70, 23, Tui::fg_bg(Rgb(255,255,255), Rgb(16,16,16), Bsp::b( Repeat(" "), Outer(true, Style::default().fg(Tui::g(96))).enclose(dialog)))) diff --git a/crates/app/app_bind.rs b/crates/app/app_bind.rs index cf11ba45..6abdd449 100644 --- a/crates/app/app_bind.rs +++ b/crates/app/app_bind.rs @@ -30,34 +30,55 @@ handle!(TuiIn:|self: App, input|{ Ok(None) }); +#[derive(Debug, Copy, Clone)] +pub enum Axis { X, Y, Z, I } + impl<'t> DslNs<'t, AppCommand> for App { dsl_exprs!(|app| -> AppCommand { /* TODO */ }); dsl_words!(|app| -> AppCommand { - "y/inc" => match app.dialog { - Dialog::Menu(index, count) => AppCommand::SetDialog { - dialog: Dialog::Menu(if count > 0 { - (index + 1) % count - } else { 0 }, count) - }, - _ => todo!(), - }, - "y/dec" => match app.dialog { - Dialog::Menu(index, count) => AppCommand::SetDialog { - dialog: Dialog::Menu(if count > 0 { - index.overflowing_sub(1).0.min(count.saturating_sub(1)) - } else { 0 }, count) - }, - _ => todo!(), - }, - "confirm" => todo!(), + "x/inc" => AppCommand::Inc { axis: Axis::X }, + "x/dec" => AppCommand::Dec { axis: Axis::X }, + "y/inc" => AppCommand::Inc { axis: Axis::Y }, + "y/dec" => AppCommand::Dec { axis: Axis::Y }, + "confirm" => AppCommand::Confirm, + "cancel" => AppCommand::Cancel, }); } +impl Default for AppCommand { fn default () -> Self { Self::Nop } } + def_command!(AppCommand: |app: App| { - SetDialog { dialog: Dialog } => - swap_value(&mut app.dialog, dialog, |dialog|Self::SetDialog { dialog }), + Nop => Ok(None), + Confirm => todo!(), + Cancel => todo!(), // TODO delegate: + Inc { axis: Axis } => Ok(match (&app.dialog, axis) { + (Dialog::None, _) => todo!(), + (Dialog::Menu(_, _), Axis::Y) => AppCommand::SetDialog { dialog: app.dialog.menu_next() } + .execute(app)?, + _ => todo!() + }), + Dec { axis: Axis } => Ok(match (&app.dialog, axis) { + (Dialog::None, _) => None, + (Dialog::Menu(_, _), Axis::Y) => AppCommand::SetDialog { dialog: app.dialog.menu_prev() } + .execute(app)?, + _ => todo!() + }), + SetDialog { dialog: Dialog } => { + swap_value(&mut app.dialog, dialog, |dialog|Self::SetDialog { dialog }) + }, }); +pub fn wrap_inc (index: usize, count: usize) -> usize { + if count > 0 { (index + 1) % count } else { 0 } +} + +pub fn wrap_dec (index: usize, count: usize) -> usize { + if count > 0 { index.overflowing_sub(1).0.min(count.saturating_sub(1)) } else { 0 } +} + +impl Dialog { +} + //AppCommand => { //("x/inc" / //("stop-all") => todo!(),//app.project.stop_all(), diff --git a/crates/app/app_data.rs b/crates/app/app_data.rs index 8c25c39d..cfb8feb3 100644 --- a/crates/app/app_data.rs +++ b/crates/app/app_data.rs @@ -43,7 +43,6 @@ impl<'t> DslNs<'t, Dialog> for App { ":dialog/device/prev" => Dialog::Device(0), ":dialog/device/next" => Dialog::Device(0), ":dialog/help" => Dialog::Help(0), - ":dialog/menu" => Dialog::Menu(0, 0), ":dialog/save" => Dialog::Browse(BrowseTarget::SaveProject, Browse::new(None).unwrap().into()), ":dialog/load" => Dialog::Browse(BrowseTarget::LoadProject, diff --git a/crates/app/app_view.rs b/crates/app/app_view.rs index 99ad7854..43ac442f 100644 --- a/crates/app/app_view.rs +++ b/crates/app/app_view.rs @@ -1,9 +1,5 @@ use crate::*; -pub fn view_nil (_: &App) -> Box> { - Box::new(Fill::xy("·")) -} - content!(TuiOut:|self: App|Fill::xy(Stack::above(|add|{ for dsl in self.mode.view.iter() { add(&Fill::xy(self.view(dsl.as_ref()))); } }))); @@ -20,6 +16,7 @@ impl App { ViewCache::update_clock(&self.project.clock.view_cache, self.clock(), self.size.w() > 80) } } + fn render_dsl <'t> ( state: &'t impl DslNs<'t, Box>>, src: &str @@ -86,6 +83,21 @@ impl<'t> DslNs<'t, Box>> for App { "max/xy" (x: u16, y: u16, c: Box>) => Box::new(Max::xy(x, y, c)), }); dsl_words!(|app| -> Box> { + ":dialog/menu" => Box::new(if let Dialog::Menu(selected, items) = &app.dialog { + let items = items.clone(); + let selected = *selected; + Some(Fill::xy(Align::c(Tui::bg(Red, Fill::x(Stack::south(move|add|{ + for (index, item) in items.iter().enumerate() { + add(&Tui::fg_bg( + if selected == index { Rgb(240,200,180) } else { Rgb(200, 200, 200) }, + if selected == index { Rgb(80, 80, 50) } else { Rgb(30, 30, 30) }, + Fixed::y(2, Align::n(Fill::x(item))) + )); + } + })))))) + } else { + None + }), ":templates" => Box::new({ let modes = app.config.modes.clone(); let height = (modes.read().unwrap().len() * 2) as u16; @@ -162,6 +174,10 @@ impl<'t> DslNs<'t, Box>> for App { } } +pub fn view_nil (_: &App) -> Box> { + Box::new(Fill::xy("·")) +} + //Bsp::s("", //Map::south(1, //move||app.config.binds.layers.iter() diff --git a/crates/cli/tek.rs b/crates/cli/tek.rs index 8a719729..255a8d8c 100644 --- a/crates/cli/tek.rs +++ b/crates/cli/tek.rs @@ -66,7 +66,7 @@ impl Cli { let app = App { jack: jack.clone(), color: ItemTheme::random(), - dialog: Dialog::Menu(0, 0), + dialog: Dialog::welcome(), mode: config.modes.clone().read().unwrap().get(":menu").cloned().unwrap(), config, project: Arrangement { diff --git a/crates/config/config.rs b/crates/config/config.rs index 1206750e..d48482bb 100644 --- a/crates/config/config.rs +++ b/crates/config/config.rs @@ -124,33 +124,28 @@ impl Mode> { modes.write().unwrap().insert(name.as_ref().into(), Arc::new(mode)); Ok(()) } - pub fn load_one (&mut self, item: impl Dsl) -> Usually<()> { - Ok(if let Ok(Some(key)) = item.expr().head() { + pub fn load_one (&mut self, dsl: impl Dsl) -> Usually<()> { + Ok(if let Ok(Some(expr)) = dsl.expr() && let Ok(Some(key)) = expr.head() { + println!("Mode::load_one: {key} {:?}", expr.tail()?); + let tail = expr.tail()?.map(|x|x.trim()).unwrap_or(""); match key { - "name" => { - self.name.push(item.tail()?.map(|x|x.trim()).unwrap_or("").into()) - }, - "info" => { - self.info.push(item.tail()?.map(|x|x.trim()).unwrap_or("").into()) - }, - "keys" => { - item.tail()?.each(|item|{ - self.keys.push(item.trim().into()); - Ok(()) - })?; - }, - "self" => if let Some(id) = item.tail()?.head()? { - Self::load_into(&self.modes, &id, &item.tail().tail())?; + "name" => self.name.push(tail.into()), + "info" => self.info.push(tail.into()), + "view" => self.view.push(tail.into()), + "keys" => tail.each(|expr|{self.keys.push(expr.trim().into()); Ok(())})?, + "mode" => if let Some(id) = tail.head()? { + Self::load_into(&self.modes, &id, &tail.tail())?; } else { - return Err(format!("Mode::load_one: self: incomplete: {item:?}").into()); + return Err(format!("Mode::load_one: self: incomplete: {expr:?}").into()); + }, + _ => { + return Err(format!("Mode::load_one: unexpected expr: {key:?} {tail:?}").into()) }, - _ if let Some(src) = item.src()? => self.view.push(src.into()), - _ => {}, }; - } else if let Ok(Some(word)) = item.word() { + } else if let Ok(Some(word)) = dsl.word() { self.view.push(word.into()); } else { - return Err(format!("Mode::load_one: unexpected: {item:?}").into()); + return Err(format!("Mode::load_one: unexpected: {dsl:?}").into()); }) } } diff --git a/tek.edn b/tek.edn index 6fb3ebe8..5066d1b1 100644 --- a/tek.edn +++ b/tek.edn @@ -1,12 +1,18 @@ -(mode :menu (keys :y :confirm) :menu) -(keys :x (@left x/dec) (@right x/inc)) -(keys :y (@up y/dec) (@down y/inc)) +(keys :axis/x (@left x/dec) (@right x/inc)) +(keys :axis/x2 (@shift/left x2/dec) (@shift/right x2/inc)) +(keys :axis/y (@up y/dec) (@down y/inc)) +(keys :axis/y2 (@shift/up y2/dec) (@shift/down y2/inc)) +(keys :axis/z (@minus z/dec) (@equal z/inc)) +(keys :axis/z2 (@underscore z2/dec) (@plus z2/inc)) +(keys :axis/i (@comma i/dec) (@period z/inc)) +(keys :axis/i2 (@lt i2/dec) (@gt z2/inc)) +(keys :axis/w (@openbracket w/dec) (@closebracket w/inc)) +(keys :axis/w2 (@openbrace w2/dec) (@closebrace w2/inc)) + +(mode :menu (keys :axis/y :confirm) :menu) (keys :confirm (@enter confirm)) (view :menu (bg (g 40) (bsp/s :ports/out (bsp/n :ports/in - (bg (g 30) (fill/xy (align/c (bg (g 40) (bsp/s - (text CONTINUE-SESSION) - (bsp/s (text LOAD-OTHER-SESSION) - (text BEGIN-NEW-SESSION))))))))))) + (bg (g 30) (fill/xy (align/c :dialog/menu))))))) (view :ports/out (fill/x (fixed/y 3 (bsp/a (fill/x (align/w (text L-AUDIO-OUT))) (bsp/a (text MIDI-OUT) (fill/x (align/e (text AUDIO-OUT R)))))))) (view :ports/in (fill/x (fixed/y 3 (bsp/a (fill/x (align/w (text L-AUDIO-IN))) @@ -16,18 +22,10 @@ (keys :help (@f1 dialog :help)) (keys :back (@escape back)) (keys :page (@pgup page/up) (@pgdn page/down)) -(keys :x2 (@shift/left x2/dec) (@shift/right x2/inc)) -(keys :y2 (@shift/up y2/dec) (@shift/down y2/inc)) -(keys :z (@minus z/dec) (@equal z/inc)) -(keys :z2 (@underscore z2/dec) (@plus z2/inc)) -(keys :i (@comma i/dec) (@period z/inc)) -(keys :i2 (@lt i2/dec) (@gt z2/inc)) -(keys :w (@openbracket w/dec) (@closebracket w/inc)) -(keys :w2 (@openbrace w2/dec) (@closebrace w2/inc)) (keys :delete (@delete delete) (@backspace delete/back)) -(keys :input (see :x :delete) (:char input)) -(keys :list (see :y :confirm)) -(keys :length (see :x :y :confirm)) +(keys :input (see :axis/x :delete) (:char input)) +(keys :list (see :axis/y :confirm)) +(keys :length (see :axis/x :axis/y :confirm)) (keys :browse (see :list :input :focus)) (keys :history (@u undo 1) (@r redo 1)) (keys :clock (@space clock/toggle 0) (@shift/space clock/toggle 0)) @@ -80,15 +78,15 @@ (@up select :select/scene/dec) (@down select :select/scene/inc)) -(keys :track (see :color :launch :z :z2 :delete) +(keys :track (see :color :launch :axis/z :axis/z2 :delete) (@r toggle :rec) (@m toggle :mon) (@p toggle :play) (@P toggle :solo)) -(keys :scene (see :color :launch :z :z2 :delete)) +(keys :scene (see :color :launch :axis/z :axis/z2 :delete)) -(keys :clip (see :color :launch :z :z2 :delete) +(keys :clip (see :color :launch :axis/z :axis/z2 :delete) (@l toggle :loop)) (mode :groovebox @@ -154,13 +152,13 @@ (keys :sequencer (see :color :launch) (@shift/I input/add) (@shift/O output/add)) -(keys :pool (see :axis-y :axis-w :z2 :color :delete) +(keys :pool (see :axis-y :axis-w :axis/z2 :color :delete) (@n rename/begin) (@t length/begin) (@m import/begin) (@x export/begin) (@shift/A clip/add :after :new/clip) (@shift/D clip/add :after :cloned/clip)) (keys :editor (see :editor/view :editor/note)) -(keys :editor/view (see :x :x2 :z :z2) +(keys :editor/view (see :axis/x :axis/x2 :axis/z :axis/z2) (@z toggle :lock)) -(keys :editor/note (see :i :i2 :y :page) +(keys :editor/note (see :axis/i :axis/i2 :axis/y :page) (@a editor/append :true) (@enter editor/append :false) (@del editor/delete/note) (@shift/del editor/delete/note))