Add keyboard commands
This commit is contained in:
parent
a5dbccee4f
commit
c2acb446e7
15
src/app.rs
15
src/app.rs
@ -6,12 +6,14 @@ use crate::app_action::AppAction;
|
||||
use crate::app_event::AppEvent;
|
||||
use crate::component::Component;
|
||||
use crate::components;
|
||||
use crate::keys::key_commands::KeyCommand;
|
||||
use crate::tui;
|
||||
|
||||
pub struct App {
|
||||
pub tui: tui::Tui,
|
||||
pub tick_rate: Duration,
|
||||
pub components: Vec<Box<dyn Component>>,
|
||||
pub key_commands: Vec<KeyCommand>,
|
||||
|
||||
should_quit: bool,
|
||||
}
|
||||
@ -20,13 +22,24 @@ impl App {
|
||||
pub fn new(tick_rate: Duration) -> Result<Self> {
|
||||
let tui = tui::init()?;
|
||||
|
||||
let global_keys = components::global_keys::GlobalKeys::default();
|
||||
let mut key_commands = vec![KeyCommand {
|
||||
key_code: "q".to_string(),
|
||||
description: "Quit molehole".to_string(),
|
||||
action: Some(AppAction::Quit),
|
||||
}];
|
||||
|
||||
let global_keys = components::global_keys::GlobalKeys {
|
||||
key_commands: key_commands.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
let hello_world = components::hello_world::HelloWorld::default();
|
||||
|
||||
Ok(Self {
|
||||
tui,
|
||||
tick_rate,
|
||||
components: vec![Box::new(hello_world), Box::new(global_keys)],
|
||||
key_commands,
|
||||
|
||||
should_quit: false,
|
||||
})
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub enum AppAction {
|
||||
Quit
|
||||
#[default]
|
||||
Quit,
|
||||
}
|
||||
|
@ -4,41 +4,69 @@ use ratatui::widgets::*;
|
||||
|
||||
use crate::app_action::AppAction;
|
||||
use crate::component::Component;
|
||||
use crate::keys::key_commands::*;
|
||||
|
||||
#[derive(Default, Clone, Copy)]
|
||||
#[derive(Default)]
|
||||
pub struct GlobalKeys {
|
||||
should_show: bool,
|
||||
pub key_commands: Vec<KeyCommand>,
|
||||
|
||||
pub should_show: bool,
|
||||
pub scroll: u16,
|
||||
}
|
||||
|
||||
impl Component for GlobalKeys {
|
||||
fn init(&mut self) -> eyre::Result<()> {
|
||||
self.key_commands.push(KeyCommand {
|
||||
key_code: "?".to_string(),
|
||||
description: "Show help menu".to_string(),
|
||||
action: None,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_key_event(
|
||||
&mut self,
|
||||
key: KeyEvent,
|
||||
) -> eyre::Result<Option<AppAction>> {
|
||||
if key.kind == KeyEventKind::Press {
|
||||
return match key.code {
|
||||
KeyCode::Char('q') => Ok(Some(AppAction::Quit)),
|
||||
KeyCode::Char('?') => {
|
||||
self.should_show = !self.should_show;
|
||||
Ok(None)
|
||||
for key_command in self.key_commands.iter_mut() {
|
||||
if key_command.key_code == serialize_key_event(key) {
|
||||
if serialize_key_event(key) == "?" {
|
||||
self.should_show = !self.should_show;
|
||||
}
|
||||
|
||||
return Ok(key_command.action);
|
||||
}
|
||||
_ => Ok(None),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn render(&mut self, frame: &mut Frame, rect: Rect) -> eyre::Result<()> {
|
||||
let horizontal_center = Layout::default()
|
||||
.direction(Direction::Horizontal);
|
||||
|
||||
let block = Block::default()
|
||||
.title("Keyboard shortcuts")
|
||||
.borders(Borders::ALL);
|
||||
|
||||
let mut lines: Vec<Line> = vec![];
|
||||
for key_command in self.key_commands.iter_mut() {
|
||||
let command = Span::from(key_command.key_code.clone());
|
||||
let description =
|
||||
Span::from(key_command.description.clone()).italic();
|
||||
let spacer = Span::from(" ");
|
||||
|
||||
let line = Line::from(vec![command, spacer, description]);
|
||||
lines.push(line);
|
||||
}
|
||||
|
||||
let commands = Paragraph::new(lines)
|
||||
.block(block)
|
||||
.wrap(Wrap { trim: true })
|
||||
.scroll((self.scroll, 0));
|
||||
|
||||
if self.should_show {
|
||||
frame.render_widget(block, rect);
|
||||
frame.render_widget(commands, rect);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -1,7 +1,9 @@
|
||||
use ratatui::prelude::*;
|
||||
use ratatui::widgets::Paragraph;
|
||||
|
||||
use crate::app_action::AppAction;
|
||||
use crate::component::Component;
|
||||
use crate::keys::key_commands::*;
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct HelloWorld {
|
||||
@ -14,7 +16,16 @@ impl Component for HelloWorld {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_key_event(
|
||||
&mut self,
|
||||
key: crossterm::event::KeyEvent,
|
||||
) -> eyre::Result<Option<AppAction>> {
|
||||
self.text = serialize_key_event(key);
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn render(&mut self, frame: &mut Frame, rect: Rect) -> eyre::Result<()> {
|
||||
|
||||
frame.render_widget(Paragraph::new(self.text.clone()), rect);
|
||||
|
||||
Ok(())
|
||||
|
56
src/keys/key_commands.rs
Normal file
56
src/keys/key_commands.rs
Normal file
@ -0,0 +1,56 @@
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
|
||||
use crate::app_action::AppAction;
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct KeyCommand {
|
||||
pub key_code: String,
|
||||
pub description: String,
|
||||
pub action: Option<AppAction>,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for KeyCommand {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}\t{}", self.key_code, self.description)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serialize_key_event(event: KeyEvent) -> String {
|
||||
let mut modifiers = Vec::with_capacity(3);
|
||||
if event.modifiers.intersects(KeyModifiers::CONTROL) {
|
||||
modifiers.push("ctrl");
|
||||
}
|
||||
if event.modifiers.intersects(KeyModifiers::SUPER)
|
||||
|| event.modifiers.intersects(KeyModifiers::HYPER)
|
||||
|| event.modifiers.intersects(KeyModifiers::META)
|
||||
{
|
||||
modifiers.push("super");
|
||||
}
|
||||
if event.modifiers.intersects(KeyModifiers::ALT) {
|
||||
modifiers.push("alt");
|
||||
}
|
||||
|
||||
let char;
|
||||
let key = match event.code {
|
||||
KeyCode::Backspace => "del",
|
||||
KeyCode::Enter => "enter",
|
||||
KeyCode::Left => "left",
|
||||
KeyCode::Right => "right",
|
||||
KeyCode::Up => "up",
|
||||
KeyCode::Down => "down",
|
||||
KeyCode::Tab => "tab",
|
||||
KeyCode::Delete => "del",
|
||||
KeyCode::Char(c) if c == ' ' => "space",
|
||||
KeyCode::Char(c) => {
|
||||
char = c.to_string();
|
||||
&char
|
||||
}
|
||||
KeyCode::Esc => "esc",
|
||||
_ => "",
|
||||
};
|
||||
let separator = if modifiers.len() > 0 { "-" } else { "" };
|
||||
let serialized_event =
|
||||
format!("{}{}{}", modifiers.join("-"), separator, key);
|
||||
|
||||
serialized_event
|
||||
}
|
1
src/keys/mod.rs
Normal file
1
src/keys/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod key_commands;
|
@ -3,6 +3,7 @@ mod app_action;
|
||||
mod app_event;
|
||||
mod component;
|
||||
mod components;
|
||||
mod keys;
|
||||
mod tui;
|
||||
|
||||
use eyre::Result;
|
||||
|
Loading…
Reference in New Issue
Block a user