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" version = "0.2.0"
dependencies = [ dependencies = [
"tek_edn", "tek_edn",
"tek_tui",
] ]
[[package]] [[package]]

View file

@ -1,18 +1,15 @@
default: default:
bacon -sj test bacon -sj test
edn: edn-view:
reset reset
cargo run --example edn cargo run --example edn-view
edn-keys:
reset
cargo run --example edn-keys
test: test:
reset cargo test --workspace
cd engine && cargo test && cd ..
cd layout && cargo test && cd ..
cd edn && cargo test && cd ..
cd tui && cargo test && cd ..
cargo test
cloc: 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 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)) _ => return Err(ParseError::Unexpected(c))
}, },
Exp(source, index, length, depth) => match c { Exp(source, index, length, balance) => match c {
')' => Exp(source, index, length + 1, depth - 1), ')' => Exp(source, index, length + 1, balance - 1),
'(' => Exp(source, index, length + 1, depth + 1), '(' => Exp(source, index, length + 1, balance + 1),
_ => Exp(source, index, length + 1, depth) _ => Exp(source, index, length + 1, balance)
}, },
}; };
chars = next chars = next

View file

@ -5,3 +5,6 @@ version = "0.2.0"
[dependencies] [dependencies]
tek_edn = { path = "../edn" } 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. /// Turns an EDN symbol sequence into a command enum variant.
pub trait EdnCommand<C>: Command<C> { pub trait EdnCommand<C>: Command<C> {
fn from_edn <'a> (state: &C, head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self; 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 { pub trait EdnInput: Input {
fn matches (&self, token: &str) -> bool; 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>>); pub struct EdnKeymap(pub Vec<EdnItem<String>>);

View file

@ -1,20 +1,20 @@
(:enter note/put) (:enter add :false)
(:del note/del) (:shift-enter add :true)
(:comma note/dur/dec) (:del del :false)
(:period note/dur/inc) (:shift-del del :true)
(:plus note/range/inc) (:comma note/length :prev-note-length)
(:underscore note/range/dec) (:period note/length :next-note-length)
(:up note/pos/inc) (:plus note/range :next-note-range)
(:down note/pos/dec) (:underscore note/range :prev-note-range)
(:left time/pos/dec) (:up note/point :next-note-point)
(:right time/pos/inc) (:down note/point :prev-note-point)
(:z time/zoom/lock) (:left time/point :next-time-point)
(:equal time/zoom/in) (:right time/point :prev-time-point)
(:minus time/zoom/out) (:z time/lock :next-time-lock)
(:space clock/toggle) (:equal time/zoom :next-time-zoom)
(:shift-space clock/toggle/zero) (:minus time/zoom :prev-time-zoom)
(:u undo) (:space clock/play :play-current)
(:r redo) (:shift-space clock/play :play-start)
(:tab compact) (:u history :history-prev)
(:q enqueue) (:r history :history-next)
(:0 stop) (: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. /// A MIDI sequence.
#[derive(Debug, Clone)] #[derive(Debug, Clone, Default)]
pub struct MidiClip { pub struct MidiClip {
pub uuid: uuid::Uuid, pub uuid: uuid::Uuid,
/// Name of clip /// Name of clip
@ -83,10 +83,7 @@ impl MidiClip {
} }
false false
} }
} pub fn stop_all () -> Self {
impl Default for MidiClip {
fn default () -> Self {
Self::new( Self::new(
"Stop", "Stop",
false, false,

View file

@ -1,8 +1,23 @@
use crate::*; use crate::*;
use self::MidiEditCommand::*; use self::MidiEditCommand::*;
use KeyCode::*; use KeyCode::*;
#[derive(Clone, Debug)] impl EdnCommand<MidiEditor> for MidiEditCommand {
pub enum 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 // TODO: 1-9 seek markers that by default start every 8th of the clip
AppendNote, AppendNote,
PutNote, PutNote,
@ -70,8 +85,3 @@ impl Command<MidiEditor> for MidiEditCommand {
Ok(None) 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, } *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 None
}); });
input_to_command!(ClipLengthCommand: |state: PoolModel, input: Event|{ input_to_command!(ClipLengthCommand: |state: PoolModel, input: Event|{
if let Some(PoolMode::Length(_, length, _)) = state.clips_mode() { if let Some(PoolMode::Length(_, length, _)) = state.clips_mode() {
match input { match input {
@ -333,10 +332,6 @@ input_to_command!(ClipLengthCommand: |state: PoolModel, input: Event|{
unreachable!() unreachable!()
} }
}); });
use crate::*;
use super::*;
impl InputToCommand<Event, PoolModel> for ClipRenameCommand { impl InputToCommand<Event, PoolModel> for ClipRenameCommand {
fn input_to_command (state: &PoolModel, input: &Event) -> Option<Self> { fn input_to_command (state: &PoolModel, input: &Event) -> Option<Self> {
use KeyCode::{Char, Backspace, Enter, Esc}; 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" edition = "2021"
version = "0.2.0" version = "0.2.0"
[dev-dependencies]
tek_tui = { path = "../tui" }
[dependencies] [dependencies]
tek_edn = { path = "../edn" } tek_edn = { path = "../edn" }
[dev-dependencies]
tek_tui = { path = "../tui" }

View file

@ -38,3 +38,6 @@
(< :track-swap-prev) (< :track-swap-prev)
(> :track-swap-next) (> :track-swap-next)
(del :track-delete)) (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:?}")
}
}
}))
}