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}
23
24#[derive(Debug)]
31pub struct ChangeTracker {
32 inner: Cell<*mut BindingHolder>,
34}
35
36impl Default for ChangeTracker {
37 fn default() -> Self {
38 Self { inner: Cell::new(core::ptr::null_mut()) }
39 }
40}
41
42impl Drop for ChangeTracker {
43 fn drop(&mut self) {
44 self.clear();
45 }
46}
47
48impl ChangeTracker {
49 pub fn init<Data, T: Default + PartialEq, EF: Fn(&Data) -> T, NF: Fn(&Data, &T)>(
55 &self,
56 data: Data,
57 eval_fn: EF,
58 notify_fn: NF,
59 ) {
60 self.init_impl(data, eval_fn, notify_fn, false);
61 }
62
63 pub fn init_delayed<Data, T: Default + PartialEq, EF: Fn(&Data) -> T, NF: Fn(&Data, &T)>(
69 &self,
70 data: Data,
71 eval_fn: EF,
72 notify_fn: NF,
73 ) {
74 self.init_impl(data, eval_fn, notify_fn, true);
75 }
76
77 fn init_impl<Data, T: Default + PartialEq, EF: Fn(&Data) -> T, NF: Fn(&Data, &T)>(
78 &self,
79 data: Data,
80 eval_fn: EF,
81 notify_fn: NF,
82 delayed: bool,
83 ) {
84 self.clear();
85 let inner = ChangeTrackerInner { eval_fn, notify_fn, value: T::default(), data };
86
87 unsafe fn evaluate<T: PartialEq, EF: Fn(&Data) -> T, NF: Fn(&Data, &T), Data>(
88 _self: *mut BindingHolder,
89 _value: *mut (),
90 ) -> BindingResult {
91 let pinned_holder = Pin::new_unchecked(&*_self);
92 let _self = _self as *mut BindingHolder<ChangeTrackerInner<T, EF, NF, Data>>;
93 let inner = core::ptr::addr_of_mut!((*_self).binding).as_mut().unwrap();
94 let new_value =
95 super::CURRENT_BINDING.set(Some(pinned_holder), || (inner.eval_fn)(&inner.data));
96 if new_value != inner.value {
97 inner.value = new_value;
98 (inner.notify_fn)(&inner.data, &inner.value);
99 }
100 BindingResult::KeepBinding
101 }
102
103 unsafe fn drop<T, EF, NF, Data>(_self: *mut BindingHolder) {
104 core::mem::drop(Box::from_raw(
105 _self as *mut BindingHolder<ChangeTrackerInner<T, EF, NF, Data>>,
106 ));
107 }
108
109 trait HasBindingVTable {
110 const VT: &'static BindingVTable;
111 }
112 impl<T: PartialEq, EF: Fn(&Data) -> T, NF: Fn(&Data, &T), Data> HasBindingVTable
113 for ChangeTrackerInner<T, EF, NF, Data>
114 {
115 const VT: &'static BindingVTable = &BindingVTable {
116 drop: drop::<T, EF, NF, Data>,
117 evaluate: evaluate::<T, EF, NF, Data>,
118 mark_dirty: ChangeTracker::mark_dirty,
119 intercept_set: |_, _| false,
120 intercept_set_binding: |_, _| false,
121 };
122 }
123 let holder = BindingHolder {
124 dependencies: Cell::new(0),
125 dep_nodes: Default::default(),
126 vtable: <ChangeTrackerInner<T, EF, NF, Data> as HasBindingVTable>::VT,
127 dirty: Cell::new(false),
128 is_two_way_binding: false,
129 pinned: PhantomPinned,
130 binding: inner,
131 #[cfg(slint_debug_property)]
132 debug_name: "<ChangeTracker>".into(),
133 };
134
135 let raw = Box::into_raw(Box::new(holder));
136 unsafe { self.set_internal(raw as *mut BindingHolder) };
137 if delayed {
138 let mut dep_nodes = SingleLinkedListPinHead::default();
139 let node = dep_nodes.push_front(DependencyNode::new(raw as *const BindingHolder));
140 CHANGED_NODES.with(|changed_nodes| {
141 changed_nodes.append(node);
142 });
143 unsafe { (*core::ptr::addr_of_mut!((*raw).dep_nodes)).set(dep_nodes) };
144 return;
145 }
146 let value = unsafe {
147 let pinned_holder = Pin::new_unchecked((raw as *mut BindingHolder).as_ref().unwrap());
148 let inner = core::ptr::addr_of!((*raw).binding).as_ref().unwrap();
149 super::CURRENT_BINDING.set(Some(pinned_holder), || (inner.eval_fn)(&inner.data))
150 };
151 unsafe { core::ptr::addr_of_mut!((*raw).binding).as_mut().unwrap().value = value };
152 }
153
154 pub fn clear(&self) {
157 let inner = self.inner.get();
158 if !inner.is_null() {
159 unsafe {
160 let drop = (*core::ptr::addr_of!((*inner).vtable)).drop;
161 drop(inner);
162 }
163 self.inner.set(core::ptr::null_mut());
164 }
165 }
166
167 pub fn run_change_handlers() {
169 CHANGED_NODES.with(|list| {
170 let old_list = DependencyListHead::default();
171 let old_list = core::pin::pin!(old_list);
172 let mut count = 0;
173 while !list.is_empty() {
174 count += 1;
175 if count > 9 {
176 crate::debug_log!("Slint: long changed callback chain detected");
177 return;
178 }
179 DependencyListHead::swap(list.as_ref(), old_list.as_ref());
180 old_list.for_each(|node| {
181 let node = *node;
182 unsafe {
183 ((*addr_of!((*node).vtable)).evaluate)(
184 node as *mut BindingHolder,
185 core::ptr::null_mut(),
186 );
187 }
188 });
189 old_list.as_ref().clear();
190 }
191 });
192 }
193
194 pub(super) unsafe fn mark_dirty(_self: *const BindingHolder, _was_dirty: bool) {
195 let _self = _self.as_ref().unwrap();
197 let node_head = _self.dep_nodes.take();
198 if let Some(node) = node_head.iter().next() {
199 node.remove();
200 CHANGED_NODES.with(|changed_nodes| {
201 changed_nodes.append(node);
202 });
203 }
204 _self.dep_nodes.set(node_head);
205 }
206
207 pub(super) unsafe fn set_internal(&self, raw: *mut BindingHolder) {
208 self.inner.set(raw);
209 }
210}
211
212#[test]
213fn change_tracker() {
214 use super::Property;
215 use std::rc::Rc;
216 let prop1 = Rc::pin(Property::new(42));
217 let prop2 = Rc::pin(Property::<i32>::default());
218 prop2.as_ref().set_binding({
219 let prop1 = prop1.clone();
220 move || prop1.as_ref().get() * 2
221 });
222
223 let change1 = ChangeTracker::default();
224 let change2 = ChangeTracker::default();
225
226 let state = Rc::new(core::cell::RefCell::new(std::string::String::new()));
227
228 change1.init(
229 (state.clone(), prop1.clone()),
230 |(_, prop1)| prop1.as_ref().get(),
231 |(state, _), val| {
232 *state.borrow_mut() += &std::format!(":1({val})");
233 },
234 );
235 change2.init(
236 (state.clone(), prop2.clone()),
237 |(_, prop2)| prop2.as_ref().get(),
238 |(state, _), val| {
239 *state.borrow_mut() += &std::format!(":2({val})");
240 },
241 );
242
243 assert_eq!(state.borrow().as_str(), "");
244 prop1.as_ref().set(10);
245 assert_eq!(state.borrow().as_str(), "");
246 prop1.as_ref().set(30);
247 assert_eq!(state.borrow().as_str(), "");
248
249 ChangeTracker::run_change_handlers();
250 assert_eq!(state.borrow().as_str(), ":1(30):2(60)");
251 ChangeTracker::run_change_handlers();
252 assert_eq!(state.borrow().as_str(), ":1(30):2(60)");
253 prop1.as_ref().set(1);
254 assert_eq!(state.borrow().as_str(), ":1(30):2(60)");
255 ChangeTracker::run_change_handlers();
256 assert_eq!(state.borrow().as_str(), ":1(30):2(60):1(1):2(2)");
257}