mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 03:36:41 +01:00
parent
cfd19062fd
commit
559d2fc4a1
7 changed files with 125 additions and 72 deletions
|
|
@ -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))))
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
46
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))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue