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 super::Modifier;
14use crate::{
15 focus_dispatch,
16 focus_within::{FocusWithinChain, current_focus_within},
17 render_state::AppContextId,
18};
19
20#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
22pub enum FocusDirection {
23 Enter,
25 Exit,
27 Next,
29 Previous,
31 Up,
33 Down,
35 Left,
37 Right,
39}
40
41type FocusChangedCallback = Rc<dyn Fn(FocusState)>;
42
43struct FocusTargetShared {
44 focus_state: Cell<FocusState>,
45 on_focus_changed: RefCell<Option<FocusChangedCallback>>,
46 observers: RefCell<Option<FocusWithinChain>>,
48}
49
50impl FocusTargetShared {
51 fn new(on_focus_changed: Option<FocusChangedCallback>) -> Self {
52 Self {
53 focus_state: Cell::new(FocusState::Inactive),
54 on_focus_changed: RefCell::new(on_focus_changed),
55 observers: RefCell::new(None),
56 }
57 }
58
59 fn set_focus_state(&self, state: FocusState) {
60 let old_state = self.focus_state.get();
61 if old_state != state {
62 self.focus_state.set(state);
63 if old_state.is_focused() != state.is_focused()
64 && let Some(observers) = self.observers.borrow().clone()
65 {
66 if state.is_focused() {
67 observers.enter();
68 } else {
69 observers.leave();
70 }
71 }
72 let callback = self.on_focus_changed.borrow().clone();
73 if let Some(callback) = callback {
74 callback(state);
75 }
76 }
77 }
78
79 fn set_observers(&self, observers: Option<FocusWithinChain>) {
81 let previous = self.observers.replace(observers.clone());
82 if previous == observers || !self.focus_state.get().is_focused() {
83 return;
84 }
85 if let Some(previous) = previous {
86 previous.leave();
87 }
88 if let Some(observers) = observers {
89 observers.enter();
90 }
91 }
92}
93
94impl focus_dispatch::FocusTargetHandle for FocusTargetShared {
95 fn set_focus_state(&self, state: FocusState) {
96 Self::set_focus_state(self, state);
97 }
98}
99
100pub(super) struct FocusTargetNode {
101 state: NodeState,
102 shared: Rc<FocusTargetShared>,
103 handle: Rc<dyn focus_dispatch::FocusTargetHandle>,
104 registered_node_id: Cell<Option<NodeId>>,
105}
106
107impl FocusTargetNode {
108 pub(super) fn new() -> Self {
109 Self::from_callback(None)
110 }
111
112 fn from_callback(on_focus_changed: Option<FocusChangedCallback>) -> Self {
113 let shared = Rc::new(FocusTargetShared::new(on_focus_changed));
114 let handle: Rc<dyn focus_dispatch::FocusTargetHandle> =
115 Rc::<FocusTargetShared>::clone(&shared);
116 Self {
117 state: NodeState::new(),
118 shared,
119 handle,
120 registered_node_id: Cell::new(None),
121 }
122 }
123
124 pub(super) fn set_focus_state(&self, state: FocusState) {
125 self.shared.set_focus_state(state);
126 }
127
128 pub(super) fn clear_focus(&self) {
129 self.set_focus_state(FocusState::Inactive);
130 }
131
132 fn set_callback(&self, callback: Option<FocusChangedCallback>) {
133 *self.shared.on_focus_changed.borrow_mut() = callback;
134 }
135}
136
137impl Default for FocusTargetNode {
138 fn default() -> Self {
139 Self::new()
140 }
141}
142
143impl DelegatableNode for FocusTargetNode {
144 fn node_state(&self) -> &NodeState {
145 &self.state
146 }
147}
148
149impl ModifierNode for FocusTargetNode {
150 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
151 if let Some(node_id) = context.node_id() {
152 self.registered_node_id.set(Some(node_id));
153 focus_dispatch::register_focus_target(node_id, Rc::clone(&self.handle));
154 }
155 }
156
157 fn on_detach(&mut self) {
158 if let Some(node_id) = self.registered_node_id.take() {
159 focus_dispatch::unregister_focus_target(node_id, &self.handle);
160 }
161 self.clear_focus();
162 }
163
164 impl_focus_node!();
165}
166
167impl FocusNode for FocusTargetNode {
168 fn focus_state(&self) -> FocusState {
169 self.shared.focus_state.get()
170 }
171
172 fn on_focus_changed(&mut self, _context: &mut dyn ModifierNodeContext, state: FocusState) {
173 self.set_focus_state(state);
174 }
175}
176
177#[derive(Clone)]
178pub(super) struct FocusTargetElement {
179 on_focus_changed: Option<FocusChangedCallback>,
180 observers: Option<FocusWithinChain>,
181}
182
183impl FocusTargetElement {
184 pub(super) fn new() -> Self {
185 Self {
186 on_focus_changed: None,
187 observers: current_focus_within(),
188 }
189 }
190
191 pub(super) fn with_callback<F>(callback: F) -> Self
192 where
193 F: Fn(FocusState) + 'static,
194 {
195 Self {
196 on_focus_changed: Some(Rc::new(callback)),
197 observers: current_focus_within(),
198 }
199 }
200}
201
202impl Default for FocusTargetElement {
203 fn default() -> Self {
204 Self::new()
205 }
206}
207
208impl std::fmt::Debug for FocusTargetElement {
209 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210 f.debug_struct("FocusTargetElement")
211 .field("has_callback", &self.on_focus_changed.is_some())
212 .finish()
213 }
214}
215
216impl PartialEq for FocusTargetElement {
217 fn eq(&self, other: &Self) -> bool {
218 self.on_focus_changed.is_some() == other.on_focus_changed.is_some()
219 && self.observers == other.observers
220 }
221}
222
223impl Hash for FocusTargetElement {
224 fn hash<H: Hasher>(&self, state: &mut H) {
225 "focus_target".hash(state);
226 self.on_focus_changed.is_some().hash(state);
227 }
228}
229
230impl ModifierNodeElement for FocusTargetElement {
231 type Node = FocusTargetNode;
232
233 fn create(&self) -> Self::Node {
234 let node = FocusTargetNode::from_callback(self.on_focus_changed.clone());
235 node.shared.set_observers(self.observers.clone());
236 node
237 }
238
239 fn update(&self, node: &mut Self::Node) {
240 node.set_callback(self.on_focus_changed.clone());
241 node.shared.set_observers(self.observers.clone());
242 }
243
244 fn inspector_name(&self) -> &'static str {
245 "focusTarget"
246 }
247
248 fn capabilities(&self) -> NodeCapabilities {
249 NodeCapabilities::FOCUS
250 }
251
252 fn always_update(&self) -> bool {
253 true
254 }
255}
256
257#[derive(Clone, Copy, Debug, PartialEq, Eq)]
259pub enum FocusRequestError {
260 NotAttached,
264 NoFocusTarget,
269}
270
271impl std::fmt::Display for FocusRequestError {
272 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
273 match self {
274 Self::NotAttached => {
275 write!(f, "FocusRequester is not attached to a node in composition")
276 }
277 Self::NoFocusTarget => write!(
278 f,
279 "FocusRequester's node has no focus target to move focus onto"
280 ),
281 }
282 }
283}
284
285impl std::error::Error for FocusRequestError {}
286
287#[derive(Clone, Copy)]
288struct FocusRequesterBinding {
289 app_context: AppContextId,
290 node_id: NodeId,
291}
292
293#[derive(Clone, Default)]
311pub struct FocusRequester {
312 binding: Rc<Cell<Option<FocusRequesterBinding>>>,
313}
314
315impl FocusRequester {
316 pub fn new() -> Self {
319 Self::default()
320 }
321
322 pub fn request_focus(&self) -> Result<(), FocusRequestError> {
327 let Some(binding) = self.binding.get() else {
328 return Err(FocusRequestError::NotAttached);
329 };
330 match focus_dispatch::request_focus_for(binding.app_context, binding.node_id) {
331 Some(true) => Ok(()),
332 Some(false) => Err(FocusRequestError::NoFocusTarget),
333 None => Err(FocusRequestError::NotAttached),
334 }
335 }
336
337 pub fn node_id(&self) -> Option<NodeId> {
339 self.binding.get().map(|binding| binding.node_id)
340 }
341
342 fn bind(&self, binding: Option<FocusRequesterBinding>) {
343 self.binding.set(binding);
344 }
345
346 fn binding_here(node_id: Option<NodeId>) -> Option<FocusRequesterBinding> {
347 Some(FocusRequesterBinding {
348 app_context: crate::render_state::current_app_context_id_opt()?,
349 node_id: node_id?,
350 })
351 }
352}
353
354impl std::fmt::Debug for FocusRequester {
355 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
356 f.debug_struct("FocusRequester")
357 .field("node_id", &self.node_id())
358 .finish()
359 }
360}
361
362pub(crate) struct FocusRequesterNode {
363 state: NodeState,
364 requester: FocusRequester,
365}
366
367impl FocusRequesterNode {
368 pub(crate) fn new(requester: FocusRequester) -> Self {
369 Self {
370 state: NodeState::new(),
371 requester,
372 }
373 }
374}
375
376impl DelegatableNode for FocusRequesterNode {
377 fn node_state(&self) -> &NodeState {
378 &self.state
379 }
380}
381
382impl ModifierNode for FocusRequesterNode {
383 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
384 self.requester
385 .bind(FocusRequester::binding_here(context.node_id()));
386 }
387
388 fn on_detach(&mut self) {
389 self.requester.bind(None);
390 }
391}
392
393#[derive(Clone)]
394pub(crate) struct FocusRequesterElement {
395 requester: FocusRequester,
396}
397
398impl FocusRequesterElement {
399 pub(crate) fn new(requester: FocusRequester) -> Self {
400 Self { requester }
401 }
402}
403
404impl std::fmt::Debug for FocusRequesterElement {
405 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
406 f.write_str("FocusRequesterElement")
407 }
408}
409
410impl PartialEq for FocusRequesterElement {
411 fn eq(&self, other: &Self) -> bool {
412 Rc::ptr_eq(&self.requester.binding, &other.requester.binding)
413 }
414}
415
416impl Eq for FocusRequesterElement {}
417
418impl Hash for FocusRequesterElement {
419 fn hash<H: Hasher>(&self, state: &mut H) {
420 Rc::as_ptr(&self.requester.binding).hash(state);
421 }
422}
423
424impl ModifierNodeElement for FocusRequesterElement {
425 type Node = FocusRequesterNode;
426
427 fn create(&self) -> Self::Node {
428 FocusRequesterNode::new(self.requester.clone())
429 }
430
431 fn update(&self, node: &mut Self::Node) {
432 if !Rc::ptr_eq(&node.requester.binding, &self.requester.binding) {
433 let bound = node.requester.binding.get();
434 node.requester.bind(None);
435 node.requester = self.requester.clone();
436 node.requester.bind(bound);
437 }
438 }
439
440 fn inspector_name(&self) -> &'static str {
441 "focusRequester"
442 }
443
444 fn capabilities(&self) -> NodeCapabilities {
445 NodeCapabilities::NONE
446 }
447}
448
449impl Modifier {
450 pub fn focus_target(self) -> Self {
456 let element = FocusTargetElement::new();
457 let modifier = Self::from_dyn_element(super::modifier_element(element));
458 self.then(modifier)
459 }
460
461 pub fn on_focus_changed<F>(self, callback: F) -> Self
466 where
467 F: Fn(FocusState) + 'static,
468 {
469 let element = FocusTargetElement::with_callback(callback);
470 let modifier = Self::from_dyn_element(super::modifier_element(element));
471 self.then(modifier)
472 }
473
474 pub fn focus_requester(self, requester: &FocusRequester) -> Self {
481 let element = FocusRequesterElement::new(requester.clone());
482 let modifier = Self::from_dyn_element(super::modifier_element(element));
483 self.then(modifier)
484 }
485}
486
487pub(crate) struct FocusRestorer {
489 fallback: RefCell<Option<FocusRequester>>,
490 saved: Cell<Option<NodeId>>,
491}
492
493impl FocusRestorer {
494 pub(crate) fn save(&self, child: NodeId) {
495 self.saved.set(Some(child));
496 }
497
498 pub(crate) fn restored(&self) -> impl Iterator<Item = NodeId> {
501 let fallback = self
502 .fallback
503 .borrow()
504 .as_ref()
505 .and_then(FocusRequester::node_id);
506 self.saved.get().into_iter().chain(fallback)
507 }
508}
509
510pub(crate) struct FocusGroupNode {
511 state: NodeState,
512 restorer: Option<Rc<FocusRestorer>>,
513 registered_node_id: Option<NodeId>,
514}
515
516impl DelegatableNode for FocusGroupNode {
517 fn node_state(&self) -> &NodeState {
518 &self.state
519 }
520}
521
522impl ModifierNode for FocusGroupNode {
523 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
524 if let Some(node_id) = context.node_id() {
525 self.registered_node_id = Some(node_id);
526 focus_dispatch::register_focus_group(node_id, self.restorer.clone());
527 }
528 }
529
530 fn on_detach(&mut self) {
531 if let Some(node_id) = self.registered_node_id.take() {
532 focus_dispatch::unregister_focus_group(node_id);
533 }
534 }
535}
536
537#[derive(Clone)]
538enum GroupKind {
539 Plain,
540 Restorer(Option<FocusRequester>),
543}
544
545#[derive(Clone)]
546pub(super) struct FocusGroupElement {
547 kind: GroupKind,
548}
549
550impl FocusGroupElement {
551 fn restores(&self) -> bool {
552 matches!(self.kind, GroupKind::Restorer(_))
553 }
554}
555
556impl std::fmt::Debug for FocusGroupElement {
557 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
558 f.debug_struct("FocusGroupElement")
559 .field("restores", &self.restores())
560 .finish()
561 }
562}
563
564impl PartialEq for FocusGroupElement {
565 fn eq(&self, other: &Self) -> bool {
566 self.restores() == other.restores()
567 }
568}
569
570impl Hash for FocusGroupElement {
571 fn hash<H: Hasher>(&self, state: &mut H) {
572 "focus_group".hash(state);
573 self.restores().hash(state);
574 }
575}
576
577impl ModifierNodeElement for FocusGroupElement {
578 type Node = FocusGroupNode;
579
580 fn create(&self) -> Self::Node {
581 FocusGroupNode {
582 state: NodeState::new(),
583 restorer: match &self.kind {
584 GroupKind::Plain => None,
585 GroupKind::Restorer(fallback) => Some(Rc::new(FocusRestorer {
586 fallback: RefCell::new(fallback.clone()),
587 saved: Cell::new(None),
588 })),
589 },
590 registered_node_id: None,
591 }
592 }
593
594 fn update(&self, node: &mut Self::Node) {
595 if let (Some(restorer), GroupKind::Restorer(fallback)) = (&node.restorer, &self.kind) {
596 restorer.fallback.replace(fallback.clone());
597 }
598 }
599
600 fn inspector_name(&self) -> &'static str {
601 if self.restores() {
602 "focusRestorer"
603 } else {
604 "focusGroup"
605 }
606 }
607
608 fn capabilities(&self) -> NodeCapabilities {
609 NodeCapabilities::NONE
610 }
611
612 fn always_update(&self) -> bool {
613 true
614 }
615}
616
617impl Modifier {
618 pub fn focus_group(self) -> Self {
623 self.then(Self::from_dyn_element(super::modifier_element(
624 FocusGroupElement {
625 kind: GroupKind::Plain,
626 },
627 )))
628 }
629
630 pub fn focus_restorer(self, fallback: Option<FocusRequester>) -> Self {
639 self.then(Self::from_dyn_element(super::modifier_element(
640 FocusGroupElement {
641 kind: GroupKind::Restorer(fallback),
642 },
643 )))
644 }
645}
646
647#[cfg(test)]
648#[path = "tests/focus_tests.rs"]
649mod tests;