Skip to main content

cranpose_core/
composer_context.rs

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