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