rmk_types/action/key_action.rs
1//! Composite key actions stored in the keymap.
2
3use postcard::experimental::max_size::MaxSize;
4use serde::{Deserialize, Serialize};
5
6use super::Action;
7
8/// A KeyAction is the action at a keyboard position, stored in keymap.
9/// It can be a single action like triggering a key, or a composite keyboard action like tap/hold
10#[derive(Debug, Copy, Clone, Eq, Serialize, Deserialize, MaxSize)]
11#[cfg_attr(feature = "defmt", derive(defmt::Format))]
12#[non_exhaustive]
13#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
14#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
15pub enum KeyAction {
16 /// No action
17 No,
18 /// Transparent action, next layer will be checked
19 Transparent,
20 /// A single action, such as triggering a key, or activating a layer. Action is triggered when pressed and cancelled when released.
21 Single(Action),
22 /// Don't wait the release of the key, auto-release after a time threshold.
23 Tap(Action),
24 /// Tap hold action. The `u8` indexes the morse profile table; an index
25 /// with no table entry (e.g. `u8::MAX`, which the default-profile macros
26 /// emit) falls back to the default [`crate::morse::MorseProfile`].
27 TapHold(Action, Action, u8),
28 /// Morse action, references a morse configuration by index.
29 Morse(u8),
30}
31
32impl KeyAction {
33 /// Convert `KeyAction` to the internal `Action`.
34 /// Only valid for `Single` and `Tap` variant, returns `Action::No` for other variants.
35 pub fn to_action(self) -> Action {
36 match self {
37 KeyAction::Single(a) | KeyAction::Tap(a) => a,
38 _ => Action::No,
39 }
40 }
41
42 /// 'morse' is an alias for the superset of tap dance and tap hold keys,
43 /// since their handling have many similarities
44 pub fn is_morse(&self) -> bool {
45 matches!(self, KeyAction::TapHold(_, _, _) | KeyAction::Morse(_))
46 }
47
48 pub fn is_empty(&self) -> bool {
49 matches!(self, KeyAction::No)
50 }
51}
52
53/// Combo and fork trigger matching compares key actions by their "identity" —
54/// the tap/hold actions — ignoring the profile-table index.
55///
56/// This is intentional: a combo or fork may store a trigger with one profile
57/// index, but if the user later rebinds the key's profile, the trigger should
58/// still match. The profile is a per-key timing config, not part of the key's
59/// logical identity.
60impl PartialEq for KeyAction {
61 fn eq(&self, other: &Self) -> bool {
62 match (self, other) {
63 (KeyAction::No, KeyAction::No) => true,
64 (KeyAction::Transparent, KeyAction::Transparent) => true,
65 (KeyAction::Single(a), KeyAction::Single(b)) => a == b,
66 (KeyAction::Tap(a), KeyAction::Tap(b)) => a == b,
67 (KeyAction::TapHold(a, b, _), KeyAction::TapHold(c, d, _)) => a == c && b == d,
68 (KeyAction::Morse(a), KeyAction::Morse(b)) => a == b,
69 _ => false,
70 }
71 }
72}