i_slint_core/model/
model_peer.rs1#![allow(unsafe_code)]
10
11use super::*;
12use crate::properties::dependency_tracker::DependencyNode;
13
14type DependencyListHead =
15 crate::properties::dependency_tracker::DependencyListHead<*const dyn ModelChangeListener>;
16
17#[derive(Clone)]
22pub struct ModelPeer<'a> {
23 inner: Pin<&'a DependencyNode<*const dyn ModelChangeListener>>,
24}
25
26enum TrackedRows {
29 Rows(Vec<usize>),
31 All,
33}
34
35impl Default for TrackedRows {
36 fn default() -> Self {
37 TrackedRows::Rows(Vec::new())
38 }
39}
40
41impl TrackedRows {
42 fn is_tracked(&self, row: usize) -> bool {
43 match self {
44 TrackedRows::Rows(rows) => rows.binary_search(&row).is_ok(),
45 TrackedRows::All => true,
46 }
47 }
48}
49
50#[pin_project]
51#[derive(Default)]
52struct ModelNotifyInner {
53 #[pin]
54 model_row_count_dirty_property: Property<()>,
55 #[pin]
56 model_row_data_dirty_property: Property<()>,
57 #[pin]
58 peers: DependencyListHead,
59 tracked_rows: RefCell<TrackedRows>,
60}
61
62#[derive(Default)]
65pub struct ModelNotify {
66 inner: Pin<Box<ModelNotifyInner>>,
67}
68
69impl ModelNotify {
70 fn inner(&self) -> Pin<&ModelNotifyInner> {
71 self.inner.as_ref()
72 }
73
74 pub fn row_changed(&self, row: usize) {
76 let inner = &self.inner;
77 if inner.tracked_rows.borrow().is_tracked(row) {
78 inner.model_row_data_dirty_property.mark_dirty();
79 }
80 inner.as_ref().project_ref().peers.for_each(|p| {
81 unsafe { Pin::new_unchecked(&**p) }.row_changed(row)
83 })
84 }
85 pub fn row_added(&self, index: usize, count: usize) {
87 let inner = &self.inner;
88 inner.model_row_count_dirty_property.mark_dirty();
89 *inner.tracked_rows.borrow_mut() = TrackedRows::default();
90 inner.model_row_data_dirty_property.mark_dirty();
91 inner.as_ref().project_ref().peers.for_each(|p| {
92 unsafe { Pin::new_unchecked(&**p) }.row_added(index, count)
94 })
95 }
96 pub fn row_removed(&self, index: usize, count: usize) {
98 let inner = &self.inner;
99 inner.model_row_count_dirty_property.mark_dirty();
100 *inner.tracked_rows.borrow_mut() = TrackedRows::default();
101 inner.model_row_data_dirty_property.mark_dirty();
102 inner.as_ref().project_ref().peers.for_each(|p| {
103 unsafe { Pin::new_unchecked(&**p) }.row_removed(index, count)
105 })
106 }
107
108 pub fn reset(&self) {
111 let inner = &self.inner;
112 inner.model_row_count_dirty_property.mark_dirty();
113 *inner.tracked_rows.borrow_mut() = TrackedRows::default();
114 inner.model_row_data_dirty_property.mark_dirty();
115 inner.as_ref().project_ref().peers.for_each(|p| {
116 unsafe { Pin::new_unchecked(&**p) }.reset()
118 })
119 }
120}
121
122impl ModelTracker for ModelNotify {
123 fn attach_peer(&self, peer: ModelPeer) {
125 self.inner().project_ref().peers.append(peer.inner)
126 }
127
128 fn track_row_count_changes(&self) {
129 self.inner().project_ref().model_row_count_dirty_property.get();
130 }
131
132 fn track_row_data_changes(&self, row: usize) {
133 if crate::properties::is_currently_tracking() {
134 let inner = self.inner().project_ref();
135
136 if let TrackedRows::Rows(rows) = &mut *inner.tracked_rows.borrow_mut()
138 && let Err(insertion_point) = rows.binary_search(&row)
139 {
140 rows.insert(insertion_point, row);
141 }
142
143 inner.model_row_data_dirty_property.get();
144 }
145 }
146
147 fn track_any_change(&self, _row_count: usize, _: crate::InternalToken) {
148 self.track_row_count_changes();
149 if crate::properties::is_currently_tracking() {
150 let inner = self.inner().project_ref();
151 *inner.tracked_rows.borrow_mut() = TrackedRows::All;
153 inner.model_row_data_dirty_property.get();
154 }
155 }
156}
157
158pub trait ModelChangeListener {
159 fn row_changed(self: Pin<&Self>, row: usize);
160 fn row_added(self: Pin<&Self>, index: usize, count: usize);
161 fn row_removed(self: Pin<&Self>, index: usize, count: usize);
162 fn reset(self: Pin<&Self>);
163}
164
165#[pin_project(PinnedDrop)]
166#[derive(Default, derive_more::Deref)]
167pub struct ModelChangeListenerContainer<T: ModelChangeListener> {
170 peer: OnceCell<DependencyNode<*const dyn ModelChangeListener>>,
173
174 #[pin]
175 #[deref]
176 data: T,
177}
178
179#[pin_project::pinned_drop]
180impl<T: ModelChangeListener> PinnedDrop for ModelChangeListenerContainer<T> {
181 fn drop(self: Pin<&mut Self>) {
182 if let Some(peer) = self.peer.get() {
183 peer.remove();
184 }
185 }
186}
187
188impl<T: ModelChangeListener + 'static> ModelChangeListenerContainer<T> {
189 pub fn new(data: T) -> Self {
190 Self { peer: Default::default(), data }
191 }
192
193 pub fn model_peer(self: Pin<&Self>) -> ModelPeer<'_> {
194 let peer = self.get_ref().peer.get_or_init(|| {
195 DependencyNode::new(
197 (&self.data) as &dyn ModelChangeListener as *const dyn ModelChangeListener,
198 )
199 });
200
201 let peer = unsafe { Pin::new_unchecked(peer) };
203
204 ModelPeer { inner: peer }
205 }
206
207 pub fn get(self: Pin<&Self>) -> Pin<&T> {
208 self.project_ref().data
209 }
210}
211
212pub struct ModelChangeListenerBox<T: ModelChangeListener + 'static> {
215 ptr: core::ptr::NonNull<ModelChangeListenerContainer<T>>,
216}
217
218impl<T: ModelChangeListener + 'static> ModelChangeListenerBox<T> {
219 pub fn new(data: T) -> Self {
220 let container = ModelChangeListenerContainer::new(data);
221 let ptr = unsafe { core::ptr::NonNull::new_unchecked(Box::into_raw(Box::new(container))) };
223 Self { ptr }
224 }
225
226 pub fn as_ref(&self) -> Pin<&ModelChangeListenerContainer<T>> {
227 unsafe { Pin::new_unchecked(self.ptr.as_ref()) }
229 }
230}
231
232impl<T: ModelChangeListener + 'static> core::ops::Deref for ModelChangeListenerBox<T> {
233 type Target = T;
234 fn deref(&self) -> &T {
235 unsafe { &self.ptr.as_ref().data }
237 }
238}
239
240impl<T: ModelChangeListener + 'static> Drop for ModelChangeListenerBox<T> {
241 fn drop(&mut self) {
242 unsafe { drop(Box::from_raw(self.ptr.as_ptr())) }
245 }
246}