menu works lol
Some checks are pending
/ build (push) Waiting to run

This commit is contained in:
🪞👃🪞 2025-08-24 01:44:18 +03:00
parent cfd19062fd
commit 559d2fc4a1
7 changed files with 125 additions and 72 deletions

View file

@ -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<str>]>),
Device(usize),
Message(Arc<str>),
Browse(BrowseTarget, Arc<Browse>),
@ -62,6 +68,25 @@ impl HasClipsSize for App { fn clips_size (&self) -> &Measure<TuiOut> { &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<usize> {
if let Self::Menu(selected, _) = self { Some(*selected) } else { None }
}
@ -166,7 +191,6 @@ impl App {
}
}
fn wrap_dialog (dialog: impl Content<TuiOut>) -> impl Content<TuiOut> {
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))))

View file

@ -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(),

View file

@ -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,

View file

@ -1,9 +1,5 @@
use crate::*;
pub fn view_nil (_: &App) -> Box<dyn Render<TuiOut>> {
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<dyn Render<TuiOut>>>,
src: &str
@ -86,6 +83,21 @@ impl<'t> DslNs<'t, Box<dyn Render<TuiOut>>> for App {
"max/xy" (x: u16, y: u16, c: Box<dyn Render<TuiOut>>) => Box::new(Max::xy(x, y, c)),
});
dsl_words!(|app| -> Box<dyn Render<TuiOut>> {
":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<dyn Render<TuiOut>>> for App {
}
}
pub fn view_nil (_: &App) -> Box<dyn Render<TuiOut>> {
Box::new(Fill::xy("·"))
}
//Bsp::s("",
//Map::south(1,
//move||app.config.binds.layers.iter()

View file

@ -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 {

View file

@ -124,33 +124,28 @@ impl Mode<Arc<str>> {
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());
})
}
}

46
tek.edn
View file

@ -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))