Skip to main content

telex/
command.rs

1//! Command system for keyboard shortcuts.
2//!
3//! Provides `use_command` hook for registering keybindings in components.
4
5use crossterm::event::{KeyCode, KeyModifiers};
6use std::cell::RefCell;
7use std::collections::HashMap;
8use std::rc::Rc;
9
10/// A keyboard shortcut definition.
11#[derive(Debug, Clone, PartialEq, Eq, Hash)]
12pub struct KeyBinding {
13    pub code: KeyCode,
14    pub modifiers: KeyModifiers,
15}
16
17impl KeyBinding {
18    /// Create a new key binding.
19    pub fn new(code: KeyCode, modifiers: KeyModifiers) -> Self {
20        Self { code, modifiers }
21    }
22
23    /// Create a binding for a single key (no modifiers).
24    pub fn key(code: KeyCode) -> Self {
25        Self::new(code, KeyModifiers::NONE)
26    }
27
28    /// Create a binding for Ctrl+key.
29    pub fn ctrl(c: char) -> Self {
30        Self::new(KeyCode::Char(c), KeyModifiers::CONTROL)
31    }
32
33    /// Create a binding for Alt+key.
34    pub fn alt(c: char) -> Self {
35        Self::new(KeyCode::Char(c), KeyModifiers::ALT)
36    }
37
38    /// Create a binding for Shift+key.
39    pub fn shift(c: char) -> Self {
40        Self::new(KeyCode::Char(c), KeyModifiers::SHIFT)
41    }
42
43    /// Check if this binding matches a key event.
44    pub fn matches(&self, code: KeyCode, modifiers: KeyModifiers) -> bool {
45        self.code == code && self.modifiers == modifiers
46    }
47}
48
49/// Callback type for commands.
50pub type CommandCallback = Rc<dyn Fn()>;
51
52/// Registry of active commands.
53#[derive(Default)]
54pub struct CommandRegistry {
55    commands: RefCell<HashMap<KeyBinding, CommandCallback>>,
56}
57
58impl CommandRegistry {
59    pub fn new() -> Self {
60        Self::default()
61    }
62
63    /// Register a command with a keybinding.
64    pub fn register(&self, binding: KeyBinding, callback: CommandCallback) {
65        self.commands.borrow_mut().insert(binding, callback);
66    }
67
68    /// Clear all registered commands (call before each render).
69    pub fn clear(&self) {
70        self.commands.borrow_mut().clear();
71    }
72
73    /// Try to execute a command matching the given key event.
74    /// Returns true if a command was executed.
75    pub fn execute(&self, code: KeyCode, modifiers: KeyModifiers) -> bool {
76        let commands = self.commands.borrow();
77        for (binding, callback) in commands.iter() {
78            if binding.matches(code, modifiers) {
79                callback();
80                return true;
81            }
82        }
83        false
84    }
85
86    /// Check if any command is registered for this key.
87    pub fn has_binding(&self, code: KeyCode, modifiers: KeyModifiers) -> bool {
88        let commands = self.commands.borrow();
89        commands.keys().any(|b| b.matches(code, modifiers))
90    }
91}