euv_core/reactive/effect/fn.rs
1use crate::*;
2
3/// Creates a new `RenderEffect` that automatically tracks signal dependencies
4/// and re-executes when any dependency changes.
5///
6/// This is the public API for creating reactive effects. During the first
7/// execution, any `Signal::get()` calls establish dependencies. When those
8/// signals change, the closure is re-executed with fresh dependency tracking.
9///
10/// # Arguments
11///
12/// - `F` - The closure to execute reactively.
13///
14/// # Returns
15///
16/// - `RenderEffect` - A handle to the created effect.
17pub fn create_render_effect<F>(effect_fn: F) -> RenderEffect
18where
19 F: FnMut() + 'static,
20{
21 RenderEffect::create(effect_fn)
22}
23
24/// Removes all effect subscriber entries for the given effect from the
25/// global registry and clears the effect's dependency list.
26///
27/// Called at the beginning of each `run_once` to clean up old
28/// subscriptions before re-tracking fresh dependencies.
29///
30/// # Arguments
31///
32/// - `usize` - The effect's inner pointer address.
33/// - `&mut Vec<usize>` - The effect's current dependency list (will be cleared).
34pub(crate) fn cleanup_effect_dependencies(effect_addr: usize, dependencies: &mut Vec<usize>) {
35 let registry: &mut HashMap<usize, Vec<usize>> = effect_subscribers_mut();
36 for signal_addr in dependencies.iter() {
37 if let Some(subscribers) = registry.get_mut(signal_addr) {
38 subscribers.retain(|addr| *addr != effect_addr);
39 if subscribers.is_empty() {
40 registry.remove(signal_addr);
41 }
42 }
43 }
44 dependencies.clear();
45}
46
47/// Ensures the effect subscribers registry is initialized and returns a mutable reference.
48///
49/// SAFETY: Must only be called from the main thread (WASM single-threaded context).
50#[allow(static_mut_refs)]
51fn ensure_effect_subscribers_mut() -> &'static mut HashMap<usize, Vec<usize>> {
52 unsafe {
53 if (*EFFECT_SUBSCRIBERS.get_0().get()).is_none() {
54 (*EFFECT_SUBSCRIBERS.get_0().get()) = Some(HashMap::new());
55 }
56 (*EFFECT_SUBSCRIBERS.get_0().get())
57 .as_mut()
58 .unwrap_unchecked()
59 }
60}
61
62/// Returns a mutable reference to the effect subscribers registry.
63///
64/// SAFETY: Must only be called from the main thread (WASM single-threaded context).
65#[allow(static_mut_refs)]
66pub(crate) fn effect_subscribers_mut() -> &'static mut HashMap<usize, Vec<usize>> {
67 ensure_effect_subscribers_mut()
68}
69
70/// Sets the currently active RenderEffect address.
71///
72/// # Arguments
73///
74/// - `Option<usize>`: The effect address to set, or `None` to clear.
75#[allow(static_mut_refs)]
76pub(crate) fn set_current_effect(addr: Option<usize>) {
77 unsafe {
78 *CURRENT_EFFECT.get_0().get() = addr;
79 }
80}
81
82/// Atomically swaps the current RenderEffect address with a new value.
83///
84/// # Arguments
85///
86/// - `Option<usize>`: The new effect address to set.
87///
88/// # Returns
89///
90/// - `Option<usize>`: The previous effect address that was replaced.
91#[allow(static_mut_refs)]
92pub(crate) fn swap_current_effect(new_addr: Option<usize>) -> Option<usize> {
93 unsafe {
94 let old: Option<usize> = *CURRENT_EFFECT.get_0().get();
95 *CURRENT_EFFECT.get_mut_0().get() = new_addr;
96 old
97 }
98}
99
100/// Returns a mutable reference to the render effect registry.
101///
102/// SAFETY: Must only be called from the main thread (WASM single-threaded context).
103#[allow(static_mut_refs)]
104pub(crate) fn render_effect_registry_mut()
105-> &'static mut HashMap<usize, Rc<RefCell<RenderEffectInner>>> {
106 unsafe {
107 if (*RENDER_EFFECT_REGISTRY.get_0().get()).is_none() {
108 (*RENDER_EFFECT_REGISTRY.get_0().get()) = Some(HashMap::new());
109 }
110 (*RENDER_EFFECT_REGISTRY.get_0().get())
111 .as_mut()
112 .unwrap_unchecked()
113 }
114}