pub struct Shortcuts<Id, Action> { /* private fields */ }Expand description
Maps key combinations to app-defined Actions, the same way
HitTester maps a pointer position to a widget id.
A lookup table an app consults, not something that owns input handling.
Bindings are either global (fire regardless of focus) or scoped to a
single FocusRing id (fire only while that id holds
focus); resolve checks the scoped binding first, so a
widget can shadow a global shortcut for the same key while it’s focused.
This does not replace ad hoc match key.code { .. } handling for
widget-specific navigation (arrow keys meaning “move selection” only
while a particular id is focused, say) – that kind of binding usually
carries extra context (list length, current offset) that doesn’t fit a
flat Action enum. Shortcuts is for the simple case: one key, always
the same Action, wherever it’s in scope. Bindings are a fixed table set
up once (there’s no per-frame begin_frame/registration step like
FocusRing’s – a key combination either exists or
it doesn’t, regardless of what happened to be drawn this frame).
§Example
use retroglyph_core::{Event, KeyCode, KeyEvent, KeyModifiers};
use retroglyph_widgets::Shortcuts;
#[derive(Clone, Copy, PartialEq, Eq)]
enum Id {
SearchBox,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Action {
ToggleTheme,
ClearSearch,
}
let mut shortcuts = Shortcuts::new();
shortcuts.bind_global(KeyCode::Char('t'), KeyModifiers::NONE, Action::ToggleTheme);
shortcuts.bind_scoped(
Id::SearchBox,
KeyCode::Escape,
KeyModifiers::NONE,
Action::ClearSearch,
);
let escape = Event::Key(KeyEvent::new(KeyCode::Escape, KeyModifiers::NONE));
assert_eq!(shortcuts.resolve(&escape, Some(Id::SearchBox)), Some(Action::ClearSearch));
assert_eq!(shortcuts.resolve(&escape, None), None); // scoped binding, nothing focused
let t = Event::Key(KeyEvent::new(KeyCode::Char('t'), KeyModifiers::NONE));
assert_eq!(shortcuts.resolve(&t, None), Some(Action::ToggleTheme)); // global, focus-independentImplementations§
Source§impl<Id: Copy + PartialEq, Action: Copy> Shortcuts<Id, Action>
impl<Id: Copy + PartialEq, Action: Copy> Shortcuts<Id, Action>
Sourcepub fn bind_global(
&mut self,
code: KeyCode,
modifiers: KeyModifiers,
action: Action,
)
pub fn bind_global( &mut self, code: KeyCode, modifiers: KeyModifiers, action: Action, )
Registers a binding that fires regardless of what holds focus.
Sourcepub fn bind_scoped(
&mut self,
id: Id,
code: KeyCode,
modifiers: KeyModifiers,
action: Action,
)
pub fn bind_scoped( &mut self, id: Id, code: KeyCode, modifiers: KeyModifiers, action: Action, )
Registers a binding that only fires while id holds focus.
Sourcepub fn resolve(&self, event: &Event, focused: Option<Id>) -> Option<Action>
pub fn resolve(&self, event: &Event, focused: Option<Id>) -> Option<Action>
Resolves event against focused (typically
FocusRing::focused).
None for anything but a key-down event. Otherwise: the first
registered binding scoped to focused with a matching
code/modifiers, or, failing that, the first matching global binding.
A scoped binding never fires for any id other than the one it named,
including when nothing is focused.