1use std::{
2 cell::{Cell, RefCell},
3 hash::{Hash, Hasher},
4 rc::Rc,
5};
6
7use cranpose_core::NodeId;
8use cranpose_foundation::{
9 DelegatableNode, FocusNode, FocusState, ModifierNode, ModifierNodeContext, ModifierNodeElement,
10 NodeCapabilities, NodeState, impl_focus_node,
11};
12
13use crate::{focus_dispatch, render_state::AppContextId};
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
17pub enum FocusDirection {
18 Enter,
20 Exit,
22 Next,
24 Previous,
26 Up,
28 Down,
30 Left,
32 Right,
34}
35
36type FocusChangedCallback = Rc<dyn Fn(FocusState)>;
37
38struct FocusTargetShared {
39 focus_state: Cell<FocusState>,
40 on_focus_changed: RefCell<Option<FocusChangedCallback>>,
41}
42
43impl FocusTargetShared {
44 fn new(on_focus_changed: Option<FocusChangedCallback>) -> Self {
45 Self {
46 focus_state: Cell::new(FocusState::Inactive),
47 on_focus_changed: RefCell::new(on_focus_changed),
48 }
49 }
50
51 fn set_focus_state(&self, state: FocusState) {
52 let old_state = self.focus_state.get();
53 if old_state != state {
54 self.focus_state.set(state);
55 let callback = self.on_focus_changed.borrow().clone();
56 if let Some(callback) = callback {
57 callback(state);
58 }
59 }
60 }
61}
62
63impl focus_dispatch::FocusTargetHandle for FocusTargetShared {
64 fn set_focus_state(&self, state: FocusState) {
65 FocusTargetShared::set_focus_state(self, state);
66 }
67}
68
69pub struct FocusTargetNode {
70 state: NodeState,
71 shared: Rc<FocusTargetShared>,
72 handle: Rc<dyn focus_dispatch::FocusTargetHandle>,
73 registered_node_id: Cell<Option<NodeId>>,
74}
75
76impl FocusTargetNode {
77 pub fn new() -> Self {
78 Self::from_callback(None)
79 }
80
81 pub fn with_callback<F>(callback: F) -> Self
82 where
83 F: Fn(FocusState) + 'static,
84 {
85 Self::from_callback(Some(Rc::new(callback) as FocusChangedCallback))
86 }
87
88 fn from_callback(on_focus_changed: Option<FocusChangedCallback>) -> Self {
89 let shared = Rc::new(FocusTargetShared::new(on_focus_changed));
90 let handle: Rc<dyn focus_dispatch::FocusTargetHandle> = shared.clone();
91 Self {
92 state: NodeState::new(),
93 shared,
94 handle,
95 registered_node_id: Cell::new(None),
96 }
97 }
98
99 pub fn set_focus_state(&self, state: FocusState) {
100 self.shared.set_focus_state(state);
101 }
102
103 pub fn clear_focus(&self) {
104 self.set_focus_state(FocusState::Inactive);
105 }
106
107 fn set_callback(&self, callback: Option<FocusChangedCallback>) {
108 *self.shared.on_focus_changed.borrow_mut() = callback;
109 }
110}
111
112impl Default for FocusTargetNode {
113 fn default() -> Self {
114 Self::new()
115 }
116}
117
118impl DelegatableNode for FocusTargetNode {
119 fn node_state(&self) -> &NodeState {
120 &self.state
121 }
122}
123
124impl ModifierNode for FocusTargetNode {
125 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
126 self.state.set_attached(true);
127 if let Some(node_id) = context.node_id() {
128 self.registered_node_id.set(Some(node_id));
129 focus_dispatch::register_focus_target(node_id, Rc::clone(&self.handle));
130 }
131 }
132
133 fn on_detach(&mut self) {
134 self.state.set_attached(false);
135 if let Some(node_id) = self.registered_node_id.take() {
136 focus_dispatch::unregister_focus_target(node_id, &self.handle);
137 }
138 self.clear_focus();
139 }
140
141 impl_focus_node!();
142}
143
144impl FocusNode for FocusTargetNode {
145 fn focus_state(&self) -> FocusState {
146 self.shared.focus_state.get()
147 }
148
149 fn on_focus_changed(&mut self, _context: &mut dyn ModifierNodeContext, state: FocusState) {
150 self.set_focus_state(state);
151 }
152}
153
154#[derive(Clone)]
155pub struct FocusTargetElement {
156 on_focus_changed: Option<FocusChangedCallback>,
157}
158
159impl FocusTargetElement {
160 pub fn new() -> Self {
161 Self {
162 on_focus_changed: None,
163 }
164 }
165
166 pub fn with_callback<F>(callback: F) -> Self
167 where
168 F: Fn(FocusState) + 'static,
169 {
170 Self {
171 on_focus_changed: Some(Rc::new(callback)),
172 }
173 }
174}
175
176impl Default for FocusTargetElement {
177 fn default() -> Self {
178 Self::new()
179 }
180}
181
182impl std::fmt::Debug for FocusTargetElement {
183 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184 f.debug_struct("FocusTargetElement")
185 .field("has_callback", &self.on_focus_changed.is_some())
186 .finish()
187 }
188}
189
190impl PartialEq for FocusTargetElement {
191 fn eq(&self, other: &Self) -> bool {
192 self.on_focus_changed.is_some() == other.on_focus_changed.is_some()
193 }
194}
195
196impl Hash for FocusTargetElement {
197 fn hash<H: Hasher>(&self, state: &mut H) {
198 "focus_target".hash(state);
199 self.on_focus_changed.is_some().hash(state);
200 }
201}
202
203impl ModifierNodeElement for FocusTargetElement {
204 type Node = FocusTargetNode;
205
206 fn create(&self) -> Self::Node {
207 if let Some(callback) = &self.on_focus_changed {
208 FocusTargetNode::with_callback({
209 let callback = callback.clone();
210 move |state| callback(state)
211 })
212 } else {
213 FocusTargetNode::new()
214 }
215 }
216
217 fn update(&self, node: &mut Self::Node) {
218 node.set_callback(self.on_focus_changed.clone());
219 }
220
221 fn inspector_name(&self) -> &'static str {
222 "focusTarget"
223 }
224
225 fn capabilities(&self) -> NodeCapabilities {
226 NodeCapabilities::FOCUS
227 }
228
229 fn always_update(&self) -> bool {
230 true
231 }
232}
233
234#[derive(Clone, Copy, Debug, PartialEq, Eq)]
236pub enum FocusRequestError {
237 NotAttached,
241 NoFocusTarget,
246}
247
248impl std::fmt::Display for FocusRequestError {
249 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250 match self {
251 FocusRequestError::NotAttached => {
252 write!(f, "FocusRequester is not attached to a node in composition")
253 }
254 FocusRequestError::NoFocusTarget => write!(
255 f,
256 "FocusRequester's node has no focus target to move focus onto"
257 ),
258 }
259 }
260}
261
262impl std::error::Error for FocusRequestError {}
263
264#[derive(Clone, Copy)]
265struct FocusRequesterBinding {
266 app_context: AppContextId,
267 node_id: NodeId,
268}
269
270#[derive(Clone, Default)]
288pub struct FocusRequester {
289 binding: Rc<Cell<Option<FocusRequesterBinding>>>,
290}
291
292impl FocusRequester {
293 pub fn new() -> Self {
294 Self::default()
295 }
296
297 pub fn request_focus(&self) -> Result<(), FocusRequestError> {
302 let Some(binding) = self.binding.get() else {
303 return Err(FocusRequestError::NotAttached);
304 };
305 match focus_dispatch::request_focus_for(binding.app_context, binding.node_id) {
306 Some(true) => Ok(()),
307 Some(false) => Err(FocusRequestError::NoFocusTarget),
308 None => Err(FocusRequestError::NotAttached),
309 }
310 }
311
312 pub fn node_id(&self) -> Option<NodeId> {
314 self.binding.get().map(|binding| binding.node_id)
315 }
316
317 fn bind(&self, binding: Option<FocusRequesterBinding>) {
318 self.binding.set(binding);
319 }
320
321 fn binding_here(node_id: Option<NodeId>) -> Option<FocusRequesterBinding> {
322 Some(FocusRequesterBinding {
323 app_context: crate::render_state::current_app_context_id_opt()?,
324 node_id: node_id?,
325 })
326 }
327}
328
329impl std::fmt::Debug for FocusRequester {
330 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
331 f.debug_struct("FocusRequester")
332 .field("node_id", &self.node_id())
333 .finish()
334 }
335}
336
337pub struct FocusRequesterNode {
338 state: NodeState,
339 requester: FocusRequester,
340}
341
342impl FocusRequesterNode {
343 pub(crate) fn new(requester: FocusRequester) -> Self {
344 Self {
345 state: NodeState::new(),
346 requester,
347 }
348 }
349}
350
351impl DelegatableNode for FocusRequesterNode {
352 fn node_state(&self) -> &NodeState {
353 &self.state
354 }
355}
356
357impl ModifierNode for FocusRequesterNode {
358 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
359 self.state.set_attached(true);
360 self.requester
361 .bind(FocusRequester::binding_here(context.node_id()));
362 }
363
364 fn on_detach(&mut self) {
365 self.state.set_attached(false);
366 self.requester.bind(None);
367 }
368}
369
370#[derive(Clone)]
372pub struct FocusRequesterElement {
373 requester: FocusRequester,
374}
375
376impl FocusRequesterElement {
377 pub(crate) fn new(requester: FocusRequester) -> Self {
378 Self { requester }
379 }
380}
381
382impl std::fmt::Debug for FocusRequesterElement {
383 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
384 f.write_str("FocusRequesterElement")
385 }
386}
387
388impl PartialEq for FocusRequesterElement {
389 fn eq(&self, other: &Self) -> bool {
390 Rc::ptr_eq(&self.requester.binding, &other.requester.binding)
391 }
392}
393
394impl Eq for FocusRequesterElement {}
395
396impl Hash for FocusRequesterElement {
397 fn hash<H: Hasher>(&self, state: &mut H) {
398 Rc::as_ptr(&self.requester.binding).hash(state);
399 }
400}
401
402impl ModifierNodeElement for FocusRequesterElement {
403 type Node = FocusRequesterNode;
404
405 fn create(&self) -> Self::Node {
406 FocusRequesterNode::new(self.requester.clone())
407 }
408
409 fn update(&self, node: &mut Self::Node) {
410 if !Rc::ptr_eq(&node.requester.binding, &self.requester.binding) {
411 let bound = node.requester.binding.get();
412 node.requester.bind(None);
413 node.requester = self.requester.clone();
414 node.requester.bind(bound);
415 }
416 }
417
418 fn inspector_name(&self) -> &'static str {
419 "focusRequester"
420 }
421
422 fn capabilities(&self) -> NodeCapabilities {
423 NodeCapabilities::NONE
424 }
425}
426
427#[cfg(test)]
428#[path = "tests/focus_tests.rs"]
429mod tests;