1use std::{
2 fmt,
3 hash::{Hash, Hasher},
4 rc::Rc,
5};
6
7use cranpose_core::{Applier, MemoryApplier, NodeId};
8use cranpose_foundation::{
9 DelegatableNode, ModifierChainNodeRef, ModifierNode, ModifierNodeElement, NodeCapabilities,
10 NodeState,
11};
12use smallvec::SmallVec;
13
14use super::{Modifier, ModifierChainHandle};
15use crate::{
16 KeyEvent, SubcomposeLayoutNode, active_focus_target, widgets::nodes::layout_node::LayoutNode,
17};
18
19#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
20enum KeyPass {
21 Preview,
22 Bubble,
23}
24
25type KeyHandler = Rc<dyn Fn(&KeyEvent) -> bool>;
26
27impl Modifier {
28 pub fn on_preview_key_event(self, handler: impl Fn(&KeyEvent) -> bool + 'static) -> Self {
35 self.then(Self::with_element(KeyInputElement {
36 pass: KeyPass::Preview,
37 handler: Rc::new(handler),
38 }))
39 }
40
41 pub fn on_key_event(self, handler: impl Fn(&KeyEvent) -> bool + 'static) -> Self {
47 self.then(Self::with_element(KeyInputElement {
48 pass: KeyPass::Bubble,
49 handler: Rc::new(handler),
50 }))
51 }
52}
53
54#[derive(Clone)]
55struct KeyInputElement {
56 pass: KeyPass,
57 handler: KeyHandler,
58}
59
60impl fmt::Debug for KeyInputElement {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 f.debug_struct("KeyInputElement")
63 .field("pass", &self.pass)
64 .finish_non_exhaustive()
65 }
66}
67
68impl PartialEq for KeyInputElement {
69 fn eq(&self, other: &Self) -> bool {
70 self.pass == other.pass
71 }
72}
73
74impl Hash for KeyInputElement {
75 fn hash<H: Hasher>(&self, state: &mut H) {
76 self.pass.hash(state);
77 }
78}
79
80impl ModifierNodeElement for KeyInputElement {
81 type Node = KeyInputNode;
82
83 fn create(&self) -> Self::Node {
84 KeyInputNode {
85 state: NodeState::new(),
86 pass: self.pass,
87 handler: self.handler.clone(),
88 }
89 }
90
91 fn update(&self, node: &mut Self::Node) {
92 node.pass = self.pass;
93 node.handler = self.handler.clone();
94 }
95
96 fn always_update(&self) -> bool {
97 true
98 }
99
100 fn capabilities(&self) -> NodeCapabilities {
101 NodeCapabilities::KEY_INPUT
102 }
103}
104
105struct KeyInputNode {
106 state: NodeState,
107 pass: KeyPass,
108 handler: KeyHandler,
109}
110
111impl DelegatableNode for KeyInputNode {
112 fn node_state(&self) -> &NodeState {
113 &self.state
114 }
115}
116
117impl ModifierNode for KeyInputNode {}
118
119pub struct KeyEventRoute {
126 focused: Option<NodeId>,
127 handlers: SmallVec<[NodeId; 8]>,
128 in_scope: bool,
129}
130
131impl KeyEventRoute {
132 pub fn new(applier: &mut MemoryApplier, root: Option<NodeId>) -> Self {
134 let mut route = Self {
135 focused: active_focus_target(),
136 handlers: SmallVec::new(),
137 in_scope: false,
138 };
139 route.in_scope = visit_focus_ancestors(applier, route.focused, root, |id, chain| {
140 if chain.capabilities().contains(NodeCapabilities::KEY_INPUT) {
141 route.handlers.push(id);
142 }
143 });
144 if !route.in_scope {
145 route.handlers.clear();
146 }
147 route
148 }
149
150 pub fn contains_focus(applier: &mut MemoryApplier, root: Option<NodeId>) -> bool {
155 visit_focus_ancestors(applier, active_focus_target(), root, |_, _| {})
156 }
157
158 pub fn focus_is_current(&self) -> bool {
163 self.focused == active_focus_target() && (self.focused.is_none() || self.in_scope)
164 }
165
166 pub fn dispatch_preview(&self, applier: &mut MemoryApplier, event: &KeyEvent) -> bool {
168 self.handlers
169 .iter()
170 .rev()
171 .any(|&node| dispatch_node(applier, node, event, KeyPass::Preview))
172 }
173
174 pub fn dispatch_bubble(&self, applier: &mut MemoryApplier, event: &KeyEvent) -> bool {
176 self.handlers
177 .iter()
178 .any(|&node| dispatch_node(applier, node, event, KeyPass::Bubble))
179 }
180}
181
182fn visit_focus_ancestors(
186 applier: &mut MemoryApplier,
187 focused: Option<NodeId>,
188 root: Option<NodeId>,
189 mut visit: impl FnMut(NodeId, &ModifierChainHandle),
190) -> bool {
191 let (Some(mut current), Some(root)) = (focused, root) else {
192 return false;
193 };
194 for _ in 0..100_000 {
195 let Ok(node) = applier.get_mut(current) else {
196 return false;
197 };
198 let parent = node.parent();
199 let node = node.as_any_mut();
200 let window_root = if let Some(layout) = node.downcast_mut::<LayoutNode>() {
201 visit(current, layout.modifier_chain());
202 layout.is_window_root()
203 } else {
204 if let Some(subcompose) = node.downcast_mut::<SubcomposeLayoutNode>() {
205 subcompose.with_modifier_chain(|chain| visit(current, chain));
206 }
207 false
208 };
209 if current == root {
210 return true;
211 }
212 if window_root {
213 return false;
214 }
215 let Some(parent) = parent else {
216 return false;
217 };
218 current = parent;
219 }
220 false
221}
222
223fn dispatch_node(
224 applier: &mut MemoryApplier,
225 node: NodeId,
226 event: &KeyEvent,
227 pass: KeyPass,
228) -> bool {
229 if let Ok(handled) = applier.with_node::<LayoutNode, _>(node, |node| {
230 dispatch_chain(node.modifier_chain(), event, pass)
231 }) {
232 return handled;
233 }
234 applier
235 .with_node::<SubcomposeLayoutNode, _>(node, |node| {
236 node.with_modifier_chain(|chain| dispatch_chain(chain, event, pass))
237 })
238 .unwrap_or(false)
239}
240
241fn dispatch_chain(chain: &ModifierChainHandle, event: &KeyEvent, pass: KeyPass) -> bool {
244 let chain = chain.chain();
245 let visit = |entry: ModifierChainNodeRef<'_>| {
246 entry.kind_set().contains(NodeCapabilities::KEY_INPUT)
247 && entry
248 .with_node(|node| {
249 let node: &dyn std::any::Any = node;
250 node.downcast_ref::<KeyInputNode>()
251 .is_some_and(|node| node.pass == pass && (node.handler)(event))
252 })
253 .unwrap_or(false)
254 };
255 match pass {
256 KeyPass::Preview => chain.head_to_tail().any(visit),
257 KeyPass::Bubble => chain.tail_to_head().any(visit),
258 }
259}