mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2025-12-06 03:36:42 +01:00
simplify
This commit is contained in:
parent
291b917970
commit
238ac2e888
25 changed files with 1018 additions and 1147 deletions
|
|
@ -3,7 +3,6 @@ use std::time::Duration;
|
|||
|
||||
mod tui_buffer; pub use self::tui_buffer::*;
|
||||
mod tui_input; pub use self::tui_input::*;
|
||||
mod tui_keys; pub use self::tui_keys::*;
|
||||
mod tui_output; pub use self::tui_output::*;
|
||||
mod tui_perf; pub use self::tui_perf::*;
|
||||
|
||||
|
|
@ -72,8 +71,7 @@ pub trait TuiRun<R: Render<TuiOut> + Handle<TuiIn> + 'static> {
|
|||
fn run (&self, state: &Arc<RwLock<R>>) -> Usually<()>;
|
||||
}
|
||||
|
||||
impl<T: Render<TuiOut> + Handle<TuiIn> + Send + Sync + 'static>
|
||||
TuiRun<T> for Arc<RwLock<Tui>> {
|
||||
impl<T: Render<TuiOut> + Handle<TuiIn> + Send + Sync + 'static> TuiRun<T> for Arc<RwLock<Tui>> {
|
||||
fn run (&self, state: &Arc<RwLock<T>>) -> Usually<()> {
|
||||
let _input_thread = TuiIn::run_input(self, state, Duration::from_millis(100));
|
||||
self.write().unwrap().setup()?;
|
||||
|
|
|
|||
|
|
@ -1,24 +1,39 @@
|
|||
use crate::*;
|
||||
use std::time::Duration;
|
||||
use std::thread::{spawn, JoinHandle};
|
||||
use crossterm::event::{poll, read};
|
||||
use crossterm::event::{Event, poll, read};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TuiIn(
|
||||
pub struct TuiIn {
|
||||
/// Exit flag
|
||||
pub Arc<AtomicBool>,
|
||||
pub exited: Arc<AtomicBool>,
|
||||
/// Input event
|
||||
pub crossterm::event::Event,
|
||||
);
|
||||
|
||||
impl Input for TuiIn {
|
||||
type Event = crossterm::event::Event;
|
||||
type Handled = bool;
|
||||
fn event (&self) -> &crossterm::event::Event { &self.1 }
|
||||
fn is_done (&self) -> bool { self.0.fetch_and(true, Relaxed) }
|
||||
fn done (&self) { self.0.store(true, Relaxed); }
|
||||
pub event: TuiEvent,
|
||||
}
|
||||
impl Input for TuiIn {
|
||||
type Event = TuiEvent;
|
||||
type Handled = bool;
|
||||
fn event (&self) -> &TuiEvent { &self.event }
|
||||
fn is_done (&self) -> bool { self.exited.fetch_and(true, Relaxed) }
|
||||
fn done (&self) { self.exited.store(true, Relaxed); }
|
||||
}
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd)]
|
||||
pub struct TuiEvent(Event);
|
||||
impl Ord for TuiEvent {
|
||||
fn cmp (&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.partial_cmp(other) .unwrap_or_else(||format!("{:?}", self).cmp(&format!("{other:?}"))) // FIXME perf
|
||||
}
|
||||
}
|
||||
impl From<Event> for TuiEvent {
|
||||
fn from (event: Event) -> Self {
|
||||
Self(event)
|
||||
}
|
||||
}
|
||||
impl From<Arc<str>> for TuiEvent {
|
||||
fn from (x: Arc<str>) -> Self {
|
||||
TuiEvent(TuiKey::new(x.as_ref()).build().unwrap_or_else(||panic!("invalid key: {x}")))
|
||||
}
|
||||
}
|
||||
|
||||
impl TuiIn {
|
||||
/// Spawn the input thread.
|
||||
pub fn run_input <T: Handle<TuiIn> + Send + Sync + 'static> (
|
||||
|
|
@ -35,8 +50,7 @@ impl TuiIn {
|
|||
if poll(timer).is_ok() {
|
||||
let event = read().unwrap();
|
||||
match event {
|
||||
|
||||
crossterm::event::Event::Key(KeyEvent {
|
||||
Event::Key(KeyEvent {
|
||||
code: KeyCode::Char('c'),
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
kind: KeyEventKind::Press,
|
||||
|
|
@ -46,7 +60,8 @@ impl TuiIn {
|
|||
},
|
||||
_ => {
|
||||
let exited = exited.clone();
|
||||
if let Err(e) = state.write().unwrap().handle(&TuiIn(exited, event)) {
|
||||
let event = event.into();
|
||||
if let Err(e) = state.write().unwrap().handle(&TuiIn { exited, event }) {
|
||||
panic!("{e}")
|
||||
}
|
||||
}
|
||||
|
|
@ -59,10 +74,101 @@ impl TuiIn {
|
|||
//#[cfg(feature = "dsl")]
|
||||
//impl DslInput for TuiIn {
|
||||
//fn matches_dsl (&self, token: &str) -> bool {
|
||||
//if let Some(event) = KeyMatcher::new(token).build() {
|
||||
//if let Some(event) = TuiKey::new(token).build() {
|
||||
//&event == self.event()
|
||||
//} else {
|
||||
//false
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
|
||||
pub struct TuiKey {
|
||||
valid: bool,
|
||||
key: Option<KeyCode>,
|
||||
mods: KeyModifiers,
|
||||
}
|
||||
|
||||
impl TuiKey {
|
||||
pub 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..])
|
||||
}
|
||||
}
|
||||
pub fn build (self) -> Option<Event> {
|
||||
if self.valid && self.key.is_some() {
|
||||
Some(Event::Key(KeyEvent::new(self.key.unwrap(), self.mods)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
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,
|
||||
"esc" | "escape" => Esc,
|
||||
"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('`'),
|
||||
"lt" => Char('<'),
|
||||
"gt" => Char('>'),
|
||||
"cbopen" | "openbrace" => Char('{'),
|
||||
"cbclose" | "closebrace" => Char('}'),
|
||||
"bropen" | "openbracket" => Char('['),
|
||||
"brclose" | "closebracket" => Char(']'),
|
||||
"pgup" | "pageup" => PageUp,
|
||||
"pgdn" | "pagedown" => PageDown,
|
||||
"f1" => F(1),
|
||||
"f2" => F(2),
|
||||
"f3" => F(3),
|
||||
"f4" => F(4),
|
||||
"f5" => F(5),
|
||||
"f6" => F(6),
|
||||
"f7" => F(7),
|
||||
"f8" => F(8),
|
||||
"f9" => F(9),
|
||||
"f10" => F(10),
|
||||
"f11" => F(11),
|
||||
"f12" => F(12),
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,92 +0,0 @@
|
|||
use crate::*;
|
||||
|
||||
pub struct KeyMatcher {
|
||||
valid: bool,
|
||||
key: Option<KeyCode>,
|
||||
mods: KeyModifiers,
|
||||
}
|
||||
|
||||
impl KeyMatcher {
|
||||
pub 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..])
|
||||
}
|
||||
}
|
||||
pub fn build (self) -> Option<Event> {
|
||||
if self.valid && self.key.is_some() {
|
||||
Some(Event::Key(KeyEvent::new(self.key.unwrap(), self.mods)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
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,
|
||||
"esc" | "escape" => Esc,
|
||||
"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('`'),
|
||||
"lt" => Char('<'),
|
||||
"gt" => Char('>'),
|
||||
"cbopen" | "openbrace" => Char('{'),
|
||||
"cbclose" | "closebrace" => Char('}'),
|
||||
"bropen" | "openbracket" => Char('['),
|
||||
"brclose" | "closebracket" => Char(']'),
|
||||
"pgup" | "pageup" => PageUp,
|
||||
"pgdn" | "pagedown" => PageDown,
|
||||
"f1" => F(1),
|
||||
"f2" => F(2),
|
||||
"f3" => F(3),
|
||||
"f4" => F(4),
|
||||
"f5" => F(5),
|
||||
"f6" => F(6),
|
||||
"f7" => F(7),
|
||||
"f8" => F(8),
|
||||
"f9" => F(9),
|
||||
"f10" => F(10),
|
||||
"f11" => F(11),
|
||||
"f12" => F(12),
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue