Skip to main content

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}