| 1 | //! Keyboard and mouse input: vim key notation, per-mode keymaps, and the bridge from winit. |
| 2 | |
| 3 | pub mod keymap; |
| 4 | pub mod keys; |
| 5 | |
| 6 | use winit::event::MouseButton; |
| 7 | use winit::keyboard::{Key as WinitKey, ModifiersState, NamedKey}; |
| 8 | |
| 9 | use keys::{Button, Code, Key, Mods, Named, Scroll}; |
| 10 | |
| 11 | pub fn mods(m: ModifiersState) -> Mods { |
| 12 | Mods { |
| 13 | ctrl: m.control_key(), |
| 14 | alt: m.alt_key(), |
| 15 | shift: m.shift_key(), |
| 16 | sup: m.super_key(), |
| 17 | } |
| 18 | } |
| 19 | |
| 20 | /// A key press in vim terms. Modifier keys on their own, dead keys and unnamed keys give `None`. |
| 21 | pub fn key(key: &WinitKey, m: ModifiersState) -> Option<Key> { |
| 22 | let code = match key { |
| 23 | WinitKey::Character(s) => { |
| 24 | let mut chars = s.chars(); |
| 25 | match (chars.next(), chars.next()) { |
| 26 | (Some(c), None) => Code::Char(c), |
| 27 | _ => return None, |
| 28 | } |
| 29 | } |
| 30 | WinitKey::Named(n) => Code::Named(match n { |
| 31 | NamedKey::Space => Named::Space, |
| 32 | NamedKey::Enter => Named::Enter, |
| 33 | NamedKey::Escape => Named::Esc, |
| 34 | NamedKey::Tab => Named::Tab, |
| 35 | NamedKey::Backspace => Named::Backspace, |
| 36 | NamedKey::Delete => Named::Delete, |
| 37 | NamedKey::Insert => Named::Insert, |
| 38 | NamedKey::Home => Named::Home, |
| 39 | NamedKey::End => Named::End, |
| 40 | NamedKey::PageUp => Named::PageUp, |
| 41 | NamedKey::PageDown => Named::PageDown, |
| 42 | NamedKey::ArrowUp => Named::Up, |
| 43 | NamedKey::ArrowDown => Named::Down, |
| 44 | NamedKey::ArrowLeft => Named::Left, |
| 45 | NamedKey::ArrowRight => Named::Right, |
| 46 | other => { |
| 47 | return function_key(*other) |
| 48 | .map(Named::F) |
| 49 | .map(|f| Key::new(mods(m), Code::Named(f))); |
| 50 | } |
| 51 | }), |
| 52 | _ => return None, |
| 53 | }; |
| 54 | Some(Key::new(mods(m), code)) |
| 55 | } |
| 56 | |
| 57 | fn function_key(n: NamedKey) -> Option<u8> { |
| 58 | use NamedKey::*; |
| 59 | const F: [NamedKey; 35] = [ |
| 60 | F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15, F16, F17, F18, F19, F20, |
| 61 | F21, F22, F23, F24, F25, F26, F27, F28, F29, F30, F31, F32, F33, F34, F35, |
| 62 | ]; |
| 63 | F.iter().position(|f| *f == n).map(|i| i as u8 + 1) |
| 64 | } |
| 65 | |
| 66 | pub fn button(b: MouseButton, m: ModifiersState) -> Option<Key> { |
| 67 | let b = match b { |
| 68 | MouseButton::Left => Button::Left, |
| 69 | MouseButton::Right => Button::Right, |
| 70 | MouseButton::Middle => Button::Middle, |
| 71 | MouseButton::Back => Button::Back, |
| 72 | MouseButton::Forward => Button::Forward, |
| 73 | MouseButton::Other(_) => return None, |
| 74 | }; |
| 75 | Some(Key::new(mods(m), Code::Mouse(b))) |
| 76 | } |
| 77 | |
| 78 | pub fn scroll(dir: Scroll, m: ModifiersState) -> Key { |
| 79 | Key::new(mods(m), Code::Scroll(dir)) |
| 80 | } |
| 81 | |
| 82 | #[cfg(test)] |
| 83 | mod tests { |
| 84 | use super::*; |
| 85 | use keys::KeySeq; |
| 86 | |
| 87 | fn k(s: &str) -> Key { |
| 88 | s.parse::<KeySeq>().unwrap().0[0] |
| 89 | } |
| 90 | |
| 91 | #[test] |
| 92 | fn winit_keys_match_notation() { |
| 93 | let none = ModifiersState::empty(); |
| 94 | let ch = |s: &str| WinitKey::Character(s.into()); |
| 95 | assert_eq!(key(&ch("n"), none), Some(k("n"))); |
| 96 | // Shift is already in the character. |
| 97 | assert_eq!(key(&ch("G"), ModifiersState::SHIFT), Some(k("G"))); |
| 98 | assert_eq!(key(&ch("?"), ModifiersState::SHIFT), Some(k("?"))); |
| 99 | assert_eq!(key(&ch("x"), ModifiersState::CONTROL), Some(k("<C-x>"))); |
| 100 | assert_eq!(key(&ch(" "), none), Some(k("<Space>"))); |
| 101 | assert_eq!( |
| 102 | key(&WinitKey::Named(NamedKey::Space), ModifiersState::CONTROL), |
| 103 | Some(k("<C-Space>")) |
| 104 | ); |
| 105 | assert_eq!( |
| 106 | key(&WinitKey::Named(NamedKey::Tab), ModifiersState::SHIFT), |
| 107 | Some(k("<S-Tab>")) |
| 108 | ); |
| 109 | assert_eq!(key(&WinitKey::Named(NamedKey::F5), none), Some(k("<F5>"))); |
| 110 | assert_eq!( |
| 111 | key(&WinitKey::Named(NamedKey::Shift), ModifiersState::SHIFT), |
| 112 | None |
| 113 | ); |
| 114 | assert_eq!(button(MouseButton::Right, none), Some(k("<RightMouse>"))); |
| 115 | assert_eq!( |
| 116 | scroll(Scroll::Up, ModifiersState::CONTROL), |
| 117 | k("<C-ScrollUp>") |
| 118 | ); |
| 119 | } |
| 120 | } |