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