vizia_core/input/
keymap.rs

1use crate::prelude::*;
2use indexmap::IndexMap;
3
4/// A keymap that associates key chords with actions.
5///
6/// This is useful if you have an application that lets the user configure their key chords.
7/// It allows you to check if a particular action is pressed rather than the actual keys.
8/// The relationship between a key chord and an action is a many-to-many relationship.
9///
10/// # Examples
11///
12/// First we need to create something that represents an action in our application.
13/// This is usually an enum.
14///
15/// ```
16/// #[derive(PartialEq, Copy, Clone)]
17/// enum Action {
18///     One,
19///     Two,
20///     Three,
21/// }
22/// ```
23///
24/// Now we can create a new keymap inside of our application and configure our key chords.
25/// We will bind `Action::One` to the key chord `A`, `Action::Two` to the key chord `CTRL+B`
26/// and `Action::Three` to the key chord `CTRL+SHIFT+C`. Every action has an associated callback
27/// function that gets triggered when the key chord is pressed.
28///
29/// ```
30/// # use vizia_core::prelude::*;
31/// #
32/// # #[derive(PartialEq, Copy, Clone)]
33/// # enum Action {
34/// #     One,
35/// #     Two,
36/// #     Three,
37/// # }
38/// #
39/// let keymap = Keymap::from(vec![
40///     (KeyChord::new(Modifiers::empty(), Code::KeyA), KeymapEntry::new(Action::One, |_| debug!("Action One"))),
41///     (KeyChord::new(Modifiers::CTRL, Code::KeyB), KeymapEntry::new(Action::Two, |_| debug!("Action Two"))),
42///     (KeyChord::new(Modifiers::CTRL | Modifiers::SHIFT, Code::KeyC), KeymapEntry::new(Action::Three, |_| debug!("Action Three"))),
43/// ]);
44/// ```
45#[derive(Default)]
46pub struct Keymap<T>
47where
48    T: 'static + Clone + PartialEq + Send + Sync,
49{
50    entries: IndexMap<KeyChord, Vec<KeymapEntry<T>>>,
51}
52
53impl<T> Keymap<T>
54where
55    T: 'static + Clone + PartialEq + Send + Sync,
56{
57    /// Creates a new keymap.
58    ///
59    /// # Examples
60    ///
61    /// ```
62    /// # use vizia_core::prelude::*;
63    /// #
64    /// # #[derive(Debug, PartialEq, Copy, Clone)]
65    /// # enum Action {
66    /// #     One,
67    /// #     Two,
68    /// #     Three,
69    /// # }
70    /// #
71    /// let keymap = Keymap::<Action>::new();
72    /// ```
73    pub fn new() -> Self {
74        Self { entries: IndexMap::new() }
75    }
76
77    /// Inserts an entry into the keymap.
78    ///
79    /// This method is for internal use only.
80    /// To insert an entry into the keymap at runtime use the [`KeymapEvent::InsertAction`] event.
81    fn insert(&mut self, chord: KeyChord, keymap_entry: KeymapEntry<T>) {
82        if let Some(actions) = self.entries.get_mut(&chord) {
83            if !actions.contains(&keymap_entry) {
84                actions.push(keymap_entry);
85            }
86        } else {
87            self.entries.insert(chord, vec![keymap_entry]);
88        }
89    }
90
91    /// Removes an entry of the keymap.
92    ///
93    /// This method is for internal use only.
94    /// To remove an entry of the keymap at runtime use the [`KeymapEvent::RemoveAction`] event.
95    fn remove(&mut self, chord: &KeyChord, action: &T) {
96        if let Some(actions) = self.entries.get_mut(chord) {
97            if let Some(index) = actions.iter().position(|x| x == action) {
98                if actions.len() == 1 {
99                    self.entries.swap_remove(chord);
100                } else {
101                    actions.swap_remove(index);
102                }
103            }
104        }
105    }
106
107    /// Returns an iterator over every pressed keymap entry.
108    ///
109    /// # Examples
110    ///
111    /// ```
112    /// # use vizia_core::prelude::*;
113    /// #
114    /// # #[derive(Debug, PartialEq, Copy, Clone)]
115    /// # enum Action {
116    /// #     One,
117    /// # }
118    /// #
119    /// # let cx = &Context::default();
120    /// # let keymap = Keymap::<Action>::new();
121    /// #
122    /// for entry in keymap.pressed_actions(cx, Code::KeyA) {
123    ///     debug!("The action {:?} is being pressed!", entry.action());
124    /// };
125    pub fn pressed_actions(
126        &self,
127        cx: &Context,
128        code: Code,
129    ) -> impl Iterator<Item = &KeymapEntry<T>> {
130        if let Some(actions) = self.entries.get(&KeyChord::new(cx.modifiers, code)) {
131            actions.iter()
132        } else {
133            [].iter()
134        }
135    }
136
137    /// Exports all keymap entries and their associated key chords.
138    ///
139    /// This is useful if you want to have a settings window and need to access every key chord
140    /// and keymap entry of a keymap.
141    ///
142    /// # Examples
143    ///
144    /// ```
145    /// # use vizia_core::prelude::*;
146    /// #
147    /// # #[derive(Debug, PartialEq, Copy, Clone)]
148    /// # enum Action {
149    /// #     One,
150    /// # }
151    /// #
152    /// # let keymap = Keymap::<Action>::new();
153    /// #
154    /// let actions_chords = keymap.export();
155    ///
156    /// for (chord, entry) in actions_chords {
157    ///     debug!("The key chord {:?} triggers the action {:?}!", chord, entry.action());
158    /// }
159    /// ```
160    pub fn export(&self) -> Vec<(&KeyChord, &KeymapEntry<T>)> {
161        let mut vec = Vec::new();
162        for (chord, entries) in self.entries.iter() {
163            for entry in entries {
164                vec.push((chord, entry));
165            }
166        }
167        vec
168    }
169}
170
171impl<T> Model for Keymap<T>
172where
173    T: 'static + Clone + PartialEq + Send + Sync,
174{
175    fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
176        event.map(|keymap_event, _| match keymap_event {
177            KeymapEvent::InsertAction(chord, entry) => self.insert(*chord, entry.clone()),
178            KeymapEvent::RemoveAction(chord, action) => self.remove(chord, action),
179        });
180        event.map(|window_event, _| match window_event {
181            WindowEvent::KeyDown(code, _) => {
182                if let Some(entries) = self.entries.get(&KeyChord::new(*cx.modifiers, *code)) {
183                    for entry in entries {
184                        (entry.on_action())(cx)
185                    }
186                }
187            }
188            _ => {}
189        })
190    }
191}
192
193impl<T> From<Vec<(KeyChord, KeymapEntry<T>)>> for Keymap<T>
194where
195    T: 'static + Clone + PartialEq + Send + Sync,
196{
197    fn from(vec: Vec<(KeyChord, KeymapEntry<T>)>) -> Self {
198        let mut keymap = Self::new();
199        for (chord, entry) in vec {
200            keymap.insert(chord, entry);
201        }
202        keymap
203    }
204}
205
206/// An event used to interact with a [`Keymap`] at runtime.
207pub enum KeymapEvent<T>
208where
209    T: 'static + Clone + PartialEq + Send + Sync,
210{
211    /// Inserts an entry into the [`Keymap`].
212    ///
213    /// # Examples
214    ///
215    /// ```
216    /// # use vizia_core::prelude::*;
217    /// #
218    /// # #[derive(PartialEq, Copy, Clone)]
219    /// # enum Action {
220    /// #     One,
221    /// # }
222    /// #
223    /// # let cx = &mut Context::default();
224    /// #
225    /// cx.emit(KeymapEvent::InsertAction(
226    ///     KeyChord::new(Modifiers::empty(), Code::KeyA),
227    ///     KeymapEntry::new(Action::One, |_| debug!("Action One")),
228    /// ));
229    /// ```
230    InsertAction(KeyChord, KeymapEntry<T>),
231    /// Removes an entry from the [`Keymap`].
232    ///
233    /// # Examples
234    ///
235    /// ```
236    /// # use vizia_core::prelude::*;
237    /// #
238    /// # #[derive(PartialEq, Copy, Clone)]
239    /// # enum Action {
240    /// #     One,
241    /// # }
242    /// #
243    /// # let cx = &mut Context::default();
244    /// #
245    /// cx.emit(KeymapEvent::RemoveAction(
246    ///     KeyChord::new(Modifiers::empty(), Code::KeyA),
247    ///     Action::One,
248    /// ));
249    /// ```
250    RemoveAction(KeyChord, T),
251}