1use escriba_keymap::Key;
12use madori::AppEvent;
13use madori::event::{KeyCode, KeyEvent};
14
15#[must_use]
20pub fn translate_key(event: &KeyEvent) -> Option<Key> {
21 if !event.pressed {
22 return None;
23 }
24 let mods = event.modifiers;
25 let key = match event.key {
26 KeyCode::Enter => Key::Enter,
27 KeyCode::Escape => Key::Esc,
28 KeyCode::Backspace => Key::Backspace,
29 KeyCode::Tab => Key::Tab,
30 KeyCode::Up => Key::Up,
31 KeyCode::Down => Key::Down,
32 KeyCode::Left => Key::Left,
33 KeyCode::Right => Key::Right,
34 KeyCode::Home => Key::Home,
35 KeyCode::End => Key::End,
36 KeyCode::PageUp => Key::PageUp,
37 KeyCode::PageDown => Key::PageDown,
38 KeyCode::Space => Key::Char(' '),
39 KeyCode::Char(c) => {
40 let extra = mods.meta || (mods.ctrl && mods.alt);
46 if extra {
47 return chord_of(c, mods).map(Key::Chord);
48 }
49 if mods.ctrl {
50 return Some(Key::Ctrl(c.to_ascii_lowercase()));
51 }
52 if mods.alt {
53 return Some(Key::Alt(c.to_ascii_lowercase()));
54 }
55 Key::Char(c)
56 }
57 KeyCode::Delete => Key::Delete,
60 KeyCode::F(n) => Key::F(n),
62 KeyCode::Unknown => return None,
63 };
64 Some(key)
65}
66
67fn chord_of(c: char, mods: madori::event::Modifiers) -> Option<awase::Hotkey> {
72 use awase::Modifiers as M;
73 let mut m = M::NONE;
74 if mods.ctrl {
75 m = m | M::CTRL;
76 }
77 if mods.alt {
78 m = m | M::ALT;
79 }
80 if mods.shift {
81 m = m | M::SHIFT;
82 }
83 if mods.meta {
84 m = m | M::CMD;
85 }
86 let key = awase::Key::from_name(&c.to_ascii_lowercase().to_string())?;
87 Some(awase::Hotkey::new(m, key))
88}
89
90#[derive(Debug, Clone, PartialEq, Eq)]
92pub enum InputOutcome {
93 Key(Key),
95 Resized { width: u32, height: u32 },
97 Focus(bool),
99 Quit,
101 None,
103}
104
105#[must_use]
106pub fn translate_app_event(event: &AppEvent) -> InputOutcome {
107 match event {
108 AppEvent::Key(ke) => translate_key(ke).map_or(InputOutcome::None, InputOutcome::Key),
109 AppEvent::Resized { width, height } => InputOutcome::Resized {
110 width: *width,
111 height: *height,
112 },
113 AppEvent::Focused(f) => InputOutcome::Focus(*f),
114 AppEvent::CloseRequested => InputOutcome::Quit,
115 _ => InputOutcome::None,
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123 use madori::event::Modifiers;
124
125 fn key(kc: KeyCode, mods: Modifiers) -> KeyEvent {
126 KeyEvent {
127 key: kc,
128 pressed: true,
129 modifiers: mods,
130 text: None,
131 }
132 }
133
134 #[test]
135 fn char_without_modifiers() {
136 let k = translate_key(&key(KeyCode::Char('h'), Modifiers::default())).unwrap();
137 assert_eq!(k, Key::Char('h'));
138 }
139
140 #[test]
141 fn ctrl_char_becomes_ctrl_key() {
142 let mods = Modifiers {
143 ctrl: true,
144 ..Modifiers::default()
145 };
146 let k = translate_key(&key(KeyCode::Char('R'), mods)).unwrap();
147 assert_eq!(k, Key::Ctrl('r'));
148 }
149
150 #[test]
151 fn alt_char_becomes_alt_key() {
152 let mods = Modifiers {
153 alt: true,
154 ..Modifiers::default()
155 };
156 let k = translate_key(&key(KeyCode::Char('f'), mods)).unwrap();
157 assert_eq!(k, Key::Alt('f'));
158 }
159
160 #[test]
161 fn named_keys_map() {
162 assert_eq!(
163 translate_key(&key(KeyCode::Escape, Modifiers::default())).unwrap(),
164 Key::Esc
165 );
166 assert_eq!(
167 translate_key(&key(KeyCode::Enter, Modifiers::default())).unwrap(),
168 Key::Enter
169 );
170 assert_eq!(
171 translate_key(&key(KeyCode::Space, Modifiers::default())).unwrap(),
172 Key::Char(' ')
173 );
174 }
175
176 #[test]
177 fn release_returns_none() {
178 let mut e = key(KeyCode::Char('a'), Modifiers::default());
179 e.pressed = false;
180 assert!(translate_key(&e).is_none());
181 }
182
183 #[test]
184 fn close_event_quits() {
185 assert_eq!(
186 translate_app_event(&AppEvent::CloseRequested),
187 InputOutcome::Quit
188 );
189 }
190}