use crate::core::Action;
use crate::view::interactivity::{ActionEvent, ActionHandler, Effects, EventContext, KeyDownEvent, TextInputEvent};
use crate::view::keymap::KeymapMatch;
use crate::view::window::Window;
impl Window {
fn focus_path(&self) -> Vec<usize> {
self.frame.dispatch.path_to_focus(self.entities.focus.focused())
}
pub fn dispatch_keystroke(&mut self, keystroke: &str, text: Option<&str>) -> bool {
let path = self.focus_path();
let contexts = self.frame.dispatch.context_stack(&path);
self.pending_keys.push(keystroke.to_owned());
let resolved = self.keymap.resolve(&self.pending_keys, &contexts);
self.entities.key_hint = (resolved == KeymapMatch::Pending).then(|| crate::core::KeyHint {
prefix: self.pending_keys.join(" "),
next: self.keymap.continuations(&self.pending_keys, &contexts),
});
match resolved {
KeymapMatch::Pending => return true,
KeymapMatch::Action(action) => {
self.pending_keys.clear();
if self.dispatch_action(action) {
return true;
}
}
KeymapMatch::None => {
let was_sequence = self.pending_keys.len() > 1;
self.pending_keys.clear();
if was_sequence {
return self.dispatch_keystroke(keystroke, text);
}
}
}
let event = KeyDownEvent { keystroke: keystroke.to_owned(), text: text.map(str::to_owned) };
let key_handlers: Vec<_> =
path.iter().rev().filter_map(|index| self.frame.dispatch.node(*index)?.key_down.clone()).collect();
for handler in key_handlers {
let mut context = EventContext { entities: &mut self.entities, effects: Effects::default() };
handler(&event, &mut context);
let effects = context.effects;
let stop = !effects.propagate;
self.finish(effects);
if stop {
return true;
}
}
let is_command = keystroke.contains("mod-") || keystroke.contains("ctrl-");
if let Some(text) = text.filter(|text| !is_command && !text.is_empty() && !text.chars().any(char::is_control))
&& self.dispatch_text_input(TextInputEvent::Insert(text.to_owned()))
{
return true;
}
match keystroke {
"tab" => self.cycle_focus(1),
"shift-tab" => self.cycle_focus(-1),
_ => false,
}
}
pub fn dispatch_action(&mut self, action: Action) -> bool {
let path = self.focus_path();
let handlers: Vec<ActionHandler> = path
.iter()
.rev()
.filter_map(|index| self.frame.dispatch.node(*index))
.flat_map(|node| {
node.actions.iter().filter(|(candidate, _)| *candidate == action).map(|(_, handler)| handler.clone())
})
.collect();
for handler in handlers {
let mut context = EventContext { entities: &mut self.entities, effects: Effects::default() };
handler(&ActionEvent { action }, &mut context);
let effects = context.effects;
let stop = !effects.propagate;
self.finish(effects);
if stop {
return true;
}
}
false
}
pub(crate) fn dispatch_text_input(&mut self, event: TextInputEvent) -> bool {
let path = self.focus_path();
let handler = path.iter().rev().find_map(|index| self.frame.dispatch.node(*index)?.text_input.clone());
let Some(handler) = handler else { return false };
let mut context = EventContext { entities: &mut self.entities, effects: Effects::default() };
handler(&event, &mut context);
let effects = context.effects;
self.finish(effects);
true
}
fn cycle_focus(&mut self, step: isize) -> bool {
let order = self.frame.dispatch.tab_order();
if order.is_empty() {
return false;
}
let current = self.entities.focus.focused().and_then(|focused| order.iter().position(|id| *id == focused));
let next = match current {
Some(index) => (index as isize + step).rem_euclid(order.len() as isize) as usize,
None if step > 0 => 0,
None => order.len() - 1,
};
self.entities.focus.focus(&crate::core::FocusHandle { id: order[next] });
true
}
}