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
31/// Access the current composer from the thread-local stack.
32///
33/// # Panics
34/// Panics if there is no active composer.
35pub fn with_composer<R>(f: impl FnOnce(&Composer) -> R) -> R {
36    COMPOSER_STACK.with(|stack| {
37        let core = stack
38            .borrow()
39            .last()
40            .expect("with_composer: no active composer")
41            .clone();
42        let composer = Composer::from_core(core);
43        f(&composer)
44    })
45}
46
47/// Return the current composer from the thread-local stack.
48pub fn current_composer() -> Option<Composer> {
49    COMPOSER_STACK.with(|stack| {
50        let core = stack.borrow().last()?.clone();
51        Some(Composer::from_core(core))
52    })
53}
54
55pub fn note_nested_slots_host(host: &std::rc::Rc<crate::SlotsHost>) {
56    let Some(composer) = current_composer() else {
57        return;
58    };
59    let holder = composer.active_slots_host();
60    if std::rc::Rc::ptr_eq(&holder, host) {
61        return;
62    }
63    holder.note_nested_host(host);
64}
65
66/// Try to access the current composer from the thread-local stack.
67/// Returns None if there is no active composer.
68pub fn try_with_composer<R>(f: impl FnOnce(&Composer) -> R) -> Option<R> {
69    current_composer().map(|composer| f(&composer))
70}