Skip to main content

cranpose_core/
composer_context.rs

1use std::{cell::RefCell, rc::Rc};
2
3use crate::{Composer, ComposerCore};
4
5// Thread-local stack of Composer handles (safe, no raw pointers).
6thread_local! {
7    static COMPOSER_STACK: RefCell<Vec<Rc<ComposerCore>>> = const { RefCell::new(Vec::new()) };
8}
9
10/// Guard that pops the composer stack on drop.
11#[must_use = "ComposerScopeGuard pops the composer stack on drop"]
12pub struct ComposerScopeGuard;
13
14impl Drop for ComposerScopeGuard {
15    fn drop(&mut self) {
16        COMPOSER_STACK.with(|stack| {
17            let mut stack = stack.borrow_mut();
18            stack.pop();
19        });
20    }
21}
22
23/// Pushes the composer onto the thread-local stack for the duration of the scope.
24/// Returns a guard that will pop it on drop.
25pub fn enter(composer: &Composer) -> ComposerScopeGuard {
26    COMPOSER_STACK.with(|stack| {
27        stack.borrow_mut().push(composer.clone_core());
28    });
29    ComposerScopeGuard
30}
31
32/// Access the current composer from the thread-local stack.
33///
34/// # Panics
35/// Panics if there is no active composer.
36pub fn with_composer<R>(f: impl FnOnce(&Composer) -> R) -> R {
37    COMPOSER_STACK.with(|stack| {
38        let core = stack
39            .borrow()
40            .last()
41            .expect("with_composer: no active composer")
42            .clone();
43        let composer = Composer::from_core(core);
44        f(&composer)
45    })
46}
47
48/// Return the current composer from the thread-local stack.
49pub fn current_composer() -> Option<Composer> {
50    COMPOSER_STACK.with(|stack| {
51        let core = stack.borrow().last()?.clone();
52        Some(Composer::from_core(core))
53    })
54}
55
56pub fn note_nested_slots_host(host: &std::rc::Rc<crate::SlotsHost>) {
57    let Some(composer) = current_composer() else {
58        return;
59    };
60    let holder = composer.active_slots_host();
61    if std::rc::Rc::ptr_eq(&holder, host) {
62        return;
63    }
64    holder.note_nested_host(host);
65}
66
67/// Try to access the current composer from the thread-local stack.
68/// Returns None if there is no active composer.
69pub fn try_with_composer<R>(f: impl FnOnce(&Composer) -> R) -> Option<R> {
70    current_composer().map(|composer| f(&composer))
71}