use crate::event::{Event, KeyEvent};
use crate::focus::FocusId;
use crate::keymap::{KeyBinding, Keymap};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum InputScope {
Focus(FocusId),
Named(String),
}
impl InputScope {
pub fn focused(id: FocusId) -> Self {
Self::Focus(id)
}
pub fn named(name: impl Into<String>) -> Self {
Self::Named(name.into())
}
}
impl From<FocusId> for InputScope {
fn from(id: FocusId) -> Self {
Self::focused(id)
}
}
impl From<&str> for InputScope {
fn from(name: &str) -> Self {
Self::named(name)
}
}
impl From<String> for InputScope {
fn from(name: String) -> Self {
Self::named(name)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InputCaptureMode {
Exclusive,
Passthrough,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InputCapture {
scope: InputScope,
mode: InputCaptureMode,
}
impl InputCapture {
pub fn scope(&self) -> &InputScope {
&self.scope
}
pub fn mode(&self) -> InputCaptureMode {
self.mode
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum InputRoute {
Captured(InputScope),
Focus(FocusId),
Global,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RoutedInput<A> {
pub action: A,
pub route: InputRoute,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InputHelpEntry {
pub route: InputRoute,
pub key: String,
pub description: String,
}
pub struct InputRouter<A: Clone> {
global: Keymap<A>,
focused: HashMap<FocusId, Keymap<A>>,
named: HashMap<String, Keymap<A>>,
captures: Vec<InputCapture>,
}
impl<A: Clone> InputRouter<A> {
pub fn new() -> Self {
Self {
global: Keymap::new(),
focused: HashMap::new(),
named: HashMap::new(),
captures: Vec::new(),
}
}
pub fn bind_global(mut self, key: KeyBinding, action: A, description: &str) -> Self {
self.register_global(key, action, description);
self
}
pub fn bind_focus(
mut self,
id: FocusId,
key: KeyBinding,
action: A,
description: &str,
) -> Self {
self.register_focus(id, key, action, description);
self
}
pub fn bind_scope<S>(mut self, scope: S, key: KeyBinding, action: A, description: &str) -> Self
where
S: Into<InputScope>,
{
self.register_scope(scope, key, action, description);
self
}
pub fn register_global(&mut self, key: KeyBinding, action: A, description: &str) {
self.global.register(key, action, description);
}
pub fn register_focus(&mut self, id: FocusId, key: KeyBinding, action: A, description: &str) {
self.focused
.entry(id)
.or_default()
.register(key, action, description);
}
pub fn register_scope<S>(&mut self, scope: S, key: KeyBinding, action: A, description: &str)
where
S: Into<InputScope>,
{
match scope.into() {
InputScope::Focus(id) => self.register_focus(id, key, action, description),
InputScope::Named(name) => {
self.named
.entry(name)
.or_default()
.register(key, action, description);
}
}
}
pub fn push_capture<S>(&mut self, scope: S)
where
S: Into<InputScope>,
{
self.push_capture_with_mode(scope, InputCaptureMode::Exclusive);
}
pub fn push_capture_with_mode<S>(&mut self, scope: S, mode: InputCaptureMode)
where
S: Into<InputScope>,
{
self.captures.push(InputCapture {
scope: scope.into(),
mode,
});
}
pub fn pop_capture(&mut self) -> Option<InputCapture> {
self.captures.pop()
}
pub fn remove_capture<S>(&mut self, scope: S) -> bool
where
S: Into<InputScope>,
{
let scope = scope.into();
if let Some(pos) = self
.captures
.iter()
.rposition(|capture| capture.scope == scope)
{
self.captures.remove(pos);
true
} else {
false
}
}
pub fn clear_captures(&mut self) {
self.captures.clear();
}
pub fn active_capture(&self) -> Option<&InputCapture> {
self.captures.last()
}
pub fn resolve_key(&self, key: &KeyEvent, focused: Option<FocusId>) -> Option<RoutedInput<A>> {
for capture in self.captures.iter().rev() {
if let Some(keymap) = self.keymap_for_scope(&capture.scope) {
if let Some(action) = keymap.resolve(key) {
return Some(RoutedInput {
action,
route: InputRoute::Captured(capture.scope.clone()),
});
}
}
if capture.mode == InputCaptureMode::Exclusive {
return None;
}
}
if let Some(id) = focused {
if let Some(keymap) = self.focused.get(&id) {
if let Some(action) = keymap.resolve(key) {
return Some(RoutedInput {
action,
route: InputRoute::Focus(id),
});
}
}
}
self.global.resolve(key).map(|action| RoutedInput {
action,
route: InputRoute::Global,
})
}
pub fn resolve_event(&self, event: &Event, focused: Option<FocusId>) -> Option<RoutedInput<A>> {
match event {
Event::Key(key) => self.resolve_key(key, focused),
_ => None,
}
}
pub fn global_help(&self) -> Vec<(String, String)> {
self.global.help()
}
pub fn focus_help(&self, id: FocusId) -> Vec<(String, String)> {
self.focused.get(&id).map(Keymap::help).unwrap_or_default()
}
pub fn scope_help<S>(&self, scope: S) -> Vec<(String, String)>
where
S: Into<InputScope>,
{
self.keymap_for_scope(&scope.into())
.map(Keymap::help)
.unwrap_or_default()
}
pub fn active_help(&self, focused: Option<FocusId>) -> Vec<InputHelpEntry> {
let mut entries = Vec::new();
for capture in self.captures.iter().rev() {
entries.extend(self.help_entries_for_scope(
&capture.scope,
InputRoute::Captured(capture.scope.clone()),
));
if capture.mode == InputCaptureMode::Exclusive {
return entries;
}
}
if let Some(id) = focused {
entries
.extend(self.help_entries_for_keymap(InputRoute::Focus(id), self.focused.get(&id)));
}
entries.extend(self.help_entries_for_keymap(InputRoute::Global, Some(&self.global)));
entries
}
fn keymap_for_scope(&self, scope: &InputScope) -> Option<&Keymap<A>> {
match scope {
InputScope::Focus(id) => self.focused.get(id),
InputScope::Named(name) => self.named.get(name),
}
}
fn help_entries_for_scope(&self, scope: &InputScope, route: InputRoute) -> Vec<InputHelpEntry> {
self.help_entries_for_keymap(route, self.keymap_for_scope(scope))
}
fn help_entries_for_keymap(
&self,
route: InputRoute,
keymap: Option<&Keymap<A>>,
) -> Vec<InputHelpEntry> {
keymap
.map(|keymap| {
keymap
.help()
.into_iter()
.map(|(key, description)| InputHelpEntry {
route: route.clone(),
key,
description,
})
.collect()
})
.unwrap_or_default()
}
}
impl<A: Clone> Default for InputRouter<A> {
fn default() -> Self {
Self::new()
}
}