Crokey helps incorporate configurable keybindings in crossterm based terminal applications by providing functions
- parsing key combinations from strings
- describing key combinations in strings
- parsing key combinations at compile time
- combining Crossterm key events in key combinations
A KeyCombination is made of 1 to 3 "normal" keys with some optional modifiers (alt, shift, ctrl).
It can be parsed, ergonomically built with the key! macro, obtained from key events.
With a Combiner, you can change raw Crossterm key events into key combinations.
When the terminal is modern enough and supports the Kitty protocol, complex combinations with up to three non-modifier keys may be formed, for example Ctrl-Alt-Shift-g-y or Space-!.
For standard ANSI terminals, only regular combinations are available, like Shift-o, Ctrl-Alt-Shift-g or i.
The combiner works in both cases:
if you presses the ctrl, i, and u keys at the same time, it will result in one combination (ctrl-i-u) on a kitty-compatible terminal, and as a sequence of 2 key combinations (ctrl-i then ctrl-u assuming you started pressing the i before the u) in other terminals.
The print_key example shows how to deal with that:
let fmt = KeyCombinationFormat::default();letmut combiner = Combiner::default();let combines = combiner.enable_combining().unwrap();if combines {println!("Your terminal supports combining keys");}else{println!("Your terminal doesn't support combining non-modifier keys");}println!("Type any key combination");loop{
terminal::enable_raw_mode().unwrap();let e = read();
terminal::disable_raw_mode().unwrap();match e {Ok(Event::Key(key_event)) => {ifletSome(key_combination) = combiner.transform(key_event){match key_combination {key!(ctrl-c) | key!(ctrl-q) => {println!("quitting");break;}
_ => {println!("You typed {}", fmt.to_string(key_combination));}}}},
...}}Those strings are usually provided by a configuration file.
use crossterm::event::{KeyCode,KeyEvent,KeyModifiers};assert_eq!(
crokey::parse("alt-enter").unwrap(),KeyEvent::new(KeyCode::Enter,KeyModifiers::ALT),);assert_eq!(
crokey::parse("shift-F6").unwrap(),KeyEvent::new(KeyCode::F(6),KeyModifiers::SHIFT),);Those key combinations are parsed at compile time and have zero runtime cost.
They're efficient and convenient for matching events or defining hardcoded keybindings.
match key_event.into(){key!(ctrl-c) => {println!("Arg! You savagely killed me with a {}", fmt.to_string(key_event).red());break;}key!(ctrl-q) => {println!("You typed {} which gracefully quits", fmt.to_string(key_event).green());break;}
_ => {println!("You typed {}", fmt.to_string(key_event).blue());}}Complete example in /examples/print_key:
The key! macro can be called in const contexts:
const quit:KeyCombination = key!(ctrl-q);use crokey::*;use crossterm::event::{KeyCode,KeyEvent,KeyModifiers};// The default formatlet format = KeyCombinationFormat::default();assert_eq!(format.to_string(key!(shift-a)),"Shift-a");assert_eq!(format.to_string(key!(ctrl-c)),"Ctrl-c");// A more compact formatlet format = KeyCombinationFormat::default().with_implicit_shift().with_control("^");assert_eq!(format.to_string(key!(shift-a)),"A");assert_eq!(format.to_string(key!(ctrl-c)),"^c");With the "serde" feature enabled, you can read configuration files in a direct way:
use{
crokey::*,
crossterm::event::KeyEvent,
serde::Deserialize,
std::collections::HashMap,};#[derive(Debug,Deserialize)]structConfig{keybindings:HashMap<KeyCombination,String>,}staticCONFIG_HJSON:&str = r#"{ keybindings: { a: aardvark shift-b: babirussa ctrl-k: koala alt-j: jaguar }}"#;let config:Config = deser_hjson::from_str(CONFIG_HJSON).unwrap();let key:KeyCombination = key!(shift-b);assert_eq!(
config.keybindings.get(&key).unwrap(),"babirussa",);You can use any Serde compatible format such as JSON or TOML.
Crokey includes and reexports Crossterm, so you don't have to import it and to avoid conflicts.
