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