Restyle and refactor shortcut labels to send hints bar and welcome screen layouts from Rust (#3447)

* Restyle UserInputLabel and refactor its usages to have all input its data sent from Rust

* Replace the welcome screen quick buttons with ones sent by backend

* Add the ShortcutLabel widget to the backend

* Replace hints bar with a backend-controlled layout; show mouse icons in place of mouse labels
This commit is contained in:
Keavon Chambers
2025-12-04 01:04:14 -08:00
committed by GitHub
parent 6ed42d06bb
commit 810ce40e9b
78 changed files with 981 additions and 997 deletions
@@ -4,10 +4,18 @@ use bitflags::bitflags;
use std::fmt::{self, Display, Formatter};
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign};
// ===========
// StorageType
// ===========
// TODO: Increase size of type
/// Edit this to specify the storage type used.
pub type StorageType = u128;
// =========
// KeyStates
// =========
// Base-2 logarithm of the storage type used to represents how many bits you need to fully address every bit in that storage type
const STORAGE_SIZE: u32 = (std::mem::size_of::<StorageType>() * 8).trailing_zeros();
const STORAGE_SIZE_BITS: usize = 1 << STORAGE_SIZE;
@@ -23,11 +31,19 @@ pub fn all_required_modifiers_pressed(keyboard_state: &KeyStates, modifiers: &Ke
all_modifiers_without_pressed_modifiers.is_empty()
}
// ===========
// KeyPosition
// ===========
pub enum KeyPosition {
Pressed,
Released,
}
// ============
// ModifierKeys
// ============
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
#[repr(transparent)]
@@ -40,6 +56,10 @@ bitflags! {
}
}
// ===
// Key
// ===
// Currently this is mostly based on the JS `KeyboardEvent.code` list: <https://www.w3.org/TR/uievents-code/>
// But in the future, especially once users can customize keyboard mappings, we should deviate more from this so we have actual symbols
// like `+` (which doesn't exist because it's the shifted version of `=` on the US keyboard, after which these scan codes are named).
@@ -198,14 +218,19 @@ pub enum Key {
// Other keys that aren't part of the W3C spec
//
/// "Cmd" on Mac (not present on other platforms)
/// "Cmd" on Mac (not present on other platforms).
Command,
/// "Ctrl" on Windows/Linux, "Cmd" on Mac
/// "Ctrl" on Windows/Linux, "Cmd" on Mac.
Accel,
/// Left mouse button click (LMB).
MouseLeft,
/// Right mouse button click (RMB).
MouseRight,
/// Middle mouse button click (MMB).
MouseMiddle,
/// Mouse backward navigation button (typically on the side of the mouse).
MouseBack,
/// Mouse forward navigation button (typically on the side of the mouse).
MouseForward,
// Fake keys for displaying special labels in the UI
@@ -225,11 +250,11 @@ impl fmt::Display for Key {
// Writing system keys
const DIGIT_PREFIX: &str = "Digit";
if key_name.len() == DIGIT_PREFIX.len() + 1 && &key_name[0..DIGIT_PREFIX.len()] == "Digit" {
if key_name.len() == DIGIT_PREFIX.len() + 1 && &key_name[0..DIGIT_PREFIX.len()] == DIGIT_PREFIX {
return write!(f, "{}", key_name.chars().skip(DIGIT_PREFIX.len()).collect::<String>());
}
const KEY_PREFIX: &str = "Key";
if key_name.len() == KEY_PREFIX.len() + 1 && &key_name[0..KEY_PREFIX.len()] == "Key" {
if key_name.len() == KEY_PREFIX.len() + 1 && &key_name[0..KEY_PREFIX.len()] == KEY_PREFIX {
return write!(f, "{}", key_name.chars().skip(KEY_PREFIX.len()).collect::<String>());
}
@@ -313,26 +338,12 @@ impl fmt::Display for Key {
}
}
impl From<Key> for LayoutKey {
fn from(key: Key) -> Self {
Self { key, label: key.to_string() }
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct LayoutKey {
key: Key,
label: String,
}
impl LayoutKey {
pub fn key(&self) -> Key {
self.key
}
}
pub const NUMBER_OF_KEYS: usize = Key::_KeysVariantCount as usize - 1;
// =========
// KeysGroup
// =========
/// Only `Key`s that exist on a physical keyboard should be used.
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct KeysGroup(pub Vec<Key>);
@@ -365,21 +376,25 @@ impl fmt::Display for KeysGroup {
}
}
impl From<KeysGroup> for String {
fn from(keys: KeysGroup) -> Self {
let layout_keys: LayoutKeysGroup = keys.into();
serde_json::to_string(&layout_keys).expect("Failed to serialize KeysGroup")
// ==========
// LabeledKey
// ==========
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct LabeledKey {
key: Key,
label: String,
}
impl LabeledKey {
pub fn key(&self) -> Key {
self.key
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct LayoutKeysGroup(pub Vec<LayoutKey>);
impl From<KeysGroup> for LayoutKeysGroup {
fn from(keys_group: KeysGroup) -> Self {
Self(keys_group.0.into_iter().map(|key| key.into()).collect())
}
}
// ===========
// MouseMotion
// ===========
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum MouseMotion {
@@ -397,6 +412,45 @@ pub enum MouseMotion {
MmbDrag,
}
// =======================
// LabeledKeyOrMouseMotion
// =======================
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
#[serde(untagged)]
pub enum LabeledKeyOrMouseMotion {
Key(LabeledKey),
MouseMotion(MouseMotion),
}
impl From<Key> for LabeledKeyOrMouseMotion {
fn from(key: Key) -> Self {
match key {
Key::MouseLeft => Self::MouseMotion(MouseMotion::Lmb),
Key::MouseRight => Self::MouseMotion(MouseMotion::Rmb),
Key::MouseMiddle => Self::MouseMotion(MouseMotion::Mmb),
_ => Self::Key(LabeledKey { key, label: key.to_string() }),
}
}
}
// ===============
// LabeledShortcut
// ===============
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct LabeledShortcut(pub Vec<LabeledKeyOrMouseMotion>);
impl From<KeysGroup> for LabeledShortcut {
fn from(keys_group: KeysGroup) -> Self {
Self(keys_group.0.into_iter().map(|key| key.into()).collect())
}
}
// =========
// BitVector
// =========
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BitVector<const LENGTH: usize>([StorageType; LENGTH]);
@@ -117,14 +117,23 @@ macro_rules! mapping {
}};
}
/// Constructs an `ActionKeys` macro with a certain `Action` variant, conveniently wrapped in `Some()`.
macro_rules! action_keys {
/// Constructs an `ActionShortcut` macro with a certain `Action` variant, conveniently wrapped in `Some()`.
macro_rules! action_shortcut {
($action:expr_2021) => {
Some(crate::messages::input_mapper::utility_types::misc::ActionKeys::Action($action.into()))
Some(crate::messages::input_mapper::utility_types::misc::ActionShortcut::Action($action.into()))
};
}
pub(crate) use action_keys;
macro_rules! action_shortcut_manual {
($($keys:expr),*) => {
Some(crate::messages::input_mapper::utility_types::misc::ActionShortcut::Shortcut(
crate::messages::input_mapper::utility_types::input_keyboard::LabeledShortcut(vec![$($keys.into()),*]).into(),
))
};
}
pub(crate) use action_shortcut;
pub(crate) use action_shortcut_manual;
pub(crate) use entry;
pub(crate) use mapping;
pub(crate) use modifiers;
@@ -1,4 +1,4 @@
use super::input_keyboard::{KeysGroup, LayoutKeysGroup, all_required_modifiers_pressed};
use super::input_keyboard::{KeysGroup, LabeledShortcut, all_required_modifiers_pressed};
use crate::messages::input_mapper::key_mapping::MappingVariant;
use crate::messages::input_mapper::utility_types::input_keyboard::{KeyStates, NUMBER_OF_KEYS};
use crate::messages::input_mapper::utility_types::input_mouse::NUMBER_OF_MOUSE_BUTTONS;
@@ -128,28 +128,24 @@ pub struct MappingEntry {
}
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum ActionKeys {
pub enum ActionShortcut {
Action(MessageDiscriminant),
#[serde(rename = "keys")]
Keys(LayoutKeysGroup),
#[serde(rename = "shortcut")]
Shortcut(LabeledShortcut),
}
impl ActionKeys {
pub fn to_keys(&mut self, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Option<KeysGroup>) -> String {
impl ActionShortcut {
pub fn realize_shortcut(&mut self, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Option<KeysGroup>) {
match self {
Self::Action(action) => {
if let Some(keys) = action_input_mapping(action) {
let description = keys.to_string();
*self = Self::Keys(keys.into());
description
*self = Self::Shortcut(keys.into());
} else {
*self = Self::Keys(KeysGroup::default().into());
String::new()
*self = Self::Shortcut(KeysGroup::default().into());
}
}
Self::Keys(keys) => {
warn!("Calling `.to_keys()` on a `ActionKeys::Keys` is a mistake/bug. Keys are: {keys:?}.");
String::new()
Self::Shortcut(shortcut) => {
warn!("Calling `.to_keys()` on a `ActionShortcut::Shortcut` is a mistake/bug. Shortcut is: {shortcut:?}.");
}
}
}