1use std::any::Any;
2use std::cell::RefCell;
3use std::collections::HashSet;
4use std::panic::Location;
5use std::rc::Rc;
6use std::sync::atomic::{AtomicU64, Ordering};
7
8use rustc_hash::{FxHashMap, FxHashSet};
9
10use crate::effects::Dispose;
11use crate::scope::Scope;
12use crate::{CursorIcon, Rect, Scene, View, input::PhysicalKey, semantics::Role};
13
14thread_local! {
15 pub static COMPOSER: RefCell<Composer> = RefCell::new(Composer::default());
16 static ROOT_SCOPE: RefCell<Option<Scope>> = const { RefCell::new(None) };
17 static INITIALIZER_STACK: RefCell<Vec<InitializerKind>> =
18 const { RefCell::new(Vec::new()) };
19 static PENDING_INITIALIZER_CURSOR: RefCell<Option<usize>> =
20 const { RefCell::new(None) };
21 static KEYED_DISPOSERS: RefCell<FxHashMap<String, KeyedDisposerEntry>> =
22 RefCell::new(FxHashMap::default());
23 static KEYED_DISPOSER_ORDER: RefCell<Vec<String>> = const { RefCell::new(Vec::new()) };
24 static PENDING_KEYED_REGISTRATIONS: RefCell<Vec<(String, Dispose, String)>> =
25 const { RefCell::new(Vec::new()) };
26
27 static FOCUS_REQUESTS: RefCell<std::collections::VecDeque<u64>> =
31 const { RefCell::new(std::collections::VecDeque::new()) };
32}
33
34pub const CLEAR_FOCUS_MARKER: u64 = u64::MAX;
35
36const ROOT_OWNER: &str = "\0repose:root-owner";
37
38struct KeyedDisposerEntry {
39 disposer: Dispose,
40 owners: FxHashSet<String>,
41}
42
43enum InitializerKind {
44 Sequential,
45 Keyed,
46}
47
48pub(crate) fn scope_owner_token(scope: Option<&str>) -> String {
49 match scope {
50 Some(scope) if !scope.is_empty() => scope.to_string(),
51 _ => ROOT_OWNER.to_string(),
52 }
53}
54
55fn queue_keyed_registration(key: String, disposer: Dispose, owner: String) {
56 let queued = PENDING_KEYED_REGISTRATIONS.try_with(|pending| {
57 pending.try_borrow_mut().ok().map(|mut pending| {
58 pending.push((key, disposer, owner));
59 true
60 })
61 });
62 if !matches!(queued, Ok(Some(true))) {
63 crate::request_frame();
64 }
65}
66
67pub(crate) fn flush_keyed_disposer_registrations() {
68 let pending = PENDING_KEYED_REGISTRATIONS.try_with(|pending| {
69 pending
70 .try_borrow_mut()
71 .ok()
72 .map(|mut pending| std::mem::take(&mut *pending))
73 });
74 let Ok(Some(pending)) = pending else {
75 crate::request_frame();
76 return;
77 };
78 for (key, disposer, owner) in pending {
79 register_keyed_disposer_for_owner(key, disposer, owner);
80 }
81}
82
83pub(crate) fn register_keyed_disposer(key: String, disposer: Dispose) {
84 let owner = scope_owner_token(crate::scope_cache::current_scope_key().as_deref());
85 register_keyed_disposer_for_owner(key, disposer, owner);
86}
87
88pub(crate) fn register_keyed_disposer_for_owner(key: String, disposer: Dispose, owner: String) {
89 let result = KEYED_DISPOSERS
90 .try_with(|registry| {
91 let mut registry = match registry.try_borrow_mut() {
92 Ok(registry) => registry,
93 Err(_) => return Err(()),
94 };
95 if let Some(entry) = registry.get_mut(&key) {
96 if entry.owners.contains(&owner) && !entry.disposer.ptr_eq(&disposer) {
97 let previous = std::mem::replace(&mut entry.disposer, disposer.clone());
98 return Ok((false, Some(previous)));
99 }
100 entry.owners.insert(owner.clone());
101 return Ok((false, None));
102 }
103 registry.insert(
104 key.clone(),
105 KeyedDisposerEntry {
106 disposer: disposer.clone(),
107 owners: FxHashSet::from_iter([owner.clone()]),
108 },
109 );
110 Ok((true, None))
111 })
112 .ok();
113 match result {
114 Some(Ok((true, previous))) => {
115 let _ = KEYED_DISPOSER_ORDER.try_with(|order| {
116 if let Ok(mut order) = order.try_borrow_mut()
117 && !order.iter().any(|registered| registered == &key)
118 {
119 order.push(key.clone());
120 }
121 });
122 drop(previous);
123 }
124 Some(Ok((false, previous))) => {
125 if let Some(previous) = previous {
126 run_keyed_disposer(previous);
127 }
128 }
129 Some(Err(())) | None => queue_keyed_registration(key, disposer, owner),
130 }
131}
132
133pub(crate) fn keyed_disposer_has_owner(key: &str, disposer: &Dispose, owner: &str) -> bool {
134 KEYED_DISPOSERS
135 .try_with(|registry| {
136 registry.try_borrow().ok().and_then(|registry| {
137 registry
138 .get(key)
139 .filter(|entry| entry.disposer.ptr_eq(disposer))
140 .map(|entry| entry.owners.contains(owner))
141 })
142 })
143 .ok()
144 .flatten()
145 .unwrap_or(false)
146}
147
148pub(crate) fn remove_keyed_disposer_for_owner(key: &str, disposer: &Dispose, owner: &str) -> bool {
149 let mut to_run = None;
150 let mut owner_removed = false;
151 let mut registry_removed = false;
152 let result = KEYED_DISPOSERS.try_with(|registry| {
153 let mut registry = match registry.try_borrow_mut() {
154 Ok(registry) => registry,
155 Err(_) => return false,
156 };
157 let Some(entry) = registry.get_mut(key) else {
158 return true;
159 };
160 if !entry.disposer.ptr_eq(disposer) {
161 return true;
162 }
163 owner_removed = entry.owners.remove(owner);
164 if entry.owners.is_empty() {
165 if let Some(entry) = registry.remove(key) {
166 to_run = Some(entry.disposer);
167 registry_removed = true;
168 }
169 }
170 true
171 });
172 if !matches!(result, Ok(true)) {
173 queue_keyed_registration(key.to_string(), disposer.clone(), owner.to_string());
174 return false;
175 }
176 if registry_removed {
177 let _ = KEYED_DISPOSER_ORDER.try_with(|order| {
178 if let Ok(mut order) = order.try_borrow_mut() {
179 order.retain(|registered| registered != key);
180 }
181 });
182 }
183 if let Some(disposer) = to_run {
184 run_keyed_disposer(disposer);
185 true
186 } else {
187 owner_removed
188 }
189}
190
191pub(crate) fn take_keyed_disposer(key: &str) -> Option<Dispose> {
192 let disposer = KEYED_DISPOSERS
193 .try_with(|registry| {
194 registry
195 .try_borrow_mut()
196 .ok()?
197 .remove(key)
198 .map(|entry| entry.disposer)
199 })
200 .ok()
201 .flatten();
202 if disposer.is_some() {
203 let _ = KEYED_DISPOSER_ORDER.try_with(|order| {
204 if let Ok(mut order) = order.try_borrow_mut() {
205 order.retain(|registered| registered != key);
206 }
207 });
208 }
209 disposer
210}
211
212pub(crate) fn take_all_keyed_disposers() -> Vec<Dispose> {
213 flush_keyed_disposer_registrations();
214 let order = KEYED_DISPOSER_ORDER
215 .try_with(|order| {
216 order
217 .try_borrow_mut()
218 .ok()
219 .map(|mut order| std::mem::take(&mut *order))
220 })
221 .ok()
222 .flatten()
223 .unwrap_or_default();
224 let disposers = KEYED_DISPOSERS
225 .try_with(|registry| {
226 registry.try_borrow_mut().ok().map(|mut registry| {
227 let mut result = Vec::with_capacity(registry.len());
228 for key in order.into_iter().rev() {
229 if let Some(entry) = registry.remove(&key) {
230 result.push(entry.disposer);
231 }
232 }
233 result.extend(registry.drain().map(|(_, entry)| entry.disposer));
234 result
235 })
236 })
237 .ok()
238 .flatten()
239 .unwrap_or_default();
240 disposers
241}
242
243pub(crate) fn run_keyed_disposer(disposer: Dispose) {
244 let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| disposer.run()));
245}
246
247static COMPONENT_ID: AtomicU64 = AtomicU64::new(1);
250
251pub fn unique_component_id() -> u64 {
253 COMPONENT_ID.fetch_add(1, Ordering::Relaxed)
254}
255
256pub fn take_focus_request() -> Option<u64> {
257 FOCUS_REQUESTS.with(|r| r.borrow_mut().pop_front())
258}
259
260pub fn drain_focus_requests() -> Vec<u64> {
262 FOCUS_REQUESTS.with(|r| r.borrow_mut().drain(..).collect())
263}
264
265#[derive(Clone)]
271pub struct FocusRequester {
272 pub target: Rc<RefCell<Option<u64>>>,
274}
275
276impl FocusRequester {
277 pub fn new() -> Self {
278 Self {
279 target: Rc::new(RefCell::new(None)),
280 }
281 }
282
283 pub fn request_focus(&self) {
288 if let Some(id) = *self.target.borrow() {
289 FOCUS_REQUESTS.with(|r| r.borrow_mut().push_back(id));
290 }
291 }
292
293 pub fn free_focus(&self) {
297 FOCUS_REQUESTS.with(|r| r.borrow_mut().push_back(CLEAR_FOCUS_MARKER));
298 }
299
300 pub fn capture_focus(&self) {
305 self.request_focus();
306 }
307}
308
309impl Default for FocusRequester {
310 fn default() -> Self {
311 Self::new()
312 }
313}
314
315#[derive(Clone, Copy, Debug, PartialEq, Eq)]
317pub enum FocusDirection {
318 Next,
319 Previous,
320 Left,
321 Right,
322 Up,
323 Down,
324}
325
326#[derive(Clone)]
331pub struct FocusManager {
332 pub chain: Vec<u64>,
334 pub focused: Option<u64>,
336 pub hit_regions: Vec<HitRegion>,
338}
339
340impl FocusManager {
341 pub fn new(chain: Vec<u64>, focused: Option<u64>) -> Self {
342 Self {
343 chain,
344 focused,
345 hit_regions: Vec::new(),
346 }
347 }
348
349 pub fn move_focus(&mut self, dir: FocusDirection) -> Option<u64> {
352 match dir {
353 FocusDirection::Next | FocusDirection::Previous => {
354 self.move_tab(dir == FocusDirection::Previous)
355 }
356 _ => None, }
358 }
359
360 pub fn clear_focus(&self, _force: bool) {
365 FOCUS_REQUESTS.with(|r| r.borrow_mut().push_back(CLEAR_FOCUS_MARKER));
366 }
367
368 pub fn move_focus_spatial(
371 &mut self,
372 dir: FocusDirection,
373 hit_regions: &[HitRegion],
374 ) -> Option<u64> {
375 let next = spatial_focus_next(&self.chain, hit_regions, self.focused, dir)?;
376 self.focused = Some(next);
377 Some(next)
378 }
379
380 pub fn move_tab(&mut self, reverse: bool) -> Option<u64> {
384 if self.chain.is_empty() {
385 return None;
386 }
387 let next = if let Some(sub_chain) =
388 focus_group_chain(&self.chain, &self.hit_regions, self.focused)
389 {
390 if sub_chain.is_empty() {
391 return None;
392 }
393 if let Some(cur) = self.focused {
394 if let Some(idx) = sub_chain.iter().position(|&id| id == cur) {
395 if reverse {
396 if idx == 0 {
397 sub_chain[sub_chain.len() - 1]
398 } else {
399 sub_chain[idx - 1]
400 }
401 } else {
402 sub_chain[(idx + 1) % sub_chain.len()]
403 }
404 } else {
405 sub_chain[0]
406 }
407 } else if reverse {
408 sub_chain[sub_chain.len() - 1]
409 } else {
410 sub_chain[0]
411 }
412 } else {
413 if let Some(cur) = self.focused {
414 if let Some(idx) = self.chain.iter().position(|&id| id == cur) {
415 if reverse {
416 if idx == 0 {
417 self.chain[self.chain.len() - 1]
418 } else {
419 self.chain[idx - 1]
420 }
421 } else {
422 self.chain[(idx + 1) % self.chain.len()]
423 }
424 } else {
425 self.chain[0]
426 }
427 } else if reverse {
428 self.chain[self.chain.len() - 1]
429 } else {
430 self.chain[0]
431 }
432 };
433 self.focused = Some(next);
434 Some(next)
435 }
436
437 pub fn set_requester_target(requester: &FocusRequester, id: u64) {
439 *requester.target.borrow_mut() = Some(id);
440 }
441}
442
443pub fn focus_group_chain(
447 chain: &[u64],
448 hit_regions: &[HitRegion],
449 current: Option<u64>,
450) -> Option<Vec<u64>> {
451 let cur = current?;
452 let group_id = hit_regions.iter().find(|h| h.id == cur)?.focus_group_id?;
453 Some(
454 chain
455 .iter()
456 .copied()
457 .filter(|&id| {
458 id == group_id
459 || hit_regions
460 .iter()
461 .any(|h| h.id == id && h.focus_group_id == Some(group_id))
462 })
463 .collect(),
464 )
465}
466
467pub fn spatial_focus_next(
472 chain: &[u64],
473 hit_regions: &[HitRegion],
474 current: Option<u64>,
475 dir: FocusDirection,
476) -> Option<u64> {
477 if chain.is_empty() {
478 return None;
479 }
480
481 let current_rect =
482 current.and_then(|id| hit_regions.iter().find(|h| h.id == id).map(|h| h.rect));
483
484 match dir {
486 FocusDirection::Next | FocusDirection::Previous => {
487 let mut fm = FocusManager {
488 chain: chain.to_vec(),
489 focused: current,
490 hit_regions: hit_regions.to_vec(),
491 };
492 return fm.move_tab(dir == FocusDirection::Previous);
493 }
494 _ => {}
495 }
496
497 let (cx, cy) = match current_rect {
498 Some(r) => (r.x + r.w / 2.0, r.y + r.h / 2.0),
499 None => {
500 return None;
502 }
503 };
504
505 let mut best: Option<(u64, f32)> = None;
506
507 let scoped: Vec<u64>;
509 let chain: &[u64] = match focus_group_chain(chain, hit_regions, current) {
510 Some(sub) => {
511 scoped = sub;
512 &scoped
513 }
514 None => chain,
515 };
516
517 for &id in chain {
518 if Some(id) == current {
519 continue;
520 }
521 let Some(hr) = hit_regions.iter().find(|h| h.id == id) else {
522 continue;
523 };
524 let r = hr.rect;
525 let other_cx = r.x + r.w / 2.0;
526 let other_cy = r.y + r.h / 2.0;
527 let dx = other_cx - cx;
528 let dy = other_cy - cy;
529
530 let in_direction = match dir {
531 FocusDirection::Left => dx < 0.0 && dy.abs() <= r.h.max(1.0),
532 FocusDirection::Right => dx > 0.0 && dy.abs() <= r.h.max(1.0),
533 FocusDirection::Up => dy < 0.0 && dx.abs() <= r.w.max(1.0),
534 FocusDirection::Down => dy > 0.0 && dx.abs() <= r.w.max(1.0),
535 _ => false,
536 };
537
538 if !in_direction {
539 continue;
540 }
541
542 let dist = dx * dx + dy * dy;
543 let weight = dist / (r.w * r.h + 1.0).max(1.0);
544
545 match best {
546 Some((_, best_weight)) if weight >= best_weight => {}
547 _ => best = Some((id, weight)),
548 }
549 }
550
551 best.map(|(id, _)| id)
552}
553
554#[derive(Default)]
555pub struct Composer {
556 pub slots: Vec<Box<dyn Any>>,
557 pub slot_callers: Vec<&'static Location<'static>>,
558 pub cursor: usize,
559 pub auto_counts: FxHashMap<String, usize>,
560 pub keyed_slots: FxHashMap<String, Box<dyn Any>>,
561 pub keyed_owner: FxHashMap<String, String>,
562 pub keyed_owners: FxHashMap<String, rustc_hash::FxHashSet<String>>,
563 pub live_keyed_owners: rustc_hash::FxHashSet<String>,
564 pub scope_caches: FxHashMap<String, crate::scope_cache::ScopeCache>,
565 pub live_scope_keys: rustc_hash::FxHashSet<String>,
566}
567
568pub struct ComposeGuard {
569 scope: Scope,
570}
571
572pub(crate) fn keyed_disposer_order(key: &str) -> usize {
573 KEYED_DISPOSER_ORDER
574 .try_with(|order| {
575 order
576 .try_borrow()
577 .ok()
578 .and_then(|order| order.iter().position(|registered| registered == key))
579 })
580 .ok()
581 .flatten()
582 .unwrap_or(usize::MAX)
583}
584
585pub(crate) fn release_keyed_owner(key: &str, owner: &str) -> (bool, Option<Box<dyn Any>>) {
586 let result = COMPOSER
587 .try_with(|composer| {
588 let mut composer = match composer.try_borrow_mut() {
589 Ok(composer) => composer,
590 Err(_) => return (false, None, None),
591 };
592 let owners = match composer.keyed_owners.get_mut(key) {
593 Some(owners) => owners,
594 None => return (false, None, None),
595 };
596 owners.remove(owner);
597 if !owners.is_empty() {
598 return (true, None, None);
599 }
600 composer.keyed_owners.remove(key);
601 let old_owner = composer.keyed_owner.remove(key);
602 (true, composer.keyed_slots.remove(key), old_owner)
603 })
604 .unwrap_or((false, None, None));
605 drop(result.2);
606 (result.0, result.1)
607}
608
609pub(crate) fn take_dead_keyed_slots(composer: &mut Composer) -> Vec<(String, Box<dyn Any>)> {
610 let dead: Vec<String> = composer
611 .keyed_slots
612 .keys()
613 .filter(|key| {
614 let live = composer
615 .keyed_owners
616 .get(*key)
617 .map(|owners| {
618 owners
619 .iter()
620 .any(|owner| composer.live_keyed_owners.contains(owner))
621 })
622 .or_else(|| {
623 composer
624 .keyed_owner
625 .get(*key)
626 .map(|owner| composer.live_keyed_owners.contains(owner))
627 })
628 .unwrap_or(false);
629 !live
630 })
631 .cloned()
632 .collect();
633 let mut removed = Vec::with_capacity(dead.len());
634 for key in dead {
635 if let Some(value) = composer.keyed_slots.remove(&key) {
636 removed.push((key.clone(), value));
637 }
638 composer.keyed_owner.remove(&key);
639 composer.keyed_owners.remove(&key);
640 }
641 removed
642}
643
644pub(crate) fn current_scope_key_for_remember() -> Option<String> {
645 crate::scope_cache::current_scope_key()
646}
647
648fn flush_initializer_cursor() {
649 let pending = PENDING_INITIALIZER_CURSOR
650 .try_with(|pending| {
651 pending
652 .try_borrow_mut()
653 .ok()
654 .and_then(|mut pending| pending.take())
655 })
656 .ok()
657 .flatten();
658 let Some(cursor) = pending else {
659 return;
660 };
661 let restored = COMPOSER
662 .try_with(|composer| {
663 composer
664 .try_borrow_mut()
665 .ok()
666 .map(|mut composer| composer.cursor = cursor)
667 })
668 .ok()
669 .flatten();
670 if restored.is_none() {
671 let _ = PENDING_INITIALIZER_CURSOR.try_with(|pending| {
672 if let Ok(mut pending) = pending.try_borrow_mut() {
673 *pending = Some(cursor);
674 }
675 });
676 }
677}
678
679impl ComposeGuard {
680 pub fn begin() -> Self {
681 flush_initializer_cursor();
682 COMPOSER.with(|c| {
683 let mut c = c.borrow_mut();
684 c.cursor = 0;
685 c.auto_counts.clear();
686 c.live_scope_keys.clear();
687 c.live_keyed_owners.clear();
688 c.live_scope_keys.insert(String::new());
689 c.live_keyed_owners.insert(scope_owner_token(None));
690 });
691
692 let scope = ROOT_SCOPE.with(|rs| {
693 if let Some(existing) = rs.borrow().clone() {
694 existing
695 } else {
696 let s = Scope::new();
697 *rs.borrow_mut() = Some(s.clone());
698 s
699 }
700 });
701
702 ComposeGuard { scope }
703 }
704
705 pub fn scope(&self) -> &Scope {
706 &self.scope
707 }
708
709 pub fn live_scope_keys(&self) -> rustc_hash::FxHashSet<String> {
710 COMPOSER.with(|c| c.borrow().live_scope_keys.clone())
711 }
712}
713
714impl Drop for ComposeGuard {
715 fn drop(&mut self) {
716 let removed = COMPOSER.with(|c| {
717 let mut c = c.borrow_mut();
718 let n = c.cursor;
719 let mut removed = Vec::new();
720 if c.slots.len() > n {
721 removed.extend(c.slots.drain(n..));
722 }
723 if c.slot_callers.len() > n {
724 c.slot_callers.truncate(n);
725 }
726 removed
727 });
728 drop(removed);
729 crate::scope_cache::gc_dead_scopes();
730 }
731}
732
733pub fn shutdown_composition() {
738 let scope = ROOT_SCOPE.with(|rs| rs.borrow_mut().take());
739 if let Some(scope) = scope {
740 scope.dispose();
741 }
742 let removed = COMPOSER.with(|c| {
743 let mut c = c.borrow_mut();
744 let slots = std::mem::take(&mut c.slots);
745 let callers = std::mem::take(&mut c.slot_callers);
746 let keyed = std::mem::take(&mut c.keyed_slots);
747 let caches = std::mem::take(&mut c.scope_caches);
748 c.keyed_owner.clear();
749 c.keyed_owners.clear();
750 c.live_keyed_owners.clear();
751 c.live_scope_keys.clear();
752 c.cursor = 0;
753 c.auto_counts.clear();
754 (slots, callers, keyed, caches)
755 });
756 drop(removed);
757 for disposer in take_all_keyed_disposers() {
758 run_keyed_disposer(disposer);
759 }
760 crate::animation_driver::shutdown();
761 crate::scope_cache::clear_all_scope_deps();
762 INITIALIZER_STACK.with(|stack| stack.borrow_mut().clear());
763 PENDING_INITIALIZER_CURSOR.with(|pending| {
764 let _ = pending.borrow_mut().take();
765 });
766}
767
768struct InitializerGuard {
769 active: bool,
770 cursor: Option<usize>,
771}
772
773impl InitializerGuard {
774 fn assert_sequential_allowed() {
775 assert!(
776 INITIALIZER_STACK.with(|stack| stack
777 .try_borrow()
778 .map(|stack| stack.is_empty())
779 .unwrap_or(false)),
780 "remember initializer re-entered composition; use an explicit keyed helper"
781 );
782 }
783
784 fn enter_sequential() -> (Self, usize) {
785 Self::assert_sequential_allowed();
786 let cursor = COMPOSER.with(|composer| {
787 let mut composer = composer.borrow_mut();
788 let cursor = composer.cursor;
789 composer.cursor = composer.cursor.wrapping_add(1);
790 cursor
791 });
792 let pushed = INITIALIZER_STACK
793 .try_with(|stack| {
794 stack.try_borrow_mut().ok().map(|mut stack| {
795 stack.push(InitializerKind::Sequential);
796 true
797 })
798 })
799 .ok()
800 .flatten();
801 if pushed != Some(true) {
802 let _ = COMPOSER.try_with(|composer| {
803 if let Ok(mut composer) = composer.try_borrow_mut() {
804 composer.cursor = cursor;
805 }
806 });
807 panic!("initializer stack busy");
808 }
809 (
810 Self {
811 active: true,
812 cursor: Some(cursor),
813 },
814 cursor,
815 )
816 }
817
818 fn assert_keyed_allowed() {
819 assert!(
820 INITIALIZER_STACK.with(|stack| stack
821 .try_borrow()
822 .map(|stack| stack.is_empty())
823 .unwrap_or(false)),
824 "remember initializer re-entered composition; use an explicit keyed helper"
825 );
826 }
827
828 fn enter_keyed() -> Self {
829 Self::assert_keyed_allowed();
830 INITIALIZER_STACK.with(|stack| {
831 stack
832 .try_borrow_mut()
833 .expect("initializer stack busy")
834 .push(InitializerKind::Keyed)
835 });
836 Self {
837 active: true,
838 cursor: None,
839 }
840 }
841
842 fn commit(&mut self) {
843 if !self.active {
844 return;
845 }
846 let _ = INITIALIZER_STACK.try_with(|stack| {
847 if let Ok(mut stack) = stack.try_borrow_mut() {
848 stack.pop();
849 }
850 });
851 self.active = false;
852 }
853}
854
855impl Drop for InitializerGuard {
856 fn drop(&mut self) {
857 if !self.active {
858 return;
859 }
860 if let Some(cursor) = self.cursor {
861 let restored = COMPOSER
862 .try_with(|composer| {
863 composer
864 .try_borrow_mut()
865 .ok()
866 .map(|mut composer| composer.cursor = cursor)
867 })
868 .ok()
869 .flatten();
870 if restored.is_none() {
871 let _ = PENDING_INITIALIZER_CURSOR.try_with(|pending| {
872 if let Ok(mut pending) = pending.try_borrow_mut() {
873 *pending = Some(cursor);
874 }
875 });
876 }
877 }
878 let _ = INITIALIZER_STACK.try_with(|stack| {
879 if let Ok(mut stack) = stack.try_borrow_mut() {
880 stack.pop();
881 }
882 });
883 }
884}
885
886#[track_caller]
889pub fn remember<T: 'static>(init: impl FnOnce() -> T) -> Rc<T> {
890 flush_initializer_cursor();
891 let caller = Location::caller();
892 let (mut initializer, cursor) = InitializerGuard::enter_sequential();
893
894 let existing = COMPOSER.with(|c| {
895 let c = c.borrow();
896 if cursor >= c.slots.len() || c.slot_callers.get(cursor).copied() != Some(caller) {
897 return None;
898 }
899 c.slots[cursor].downcast_ref::<Rc<T>>().cloned()
900 });
901 if let Some(rc) = existing {
902 initializer.commit();
903 return rc;
904 }
905
906 if COMPOSER.with(|c| {
907 let c = c.borrow();
908 cursor < c.slots.len() && c.slot_callers.get(cursor).copied() == Some(caller)
909 }) {
910 log::warn!(
911 "remember: slot {} type changed {}. \
912 Use remember_with_key(key, || ...) for conditional branches.",
913 cursor,
914 std::any::type_name::<T>(),
915 );
916 }
917
918 let rc = Rc::new(init());
919 let old = COMPOSER.with(|c| {
920 let mut c = c.borrow_mut();
921 let value: Box<dyn Any> = Box::new(rc.clone());
922 let old = if cursor < c.slots.len() {
923 Some(std::mem::replace(&mut c.slots[cursor], value))
924 } else {
925 c.slots.push(value);
926 None
927 };
928 if cursor < c.slot_callers.len() {
929 c.slot_callers[cursor] = caller;
930 } else {
931 c.slot_callers.push(caller);
932 }
933 old
934 });
935 initializer.commit();
936 drop(old);
937 rc
938}
939
940fn push_key_part(key: &mut String, part: &str) {
941 key.push_str(&part.len().to_string());
942 key.push(':');
943 key.push_str(part);
944 key.push('|');
945}
946
947#[track_caller]
948pub fn remember_auto<T: 'static>(slot: &str, init: impl FnOnce() -> T) -> Rc<T> {
949 let caller = Location::caller();
950 let scope = current_scope_key_for_remember().unwrap_or_default();
951 let mut base = String::from("auto:v1|");
952 push_key_part(&mut base, &scope);
953 push_key_part(&mut base, caller.file());
954 push_key_part(&mut base, &caller.line().to_string());
955 push_key_part(&mut base, &caller.column().to_string());
956 push_key_part(&mut base, slot);
957 let occurrence = COMPOSER.with(|composer| {
958 let mut composer = composer.borrow_mut();
959 let count = composer.auto_counts.entry(base.clone()).or_insert(0);
960 let occurrence = *count;
961 *count += 1;
962 occurrence
963 });
964 push_key_part(&mut base, &occurrence.to_string());
965 remember_with_key(base, init)
966}
967
968pub fn keyed<T>(key: impl Into<String>, f: impl FnOnce() -> T) -> T {
969 let key = key.into();
970 assert!(!key.is_empty(), "keyed scopes need non-empty keys");
971 let parent = current_scope_key_for_remember().unwrap_or_default();
972 let mut full_key = String::new();
973 push_key_part(&mut full_key, &parent);
974 push_key_part(&mut full_key, &key);
975 crate::scope_cache::with_scope_key(&full_key, f)
976}
977
978pub fn remember_with_key<T: 'static>(key: impl Into<String>, init: impl FnOnce() -> T) -> Rc<T> {
980 flush_initializer_cursor();
981 InitializerGuard::assert_keyed_allowed();
982 let key = key.into();
983 let scope = current_scope_key_for_remember();
984 let owner = scope_owner_token(scope.as_deref());
985 let existing = COMPOSER.with(|composer| {
986 let composer = composer.borrow();
987 composer
988 .keyed_slots
989 .get(&key)
990 .and_then(|value| value.downcast_ref::<Rc<T>>().cloned())
991 });
992 if let Some(existing) = existing {
993 let old_owner = COMPOSER.with(|composer| {
994 let mut composer = composer.borrow_mut();
995 let old_owner = composer.keyed_owner.insert(key.clone(), owner.clone());
996 composer
997 .keyed_owners
998 .entry(key.clone())
999 .or_default()
1000 .insert(owner.clone());
1001 composer.live_keyed_owners.insert(owner);
1002 old_owner
1003 });
1004 drop(old_owner);
1005 return existing;
1006 }
1007
1008 let has_wrong_type = COMPOSER.with(|composer| {
1009 let composer = composer.borrow();
1010 if composer.keyed_slots.contains_key(&key) {
1011 log::warn!(
1012 "remember_with_key: key '{}' reused with a different type; replacing.",
1013 key
1014 );
1015 }
1016 cfg!(debug_assertions) && composer.keyed_slots.len() > 10_000
1017 });
1018 if has_wrong_type {
1019 log::warn!(
1020 "remember_with_key: more than 10k keys stored; are you generating unbounded dynamic keys?"
1021 );
1022 }
1023
1024 let mut initializer = InitializerGuard::enter_keyed();
1025 let value = Rc::new(init());
1026 let (result, old) = COMPOSER.with(|composer| {
1027 let mut composer = composer.borrow_mut();
1028 if let Some(existing) = composer.keyed_slots.get(&key)
1029 && let Some(existing) = existing.downcast_ref::<Rc<T>>()
1030 {
1031 let existing = existing.clone();
1032 composer.keyed_owner.insert(key.clone(), owner.clone());
1033 composer
1034 .keyed_owners
1035 .entry(key.clone())
1036 .or_default()
1037 .insert(owner.clone());
1038 composer.live_keyed_owners.insert(owner);
1039 return (existing, None);
1040 }
1041 let old = composer
1042 .keyed_slots
1043 .insert(key.clone(), Box::new(value.clone()));
1044 composer.keyed_owner.insert(key.clone(), owner.clone());
1045 composer
1046 .keyed_owners
1047 .entry(key)
1048 .or_default()
1049 .insert(owner.clone());
1050 (value, old)
1051 });
1052 initializer.commit();
1053 drop(old);
1054 result
1055}
1056
1057#[track_caller]
1060pub fn remember_state<T: 'static>(init: impl FnOnce() -> T) -> Rc<RefCell<T>> {
1061 remember(|| RefCell::new(init()))
1062}
1063
1064pub fn remember_state_with_key<T: 'static>(
1066 key: impl Into<String>,
1067 init: impl FnOnce() -> T,
1068) -> Rc<RefCell<T>> {
1069 remember_with_key(key, || RefCell::new(init()))
1070}
1071
1072#[derive(Clone)]
1074pub struct Frame {
1075 pub scene: Scene,
1076 pub hit_regions: Vec<HitRegion>,
1077 pub semantics_nodes: Vec<SemNode>,
1078 pub focus_chain: Vec<u64>,
1079}
1080
1081#[derive(Clone, Default)]
1084pub struct HitRegion {
1085 pub id: u64,
1086 pub rect: Rect,
1087 pub depth: u32,
1090 pub parent: Option<u64>,
1091 pub on_click: Option<Rc<dyn Fn()>>,
1092 pub on_double_click: Option<Rc<dyn Fn()>>,
1093 pub on_long_click: Option<Rc<dyn Fn()>>,
1094 pub on_scroll: Option<Rc<dyn Fn(crate::Vec2) -> crate::Vec2>>,
1095 pub focusable: bool,
1096 pub on_pointer_down: Option<Rc<dyn Fn(crate::input::PointerEvent)>>,
1097 pub on_pointer_move: Option<Rc<dyn Fn(crate::input::PointerEvent)>>,
1098 pub on_pointer_up: Option<Rc<dyn Fn(crate::input::PointerEvent)>>,
1099 pub on_pointer_cancel: Option<Rc<dyn Fn(crate::input::PointerEvent)>>,
1100 pub on_pointer_enter: Option<Rc<dyn Fn(crate::input::PointerEvent)>>,
1101 pub on_pointer_leave: Option<Rc<dyn Fn(crate::input::PointerEvent)>>,
1102 pub z_index: f32,
1103 pub disabled: bool,
1104 pub on_text_change: Option<Rc<dyn Fn(String)>>,
1105 pub on_text_submit: Option<Rc<dyn Fn(String)>>,
1106 pub tf_state_key: Option<u64>,
1109
1110 pub tf_multiline: bool,
1112
1113 pub tf_content_origin: Option<(f32, f32)>,
1118
1119 pub tf_enabled: bool,
1121
1122 pub tf_read_only: bool,
1124
1125 pub tf_sensitive: bool,
1126
1127 pub tf_value: String,
1129
1130 pub tf_font_size: crate::units::Sp,
1133
1134 pub on_drag_start: Option<Rc<dyn Fn(crate::dnd::DragStart) -> Option<crate::dnd::DragPayload>>>,
1136 pub on_drag_end: Option<Rc<dyn Fn(crate::dnd::DragEnd)>>,
1137 pub on_drag_enter: Option<Rc<dyn Fn(crate::dnd::DragOver)>>,
1138 pub on_drag_over: Option<Rc<dyn Fn(crate::dnd::DragOver)>>,
1139 pub on_drag_leave: Option<Rc<dyn Fn(crate::dnd::DragOver)>>,
1140 pub on_drop: Option<Rc<dyn Fn(crate::dnd::DropEvent) -> bool>>,
1141 pub drag_preview: Option<crate::dnd::DragPreview>,
1143
1144 pub on_action: Option<Rc<dyn Fn(crate::shortcuts::Action) -> bool>>,
1145
1146 pub on_key_event: Option<Rc<dyn Fn(crate::input::KeyEvent) -> bool>>,
1149 pub on_preview_key_event: Option<Rc<dyn Fn(crate::input::KeyEvent) -> bool>>,
1151
1152 pub cursor: Option<crate::CursorIcon>,
1154
1155 pub focus_group_id: Option<u64>,
1160
1161 pub keyboard_type: crate::text::KeyboardType,
1164 pub capitalization: crate::text::KeyboardCapitalization,
1165 pub ime_action: crate::text::ImeAction,
1166 pub auto_correct: Option<bool>,
1169
1170 pub interaction_source: Option<crate::modifier::InteractionSource>,
1175}
1176
1177impl HitRegion {
1178 pub fn from_modifier(id: u64, rect: Rect, m: &crate::modifier::Modifier) -> Self {
1182 Self {
1183 id,
1184 rect,
1185 z_index: m.z_index,
1186 on_click: m.on_click.clone(),
1187 on_double_click: m.on_double_click.clone(),
1188 on_long_click: m.on_long_click.clone(),
1189 on_pointer_down: m.on_pointer_down.clone(),
1190 on_pointer_move: m.on_pointer_move.clone(),
1191 on_pointer_up: m.on_pointer_up.clone(),
1192 on_pointer_cancel: m.on_pointer_cancel.clone(),
1193 on_pointer_enter: m.on_pointer_enter.clone(),
1194 on_pointer_leave: m.on_pointer_leave.clone(),
1195 on_action: m.on_action.clone(),
1196 on_key_event: m.on_key_event.clone(),
1197 on_preview_key_event: m.on_preview_key_event.clone(),
1198 cursor: m.cursor.clone(),
1199 on_drag_start: m.on_drag_start.clone(),
1200 on_drag_end: m.on_drag_end.clone(),
1201 on_drag_enter: m.on_drag_enter.clone(),
1202 on_drag_over: m.on_drag_over.clone(),
1203 on_drag_leave: m.on_drag_leave.clone(),
1204 on_scroll: m.on_scroll.clone(),
1205 on_drop: m.on_drop.clone(),
1206 drag_preview: m.drag_preview.clone(),
1207 disabled: m.disabled,
1208 tf_enabled: true,
1209 tf_read_only: false,
1210 ..Default::default()
1211 }
1212 }
1213}
1214
1215#[derive(Clone)]
1223pub struct SemNode {
1224 pub id: u64,
1226
1227 pub parent: Option<u64>,
1229
1230 pub role: Role,
1231 pub label: Option<String>,
1232 pub rect: Rect,
1233 pub focused: bool,
1234 pub enabled: bool,
1235 pub selectable_group: bool,
1237 pub checked: Option<bool>,
1238 pub selected: Option<bool>,
1239 pub value: Option<String>,
1240}
1241
1242impl Default for SemNode {
1243 fn default() -> Self {
1244 Self {
1245 id: 0,
1246 parent: None,
1247 role: Role::default(),
1248 label: None,
1249 rect: Rect::default(),
1250 focused: false,
1251 enabled: true,
1252 selectable_group: false,
1253 checked: None,
1254 selected: None,
1255 value: None,
1256 }
1257 }
1258}
1259
1260pub struct Scheduler {
1261 next_id: u64,
1262 scope_key_to_id: FxHashMap<String, u32>,
1265 next_scope_id: u32,
1266 current_scope: Vec<String>,
1271 scope_local_counters: FxHashMap<String, u32>,
1273 pub focused: Option<u64>,
1274 pub size: (u32, u32),
1275 pub pointer_pos_px: Option<(f32, f32)>,
1278 pub held_keys: HashSet<PhysicalKey>,
1285 pub window_focused: bool,
1288 pub touch_points: Vec<(u64, f32, f32)>,
1295 pub mouse_primary: bool,
1299 pub mouse_secondary: bool,
1300 pub mouse_middle: bool,
1301 pub cursor_override: Option<CursorIcon>,
1309}
1310
1311impl Default for Scheduler {
1312 fn default() -> Self {
1313 Self::new()
1314 }
1315}
1316
1317impl Scheduler {
1318 pub fn new() -> Self {
1319 Self {
1320 next_id: 1,
1321 scope_key_to_id: FxHashMap::default(),
1322 next_scope_id: 1,
1323 current_scope: Vec::new(),
1324 scope_local_counters: FxHashMap::default(),
1325 focused: None,
1326 size: (1280, 800),
1327 pointer_pos_px: None,
1328 held_keys: HashSet::new(),
1329 window_focused: true,
1330 touch_points: Vec::new(),
1331 mouse_primary: false,
1332 mouse_secondary: false,
1333 mouse_middle: false,
1334 cursor_override: None,
1335 }
1336 }
1337
1338 pub fn enter_scope(&mut self, key: &str) {
1343 if !self.current_scope.iter().any(|k| k == key) {
1344 self.scope_local_counters.insert(key.to_string(), 0);
1345 }
1346 self.current_scope.push(key.to_string());
1347 self.get_or_create_scope_id(key);
1348 }
1349
1350 pub fn exit_scope(&mut self) {
1353 self.current_scope.pop();
1354 }
1355
1356 pub fn scope_guard<'a>(&'a mut self, key: &str) -> SchedulerScopeGuard<'a> {
1360 self.enter_scope(key);
1361 SchedulerScopeGuard { sched: self }
1362 }
1363
1364 pub fn scope_guard_raw(&mut self, key: &str) -> SchedulerScopeGuardRaw {
1368 let ptr = self as *mut Scheduler;
1369 unsafe { SchedulerScopeGuardRaw::enter(ptr, key) }
1370 }
1371
1372 fn get_or_create_scope_id(&mut self, key: &str) -> u32 {
1373 if let Some(&id) = self.scope_key_to_id.get(key) {
1374 id
1375 } else {
1376 let id = self.next_scope_id;
1377 self.next_scope_id += 1;
1378 self.scope_key_to_id.insert(key.to_string(), id);
1379 id
1380 }
1381 }
1382
1383 pub fn id(&mut self) -> u64 {
1384 if let Some(key) = self.current_scope.last().cloned() {
1385 let scope_id = self.scope_key_to_id.get(&key).copied().unwrap_or(0);
1386 let local = self.scope_local_counters.get_mut(&key).unwrap();
1387 let id = *local;
1388 *local += 1;
1389 (scope_id as u64) << 32 | id as u64
1390 } else {
1391 let id = self.next_id;
1393 self.next_id += 1;
1394 id
1395 }
1396 }
1397
1398 pub fn id_count(&self) -> u64 {
1399 self.next_id - 1
1400 }
1401
1402 pub fn is_held(&self, key: PhysicalKey) -> bool {
1404 self.held_keys.contains(&key)
1405 }
1406
1407 pub fn snapshot_id(&self) -> u64 {
1410 self.next_id
1411 }
1412
1413 pub fn advance_id(&mut self, count: u32) {
1416 self.next_id += count as u64;
1417 }
1418
1419 pub fn ids_used_since(&self, prev_id: u64) -> u32 {
1422 (self.next_id - prev_id) as u32
1423 }
1424
1425 pub fn gc_scope_state(&mut self, live_keys: &rustc_hash::FxHashSet<String>) {
1426 self.scope_key_to_id
1427 .retain(|key, _| live_keys.contains(key));
1428 self.scope_local_counters
1429 .retain(|key, _| live_keys.contains(key));
1430 }
1431}
1432
1433pub struct SchedulerScopeGuard<'a> {
1435 sched: &'a mut Scheduler,
1436}
1437
1438impl Drop for SchedulerScopeGuard<'_> {
1439 fn drop(&mut self) {
1440 self.sched.exit_scope();
1441 }
1442}
1443
1444pub struct SchedulerScopeGuardRaw {
1451 sched: *mut Scheduler,
1452}
1453
1454impl SchedulerScopeGuardRaw {
1455 pub unsafe fn enter(sched: *mut Scheduler, key: &str) -> Self {
1460 unsafe {
1461 (*sched).enter_scope(key);
1462 }
1463 Self { sched }
1464 }
1465}
1466
1467impl Drop for SchedulerScopeGuardRaw {
1468 fn drop(&mut self) {
1469 unsafe {
1470 (*self.sched).exit_scope();
1471 }
1472 }
1473}
1474
1475impl Scheduler {
1476 pub fn repose<F>(
1477 &mut self,
1478 mut build_root: F,
1479 layout_paint: impl Fn(&View, (u32, u32)) -> (Scene, Vec<HitRegion>, Vec<SemNode>),
1480 ) -> Frame
1481 where
1482 F: FnMut(&mut Scheduler) -> View,
1483 {
1484 let guard = ComposeGuard::begin();
1485 let root = guard.scope.run(|| build_root(self));
1486 let (scene, hits, sem) = layout_paint(&root, self.size);
1487
1488 let focus_chain: Vec<u64> = hits.iter().filter(|h| h.focusable).map(|h| h.id).collect();
1489
1490 let live_scope_keys = guard.live_scope_keys();
1491 let frame = Frame {
1492 scene,
1493 hit_regions: hits,
1494 semantics_nodes: sem,
1495 focus_chain,
1496 };
1497 drop(guard);
1498 self.gc_scope_state(&live_scope_keys);
1499 frame
1500 }
1501}
1502
1503#[cfg(test)]
1506pub fn clear_composer() {
1507 shutdown_composition();
1508}
1509
1510#[cfg(test)]
1511mod focus_trap_tests {
1512 use super::*;
1513
1514 fn region(id: u64, x: f32, group: Option<u64>) -> HitRegion {
1515 HitRegion {
1516 id,
1517 rect: Rect {
1518 x,
1519 y: 0.0,
1520 w: 10.0,
1521 h: 10.0,
1522 },
1523 focus_group_id: group,
1524 ..Default::default()
1525 }
1526 }
1527
1528 #[test]
1529 fn arrows_stay_inside_group() {
1530 let chain = vec![1, 2, 3, 4];
1533 let regions = vec![
1534 region(1, 0.0, None),
1535 region(2, 20.0, Some(9)),
1536 region(3, 40.0, Some(9)),
1537 region(4, 60.0, None),
1538 ];
1539 assert_eq!(
1540 spatial_focus_next(&chain, ®ions, Some(2), FocusDirection::Left),
1541 None,
1542 "outsider 1 is left of 2 but outside the group: trapped"
1543 );
1544 assert_eq!(
1545 spatial_focus_next(&chain, ®ions, Some(2), FocusDirection::Right),
1546 Some(3)
1547 );
1548 assert_eq!(
1549 spatial_focus_next(&chain, ®ions, Some(3), FocusDirection::Left),
1550 Some(2)
1551 );
1552 assert_eq!(
1553 spatial_focus_next(&chain, ®ions, Some(1), FocusDirection::Right),
1554 Some(2),
1555 "ungrouped focus still sees the full chain"
1556 );
1557 }
1558
1559 #[test]
1560 fn tab_cycles_inside_group() {
1561 let chain = vec![1, 2, 3, 4];
1562 let regions = vec![
1563 region(1, 0.0, None),
1564 region(2, 20.0, Some(9)),
1565 region(3, 40.0, Some(9)),
1566 region(4, 60.0, None),
1567 ];
1568 let mut fm = FocusManager::new(chain, Some(2));
1569 fm.hit_regions = regions;
1570 assert_eq!(fm.move_tab(false), Some(3));
1571 assert_eq!(fm.move_tab(false), Some(2));
1572 assert_eq!(fm.move_tab(true), Some(3));
1573 }
1574
1575 #[test]
1576 fn tab_from_outside_can_enter_group() {
1577 let chain = vec![1, 2, 3, 4];
1578 let regions = vec![
1579 region(1, 0.0, None),
1580 region(2, 20.0, Some(9)),
1581 region(3, 40.0, Some(9)),
1582 region(4, 60.0, None),
1583 ];
1584 let mut fm = FocusManager::new(chain, Some(1));
1585 fm.hit_regions = regions;
1586 assert_eq!(fm.move_tab(false), Some(2));
1587 let mut fm = FocusManager::new(vec![1, 2, 3, 4], Some(1));
1588 fm.hit_regions = vec![
1589 region(1, 0.0, None),
1590 region(2, 20.0, Some(9)),
1591 region(3, 40.0, Some(9)),
1592 region(4, 60.0, None),
1593 ];
1594 assert_eq!(fm.move_tab(true), Some(4));
1595 }
1596
1597 #[test]
1598 fn empty_group_never_moves() {
1599 let chain = vec![1, 4];
1600 let regions = vec![region(1, 0.0, None), region(4, 60.0, None)];
1601 let mut fm = FocusManager::new(chain, Some(1));
1602 fm.hit_regions = regions.clone();
1603 assert_eq!(fm.move_tab(false), Some(4));
1604 let chain = vec![1, 2, 4];
1605 let regions = vec![
1606 region(1, 0.0, None),
1607 region(2, 20.0, Some(77)),
1608 region(4, 60.0, None),
1609 ];
1610 let mut fm = FocusManager::new(chain, Some(2));
1611 fm.hit_regions = regions;
1612 assert_eq!(fm.move_tab(false), Some(2));
1613 assert_eq!(
1614 spatial_focus_next(&fm.chain, &fm.hit_regions, Some(2), FocusDirection::Right),
1615 None
1616 );
1617 }
1618}