wip: script arrow navigation in arrangement

This commit is contained in:
🪞👃🪞 2025-01-14 21:42:37 +01:00
parent b9cc594bdb
commit 0c6add7038
8 changed files with 124 additions and 189 deletions

View file

@ -1,8 +1,8 @@
(@n rename/begin) (@n rename begin)
(@t length/begin) (@t length begin)
(@m import/begin) (@m import begin)
(@x export/begin) (@x export begin)
(@c clip/color :current :random-color) (@c clip color :current :random-color)
(@openbracket select :previous) (@openbracket select :previous)
(@closebracket select :next) (@closebracket select :next)
(@lt swap :current :previous) (@lt swap :current :previous)

View file

@ -1,3 +1,8 @@
(@up select :track :scene-prev)
(@down select :track :scene-next)
(@left select :track-prev :scene)
(@right select :track-next :scene)
(@q clip launch) (@q clip launch)
(@c clip color) (@c clip color)
(@g clip get) (@g clip get)

View file

@ -1,3 +1,7 @@
(@up select 0 :scene-prev)
(@down select 0 :scene-next)
(@right select 1 :scene)
(@q scene launch) (@q scene launch)
(@c scene color) (@c scene color)
(@comma scene prev) (@comma scene prev)

View file

@ -1,3 +1,7 @@
(@left select :track-prev 0)
(@right select :track-next 0)
(@down select :track down)
(@q track launch) (@q track launch)
(@c track color) (@c track color)
(@comma track prev) (@comma track prev)

View file

@ -68,7 +68,14 @@ has_editor!(|self: App|{
editor_h = 15; editor_h = 15;
is_editing = self.editing.load(Relaxed); is_editing = self.editing.load(Relaxed);
}); });
edn_provide!(# usize: |self: App| {}); edn_provide!(# usize: |self: App| {
":scene" => 0,
":scene-next" => 0,
":scene-prev" => 0,
":track" => 0,
":track-next" => 0,
":track-prev" => 0,
});
edn_view!(TuiOut: |self: App| self.size.of(EdnView::from_source(self, self.edn.as_ref())); { edn_view!(TuiOut: |self: App| self.size.of(EdnView::from_source(self, self.edn.as_ref())); {
bool {}; bool {};
isize {}; isize {};

View file

@ -23,7 +23,6 @@ mod tui_border; pub use self::tui_border::*;
mod tui_field; pub use self::tui_field::*; mod tui_field; pub use self::tui_field::*;
mod tui_buffer; pub use self::tui_buffer::*; mod tui_buffer; pub use self::tui_buffer::*;
mod tui_file; pub use self::tui_file::*; mod tui_file; pub use self::tui_file::*;
mod tui_edn_keymap; pub use self::tui_edn_keymap::*;
pub(crate) use std::sync::{Arc, RwLock, atomic::{AtomicBool, Ordering::*}}; pub(crate) use std::sync::{Arc, RwLock, atomic::{AtomicBool, Ordering::*}};
pub(crate) use std::io::{stdout, Stdout}; pub(crate) use std::io::{stdout, Stdout};
pub(crate) use std::path::PathBuf; pub(crate) use std::path::PathBuf;

View file

@ -1,95 +0,0 @@
use crate::*;
impl EdnInput for TuiIn {
fn matches_edn (&self, token: &str) -> bool {
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) => KeyMatcher::new(s).build(), _ => None }
}
}
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}")))
}
}
}
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 ));
}

View file

@ -46,90 +46,101 @@ impl TuiIn {
}) })
} }
} }
//. impl EdnInput for TuiIn {
///// Define a key fn matches_edn (&self, token: &str) -> bool {
//pub const fn key (code: KeyCode) -> Event { if let Some(event) = KeyMatcher::new(token).build() {
//let modifiers = KeyModifiers::NONE; &event == self.event()
//let kind = KeyEventKind::Press; } else {
//let state = KeyEventState::NONE; false
//Event::Key(KeyEvent { code, modifiers, kind, state }) }
//} }
///// Add Ctrl modifier to key fn get_event <S: AsRef<str>> (item: &EdnItem<S>) -> Option<Event> {
//pub const fn ctrl (event: Event) -> Event { match item { EdnItem::Sym(s) => KeyMatcher::new(s).build(), _ => None }
//match event { }
//Event::Key(mut event) => { }
//event.modifiers = event.modifiers.union(KeyModifiers::CONTROL) struct KeyMatcher {
//}, valid: bool,
//_ => {} key: Option<KeyCode>,
//} mods: KeyModifiers,
//event }
//} impl KeyMatcher {
///// Add Alt modifier to key fn new (token: impl AsRef<str>) -> Self {
//pub const fn alt (event: Event) -> Event { let token = token.as_ref();
//match event { if token.len() < 2 {
//Event::Key(mut event) => { Self { valid: false, key: None, mods: KeyModifiers::NONE }
//event.modifiers = event.modifiers.union(KeyModifiers::ALT) } 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..])
//event }
//} }
///// Add Shift modifier to key fn next (mut self, token: &str) -> Self {
//pub const fn shift (event: Event) -> Event { let mut tokens = token.split('-').peekable();
//match event { while let Some(token) = tokens.next() {
//Event::Key(mut event) => { if tokens.peek().is_some() {
//event.modifiers = event.modifiers.union(KeyModifiers::SHIFT) match token {
//}, "ctrl" | "Ctrl" | "c" | "C" => self.mods |= KeyModifiers::CONTROL,
//_ => {} "alt" | "Alt" | "m" | "M" => self.mods |= KeyModifiers::ALT,
//} "shift" | "Shift" | "s" | "S" => {
//event self.mods |= KeyModifiers::SHIFT;
//} // + TODO normalize character case, BackTab, etc.
//#[macro_export] macro_rules! kpat { },
//(Ctrl-Alt-$code:pat) => { kpat!($code, KeyModifiers::CONTROL | KeyModifiers::ALT) }; _ => panic!("unknown modifier {token}"),
//(Ctrl-$code:pat) => { kpat!($code, KeyModifiers::CONTROL) }; }
//(Alt-$code:pat) => { kpat!($code, KeyModifiers::ALT) }; } else {
//(Shift-$code:pat) => { kpat!($code, KeyModifiers::SHIFT) }; self.key = if token.len() == 1 {
//($code:pat) => { Some(KeyCode::Char(token.chars().next().unwrap()))
//crossterm::event::Event::Key(KeyEvent { } else {
//code: $code, Some(Self::named_key(token).unwrap_or_else(||panic!("unknown character {token}")))
//modifiers: KeyModifiers::NONE, }
//kind: KeyEventKind::Press, }
//state: KeyEventState::NONE }
//}) self
//}; }
//($code:pat, $modifiers: pat) => { fn named_key (token: &str) -> Option<KeyCode> {
//crossterm::event::Event::Key(KeyEvent { use KeyCode::*;
//code: $code, Some(match token {
//modifiers: $modifiers, "up" => Up,
//kind: KeyEventKind::Press, "down" => Down,
//state: KeyEventState::NONE "left" => Left,
//}) "right" => Right,
//}; "enter" | "return" => Enter,
//} "delete" | "del" => Delete,
//#[macro_export] macro_rules! kexp { "tab" => Tab,
//(Ctrl-Alt-$code:ident) => { key_event_expr!($code, KeyModifiers::from_bits(0b0000_0110).unwrap()) }; "space" => Char(' '),
//(Ctrl-$code:ident) => { key_event_expr!($code, KeyModifiers::CONTROL) }; "comma" => Char(','),
//(Alt-$code:ident) => { key_event_expr!($code, KeyModifiers::ALT) }; "period" => Char('.'),
//(Shift-$code:ident) => { key_event_expr!($code, KeyModifiers::SHIFT) }; "plus" => Char('+'),
//($code:ident) => { key_event_expr!($code) }; "minus" | "dash" => Char('-'),
//($code:expr) => { key_event_expr!($code) }; "equal" | "equals" => Char('='),
//} "underscore" => Char('_'),
//#[macro_export] macro_rules! key_event_expr { "backtick" => Char('`'),
//($code:expr, $modifiers: expr) => { "lt" => Char('<'),
//crossterm::event::Event::Key(KeyEvent { "gt" => Char('>'),
//code: $code, "openbracket" => Char('['),
//modifiers: $modifiers, "closebracket" => Char(']'),
//kind: KeyEventKind::Press, _ => return None,
//state: KeyEventState::NONE })
//}) }
//}; fn build (self) -> Option<Event> {
//($code:expr) => { if self.valid && self.key.is_some() {
//crossterm::event::Event::Key(KeyEvent { Some(Event::Key(KeyEvent::new(self.key.unwrap(), self.mods)))
//code: $code, } else {
//modifiers: KeyModifiers::NONE, None
//kind: KeyEventKind::Press, }
//state: KeyEventState::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 ));
}