wip: let's figure out how edn keymaps will work

This commit is contained in:
🪞👃🪞 2025-01-12 13:01:15 +01:00
parent 4ab08e48e5
commit 794d4210c6
33 changed files with 161 additions and 85 deletions

1
Cargo.lock generated
View file

@ -1448,6 +1448,7 @@ name = "tek_input"
version = "0.2.0"
dependencies = [
"tek_edn",
"tek_tui",
]
[[package]]

View file

@ -1,18 +1,15 @@
default:
bacon -sj test
edn:
edn-view:
reset
cargo run --example edn
cargo run --example edn-view
edn-keys:
reset
cargo run --example edn-keys
test:
reset
cd engine && cargo test && cd ..
cd layout && cargo test && cd ..
cd edn && cargo test && cd ..
cd tui && cargo test && cd ..
cargo test
cargo test --workspace
cloc:
for src in {cli,edn/src,input/src,jack/src,midi/src,output/src,plugin/src,sampler/src,tek/src,time/src,tui/src}; do echo; echo $src; cloc --quiet $src; done

View file

@ -62,10 +62,10 @@ impl<'a> Token<'a> {
)),
_ => return Err(ParseError::Unexpected(c))
},
Exp(source, index, length, depth) => match c {
')' => Exp(source, index, length + 1, depth - 1),
'(' => Exp(source, index, length + 1, depth + 1),
_ => Exp(source, index, length + 1, depth)
Exp(source, index, length, balance) => match c {
')' => Exp(source, index, length + 1, balance - 1),
'(' => Exp(source, index, length + 1, balance + 1),
_ => Exp(source, index, length + 1, balance)
},
};
chars = next

View file

@ -5,3 +5,6 @@ version = "0.2.0"
[dependencies]
tek_edn = { path = "../edn" }
[dev-dependencies]
tek_tui = { path = "../tui" }

View file

View file

View file

View file

View file

@ -5,10 +5,22 @@ use std::marker::PhantomData;
/// Turns an EDN symbol sequence into a command enum variant.
pub trait EdnCommand<C>: Command<C> {
fn from_edn <'a> (state: &C, head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self;
fn get_isize (state: &C, item: &EdnItem<&str>) -> Option<isize> {
None
}
fn get_usize (state: &C, item: &EdnItem<&str>) -> Option<usize> {
None
}
fn get_bool (state: &C, item: &EdnItem<&str>) -> Option<usize> {
None
}
}
pub trait EdnInput: Input {
fn matches (&self, token: &str) -> bool;
fn get_event <S: AsRef<str>> (_: &EdnItem<S>) -> Option<Self::Event> {
None
}
}
pub struct EdnKeymap(pub Vec<EdnItem<String>>);

View file

@ -1,20 +1,20 @@
(:enter note/put)
(:del note/del)
(:comma note/dur/dec)
(:period note/dur/inc)
(:plus note/range/inc)
(:underscore note/range/dec)
(:up note/pos/inc)
(:down note/pos/dec)
(:left time/pos/dec)
(:right time/pos/inc)
(:z time/zoom/lock)
(:equal time/zoom/in)
(:minus time/zoom/out)
(:space clock/toggle)
(:shift-space clock/toggle/zero)
(:u undo)
(:r redo)
(:tab compact)
(:q enqueue)
(:0 stop)
(:enter add :false)
(:shift-enter add :true)
(:del del :false)
(:shift-del del :true)
(:comma note/length :prev-note-length)
(:period note/length :next-note-length)
(:plus note/range :next-note-range)
(:underscore note/range :prev-note-range)
(:up note/point :next-note-point)
(:down note/point :prev-note-point)
(:left time/point :next-time-point)
(:right time/point :prev-time-point)
(:z time/lock :next-time-lock)
(:equal time/zoom :next-time-zoom)
(:minus time/zoom :prev-time-zoom)
(:space clock/play :play-current)
(:shift-space clock/play :play-start)
(:u history :history-prev)
(:r history :history-next)
(:tab compact :next-compact)

View file

@ -1 +1,13 @@
;TODO
(:n clip/rename/begin)
(:t clip/length/begin)
(:m clip/import/begin)
(:x clip/export/begin)
(:c clip/color/random)
(:bracket-open clip/select/prev)
(:bracket-close clip/select/next)
(:lt clip/move/prev)
(:gt clip/move/next)
(:del clip/delete)
(:shift-a clip/add)
(:i clip/insert)
(:d clip/duplicate)

View file

@ -13,7 +13,7 @@ pub trait HasMidiClip {
}
/// A MIDI sequence.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Default)]
pub struct MidiClip {
pub uuid: uuid::Uuid,
/// Name of clip
@ -83,10 +83,7 @@ impl MidiClip {
}
false
}
}
impl Default for MidiClip {
fn default () -> Self {
pub fn stop_all () -> Self {
Self::new(
"Stop",
false,

View file

@ -1,8 +1,23 @@
use crate::*;
use self::MidiEditCommand::*;
use KeyCode::*;
#[derive(Clone, Debug)]
pub enum MidiEditCommand {
impl EdnCommand<MidiEditor> for MidiEditCommand {
fn from_edn <'a> (state: &MidiEditor, head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self {
use EdnItem::*;
match (head, tail) {
(Key("note/put"), [a]) => Self::PutNote,
(Key("note/del"), [a]) => Self::AppendNote,
(Key("note/dur"), [a]) => Self::AppendNote,
(Key("note/range"), [a]) => Self::AppendNote,
(Key("note/pos"), [a]) => Self::AppendNote,
(Key("time/pos"), [a]) => Self::AppendNote,
(Key("time/zoom"), [a]) => Self::AppendNote,
(Key("time/lock"), [a]) => Self::AppendNote,
_ => todo!()
}
}
}
#[derive(Clone, Debug)] pub enum MidiEditCommand {
// TODO: 1-9 seek markers that by default start every 8th of the clip
AppendNote,
PutNote,
@ -70,8 +85,3 @@ impl Command<MidiEditor> for MidiEditCommand {
Ok(None)
}
}
impl EdnCommand<MidiEditor> for MidiEditCommand {
fn from_edn <'a> (state: &MidiEditor, head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self {
todo!()
}
}

View file

@ -135,34 +135,3 @@ impl ClipLengthFocus {
*self = match self { Self::Bar => Self::Tick, Self::Beat => Self::Bar, Self::Tick => Self::Beat, }
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum ClipRenameCommand {
Begin,
Cancel,
Confirm,
Set(Arc<str>),
}
impl Command<PoolModel> for ClipRenameCommand {
fn execute (self, state: &mut PoolModel) -> Perhaps<Self> {
use ClipRenameCommand::*;
match state.clips_mode_mut().clone() {
Some(PoolMode::Rename(clip, ref mut old_name)) => match self {
Set(s) => {
state.clips()[clip].write().unwrap().name = s;
return Ok(Some(Self::Set(old_name.clone().into())))
},
Confirm => {
let old_name = old_name.clone();
*state.clips_mode_mut() = None;
return Ok(Some(Self::Set(old_name)))
},
Cancel => {
state.clips()[clip].write().unwrap().name = old_name.clone().into();
},
_ => unreachable!()
},
_ => unreachable!()
};
Ok(None)
}
}

View file

@ -317,7 +317,6 @@ command!(|self: ClipLengthCommand,state:PoolModel|{
};
None
});
input_to_command!(ClipLengthCommand: |state: PoolModel, input: Event|{
if let Some(PoolMode::Length(_, length, _)) = state.clips_mode() {
match input {
@ -333,10 +332,6 @@ input_to_command!(ClipLengthCommand: |state: PoolModel, input: Event|{
unreachable!()
}
});
use crate::*;
use super::*;
impl InputToCommand<Event, PoolModel> for ClipRenameCommand {
fn input_to_command (state: &PoolModel, input: &Event) -> Option<Self> {
use KeyCode::{Char, Backspace, Enter, Esc};
@ -361,3 +356,34 @@ impl InputToCommand<Event, PoolModel> for ClipRenameCommand {
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum ClipRenameCommand {
Begin,
Cancel,
Confirm,
Set(Arc<str>),
}
impl Command<PoolModel> for ClipRenameCommand {
fn execute (self, state: &mut PoolModel) -> Perhaps<Self> {
use ClipRenameCommand::*;
match state.clips_mode_mut().clone() {
Some(PoolMode::Rename(clip, ref mut old_name)) => match self {
Set(s) => {
state.clips()[clip].write().unwrap().name = s;
return Ok(Some(Self::Set(old_name.clone().into())))
},
Confirm => {
let old_name = old_name.clone();
*state.clips_mode_mut() = None;
return Ok(Some(Self::Set(old_name)))
},
Cancel => {
state.clips()[clip].write().unwrap().name = old_name.clone().into();
},
_ => unreachable!()
},
_ => unreachable!()
};
Ok(None)
}
}

View file

@ -3,8 +3,8 @@ name = "tek_output"
edition = "2021"
version = "0.2.0"
[dev-dependencies]
tek_tui = { path = "../tui" }
[dependencies]
tek_edn = { path = "../edn" }
[dev-dependencies]
tek_tui = { path = "../tui" }

View file

@ -38,3 +38,6 @@
(< :track-swap-prev)
(> :track-swap-next)
(del :track-delete))
(:q enqueue :clip)
(:0 enqueue :stop)

View file

@ -0,0 +1,46 @@
use crate::*;
impl EdnInput for TuiIn {
fn matches (&self, token: &str) -> bool {
false
}
fn get_event <S: AsRef<str>> (item: &EdnItem<S>) -> Option<Event> {
match item { EdnItem::Sym(s) => parse_key_spec(s.as_ref().to_string(), KeyModifiers::NONE), _ => None }
}
}
fn parse_key_spec (spec: String, mut mods: KeyModifiers) -> Option<Event> {
Some(Event::Key(match spec.as_str() {
":enter" | ":return" => KeyEvent::new(KeyCode::Enter, mods),
":delete" | ":del" => KeyEvent::new(KeyCode::Enter, mods),
":comma" => KeyEvent::new(KeyCode::Char(','), mods),
":period" => KeyEvent::new(KeyCode::Char('.'), mods),
":plus" => KeyEvent::new(KeyCode::Char('+'), mods),
":underscore" => KeyEvent::new(KeyCode::Char('_'), mods),
":up" => KeyEvent::new(KeyCode::Up, mods),
":down" => KeyEvent::new(KeyCode::Down, mods),
":left" => KeyEvent::new(KeyCode::Left, mods),
":right" => KeyEvent::new(KeyCode::Right, mods),
_ => {
let chars = spec.chars().collect::<Vec<_>>();
match chars.as_slice() {
[':', c] => KeyEvent::new(KeyCode::Char(*c), mods),
[':', c @ ..] => {
let mut splits = c.split(|c|*c=='-');
while let Some(split) = splits.next() {
match split {
['c','t','r','l'] => mods |= KeyModifiers::CONTROL,
['a','l','t'] => mods |= KeyModifiers::ALT,
['s','h','i','f','t'] => mods |= KeyModifiers::SHIFT,
_ => return parse_key_spec(split.iter().collect(), mods)
}
}
panic!("invalid key event {spec:?}")
}
_ => panic!("invalid key event {spec:?}")
}
}
}))
}