From c2a0942902c97f594db7ba5bd0f3f064d83a2e8c Mon Sep 17 00:00:00 2001 From: pointer-to-bios Date: Wed, 25 Sep 2024 14:29:31 +0800 Subject: [PATCH] boost command typing experience --- config/default.toml | 9 ++- src/application.rs | 143 +++++++++++++------------------------------- src/colortheme.rs | 11 +++- src/command.rs | 125 ++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 5 files changed, 184 insertions(+), 105 deletions(-) create mode 100644 src/command.rs diff --git a/config/default.toml b/config/default.toml index 1f29b0b..2a585b7 100644 --- a/config/default.toml +++ b/config/default.toml @@ -1,7 +1,10 @@ [colortheme] -background = [26, 26, 28] -command_bar = [20, 28, 20] +background = [0x26, 0x26, 0x28] +command-bar = [0x20, 0x28, 0x20] +cmdbar-prompt = [0x40, 0x40, 0x40] +cmdbar-cmdexist = [0x40, 0xff, 0x40] +cmdbar-cmdunexist = [0xff, 0x40, 0x40] [command] -new_window = "n" +new-window = "n" quit = "q" diff --git a/src/application.rs b/src/application.rs index c4c1b6c..68a7f71 100644 --- a/src/application.rs +++ b/src/application.rs @@ -15,7 +15,9 @@ use crossterm::{ use unicode_width::UnicodeWidthStr; -use crate::{colortheme::ColorTheme, iterate_config, window::Window, Config}; +use crate::{ + colortheme::ColorTheme, command::CommandProcessor, iterate_config, window::Window, Config, +}; pub struct Application { config: Config, @@ -23,10 +25,7 @@ pub struct Application { stdout: Stdout, size: (u16, u16), windows: HashMap, - command_mode: bool, - command: String, - cmdbar_show: String, - cmdbar_prompt: String, + cmdprocessor: CommandProcessor, } impl Application { @@ -46,142 +45,84 @@ impl Application { stdout: stdout(), size: terminal::size()?, windows: HashMap::new(), - command_mode: true, - command: String::new(), - cmdbar_show: String::new(), - cmdbar_prompt: String::from("New workspace"), + cmdprocessor: CommandProcessor::new(), }) } pub fn exec(&mut self) -> io::Result<()> { self.render()?; - self.stdout.flush()?; loop { if event::poll(Duration::from_secs(5))? { match event::read()? { event::Event::FocusGained => queue!(self.stdout, cursor::Show)?, event::Event::FocusLost => queue!(self.stdout, cursor::Hide)?, event::Event::Key(key_event) => { - let KeyEvent { - code, modifiers, .. - } = key_event; - match code { - event::KeyCode::Backspace => { - if self.command_mode { - self.command.pop(); - self.cmdbar_show.clone_from(&self.command); - } - } - event::KeyCode::Enter => { - if self.command_mode { - self.command.clear(); - self.cmdbar_show.clear(); - } - } - event::KeyCode::Left => (), - event::KeyCode::Right => (), - event::KeyCode::Up => (), - event::KeyCode::Down => (), - event::KeyCode::Home => (), - event::KeyCode::End => (), - event::KeyCode::PageUp => (), - event::KeyCode::PageDown => (), - event::KeyCode::Tab => (), - event::KeyCode::BackTab => (), - event::KeyCode::Delete => (), - event::KeyCode::Insert => (), - event::KeyCode::F(_) => (), - event::KeyCode::Char(c) => { - if self.command_mode { - let mut b = true; - if modifiers.contains(KeyModifiers::CONTROL) { - self.command += "C-"; - } else if modifiers.contains(KeyModifiers::ALT) { - self.command += "A-"; - } else { - b = false; - } - self.command.push(c); - if b { - self.command.push(' '); - } - self.cmdbar_show.clone_from(&self.command); - } - } - event::KeyCode::Null => (), - event::KeyCode::Esc => (), - event::KeyCode::CapsLock => (), - event::KeyCode::ScrollLock => (), - event::KeyCode::NumLock => (), - event::KeyCode::PrintScreen => (), - event::KeyCode::Pause => (), - event::KeyCode::Menu => (), - event::KeyCode::KeypadBegin => (), - event::KeyCode::Media(media) => (), - event::KeyCode::Modifier(modifier) => (), + if self.cmdprocessor.is_command_mode() { + self.cmdprocessor.key_event(key_event); } } event::Event::Mouse(mouse_event) => (), event::Event::Paste(_) => (), event::Event::Resize(w, h) => self.size = (w, h), } - self.render()?; - let f = |_, value| { - if let &toml::Value::String(ref s) = value { - s == &self.command - } else { - false - } - }; - if let toml::Value::Table(map) = &self.config.get("command").unwrap() { - if let Some((name, _)) = iterate_config(map, &f) { - match name.as_str() { - "quit" => break, - _ => self.cmdbar_prompt = String::from("Unknown command"), - } - self.command.clear(); - } + if self.command_process() { + break; } - self.stdout.flush()?; + self.render()?; } else { } } self.render()?; - self.stdout.flush()?; thread::sleep(Duration::from_millis(200)); Ok(()) } + fn command_process(&mut self) -> bool { + if let toml::Value::Table(map) = &self.config.get("command").unwrap() { + if self.cmdprocessor.process(map) { + true + } else { + false + } + } else { + false + } + } + fn render(&mut self) -> io::Result<()> { let mut show = String::new(); - for c in self.cmdbar_show.chars() { + for c in self.cmdprocessor.get_show().chars() { show.push(c); if UnicodeWidthStr::width(show.as_str()) as u16 >= self.size.0 - 1 { break; } } - let x = cursor::position().unwrap().0; - let mut prompt = String::new(); - for c in self.cmdbar_prompt.chars() { - show.push(c); - if UnicodeWidthStr::width(prompt.as_str()) as u16 >= self.size.0 - 1 - x { - break; - } - } + let x = UnicodeWidthStr::width(show.as_str()) as u16; + let mut prompt = String::new(); + for c in self.cmdprocessor.get_prompt().chars() { + prompt.push(c); + if UnicodeWidthStr::width(prompt.as_str()) as u16 >= self.size.0 - 1 - x { + break; + } + } + let x = x + UnicodeWidthStr::width(prompt.as_str()) as u16; queue!( self.stdout, style::SetBackgroundColor(self.colortheme.command_bar), cursor::MoveTo(0, self.size.1 - 1), + style::SetForegroundColor(if self.cmdprocessor.unknown() { + self.colortheme.cmdbar_cmdunexist + } else { + self.colortheme.cmdbar_cmdexist + }), style::Print(show), - style::SetForegroudColor(self.colortheme.cmdbar_prompt), - style::Print(prompt), - )?; - let x = cursor::position().unwrap().0; - queue!( - self.stdout, + cursor::SavePosition, + style::SetForegroundColor(self.colortheme.cmdbar_prompt), + style::Print(prompt), style::Print(" ".repeat((self.size.0 - x) as usize)), - cursor::MoveTo(x, self.size.1 - 1), + cursor::RestorePosition, )?; + self.stdout.flush()?; Ok(()) } } diff --git a/src/colortheme.rs b/src/colortheme.rs index 649953d..c12570d 100644 --- a/src/colortheme.rs +++ b/src/colortheme.rs @@ -4,6 +4,9 @@ use toml::{Table, Value}; pub struct ColorTheme { pub background: Color, pub command_bar: Color, + pub cmdbar_prompt: Color, + pub cmdbar_cmdexist: Color, + pub cmdbar_cmdunexist: Color, } impl From<&Table> for ColorTheme { @@ -20,10 +23,16 @@ impl From<&Table> for ColorTheme { } }; let background = f("background"); - let command_bar = f("command_bar"); + let command_bar = f("command-bar"); + let cmdbar_prompt = f("cmdbar-prompt"); + let cmdbar_cmdexist = f("cmdbar-cmdexist"); + let cmdbar_cmdunexist = f("cmdbar-cmdunexist"); Self { background, command_bar, + cmdbar_prompt, + cmdbar_cmdexist, + cmdbar_cmdunexist, } } } diff --git a/src/command.rs b/src/command.rs new file mode 100644 index 0000000..9ba3a02 --- /dev/null +++ b/src/command.rs @@ -0,0 +1,125 @@ +use crossterm::event::{self, KeyEvent, KeyModifiers}; + +use crate::{iterate_config, Config}; + +pub struct CommandProcessor { + command_mode: bool, + command: String, + unknown_cmd: bool, + cmdbar_show: String, + cmdbar_prompt: String, +} + +impl CommandProcessor { + pub fn new() -> Self { + Self { + command_mode: true, + command: String::new(), + unknown_cmd: false, + cmdbar_show: String::new(), + cmdbar_prompt: String::from("New workspace"), + } + } + + pub fn get_show(&self) -> &String { + &self.cmdbar_show + } + + pub fn get_prompt(&self) -> &String { + &self.cmdbar_prompt + } + + pub fn unknown(&self) -> bool { + self.unknown_cmd + } + + pub fn is_command_mode(&self) -> bool { + self.command_mode + } + + pub fn process(&mut self, map: &Config) -> bool { + let f = |_, value| { + if let &toml::Value::String(ref s) = value { + s == &self.command + } else { + false + } + }; + if let Some((name, _)) = iterate_config(map, &f) { + self.command.clear(); + self.cmdbar_prompt = format!(" - {}", name); + self.unknown_cmd = false; + match name.as_str() { + "quit" => return true, + _ => (), + } + } else if !self.cmdbar_show.is_empty() { + self.cmdbar_prompt = String::from(" | Unknown command"); + self.unknown_cmd = true; + } else { + self.cmdbar_prompt.clear(); + } + false + } + + pub fn key_event(&mut self, key: KeyEvent) { + let KeyEvent { + code, modifiers, .. + } = key; + match code { + event::KeyCode::Backspace => { + if self.command_mode { + self.command.pop(); + self.cmdbar_show.clone_from(&self.command); + } + } + event::KeyCode::Enter => { + if self.command_mode { + self.command.clear(); + self.cmdbar_show.clear(); + } + } + event::KeyCode::Left => (), + event::KeyCode::Right => (), + event::KeyCode::Up => (), + event::KeyCode::Down => (), + event::KeyCode::Home => (), + event::KeyCode::End => (), + event::KeyCode::PageUp => (), + event::KeyCode::PageDown => (), + event::KeyCode::Tab => (), + event::KeyCode::BackTab => (), + event::KeyCode::Delete => (), + event::KeyCode::Insert => (), + event::KeyCode::F(_) => (), + event::KeyCode::Char(c) => { + if self.command_mode { + let mut b = true; + if modifiers.contains(KeyModifiers::CONTROL) { + self.command += "C-"; + } else if modifiers.contains(KeyModifiers::ALT) { + self.command += "A-"; + } else { + b = false; + } + self.command.push(c); + if b { + self.command.push(' '); + } + self.cmdbar_show.clone_from(&self.command); + } + } + event::KeyCode::Null => (), + event::KeyCode::Esc => (), + event::KeyCode::CapsLock => (), + event::KeyCode::ScrollLock => (), + event::KeyCode::NumLock => (), + event::KeyCode::PrintScreen => (), + event::KeyCode::Pause => (), + event::KeyCode::Menu => (), + event::KeyCode::KeypadBegin => (), + event::KeyCode::Media(media) => (), + event::KeyCode::Modifier(modifier) => (), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 44ecab1..3413406 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ pub mod application; pub mod colortheme; pub mod window; +pub mod command; use std::io;