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 && let Some(entry) = registry.remove(key)
166 {
167 to_run = Some(entry.disposer);
168 registry_removed = true;
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
225 KEYED_DISPOSERS
226 .try_with(|registry| {
227 registry.try_borrow_mut().ok().map(|mut registry| {
228 let mut result = Vec::with_capacity(registry.len());
229 for key in order.into_iter().rev() {
230 if let Some(entry) = registry.remove(&key) {
231 result.push(entry.disposer);
232 }
233 }
234 result.extend(registry.drain().map(|(_, entry)| entry.disposer));
235 result
236 })
237 })
238 .ok()
239 .flatten()
240 .unwrap_or_default()
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 pub on_ime: Option<Rc<dyn Fn(crate::input::ImeEvent) -> bool>>,
1203
1204 pub cursor: Option<crate::CursorIcon>,
1206
1207 pub focus_group_id: Option<u64>,
1212
1213 pub keyboard_type: crate::text::KeyboardType,
1216 pub capitalization: crate::text::KeyboardCapitalization,
1217 pub ime_action: crate::text::ImeAction,
1218 pub auto_correct: Option<bool>,
1221
1222 pub interaction_source: Option<crate::modifier::InteractionSource>,
1227}
1228
1229impl HitRegion {
1230 pub fn from_modifier(id: u64, rect: Rect, m: &crate::modifier::Modifier) -> Self {
1234 Self {
1235 id,
1236 rect,
1237 z_index: m.z_index,
1238 on_click: m.on_click.clone(),
1239 on_double_click: m.on_double_click.clone(),
1240 on_long_click: m.on_long_click.clone(),
1241 on_pointer_down: m.on_pointer_down.clone(),
1242 on_pointer_move: m.on_pointer_move.clone(),
1243 on_pointer_up: m.on_pointer_up.clone(),
1244 on_pointer_cancel: m.on_pointer_cancel.clone(),
1245 on_pointer_enter: m.on_pointer_enter.clone(),
1246 on_pointer_leave: m.on_pointer_leave.clone(),
1247 on_action: m.on_action.clone(),
1248 on_key_event: m.on_key_event.clone(),
1249 on_preview_key_event: m.on_preview_key_event.clone(),
1250 on_ime: m.on_ime.clone(),
1251 cursor: m.cursor.clone(),
1252 on_drag_start: m.on_drag_start.clone(),
1253 on_drag_end: m.on_drag_end.clone(),
1254 on_drag_enter: m.on_drag_enter.clone(),
1255 on_drag_over: m.on_drag_over.clone(),
1256 on_drag_leave: m.on_drag_leave.clone(),
1257 on_scroll: m.on_scroll.clone(),
1258 on_drop: m.on_drop.clone(),
1259 drag_preview: m.drag_preview.clone(),
1260 disabled: m.disabled,
1261 tf_enabled: true,
1262 tf_read_only: false,
1263 ..Default::default()
1264 }
1265 }
1266}
1267
1268#[derive(Clone)]
1276pub struct SemNode {
1277 pub id: u64,
1279
1280 pub parent: Option<u64>,
1282
1283 pub role: Role,
1284 pub label: Option<String>,
1285 pub rect: Rect,
1286 pub focused: bool,
1287 pub enabled: bool,
1288 pub selectable_group: bool,
1290 pub checked: Option<bool>,
1291 pub selected: Option<bool>,
1292 pub value: Option<String>,
1293}
1294
1295impl Default for SemNode {
1296 fn default() -> Self {
1297 Self {
1298 id: 0,
1299 parent: None,
1300 role: Role::default(),
1301 label: None,
1302 rect: Rect::default(),
1303 focused: false,
1304 enabled: true,
1305 selectable_group: false,
1306 checked: None,
1307 selected: None,
1308 value: None,
1309 }
1310 }
1311}
1312
1313pub struct Scheduler {
1314 next_id: u64,
1315 scope_key_to_id: FxHashMap<String, u32>,
1318 next_scope_id: u32,
1319 current_scope: Vec<String>,
1324 scope_local_counters: FxHashMap<String, u32>,
1326 pub focused: Option<u64>,
1327 pub size: (u32, u32),
1328 pub pointer_pos_px: Option<(f32, f32)>,
1331 pub held_keys: HashSet<PhysicalKey>,
1338 pub window_focused: bool,
1341 pub touch_points: Vec<(u64, f32, f32)>,
1348 pub mouse_primary: bool,
1352 pub mouse_secondary: bool,
1353 pub mouse_middle: bool,
1354 pub cursor_override: Option<CursorIcon>,
1362}
1363
1364impl Default for Scheduler {
1365 fn default() -> Self {
1366 Self::new()
1367 }
1368}
1369
1370impl Scheduler {
1371 pub fn new() -> Self {
1372 Self {
1373 next_id: 1,
1374 scope_key_to_id: FxHashMap::default(),
1375 next_scope_id: 1,
1376 current_scope: Vec::new(),
1377 scope_local_counters: FxHashMap::default(),
1378 focused: None,
1379 size: (1280, 800),
1380 pointer_pos_px: None,
1381 held_keys: HashSet::new(),
1382 window_focused: true,
1383 touch_points: Vec::new(),
1384 mouse_primary: false,
1385 mouse_secondary: false,
1386 mouse_middle: false,
1387 cursor_override: None,
1388 }
1389 }
1390
1391 pub fn enter_scope(&mut self, key: &str) {
1396 if !self.current_scope.iter().any(|k| k == key) {
1397 self.scope_local_counters.insert(key.to_string(), 0);
1398 }
1399 self.current_scope.push(key.to_string());
1400 self.get_or_create_scope_id(key);
1401 }
1402
1403 pub fn exit_scope(&mut self) {
1406 self.current_scope.pop();
1407 }
1408
1409 pub fn scope_guard<'a>(&'a mut self, key: &str) -> SchedulerScopeGuard<'a> {
1413 self.enter_scope(key);
1414 SchedulerScopeGuard { sched: self }
1415 }
1416
1417 pub fn scope_guard_raw(&mut self, key: &str) -> SchedulerScopeGuardRaw {
1421 let ptr = self as *mut Scheduler;
1422 unsafe { SchedulerScopeGuardRaw::enter(ptr, key) }
1423 }
1424
1425 fn get_or_create_scope_id(&mut self, key: &str) -> u32 {
1426 if let Some(&id) = self.scope_key_to_id.get(key) {
1427 id
1428 } else {
1429 let id = self.next_scope_id;
1430 self.next_scope_id += 1;
1431 self.scope_key_to_id.insert(key.to_string(), id);
1432 id
1433 }
1434 }
1435
1436 pub fn id(&mut self) -> u64 {
1437 if let Some(key) = self.current_scope.last().cloned() {
1438 let scope_id = self.scope_key_to_id.get(&key).copied().unwrap_or(0);
1439 let local = self.scope_local_counters.get_mut(&key).unwrap();
1440 let id = *local;
1441 *local += 1;
1442 (scope_id as u64) << 32 | id as u64
1443 } else {
1444 let id = self.next_id;
1446 self.next_id += 1;
1447 id
1448 }
1449 }
1450
1451 pub fn id_count(&self) -> u64 {
1452 self.next_id - 1
1453 }
1454
1455 pub fn is_held(&self, key: PhysicalKey) -> bool {
1457 self.held_keys.contains(&key)
1458 }
1459
1460 pub fn snapshot_id(&self) -> u64 {
1463 self.next_id
1464 }
1465
1466 pub fn advance_id(&mut self, count: u32) {
1469 self.next_id += count as u64;
1470 }
1471
1472 pub fn ids_used_since(&self, prev_id: u64) -> u32 {
1475 (self.next_id - prev_id) as u32
1476 }
1477
1478 pub fn gc_scope_state(&mut self, live_keys: &rustc_hash::FxHashSet<String>) {
1479 self.scope_key_to_id
1480 .retain(|key, _| live_keys.contains(key));
1481 self.scope_local_counters
1482 .retain(|key, _| live_keys.contains(key));
1483 }
1484}
1485
1486pub struct SchedulerScopeGuard<'a> {
1488 sched: &'a mut Scheduler,
1489}
1490
1491impl Drop for SchedulerScopeGuard<'_> {
1492 fn drop(&mut self) {
1493 self.sched.exit_scope();
1494 }
1495}
1496
1497pub struct SchedulerScopeGuardRaw {
1504 sched: *mut Scheduler,
1505}
1506
1507impl SchedulerScopeGuardRaw {
1508 pub unsafe fn enter(sched: *mut Scheduler, key: &str) -> Self {
1513 unsafe {
1514 (*sched).enter_scope(key);
1515 }
1516 Self { sched }
1517 }
1518}
1519
1520impl Drop for SchedulerScopeGuardRaw {
1521 fn drop(&mut self) {
1522 unsafe {
1523 (*self.sched).exit_scope();
1524 }
1525 }
1526}
1527
1528impl Scheduler {
1529 pub fn repose<F>(
1530 &mut self,
1531 mut build_root: F,
1532 layout_paint: impl Fn(&View, (u32, u32)) -> (Scene, Vec<HitRegion>, Vec<SemNode>),
1533 ) -> Frame
1534 where
1535 F: FnMut(&mut Scheduler) -> View,
1536 {
1537 let guard = ComposeGuard::begin();
1538 let root = guard.scope.run(|| build_root(self));
1539 let (scene, hits, sem) = layout_paint(&root, self.size);
1540
1541 let focus_chain: Vec<u64> = hits.iter().filter(|h| h.focusable).map(|h| h.id).collect();
1542
1543 let live_scope_keys = guard.live_scope_keys();
1544 let frame = Frame {
1545 scene,
1546 hit_regions: hits,
1547 semantics_nodes: sem,
1548 focus_chain,
1549 };
1550 drop(guard);
1551 self.gc_scope_state(&live_scope_keys);
1552 frame
1553 }
1554}
1555
1556#[cfg(test)]
1559pub fn clear_composer() {
1560 shutdown_composition();
1561}
1562
1563#[cfg(test)]
1564mod focus_trap_tests {
1565 use super::*;
1566
1567 fn region(id: u64, x: f32, group: Option<u64>) -> HitRegion {
1568 HitRegion {
1569 id,
1570 rect: Rect {
1571 x,
1572 y: 0.0,
1573 w: 10.0,
1574 h: 10.0,
1575 },
1576 focus_group_id: group,
1577 ..Default::default()
1578 }
1579 }
1580
1581 #[test]
1582 fn arrows_stay_inside_group() {
1583 let chain = vec![1, 2, 3, 4];
1586 let regions = vec![
1587 region(1, 0.0, None),
1588 region(2, 20.0, Some(9)),
1589 region(3, 40.0, Some(9)),
1590 region(4, 60.0, None),
1591 ];
1592 assert_eq!(
1593 spatial_focus_next(&chain, ®ions, Some(2), FocusDirection::Left),
1594 None,
1595 "outsider 1 is left of 2 but outside the group: trapped"
1596 );
1597 assert_eq!(
1598 spatial_focus_next(&chain, ®ions, Some(2), FocusDirection::Right),
1599 Some(3)
1600 );
1601 assert_eq!(
1602 spatial_focus_next(&chain, ®ions, Some(3), FocusDirection::Left),
1603 Some(2)
1604 );
1605 assert_eq!(
1606 spatial_focus_next(&chain, ®ions, Some(1), FocusDirection::Right),
1607 Some(2),
1608 "ungrouped focus still sees the full chain"
1609 );
1610 }
1611
1612 #[test]
1613 fn tab_cycles_inside_group() {
1614 let chain = vec![1, 2, 3, 4];
1615 let regions = vec![
1616 region(1, 0.0, None),
1617 region(2, 20.0, Some(9)),
1618 region(3, 40.0, Some(9)),
1619 region(4, 60.0, None),
1620 ];
1621 let mut fm = FocusManager::new(chain, Some(2));
1622 fm.hit_regions = regions;
1623 assert_eq!(fm.move_tab(false), Some(3));
1624 assert_eq!(fm.move_tab(false), Some(2));
1625 assert_eq!(fm.move_tab(true), Some(3));
1626 }
1627
1628 #[test]
1629 fn tab_from_outside_can_enter_group() {
1630 let chain = vec![1, 2, 3, 4];
1631 let regions = vec![
1632 region(1, 0.0, None),
1633 region(2, 20.0, Some(9)),
1634 region(3, 40.0, Some(9)),
1635 region(4, 60.0, None),
1636 ];
1637 let mut fm = FocusManager::new(chain, Some(1));
1638 fm.hit_regions = regions;
1639 assert_eq!(fm.move_tab(false), Some(2));
1640 let mut fm = FocusManager::new(vec![1, 2, 3, 4], Some(1));
1641 fm.hit_regions = vec![
1642 region(1, 0.0, None),
1643 region(2, 20.0, Some(9)),
1644 region(3, 40.0, Some(9)),
1645 region(4, 60.0, None),
1646 ];
1647 assert_eq!(fm.move_tab(true), Some(4));
1648 }
1649
1650 #[test]
1651 fn empty_group_never_moves() {
1652 let chain = vec![1, 4];
1653 let regions = vec![region(1, 0.0, None), region(4, 60.0, None)];
1654 let mut fm = FocusManager::new(chain, Some(1));
1655 fm.hit_regions = regions.clone();
1656 assert_eq!(fm.move_tab(false), Some(4));
1657 let chain = vec![1, 2, 4];
1658 let regions = vec![
1659 region(1, 0.0, None),
1660 region(2, 20.0, Some(77)),
1661 region(4, 60.0, None),
1662 ];
1663 let mut fm = FocusManager::new(chain, Some(2));
1664 fm.hit_regions = regions;
1665 assert_eq!(fm.move_tab(false), Some(2));
1666 assert_eq!(
1667 spatial_focus_next(&fm.chain, &fm.hit_regions, Some(2), FocusDirection::Right),
1668 None
1669 );
1670 }
1671}