Trait Model
pub trait Model {
type Data;
// Required methods
fn row_count(&self) -> usize;
fn row_data(&self, row: usize) -> Option<Self::Data>;
fn model_tracker(&self) -> &dyn ModelTracker;
// Provided methods
fn set_row_data(&self, _row: usize, _data: Self::Data) { ... }
fn iter(&self) -> ModelIterator<'_, Self::Data> ⓘ
where Self: Sized { ... }
fn as_any(&self) -> &(dyn Any + 'static) { ... }
}
Expand description
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<T>
, which wraps a Rc<Model<Data = T>>.
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
.
pub struct VecModel<T> {
// the backing data, stored in a `RefCell` as this model can be modified
array: std::cell::RefCell<Vec<T>>,
// the ModelNotify will allow to notify the UI that the model changes
notify: ModelNotify,
}
impl<T: Clone + 'static> Model for VecModel<T> {
type Data = T;
fn row_count(&self) -> usize {
self.array.borrow().len()
}
fn row_data(&self, row: usize) -> Option<Self::Data> {
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<T> VecModel<T> {
/// 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)
}
}
Required Associated Types§
type Data
type Data
The model data: A model is a set of rows and each row has this data
Required Methods§
fn row_data(&self, row: usize) -> Option<Self::Data>
fn row_data(&self, row: usize) -> Option<Self::Data>
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 model_tracker(&self) -> &dyn ModelTracker
fn model_tracker(&self) -> &dyn ModelTracker
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.
Provided Methods§
fn set_row_data(&self, _row: usize, _data: Self::Data)
fn set_row_data(&self, _row: usize, _data: Self::Data)
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 iter(&self) -> ModelIterator<'_, Self::Data> ⓘwhere
Self: Sized,
fn iter(&self) -> ModelIterator<'_, Self::Data> ⓘwhere
Self: Sized,
Returns an iterator visiting all elements of the model.
fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
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.
let handle = ModelRc::new(VecModel::from(vec![1i32, 2, 3]));
// later:
handle.as_any().downcast_ref::<VecModel<i32>>().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
:
fn as_any(&self) -> &dyn core::any::Any { self }
§Troubleshooting
A common reason why the dowcast 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:
let model = VecModel::from_slice(&[3i32, 2, 1])
.filter(Box::new(|v: &i32| *v >= 2) as Box<dyn Fn(&i32) -> bool>);
let model_rc = ModelRc::new(model);
assert!(model_rc.as_any()
.downcast_ref::<FilterModel<VecModel<i32>, Box<dyn Fn(&i32) -> bool>>>()
.is_none());
To debug this, let’s make the type explicit. It fails to compile.
let model: FilterModel<VecModel<i32>, Box<dyn Fn(&i32) -> bool>>
= VecModel::from_slice(&[3i32, 2, 1])
.filter(Box::new(|v: &i32| *v >= 2) as Box<dyn Fn(&i32) -> bool>);
let model_rc = ModelRc::new(model);
assert!(model_rc.as_any()
.downcast_ref::<FilterModel<VecModel<i32>, Box<dyn Fn(&i32) -> bool>>>()
.is_none());
The compiler tells us that the type of model is not FilterModel<VecModel<..>>
,
but instead from_slice()
already returns a ModelRc
, so the correct type to
use for the downcast is wrapped in ModelRc
:
let model: FilterModel<ModelRc<i32>, Box<dyn Fn(&i32) -> bool>>
= VecModel::from_slice(&[3i32, 2, 1])
.filter(Box::new(|v: &i32| *v >= 2) as Box<dyn Fn(&i32) -> bool>);
let model_rc = ModelRc::new(model);
assert!(model_rc.as_any()
.downcast_ref::<FilterModel<ModelRc<i32>, Box<dyn Fn(&i32) -> bool>>>()
.is_some());