mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
wip: script arrow navigation in arrangement
This commit is contained in:
parent
b9cc594bdb
commit
0c6add7038
8 changed files with 124 additions and 189 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 {};
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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 ));
|
|
||||||
}
|
|
||||||
|
|
@ -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 ));
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue