use anyhow::{anyhow, Result}; use iocraft::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers}; #[derive(Clone, Copy, Debug)] pub struct KeyBinding { pub code: KeyCode, pub modifiers: KeyModifiers, } impl KeyBinding { pub fn parse(s: &str) -> Result { let s = s.trim(); let (modifiers, key_str) = if s.contains('+') { let parts: Vec<&str> = s.splitn(2, '+').collect(); let mut mods = KeyModifiers::NONE; for m in parts[0].to_lowercase().split('+') { match m.trim() { "ctrl" | "control" => mods.insert(KeyModifiers::CONTROL), "shift" => mods.insert(KeyModifiers::SHIFT), "alt" => mods.insert(KeyModifiers::ALT), other => return Err(anyhow!("unknown modifier: {}", other)), } } (mods, parts[1].trim()) } else { (KeyModifiers::NONE, s) }; let code = parse_key_code(key_str)?; Ok(Self { code, modifiers }) } pub fn matches(&self, event: &KeyEvent) -> bool { event.kind != KeyEventKind::Release && event.code == self.code && event.modifiers == self.modifiers } } fn parse_key_code(s: &str) -> Result { let lower = s.to_lowercase(); match lower.as_str() { "enter" | "return" => Ok(KeyCode::Enter), "tab" => Ok(KeyCode::Tab), "backtab" => Ok(KeyCode::BackTab), "esc" | "escape" => Ok(KeyCode::Esc), "up" => Ok(KeyCode::Up), "down" => Ok(KeyCode::Down), "left" => Ok(KeyCode::Left), "right" => Ok(KeyCode::Right), "home" => Ok(KeyCode::Home), "end" => Ok(KeyCode::End), "pageup" | "page_up" => Ok(KeyCode::PageUp), "pagedown" | "page_down" => Ok(KeyCode::PageDown), "delete" | "del" => Ok(KeyCode::Delete), "space" => Ok(KeyCode::Char(' ')), "backspace" => Ok(KeyCode::Backspace), "insert" => Ok(KeyCode::Insert), "f1" => Ok(KeyCode::F(1)), "f2" => Ok(KeyCode::F(2)), "f3" => Ok(KeyCode::F(3)), "f4" => Ok(KeyCode::F(4)), "f5" => Ok(KeyCode::F(5)), "f6" => Ok(KeyCode::F(6)), "f7" => Ok(KeyCode::F(7)), "f8" => Ok(KeyCode::F(8)), "f9" => Ok(KeyCode::F(9)), "f10" => Ok(KeyCode::F(10)), "f11" => Ok(KeyCode::F(11)), "f12" => Ok(KeyCode::F(12)), "null" => Ok(KeyCode::Null), c if c.len() == 1 => Ok(KeyCode::Char(c.chars().next().unwrap())), _ => Err(anyhow!("unknown key: {}", s)), } } #[derive(Clone, Debug)] pub struct Keybindings { pub quit: KeyBinding, pub tab_next: KeyBinding, pub tab_prev: KeyBinding, pub up: KeyBinding, pub down: KeyBinding, pub left: KeyBinding, pub right: KeyBinding, pub select: KeyBinding, pub close_connection: KeyBinding, pub close_all_connections: KeyBinding, pub refresh: KeyBinding, } impl Keybindings { pub fn from_config(cfg: &crate::config::KeybindingsConfig) -> Result { Ok(Self { quit: KeyBinding::parse(&cfg.quit)?, tab_next: KeyBinding::parse(&cfg.tab_next)?, tab_prev: KeyBinding::parse(&cfg.tab_prev)?, up: KeyBinding::parse(&cfg.up)?, down: KeyBinding::parse(&cfg.down)?, left: KeyBinding::parse(&cfg.left)?, right: KeyBinding::parse(&cfg.right)?, select: KeyBinding::parse(&cfg.select)?, close_connection: KeyBinding::parse(&cfg.close_connection)?, close_all_connections: KeyBinding::parse(&cfg.close_all_connections)?, refresh: KeyBinding::parse(&cfg.refresh)?, }) } }