Skip to main content

ferroui_base/input/platform/
key_gesture_format_info.rs

1use crate::input::Key;
2use crate::{FerroLocator, LocatorExtensions};
3use std::collections::HashMap;
4use std::rc::Rc;
5
6/// Provides platform specific formatting information for the `KeyGesture`
7/// class.
8///
9/// A format info without platform key overrides is the invariant one: keys
10/// are formatted with their plain names.
11#[derive(Clone, Debug, PartialEq)]
12pub struct KeyGestureFormatInfo {
13    platform_key_overrides: Option<HashMap<Key, String>>,
14    meta: String,
15    ctrl: String,
16    alt: String,
17    shift: String,
18}
19
20impl Default for KeyGestureFormatInfo {
21    fn default() -> Self {
22        Self::new(None, "Cmd", "Ctrl", "Alt", "Shift")
23    }
24}
25
26impl KeyGestureFormatInfo {
27    /// Creates a format info.
28    ///
29    /// `platform_key_overrides` holds the platform specific key names;
30    /// `meta`, `ctrl`, `alt` and `shift` are the names of the modifier
31    /// keys.
32    pub fn new(
33        platform_key_overrides: Option<HashMap<Key, String>>,
34        meta: &str,
35        ctrl: &str,
36        alt: &str,
37        shift: &str,
38    ) -> Self {
39        Self {
40            platform_key_overrides,
41            meta: meta.to_string(),
42            ctrl: ctrl.to_string(),
43            alt: alt.to_string(),
44            shift: shift.to_string(),
45        }
46    }
47
48    /// The invariant format info, used as formatting fallback.
49    pub fn invariant() -> Rc<KeyGestureFormatInfo> {
50        crate::ferro_thread_local! {
51            static INVARIANT: Rc<KeyGestureFormatInfo> = Rc::new(KeyGestureFormatInfo::default());
52        }
53        INVARIANT.with(Rc::clone)
54    }
55
56    /// The string used to represent Meta on the platform.
57    pub fn meta(&self) -> &str {
58        &self.meta
59    }
60
61    /// The string used to represent Ctrl on the platform.
62    pub fn ctrl(&self) -> &str {
63        &self.ctrl
64    }
65
66    /// The string used to represent Alt on the platform.
67    pub fn alt(&self) -> &str {
68        &self.alt
69    }
70
71    /// The string used to represent Shift on the platform.
72    pub fn shift(&self) -> &str {
73        &self.shift
74    }
75
76    /// Returns `format_provider` if given, otherwise the format info
77    /// registered with the locator, otherwise the invariant one.
78    pub fn get_instance(format_provider: Option<Rc<KeyGestureFormatInfo>>) -> Rc<KeyGestureFormatInfo> {
79        format_provider
80            .or_else(|| FerroLocator::current().get_service::<KeyGestureFormatInfo>())
81            .unwrap_or_else(Self::invariant)
82    }
83
84    /// A common source of key names, irrespective of platform, used when a
85    /// platform does not override the name of a key.
86    fn common_key_override(key: Key) -> Option<&'static str> {
87        Some(match key {
88            Key::Add => "+",
89            Key::D0 => "0",
90            Key::D1 => "1",
91            Key::D2 => "2",
92            Key::D3 => "3",
93            Key::D4 => "4",
94            Key::D5 => "5",
95            Key::D6 => "6",
96            Key::D7 => "7",
97            Key::D8 => "8",
98            Key::D9 => "9",
99            Key::Decimal => ".",
100            Key::Divide => "/",
101            Key::Multiply => "*",
102            Key::OemBackslash => "\\",
103            Key::OemCloseBrackets => "]",
104            Key::OemComma => ",",
105            Key::OemMinus => "-",
106            Key::OemOpenBrackets => "[",
107            Key::OemPeriod => ".",
108            Key::OemPipe => "|",
109            Key::OemPlus => "+",
110            Key::OemQuestion => "/",
111            Key::OemQuotes => "\"",
112            Key::OemSemicolon => ";",
113            Key::OemTilde => "`",
114            Key::Separator => "/",
115            Key::Subtract => "-",
116            Key::Back => "Backspace",
117            Key::Down => "Down Arrow",
118            Key::Left => "Left Arrow",
119            Key::Right => "Right Arrow",
120            Key::Up => "Up Arrow",
121            _ => return None,
122        })
123    }
124
125    /// Checks the platform overrides and the common overrides, in that
126    /// order, and returns the name of the key in the appropriate style, or
127    /// the key's plain name if there is none.
128    pub fn format_key(&self, key: Key) -> String {
129        // The absence of an overrides dictionary indicates this is the
130        // invariant, and so should just return the default name.
131        let Some(overrides) = &self.platform_key_overrides else { return key.name().to_string() };
132
133        match overrides.get(&key) {
134            Some(result) => result.clone(),
135            None => Self::common_key_override(key).unwrap_or(key.name()).to_string(),
136        }
137    }
138}