1use crate::event::{Event, KeyEvent};
9use crate::focus::FocusId;
10use crate::keymap::{KeyBinding, Keymap};
11use std::collections::HashMap;
12
13#[derive(Debug, Clone, PartialEq, Eq, Hash)]
15pub enum InputScope {
16 Focus(FocusId),
18 Named(String),
20}
21
22impl InputScope {
23 pub fn focused(id: FocusId) -> Self {
25 Self::Focus(id)
26 }
27
28 pub fn named(name: impl Into<String>) -> Self {
30 Self::Named(name.into())
31 }
32}
33
34impl From<FocusId> for InputScope {
35 fn from(id: FocusId) -> Self {
36 Self::focused(id)
37 }
38}
39
40impl From<&str> for InputScope {
41 fn from(name: &str) -> Self {
42 Self::named(name)
43 }
44}
45
46impl From<String> for InputScope {
47 fn from(name: String) -> Self {
48 Self::named(name)
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub enum InputCaptureMode {
55 Exclusive,
57 Passthrough,
59}
60
61#[derive(Debug, Clone, PartialEq, Eq)]
63pub struct InputCapture {
64 scope: InputScope,
65 mode: InputCaptureMode,
66}
67
68impl InputCapture {
69 pub fn scope(&self) -> &InputScope {
71 &self.scope
72 }
73
74 pub fn mode(&self) -> InputCaptureMode {
76 self.mode
77 }
78}
79
80#[derive(Debug, Clone, PartialEq, Eq)]
82pub enum InputRoute {
83 Captured(InputScope),
85 Focus(FocusId),
87 Global,
89}
90
91#[derive(Debug, Clone, PartialEq, Eq)]
93pub struct RoutedInput<A> {
94 pub action: A,
96 pub route: InputRoute,
98}
99
100#[derive(Debug, Clone, PartialEq, Eq)]
102pub struct InputHelpEntry {
103 pub route: InputRoute,
105 pub key: String,
107 pub description: String,
109}
110
111pub struct InputRouter<A: Clone> {
124 global: Keymap<A>,
125 focused: HashMap<FocusId, Keymap<A>>,
126 named: HashMap<String, Keymap<A>>,
127 captures: Vec<InputCapture>,
128}
129
130impl<A: Clone> InputRouter<A> {
131 pub fn new() -> Self {
133 Self {
134 global: Keymap::new(),
135 focused: HashMap::new(),
136 named: HashMap::new(),
137 captures: Vec::new(),
138 }
139 }
140
141 pub fn bind_global(mut self, key: KeyBinding, action: A, description: &str) -> Self {
143 self.register_global(key, action, description);
144 self
145 }
146
147 pub fn bind_focus(
149 mut self,
150 id: FocusId,
151 key: KeyBinding,
152 action: A,
153 description: &str,
154 ) -> Self {
155 self.register_focus(id, key, action, description);
156 self
157 }
158
159 pub fn bind_scope<S>(mut self, scope: S, key: KeyBinding, action: A, description: &str) -> Self
161 where
162 S: Into<InputScope>,
163 {
164 self.register_scope(scope, key, action, description);
165 self
166 }
167
168 pub fn register_global(&mut self, key: KeyBinding, action: A, description: &str) {
170 self.global.register(key, action, description);
171 }
172
173 pub fn register_focus(&mut self, id: FocusId, key: KeyBinding, action: A, description: &str) {
175 self.focused
176 .entry(id)
177 .or_default()
178 .register(key, action, description);
179 }
180
181 pub fn register_scope<S>(&mut self, scope: S, key: KeyBinding, action: A, description: &str)
183 where
184 S: Into<InputScope>,
185 {
186 match scope.into() {
187 InputScope::Focus(id) => self.register_focus(id, key, action, description),
188 InputScope::Named(name) => {
189 self.named
190 .entry(name)
191 .or_default()
192 .register(key, action, description);
193 }
194 }
195 }
196
197 pub fn push_capture<S>(&mut self, scope: S)
199 where
200 S: Into<InputScope>,
201 {
202 self.push_capture_with_mode(scope, InputCaptureMode::Exclusive);
203 }
204
205 pub fn push_capture_with_mode<S>(&mut self, scope: S, mode: InputCaptureMode)
207 where
208 S: Into<InputScope>,
209 {
210 self.captures.push(InputCapture {
211 scope: scope.into(),
212 mode,
213 });
214 }
215
216 pub fn pop_capture(&mut self) -> Option<InputCapture> {
218 self.captures.pop()
219 }
220
221 pub fn remove_capture<S>(&mut self, scope: S) -> bool
223 where
224 S: Into<InputScope>,
225 {
226 let scope = scope.into();
227 if let Some(pos) = self
228 .captures
229 .iter()
230 .rposition(|capture| capture.scope == scope)
231 {
232 self.captures.remove(pos);
233 true
234 } else {
235 false
236 }
237 }
238
239 pub fn clear_captures(&mut self) {
241 self.captures.clear();
242 }
243
244 pub fn active_capture(&self) -> Option<&InputCapture> {
246 self.captures.last()
247 }
248
249 pub fn resolve_key(&self, key: &KeyEvent, focused: Option<FocusId>) -> Option<RoutedInput<A>> {
251 for capture in self.captures.iter().rev() {
252 if let Some(keymap) = self.keymap_for_scope(&capture.scope) {
253 if let Some(action) = keymap.resolve(key) {
254 return Some(RoutedInput {
255 action,
256 route: InputRoute::Captured(capture.scope.clone()),
257 });
258 }
259 }
260
261 if capture.mode == InputCaptureMode::Exclusive {
262 return None;
263 }
264 }
265
266 if let Some(id) = focused {
267 if let Some(keymap) = self.focused.get(&id) {
268 if let Some(action) = keymap.resolve(key) {
269 return Some(RoutedInput {
270 action,
271 route: InputRoute::Focus(id),
272 });
273 }
274 }
275 }
276
277 self.global.resolve(key).map(|action| RoutedInput {
278 action,
279 route: InputRoute::Global,
280 })
281 }
282
283 pub fn resolve_event(&self, event: &Event, focused: Option<FocusId>) -> Option<RoutedInput<A>> {
285 match event {
286 Event::Key(key) => self.resolve_key(key, focused),
287 _ => None,
288 }
289 }
290
291 pub fn global_help(&self) -> Vec<(String, String)> {
293 self.global.help()
294 }
295
296 pub fn focus_help(&self, id: FocusId) -> Vec<(String, String)> {
298 self.focused.get(&id).map(Keymap::help).unwrap_or_default()
299 }
300
301 pub fn scope_help<S>(&self, scope: S) -> Vec<(String, String)>
303 where
304 S: Into<InputScope>,
305 {
306 self.keymap_for_scope(&scope.into())
307 .map(Keymap::help)
308 .unwrap_or_default()
309 }
310
311 pub fn active_help(&self, focused: Option<FocusId>) -> Vec<InputHelpEntry> {
313 let mut entries = Vec::new();
314
315 for capture in self.captures.iter().rev() {
316 entries.extend(self.help_entries_for_scope(
317 &capture.scope,
318 InputRoute::Captured(capture.scope.clone()),
319 ));
320 if capture.mode == InputCaptureMode::Exclusive {
321 return entries;
322 }
323 }
324
325 if let Some(id) = focused {
326 entries
327 .extend(self.help_entries_for_keymap(InputRoute::Focus(id), self.focused.get(&id)));
328 }
329
330 entries.extend(self.help_entries_for_keymap(InputRoute::Global, Some(&self.global)));
331 entries
332 }
333
334 fn keymap_for_scope(&self, scope: &InputScope) -> Option<&Keymap<A>> {
335 match scope {
336 InputScope::Focus(id) => self.focused.get(id),
337 InputScope::Named(name) => self.named.get(name),
338 }
339 }
340
341 fn help_entries_for_scope(&self, scope: &InputScope, route: InputRoute) -> Vec<InputHelpEntry> {
342 self.help_entries_for_keymap(route, self.keymap_for_scope(scope))
343 }
344
345 fn help_entries_for_keymap(
346 &self,
347 route: InputRoute,
348 keymap: Option<&Keymap<A>>,
349 ) -> Vec<InputHelpEntry> {
350 keymap
351 .map(|keymap| {
352 keymap
353 .help()
354 .into_iter()
355 .map(|(key, description)| InputHelpEntry {
356 route: route.clone(),
357 key,
358 description,
359 })
360 .collect()
361 })
362 .unwrap_or_default()
363 }
364}
365
366impl<A: Clone> Default for InputRouter<A> {
367 fn default() -> Self {
368 Self::new()
369 }
370}