use std::{
fmt,
hash::{Hash, Hasher},
rc::Rc,
};
use cranpose_core::{Applier, MemoryApplier, NodeId};
use cranpose_foundation::{
DelegatableNode, ModifierChainNodeRef, ModifierNode, ModifierNodeElement, NodeCapabilities,
NodeState,
};
use smallvec::SmallVec;
use super::{Modifier, ModifierChainHandle};
use crate::{
KeyEvent, SubcomposeLayoutNode, active_focus_target, widgets::nodes::layout_node::LayoutNode,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
enum KeyPass {
Preview,
Bubble,
}
type KeyHandler = Rc<dyn Fn(&KeyEvent) -> bool>;
impl Modifier {
pub fn on_preview_key_event(self, handler: impl Fn(&KeyEvent) -> bool + 'static) -> Self {
self.then(Self::with_element(KeyInputElement {
pass: KeyPass::Preview,
handler: Rc::new(handler),
}))
}
pub fn on_key_event(self, handler: impl Fn(&KeyEvent) -> bool + 'static) -> Self {
self.then(Self::with_element(KeyInputElement {
pass: KeyPass::Bubble,
handler: Rc::new(handler),
}))
}
}
#[derive(Clone)]
struct KeyInputElement {
pass: KeyPass,
handler: KeyHandler,
}
impl fmt::Debug for KeyInputElement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("KeyInputElement")
.field("pass", &self.pass)
.finish_non_exhaustive()
}
}
impl PartialEq for KeyInputElement {
fn eq(&self, other: &Self) -> bool {
self.pass == other.pass
}
}
impl Hash for KeyInputElement {
fn hash<H: Hasher>(&self, state: &mut H) {
self.pass.hash(state);
}
}
impl ModifierNodeElement for KeyInputElement {
type Node = KeyInputNode;
fn create(&self) -> Self::Node {
KeyInputNode {
state: NodeState::new(),
pass: self.pass,
handler: self.handler.clone(),
}
}
fn update(&self, node: &mut Self::Node) {
node.pass = self.pass;
node.handler = self.handler.clone();
}
fn always_update(&self) -> bool {
true
}
fn capabilities(&self) -> NodeCapabilities {
NodeCapabilities::KEY_INPUT
}
}
struct KeyInputNode {
state: NodeState,
pass: KeyPass,
handler: KeyHandler,
}
impl DelegatableNode for KeyInputNode {
fn node_state(&self) -> &NodeState {
&self.state
}
}
impl ModifierNode for KeyInputNode {}
pub struct KeyEventRoute {
focused: Option<NodeId>,
handlers: SmallVec<[NodeId; 8]>,
in_scope: bool,
}
impl KeyEventRoute {
pub fn new(applier: &mut MemoryApplier, root: Option<NodeId>) -> Self {
let mut route = Self {
focused: active_focus_target(),
handlers: SmallVec::new(),
in_scope: false,
};
route.in_scope = visit_focus_ancestors(applier, route.focused, root, |id, chain| {
if chain.capabilities().contains(NodeCapabilities::KEY_INPUT) {
route.handlers.push(id);
}
});
if !route.in_scope {
route.handlers.clear();
}
route
}
pub fn contains_focus(applier: &mut MemoryApplier, root: Option<NodeId>) -> bool {
visit_focus_ancestors(applier, active_focus_target(), root, |_, _| {})
}
pub fn focus_is_current(&self) -> bool {
self.focused == active_focus_target() && (self.focused.is_none() || self.in_scope)
}
pub fn dispatch_preview(&self, applier: &mut MemoryApplier, event: &KeyEvent) -> bool {
self.handlers
.iter()
.rev()
.any(|&node| dispatch_node(applier, node, event, KeyPass::Preview))
}
pub fn dispatch_bubble(&self, applier: &mut MemoryApplier, event: &KeyEvent) -> bool {
self.handlers
.iter()
.any(|&node| dispatch_node(applier, node, event, KeyPass::Bubble))
}
}
fn visit_focus_ancestors(
applier: &mut MemoryApplier,
focused: Option<NodeId>,
root: Option<NodeId>,
mut visit: impl FnMut(NodeId, &ModifierChainHandle),
) -> bool {
let (Some(mut current), Some(root)) = (focused, root) else {
return false;
};
for _ in 0..100_000 {
let Ok(node) = applier.get_mut(current) else {
return false;
};
let parent = node.parent();
let node = node.as_any_mut();
let window_root = if let Some(layout) = node.downcast_mut::<LayoutNode>() {
visit(current, layout.modifier_chain());
layout.is_window_root()
} else {
if let Some(subcompose) = node.downcast_mut::<SubcomposeLayoutNode>() {
subcompose.with_modifier_chain(|chain| visit(current, chain));
}
false
};
if current == root {
return true;
}
if window_root {
return false;
}
let Some(parent) = parent else {
return false;
};
current = parent;
}
false
}
fn dispatch_node(
applier: &mut MemoryApplier,
node: NodeId,
event: &KeyEvent,
pass: KeyPass,
) -> bool {
if let Ok(handled) = applier.with_node::<LayoutNode, _>(node, |node| {
dispatch_chain(node.modifier_chain(), event, pass)
}) {
return handled;
}
applier
.with_node::<SubcomposeLayoutNode, _>(node, |node| {
node.with_modifier_chain(|chain| dispatch_chain(chain, event, pass))
})
.unwrap_or(false)
}
fn dispatch_chain(chain: &ModifierChainHandle, event: &KeyEvent, pass: KeyPass) -> bool {
let chain = chain.chain();
let visit = |entry: ModifierChainNodeRef<'_>| {
entry.kind_set().contains(NodeCapabilities::KEY_INPUT)
&& entry
.with_node(|node| {
let node: &dyn std::any::Any = node;
node.downcast_ref::<KeyInputNode>()
.is_some_and(|node| node.pass == pass && (node.handler)(event))
})
.unwrap_or(false)
};
match pass {
KeyPass::Preview => chain.head_to_tail().any(visit),
KeyPass::Bubble => chain.tail_to_head().any(visit),
}
}