egui_keybind/
bind.rs

1use egui::{InputState, Key, KeyboardShortcut, ModifierNames, PointerButton};
2
3/// A trait can can be used for keybindings.
4///
5/// Must have a function to update the keybinding with a given [Key] and
6/// [Modifiers], aswell as a method that formats the keybinding as a [String].
7///
8/// Must implement [Clone].
9pub trait Bind: Clone {
10    /// Set the keybind with a given [KeyboardShortcut] and/or [PointerButton].
11    ///
12    /// # Arguments
13    /// * `keyboard` - The keyboard shortcut to set ([KeyboardShortcut]), or [None].
14    /// * `pointer` - The pointer button to set ([PointerButton]), or [None].
15    fn set(&mut self, keyboard: Option<KeyboardShortcut>, pointer: Option<PointerButton>);
16
17    /// Format the current keybind as a [String].
18    ///
19    /// # Arguments
20    /// * `names` - The [ModifierNames] to use.
21    /// * `is_mac` - Whether to use MacOS symbols.
22    ///
23    /// # Returns
24    /// The formatted keybind as a [String].
25    fn format(&self, names: &ModifierNames<'_>, is_mac: bool) -> String;
26
27    /// Check if the keybind is pressed.
28    ///
29    /// # Arguments
30    /// * `input` - The [InputState] to check with.
31    ///
32    /// # Returns
33    /// Whether the keybind is pressed.
34    fn pressed(&self, input: &mut InputState) -> bool;
35}
36
37/// A [Bind] implementation for [egui]'s [KeyboardShortcut].
38impl Bind for KeyboardShortcut {
39    fn set(&mut self, keyboard: Option<KeyboardShortcut>, _pointer: Option<PointerButton>) {
40        if let Some(keyboard) = keyboard {
41            *self = keyboard
42        }
43    }
44
45    fn format(&self, names: &ModifierNames<'_>, is_mac: bool) -> String {
46        self.format(names, is_mac)
47    }
48
49    fn pressed(&self, input: &mut InputState) -> bool {
50        input.consume_shortcut(self)
51    }
52}
53
54impl Bind for Option<KeyboardShortcut> {
55    fn set(&mut self, keyboard: Option<KeyboardShortcut>, _pointer: Option<PointerButton>) {
56        *self = keyboard;
57    }
58
59    fn format(&self, names: &ModifierNames<'_>, is_mac: bool) -> String {
60        self.as_ref().map_or_else(
61            || "None".to_string(),
62            |shortcut| shortcut.format(names, is_mac),
63        )
64    }
65
66    fn pressed(&self, input: &mut InputState) -> bool {
67        if let Some(shortcut) = self {
68            input.consume_shortcut(shortcut)
69        } else {
70            false
71        }
72    }
73}
74
75/// A [Bind] implementation for [egui]'s [Key]. Ignores modifiers.
76impl Bind for Key {
77    fn set(&mut self, keyboard: Option<KeyboardShortcut>, _pointer: Option<PointerButton>) {
78        if let Some(keyboard) = keyboard {
79            *self = keyboard.logical_key
80        }
81    }
82
83    fn format(&self, _names: &ModifierNames<'_>, _is_mac: bool) -> String {
84        self.name().to_string()
85    }
86
87    fn pressed(&self, input: &mut InputState) -> bool {
88        input.key_pressed(*self)
89    }
90}
91
92impl Bind for Option<Key> {
93    fn set(&mut self, keyboard: Option<KeyboardShortcut>, _pointer: Option<PointerButton>) {
94        if let Some(keyboard) = keyboard {
95            *self = Some(keyboard.logical_key)
96        }
97    }
98
99    fn format(&self, _names: &ModifierNames<'_>, _is_mac: bool) -> String {
100        self.as_ref()
101            .map_or_else(|| "None".to_string(), |key| key.name().to_string())
102    }
103
104    fn pressed(&self, input: &mut InputState) -> bool {
105        if let Some(key) = self {
106            input.key_pressed(*key)
107        } else {
108            false
109        }
110    }
111}
112
113/// A [Bind] implementation for [egui]'s [PointerButton]. Ignores keys and modifiers.
114impl Bind for PointerButton {
115    fn set(&mut self, _keyboard: Option<KeyboardShortcut>, pointer: Option<PointerButton>) {
116        if let Some(pointer) = pointer {
117            *self = pointer
118        }
119    }
120
121    fn format(&self, _names: &ModifierNames<'_>, _is_mac: bool) -> String {
122        format!("{:?}", self)
123    }
124
125    fn pressed(&self, input: &mut InputState) -> bool {
126        input.pointer.button_pressed(*self)
127    }
128}
129
130impl Bind for Option<PointerButton> {
131    fn set(&mut self, _keyboard: Option<KeyboardShortcut>, pointer: Option<PointerButton>) {
132        *self = pointer;
133    }
134
135    fn format(&self, _names: &ModifierNames<'_>, _is_mac: bool) -> String {
136        self.as_ref()
137            .map_or_else(|| "None".to_string(), |button| format!("{:?}", button))
138    }
139
140    fn pressed(&self, input: &mut InputState) -> bool {
141        if let Some(button) = self {
142            input.pointer.button_pressed(*button)
143        } else {
144            false
145        }
146    }
147}
148
149/// A keybind that can be set with either the keyboard or a mouse.
150#[derive(Debug, Clone, Copy, Default, PartialEq)]
151#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
152pub struct Shortcut {
153    /// Keyboard shortcut, if any. This can be set along with the mouse shortcut.
154    keyboard: Option<KeyboardShortcut>,
155    /// Mouse button, if any. This can be set along with the keyboard shortcut.
156    pointer: Option<PointerButton>,
157}
158
159impl Shortcut {
160    /// No keybind.
161    pub const NONE: Self = Self {
162        keyboard: None,
163        pointer: None,
164    };
165
166    /// Create a new [Shortcut].
167    ///
168    /// # Arguments
169    ///
170    /// * `keyboard` - The keyboard shortcut to set ([KeyboardShortcut]), or [None].
171    /// * `pointer` - The pointer button to set ([PointerButton]), or [None].
172    pub fn new(keyboard: Option<KeyboardShortcut>, pointer: Option<PointerButton>) -> Self {
173        Self { keyboard, pointer }
174    }
175
176    /// Keyboard shortcut, if any. This can be set along with the mouse shortcut.
177    #[inline]
178    pub fn keyboard(&self) -> Option<KeyboardShortcut> {
179        self.keyboard
180    }
181
182    /// Mouse button, if any. This can be set along with the keyboard shortcut.
183    #[inline]
184    pub const fn pointer(&self) -> Option<PointerButton> {
185        self.pointer
186    }
187}
188
189impl Bind for Shortcut {
190    fn set(&mut self, keyboard: Option<KeyboardShortcut>, pointer: Option<PointerButton>) {
191        self.keyboard = keyboard;
192        self.pointer = pointer;
193    }
194
195    fn format(&self, names: &ModifierNames<'_>, is_mac: bool) -> String {
196        let mut string = self.keyboard.map_or_else(
197            || String::with_capacity(9),
198            |kb| Into::<KeyboardShortcut>::into(kb).format(names, is_mac),
199        );
200        if let Some(pointer) = self.pointer {
201            if !string.is_empty() {
202                string.push('+');
203            }
204            string.push_str(&pointer.format(names, is_mac));
205        }
206        if string.is_empty() {
207            string.push_str("None");
208        }
209        string
210    }
211
212    fn pressed(&self, input: &mut InputState) -> bool {
213        let mut pressed = false;
214        if let Some(kb) = &self.keyboard {
215            pressed = input.consume_shortcut(kb);
216        }
217        if let Some(button) = self.pointer {
218            if self.keyboard.is_none() {
219                return input.pointer.button_clicked(button);
220            }
221            pressed &= input.pointer.button_clicked(button);
222        }
223        pressed
224    }
225}
226
227impl From<Shortcut> for Option<KeyboardShortcut> {
228    fn from(value: Shortcut) -> Self {
229        value.keyboard
230    }
231}
232
233impl From<Shortcut> for Option<PointerButton> {
234    fn from(value: Shortcut) -> Self {
235        value.pointer
236    }
237}