// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 // cSpell: ignore vecmodel doesnt downcasted modeliter modelrc //! Model and Repeater use crate::items::StandardListViewItem; use crate::{Property, SharedString, SharedVector}; pub use adapters::{FilterModel, MapModel, ReverseModel, SortModel}; use alloc::boxed::Box; use alloc::rc::Rc; use alloc::vec::Vec; use core::cell::{Cell, RefCell}; use core::pin::Pin; #[allow(unused)] use euclid::num::{Ceil, Floor}; pub use model_peer::*; use once_cell::unsync::OnceCell; use pin_project::pin_project; mod adapters; mod model_peer; pub mod repeater; // vendored: `pub` for the jank-hunt LV_* probes pub use repeater::{Conditional, ListViewProperties, RepeatedItemTree, Repeater, RepeaterTracker}; /// Error returned by the row mutation functions of [`Model`] when the modification /// was not applied. #[derive(Debug, Clone, Eq, PartialEq, derive_more::Error, derive_more::Display)] pub struct ModelError(#[error(not(source))] ErrorImpl); #[derive(Debug, Clone, Eq, PartialEq, derive_more::Display)] enum ErrorImpl { #[display("the row index is out of bounds (the model has {_0} rows)")] OutOfBounds(usize), #[display("the model {_0} does not support this modification")] Unsupported(alloc::borrow::Cow<'static, str>), } impl ModelError { /// The row index is out of the model's bounds. `row_count` is the model's number of rows. pub fn out_of_bounds(row_count: usize) -> Self { Self(ErrorImpl::OutOfBounds(row_count)) } /// The model does not support this modification. pub fn unsupported(model: &(impl Model + ?Sized)) -> Self { Self(ErrorImpl::Unsupported(core::any::type_name_of_val(model).into())) } /// The model with the given type name does not support this modification. /// /// Not part of the public API: for the bridges to models implemented in /// another language, where the type name of the [`Model`] implementation /// would name the wrapper instead of the model. #[doc(hidden)] pub fn unsupported_by_name( model_type_name: impl Into>, _: crate::InternalToken, ) -> Self { Self(ErrorImpl::Unsupported(model_type_name.into())) } } /// This trait defines the interface that users of a model can use to track changes /// to a model. It is supplied via [`Model::model_tracker`] and implementation usually /// return a reference to its field of [`ModelNotify`]. pub trait ModelTracker { /// Attach one peer. The peer will be notified when the model changes fn attach_peer(&self, peer: ModelPeer); /// Register the model as a dependency to the current binding being evaluated, so /// that it will be notified when the model changes its size. fn track_row_count_changes(&self); /// Register a row as a dependency to the current binding being evaluated, so that /// it will be notified when the value of that row changes. fn track_row_data_changes(&self, row: usize); /// Register the whole model as a dependency to the current binding being evaluated, /// so that it will be notified of any change to the model: the row count as well as /// the data of any of the `row_count` rows. /// /// This is equivalent to calling [`Self::track_row_count_changes`] and then /// [`Self::track_row_data_changes`] for every row, which is what the default /// implementation does, but implementations such as [`ModelNotify`] register a /// single dependency whose cost is independent of the number of rows. /// /// Not part of the public API: the `row_count` parameter only exists for the /// default implementation and may change. #[doc(hidden)] fn track_any_change(&self, row_count: usize, _: crate::InternalToken) { self.track_row_count_changes(); for row in 0..row_count { self.track_row_data_changes(row); } } } impl ModelTracker for () { fn attach_peer(&self, _peer: ModelPeer) {} fn track_row_count_changes(&self) {} fn track_row_data_changes(&self, _row: usize) {} fn track_any_change(&self, _row_count: usize, _: crate::InternalToken) {} } /// A Model is providing Data for the repeated elements with `for` in the `.slint` language /// /// If the model can be changed, the type implementing the Model trait should hold /// a [`ModelNotify`], and is responsible to call functions on it to let the UI know that /// something has changed. /// /// Properties of type array will be mapped to a [`ModelRc`], which wraps a `Rc>.` /// The [`ModelRc`] documentation has examples on how to set models to array properties. /// /// It is more efficient to operate on the model and send changes through the `ModelNotify` rather than /// resetting the property with a different model. /// /// ## Example /// /// As an example, let's see the implementation of [`VecModel`]. /// /// ``` /// # use i_slint_core::model::{Model, ModelNotify, ModelPeer, ModelTracker}; /// pub struct VecModel { /// // the backing data, stored in a `RefCell` as this model can be modified /// array: std::cell::RefCell>, /// // the ModelNotify will allow to notify the UI that the model changes /// notify: ModelNotify, /// } /// /// impl Model for VecModel { /// type Data = T; /// /// fn row_count(&self) -> usize { /// self.array.borrow().len() /// } /// /// fn row_data(&self, row: usize) -> Option { /// self.array.borrow().get(row).cloned() /// } /// /// fn set_row_data(&self, row: usize, data: Self::Data) { /// self.array.borrow_mut()[row] = data; /// // don't forget to call row_changed /// self.notify.row_changed(row); /// } /// /// fn model_tracker(&self) -> &dyn ModelTracker { /// &self.notify /// } /// /// fn as_any(&self) -> &dyn core::any::Any { /// // a typical implementation just return `self` /// self /// } /// } /// /// // when modifying the model, we call the corresponding function in /// // the ModelNotify /// impl VecModel { /// /// Add a row at the end of the model /// pub fn push(&self, value: T) { /// self.array.borrow_mut().push(value); /// self.notify.row_added(self.array.borrow().len() - 1, 1) /// } /// /// /// Remove the row at the given index from the model /// pub fn remove(&self, index: usize) { /// self.array.borrow_mut().remove(index); /// self.notify.row_removed(index, 1) /// } /// } /// ``` pub trait Model { /// The model data: A model is a set of rows and each row has this data type Data; /// The number of rows in the model fn row_count(&self) -> usize; /// Returns the data for a particular row. /// /// This function should normally be called with `row < row_count()` and should return None otherwise. /// /// This function does not register dependencies on the current binding. For an equivalent /// function that tracks dependencies, see [`ModelExt::row_data_tracked`] fn row_data(&self, row: usize) -> Option; /// Sets the data for a particular row. /// /// This function should be called with `row < row_count()`, otherwise the implementation can panic. /// /// If the model cannot support data changes, then it is ok to do nothing. /// The default implementation will print a warning to stderr. /// /// If the model can update the data, it should also call [`ModelNotify::row_changed`] on its /// internal [`ModelNotify`]. fn set_row_data(&self, _row: usize, _data: Self::Data) { #[cfg(feature = "std")] crate::debug_log!( "Model::set_row_data called on a model of type {} which does not re-implement this method. \ This happens when trying to modify a read-only model", core::any::type_name::(), ); } /// Add a new row at the end of the model. /// /// The default implementation inserts the row after the last one with /// [`insert_row`](Self::insert_row). fn push_row(&self, data: Self::Data) -> Result<(), ModelError> { self.insert_row(self.row_count(), data) } /// Remove the row at the specified index from the model. /// /// This function should be called with `row < row_count()`. /// /// The default implementation returns [`ModelError::unsupported`]; implementations /// should return [`ModelError::out_of_bounds`] when `row` is out of bounds. /// /// If the model can update the data, it should also call [`ModelNotify::row_removed`] on its /// internal [`ModelNotify`]. fn remove_row(&self, _row: usize) -> Result<(), ModelError> { Err(ModelError::unsupported(self)) } /// Insert a new row at the specified index and move the next rows by 1 step to the right. /// /// The default implementation returns [`ModelError::unsupported`]; implementations /// should return [`ModelError::out_of_bounds`] when `row` is out of bounds. /// /// If the model can update the data, it should also call [`ModelNotify::row_added`] on its /// internal [`ModelNotify`]. fn insert_row(&self, _row: usize, _data: Self::Data) -> Result<(), ModelError> { Err(ModelError::unsupported(self)) } /// The implementation should return a reference to its [`ModelNotify`] field. /// /// You can return `&()` if you your `Model` is constant and does not have a ModelNotify field. fn model_tracker(&self) -> &dyn ModelTracker; /// Returns an iterator visiting all elements of the model. fn iter(&self) -> ModelIterator<'_, Self::Data> where Self: Sized, { ModelIterator::new(self) } /// Return something that can be downcast'ed (typically self). /// /// Use this to retrieve the concrete model from a [`ModelRc`] stored /// in your tree of UI elements. /// /// ``` /// # use i_slint_core::model::*; /// # use std::rc::Rc; /// let handle = ModelRc::new(VecModel::from(vec![1i32, 2, 3])); /// // later: /// handle.as_any().downcast_ref::>().unwrap().push(4); /// assert_eq!(handle.row_data(3).unwrap(), 4); /// ``` /// /// Note: Custom models must implement this method for the cast to succeed. /// A valid implementation is to return `self`: /// ```ignore /// fn as_any(&self) -> &dyn core::any::Any { self } /// ``` /// /// ## Troubleshooting /// A common reason why the downcast fails at run-time is because of a type-mismatch /// between the model created and the model downcasted. To debug this at compile time, /// try matching the model type used for the downcast explicitly at model creation time. /// In the following example, the downcast fails at run-time: /// /// ``` /// # use i_slint_core::model::*; /// # use std::rc::Rc; /// let model = VecModel::from_slice(&[3i32, 2, 1]) /// .filter(Box::new(|v: &i32| *v >= 2) as Box bool>); /// let model_rc = ModelRc::new(model); /// assert!(model_rc.as_any() /// .downcast_ref::, Box bool>>>() /// .is_none()); /// ``` /// /// To debug this, let's make the type explicit. It fails to compile. /// /// ```compile_fail /// # use i_slint_core::model::*; /// # use std::rc::Rc; /// let model: FilterModel, Box bool>> /// = VecModel::from_slice(&[3i32, 2, 1]) /// .filter(Box::new(|v: &i32| *v >= 2) as Box bool>); /// let model_rc = ModelRc::new(model); /// assert!(model_rc.as_any() /// .downcast_ref::, Box bool>>>() /// .is_none()); /// ``` /// /// The compiler tells us that the type of model is not `FilterModel>`, /// but instead `from_slice()` already returns a `ModelRc`, so the correct type to /// use for the downcast is wrapped in `ModelRc`: /// /// ``` /// # use i_slint_core::model::*; /// # use std::rc::Rc; /// let model: FilterModel, Box bool>> /// = VecModel::from_slice(&[3i32, 2, 1]) /// .filter(Box::new(|v: &i32| *v >= 2) as Box bool>); /// let model_rc = ModelRc::new(model); /// assert!(model_rc.as_any() /// .downcast_ref::, Box bool>>>() /// .is_some()); /// ``` fn as_any(&self) -> &dyn core::any::Any { &() } } /// Extension trait with extra methods implemented on types that implement [`Model`] pub trait ModelExt: Model { /// Convenience function that calls [`ModelTracker::track_row_data_changes`] /// before returning [`Model::row_data`]. /// /// Calling [`row_data(row)`](Model::row_data) does not register the row as a dependency when calling it while /// evaluating a property binding. This function calls [`track_row_data_changes(row)`](ModelTracker::track_row_data_changes) /// on the [`self.model_tracker()`](Model::model_tracker) to enable tracking. fn row_data_tracked(&self, row: usize) -> Option { self.model_tracker().track_row_data_changes(row); self.row_data(row) } /// Returns a new Model where all elements are mapped by the function `map_function`. /// This is a shortcut for [`MapModel::new()`]. fn map(self, map_function: F) -> MapModel where Self: Sized + 'static, F: Fn(Self::Data) -> U + 'static, { MapModel::new(self, map_function) } /// Returns a new Model where the elements are filtered by the function `filter_function`. /// This is a shortcut for [`FilterModel::new()`]. fn filter(self, filter_function: F) -> FilterModel where Self: Sized + 'static, F: Fn(&Self::Data) -> bool + 'static, { FilterModel::new(self, filter_function) } /// Returns a new Model where the elements are sorted ascending. /// This is a shortcut for [`SortModel::new_ascending()`]. #[must_use] fn sort(self) -> SortModel where Self: Sized + 'static, Self::Data: core::cmp::Ord, { SortModel::new_ascending(self) } /// Returns a new Model where the elements are sorted by the function `sort_function`. /// This is a shortcut for [`SortModel::new()`]. fn sort_by(self, sort_function: F) -> SortModel where Self: Sized + 'static, F: FnMut(&Self::Data, &Self::Data) -> core::cmp::Ordering + 'static, { SortModel::new(self, sort_function) } /// Returns a new Model where the elements are reversed. /// This is a shortcut for [`ReverseModel::new()`]. fn reverse(self) -> ReverseModel where Self: Sized + 'static, { ReverseModel::new(self) } } impl ModelExt for T {} /// Reports the error of a rejected model modification as a log message. /// /// Called with the result of the `.slint` array functions by the generated code /// and the interpreter, which otherwise ignore the error. #[doc(hidden)] pub fn report_model_error( function: &str, location: Option>, result: Result<(), ModelError>, ) { if let Err(err) = result { crate::debug_log::log_message(crate::debug_log::LogMessage::new( crate::debug_log::LogMessageSource::SlintCode, location, format_args!("array.{function}(): {err}"), )); } } pub fn model_any(model: &dyn Model, mut predicate: impl FnMut(T) -> bool) -> bool { let row_count = model.row_count(); model.model_tracker().track_any_change(row_count, crate::InternalToken); (0..row_count).any(|index| model.row_data(index).is_some_and(&mut predicate)) } pub fn model_all(model: &dyn Model, mut predicate: impl FnMut(T) -> bool) -> bool { let row_count = model.row_count(); model.model_tracker().track_any_change(row_count, crate::InternalToken); // `is_none_or`, not `is_some_and`: a row without data is skipped, as it is by // model_any and model_find_index, rather than failing the whole model. (0..row_count).all(|index| model.row_data(index).is_none_or(&mut predicate)) } /// Returns the index of the first row for which `predicate` returns `true`, or `-1` /// if no row matches. pub fn model_find_index( model: &dyn Model, mut predicate: impl FnMut(T) -> bool, ) -> i32 { let row_count = model.row_count(); model.model_tracker().track_any_change(row_count, crate::InternalToken); (0..row_count) .find(|index| model.row_data(*index).is_some_and(&mut predicate)) .map_or(-1, |index| index as i32) } /// An iterator over the elements of a model. /// This struct is created by the [`Model::iter()`] trait function. pub struct ModelIterator<'a, T> { model: &'a dyn Model, row: usize, } impl<'a, T> ModelIterator<'a, T> { /// Creates a new model iterator for a model reference. /// This is the same as calling [`model.iter()`](Model::iter) pub fn new(model: &'a dyn Model) -> Self { Self { model, row: 0 } } } impl Iterator for ModelIterator<'_, T> { type Item = T; fn next(&mut self) -> Option { if self.row >= self.model.row_count() { return None; } let row = self.row; self.row += 1; self.model.row_data(row) } fn size_hint(&self) -> (usize, Option) { let len = self.model.row_count(); (len, Some(len)) } fn nth(&mut self, n: usize) -> Option { self.row = self.row.checked_add(n)?; self.next() } } impl ExactSizeIterator for ModelIterator<'_, T> {} impl Model for Rc { type Data = M::Data; fn row_count(&self) -> usize { (**self).row_count() } fn row_data(&self, row: usize) -> Option { (**self).row_data(row) } fn model_tracker(&self) -> &dyn ModelTracker { (**self).model_tracker() } fn as_any(&self) -> &dyn core::any::Any { (**self).as_any() } fn set_row_data(&self, row: usize, data: Self::Data) { (**self).set_row_data(row, data) } fn push_row(&self, data: Self::Data) -> Result<(), ModelError> { (**self).push_row(data) } fn remove_row(&self, row: usize) -> Result<(), ModelError> { (**self).remove_row(row) } fn insert_row(&self, row: usize, data: Self::Data) -> Result<(), ModelError> { (**self).insert_row(row, data) } } /// A [`Model`] backed by a `Vec`, using interior mutability. pub struct VecModel { array: RefCell>, notify: ModelNotify, } impl Default for VecModel { fn default() -> Self { Self { array: Default::default(), notify: Default::default() } } } impl VecModel { /// Allocate a new model from a slice pub fn from_slice(slice: &[T]) -> ModelRc where T: Clone, { ModelRc::new(Self::from(slice.to_vec())) } /// Add a row at the end of the model pub fn push(&self, value: T) { self.array.borrow_mut().push(value); self.notify.row_added(self.array.borrow().len() - 1, 1) } /// Inserts a row at position index. All rows after that are shifted. /// This function panics if index is > row_count(). pub fn insert(&self, index: usize, value: T) { self.array.borrow_mut().insert(index, value); self.notify.row_added(index, 1) } /// Remove the row at the given index from the model /// /// Returns the removed row pub fn remove(&self, index: usize) -> T { let r = self.array.borrow_mut().remove(index); self.notify.row_removed(index, 1); r } /// Replace inner Vec with new data pub fn set_vec(&self, new: impl Into>) { *self.array.borrow_mut() = new.into(); self.notify.reset(); } /// Extend the model with the content of the iterator /// /// Similar to [`Vec::extend`] pub fn extend>(&self, iter: I) { let mut array = self.array.borrow_mut(); let old_idx = array.len(); array.extend(iter); let count = array.len() - old_idx; drop(array); self.notify.row_added(old_idx, count); } /// Clears the model, removing all values /// /// Similar to [`Vec::clear`] pub fn clear(&self) { self.array.borrow_mut().clear(); self.notify.reset(); } /// Swaps two elements in the model. pub fn swap(&self, a: usize, b: usize) { if a == b { return; } self.array.borrow_mut().swap(a, b); self.notify.row_changed(a); self.notify.row_changed(b); } } impl VecModel { /// Appends all the elements in the slice to the model /// /// Similar to [`Vec::extend_from_slice`] pub fn extend_from_slice(&self, src: &[T]) { let mut array = self.array.borrow_mut(); let old_idx = array.len(); array.extend_from_slice(src); drop(array); self.notify.row_added(old_idx, src.len()); } } impl From> for VecModel { fn from(array: Vec) -> Self { VecModel { array: RefCell::new(array), notify: Default::default() } } } impl FromIterator for VecModel { fn from_iter>(iter: I) -> Self { VecModel::from(Vec::from_iter(iter)) } } impl Model for VecModel { type Data = T; fn row_count(&self) -> usize { self.array.borrow().len() } fn row_data(&self, row: usize) -> Option { self.array.borrow().get(row).cloned() } fn set_row_data(&self, row: usize, data: Self::Data) { if row < self.row_count() { self.array.borrow_mut()[row] = data; self.notify.row_changed(row); } } fn remove_row(&self, row: usize) -> Result<(), ModelError> { if row >= self.row_count() { return Err(ModelError::out_of_bounds(self.row_count())); } self.remove(row); Ok(()) } fn insert_row(&self, row: usize, data: Self::Data) -> Result<(), ModelError> { if row > self.row_count() { return Err(ModelError::out_of_bounds(self.row_count())); } self.insert(row, data); Ok(()) } fn model_tracker(&self) -> &dyn ModelTracker { &self.notify } fn as_any(&self) -> &dyn core::any::Any { self } } /// A model backed by a `SharedVector` #[derive(Default)] pub struct SharedVectorModel { array: RefCell>, notify: ModelNotify, } impl SharedVectorModel { /// Add a row at the end of the model pub fn push(&self, value: T) { self.array.borrow_mut().push(value); self.notify.row_added(self.array.borrow().len() - 1, 1) } } impl SharedVectorModel { /// Returns a clone of the model's backing shared vector. pub fn shared_vector(&self) -> SharedVector { self.array.borrow_mut().clone() } } impl From> for SharedVectorModel { fn from(array: SharedVector) -> Self { SharedVectorModel { array: RefCell::new(array), notify: Default::default() } } } impl Model for SharedVectorModel { type Data = T; fn row_count(&self) -> usize { self.array.borrow().len() } fn row_data(&self, row: usize) -> Option { self.array.borrow().get(row).cloned() } fn set_row_data(&self, row: usize, data: Self::Data) { self.array.borrow_mut().make_mut_slice()[row] = data; self.notify.row_changed(row); } fn remove_row(&self, row: usize) -> Result<(), ModelError> { if row >= self.row_count() { return Err(ModelError::out_of_bounds(self.row_count())); } self.array.borrow_mut().remove(row); self.notify.row_removed(row, 1); Ok(()) } fn insert_row(&self, row: usize, data: Self::Data) -> Result<(), ModelError> { if row > self.row_count() { return Err(ModelError::out_of_bounds(self.row_count())); } self.array.borrow_mut().insert(row, data); self.notify.row_added(row, 1); Ok(()) } fn model_tracker(&self) -> &dyn ModelTracker { &self.notify } fn as_any(&self) -> &dyn core::any::Any { self } } impl Model for usize { type Data = i32; fn row_count(&self) -> usize { *self } fn row_data(&self, row: usize) -> Option { (row < self.row_count()).then_some(row as i32) } fn as_any(&self) -> &dyn core::any::Any { self } fn model_tracker(&self) -> &dyn ModelTracker { &() } } impl Model for bool { type Data = (); fn row_count(&self) -> usize { if *self { 1 } else { 0 } } fn row_data(&self, row: usize) -> Option { (row < self.row_count()).then_some(()) } fn as_any(&self) -> &dyn core::any::Any { self } fn model_tracker(&self) -> &dyn ModelTracker { &() } } /// ModelRc is a type wrapper for a reference counted implementation of the [`Model`] trait. /// /// Models are used to represent sequences of the same data type. In `.slint` code those /// are represented using the `[T]` array syntax and typically used in `for` expressions, /// array properties, and array struct fields. /// /// For example, a `property <[string]> foo` will be of type `ModelRc` /// and, behind the scenes, wraps a `Rc>.` /// /// An array struct field will also be of type `ModelRc`: /// /// ```slint,no-preview /// export struct AddressBook { /// names: [string] /// } /// ``` /// /// When accessing `AddressBook` from Rust, the `names` field will be of type `ModelRc`. /// /// There are several ways of constructing a ModelRc in Rust: /// /// * An empty ModelRc can be constructed with [`ModelRc::default()`]. /// * A `ModelRc` can be constructed from a slice or an array using the [`From`] trait. /// This allocates a [`VecModel`]. /// * Use [`ModelRc::new()`] to construct a `ModelRc` from a type that implements the /// [`Model`] trait, such as [`VecModel`] or your own implementation. /// * If you have your model already in an `Rc`, then you can use the [`From`] trait /// to convert from `Rc>` to `ModelRc`. /// /// ## Example /// /// ```rust /// # i_slint_backend_testing::init_no_event_loop(); /// use slint::{slint, SharedString, ModelRc, Model, VecModel}; /// use std::rc::Rc; /// slint!{ /// import { Button } from "std-widgets.slint"; /// export component Example { /// callback add_item <=> btn.clicked; /// in property <[string]> the_model; /// HorizontalLayout { /// for it in the_model : Text { text: it; } /// btn := Button { text: "Add"; } /// } /// } /// } /// let ui = Example::new().unwrap(); /// // Create a VecModel and put it in an Rc. /// let the_model : Rc> = /// Rc::new(VecModel::from(vec!["Hello".into(), "World".into()])); /// // Convert it to a ModelRc. /// let the_model_rc = ModelRc::from(the_model.clone()); /// // Pass the model to the ui: The generated set_the_model setter from the /// // the_model property takes a ModelRc. /// ui.set_the_model(the_model_rc); /// /// // We have kept a strong reference to the_model, to modify it in a callback. /// ui.on_add_item(move || { /// // Use VecModel API: VecModel uses the Model notification mechanism to let Slint /// // know it needs to refresh the UI. /// the_model.push("SomeValue".into()); /// }); /// /// // Alternative: we can re-use a getter. /// let ui_weak = ui.as_weak(); /// ui.on_add_item(move || { /// let ui = ui_weak.unwrap(); /// let the_model_rc = ui.get_the_model(); /// let the_model = the_model_rc.as_any().downcast_ref::>() /// .expect("We know we set a VecModel earlier"); /// the_model.push("An Item".into()); /// }); /// ``` /// /// ### Updating the Model from a Thread /// /// `ModelRc` is not `Send` and can only be used in the main thread. /// If you want to update the model based on data coming from another thread, you need to send back the data to the main thread /// using [`invoke_from_event_loop`](crate::api::invoke_from_event_loop) or /// [`Weak::upgrade_in_event_loop`](crate::api::Weak::upgrade_in_event_loop). /// /// ```rust /// # i_slint_backend_testing::init_integration_test_with_mock_time(); /// use slint::Model; /// slint::slint!{ /// export component TestCase inherits Window { /// in property <[string]> the_model; /// //... /// } /// } /// let ui = TestCase::new().unwrap(); /// // set a model (a VecModel) /// let model = std::rc::Rc::new(slint::VecModel::::default()); /// ui.set_the_model(model.clone().into()); /// /// // do some work in a thread /// let ui_weak = ui.as_weak(); /// let thread = std::thread::spawn(move || { /// // do some work /// let new_strings = vec!["foo".into(), "bar".into()]; /// // send the data back to the main thread /// ui_weak.upgrade_in_event_loop(move |ui| { /// let model = ui.get_the_model(); /// let model = model.as_any().downcast_ref::>() /// .expect("We know we set a VecModel earlier"); /// model.set_vec(new_strings); /// # slint::quit_event_loop().unwrap(); /// }); /// }); /// ui.run().unwrap(); /// ``` pub struct ModelRc(Option>>); impl core::fmt::Debug for ModelRc { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "ModelRc(dyn Model)") } } impl Clone for ModelRc { fn clone(&self) -> Self { Self(self.0.clone()) } } impl Default for ModelRc { /// Construct an empty model fn default() -> Self { Self(None) } } impl core::cmp::PartialEq for ModelRc { fn eq(&self, other: &Self) -> bool { match (&self.0, &other.0) { (None, None) => true, (Some(a), Some(b)) => core::ptr::eq( (&**a) as *const dyn Model as *const u8, (&**b) as *const dyn Model as *const u8, ), _ => false, } } } #[cfg(feature = "serde")] use serde::ser::SerializeSeq; #[cfg(feature = "serde")] impl serde::Serialize for ModelRc where T: serde::Serialize, { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { let mut seq = serializer.serialize_seq(Some(self.row_count()))?; for item in self.iter() { seq.serialize_element(&item)?; } seq.end() } } #[cfg(feature = "serde")] impl<'de, T> serde::Deserialize<'de> for ModelRc where T: serde::Deserialize<'de> + Clone + 'static, { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { let vec = Vec::::deserialize(deserializer)?; if vec.is_empty() { return Ok(ModelRc::default()); } Ok(ModelRc::new(VecModel::from(vec))) } } impl ModelRc { pub fn new(model: impl Model + 'static) -> Self { Self(Some(Rc::new(model))) } } impl + 'static> From> for ModelRc { fn from(model: Rc) -> Self { Self(Some(model)) } } impl From + 'static>> for ModelRc { fn from(model: Rc + 'static>) -> Self { Self(Some(model)) } } impl From<&[T]> for ModelRc { fn from(slice: &[T]) -> Self { VecModel::from_slice(slice) } } impl From<[T; N]> for ModelRc { fn from(array: [T; N]) -> Self { VecModel::from_slice(&array) } } impl TryInto>> for ModelRc { type Error = (); fn try_into(self) -> Result>, Self::Error> { self.0.ok_or(()) } } impl Model for ModelRc { type Data = T; fn row_count(&self) -> usize { self.0.as_ref().map_or(0, |model| model.row_count()) } fn row_data(&self, row: usize) -> Option { self.0.as_ref().and_then(|model| model.row_data(row)) } fn set_row_data(&self, row: usize, data: Self::Data) { if let Some(model) = self.0.as_ref() { model.set_row_data(row, data); } } fn push_row(&self, data: Self::Data) -> Result<(), ModelError> { match self.0.as_ref() { Some(model) => model.push_row(data), None => Err(ModelError::unsupported(self)), } } fn remove_row(&self, row: usize) -> Result<(), ModelError> { match self.0.as_ref() { Some(model) => model.remove_row(row), None => Err(ModelError::unsupported(self)), } } fn insert_row(&self, row: usize, data: Self::Data) -> Result<(), ModelError> { match self.0.as_ref() { Some(model) => model.insert_row(row, data), None => Err(ModelError::unsupported(self)), } } fn model_tracker(&self) -> &dyn ModelTracker { self.0.as_ref().map_or(&(), |model| model.model_tracker()) } fn as_any(&self) -> &dyn core::any::Any { self.0.as_ref().map_or(&(), |model| model.as_any()) } } impl From for StandardListViewItem { fn from(value: SharedString) -> Self { StandardListViewItem { text: value } } } impl From<&str> for StandardListViewItem { fn from(value: &str) -> Self { StandardListViewItem { text: value.into() } } } #[cfg(test)] mod tests { use super::*; use std::vec; #[cfg(feature = "serde")] #[test] fn test_serialize_deserialize_modelrc() { let model_rc = ModelRc::new(VecModel::from(vec![1, 2, 3])); let serialized = serde_json::to_string(&model_rc).unwrap(); let deserialized: ModelRc = serde_json::from_str(&serialized).unwrap(); assert_eq!(deserialized.row_count(), 3); assert_eq!(deserialized.row_data(0), Some(1)); assert_eq!(deserialized.row_data(1), Some(2)); assert_eq!(deserialized.row_data(2), Some(3)); } #[test] fn test_tracking_model_handle() { let model: Rc> = Rc::new(Default::default()); let handle = ModelRc::from(model.clone() as Rc>); let tracker = Box::pin(::default()); assert_eq!( tracker.as_ref().evaluate(|| { handle.model_tracker().track_row_count_changes(); handle.row_count() }), 0 ); assert!(!tracker.is_dirty()); model.push(42); model.push(100); assert!(tracker.is_dirty()); assert_eq!( tracker.as_ref().evaluate(|| { handle.model_tracker().track_row_count_changes(); handle.row_count() }), 2 ); assert!(!tracker.is_dirty()); model.set_row_data(0, 41); assert!(!tracker.is_dirty()); model.remove(0); assert!(tracker.is_dirty()); assert_eq!( tracker.as_ref().evaluate(|| { handle.model_tracker().track_row_count_changes(); handle.row_count() }), 1 ); assert!(!tracker.is_dirty()); model.set_vec(vec![1, 2, 3]); assert!(tracker.is_dirty()); } #[test] fn test_shared_vector_model_bounds() { let model: Rc> = Rc::new(SharedVectorModel::from(SharedVector::from_slice(&[1, 2, 3]))); let handle = ModelRc::from(model.clone()); let tracker = Box::pin(::default()); let count = || { tracker.as_ref().evaluate(|| { handle.model_tracker().track_row_count_changes(); handle.row_count() }) }; assert_eq!(count(), 3); assert!(!tracker.is_dirty()); // Out-of-range operations return an error, do nothing, and must not notify the views. assert!(model.remove_row(3).is_err()); assert!(model.insert_row(4, 42).is_err()); assert!(!tracker.is_dirty()); assert_eq!(model.row_count(), 3); assert_eq!(model.row_data(2), Some(3)); // In-range operations change the data and notify. model.insert_row(3, 4).unwrap(); assert!(tracker.is_dirty()); assert_eq!(count(), 4); model.remove_row(0).unwrap(); assert_eq!(model.row_count(), 3); assert_eq!(model.row_data(0), Some(2)); } #[test] fn test_data_tracking() { let model: Rc> = Rc::new(VecModel::from(vec![0, 1, 2, 3, 4])); let handle = ModelRc::from(model.clone()); let tracker = Box::pin(::default()); assert_eq!( tracker.as_ref().evaluate(|| { handle.model_tracker().track_row_data_changes(1); handle.row_data(1).unwrap() }), 1 ); assert!(!tracker.is_dirty()); model.set_row_data(2, 42); assert!(!tracker.is_dirty()); model.set_row_data(1, 100); assert!(tracker.is_dirty()); assert_eq!( tracker.as_ref().evaluate(|| { handle.model_tracker().track_row_data_changes(1); handle.row_data(1).unwrap() }), 100 ); assert!(!tracker.is_dirty()); // Any changes to rows (even if after tracked rows) for now also marks watched rows as dirty, to // keep the logic simple. model.push(200); assert!(tracker.is_dirty()); assert_eq!(tracker.as_ref().evaluate(|| { handle.row_data_tracked(1).unwrap() }), 100); assert!(!tracker.is_dirty()); model.insert(0, 255); assert!(tracker.is_dirty()); model.set_vec(Vec::new()); assert!(tracker.is_dirty()); } #[test] fn test_data_tracking_outside_a_binding() { let model: Rc> = Rc::new(VecModel::from(vec![0, 1, 2, 3, 4])); let handle = ModelRc::from(model.clone()); let tracker = Box::pin(::default()); assert_eq!( tracker.as_ref().evaluate(|| { handle.model_tracker().track_row_data_changes(1); handle.row_data(1).unwrap() }), 1 ); assert!(!tracker.is_dirty()); // Tracking a row while no binding is being evaluated registers no dependency, so it // must not make later changes to that row dirty bindings that never asked for it. handle.model_tracker().track_row_data_changes(0); model.set_row_data(0, 100); assert!(!tracker.is_dirty()); // The row the tracker did ask for still works. model.set_row_data(1, 100); assert!(tracker.is_dirty()); } #[test] fn test_any_change_tracking() { let model: Rc> = Rc::new(VecModel::from(vec![0, 1, 2, 3, 4])); let handle = ModelRc::from(model.clone()); let tracker = Box::pin(::default()); let find_two = || tracker.as_ref().evaluate(|| model_find_index(&handle, |x| x == 2)); assert_eq!(find_two(), 2); assert!(!tracker.is_dirty()); // Any row change dirties the binding, including rows past the match, // as track_any_change() tracks all rows regardless of short-circuiting. model.set_row_data(4, 42); assert!(tracker.is_dirty()); assert_eq!(find_two(), 2); assert!(!tracker.is_dirty()); model.set_row_data(2, 22); assert!(tracker.is_dirty()); assert_eq!(find_two(), -1); assert!(!tracker.is_dirty()); model.push(2); assert!(tracker.is_dirty()); assert_eq!(find_two(), 5); assert!(!tracker.is_dirty()); model.remove(0); assert!(tracker.is_dirty()); assert_eq!(find_two(), 4); assert!(!tracker.is_dirty()); // A row change right after add/remove cleared the tracking state still // dirties the binding, because the re-evaluation re-tracked the model. model.set_row_data(0, 7); assert!(tracker.is_dirty()); assert_eq!(find_two(), 4); assert!(!tracker.is_dirty()); model.set_vec(Vec::new()); assert!(tracker.is_dirty()); assert_eq!(find_two(), -1); assert!(!tracker.is_dirty()); } #[test] fn test_track_any_change_default_impl() { // A tracker that doesn't override track_any_change(), to exercise // ModelTracker's default implementation, which must be equivalent to // calling track_row_count_changes() and then track_row_data_changes() // for every row. #[derive(Default)] struct RecordingTracker { row_count_calls: Cell, row_data_calls: RefCell>, } impl ModelTracker for RecordingTracker { fn attach_peer(&self, _peer: ModelPeer) {} fn track_row_count_changes(&self) { self.row_count_calls.set(self.row_count_calls.get() + 1); } fn track_row_data_changes(&self, row: usize) { self.row_data_calls.borrow_mut().push(row); } } let tracker = RecordingTracker::default(); tracker.track_any_change(3, crate::InternalToken); assert_eq!(tracker.row_count_calls.get(), 1); assert_eq!(*tracker.row_data_calls.borrow(), vec![0, 1, 2]); tracker.track_any_change(0, crate::InternalToken); assert_eq!(tracker.row_count_calls.get(), 2); assert_eq!(*tracker.row_data_calls.borrow(), vec![0, 1, 2]); } /// A model whose middle row has no data, to pin down how the array predicates /// treat a row that is in range but unreadable. struct AbsentRowModel; impl Model for AbsentRowModel { type Data = i32; fn row_count(&self) -> usize { 3 } fn row_data(&self, row: usize) -> Option { match row { 0 => Some(1), 1 => None, _ => Some(3), } } fn model_tracker(&self) -> &dyn ModelTracker { &() } } #[test] fn test_predicates_skip_absent_rows() { // All three predicates skip the absent row, rather than failing the whole // model or feeding the predicate a default value in its place. assert!(model_all(&AbsentRowModel, |x| x > 0)); assert!(!model_any(&AbsentRowModel, |x| x == 0)); assert_eq!(model_find_index(&AbsentRowModel, |x| x == 3), 2); } #[test] fn test_any_change_subsumes_row_tracking() { let model: Rc> = Rc::new(VecModel::from(vec![0, 1, 2, 3, 4])); let handle = ModelRc::from(model.clone()); // Track the whole model, so that every row is now implicitly tracked. let any_tracker = Box::pin(::default()); any_tracker.as_ref().evaluate(|| model_find_index(&handle, |x| x == 2)); assert!(!any_tracker.is_dirty()); // track_row_data_changes() no longer records the row individually while that // is the case, but a binding tracking a single row must still be notified. let row_tracker = Box::pin(::default()); row_tracker.as_ref().evaluate(|| handle.model_tracker().track_row_data_changes(0)); assert!(!row_tracker.is_dirty()); model.set_row_data(0, 9); assert!(row_tracker.is_dirty()); assert!(any_tracker.is_dirty()); } #[derive(Default)] struct TestView { // Track the parameters reported by the model (row counts, indices, etc.). // The last field in the tuple is the row size the model reports at the time // of callback changed_rows: RefCell>, added_rows: RefCell>, removed_rows: RefCell>, reset: RefCell, model: RefCell>>>, } impl TestView { fn clear(&self) { self.changed_rows.borrow_mut().clear(); self.added_rows.borrow_mut().clear(); self.removed_rows.borrow_mut().clear(); *self.reset.borrow_mut() = 0; } fn row_count(&self) -> usize { self.model .borrow() .as_ref() .and_then(|model| model.upgrade()) .map_or(0, |model| model.row_count()) } } impl ModelChangeListener for TestView { fn row_changed(self: Pin<&Self>, row: usize) { self.changed_rows.borrow_mut().push((row, self.row_count())); } fn row_added(self: Pin<&Self>, index: usize, count: usize) { self.added_rows.borrow_mut().push((index, count, self.row_count())); } fn row_removed(self: Pin<&Self>, index: usize, count: usize) { self.removed_rows.borrow_mut().push((index, count, self.row_count())); } fn reset(self: Pin<&Self>) { *self.reset.borrow_mut() += 1; } } #[test] fn test_vecmodel_set_vec() { let view = Box::pin(ModelChangeListenerContainer::::default()); let model = Rc::new(VecModel::from(vec![1i32, 2, 3, 4])); model.model_tracker().attach_peer(Pin::as_ref(&view).model_peer()); *view.model.borrow_mut() = Some(std::rc::Rc::downgrade(&(model.clone() as Rc>))); model.push(5); assert!(view.changed_rows.borrow().is_empty()); assert_eq!(&*view.added_rows.borrow(), &[(4, 1, 5)]); assert!(view.removed_rows.borrow().is_empty()); assert_eq!(*view.reset.borrow(), 0); view.clear(); model.set_vec(vec![6, 7, 8]); assert!(view.changed_rows.borrow().is_empty()); assert!(view.added_rows.borrow().is_empty()); assert!(view.removed_rows.borrow().is_empty()); assert_eq!(*view.reset.borrow(), 1); view.clear(); model.extend_from_slice(&[9, 10, 11]); assert!(view.changed_rows.borrow().is_empty()); assert_eq!(&*view.added_rows.borrow(), &[(3, 3, 6)]); assert!(view.removed_rows.borrow().is_empty()); assert_eq!(*view.reset.borrow(), 0); view.clear(); model.extend([12, 13]); assert!(view.changed_rows.borrow().is_empty()); assert_eq!(&*view.added_rows.borrow(), &[(6, 2, 8)]); assert!(view.removed_rows.borrow().is_empty()); assert_eq!(*view.reset.borrow(), 0); view.clear(); assert_eq!(model.iter().collect::>(), vec![6, 7, 8, 9, 10, 11, 12, 13]); model.swap(1, 1); assert!(view.changed_rows.borrow().is_empty()); assert!(view.added_rows.borrow().is_empty()); assert!(view.removed_rows.borrow().is_empty()); assert_eq!(*view.reset.borrow(), 0); view.clear(); model.swap(1, 2); assert_eq!(&*view.changed_rows.borrow(), &[(1, 8), (2, 8)]); assert!(view.added_rows.borrow().is_empty()); assert!(view.removed_rows.borrow().is_empty()); assert_eq!(*view.reset.borrow(), 0); view.clear(); assert_eq!(model.iter().collect::>(), vec![6, 8, 7, 9, 10, 11, 12, 13]); } #[test] fn test_vecmodel_clear() { let view = Box::pin(ModelChangeListenerContainer::::default()); let model = Rc::new(VecModel::from(vec![1, 2, 3, 4])); model.model_tracker().attach_peer(Pin::as_ref(&view).model_peer()); *view.model.borrow_mut() = Some(std::rc::Rc::downgrade(&(model.clone() as Rc>))); model.clear(); assert_eq!(*view.reset.borrow(), 1); assert_eq!(model.row_count(), 0); } #[test] fn test_vecmodel_swap() { let view = Box::pin(ModelChangeListenerContainer::::default()); let model = Rc::new(VecModel::from(vec![1, 2, 3, 4])); model.model_tracker().attach_peer(Pin::as_ref(&view).model_peer()); *view.model.borrow_mut() = Some(std::rc::Rc::downgrade(&(model.clone() as Rc>))); model.swap(1, 1); assert!(view.changed_rows.borrow().is_empty()); assert!(view.added_rows.borrow().is_empty()); assert!(view.removed_rows.borrow().is_empty()); assert_eq!(*view.reset.borrow(), 0); view.clear(); model.swap(1, 2); assert_eq!(&*view.changed_rows.borrow(), &[(1, 4), (2, 4)]); assert!(view.added_rows.borrow().is_empty()); assert!(view.removed_rows.borrow().is_empty()); assert_eq!(*view.reset.borrow(), 0); view.clear(); } #[test] fn modeliter_in_bounds() { struct TestModel { length: usize, max_requested_row: Cell, notify: ModelNotify, } impl Model for TestModel { type Data = usize; fn row_count(&self) -> usize { self.length } fn row_data(&self, row: usize) -> Option { self.max_requested_row.set(self.max_requested_row.get().max(row)); (row < self.length).then_some(row) } fn model_tracker(&self) -> &dyn ModelTracker { &self.notify } } let model = Rc::new(TestModel { length: 10, max_requested_row: Cell::new(0), notify: Default::default(), }); assert_eq!(model.iter().max().unwrap(), 9); assert_eq!(model.max_requested_row.get(), 9); } #[test] fn vecmodel_doesnt_require_default() { #[derive(Clone)] struct MyNoDefaultType { _foo: bool, } let model = VecModel::::default(); assert_eq!(model.row_count(), 0); model.push(MyNoDefaultType { _foo: true }); } }