10 unstable releases (4 breaking)
new 0.5.0 | Dec 9, 2024 |
---|---|
0.4.1 | Dec 8, 2024 |
0.4.0 | Nov 28, 2024 |
0.3.0 | Jul 5, 2024 |
0.1.1 | Feb 20, 2024 |
#123 in GUI
453 downloads per month
Used in 2 crates
48KB
243 lines
keyseq
Specify key chords using Ctrl-A
short-hand, supports bevy and
winit.
Objective
-
Specify key chords in code the same way as they are specified in documentation.
-
For the sake of finding key chords in code, prefer one way of describing the keys, e.g., accept "Ctrl-A"; do not accept "control-A" or "C-A". ("Ctrl+A" can be accepted by using the "permit-plus" feature flag.)
Install
cargo add keyseq --features bevy; # OR --features winit
Principal Macros
- The
pkey!
macro specifies a physical key chord, e.g.,pkey! { Ctrl-A }
. - The
pkeyseq!
macro specifies a physical key chord sequence, e.g.,pkeyseq! { Ctrl-A Alt-B C }
. - The
lkey!
macro specifies a logical key chord, .e.g,lkey! { Ctrl-a }
. - The
lkeyseq!
macro specifies a logical key chord sequence, e.g.lkeyseq! { Ctrl-a Alt-b c }
.
Concepts
- A physical key denotes a particular key on the keyboard. It emits a key code that does not change no matter what modifiers are held down. For instance there is a physical 'Q' key, often to the right of the tab key. There is no physical lower-case 'q' key.
- A logical key is specified by what the key produces. If pressing the key produces a 'q' character, then it is logically a 'q' key.
Usage
Winit
With the "winit" feature the keyseq::winit::pkey!
macro returns a
(Modifiers, KeyCode)
tuple.
Physical Keys
use keyseq::{Modifiers, winit::pkey};
use winit::keyboard::KeyCode;
assert_eq!(pkey! { A }, (Modifiers::NONE, KeyCode::KeyA));
assert_eq!(pkey! { Ctrl-A }, (Modifiers::CONTROL, KeyCode::KeyA));
assert_eq!(pkey! { Alt-A }, (Modifiers::ALT, KeyCode::KeyA));
assert_eq!(pkey! { Shift-A }, (Modifiers::SHIFT, KeyCode::KeyA));
assert_eq!(pkey! { Super-A }, (Modifiers::SUPER, KeyCode::KeyA));
assert_eq!(pkey! { Ctrl-Alt-; }, (Modifiers::ALT |
Modifiers::CONTROL, KeyCode::Semicolon));
Physical Key Sequences
# use keyseq::Modifiers;
# use winit::keyboard::KeyCode;
use keyseq::winit::pkeyseq;
assert_eq!(pkeyseq! { A Ctrl-B }, [(Modifiers::NONE, KeyCode::KeyA),
(Modifiers::CONTROL, KeyCode::KeyB)]);
Logical Keys
With the "winit" feature the keyseq::winit::lkey!
macro returns a
(Modifiers, Key)
tuple.
use keyseq::{Modifiers, winit::lkey};
use winit::keyboard::Key;
assert_eq!(lkey! { a }, (Modifiers::NONE, Key::Character('a')));
assert_eq!(lkey! { Ctrl-a }, (Modifiers::CONTROL, Key::Character('a')));
assert_eq!(lkey! { Alt-a }, (Modifiers::ALT, Key::Character('a')));
assert_eq!(lkey! { Shift-a }, (Modifiers::SHIFT, Key::Character('a')));
assert_eq!(lkey! { Super-a }, (Modifiers::SUPER, Key::Character('a')));
assert_eq!(lkey! { Ctrl-Alt-; }, (Modifiers::ALT |
Modifiers::CONTROL, Key::Character(';')));
Logical Key Sequences
# use keyseq::Modifiers;
# use winit::keyboard::Key;
use keyseq::winit::lkeyseq;
assert_eq!(lkeyseq! { a Ctrl-b }, [(Modifiers::NONE, Key::Character('a')),
(Modifiers::CONTROL, Key::Character('b'))]);
No lower case physical keys
The following code will fail to compile. It insists on a capital 'A' for specifying the A key.
# use keyseq::winit::pkey;
let (mods, key) = pkey! { a }; // error: Use uppercase key names for physical keys
Strict modifier order
With the "strict-order" feature enabled by default, modifiers out of order will produce compiler errors. Without the feature, it will emit warnings.
# use keyseq::winit::pkey;
let _ = pkey! { Alt-Ctrl-A }; // error: Modifiers must occur in this order: control, Alt, Shift, Super.
Why not use winit::keyboard::ModifiersState
?
Why return keyseq::Modifiers
and not winit
's own ModifiersState
? Both
keyseq::Modifiers
and winit::keyboard::ModifiersState
are generated using
the bitflags crate. Originally this
crate did return winit
's native modifiers struct because it desugared to nearly
the same thing:
// keyseq::winit::pkey! { Ctrl-Alt-A } desugared to
( ModifiersState::CONTROL
| ModifiersState::ALT
| ModifiersState::empty(), winit::keyboard::KeyCode::KeyA)
// keyseq::bevy::pkey! { Ctrl-Alt-A } desugars to
( Modifiers::CONTROL
| Modifiers::ALT
| Modifiers::empty(), bevy::prelude::KeyCode::KeyA)
However, this these bitflags put together with bit-or pipes had a problem with match expressions.
fn f(modifiers: ModifiersState) {
match (modifiers.into(), key_code) {
// pkey! { Ctrl-Alt-A } => println!("Just pressed Ctrl-Alt-A!"),
// desugared to
(ModifiersState::CONTROL |
ModifiersState::ALT |
ModifiersState::empty(),
KeyCode::KeyA) => println!("Just pressed Ctrl-Alt-A!"),
}
}
When desugared the bit-or |
is now interpretered as a match-or |
, which does
not match Ctrl-Alt
; it only matches Ctrl
or Alt
or no modifiers. (This
actually seems like a pretty big expressive deficiency for bitflags
generated
structs.)
To avoid this problem keyseq::Modifiers
is defined as Modifiers(pub u8)
and
the bitflags are computed in the macro. That allows the following match
expressions to work as expected.
match (modifiers.into(), key_code) {
// pkey! { Ctrl-Alt-A } => println!("Just pressed Ctrl-Alt-A!"),
// now desugars to
(Modifiers(3), KeyCode::KeyA) => println!("Just pressed Ctrl-Alt-A!"),
// And we can use the match-or to match multiple keychords.
pkey! { Ctrl-A } | pkey! { Super-A } => println!("Just pressed Ctrl-A or Super-A!"),
}
In addition keyseq::Modifiers
implements From<ModifiersState>
and vice
versa.
Bevy
Physical Keys
With the "bevy" feature the keyseq::bevy::pkey!
macro returns a
(keyseq::Modifiers, KeyCode)
tuple.
Bevy doesn't have a logical key representation so there are no lkey!
and
lkeyseq!
macros.
use bevy::prelude::KeyCode;
use keyseq::{Modifiers, bevy::pkey};
assert_eq!(pkey! { Ctrl-A }, (Modifiers::CONTROL, KeyCode::KeyA));
assert_eq!(pkey! { Alt-A }, (Modifiers::ALT, KeyCode::KeyA));
assert_eq!(pkey! { Shift-A }, (Modifiers::SHIFT, KeyCode::KeyA));
assert_eq!(pkey! { Super-A }, (Modifiers::SUPER, KeyCode::KeyA));
assert_eq!(pkey! { Ctrl-Shift-A },
(Modifiers::SHIFT |
Modifiers::CONTROL, KeyCode::KeyA));
Logical Keys
With the "bevy" feature the keyseq::bevy::lkey!
macro returns a
(Modifiers, Key)
tuple.
use keyseq::{Modifiers, bevy::lkey as key};
use bevy::input::keyboard::Key;
assert_eq!(key! { a }, (Modifiers::NONE, Key::Character("a".into())));
assert_eq!(key! { Ctrl-a }, (Modifiers::CONTROL, Key::Character("a".into())));
assert_eq!(key! { Alt-a }, (Modifiers::ALT, Key::Character("a".into())));
assert_eq!(key! { Shift-a }, (Modifiers::SHIFT, Key::Character("a".into())));
assert_eq!(key! { Super-a }, (Modifiers::SUPER, Key::Character("a".into())));
assert_eq!(key! { Ctrl-Alt-; }, (Modifiers::ALT |
Modifiers::CONTROL, Key::Character(";".into())));
Logical Key Sequences
# use keyseq::Modifiers;
# use bevy::input::keyboard::Key;
use keyseq::bevy::lkeyseq;
assert_eq!(lkeyseq! { a Ctrl-b }, [(Modifiers::NONE, Key::Character("a".into())),
(Modifiers::CONTROL, Key::Character("b".into()))]);
Features
- winit, include support for winit
- bevy, include support for bevy
- poor, an anemic representation for internal testing
- strict-order, use a strict order for modifiers: Ctrl, Alt, Shift, Super (enabled by default)
Examples
For both examples press A
with modifiers and it will print a message showing
what keychord matched.
Winit Example
cargo run --example winit --features winit
Bevy Example
cargo run --example bevy --features bevy
Notes
Macro Notation
Although using parentheses will work pkey!(Ctrl-Alt-A)
, rustfmt
will add
spaces around the hyphen changing it to pkey!(Ctrl - Alt - A)
as will square
brackets. Therefore, it is suggested to use curly braces pkey! { Ctrl-Alt-A }
which are not reformatted like that.
Compatibility
keyseq | bevy | winit |
---|---|---|
0.5.* | 0.15.* | 0.30.* |
0.4.* | 0.14.* | 0.30.* |
0.3.* | 0.14.* | 0.29.* |
0.2.0 | 0.13.* | 0.29.* |
0.1.0 | 0.12.* | 0.29.* |
License
This crate is licensed under the MIT License or the Apache License 2.0.
Dependencies
~0.2–36MB
~575K SLoC