Skip to content

Commit 06743d5

Browse files
committed
add tests for winit support
1 parent ae997e4 commit 06743d5

File tree

6 files changed

+103
-29
lines changed

6 files changed

+103
-29
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
/target
2+
/env.sh
3+
/.git-hooks/pre-push

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "keybinds"
3-
version = "0.0.1"
3+
version = "0.0.2"
44
edition = "2021"
55
authors = ["rhysd <[email protected]>"]
66
description = "Platform&Framework-agnostic key bindings parser and matcher"

examples/winit.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use keybinds::winit::KeyEventConverter;
1+
use keybinds::winit::WinitEventConverter;
22
use keybinds::{KeyBind, KeyBindMatcher, KeyBinds};
33
use winit::application::ApplicationHandler;
44
use winit::event::WindowEvent;
@@ -17,7 +17,7 @@ enum Action {
1717
struct App {
1818
window: Option<Window>,
1919
matcher: KeyBindMatcher<Action>,
20-
converter: KeyEventConverter,
20+
converter: WinitEventConverter,
2121
}
2222

2323
impl Default for App {
@@ -32,7 +32,7 @@ impl Default for App {
3232
Self {
3333
window: None,
3434
matcher: KeyBindMatcher::new(keybinds),
35-
converter: KeyEventConverter::default(),
35+
converter: WinitEventConverter::default(),
3636
}
3737
}
3838
}
@@ -45,8 +45,10 @@ impl ApplicationHandler for App {
4545
}
4646

4747
fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
48+
// Convert the window event into key input
4849
let input = self.converter.convert(&event);
4950

51+
// Check if the converted key input triggers some action
5052
if let Some(action) = self.matcher.trigger(input) {
5153
println!("Action: {action:?}");
5254

src/crossterm.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ mod tests {
103103
assert_eq!(Key::from(KeyCode::Char('a')), Key::Char('a'));
104104
assert_eq!(Key::from(KeyCode::Char('A')), Key::Char('a'));
105105
assert_eq!(Key::from(KeyCode::Char('A')), Key::Char('a'));
106+
assert_eq!(Key::from(KeyCode::KeypadBegin), Key::Unidentified);
106107
assert_eq!(Key::from(KeyCode::Null), Key::Ignored);
107108
assert_eq!(
108109
Key::from(KeyCode::Modifier(ModifierKeyCode::LeftControl)),
@@ -124,14 +125,7 @@ mod tests {
124125
),
125126
Mods::CTRL | Mods::SHIFT | Mods::ALT | Mods::CMD,
126127
);
127-
#[cfg(not(target_os = "macos"))]
128-
{
129-
assert_eq!(Mods::from(KeyModifiers::SUPER), Mods::WIN);
130-
}
131-
#[cfg(target_os = "macos")]
132-
{
133-
assert_eq!(Mods::from(KeyModifiers::SUPER), Mods::CMD);
134-
}
128+
assert_eq!(Mods::from(KeyModifiers::SUPER), Mods::SUPER);
135129
}
136130

137131
#[test]

src/winit.rs

Lines changed: 92 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{Key, KeyInput, Mods};
2-
use winit::event::{ElementState, Event, Modifiers, WindowEvent};
2+
use winit::event::{ElementState, Event, KeyEvent, Modifiers, WindowEvent};
33
use winit::keyboard::{Key as WinitKey, ModifiersState, NamedKey};
44

55
impl From<&WinitKey> for Key {
@@ -114,44 +114,50 @@ impl From<ModifiersState> for Mods {
114114
}
115115
}
116116

117-
pub trait KeyInputConvertible {
118-
fn convert_key_input(&self, conv: &mut KeyEventConverter) -> KeyInput;
117+
pub trait WinitEvent {
118+
fn to_key_input(&self, conv: &mut WinitEventConverter) -> KeyInput;
119119
}
120120

121-
impl KeyInputConvertible for WindowEvent {
122-
fn convert_key_input(&self, conv: &mut KeyEventConverter) -> KeyInput {
121+
impl WinitEvent for KeyEvent {
122+
fn to_key_input(&self, conv: &mut WinitEventConverter) -> KeyInput {
123+
KeyInput {
124+
key: Key::from(&self.logical_key),
125+
mods: conv.mods,
126+
}
127+
}
128+
}
129+
130+
impl WinitEvent for WindowEvent {
131+
fn to_key_input(&self, conv: &mut WinitEventConverter) -> KeyInput {
123132
match self {
124133
WindowEvent::ModifiersChanged(mods) => {
125134
conv.on_modifiers_changed(mods);
126135
Key::Ignored.into()
127136
}
128137
WindowEvent::KeyboardInput { event, .. } if event.state == ElementState::Pressed => {
129-
KeyInput {
130-
key: Key::from(&event.logical_key),
131-
mods: conv.mods,
132-
}
138+
event.to_key_input(conv)
133139
}
134140
_ => Key::Ignored.into(),
135141
}
136142
}
137143
}
138144

139-
impl<T> KeyInputConvertible for Event<T> {
140-
fn convert_key_input(&self, conv: &mut KeyEventConverter) -> KeyInput {
145+
impl<T> WinitEvent for Event<T> {
146+
fn to_key_input(&self, conv: &mut WinitEventConverter) -> KeyInput {
141147
if let Event::WindowEvent { event, .. } = self {
142-
event.convert_key_input(conv)
148+
event.to_key_input(conv)
143149
} else {
144150
Key::Ignored.into()
145151
}
146152
}
147153
}
148154

149155
#[derive(Default)]
150-
pub struct KeyEventConverter {
156+
pub struct WinitEventConverter {
151157
mods: Mods,
152158
}
153159

154-
impl KeyEventConverter {
160+
impl WinitEventConverter {
155161
pub fn mods(&self) -> Mods {
156162
self.mods
157163
}
@@ -160,7 +166,77 @@ impl KeyEventConverter {
160166
self.mods = mods.state().into();
161167
}
162168

163-
pub fn convert<C: KeyInputConvertible>(&mut self, event: &C) -> KeyInput {
164-
event.convert_key_input(self)
169+
pub fn convert<E: WinitEvent>(&mut self, event: &E) -> KeyInput {
170+
event.to_key_input(self)
171+
}
172+
}
173+
174+
#[cfg(test)]
175+
mod tests {
176+
use super::*;
177+
use winit::keyboard::{NativeKey, SmolStr};
178+
use NamedKey::*;
179+
use WinitKey::*;
180+
181+
#[test]
182+
fn convert_key() {
183+
assert_eq!(Key::from(Named(Space)), Key::Char(' '));
184+
assert_eq!(Key::from(Named(ArrowUp)), Key::Up);
185+
assert_eq!(Key::from(Named(F1)), Key::F(1));
186+
assert_eq!(Key::from(Named(Control)), Key::Ignored);
187+
assert_eq!(Key::from(Named(TVInput)), Key::Unidentified);
188+
assert_eq!(Key::from(Character(SmolStr::new("a"))), Key::Char('a'));
189+
assert_eq!(Key::from(Character(SmolStr::new("A"))), Key::Char('a'));
190+
assert_eq!(Key::from(Character(SmolStr::new("foo"))), Key::Unidentified);
191+
assert_eq!(
192+
Key::from(Unidentified(NativeKey::Unidentified)),
193+
Key::Unidentified,
194+
);
195+
assert_eq!(Key::from(Dead(None)), Key::Unidentified);
196+
}
197+
198+
#[test]
199+
fn convert_modifiers_state() {
200+
assert_eq!(Mods::from(ModifiersState::CONTROL), Mods::CTRL);
201+
assert_eq!(
202+
Mods::from(ModifiersState::CONTROL | ModifiersState::SHIFT | ModifiersState::ALT),
203+
Mods::CTRL | Mods::SHIFT | Mods::ALT,
204+
);
205+
assert_eq!(Mods::from(ModifiersState::SUPER), Mods::SUPER);
206+
}
207+
208+
// Unformatunately `WinitEventConverter::convert` is not testable because winit does not provide
209+
// to create an instance of `KeyEvent`. It provides no constructor and contains some pvivate fields.
210+
// Instead, we use `winit::keyboard::Key` directly.
211+
impl WinitEvent for WinitKey {
212+
fn to_key_input(&self, conv: &mut WinitEventConverter) -> KeyInput {
213+
KeyInput {
214+
key: self.into(),
215+
mods: conv.mods,
216+
}
217+
}
218+
}
219+
220+
#[test]
221+
fn converter_mods_state() {
222+
let mut conv = WinitEventConverter::default();
223+
224+
// We cannot test `on_modifiers_changed` because `winit::event::Modifiers` does not provide
225+
// a constructor.
226+
assert_eq!(conv.mods(), Mods::NONE);
227+
228+
assert_eq!(conv.convert(&Named(Space)), KeyInput::new(' ', Mods::NONE));
229+
assert_eq!(
230+
conv.convert(&Character(SmolStr::new("A"))),
231+
KeyInput::new('a', Mods::NONE),
232+
);
233+
assert_eq!(
234+
conv.convert(&Named(TVInputHDMI1)),
235+
KeyInput::new(Key::Unidentified, Mods::NONE),
236+
);
237+
assert_eq!(
238+
conv.convert(&Named(Control)),
239+
KeyInput::new(Key::Ignored, Mods::NONE),
240+
);
165241
}
166242
}

0 commit comments

Comments
 (0)