Crates.io | keyseq |
lib.rs | keyseq |
version | 0.3.0 |
source | src |
created_at | 2024-02-18 09:56:21.510791 |
updated_at | 2024-07-05 05:54:50.205181 |
description | Specify key chords using `ctrl-A` short-hand |
homepage | |
repository | https://github.com/shanecelis/keyseq |
max_upload_size | |
id | 1143972 |
size | 152,523 |
Specify key chords using ctrl-A
short-hand, supports bevy and
winit.
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" or "Ctrl+A".
cargo add keyseq --features bevy; # OR --features winit
pkey!
macro specifies a physical key chord, e.g., pkey! { ctrl-A }
.pkeyseq!
macro specifies a physical key chord sequence, e.g., pkeyseq! { ctrl-A alt-B C }
.lkey!
macro specifies a logical key chord, .e.g, lkey! { ctrl-a }
.lkeyseq!
macro specifies a logical key chord sequence, e.g. lkeyseq! { ctrl-a alt-b c }
.With the "winit" feature the keyseq::winit::pkey!
macro returns a
(Modifiers, KeyCode)
tuple.
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));
# 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)]);
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(';')));
# 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'))]);
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
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.
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.
let 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.
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));
For both examples press A
with modifiers and it will print a message showing
what keychord matched.
cargo run --example winit --features winit
cargo run --example bevy --features bevy
Although using parens will work pkey!(ctrl-alt-A)
, rustfmt will add spaces
around the hyphen changing it to pkey!(ctrl - alt - A)
. Therefore, it's
suggested to use curly braces pkey! { ctrl-alt-A }
which are not reformatted
like that.
keyseq | bevy | winit |
---|---|---|
0.1.0 | 0.12.* | 0.29.* |
0.2.0 | 0.13.* | 0.29.* |
This crate is licensed under the MIT License or the Apache License 2.0.