cranpose_core/
composer_context.rs1use std::{
2 cell::RefCell,
3 rc::Rc,
4 sync::atomic::{AtomicUsize, Ordering},
5};
6
7use crate::{Composer, ComposerCore};
8
9thread_local! {
10 static CURRENT_COMPOSER: RefCell<Option<Rc<ComposerCore>>> = const { RefCell::new(None) };
11}
12
13static INSTALLED_COMPOSERS: AtomicUsize = AtomicUsize::new(0);
17
18fn any_composer_installed() -> bool {
20 INSTALLED_COMPOSERS.load(Ordering::Relaxed) != 0
21}
22
23#[must_use = "ComposerScopeGuard restores the previous composer on drop"]
25pub struct ComposerScopeGuard {
26 previous: Option<Rc<ComposerCore>>,
27}
28
29impl Drop for ComposerScopeGuard {
30 fn drop(&mut self) {
31 replace_current(self.previous.take());
32 }
33}
34
35pub fn enter(composer: &Composer) -> ComposerScopeGuard {
38 ComposerScopeGuard {
39 previous: replace_current(Some(composer.clone_core())),
40 }
41}
42
43fn replace_current(composer: Option<Rc<ComposerCore>>) -> Option<Rc<ComposerCore>> {
44 let installs = composer.is_some();
45 let previous = CURRENT_COMPOSER.with(|current| current.replace(composer));
46 match (previous.is_some(), installs) {
47 (false, true) => {
48 INSTALLED_COMPOSERS.fetch_add(1, Ordering::Relaxed);
49 }
50 (true, false) => {
51 INSTALLED_COMPOSERS.fetch_sub(1, Ordering::Relaxed);
52 }
53 _ => {}
54 }
55 previous
56}
57
58pub(crate) fn without_composer<R>(f: impl FnOnce() -> R) -> R {
59 let _suspended = ComposerScopeGuard {
60 previous: replace_current(None),
61 };
62 f()
63}
64
65pub fn with_composer<R>(f: impl FnOnce(&Composer) -> R) -> R {
70 CURRENT_COMPOSER.with(|current| {
71 let core = current
72 .borrow()
73 .as_ref()
74 .expect("with_composer: no active composer")
75 .clone();
76 let composer = Composer::from_core(core);
77 f(&composer)
78 })
79}
80
81#[doc(hidden)]
86pub fn __current_composer() -> Composer {
87 CURRENT_COMPOSER.with(|current| {
88 Composer::from_core(
89 current
90 .borrow()
91 .as_ref()
92 .expect("with_composer: no active composer")
93 .clone(),
94 )
95 })
96}
97
98pub(crate) fn with_current_core<R>(f: impl FnOnce(&ComposerCore) -> R) -> Option<R> {
99 if !any_composer_installed() {
100 return None;
101 }
102 CURRENT_COMPOSER.with(|current| current.borrow().as_deref().map(f))
103}
104
105pub fn current_composer() -> Option<Composer> {
107 if !any_composer_installed() {
108 return None;
109 }
110 CURRENT_COMPOSER.with(|current| {
111 let core = current.borrow().as_ref()?.clone();
112 Some(Composer::from_core(core))
113 })
114}
115
116pub fn note_nested_slots_host(host: &std::rc::Rc<crate::SlotsHost>) {
117 let Some(composer) = current_composer() else {
118 return;
119 };
120 let holder = composer.active_slots_host();
121 if std::rc::Rc::ptr_eq(&holder, host) {
122 return;
123 }
124 holder.note_nested_host(host);
125}
126
127pub fn try_with_composer<R>(f: impl FnOnce(&Composer) -> R) -> Option<R> {
130 current_composer().map(|composer| f(&composer))
131}