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>,
565 pub live_keyed_keys: rustc_hash::FxHashSet<String>,
567 pub cache_preserved_owners: rustc_hash::FxHashSet<String>,
570 pub scope_caches: FxHashMap<String, crate::scope_cache::ScopeCache>,
571 pub live_scope_keys: rustc_hash::FxHashSet<String>,
572}
573
574pub struct ComposeGuard {
575 scope: Scope,
576}
577
578pub(crate) fn keyed_disposer_order(key: &str) -> usize {
579 KEYED_DISPOSER_ORDER
580 .try_with(|order| {
581 order
582 .try_borrow()
583 .ok()
584 .and_then(|order| order.iter().position(|registered| registered == key))
585 })
586 .ok()
587 .flatten()
588 .unwrap_or(usize::MAX)
589}
590
591pub(crate) fn release_keyed_owner(key: &str, owner: &str) -> (bool, Option<Box<dyn Any>>) {
592 let result = COMPOSER
593 .try_with(|composer| {
594 let mut composer = match composer.try_borrow_mut() {
595 Ok(composer) => composer,
596 Err(_) => return (false, None, None),
597 };
598 let owners = match composer.keyed_owners.get_mut(key) {
599 Some(owners) => owners,
600 None => return (false, None, None),
601 };
602 owners.remove(owner);
603 if !owners.is_empty() {
604 return (true, None, None);
605 }
606 composer.keyed_owners.remove(key);
607 let old_owner = composer.keyed_owner.remove(key);
608 (true, composer.keyed_slots.remove(key), old_owner)
609 })
610 .unwrap_or((false, None, None));
611 drop(result.2);
612 (result.0, result.1)
613}
614
615pub(crate) fn take_dead_keyed_slots(composer: &mut Composer) -> Vec<(String, Box<dyn Any>)> {
629 fn owners_of<'a>(composer: &'a Composer, key: &str) -> Vec<&'a String> {
630 match composer.keyed_owners.get(key) {
631 Some(owners) => owners.iter().collect(),
632 None => composer.keyed_owner.get(key).into_iter().collect(),
633 }
634 }
635 let dead: Vec<String> = composer
636 .keyed_slots
637 .keys()
638 .filter(|key| {
639 let owners = owners_of(composer, key);
640 let scope_live = owners
641 .iter()
642 .any(|owner| composer.live_keyed_owners.contains(*owner));
643 if !scope_live {
644 return true;
645 }
646 if composer.live_keyed_keys.contains(*key) {
647 return false;
648 }
649 !owners
650 .iter()
651 .any(|owner| composer.cache_preserved_owners.contains(*owner))
652 })
653 .cloned()
654 .collect();
655 let mut removed = Vec::with_capacity(dead.len());
656 for key in dead {
657 if let Some(value) = composer.keyed_slots.remove(&key) {
658 removed.push((key.clone(), value));
659 }
660 composer.keyed_owner.remove(&key);
661 composer.keyed_owners.remove(&key);
662 }
663 removed
664}
665
666pub(crate) fn current_scope_key_for_remember() -> Option<String> {
667 crate::scope_cache::current_scope_key()
668}
669
670fn flush_initializer_cursor() {
671 let pending = PENDING_INITIALIZER_CURSOR
672 .try_with(|pending| {
673 pending
674 .try_borrow_mut()
675 .ok()
676 .and_then(|mut pending| pending.take())
677 })
678 .ok()
679 .flatten();
680 let Some(cursor) = pending else {
681 return;
682 };
683 let restored = COMPOSER
684 .try_with(|composer| {
685 composer
686 .try_borrow_mut()
687 .ok()
688 .map(|mut composer| composer.cursor = cursor)
689 })
690 .ok()
691 .flatten();
692 if restored.is_none() {
693 let _ = PENDING_INITIALIZER_CURSOR.try_with(|pending| {
694 if let Ok(mut pending) = pending.try_borrow_mut() {
695 *pending = Some(cursor);
696 }
697 });
698 }
699}
700
701impl ComposeGuard {
702 pub fn begin() -> Self {
703 flush_initializer_cursor();
704 COMPOSER.with(|c| {
705 let mut c = c.borrow_mut();
706 c.cursor = 0;
707 c.auto_counts.clear();
708 c.live_scope_keys.clear();
709 c.live_keyed_owners.clear();
710 c.live_keyed_keys.clear();
711 c.cache_preserved_owners.clear();
712 c.live_scope_keys.insert(String::new());
713 c.live_keyed_owners.insert(scope_owner_token(None));
714 });
715
716 let scope = ROOT_SCOPE.with(|rs| {
717 if let Some(existing) = rs.borrow().clone() {
718 existing
719 } else {
720 let s = Scope::new();
721 *rs.borrow_mut() = Some(s.clone());
722 s
723 }
724 });
725
726 ComposeGuard { scope }
727 }
728
729 pub fn scope(&self) -> &Scope {
730 &self.scope
731 }
732
733 pub fn live_scope_keys(&self) -> rustc_hash::FxHashSet<String> {
734 COMPOSER.with(|c| c.borrow().live_scope_keys.clone())
735 }
736}
737
738impl Drop for ComposeGuard {
739 fn drop(&mut self) {
740 let removed = COMPOSER.with(|c| {
741 let mut c = c.borrow_mut();
742 let n = c.cursor;
743 let mut removed = Vec::new();
744 if c.slots.len() > n {
745 removed.extend(c.slots.drain(n..));
746 }
747 if c.slot_callers.len() > n {
748 c.slot_callers.truncate(n);
749 }
750 removed
751 });
752 drop(removed);
753 crate::scope_cache::gc_dead_scopes();
754 }
755}
756
757pub fn shutdown_composition() {
762 let scope = ROOT_SCOPE.with(|rs| rs.borrow_mut().take());
763 if let Some(scope) = scope {
764 scope.dispose();
765 }
766 let removed = COMPOSER.with(|c| {
767 let mut c = c.borrow_mut();
768 let slots = std::mem::take(&mut c.slots);
769 let callers = std::mem::take(&mut c.slot_callers);
770 let keyed = std::mem::take(&mut c.keyed_slots);
771 let caches = std::mem::take(&mut c.scope_caches);
772 c.keyed_owner.clear();
773 c.keyed_owners.clear();
774 c.live_keyed_owners.clear();
775 c.live_keyed_keys.clear();
776 c.cache_preserved_owners.clear();
777 c.live_scope_keys.clear();
778 c.cursor = 0;
779 c.auto_counts.clear();
780 (slots, callers, keyed, caches)
781 });
782 drop(removed);
783 for disposer in take_all_keyed_disposers() {
784 run_keyed_disposer(disposer);
785 }
786 crate::animation_driver::shutdown();
787 crate::scope_cache::clear_all_scope_deps();
788 INITIALIZER_STACK.with(|stack| stack.borrow_mut().clear());
789 PENDING_INITIALIZER_CURSOR.with(|pending| {
790 let _ = pending.borrow_mut().take();
791 });
792}
793
794struct InitializerGuard {
795 active: bool,
796 cursor: Option<usize>,
797}
798
799impl InitializerGuard {
800 fn assert_sequential_allowed() {
801 assert!(
802 INITIALIZER_STACK.with(|stack| stack
803 .try_borrow()
804 .map(|stack| stack.is_empty())
805 .unwrap_or(false)),
806 "remember initializer re-entered composition; use an explicit keyed helper"
807 );
808 }
809
810 fn enter_sequential() -> (Self, usize) {
811 Self::assert_sequential_allowed();
812 let cursor = COMPOSER.with(|composer| {
813 let mut composer = composer.borrow_mut();
814 let cursor = composer.cursor;
815 composer.cursor = composer.cursor.wrapping_add(1);
816 cursor
817 });
818 let pushed = INITIALIZER_STACK
819 .try_with(|stack| {
820 stack.try_borrow_mut().ok().map(|mut stack| {
821 stack.push(InitializerKind::Sequential);
822 true
823 })
824 })
825 .ok()
826 .flatten();
827 if pushed != Some(true) {
828 let _ = COMPOSER.try_with(|composer| {
829 if let Ok(mut composer) = composer.try_borrow_mut() {
830 composer.cursor = cursor;
831 }
832 });
833 panic!("initializer stack busy");
834 }
835 (
836 Self {
837 active: true,
838 cursor: Some(cursor),
839 },
840 cursor,
841 )
842 }
843
844 fn assert_keyed_allowed() {
845 assert!(
846 INITIALIZER_STACK.with(|stack| stack
847 .try_borrow()
848 .map(|stack| stack.is_empty())
849 .unwrap_or(false)),
850 "remember initializer re-entered composition; use an explicit keyed helper"
851 );
852 }
853
854 fn enter_keyed() -> Self {
855 Self::assert_keyed_allowed();
856 INITIALIZER_STACK.with(|stack| {
857 stack
858 .try_borrow_mut()
859 .expect("initializer stack busy")
860 .push(InitializerKind::Keyed)
861 });
862 Self {
863 active: true,
864 cursor: None,
865 }
866 }
867
868 fn commit(&mut self) {
869 if !self.active {
870 return;
871 }
872 let _ = INITIALIZER_STACK.try_with(|stack| {
873 if let Ok(mut stack) = stack.try_borrow_mut() {
874 stack.pop();
875 }
876 });
877 self.active = false;
878 }
879}
880
881impl Drop for InitializerGuard {
882 fn drop(&mut self) {
883 if !self.active {
884 return;
885 }
886 if let Some(cursor) = self.cursor {
887 let restored = COMPOSER
888 .try_with(|composer| {
889 composer
890 .try_borrow_mut()
891 .ok()
892 .map(|mut composer| composer.cursor = cursor)
893 })
894 .ok()
895 .flatten();
896 if restored.is_none() {
897 let _ = PENDING_INITIALIZER_CURSOR.try_with(|pending| {
898 if let Ok(mut pending) = pending.try_borrow_mut() {
899 *pending = Some(cursor);
900 }
901 });
902 }
903 }
904 let _ = INITIALIZER_STACK.try_with(|stack| {
905 if let Ok(mut stack) = stack.try_borrow_mut() {
906 stack.pop();
907 }
908 });
909 }
910}
911
912#[track_caller]
915pub fn remember<T: 'static>(init: impl FnOnce() -> T) -> Rc<T> {
916 flush_initializer_cursor();
917 let caller = Location::caller();
918 let (mut initializer, cursor) = InitializerGuard::enter_sequential();
919
920 let existing = COMPOSER.with(|c| {
921 let c = c.borrow();
922 if cursor >= c.slots.len() || c.slot_callers.get(cursor).copied() != Some(caller) {
923 return None;
924 }
925 c.slots[cursor].downcast_ref::<Rc<T>>().cloned()
926 });
927 if let Some(rc) = existing {
928 initializer.commit();
929 return rc;
930 }
931
932 if COMPOSER.with(|c| {
933 let c = c.borrow();
934 cursor < c.slots.len() && c.slot_callers.get(cursor).copied() == Some(caller)
935 }) {
936 log::warn!(
937 "remember: slot {} type changed {}. \
938 Use remember_with_key(key, || ...) for conditional branches.",
939 cursor,
940 std::any::type_name::<T>(),
941 );
942 }
943
944 let rc = Rc::new(init());
945 let old = COMPOSER.with(|c| {
946 let mut c = c.borrow_mut();
947 let value: Box<dyn Any> = Box::new(rc.clone());
948 let old = if cursor < c.slots.len() {
949 Some(std::mem::replace(&mut c.slots[cursor], value))
950 } else {
951 c.slots.push(value);
952 None
953 };
954 if cursor < c.slot_callers.len() {
955 c.slot_callers[cursor] = caller;
956 } else {
957 c.slot_callers.push(caller);
958 }
959 old
960 });
961 initializer.commit();
962 drop(old);
963 rc
964}
965
966fn push_key_part(key: &mut String, part: &str) {
967 key.push_str(&part.len().to_string());
968 key.push(':');
969 key.push_str(part);
970 key.push('|');
971}
972
973#[track_caller]
978pub fn remember_auto<T: 'static>(slot: &str, init: impl FnOnce() -> T) -> Rc<T> {
979 let caller = Location::caller();
980 let scope = current_scope_key_for_remember().unwrap_or_default();
981 let mut base = String::from("auto:v1|");
982 push_key_part(&mut base, &scope);
983 push_key_part(&mut base, caller.file());
984 push_key_part(&mut base, &caller.line().to_string());
985 push_key_part(&mut base, &caller.column().to_string());
986 push_key_part(&mut base, slot);
987 let occurrence = COMPOSER.with(|composer| {
988 let mut composer = composer.borrow_mut();
989 let count = composer.auto_counts.entry(base.clone()).or_insert(0);
990 let occurrence = *count;
991 *count += 1;
992 occurrence
993 });
994 push_key_part(&mut base, &occurrence.to_string());
995 remember_with_key(base, init)
996}
997
998pub fn keyed<T>(key: impl Into<String>, f: impl FnOnce() -> T) -> T {
999 let key = key.into();
1000 assert!(!key.is_empty(), "keyed scopes need non-empty keys");
1001 let parent = current_scope_key_for_remember().unwrap_or_default();
1002 let mut full_key = String::new();
1003 push_key_part(&mut full_key, &parent);
1004 push_key_part(&mut full_key, &key);
1005 crate::scope_cache::with_scope_key(&full_key, f)
1006}
1007
1008pub fn remember_with_key<T: 'static>(key: impl Into<String>, init: impl FnOnce() -> T) -> Rc<T> {
1010 flush_initializer_cursor();
1011 InitializerGuard::assert_keyed_allowed();
1012 let key = key.into();
1013 let scope = current_scope_key_for_remember();
1014 let owner = scope_owner_token(scope.as_deref());
1015 let existing = COMPOSER.with(|composer| {
1016 let composer = composer.borrow();
1017 composer
1018 .keyed_slots
1019 .get(&key)
1020 .and_then(|value| value.downcast_ref::<Rc<T>>().cloned())
1021 });
1022 if let Some(existing) = existing {
1023 let old_owner = COMPOSER.with(|composer| {
1024 let mut composer = composer.borrow_mut();
1025 composer.live_keyed_keys.insert(key.clone());
1026 let old_owner = composer.keyed_owner.insert(key.clone(), owner.clone());
1027 composer
1028 .keyed_owners
1029 .entry(key.clone())
1030 .or_default()
1031 .insert(owner.clone());
1032 composer.live_keyed_owners.insert(owner);
1033 old_owner
1034 });
1035 drop(old_owner);
1036 return existing;
1037 }
1038
1039 let (wrong_type, key_count_high) = COMPOSER.with(|composer| {
1045 let composer = composer.borrow();
1046 (
1047 composer.keyed_slots.contains_key(&key),
1048 composer.keyed_slots.len() > 10_000,
1049 )
1050 });
1051 if wrong_type {
1052 log::warn!(
1053 "remember_with_key: key '{}' reused with a different type; replacing.",
1054 key
1055 );
1056 }
1057 if key_count_high {
1058 log::warn!(
1059 "remember_with_key: {} keys stored; are you generating unbounded dynamic keys?",
1060 COMPOSER.with(|composer| composer.borrow().keyed_slots.len())
1061 );
1062 }
1063
1064 let mut initializer = InitializerGuard::enter_keyed();
1065 let value = Rc::new(init());
1066 let (result, old) = COMPOSER.with(|composer| {
1067 let mut composer = composer.borrow_mut();
1068 if let Some(existing) = composer.keyed_slots.get(&key)
1069 && let Some(existing) = existing.downcast_ref::<Rc<T>>()
1070 {
1071 let existing = existing.clone();
1072 composer.live_keyed_keys.insert(key.clone());
1073 composer.keyed_owner.insert(key.clone(), owner.clone());
1074 composer
1075 .keyed_owners
1076 .entry(key.clone())
1077 .or_default()
1078 .insert(owner.clone());
1079 composer.live_keyed_owners.insert(owner);
1080 return (existing, None);
1081 }
1082 let old = composer
1083 .keyed_slots
1084 .insert(key.clone(), Box::new(value.clone()));
1085 composer.live_keyed_keys.insert(key.clone());
1086 composer.keyed_owner.insert(key.clone(), owner.clone());
1087 composer
1088 .keyed_owners
1089 .entry(key)
1090 .or_default()
1091 .insert(owner.clone());
1092 (value, old)
1093 });
1094 initializer.commit();
1095 drop(old);
1096 result
1097}
1098
1099#[track_caller]
1102pub fn remember_state<T: 'static>(init: impl FnOnce() -> T) -> Rc<RefCell<T>> {
1103 remember(|| RefCell::new(init()))
1104}
1105
1106pub fn remember_state_with_key<T: 'static>(
1108 key: impl Into<String>,
1109 init: impl FnOnce() -> T,
1110) -> Rc<RefCell<T>> {
1111 remember_with_key(key, || RefCell::new(init()))
1112}
1113
1114#[track_caller]
1117pub fn remember_state_auto<T: 'static>(slot: &str, init: impl FnOnce() -> T) -> Rc<RefCell<T>> {
1118 remember_auto(slot, || RefCell::new(init()))
1119}
1120
1121#[derive(Clone)]
1123pub struct Frame {
1124 pub scene: Scene,
1125 pub hit_regions: Vec<HitRegion>,
1126 pub semantics_nodes: Vec<SemNode>,
1127 pub focus_chain: Vec<u64>,
1128}
1129
1130#[derive(Clone, Default)]
1133pub struct HitRegion {
1134 pub id: u64,
1135 pub rect: Rect,
1136 pub depth: u32,
1139 pub parent: Option<u64>,
1140 pub on_click: Option<Rc<dyn Fn()>>,
1141 pub on_double_click: Option<Rc<dyn Fn()>>,
1142 pub on_long_click: Option<Rc<dyn Fn()>>,
1143 pub on_scroll: Option<Rc<dyn Fn(crate::Vec2) -> crate::Vec2>>,
1144 pub focusable: bool,
1145 pub on_pointer_down: Option<Rc<dyn Fn(crate::input::PointerEvent)>>,
1146 pub on_pointer_move: Option<Rc<dyn Fn(crate::input::PointerEvent)>>,
1147 pub on_pointer_up: Option<Rc<dyn Fn(crate::input::PointerEvent)>>,
1148 pub on_pointer_cancel: Option<Rc<dyn Fn(crate::input::PointerEvent)>>,
1149 pub on_pointer_enter: Option<Rc<dyn Fn(crate::input::PointerEvent)>>,
1150 pub on_pointer_leave: Option<Rc<dyn Fn(crate::input::PointerEvent)>>,
1151 pub z_index: f32,
1152 pub disabled: bool,
1153 pub on_text_change: Option<Rc<dyn Fn(String)>>,
1154 pub on_text_submit: Option<Rc<dyn Fn(String)>>,
1155 pub tf_state_key: Option<u64>,
1158
1159 pub tf_multiline: bool,
1161
1162 pub tf_content_origin: Option<(f32, f32)>,
1167
1168 pub tf_enabled: bool,
1170
1171 pub tf_read_only: bool,
1173
1174 pub tf_sensitive: bool,
1175
1176 pub tf_value: String,
1178
1179 pub tf_font_size: crate::units::Sp,
1182
1183 pub on_drag_start: Option<Rc<dyn Fn(crate::dnd::DragStart) -> Option<crate::dnd::DragPayload>>>,
1185 pub on_drag_end: Option<Rc<dyn Fn(crate::dnd::DragEnd)>>,
1186 pub on_drag_enter: Option<Rc<dyn Fn(crate::dnd::DragOver)>>,
1187 pub on_drag_over: Option<Rc<dyn Fn(crate::dnd::DragOver)>>,
1188 pub on_drag_leave: Option<Rc<dyn Fn(crate::dnd::DragOver)>>,
1189 pub on_drop: Option<Rc<dyn Fn(crate::dnd::DropEvent) -> bool>>,
1190 pub drag_preview: Option<crate::dnd::DragPreview>,
1192
1193 pub on_action: Option<Rc<dyn Fn(crate::shortcuts::Action) -> bool>>,
1194
1195 pub on_key_event: Option<Rc<dyn Fn(crate::input::KeyEvent) -> bool>>,
1198 pub on_preview_key_event: Option<Rc<dyn Fn(crate::input::KeyEvent) -> bool>>,
1200
1201 pub cursor: Option<crate::CursorIcon>,
1203
1204 pub focus_group_id: Option<u64>,
1209
1210 pub keyboard_type: crate::text::KeyboardType,
1213 pub capitalization: crate::text::KeyboardCapitalization,
1214 pub ime_action: crate::text::ImeAction,
1215 pub auto_correct: Option<bool>,
1218
1219 pub interaction_source: Option<crate::modifier::InteractionSource>,
1224}
1225
1226impl HitRegion {
1227 pub fn from_modifier(id: u64, rect: Rect, m: &crate::modifier::Modifier) -> Self {
1231 Self {
1232 id,
1233 rect,
1234 z_index: m.z_index,
1235 on_click: m.on_click.clone(),
1236 on_double_click: m.on_double_click.clone(),
1237 on_long_click: m.on_long_click.clone(),
1238 on_pointer_down: m.on_pointer_down.clone(),
1239 on_pointer_move: m.on_pointer_move.clone(),
1240 on_pointer_up: m.on_pointer_up.clone(),
1241 on_pointer_cancel: m.on_pointer_cancel.clone(),
1242 on_pointer_enter: m.on_pointer_enter.clone(),
1243 on_pointer_leave: m.on_pointer_leave.clone(),
1244 on_action: m.on_action.clone(),
1245 on_key_event: m.on_key_event.clone(),
1246 on_preview_key_event: m.on_preview_key_event.clone(),
1247 cursor: m.cursor.clone(),
1248 on_drag_start: m.on_drag_start.clone(),
1249 on_drag_end: m.on_drag_end.clone(),
1250 on_drag_enter: m.on_drag_enter.clone(),
1251 on_drag_over: m.on_drag_over.clone(),
1252 on_drag_leave: m.on_drag_leave.clone(),
1253 on_scroll: m.on_scroll.clone(),
1254 on_drop: m.on_drop.clone(),
1255 drag_preview: m.drag_preview.clone(),
1256 disabled: m.disabled,
1257 tf_enabled: true,
1258 tf_read_only: false,
1259 ..Default::default()
1260 }
1261 }
1262}
1263
1264#[derive(Clone)]
1272pub struct SemNode {
1273 pub id: u64,
1275
1276 pub parent: Option<u64>,
1278
1279 pub role: Role,
1280 pub label: Option<String>,
1281 pub rect: Rect,
1282 pub focused: bool,
1283 pub enabled: bool,
1284 pub selectable_group: bool,
1286 pub checked: Option<bool>,
1287 pub selected: Option<bool>,
1288 pub value: Option<String>,
1289}
1290
1291impl Default for SemNode {
1292 fn default() -> Self {
1293 Self {
1294 id: 0,
1295 parent: None,
1296 role: Role::default(),
1297 label: None,
1298 rect: Rect::default(),
1299 focused: false,
1300 enabled: true,
1301 selectable_group: false,
1302 checked: None,
1303 selected: None,
1304 value: None,
1305 }
1306 }
1307}
1308
1309pub struct Scheduler {
1310 next_id: u64,
1311 scope_key_to_id: FxHashMap<String, u32>,
1314 next_scope_id: u32,
1315 current_scope: Vec<String>,
1320 scope_local_counters: FxHashMap<String, u32>,
1322 pub focused: Option<u64>,
1323 pub size: (u32, u32),
1324 pub pointer_pos_px: Option<(f32, f32)>,
1327 pub held_keys: HashSet<PhysicalKey>,
1334 pub window_focused: bool,
1337 pub touch_points: Vec<(u64, f32, f32)>,
1344 pub mouse_primary: bool,
1348 pub mouse_secondary: bool,
1349 pub mouse_middle: bool,
1350 pub cursor_override: Option<CursorIcon>,
1358}
1359
1360impl Default for Scheduler {
1361 fn default() -> Self {
1362 Self::new()
1363 }
1364}
1365
1366impl Scheduler {
1367 pub fn new() -> Self {
1368 Self {
1369 next_id: 1,
1370 scope_key_to_id: FxHashMap::default(),
1371 next_scope_id: 1,
1372 current_scope: Vec::new(),
1373 scope_local_counters: FxHashMap::default(),
1374 focused: None,
1375 size: (1280, 800),
1376 pointer_pos_px: None,
1377 held_keys: HashSet::new(),
1378 window_focused: true,
1379 touch_points: Vec::new(),
1380 mouse_primary: false,
1381 mouse_secondary: false,
1382 mouse_middle: false,
1383 cursor_override: None,
1384 }
1385 }
1386
1387 pub fn enter_scope(&mut self, key: &str) {
1392 if !self.current_scope.iter().any(|k| k == key) {
1393 self.scope_local_counters.insert(key.to_string(), 0);
1394 }
1395 self.current_scope.push(key.to_string());
1396 self.get_or_create_scope_id(key);
1397 }
1398
1399 pub fn exit_scope(&mut self) {
1402 self.current_scope.pop();
1403 }
1404
1405 pub fn scope_guard<'a>(&'a mut self, key: &str) -> SchedulerScopeGuard<'a> {
1409 self.enter_scope(key);
1410 SchedulerScopeGuard { sched: self }
1411 }
1412
1413 pub fn scope_guard_raw(&mut self, key: &str) -> SchedulerScopeGuardRaw {
1417 let ptr = self as *mut Scheduler;
1418 unsafe { SchedulerScopeGuardRaw::enter(ptr, key) }
1419 }
1420
1421 fn get_or_create_scope_id(&mut self, key: &str) -> u32 {
1422 if let Some(&id) = self.scope_key_to_id.get(key) {
1423 id
1424 } else {
1425 let id = self.next_scope_id;
1426 self.next_scope_id += 1;
1427 self.scope_key_to_id.insert(key.to_string(), id);
1428 id
1429 }
1430 }
1431
1432 pub fn id(&mut self) -> u64 {
1433 if let Some(key) = self.current_scope.last().cloned() {
1434 let scope_id = self.scope_key_to_id.get(&key).copied().unwrap_or(0);
1435 let local = self.scope_local_counters.get_mut(&key).unwrap();
1436 let id = *local;
1437 *local += 1;
1438 (scope_id as u64) << 32 | id as u64
1439 } else {
1440 let id = self.next_id;
1442 self.next_id += 1;
1443 id
1444 }
1445 }
1446
1447 pub fn id_count(&self) -> u64 {
1448 self.next_id - 1
1449 }
1450
1451 pub fn is_held(&self, key: PhysicalKey) -> bool {
1453 self.held_keys.contains(&key)
1454 }
1455
1456 pub fn snapshot_id(&self) -> u64 {
1459 self.next_id
1460 }
1461
1462 pub fn advance_id(&mut self, count: u32) {
1465 self.next_id += count as u64;
1466 }
1467
1468 pub fn ids_used_since(&self, prev_id: u64) -> u32 {
1471 (self.next_id - prev_id) as u32
1472 }
1473
1474 pub fn gc_scope_state(&mut self, live_keys: &rustc_hash::FxHashSet<String>) {
1475 self.scope_key_to_id
1476 .retain(|key, _| live_keys.contains(key));
1477 self.scope_local_counters
1478 .retain(|key, _| live_keys.contains(key));
1479 }
1480}
1481
1482pub struct SchedulerScopeGuard<'a> {
1484 sched: &'a mut Scheduler,
1485}
1486
1487impl Drop for SchedulerScopeGuard<'_> {
1488 fn drop(&mut self) {
1489 self.sched.exit_scope();
1490 }
1491}
1492
1493pub struct SchedulerScopeGuardRaw {
1500 sched: *mut Scheduler,
1501}
1502
1503impl SchedulerScopeGuardRaw {
1504 pub unsafe fn enter(sched: *mut Scheduler, key: &str) -> Self {
1509 unsafe {
1510 (*sched).enter_scope(key);
1511 }
1512 Self { sched }
1513 }
1514}
1515
1516impl Drop for SchedulerScopeGuardRaw {
1517 fn drop(&mut self) {
1518 unsafe {
1519 (*self.sched).exit_scope();
1520 }
1521 }
1522}
1523
1524impl Scheduler {
1525 pub fn repose<F>(
1526 &mut self,
1527 mut build_root: F,
1528 layout_paint: impl Fn(&View, (u32, u32)) -> (Scene, Vec<HitRegion>, Vec<SemNode>),
1529 ) -> Frame
1530 where
1531 F: FnMut(&mut Scheduler) -> View,
1532 {
1533 let guard = ComposeGuard::begin();
1534 let root = guard.scope.run(|| build_root(self));
1535 let (scene, hits, sem) = layout_paint(&root, self.size);
1536
1537 let focus_chain: Vec<u64> = hits.iter().filter(|h| h.focusable).map(|h| h.id).collect();
1538
1539 let live_scope_keys = guard.live_scope_keys();
1540 let frame = Frame {
1541 scene,
1542 hit_regions: hits,
1543 semantics_nodes: sem,
1544 focus_chain,
1545 };
1546 drop(guard);
1547 self.gc_scope_state(&live_scope_keys);
1548 frame
1549 }
1550}
1551
1552#[cfg(test)]
1555pub fn clear_composer() {
1556 shutdown_composition();
1557}
1558
1559#[cfg(test)]
1560mod focus_trap_tests {
1561 use super::*;
1562
1563 fn region(id: u64, x: f32, group: Option<u64>) -> HitRegion {
1564 HitRegion {
1565 id,
1566 rect: Rect {
1567 x,
1568 y: 0.0,
1569 w: 10.0,
1570 h: 10.0,
1571 },
1572 focus_group_id: group,
1573 ..Default::default()
1574 }
1575 }
1576
1577 #[test]
1578 fn arrows_stay_inside_group() {
1579 let chain = vec![1, 2, 3, 4];
1582 let regions = vec![
1583 region(1, 0.0, None),
1584 region(2, 20.0, Some(9)),
1585 region(3, 40.0, Some(9)),
1586 region(4, 60.0, None),
1587 ];
1588 assert_eq!(
1589 spatial_focus_next(&chain, ®ions, Some(2), FocusDirection::Left),
1590 None,
1591 "outsider 1 is left of 2 but outside the group: trapped"
1592 );
1593 assert_eq!(
1594 spatial_focus_next(&chain, ®ions, Some(2), FocusDirection::Right),
1595 Some(3)
1596 );
1597 assert_eq!(
1598 spatial_focus_next(&chain, ®ions, Some(3), FocusDirection::Left),
1599 Some(2)
1600 );
1601 assert_eq!(
1602 spatial_focus_next(&chain, ®ions, Some(1), FocusDirection::Right),
1603 Some(2),
1604 "ungrouped focus still sees the full chain"
1605 );
1606 }
1607
1608 #[test]
1609 fn tab_cycles_inside_group() {
1610 let chain = vec![1, 2, 3, 4];
1611 let regions = vec![
1612 region(1, 0.0, None),
1613 region(2, 20.0, Some(9)),
1614 region(3, 40.0, Some(9)),
1615 region(4, 60.0, None),
1616 ];
1617 let mut fm = FocusManager::new(chain, Some(2));
1618 fm.hit_regions = regions;
1619 assert_eq!(fm.move_tab(false), Some(3));
1620 assert_eq!(fm.move_tab(false), Some(2));
1621 assert_eq!(fm.move_tab(true), Some(3));
1622 }
1623
1624 #[test]
1625 fn tab_from_outside_can_enter_group() {
1626 let chain = vec![1, 2, 3, 4];
1627 let regions = vec![
1628 region(1, 0.0, None),
1629 region(2, 20.0, Some(9)),
1630 region(3, 40.0, Some(9)),
1631 region(4, 60.0, None),
1632 ];
1633 let mut fm = FocusManager::new(chain, Some(1));
1634 fm.hit_regions = regions;
1635 assert_eq!(fm.move_tab(false), Some(2));
1636 let mut fm = FocusManager::new(vec![1, 2, 3, 4], Some(1));
1637 fm.hit_regions = vec![
1638 region(1, 0.0, None),
1639 region(2, 20.0, Some(9)),
1640 region(3, 40.0, Some(9)),
1641 region(4, 60.0, None),
1642 ];
1643 assert_eq!(fm.move_tab(true), Some(4));
1644 }
1645
1646 #[test]
1647 fn empty_group_never_moves() {
1648 let chain = vec![1, 4];
1649 let regions = vec![region(1, 0.0, None), region(4, 60.0, None)];
1650 let mut fm = FocusManager::new(chain, Some(1));
1651 fm.hit_regions = regions.clone();
1652 assert_eq!(fm.move_tab(false), Some(4));
1653 let chain = vec![1, 2, 4];
1654 let regions = vec![
1655 region(1, 0.0, None),
1656 region(2, 20.0, Some(77)),
1657 region(4, 60.0, None),
1658 ];
1659 let mut fm = FocusManager::new(chain, Some(2));
1660 fm.hit_regions = regions;
1661 assert_eq!(fm.move_tab(false), Some(2));
1662 assert_eq!(
1663 spatial_focus_next(&fm.chain, &fm.hit_regions, Some(2), FocusDirection::Right),
1664 None
1665 );
1666 }
1667}