cranpose_core/
callbacks.rs1use std::{cell::RefCell, rc::Rc};
2
3use crate::{Composer, ComposerCore, RecomposeScope, composer_context};
4
5pub struct ParamState<T> {
6 pub(crate) value: Option<T>,
7}
8
9pub trait SharedParam {
12 fn same_allocation(&self, other: &Self) -> bool;
14}
15
16impl<T: ?Sized> SharedParam for Rc<T> {
17 fn same_allocation(&self, other: &Self) -> bool {
18 Rc::ptr_eq(self, other)
19 }
20}
21
22impl<T: ?Sized> SharedParam for std::sync::Arc<T> {
23 fn same_allocation(&self, other: &Self) -> bool {
24 std::sync::Arc::ptr_eq(self, other)
25 }
26}
27
28impl<T> ParamState<T> {
29 pub fn update_fields(
34 &mut self,
35 fresh: impl FnOnce() -> T,
36 refresh: impl FnOnce(&mut T) -> bool,
37 ) -> bool {
38 match self.value.as_mut() {
39 Some(stored) => refresh(stored),
40 None => {
41 self.value = Some(fresh());
42 true
43 }
44 }
45 }
46
47 pub fn value(&self) -> Option<T>
48 where
49 T: Clone,
50 {
51 self.value.clone()
52 }
53}
54
55pub struct ParamReturnState<T, R> {
58 params: ParamState<T>,
59 returned: Option<R>,
60}
61
62impl<T, R> ParamReturnState<T, R> {
63 pub fn update_fields(
65 &mut self,
66 fresh: impl FnOnce() -> T,
67 refresh: impl FnOnce(&mut T) -> bool,
68 ) -> bool {
69 self.params.update_fields(fresh, refresh)
70 }
71
72 pub fn value(&self) -> Option<T>
73 where
74 T: Clone,
75 {
76 self.params.value()
77 }
78
79 pub fn returned(&self) -> Option<R>
81 where
82 R: Clone,
83 {
84 self.returned.clone()
85 }
86
87 pub fn store_return(&mut self, value: R) {
88 self.returned = Some(value);
89 }
90}
91
92impl<T, R> Default for ParamReturnState<T, R> {
93 fn default() -> Self {
94 Self {
95 params: ParamState::default(),
96 returned: None,
97 }
98 }
99}
100
101pub fn refresh_param<T: PartialEq + Clone>(stored: &mut T, new: &T) -> bool {
104 if stored == new {
105 return false;
106 }
107 stored.clone_from(new);
108 true
109}
110
111pub fn refresh_shared_param<T: SharedParam + PartialEq + Clone>(stored: &mut T, new: &T) -> bool {
115 if stored.same_allocation(new) || stored == new {
116 return false;
117 }
118 stored.clone_from(new);
119 true
120}
121
122pub struct ParamSlot<T> {
125 val: RefCell<Option<T>>,
126}
127
128impl<T> Default for ParamSlot<T> {
129 fn default() -> Self {
130 Self {
131 val: RefCell::new(None),
132 }
133 }
134}
135
136impl<T> ParamSlot<T> {
137 pub fn set(&self, v: T) {
138 *self.val.borrow_mut() = Some(v);
139 }
140
141 pub fn take(&self) -> Option<T> {
143 self.val.borrow_mut().take()
144 }
145}
146
147type CallbackCell = Rc<RefCell<Option<Box<dyn FnMut()>>>>;
148type CallbackScopeCell = Rc<RefCell<Option<RecomposeScope>>>;
149
150struct CallbackScopeGuard {
151 core: Rc<ComposerCore>,
152}
153
154impl CallbackScopeGuard {
155 fn push(composer: &Composer, scope: RecomposeScope) -> Self {
156 composer.core.scope_stack.borrow_mut().push(scope);
157 Self {
158 core: composer.clone_core(),
159 }
160 }
161}
162
163impl Drop for CallbackScopeGuard {
164 fn drop(&mut self) {
165 self.core.scope_stack.borrow_mut().pop();
166 }
167}
168
169fn with_callback_scope<R>(scope: &CallbackScopeCell, f: impl FnOnce() -> R) -> R {
170 let captured_scope = scope.borrow().clone();
171 if let Some(saved_scope) = captured_scope
172 && let Some(composer) = composer_context::current_composer()
173 {
174 let _scope_guard = CallbackScopeGuard::push(&composer, saved_scope);
175 return f();
176 }
177
178 f()
179}
180
181fn callback_owner_is_active(scope: &CallbackScopeCell) -> bool {
182 scope
183 .borrow()
184 .as_ref()
185 .is_none_or(RecomposeScope::is_effectively_active)
186}
187
188fn callback_owner_scope(composer: &Composer) -> Option<RecomposeScope> {
189 composer.core.scope_stack.borrow().last().cloned()
190}
191
192#[derive(Clone)]
193pub struct CallbackHolder {
194 rc: CallbackCell,
195 creator_scope: CallbackScopeCell,
196}
197
198impl CallbackHolder {
199 pub fn new() -> Self {
201 Self::default()
202 }
203
204 pub fn update<F>(&self, f: F)
206 where
207 F: FnMut() + 'static,
208 {
209 self.update_boxed(Box::new(f));
210 }
211
212 pub fn update_boxed(&self, f: Box<dyn FnMut() + 'static>) {
216 *self.rc.borrow_mut() = Some(f);
217 *self.creator_scope.borrow_mut() =
218 composer_context::try_with_composer(callback_owner_scope).flatten();
219 }
220
221 pub fn clone_rc(&self) -> impl Fn() + 'static + use<> {
223 let rc = self.rc.clone();
224 let creator_scope = self.creator_scope.clone();
225 move || {
226 if !callback_owner_is_active(&creator_scope) {
227 return;
228 }
229 with_callback_scope(&creator_scope, || {
230 if let Some(callback) = rc.borrow_mut().as_mut() {
231 callback();
232 }
233 });
234 }
235 }
236}
237
238impl Default for CallbackHolder {
239 fn default() -> Self {
240 Self {
241 rc: Rc::new(RefCell::new(None)),
242 creator_scope: Rc::new(RefCell::new(None)),
243 }
244 }
245}
246
247#[derive(Clone)]
250pub struct CallbackHolder1<A: 'static> {
251 #[expect(clippy::type_complexity)]
252 rc: Rc<RefCell<Option<Box<dyn FnMut(A)>>>>,
253 creator_scope: CallbackScopeCell,
254}
255
256impl<A: 'static> CallbackHolder1<A> {
257 pub fn new() -> Self {
259 Self::default()
260 }
261
262 pub fn update<F>(&self, f: F)
264 where
265 F: FnMut(A) + 'static,
266 {
267 *self.rc.borrow_mut() = Some(Box::new(f));
268 *self.creator_scope.borrow_mut() =
269 composer_context::try_with_composer(callback_owner_scope).flatten();
270 }
271
272 pub fn clone_rc(&self) -> impl Fn(A) + 'static {
274 let rc = self.rc.clone();
275 let creator_scope = self.creator_scope.clone();
276 move |arg| {
277 if !callback_owner_is_active(&creator_scope) {
278 return;
279 }
280 with_callback_scope(&creator_scope, || {
281 if let Some(callback) = rc.borrow_mut().as_mut() {
282 callback(arg);
283 }
284 });
285 }
286 }
287}
288
289impl<A: 'static> Default for CallbackHolder1<A> {
290 fn default() -> Self {
291 Self {
292 rc: Rc::new(RefCell::new(None)),
293 creator_scope: Rc::new(RefCell::new(None)),
294 }
295 }
296}
297
298pub struct ReturnSlot<T> {
299 value: Option<T>,
300}
301
302impl<T: Clone> ReturnSlot<T> {
303 pub fn store(&mut self, value: T) {
304 self.value = Some(value);
305 }
306
307 pub fn get(&self) -> Option<T> {
308 self.value.clone()
309 }
310}
311
312impl<T> Default for ParamState<T> {
313 fn default() -> Self {
314 Self { value: None }
315 }
316}
317
318impl<T> Default for ReturnSlot<T> {
319 fn default() -> Self {
320 Self { value: None }
321 }
322}
323
324#[cfg(test)]
325#[path = "tests/callbacks_callback_holder_tests.rs"]
326mod callback_holder_tests;
327
328#[cfg(test)]
329#[path = "tests/callbacks_param_state_tests.rs"]
330mod param_state_tests;