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}