use std::collections::BTreeMap;
use std::sync::RwLock;
use winit::keyboard::KeyCode;
pub const ALL: [(&str, &str); 25] = [
("D", "Dialing directory"),
("N", "Call in a new tab"),
("T", "Next tab (Shift: back)"),
("R", "Call again, or redial now"),
("L", "Send login name, password"),
("H", "Hang up"),
("J", "Run or stop a script"),
("X", "Exit BadTerm"),
("U", "Upload"),
("G", "Download"),
("C", "Capture to a file"),
("S", "Save scrollback or screen"),
("F", "View ANSI art"),
("B", "Scrollback"),
("V", "Paste"),
("O", "Settings"),
("P", "Phosphor mode"),
("A", "Aspect ratio"),
("W", "8- or 9-dot characters"),
("Enter", "Full screen"),
("I", "iCE colors or blinking"),
("E", "CP437 or UTF-8"),
("K", "Reveal (Viewdata)"),
("M", "Sound and music"),
("Z", "Help"),
];
pub const KEYS: [&str; 27] = [
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
"X", "Y", "Z", "Enter",
];
static MOVED: RwLock<BTreeMap<String, String>> = RwLock::new(BTreeMap::new());
pub fn set(moved: &BTreeMap<String, String>) {
let valid = moved
.iter()
.filter(|(from, to)| ALL.iter().any(|(k, _)| k == from) && KEYS.contains(&to.as_str()))
.map(|(a, b)| (a.clone(), b.clone()))
.collect();
if let Ok(mut m) = MOVED.write() {
*m = valid;
}
}
pub fn key(usual: &str) -> String {
MOVED.read().ok().and_then(|m| m.get(usual).cloned()).unwrap_or_else(|| usual.to_string())
}
pub fn label(usual: &str) -> String {
format!("{}-{}", crate::ui::HOTKEY, key(usual))
}
pub fn action(pressed: &str) -> Option<&'static str> {
ALL.iter().map(|(k, _)| *k).find(|k| key(k) == pressed)
}
pub fn assign(moved: &mut BTreeMap<String, String>, usual: &str, to: &str) {
set(moved);
let old = key(usual);
if let Some(other) = action(to).filter(|o| *o != usual) {
put(moved, other, &old);
}
put(moved, usual, to);
set(moved);
}
fn put(moved: &mut BTreeMap<String, String>, usual: &str, to: &str) {
if usual == to {
moved.remove(usual);
} else {
moved.insert(usual.to_string(), to.to_string());
}
}
pub fn name(code: KeyCode) -> Option<&'static str> {
Some(match code {
KeyCode::Enter | KeyCode::NumpadEnter => "Enter",
KeyCode::KeyA => "A",
KeyCode::KeyB => "B",
KeyCode::KeyC => "C",
KeyCode::KeyD => "D",
KeyCode::KeyE => "E",
KeyCode::KeyF => "F",
KeyCode::KeyG => "G",
KeyCode::KeyH => "H",
KeyCode::KeyI => "I",
KeyCode::KeyJ => "J",
KeyCode::KeyK => "K",
KeyCode::KeyL => "L",
KeyCode::KeyM => "M",
KeyCode::KeyN => "N",
KeyCode::KeyO => "O",
KeyCode::KeyP => "P",
KeyCode::KeyQ => "Q",
KeyCode::KeyR => "R",
KeyCode::KeyS => "S",
KeyCode::KeyT => "T",
KeyCode::KeyU => "U",
KeyCode::KeyV => "V",
KeyCode::KeyW => "W",
KeyCode::KeyX => "X",
KeyCode::KeyY => "Y",
KeyCode::KeyZ => "Z",
_ => return None,
})
}
pub fn code(name: &str) -> Option<KeyCode> {
Some(match name {
"Enter" => KeyCode::Enter,
"A" => KeyCode::KeyA,
"B" => KeyCode::KeyB,
"C" => KeyCode::KeyC,
"D" => KeyCode::KeyD,
"E" => KeyCode::KeyE,
"F" => KeyCode::KeyF,
"G" => KeyCode::KeyG,
"H" => KeyCode::KeyH,
"I" => KeyCode::KeyI,
"J" => KeyCode::KeyJ,
"K" => KeyCode::KeyK,
"L" => KeyCode::KeyL,
"M" => KeyCode::KeyM,
"N" => KeyCode::KeyN,
"O" => KeyCode::KeyO,
"P" => KeyCode::KeyP,
"Q" => KeyCode::KeyQ,
"R" => KeyCode::KeyR,
"S" => KeyCode::KeyS,
"T" => KeyCode::KeyT,
"U" => KeyCode::KeyU,
"V" => KeyCode::KeyV,
"W" => KeyCode::KeyW,
"X" => KeyCode::KeyX,
"Y" => KeyCode::KeyY,
"Z" => KeyCode::KeyZ,
_ => return None,
})
}
pub fn translate(pressed: KeyCode) -> Option<KeyCode> {
match name(pressed) {
Some(n) => action(n).and_then(code),
None => Some(pressed),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn descriptions_fit_the_editor() {
let half = ALL.len().div_ceil(2);
for (i, (_, what)) in ALL.iter().enumerate() {
assert!(what.len() <= if i < half { 25 } else { 23 }, "{what}");
}
}
#[test]
fn moving_keys_swaps_and_translates() {
let mut moved = BTreeMap::new();
assign(&mut moved, "D", "Y");
assert_eq!(key("D"), "Y");
assert_eq!(translate(KeyCode::KeyY), Some(KeyCode::KeyD));
assert_eq!(translate(KeyCode::KeyD), None);
assert_eq!(translate(KeyCode::Digit1), Some(KeyCode::Digit1));
assign(&mut moved, "Z", "Y");
assert_eq!((key("Z"), key("D")), ("Y".to_string(), "Z".to_string()));
assign(&mut moved, "Z", "Z");
assign(&mut moved, "D", "D");
assert!(moved.is_empty(), "{moved:?}");
assert_eq!(translate(KeyCode::KeyD), Some(KeyCode::KeyD));
set(&BTreeMap::new());
}
}