Skip to content

Commit ae997e4

Browse files
committed
ignore modifier-only key presses
1 parent 155e032 commit ae997e4

File tree

4 files changed

+57
-35
lines changed

4 files changed

+57
-35
lines changed

examples/winit.rs

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ use keybinds::{KeyBind, KeyBindMatcher, KeyBinds};
33
use winit::application::ApplicationHandler;
44
use winit::event::WindowEvent;
55
use winit::event_loop::{ActiveEventLoop, EventLoop};
6-
use winit::window::{Window, WindowId};
6+
use winit::window::{Theme, Window, WindowId};
77

88
// Actions triggered by key bindings
99
#[derive(Debug)]
1010
enum Action {
1111
SayHi,
1212
ToggleMaximized,
13-
ToggleVisible,
14-
ExitApp,
13+
ToggleTheme,
14+
Exit,
1515
}
1616

1717
struct App {
@@ -26,8 +26,8 @@ impl Default for App {
2626
let keybinds = KeyBinds::new(vec![
2727
KeyBind::multiple("h i".parse().unwrap(), Action::SayHi),
2828
KeyBind::single("Mod+m".parse().unwrap(), Action::ToggleMaximized),
29-
KeyBind::single("Mod+Shift+v".parse().unwrap(), Action::ToggleVisible),
30-
KeyBind::multiple("Mod+x Mod+c".parse().unwrap(), Action::ExitApp),
29+
KeyBind::single("Mod+Shift+t".parse().unwrap(), Action::ToggleTheme),
30+
KeyBind::multiple("Mod+x Mod+c".parse().unwrap(), Action::Exit),
3131
]);
3232
Self {
3333
window: None,
@@ -39,31 +39,32 @@ impl Default for App {
3939

4040
impl ApplicationHandler for App {
4141
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
42-
let window = event_loop
43-
.create_window(Window::default_attributes())
44-
.unwrap();
42+
let attrs = Window::default_attributes().with_theme(Some(Theme::Dark));
43+
let window = event_loop.create_window(attrs).unwrap();
4544
self.window = Some(window);
4645
}
4746

4847
fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
49-
if let Some(input) = self.converter.convert(&event) {
50-
println!("Key input: {input:?}");
48+
let input = self.converter.convert(&event);
5149

52-
if let Some(action) = self.matcher.trigger(input) {
53-
println!("Action: {action:?}");
50+
if let Some(action) = self.matcher.trigger(input) {
51+
println!("Action: {action:?}");
5452

55-
match action {
56-
Action::SayHi => println!("Hi!"),
57-
Action::ToggleMaximized => {
58-
let window = self.window.as_ref().unwrap();
59-
window.set_maximized(!window.is_maximized());
60-
}
61-
Action::ToggleVisible => {
62-
let window = self.window.as_ref().unwrap();
63-
window.set_visible(!window.is_visible().unwrap_or(false));
64-
}
65-
Action::ExitApp => event_loop.exit(),
53+
match action {
54+
Action::SayHi => println!("Hi!"),
55+
Action::ToggleMaximized => {
56+
let window = self.window.as_ref().unwrap();
57+
window.set_maximized(!window.is_maximized());
6658
}
59+
Action::ToggleTheme => {
60+
let window = self.window.as_ref().unwrap();
61+
let theme = match window.theme() {
62+
Some(Theme::Dark) => Theme::Light,
63+
_ => Theme::Dark,
64+
};
65+
window.set_theme(Some(theme));
66+
}
67+
Action::Exit => event_loop.exit(),
6768
}
6869
}
6970

src/crossterm.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ impl From<KeyCode> for Key {
3535
KeyCode::Media(MediaKeyCode::LowerVolume) => Self::VolumeDown,
3636
KeyCode::Media(MediaKeyCode::RaiseVolume) => Self::VolumeUp,
3737
KeyCode::Media(MediaKeyCode::MuteVolume) => Self::Mute,
38+
KeyCode::Modifier(_) | KeyCode::Null => Self::Ignored,
3839
_ => Self::Unidentified,
3940
}
4041
}
@@ -94,15 +95,19 @@ impl From<Event> for KeyInput {
9495
#[cfg(test)]
9596
mod tests {
9697
use super::*;
97-
use crossterm::event::{KeyEventKind, KeyEventState};
98+
use crossterm::event::{KeyEventKind, KeyEventState, ModifierKeyCode};
9899

99100
#[test]
100101
fn convert_key_code() {
101102
assert_eq!(Key::from(KeyCode::Backspace), Key::Backspace);
102103
assert_eq!(Key::from(KeyCode::Char('a')), Key::Char('a'));
103104
assert_eq!(Key::from(KeyCode::Char('A')), Key::Char('a'));
104105
assert_eq!(Key::from(KeyCode::Char('A')), Key::Char('a'));
105-
assert_eq!(Key::from(KeyCode::Null), Key::Unidentified);
106+
assert_eq!(Key::from(KeyCode::Null), Key::Ignored);
107+
assert_eq!(
108+
Key::from(KeyCode::Modifier(ModifierKeyCode::LeftControl)),
109+
Key::Ignored,
110+
);
106111
assert_eq!(Key::from(KeyCode::Media(MediaKeyCode::Play)), Key::Play);
107112
assert_eq!(Key::from(KeyCode::F(12)), Key::F(12));
108113
}

src/lib.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ pub enum Key {
5959
Mute,
6060
F(u8),
6161
Unidentified,
62+
Ignored,
6263
}
6364

6465
impl From<char> for Key {
@@ -182,6 +183,10 @@ impl KeyInput {
182183
mods,
183184
}
184185
}
186+
187+
fn is_ignored(&self) -> bool {
188+
self.key == Key::Ignored
189+
}
185190
}
186191

187192
impl FromStr for KeyInput {
@@ -314,8 +319,12 @@ impl<A> KeyBindMatcher<A> {
314319
}
315320

316321
pub fn trigger<I: Into<KeyInput>>(&mut self, input: I) -> Option<&A> {
322+
let input = input.into();
323+
if input.is_ignored() {
324+
return None;
325+
}
317326
self.handle_timeout();
318-
self.ongoing.push(input.into());
327+
self.ongoing.push(input);
319328

320329
// TODO: When no keybind is prefix-matched, call `self.reset()`
321330
let action = self.binds.find(&self.ongoing).map(|b| &b.action)?;

src/winit.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ impl From<&WinitKey> for Key {
6161
NamedKey::F18 => Self::F(18),
6262
NamedKey::F19 => Self::F(19),
6363
NamedKey::F20 => Self::F(20),
64+
NamedKey::Alt
65+
| NamedKey::Control
66+
| NamedKey::Shift
67+
| NamedKey::Super
68+
| NamedKey::Hyper
69+
| NamedKey::Meta
70+
| NamedKey::Symbol => Self::Ignored,
6471
_ => Self::Unidentified,
6572
},
6673
WinitKey::Character(s) => {
@@ -108,33 +115,33 @@ impl From<ModifiersState> for Mods {
108115
}
109116

110117
pub trait KeyInputConvertible {
111-
fn convert_key_input(&self, conv: &mut KeyEventConverter) -> Option<KeyInput>;
118+
fn convert_key_input(&self, conv: &mut KeyEventConverter) -> KeyInput;
112119
}
113120

114121
impl KeyInputConvertible for WindowEvent {
115-
fn convert_key_input(&self, conv: &mut KeyEventConverter) -> Option<KeyInput> {
122+
fn convert_key_input(&self, conv: &mut KeyEventConverter) -> KeyInput {
116123
match self {
117124
WindowEvent::ModifiersChanged(mods) => {
118125
conv.on_modifiers_changed(mods);
119-
None
126+
Key::Ignored.into()
120127
}
121128
WindowEvent::KeyboardInput { event, .. } if event.state == ElementState::Pressed => {
122-
Some(KeyInput {
129+
KeyInput {
123130
key: Key::from(&event.logical_key),
124131
mods: conv.mods,
125-
})
132+
}
126133
}
127-
_ => None,
134+
_ => Key::Ignored.into(),
128135
}
129136
}
130137
}
131138

132139
impl<T> KeyInputConvertible for Event<T> {
133-
fn convert_key_input(&self, conv: &mut KeyEventConverter) -> Option<KeyInput> {
140+
fn convert_key_input(&self, conv: &mut KeyEventConverter) -> KeyInput {
134141
if let Event::WindowEvent { event, .. } = self {
135142
event.convert_key_input(conv)
136143
} else {
137-
None
144+
Key::Ignored.into()
138145
}
139146
}
140147
}
@@ -153,7 +160,7 @@ impl KeyEventConverter {
153160
self.mods = mods.state().into();
154161
}
155162

156-
pub fn convert<C: KeyInputConvertible>(&mut self, event: &C) -> Option<KeyInput> {
163+
pub fn convert<C: KeyInputConvertible>(&mut self, event: &C) -> KeyInput {
157164
event.convert_key_input(self)
158165
}
159166
}

0 commit comments

Comments
 (0)