aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShav Kinderlehrer <[email protected]>2024-03-06 14:14:07 -0500
committerShav Kinderlehrer <[email protected]>2024-03-06 14:14:07 -0500
commitc2acb446e7868b67e5743d54cee5c64c469e5a18 (patch)
tree0a0243b2d064fe2d1d6acf2a21ea93a40ad9a02a
parenta5dbccee4f22de991e33449ae1d1835269c6075d (diff)
downloadmolehole-c2acb446e7868b67e5743d54cee5c64c469e5a18.tar.gz
Add keyboard commands
-rw-r--r--src/app.rs15
-rw-r--r--src/app_action.rs4
-rw-r--r--src/components/global_keys.rs54
-rw-r--r--src/components/hello_world.rs11
-rw-r--r--src/keys/key_commands.rs56
-rw-r--r--src/keys/mod.rs1
-rw-r--r--src/main.rs1
7 files changed, 127 insertions, 15 deletions
diff --git a/src/app.rs b/src/app.rs
index 22a8c40..a1fd1e4 100644
--- a/src/app.rs
+++ b/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,
})
}
diff --git a/src/app_action.rs b/src/app_action.rs
index 104e4f9..8bbe9dd 100644
--- a/src/app_action.rs
+++ b/src/app_action.rs
@@ -1,3 +1,5 @@
+#[derive(Default, Clone, Copy)]
pub enum AppAction {
- Quit
+ #[default]
+ Quit,
}
diff --git a/src/components/global_keys.rs b/src/components/global_keys.rs
index e99e2e0..67b1b6a 100644
--- a/src/components/global_keys.rs
+++ b/src/components/global_keys.rs
@@ -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(())
diff --git a/src/components/hello_world.rs b/src/components/hello_world.rs
index afb9d47..2e3a2ee 100644
--- a/src/components/hello_world.rs
+++ b/src/components/hello_world.rs
@@ -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(())
diff --git a/src/keys/key_commands.rs b/src/keys/key_commands.rs
new file mode 100644
index 0000000..6124560
--- /dev/null
+++ b/src/keys/key_commands.rs
@@ -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
+}
diff --git a/src/keys/mod.rs b/src/keys/mod.rs
new file mode 100644
index 0000000..c884843
--- /dev/null
+++ b/src/keys/mod.rs
@@ -0,0 +1 @@
+pub mod key_commands;
diff --git a/src/main.rs b/src/main.rs
index 16bee88..96ac2f0 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,6 +3,7 @@ mod app_action;
mod app_event;
mod component;
mod components;
+mod keys;
mod tui;
use eyre::Result;