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 self.state.set_attached(true);
152 if let Some(node_id) = context.node_id() {
153 self.registered_node_id.set(Some(node_id));
154 focus_dispatch::register_focus_target(node_id, Rc::clone(&self.handle));
155 }
156 }
157
158 fn on_detach(&mut self) {
159 self.state.set_attached(false);
160 if let Some(node_id) = self.registered_node_id.take() {
161 focus_dispatch::unregister_focus_target(node_id, &self.handle);
162 }
163 self.clear_focus();
164 }
165
166 impl_focus_node!();
167}
168
169impl FocusNode for FocusTargetNode {
170 fn focus_state(&self) -> FocusState {
171 self.shared.focus_state.get()
172 }
173
174 fn on_focus_changed(&mut self, _context: &mut dyn ModifierNodeContext, state: FocusState) {
175 self.set_focus_state(state);
176 }
177}
178
179#[derive(Clone)]
180pub(super) struct FocusTargetElement {
181 on_focus_changed: Option<FocusChangedCallback>,
182 observers: Option<FocusWithinChain>,
183}
184
185impl FocusTargetElement {
186 pub(super) fn new() -> Self {
187 Self {
188 on_focus_changed: None,
189 observers: current_focus_within(),
190 }
191 }
192
193 pub(super) fn with_callback<F>(callback: F) -> Self
194 where
195 F: Fn(FocusState) + 'static,
196 {
197 Self {
198 on_focus_changed: Some(Rc::new(callback)),
199 observers: current_focus_within(),
200 }
201 }
202}
203
204impl Default for FocusTargetElement {
205 fn default() -> Self {
206 Self::new()
207 }
208}
209
210impl std::fmt::Debug for FocusTargetElement {
211 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
212 f.debug_struct("FocusTargetElement")
213 .field("has_callback", &self.on_focus_changed.is_some())
214 .finish()
215 }
216}
217
218impl PartialEq for FocusTargetElement {
219 fn eq(&self, other: &Self) -> bool {
220 self.on_focus_changed.is_some() == other.on_focus_changed.is_some()
221 && self.observers == other.observers
222 }
223}
224
225impl Hash for FocusTargetElement {
226 fn hash<H: Hasher>(&self, state: &mut H) {
227 "focus_target".hash(state);
228 self.on_focus_changed.is_some().hash(state);
229 }
230}
231
232impl ModifierNodeElement for FocusTargetElement {
233 type Node = FocusTargetNode;
234
235 fn create(&self) -> Self::Node {
236 let node = FocusTargetNode::from_callback(self.on_focus_changed.clone());
237 node.shared.set_observers(self.observers.clone());
238 node
239 }
240
241 fn update(&self, node: &mut Self::Node) {
242 node.set_callback(self.on_focus_changed.clone());
243 node.shared.set_observers(self.observers.clone());
244 }
245
246 fn inspector_name(&self) -> &'static str {
247 "focusTarget"
248 }
249
250 fn capabilities(&self) -> NodeCapabilities {
251 NodeCapabilities::FOCUS
252 }
253
254 fn always_update(&self) -> bool {
255 true
256 }
257}
258
259#[derive(Clone, Copy, Debug, PartialEq, Eq)]
261pub enum FocusRequestError {
262 NotAttached,
266 NoFocusTarget,
271}
272
273impl std::fmt::Display for FocusRequestError {
274 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
275 match self {
276 Self::NotAttached => {
277 write!(f, "FocusRequester is not attached to a node in composition")
278 }
279 Self::NoFocusTarget => write!(
280 f,
281 "FocusRequester's node has no focus target to move focus onto"
282 ),
283 }
284 }
285}
286
287impl std::error::Error for FocusRequestError {}
288
289#[derive(Clone, Copy)]
290struct FocusRequesterBinding {
291 app_context: AppContextId,
292 node_id: NodeId,
293}
294
295#[derive(Clone, Default)]
313pub struct FocusRequester {
314 binding: Rc<Cell<Option<FocusRequesterBinding>>>,
315}
316
317impl FocusRequester {
318 pub fn new() -> Self {
321 Self::default()
322 }
323
324 pub fn request_focus(&self) -> Result<(), FocusRequestError> {
329 let Some(binding) = self.binding.get() else {
330 return Err(FocusRequestError::NotAttached);
331 };
332 match focus_dispatch::request_focus_for(binding.app_context, binding.node_id) {
333 Some(true) => Ok(()),
334 Some(false) => Err(FocusRequestError::NoFocusTarget),
335 None => Err(FocusRequestError::NotAttached),
336 }
337 }
338
339 pub fn node_id(&self) -> Option<NodeId> {
341 self.binding.get().map(|binding| binding.node_id)
342 }
343
344 fn bind(&self, binding: Option<FocusRequesterBinding>) {
345 self.binding.set(binding);
346 }
347
348 fn binding_here(node_id: Option<NodeId>) -> Option<FocusRequesterBinding> {
349 Some(FocusRequesterBinding {
350 app_context: crate::render_state::current_app_context_id_opt()?,
351 node_id: node_id?,
352 })
353 }
354}
355
356impl std::fmt::Debug for FocusRequester {
357 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
358 f.debug_struct("FocusRequester")
359 .field("node_id", &self.node_id())
360 .finish()
361 }
362}
363
364pub(crate) struct FocusRequesterNode {
365 state: NodeState,
366 requester: FocusRequester,
367}
368
369impl FocusRequesterNode {
370 pub(crate) fn new(requester: FocusRequester) -> Self {
371 Self {
372 state: NodeState::new(),
373 requester,
374 }
375 }
376}
377
378impl DelegatableNode for FocusRequesterNode {
379 fn node_state(&self) -> &NodeState {
380 &self.state
381 }
382}
383
384impl ModifierNode for FocusRequesterNode {
385 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
386 self.state.set_attached(true);
387 self.requester
388 .bind(FocusRequester::binding_here(context.node_id()));
389 }
390
391 fn on_detach(&mut self) {
392 self.state.set_attached(false);
393 self.requester.bind(None);
394 }
395}
396
397#[derive(Clone)]
398pub(crate) struct FocusRequesterElement {
399 requester: FocusRequester,
400}
401
402impl FocusRequesterElement {
403 pub(crate) fn new(requester: FocusRequester) -> Self {
404 Self { requester }
405 }
406}
407
408impl std::fmt::Debug for FocusRequesterElement {
409 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
410 f.write_str("FocusRequesterElement")
411 }
412}
413
414impl PartialEq for FocusRequesterElement {
415 fn eq(&self, other: &Self) -> bool {
416 Rc::ptr_eq(&self.requester.binding, &other.requester.binding)
417 }
418}
419
420impl Eq for FocusRequesterElement {}
421
422impl Hash for FocusRequesterElement {
423 fn hash<H: Hasher>(&self, state: &mut H) {
424 Rc::as_ptr(&self.requester.binding).hash(state);
425 }
426}
427
428impl ModifierNodeElement for FocusRequesterElement {
429 type Node = FocusRequesterNode;
430
431 fn create(&self) -> Self::Node {
432 FocusRequesterNode::new(self.requester.clone())
433 }
434
435 fn update(&self, node: &mut Self::Node) {
436 if !Rc::ptr_eq(&node.requester.binding, &self.requester.binding) {
437 let bound = node.requester.binding.get();
438 node.requester.bind(None);
439 node.requester = self.requester.clone();
440 node.requester.bind(bound);
441 }
442 }
443
444 fn inspector_name(&self) -> &'static str {
445 "focusRequester"
446 }
447
448 fn capabilities(&self) -> NodeCapabilities {
449 NodeCapabilities::NONE
450 }
451}
452
453impl Modifier {
454 pub fn focus_target(self) -> Self {
460 let element = FocusTargetElement::new();
461 let modifier = Self::from_dyn_element(super::modifier_element(element));
462 self.then(modifier)
463 }
464
465 pub fn on_focus_changed<F>(self, callback: F) -> Self
470 where
471 F: Fn(FocusState) + 'static,
472 {
473 let element = FocusTargetElement::with_callback(callback);
474 let modifier = Self::from_dyn_element(super::modifier_element(element));
475 self.then(modifier)
476 }
477
478 pub fn focus_requester(self, requester: &FocusRequester) -> Self {
485 let element = FocusRequesterElement::new(requester.clone());
486 let modifier = Self::from_dyn_element(super::modifier_element(element));
487 self.then(modifier)
488 }
489}
490
491pub(crate) struct FocusRestorer {
493 fallback: RefCell<Option<FocusRequester>>,
494 saved: Cell<Option<NodeId>>,
495}
496
497impl FocusRestorer {
498 pub(crate) fn save(&self, child: NodeId) {
499 self.saved.set(Some(child));
500 }
501
502 pub(crate) fn restored(&self) -> impl Iterator<Item = NodeId> {
505 let fallback = self
506 .fallback
507 .borrow()
508 .as_ref()
509 .and_then(FocusRequester::node_id);
510 self.saved.get().into_iter().chain(fallback)
511 }
512}
513
514pub(crate) struct FocusGroupNode {
515 state: NodeState,
516 restorer: Option<Rc<FocusRestorer>>,
517 registered_node_id: Option<NodeId>,
518}
519
520impl DelegatableNode for FocusGroupNode {
521 fn node_state(&self) -> &NodeState {
522 &self.state
523 }
524}
525
526impl ModifierNode for FocusGroupNode {
527 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
528 self.state.set_attached(true);
529 if let Some(node_id) = context.node_id() {
530 self.registered_node_id = Some(node_id);
531 focus_dispatch::register_focus_group(node_id, self.restorer.clone());
532 }
533 }
534
535 fn on_detach(&mut self) {
536 self.state.set_attached(false);
537 if let Some(node_id) = self.registered_node_id.take() {
538 focus_dispatch::unregister_focus_group(node_id);
539 }
540 }
541}
542
543#[derive(Clone)]
544enum GroupKind {
545 Plain,
546 Restorer(Option<FocusRequester>),
549}
550
551#[derive(Clone)]
552pub(super) struct FocusGroupElement {
553 kind: GroupKind,
554}
555
556impl FocusGroupElement {
557 fn restores(&self) -> bool {
558 matches!(self.kind, GroupKind::Restorer(_))
559 }
560}
561
562impl std::fmt::Debug for FocusGroupElement {
563 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
564 f.debug_struct("FocusGroupElement")
565 .field("restores", &self.restores())
566 .finish()
567 }
568}
569
570impl PartialEq for FocusGroupElement {
571 fn eq(&self, other: &Self) -> bool {
572 self.restores() == other.restores()
573 }
574}
575
576impl Hash for FocusGroupElement {
577 fn hash<H: Hasher>(&self, state: &mut H) {
578 "focus_group".hash(state);
579 self.restores().hash(state);
580 }
581}
582
583impl ModifierNodeElement for FocusGroupElement {
584 type Node = FocusGroupNode;
585
586 fn create(&self) -> Self::Node {
587 FocusGroupNode {
588 state: NodeState::new(),
589 restorer: match &self.kind {
590 GroupKind::Plain => None,
591 GroupKind::Restorer(fallback) => Some(Rc::new(FocusRestorer {
592 fallback: RefCell::new(fallback.clone()),
593 saved: Cell::new(None),
594 })),
595 },
596 registered_node_id: None,
597 }
598 }
599
600 fn update(&self, node: &mut Self::Node) {
601 if let (Some(restorer), GroupKind::Restorer(fallback)) = (&node.restorer, &self.kind) {
602 restorer.fallback.replace(fallback.clone());
603 }
604 }
605
606 fn inspector_name(&self) -> &'static str {
607 if self.restores() {
608 "focusRestorer"
609 } else {
610 "focusGroup"
611 }
612 }
613
614 fn capabilities(&self) -> NodeCapabilities {
615 NodeCapabilities::NONE
616 }
617
618 fn always_update(&self) -> bool {
619 true
620 }
621}
622
623impl Modifier {
624 pub fn focus_group(self) -> Self {
629 self.then(Self::from_dyn_element(super::modifier_element(
630 FocusGroupElement {
631 kind: GroupKind::Plain,
632 },
633 )))
634 }
635
636 pub fn focus_restorer(self, fallback: Option<FocusRequester>) -> Self {
645 self.then(Self::from_dyn_element(super::modifier_element(
646 FocusGroupElement {
647 kind: GroupKind::Restorer(fallback),
648 },
649 )))
650 }
651}
652
653#[cfg(test)]
654#[path = "tests/focus_tests.rs"]
655mod tests;