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
26#[pin_project]
27#[derive(Default)]
28struct ModelNotifyInner {
29 #[pin]
30 model_row_count_dirty_property: Property<()>,
31 #[pin]
32 model_row_data_dirty_property: Property<()>,
33 #[pin]
34 peers: DependencyListHead,
35 tracked_rows: RefCell<Vec<usize>>,
37}
38
39#[derive(Default)]
42pub struct ModelNotify {
43 inner: Pin<Box<ModelNotifyInner>>,
44}
45
46impl ModelNotify {
47 fn inner(&self) -> Pin<&ModelNotifyInner> {
48 self.inner.as_ref()
49 }
50
51 pub fn row_changed(&self, row: usize) {
53 let inner = &self.inner;
54 if inner.tracked_rows.borrow().binary_search(&row).is_ok() {
55 inner.model_row_data_dirty_property.mark_dirty();
56 }
57 inner.as_ref().project_ref().peers.for_each(|p| {
58 unsafe { Pin::new_unchecked(&**p) }.row_changed(row)
60 })
61 }
62 pub fn row_added(&self, index: usize, count: usize) {
64 let inner = &self.inner;
65 inner.model_row_count_dirty_property.mark_dirty();
66 inner.tracked_rows.borrow_mut().clear();
67 inner.model_row_data_dirty_property.mark_dirty();
68 inner.as_ref().project_ref().peers.for_each(|p| {
69 unsafe { Pin::new_unchecked(&**p) }.row_added(index, count)
71 })
72 }
73 pub fn row_removed(&self, index: usize, count: usize) {
75 let inner = &self.inner;
76 inner.model_row_count_dirty_property.mark_dirty();
77 inner.tracked_rows.borrow_mut().clear();
78 inner.model_row_data_dirty_property.mark_dirty();
79 inner.as_ref().project_ref().peers.for_each(|p| {
80 unsafe { Pin::new_unchecked(&**p) }.row_removed(index, count)
82 })
83 }
84
85 pub fn reset(&self) {
88 let inner = &self.inner;
89 inner.model_row_count_dirty_property.mark_dirty();
90 inner.tracked_rows.borrow_mut().clear();
91 inner.model_row_data_dirty_property.mark_dirty();
92 inner.as_ref().project_ref().peers.for_each(|p| {
93 unsafe { Pin::new_unchecked(&**p) }.reset()
95 })
96 }
97}
98
99impl ModelTracker for ModelNotify {
100 fn attach_peer(&self, peer: ModelPeer) {
102 self.inner().project_ref().peers.append(peer.inner)
103 }
104
105 fn track_row_count_changes(&self) {
106 self.inner().project_ref().model_row_count_dirty_property.get();
107 }
108
109 fn track_row_data_changes(&self, row: usize) {
110 if crate::properties::is_currently_tracking() {
111 let inner = self.inner().project_ref();
112
113 let mut tracked_rows = inner.tracked_rows.borrow_mut();
114 if let Err(insertion_point) = tracked_rows.binary_search(&row) {
115 tracked_rows.insert(insertion_point, row);
116 }
117
118 inner.model_row_data_dirty_property.get();
119 }
120 }
121}
122
123pub trait ModelChangeListener {
124 fn row_changed(self: Pin<&Self>, row: usize);
125 fn row_added(self: Pin<&Self>, index: usize, count: usize);
126 fn row_removed(self: Pin<&Self>, index: usize, count: usize);
127 fn reset(self: Pin<&Self>);
128}
129
130#[pin_project(PinnedDrop)]
131#[derive(Default, derive_more::Deref)]
132pub struct ModelChangeListenerContainer<T: ModelChangeListener> {
135 peer: OnceCell<DependencyNode<*const dyn ModelChangeListener>>,
138
139 #[pin]
140 #[deref]
141 data: T,
142}
143
144#[pin_project::pinned_drop]
145impl<T: ModelChangeListener> PinnedDrop for ModelChangeListenerContainer<T> {
146 fn drop(self: Pin<&mut Self>) {
147 if let Some(peer) = self.peer.get() {
148 peer.remove();
149 }
150 }
151}
152
153impl<T: ModelChangeListener + 'static> ModelChangeListenerContainer<T> {
154 pub fn new(data: T) -> Self {
155 Self { peer: Default::default(), data }
156 }
157
158 pub fn model_peer(self: Pin<&Self>) -> ModelPeer<'_> {
159 let peer = self.get_ref().peer.get_or_init(|| {
160 DependencyNode::new(
162 (&self.data) as &dyn ModelChangeListener as *const dyn ModelChangeListener,
163 )
164 });
165
166 let peer = unsafe { Pin::new_unchecked(peer) };
168
169 ModelPeer { inner: peer }
170 }
171
172 pub fn get(self: Pin<&Self>) -> Pin<&T> {
173 self.project_ref().data
174 }
175}
176
177pub struct ModelChangeListenerBox<T: ModelChangeListener + 'static> {
180 ptr: core::ptr::NonNull<ModelChangeListenerContainer<T>>,
181}
182
183impl<T: ModelChangeListener + 'static> ModelChangeListenerBox<T> {
184 pub fn new(data: T) -> Self {
185 let container = ModelChangeListenerContainer::new(data);
186 let ptr = unsafe { core::ptr::NonNull::new_unchecked(Box::into_raw(Box::new(container))) };
188 Self { ptr }
189 }
190
191 pub fn as_ref(&self) -> Pin<&ModelChangeListenerContainer<T>> {
192 unsafe { Pin::new_unchecked(self.ptr.as_ref()) }
194 }
195}
196
197impl<T: ModelChangeListener + 'static> core::ops::Deref for ModelChangeListenerBox<T> {
198 type Target = T;
199 fn deref(&self) -> &T {
200 unsafe { &self.ptr.as_ref().data }
202 }
203}
204
205impl<T: ModelChangeListener + 'static> Drop for ModelChangeListenerBox<T> {
206 fn drop(&mut self) {
207 unsafe { drop(Box::from_raw(self.ptr.as_ptr())) }
210 }
211}