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: OnceCell<Pin<Box<ModelNotifyInner>>>,
44}
45
46impl ModelNotify {
47 fn inner(&self) -> Pin<&ModelNotifyInner> {
48 self.inner.get_or_init(|| Box::pin(ModelNotifyInner::default())).as_ref()
49 }
50
51 pub fn row_changed(&self, row: usize) {
53 if let Some(inner) = self.inner.get() {
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 }
63 pub fn row_added(&self, index: usize, count: usize) {
65 if let Some(inner) = self.inner.get() {
66 inner.model_row_count_dirty_property.mark_dirty();
67 inner.tracked_rows.borrow_mut().clear();
68 inner.model_row_data_dirty_property.mark_dirty();
69 inner.as_ref().project_ref().peers.for_each(|p| {
70 unsafe { Pin::new_unchecked(&**p) }.row_added(index, count)
72 })
73 }
74 }
75 pub fn row_removed(&self, index: usize, count: usize) {
77 if let Some(inner) = self.inner.get() {
78 inner.model_row_count_dirty_property.mark_dirty();
79 inner.tracked_rows.borrow_mut().clear();
80 inner.model_row_data_dirty_property.mark_dirty();
81 inner.as_ref().project_ref().peers.for_each(|p| {
82 unsafe { Pin::new_unchecked(&**p) }.row_removed(index, count)
84 })
85 }
86 }
87
88 pub fn reset(&self) {
91 if let Some(inner) = self.inner.get() {
92 inner.model_row_count_dirty_property.mark_dirty();
93 inner.tracked_rows.borrow_mut().clear();
94 inner.model_row_data_dirty_property.mark_dirty();
95 inner.as_ref().project_ref().peers.for_each(|p| {
96 unsafe { Pin::new_unchecked(&**p) }.reset()
98 })
99 }
100 }
101}
102
103impl ModelTracker for ModelNotify {
104 fn attach_peer(&self, peer: ModelPeer) {
106 self.inner().project_ref().peers.append(peer.inner)
107 }
108
109 fn track_row_count_changes(&self) {
110 self.inner().project_ref().model_row_count_dirty_property.get();
111 }
112
113 fn track_row_data_changes(&self, row: usize) {
114 if crate::properties::is_currently_tracking() {
115 let inner = self.inner().project_ref();
116
117 let mut tracked_rows = inner.tracked_rows.borrow_mut();
118 if let Err(insertion_point) = tracked_rows.binary_search(&row) {
119 tracked_rows.insert(insertion_point, row);
120 }
121
122 inner.model_row_data_dirty_property.get();
123 }
124 }
125}
126
127pub trait ModelChangeListener {
128 fn row_changed(self: Pin<&Self>, row: usize);
129 fn row_added(self: Pin<&Self>, index: usize, count: usize);
130 fn row_removed(self: Pin<&Self>, index: usize, count: usize);
131 fn reset(self: Pin<&Self>);
132}
133
134#[pin_project(PinnedDrop)]
135#[derive(Default, derive_more::Deref)]
136pub struct ModelChangeListenerContainer<T: ModelChangeListener> {
139 peer: OnceCell<DependencyNode<*const dyn ModelChangeListener>>,
142
143 #[pin]
144 #[deref]
145 data: T,
146}
147
148#[pin_project::pinned_drop]
149impl<T: ModelChangeListener> PinnedDrop for ModelChangeListenerContainer<T> {
150 fn drop(self: Pin<&mut Self>) {
151 if let Some(peer) = self.peer.get() {
152 peer.remove();
153 }
154 }
155}
156
157impl<T: ModelChangeListener + 'static> ModelChangeListenerContainer<T> {
158 pub fn new(data: T) -> Self {
159 Self { peer: Default::default(), data }
160 }
161
162 pub fn model_peer(self: Pin<&Self>) -> ModelPeer<'_> {
163 let peer = self.get_ref().peer.get_or_init(|| {
164 DependencyNode::new(
166 (&self.data) as &dyn ModelChangeListener as *const dyn ModelChangeListener,
167 )
168 });
169
170 let peer = unsafe { Pin::new_unchecked(peer) };
172
173 ModelPeer { inner: peer }
174 }
175
176 pub fn get(self: Pin<&Self>) -> Pin<&T> {
177 self.project_ref().data
178 }
179}