#winit #bevy #keyboard #hotkey

keyseq

使用 ctrl-A 简写指定按键组合

7 个版本

0.3.0 2024年7月5日
0.2.3 2024年4月8日
0.2.2 2024年3月6日
0.2.1 2024年2月20日
0.1.1 2024年2月20日

#127GUI


被用于 bevy-input-sequence

MIT/Apache

43KB
201

keyseq

Maintenance CI crates-io api-docs

使用 ctrl-A 简写指定按键组合,支持 bevywinit

目标

  • 在代码中指定按键组合的方式与文档中指定的方式相同。

  • 为了在代码中找到按键组合,建议使用一种描述键的方式,例如,接受 "ctrl-A";不接受 "control-A" 或 "C-A" 或 "Ctrl+A"。

安装

cargo add keyseq --features bevy; # OR --features winit

主要宏

  • pkey! 宏指定物理按键组合,例如,pkey! { ctrl-A }
  • pkeyseq! 宏指定物理按键组合序列,例如,pkeyseq! { ctrl-A alt-B C }
  • lkey! 宏指定逻辑按键组合,例如,lkey! { ctrl-a }
  • lkeyseq! 宏指定逻辑按键组合序列,例如,lkeyseq! { ctrl-a alt-b c }

概念

  • 物理按键表示键盘上的特定按键。它发出的键码不会因按下什么修饰键而改变。例如,有一个物理的 'Q' 键,通常位于制表键的右侧。没有物理的 lowercase 'q' 键。
  • 逻辑按键是由按键产生的。如果按下一个键产生一个 'q' 字符,那么它逻辑上就是一个 'q' 键。

用法

Winit

使用 "winit" 功能,keyseq::winit::pkey! 宏返回一个 (Modifiers, KeyCode) 元组。

物理按键

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)]);

逻辑按键

使用 "winit" 功能,宏 keyseq::winit::lkey! 返回一个 (Modifiers, Key) 元组。

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'))]);

没有小写物理按键

以下代码将无法编译。它坚持使用大写 'A' 来指定 A 键。

# use keyseq::winit::pkey;
let (mods, key) = pkey! { a }; // error: Use uppercase key names for physical keys

严格的修饰符顺序

默认启用 "strict-order" 功能,修饰符顺序错误将产生编译错误。如果没有该功能,它将发出警告。

# use keyseq::winit::pkey;
let _ = pkey! { alt-ctrl-A }; // error: Modifiers must occur in this order: control, alt, shift, super.

为什么不使用 winit::keyboard::ModifiersState

为什么返回 keyseq::Modifiers 而不是 winit 的自身 ModifiersStatekeyseq::Modifierswinit::keyboard::ModifiersState 都是通过 bitflags 包生成的。最初,这个包返回 winit 的本地修饰符结构,因为它反编译成几乎相同的东西

// 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)

然而,这些位标志与位或管道结合在一起时存在一个问题。

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!"),

反编译后,位或 | 现在被解释为匹配或 |,它不匹配 ctrl-alt;它只匹配 ctrlalt 或没有修饰符。(这实际上对 bitflags 生成的结构体来说似乎是一个相当大的表达能力缺陷。)

为了避免这个问题,keyseq::Modifiers 被定义为 Modifiers(pub u8),并且位标志在宏中计算。这允许以下匹配表达式按预期工作。

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!"),

此外 keyseq::Modifiers 实现 keyseq::ModifiersState 和反之亦然。

Bevy

启用 "bevy" 功能,宏 keyseq::keyseq::bevy::pkey! 返回一个 (keyseqModifiers, KeyCode) 元组。

Bevy 没有逻辑键表示,因此没有 lkey!lkeyseq! 宏。

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));

功能

  • Winit,包括对 winit 的支持
  • Bevy,包括对 bevy 的支持
  • Poor,内部测试的瘦表示
  • Strict-order,使用严格的修饰符顺序:ctrl、alt、shift、super(默认启用)

示例

对于这两个示例,按 A 并带有修饰符,将打印出匹配的键序列。

Winit 示例

cargo run --example winit --features winit

Bevy 示例

cargo run --example bevy --features bevy

注意

宏表示法

虽然使用括号也可以工作 pkey!(ctrl-alt-A),但 rustfmt 会自动在破折号周围添加空格,将其变为 pkey!(ctrl - alt - A)。因此,建议使用花括号 pkey! { ctrl-alt-A },这样就不会被重新格式化。

兼容性

keyseq bevy winit
0.1.0 0.12.* 0.29.*
0.2.0 0.13.* 0.29.*

许可

此软件包受 MIT 许可证或 Apache 许可证 2.0 的许可。

依赖项

~0.2–37MB
~584K SLoC