i_slint_core/model/
model_peer.rs#![allow(unsafe_code)]
use super::*;
use crate::properties::dependency_tracker::DependencyNode;
type DependencyListHead =
crate::properties::dependency_tracker::DependencyListHead<*const dyn ModelChangeListener>;
#[derive(Clone)]
pub struct ModelPeer<'a> {
inner: Pin<&'a DependencyNode<*const dyn ModelChangeListener>>,
}
#[pin_project]
#[derive(Default)]
struct ModelNotifyInner {
#[pin]
model_row_count_dirty_property: Property<()>,
#[pin]
model_row_data_dirty_property: Property<()>,
#[pin]
peers: DependencyListHead,
tracked_rows: RefCell<Vec<usize>>,
}
#[derive(Default)]
pub struct ModelNotify {
inner: OnceCell<Pin<Box<ModelNotifyInner>>>,
}
impl ModelNotify {
fn inner(&self) -> Pin<&ModelNotifyInner> {
self.inner.get_or_init(|| Box::pin(ModelNotifyInner::default())).as_ref()
}
pub fn row_changed(&self, row: usize) {
if let Some(inner) = self.inner.get() {
if inner.tracked_rows.borrow().binary_search(&row).is_ok() {
inner.model_row_data_dirty_property.mark_dirty();
}
inner.as_ref().project_ref().peers.for_each(|p| {
unsafe { Pin::new_unchecked(&**p) }.row_changed(row)
})
}
}
pub fn row_added(&self, index: usize, count: usize) {
if let Some(inner) = self.inner.get() {
inner.model_row_count_dirty_property.mark_dirty();
inner.tracked_rows.borrow_mut().clear();
inner.model_row_data_dirty_property.mark_dirty();
inner.as_ref().project_ref().peers.for_each(|p| {
unsafe { Pin::new_unchecked(&**p) }.row_added(index, count)
})
}
}
pub fn row_removed(&self, index: usize, count: usize) {
if let Some(inner) = self.inner.get() {
inner.model_row_count_dirty_property.mark_dirty();
inner.tracked_rows.borrow_mut().clear();
inner.model_row_data_dirty_property.mark_dirty();
inner.as_ref().project_ref().peers.for_each(|p| {
unsafe { Pin::new_unchecked(&**p) }.row_removed(index, count)
})
}
}
pub fn reset(&self) {
if let Some(inner) = self.inner.get() {
inner.model_row_count_dirty_property.mark_dirty();
inner.tracked_rows.borrow_mut().clear();
inner.model_row_data_dirty_property.mark_dirty();
inner.as_ref().project_ref().peers.for_each(|p| {
unsafe { Pin::new_unchecked(&**p) }.reset()
})
}
}
}
impl ModelTracker for ModelNotify {
fn attach_peer(&self, peer: ModelPeer) {
self.inner().project_ref().peers.append(peer.inner)
}
fn track_row_count_changes(&self) {
self.inner().project_ref().model_row_count_dirty_property.get();
}
fn track_row_data_changes(&self, row: usize) {
if crate::properties::is_currently_tracking() {
let inner = self.inner().project_ref();
let mut tracked_rows = inner.tracked_rows.borrow_mut();
if let Err(insertion_point) = tracked_rows.binary_search(&row) {
tracked_rows.insert(insertion_point, row);
}
inner.model_row_data_dirty_property.get();
}
}
}
pub trait ModelChangeListener {
fn row_changed(self: Pin<&Self>, row: usize);
fn row_added(self: Pin<&Self>, index: usize, count: usize);
fn row_removed(self: Pin<&Self>, index: usize, count: usize);
fn reset(self: Pin<&Self>);
}
#[pin_project(PinnedDrop)]
#[derive(Default, derive_more::Deref)]
pub struct ModelChangeListenerContainer<T: ModelChangeListener> {
peer: OnceCell<DependencyNode<*const dyn ModelChangeListener>>,
#[pin]
#[deref]
data: T,
}
#[pin_project::pinned_drop]
impl<T: ModelChangeListener> PinnedDrop for ModelChangeListenerContainer<T> {
fn drop(self: Pin<&mut Self>) {
if let Some(peer) = self.peer.get() {
peer.remove();
}
}
}
impl<T: ModelChangeListener + 'static> ModelChangeListenerContainer<T> {
pub fn new(data: T) -> Self {
Self { peer: Default::default(), data }
}
pub fn model_peer(self: Pin<&Self>) -> ModelPeer {
let peer = self.get_ref().peer.get_or_init(|| {
DependencyNode::new(
(&self.data) as &dyn ModelChangeListener as *const dyn ModelChangeListener,
)
});
let peer = unsafe { Pin::new_unchecked(peer) };
ModelPeer { inner: peer }
}
pub fn get(self: Pin<&Self>) -> Pin<&T> {
self.project_ref().data
}
}