mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-24 08:58:12 +08:00
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:
@@ -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:?}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user