1use super::{BindingHolder, BindingResult, BindingVTable, DependencyListHead, DependencyNode};
5use alloc::boxed::Box;
6use core::cell::{Cell, UnsafeCell};
7use core::ffi::c_void;
8use core::marker::PhantomPinned;
9use core::pin::Pin;
10use core::ptr::addr_of;
11
12crate::thread_local! {static CHANGED_NODES : Pin<Box<DependencyListHead>> = Box::pin(DependencyListHead::default()) }
14
15struct ChangeTrackerInner<T, EvalFn, NotifyFn, Data> {
16 eval_fn: EvalFn,
17 notify_fn: NotifyFn,
18 value: UnsafeCell<T>,
20 data: Data,
21 evaluating: Cell<bool>,
23}
24
25#[derive(Debug)]
32pub struct ChangeTracker {
33 inner: Cell<*mut BindingHolder>,
35}
36
37impl Default for ChangeTracker {
38 fn default() -> Self {
39 Self { inner: Cell::new(core::ptr::null_mut()) }
40 }
41}
42
43impl Drop for ChangeTracker {
44 fn drop(&mut self) {
45 self.clear();
46 }
47}
48
49impl ChangeTracker {
50 pub fn init<
56 Data: 'static,
57 T: Default + PartialEq,
58 EF: Fn(&Data) -> T + 'static,
59 NF: Fn(&Data, &T) + 'static,
60 >(
61 &self,
62 data: Data,
63 eval_fn: EF,
64 notify_fn: NF,
65 ) {
66 self.init_impl(data, eval_fn, notify_fn, false);
67 }
68
69 pub fn init_delayed<
75 Data: 'static,
76 T: Default + PartialEq,
77 EF: Fn(&Data) -> T + 'static,
78 NF: Fn(&Data, &T) + 'static,
79 >(
80 &self,
81 data: Data,
82 eval_fn: EF,
83 notify_fn: NF,
84 ) {
85 self.init_impl(data, eval_fn, notify_fn, true);
86 }
87
88 fn init_impl<
89 Data: 'static,
90 T: Default + PartialEq,
91 EF: Fn(&Data) -> T + 'static,
92 NF: Fn(&Data, &T) + 'static,
93 >(
94 &self,
95 data: Data,
96 eval_fn: EF,
97 notify_fn: NF,
98 delayed: bool,
99 ) {
100 self.clear();
101 let inner = ChangeTrackerInner {
102 eval_fn,
103 notify_fn,
104 value: T::default().into(),
105 data,
106 evaluating: false.into(),
107 };
108
109 unsafe fn evaluate<
110 T: PartialEq,
111 EF: Fn(&Data) -> T + 'static,
112 NF: Fn(&Data, &T) + 'static,
113 Data: 'static,
114 >(
115 _self: *const BindingHolder,
116 _value: *mut c_void,
117 ) -> BindingResult {
118 unsafe {
119 let _self_raw = _self;
120 let _self = _self as *const BindingHolder<ChangeTrackerInner<T, EF, NF, Data>>;
121 let inner = core::ptr::addr_of!((*_self).binding).as_ref().unwrap();
122 *(*core::ptr::addr_of!((*_self).dep_nodes)).get() = Default::default();
123 assert!(!inner.evaluating.get());
124 inner.evaluating.set(true);
125 let new_value = super::current_binding_storage::set(Some(_self_raw), || {
126 (inner.eval_fn)(&inner.data)
127 });
128 {
129 let inner_value = &mut *inner.value.get();
131 if new_value != *inner_value {
132 *inner_value = new_value;
133 (inner.notify_fn)(&inner.data, inner_value);
134 }
135 }
136
137 if !inner.evaluating.replace(false) {
138 core::mem::drop(Box::from_raw(
140 _self as *mut BindingHolder<ChangeTrackerInner<T, EF, NF, Data>>,
141 ));
142 }
143 BindingResult::KeepBinding
144 }
145 }
146
147 unsafe fn drop<T, EF, NF, Data>(_self: *mut BindingHolder) {
148 unsafe {
149 let _self = _self as *mut BindingHolder<ChangeTrackerInner<T, EF, NF, Data>>;
150 let evaluating = core::ptr::addr_of!((*_self).binding)
151 .as_ref()
152 .unwrap()
153 .evaluating
154 .replace(false);
155 if !evaluating {
156 core::mem::drop(Box::from_raw(_self));
157 }
158 }
159 }
160
161 trait HasBindingVTable {
162 const VT: &'static BindingVTable;
163 }
164 impl<T: PartialEq, EF: Fn(&Data) -> T + 'static, NF: Fn(&Data, &T) + 'static, Data: 'static>
165 HasBindingVTable for ChangeTrackerInner<T, EF, NF, Data>
166 {
167 const VT: &'static BindingVTable = &BindingVTable {
168 drop: drop::<T, EF, NF, Data>,
169 evaluate: evaluate::<T, EF, NF, Data>,
170 mark_dirty: ChangeTracker::mark_dirty,
171 intercept_set: |_, _| false,
172 intercept_set_binding: |_, _| false,
173 };
174 }
175 let holder = BindingHolder {
176 dependencies: Cell::new(core::ptr::null_mut()),
177 dep_nodes: Default::default(),
178 vtable: <ChangeTrackerInner<T, EF, NF, Data> as HasBindingVTable>::VT,
179 dirty: Cell::new(false),
180 is_two_way_binding: false,
181 pinned: PhantomPinned,
182 binding: inner,
183 #[cfg(slint_debug_property)]
184 debug_name: "<ChangeTracker>".into(),
185 };
186
187 let raw = Box::into_raw(Box::new(holder));
188 unsafe { self.set_internal(raw as *mut BindingHolder) };
189 if delayed {
190 unsafe {
192 let dep_nodes = &mut *(*core::ptr::addr_of!((*raw).dep_nodes)).get();
193 let node = dep_nodes.push_front(DependencyNode::new(raw as *const BindingHolder));
194 CHANGED_NODES.with(|changed_nodes| {
195 changed_nodes.append(node);
196 });
197 }
198 return;
199 }
200 let value = unsafe {
201 let inner = core::ptr::addr_of!((*raw).binding).as_ref().unwrap();
202 super::current_binding_storage::set(Some(raw as *const BindingHolder), || {
203 (inner.eval_fn)(&inner.data)
204 })
205 };
206 unsafe {
207 *core::ptr::addr_of_mut!((*raw).binding).as_mut().unwrap().value.get_mut() = value
208 };
209 }
210
211 #[cfg(test)]
213 pub(crate) fn test_dep_node_count(&self) -> usize {
214 let inner = self.inner.get();
215 if inner.is_null() {
216 return 0;
217 }
218 unsafe { (*(*core::ptr::addr_of!((*inner).dep_nodes)).get()).iter().count() }
219 }
220
221 pub fn clear(&self) {
224 let inner = self.inner.get();
225 if !inner.is_null() {
226 unsafe {
227 let drop = (*core::ptr::addr_of!((*inner).vtable)).drop;
228 drop(inner);
229 }
230 self.inner.set(core::ptr::null_mut());
231 }
232 }
233
234 pub fn run_change_handlers() {
237 for _ in 0..10 {
238 if !Self::run_change_handlers_once() {
239 return;
240 }
241 }
242 crate::debug_log!("Slint: long changed callback chain detected");
243 }
244
245 pub fn run_change_handlers_once() -> bool {
248 CHANGED_NODES.with(|list| {
249 if list.is_empty() {
250 return false;
251 }
252 let old_list = DependencyListHead::default();
253 let old_list = core::pin::pin!(old_list);
254 DependencyListHead::swap(list.as_ref(), old_list.as_ref());
255 while let Some(node) = old_list.take_head() {
256 unsafe {
257 ((*addr_of!((*node).vtable)).evaluate)(
258 node as *mut BindingHolder,
259 core::ptr::null_mut(),
260 );
261 }
262 }
263 true
264 })
265 }
266
267 pub(super) unsafe fn mark_dirty(_self: *const BindingHolder, _was_dirty: bool) {
268 unsafe {
269 let dep_nodes = &mut *(*_self).dep_nodes.get();
271 let node_head = core::mem::take(dep_nodes);
272 if let Some(node) = node_head.iter().next() {
273 node.remove();
274 CHANGED_NODES.with(|changed_nodes| {
275 changed_nodes.append(node);
276 });
277 }
278 let other = core::mem::replace(dep_nodes, node_head);
280 debug_assert!(other.iter().next().is_none());
281 }
282 }
283
284 pub(super) unsafe fn set_internal(&self, raw: *mut BindingHolder) {
285 self.inner.set(raw);
286 }
287}
288
289#[test]
290fn change_tracker() {
291 use super::Property;
292 use std::rc::Rc;
293 let prop1 = Rc::pin(Property::new(42));
294 let prop2 = Rc::pin(Property::<i32>::default());
295 prop2.as_ref().set_binding({
296 let prop1 = prop1.clone();
297 move || prop1.as_ref().get() * 2
298 });
299
300 let change1 = ChangeTracker::default();
301 let change2 = ChangeTracker::default();
302
303 let state = Rc::new(core::cell::RefCell::new(std::string::String::new()));
304
305 change1.init(
306 (state.clone(), prop1.clone()),
307 |(_, prop1)| prop1.as_ref().get(),
308 |(state, _), val| {
309 *state.borrow_mut() += &std::format!(":1({val})");
310 },
311 );
312 change2.init(
313 (state.clone(), prop2.clone()),
314 |(_, prop2)| prop2.as_ref().get(),
315 |(state, _), val| {
316 *state.borrow_mut() += &std::format!(":2({val})");
317 },
318 );
319
320 assert_eq!(state.borrow().as_str(), "");
321 prop1.as_ref().set(10);
322 assert_eq!(state.borrow().as_str(), "");
323 prop1.as_ref().set(30);
324 assert_eq!(state.borrow().as_str(), "");
325
326 ChangeTracker::run_change_handlers();
327 assert_eq!(state.borrow().as_str(), ":1(30):2(60)");
328 ChangeTracker::run_change_handlers();
329 assert_eq!(state.borrow().as_str(), ":1(30):2(60)");
330 prop1.as_ref().set(1);
331 assert_eq!(state.borrow().as_str(), ":1(30):2(60)");
332 ChangeTracker::run_change_handlers();
333 assert_eq!(state.borrow().as_str(), ":1(30):2(60):1(1):2(2)");
334}
335
336#[test]
338fn delete_from_eval_fn() {
339 use std::cell::RefCell;
340 use std::rc::Rc;
341 use std::string::String;
342
343 let change = Rc::<RefCell<Option<ChangeTracker>>>::new(Some(ChangeTracker::default()).into());
344 let xyz = RefCell::new(String::from("*"));
345 let result = Rc::new(RefCell::new(String::new()));
346 let result2 = result.clone();
347 let another = Rc::<RefCell<Option<ChangeTracker>>>::new(Some(ChangeTracker::default()).into());
349 another.borrow().as_ref().unwrap().init_delayed(
350 (),
351 |()| unreachable!(),
352 move |(), &()| unreachable!(),
353 );
354 change.borrow().as_ref().unwrap().init_delayed(
355 change.clone(),
356 |x| {
357 x.borrow_mut().take().unwrap();
358 String::from("hi")
359 },
360 move |x, val| {
361 assert!(x.borrow().is_none());
362 assert_eq!(val, "hi");
363 xyz.borrow_mut().push_str("+");
364 assert!(xyz.borrow().as_str().starts_with("*+"));
365 result2.replace(xyz.borrow().clone());
366 another.borrow_mut().take().unwrap();
367 },
368 );
369
370 assert_eq!(result.borrow().as_str(), "");
371 ChangeTracker::run_change_handlers();
372 assert_eq!(result.borrow().as_str(), "*+");
373 ChangeTracker::run_change_handlers();
374 assert_eq!(result.borrow().as_str(), "*+");
375}
376
377#[test]
378fn change_multiple_dependencies() {
379 use super::Property;
380 use std::cell::RefCell;
381 use std::rc::Rc;
382 use std::string::String;
383 let prop1 = Rc::pin(Property::new(1));
384 let prop2 = Rc::pin(Property::new(2));
385 let prop3 = Rc::pin(Property::new(3));
386 let prop4 = Rc::pin(Property::new(4));
387 let prop_with_deps = Rc::pin(Property::new(5));
388 let result = Rc::new(RefCell::new(String::new()));
389
390 let change_tracker = ChangeTracker::default();
391 change_tracker.init(
392 result.clone(),
393 {
394 let prop1 = prop1.clone();
395 let prop2 = prop2.clone();
396 let prop3 = prop3.clone();
397 let prop4 = prop4.clone();
398 let prop_with_deps = prop_with_deps.clone();
399 move |_| {
400 prop1.as_ref().get()
401 + prop2.as_ref().get()
402 + prop3.as_ref().get()
403 + prop4.as_ref().get()
404 + prop_with_deps.as_ref().get()
405 }
406 },
407 move |result, val| {
408 *result.borrow_mut() += &std::format!("[{val}]");
409 },
410 );
411
412 assert_eq!(result.borrow().as_str(), "");
413 ChangeTracker::run_change_handlers();
414 assert_eq!(result.borrow().as_str(), "");
415
416 prop_with_deps.as_ref().set_binding({
417 let prop1 = prop1.clone();
418 let prop2 = prop2.clone();
419 move || prop1.as_ref().get() + prop2.as_ref().get()
420 });
421
422 assert_eq!(result.borrow().as_str(), "");
423 ChangeTracker::run_change_handlers();
424 assert_eq!(prop_with_deps.as_ref().get(), 3);
425 assert_eq!(result.borrow().as_str(), "[13]"); ChangeTracker::run_change_handlers();
428 assert_eq!(result.borrow().as_str(), "[13]");
429
430 prop1.as_ref().set(10);
431 assert_eq!(result.borrow().as_str(), "[13]");
432 ChangeTracker::run_change_handlers();
433 assert_eq!(result.borrow().as_str(), "[13][31]"); prop2.as_ref().set(20);
436 prop3.as_ref().set(30);
437 assert_eq!(result.borrow().as_str(), "[13][31]");
438 ChangeTracker::run_change_handlers();
439 assert_eq!(result.borrow().as_str(), "[13][31][94]"); ChangeTracker::run_change_handlers();
442 assert_eq!(result.borrow().as_str(), "[13][31][94]");
443
444 prop1.as_ref().set(20);
446 prop2.as_ref().set(10);
447 ChangeTracker::run_change_handlers();
448 assert_eq!(result.borrow().as_str(), "[13][31][94]");
449}