a random KeyMatcher appears

This commit is contained in:
🪞👃🪞 2025-01-14 19:44:50 +01:00
parent 12faadef44
commit 44201ebf76
6 changed files with 152 additions and 80 deletions

View file

@ -169,7 +169,7 @@ pub struct PoolView<'a>(pub bool, pub &'a MidiPool);
content!(TuiOut: |self: PoolView<'a>| {
let Self(compact, model) = self;
let MidiPool { clips, mode, .. } = self.1;
let color = self.1.clip().map(|c|c.read().unwrap().color).unwrap_or_else(||TuiTheme::g(32).into());
//let color = self.1.clip().map(|c|c.read().unwrap().color).unwrap_or_else(||TuiTheme::g(32).into());
let on_bg = |x|x;//Bsp::b(Repeat(" "), Tui::bg(color.darkest.rgb, x));
let border = |x|x;//Outer(Style::default().fg(color.dark.rgb).bg(color.darkest.rgb)).enclose(x);
let iter = | |model.clips().clone().into_iter();

View file

@ -1,44 +1,8 @@
(def keys
(u :undo 1)
(shift-u :redo 1)
(ctrl-k :todo "keyboard")
(space :play-toggle)
(shift-space :play-start-toggle)
(e :editor-show :pool-phrase)
(ctrl-a :scene-add)
(ctrl-t :track-add)
(tab :pool-toggle))
(def keys-clip
(q :clip-launch)
(c :clip-color)
(g :clip-get)
(p :clip-put)
(del :clip-del)
(, :clip-prev)
(. :clip-next)
(< :clip-swap-prev)
(> :clip-swap-next)
(l :clip-loop-toggle))
(def keys-scene
(q :scene-launch)
(c :scene-color)
(, :scene-prev)
(. :scene-next)
(< :scene-swap-prev)
(> :scene-swap-next)
(del :scene-delete))
(def keys-track
(q :track-launch)
(c :track-color)
(, :track-prev)
(. :track-next)
(< :track-swap-prev)
(> :track-swap-next)
(del :track-delete))
(:q enqueue :clip)
(:0 enqueue :stop)
(:u undo 1)
(:shift-u redo 1)
(:space play/toggle)
(:shift-space play/start-toggle)
(:e editor/show :pool-phrase)
(:ctrl-a scene/add)
(:ctrl-t track/add)
(:tab pool/toggle)

View file

@ -0,0 +1,10 @@
(q :clip-launch)
(c :clip-color)
(g :clip-get)
(p :clip-put)
(del :clip-del)
(, :clip-prev)
(. :clip-next)
(< :clip-swap-prev)
(> :clip-swap-next)
(l :clip-loop-toggle)

View file

@ -0,0 +1,7 @@
(q :scene-launch)
(c :scene-color)
(, :scene-prev)
(. :scene-next)
(< :scene-swap-prev)
(> :scene-swap-next)
(del :scene-delete)

View file

@ -0,0 +1,7 @@
(q :track-launch)
(c :track-color)
(, :track-prev)
(. :track-next)
(< :track-swap-prev)
(> :track-swap-next)
(del :track-delete)

View file

@ -2,50 +2,134 @@ use crate::*;
impl EdnInput for TuiIn {
fn matches_edn (&self, token: &str) -> bool {
if let Some(event) = parse_key_spec(token.to_string(), KeyModifiers::NONE) {
if let Some(event) = KeyMatcher::new(token).build() {
&event == self.event()
} else {
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 }
match item { EdnItem::Sym(s) => KeyMatcher::new(s).build(), _ => 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:?}")
struct KeyMatcher {
valid: bool,
key: Option<KeyCode>,
mods: KeyModifiers,
}
impl KeyMatcher {
fn new (token: impl AsRef<str>) -> Self {
let token = token.as_ref();
if token.len() < 2 {
Self { valid: false, key: None, mods: KeyModifiers::NONE }
} else if token.chars().next() != Some(':') {
Self { valid: false, key: None, mods: KeyModifiers::NONE }
} else {
Self { valid: true, key: None, mods: KeyModifiers::NONE }.next(&token[1..])
}
}
fn next (mut self, token: &str) -> Self {
let mut tokens = token.split('-').peekable();
while let Some(token) = tokens.next() {
if tokens.peek().is_some() {
match token {
"ctrl" | "Ctrl" | "c" | "C" => self.mods |= KeyModifiers::CONTROL,
"alt" | "Alt" | "m" | "M" => self.mods |= KeyModifiers::ALT,
"shift" | "Shift" | "s" | "S" => {
self.mods |= KeyModifiers::SHIFT;
// + TODO normalize character case, BackTab, etc.
},
_ => panic!("unknown modifier {token}"),
}
} else {
self.key = if token.len() == 1 {
Some(KeyCode::Char(token.chars().next().unwrap()))
} else {
Some(Self::named_key(token).unwrap_or_else(||panic!("unknown character {token}")))
}
_ => panic!("invalid key event {spec:?}")
}
}
}))
self
}
fn named_key (token: &str) -> Option<KeyCode> {
use KeyCode::*;
Some(match token {
"up" => Up,
"down" => Down,
"left" => Left,
"right" => Right,
"enter" | "return" => Enter,
"delete" | "del" => Delete,
"tab" => Tab,
"space" => Char(' '),
"comma" => Char(','),
"period" => Char('.'),
"plus" => Char('+'),
"minus" | "dash" => Char('-'),
"equal" | "equals" => Char('='),
"underscore" => Char('_'),
"backtick" => Char('`'),
_ => return None,
})
}
fn build (self) -> Option<Event> {
if self.valid && self.key.is_some() {
Some(Event::Key(KeyEvent::new(self.key.unwrap(), self.mods)))
} else {
None
}
}
}
#[cfg(test)] #[test] fn test_parse_key () {
use KeyModifiers as Mods;
let test = |x: &str, y|assert_eq!(KeyMatcher::new(x).build(), Some(Event::Key(y)));
test(":x",
KeyEvent::new(KeyCode::Char('x'), Mods::NONE));
test(":ctrl-x",
KeyEvent::new(KeyCode::Char('x'), Mods::CONTROL));
test(":alt-x",
KeyEvent::new(KeyCode::Char('x'), Mods::ALT));
test(":shift-x",
KeyEvent::new(KeyCode::Char('x'), Mods::SHIFT));
test(":ctrl-alt-shift-x",
KeyEvent::new(KeyCode::Char('x'), Mods::CONTROL | Mods::ALT | Mods::SHIFT ));
}
//fn parse_key (spec: std::str::Chars<'_>, mut mods: KeyModifiers) -> Option<Event> {
//Some(Event::Key(match spec.chars() {
//":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(split.iter().collect(), mods)
//}
//}
//panic!("invalid key event {spec:?}")
//}
//_ => panic!("invalid key event {spec:?}")
//}
//}
//}))
//}