boost command typing experience

This commit is contained in:
pointer-to-bios 2024-09-25 14:29:31 +08:00
parent e0ba4e0661
commit c2a0942902
5 changed files with 184 additions and 105 deletions

View File

@ -1,7 +1,10 @@
[colortheme] [colortheme]
background = [26, 26, 28] background = [0x26, 0x26, 0x28]
command_bar = [20, 28, 20] command-bar = [0x20, 0x28, 0x20]
cmdbar-prompt = [0x40, 0x40, 0x40]
cmdbar-cmdexist = [0x40, 0xff, 0x40]
cmdbar-cmdunexist = [0xff, 0x40, 0x40]
[command] [command]
new_window = "n" new-window = "n"
quit = "q" quit = "q"

View File

@ -15,7 +15,9 @@ use crossterm::{
use unicode_width::UnicodeWidthStr; 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 { pub struct Application {
config: Config, config: Config,
@ -23,10 +25,7 @@ pub struct Application {
stdout: Stdout, stdout: Stdout,
size: (u16, u16), size: (u16, u16),
windows: HashMap<usize, Window>, windows: HashMap<usize, Window>,
command_mode: bool, cmdprocessor: CommandProcessor,
command: String,
cmdbar_show: String,
cmdbar_prompt: String,
} }
impl Application { impl Application {
@ -46,142 +45,84 @@ impl Application {
stdout: stdout(), stdout: stdout(),
size: terminal::size()?, size: terminal::size()?,
windows: HashMap::new(), windows: HashMap::new(),
command_mode: true, cmdprocessor: CommandProcessor::new(),
command: String::new(),
cmdbar_show: String::new(),
cmdbar_prompt: String::from("New workspace"),
}) })
} }
pub fn exec(&mut self) -> io::Result<()> { pub fn exec(&mut self) -> io::Result<()> {
self.render()?; self.render()?;
self.stdout.flush()?;
loop { loop {
if event::poll(Duration::from_secs(5))? { if event::poll(Duration::from_secs(5))? {
match event::read()? { match event::read()? {
event::Event::FocusGained => queue!(self.stdout, cursor::Show)?, event::Event::FocusGained => queue!(self.stdout, cursor::Show)?,
event::Event::FocusLost => queue!(self.stdout, cursor::Hide)?, event::Event::FocusLost => queue!(self.stdout, cursor::Hide)?,
event::Event::Key(key_event) => { event::Event::Key(key_event) => {
let KeyEvent { if self.cmdprocessor.is_command_mode() {
code, modifiers, .. self.cmdprocessor.key_event(key_event);
} = 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) => (),
} }
} }
event::Event::Mouse(mouse_event) => (), event::Event::Mouse(mouse_event) => (),
event::Event::Paste(_) => (), event::Event::Paste(_) => (),
event::Event::Resize(w, h) => self.size = (w, h), event::Event::Resize(w, h) => self.size = (w, h),
} }
if self.command_process() {
break;
}
self.render()?; 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();
}
}
self.stdout.flush()?;
} else { } else {
} }
} }
self.render()?; self.render()?;
self.stdout.flush()?;
thread::sleep(Duration::from_millis(200)); thread::sleep(Duration::from_millis(200));
Ok(()) 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<()> { fn render(&mut self) -> io::Result<()> {
let mut show = String::new(); let mut show = String::new();
for c in self.cmdbar_show.chars() { for c in self.cmdprocessor.get_show().chars() {
show.push(c); show.push(c);
if UnicodeWidthStr::width(show.as_str()) as u16 >= self.size.0 - 1 { if UnicodeWidthStr::width(show.as_str()) as u16 >= self.size.0 - 1 {
break; break;
} }
} }
let x = cursor::position().unwrap().0; let x = UnicodeWidthStr::width(show.as_str()) as u16;
let mut prompt = String::new(); let mut prompt = String::new();
for c in self.cmdbar_prompt.chars() { for c in self.cmdprocessor.get_prompt().chars() {
show.push(c); prompt.push(c);
if UnicodeWidthStr::width(prompt.as_str()) as u16 >= self.size.0 - 1 - x { if UnicodeWidthStr::width(prompt.as_str()) as u16 >= self.size.0 - 1 - x {
break; break;
} }
} }
let x = x + UnicodeWidthStr::width(prompt.as_str()) as u16;
queue!( queue!(
self.stdout, self.stdout,
style::SetBackgroundColor(self.colortheme.command_bar), style::SetBackgroundColor(self.colortheme.command_bar),
cursor::MoveTo(0, self.size.1 - 1), 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::Print(show),
style::SetForegroudColor(self.colortheme.cmdbar_prompt), cursor::SavePosition,
style::SetForegroundColor(self.colortheme.cmdbar_prompt),
style::Print(prompt), style::Print(prompt),
)?;
let x = cursor::position().unwrap().0;
queue!(
self.stdout,
style::Print(" ".repeat((self.size.0 - x) as usize)), style::Print(" ".repeat((self.size.0 - x) as usize)),
cursor::MoveTo(x, self.size.1 - 1), cursor::RestorePosition,
)?; )?;
self.stdout.flush()?;
Ok(()) Ok(())
} }
} }

View File

@ -4,6 +4,9 @@ use toml::{Table, Value};
pub struct ColorTheme { pub struct ColorTheme {
pub background: Color, pub background: Color,
pub command_bar: Color, pub command_bar: Color,
pub cmdbar_prompt: Color,
pub cmdbar_cmdexist: Color,
pub cmdbar_cmdunexist: Color,
} }
impl From<&Table> for ColorTheme { impl From<&Table> for ColorTheme {
@ -20,10 +23,16 @@ impl From<&Table> for ColorTheme {
} }
}; };
let background = f("background"); 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 { Self {
background, background,
command_bar, command_bar,
cmdbar_prompt,
cmdbar_cmdexist,
cmdbar_cmdunexist,
} }
} }
} }

125
src/command.rs Normal file
View File

@ -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) => (),
}
}
}

View File

@ -1,6 +1,7 @@
pub mod application; pub mod application;
pub mod colortheme; pub mod colortheme;
pub mod window; pub mod window;
pub mod command;
use std::io; use std::io;