slint/internal/core/model/adapters.rs
Olivier Goffart c98d234b9e Janitor: Always use `#![no_std] for runtime lib
And call `extern crate std` when the feature is enabled.
I've read this is the good practice on how to do it.
So that the std prelude is no longer included automatically.
There is then less difference between std and and no-std build which
should avoid surprises in the CI when we use things from the prelude.

The downside is that there is a bit of churn in the tests
2025-01-27 19:22:00 +01:00

1427 lines
47 KiB
Rust

// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
//! This module contains adapter models.
use super::*;
#[cfg(test)]
#[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<Vec<usize>>,
added_rows: RefCell<Vec<(usize, usize)>>,
removed_rows: RefCell<Vec<(usize, usize)>>,
reset: RefCell<usize>,
}
#[cfg(test)]
impl TestView {
fn clear(&self) {
self.changed_rows.borrow_mut().clear();
self.added_rows.borrow_mut().clear();
self.removed_rows.borrow_mut().clear();
}
}
#[cfg(test)]
impl ModelChangeListener for TestView {
fn row_changed(self: Pin<&Self>, row: usize) {
self.changed_rows.borrow_mut().push(row);
}
fn row_added(self: Pin<&Self>, index: usize, count: usize) {
self.added_rows.borrow_mut().push((index, count));
}
fn row_removed(self: Pin<&Self>, index: usize, count: usize) {
self.removed_rows.borrow_mut().push((index, count));
}
fn reset(self: Pin<&Self>) {
*self.reset.borrow_mut() += 1;
}
}
#[cfg(test)]
struct ModelChecker<Data: PartialEq + core::fmt::Debug + 'static> {
model: Rc<dyn Model<Data = Data>>,
rows_copy: RefCell<Vec<Data>>,
}
#[cfg(test)]
impl<Data: PartialEq + core::fmt::Debug + 'static> ModelChangeListener for ModelChecker<Data> {
fn row_changed(self: Pin<&Self>, row: usize) {
self.rows_copy.borrow_mut()[row] = self.model.row_data(row).unwrap();
}
fn row_added(self: Pin<&Self>, index: usize, count: usize) {
let mut copy = self.rows_copy.borrow_mut();
for row in index..index + count {
copy.insert(row, self.model.row_data(row).unwrap());
}
}
fn row_removed(self: Pin<&Self>, index: usize, count: usize) {
self.rows_copy.borrow_mut().drain(index..index + count);
}
fn reset(self: Pin<&Self>) {
*self.rows_copy.borrow_mut() = ModelRc::from(self.model.clone()).iter().collect()
}
}
#[cfg(test)]
impl<Data: PartialEq + core::fmt::Debug + 'static> ModelChecker<Data> {
pub fn new(
model: Rc<impl Model<Data = Data> + 'static>,
) -> Pin<Box<ModelChangeListenerContainer<Self>>> {
let s = Self { rows_copy: RefCell::new(model.iter().collect()), model: model.clone() };
let s = Box::pin(ModelChangeListenerContainer::new(s));
model.model_tracker().attach_peer(s.as_ref().model_peer());
s
}
#[track_caller]
pub fn check(&self) {
assert_eq!(
*self.rows_copy.borrow(),
ModelRc::from(self.model.clone()).iter().collect::<Vec<_>>()
);
}
}
#[cfg(test)]
impl<Data: PartialEq + core::fmt::Debug + 'static> Drop for ModelChecker<Data> {
fn drop(&mut self) {
self.check();
}
}
/// Provides rows that are generated by a map function based on the rows of another Model
///
/// When the other Model is updated, the `MapModel` is updated accordingly.
///
/// Generic parameters:
/// * `M` the type of the wrapped `Model`.
/// * `F` the map function.
///
/// ## Example
///
/// Here we have a [`VecModel`] holding rows of a custom type `Name`.
/// It is then mapped into a `MapModel` of [`SharedString`]s
///
/// ```
/// # use slint::{Model, VecModel, SharedString, MapModel};
/// #[derive(Clone)]
/// struct Name {
/// first: String,
/// last: String,
/// }
///
/// let model = VecModel::from(vec![
/// Name { first: "Hans".to_string(), last: "Emil".to_string() },
/// Name { first: "Max".to_string(), last: "Mustermann".to_string() },
/// Name { first: "Roman".to_string(), last: "Tisch".to_string() },
/// ]);
///
/// let mapped_model = MapModel::new(model, |n|
/// slint::format!("{}, {}", n.last, n.first)
/// );
///
/// assert_eq!(mapped_model.row_data(0).unwrap(), SharedString::from("Emil, Hans"));
/// assert_eq!(mapped_model.row_data(1).unwrap(), SharedString::from("Mustermann, Max"));
/// assert_eq!(mapped_model.row_data(2).unwrap(), SharedString::from("Tisch, Roman"));
///
/// ```
///
/// Alternatively you can use the shortcut [`ModelExt::map`].
/// ```
/// # use slint::{Model, ModelExt, VecModel, SharedString, MapModel};
/// # #[derive(Clone)]
/// # struct Name {
/// # first: String,
/// # last: String,
/// # }
/// let mapped_model = VecModel::from(vec![
/// Name { first: "Hans".to_string(), last: "Emil".to_string() },
/// Name { first: "Max".to_string(), last: "Mustermann".to_string() },
/// Name { first: "Roman".to_string(), last: "Tisch".to_string() },
/// ])
/// .map(|n| slint::format!("{}, {}", n.last, n.first));
/// # assert_eq!(mapped_model.row_data(0).unwrap(), SharedString::from("Emil, Hans"));
/// # assert_eq!(mapped_model.row_data(1).unwrap(), SharedString::from("Mustermann, Max"));
/// # assert_eq!(mapped_model.row_data(2).unwrap(), SharedString::from("Tisch, Roman"));
/// ```
///
/// If you want to modify the underlying [`VecModel`] you can give it a [`Rc`] of the MapModel:
/// ```
/// # use std::rc::Rc;
/// # use slint::{Model, VecModel, SharedString, MapModel};
/// # #[derive(Clone)]
/// # struct Name {
/// # first: String,
/// # last: String,
/// # }
/// let model = Rc::new(VecModel::from(vec![
/// Name { first: "Hans".to_string(), last: "Emil".to_string() },
/// Name { first: "Max".to_string(), last: "Mustermann".to_string() },
/// Name { first: "Roman".to_string(), last: "Tisch".to_string() },
/// ]));
///
/// let mapped_model = MapModel::new(model.clone(), |n|
/// slint::format!("{}, {}", n.last, n.first)
/// );
///
/// model.set_row_data(1, Name { first: "Minnie".to_string(), last: "Musterfrau".to_string() });
///
/// assert_eq!(mapped_model.row_data(0).unwrap(), SharedString::from("Emil, Hans"));
/// assert_eq!(mapped_model.row_data(1).unwrap(), SharedString::from("Musterfrau, Minnie"));
/// assert_eq!(mapped_model.row_data(2).unwrap(), SharedString::from("Tisch, Roman"));
///
/// ```
pub struct MapModel<M, F> {
wrapped_model: M,
map_function: F,
}
impl<M, F, T, U> Model for MapModel<M, F>
where
M: 'static,
F: 'static,
F: Fn(T) -> U,
M: Model<Data = T>,
{
type Data = U;
fn row_count(&self) -> usize {
self.wrapped_model.row_count()
}
fn row_data(&self, row: usize) -> Option<Self::Data> {
self.wrapped_model.row_data(row).map(|x| (self.map_function)(x))
}
fn model_tracker(&self) -> &dyn ModelTracker {
self.wrapped_model.model_tracker()
}
fn as_any(&self) -> &dyn core::any::Any {
self
}
}
impl<M, F, T, U> MapModel<M, F>
where
M: 'static,
F: 'static,
F: Fn(T) -> U,
M: Model<Data = T>,
{
/// Creates a new MapModel based on the given `wrapped_model` and `map_function`.
/// Alternatively you can use [`ModelExt::map`] on your Model.
pub fn new(wrapped_model: M, map_function: F) -> Self {
Self { wrapped_model, map_function }
}
/// Returns a reference to the inner model
pub fn source_model(&self) -> &M {
&self.wrapped_model
}
}
#[test]
fn test_map_model() {
use alloc::string::ToString;
let wrapped_rc = Rc::new(VecModel::from(std::vec![1, 2, 3]));
let map = MapModel::new(wrapped_rc.clone(), |x| x.to_string());
wrapped_rc.set_row_data(2, 42);
wrapped_rc.push(4);
assert_eq!(map.row_data(2).unwrap(), "42");
assert_eq!(map.row_data(3).unwrap(), "4");
assert_eq!(map.row_data(1).unwrap(), "2");
}
struct FilterModelInner<M, F>
where
M: Model + 'static,
F: Fn(&M::Data) -> bool + 'static,
{
wrapped_model: M,
filter_function: F,
// This vector saves the indices of the elements that are not filtered out
mapping: RefCell<Vec<usize>>,
notify: ModelNotify,
}
impl<M, F> FilterModelInner<M, F>
where
M: Model + 'static,
F: Fn(&M::Data) -> bool + 'static,
{
fn build_mapping_vec(&self) {
let mut mapping = self.mapping.borrow_mut();
*mapping = self
.wrapped_model
.iter()
.enumerate()
.filter_map(|(i, e)| (self.filter_function)(&e).then_some(i))
.collect();
}
}
impl<M, F> ModelChangeListener for FilterModelInner<M, F>
where
M: Model + 'static,
F: Fn(&M::Data) -> bool + 'static,
{
fn row_changed(self: Pin<&Self>, row: usize) {
let mut mapping = self.mapping.borrow_mut();
let (index, is_contained) = match mapping.binary_search(&row) {
Ok(index) => (index, true),
Err(index) => (index, false),
};
let should_be_contained =
(self.filter_function)(&self.wrapped_model.row_data(row).unwrap());
if is_contained && should_be_contained {
drop(mapping);
self.notify.row_changed(index);
} else if !is_contained && should_be_contained {
mapping.insert(index, row);
drop(mapping);
self.notify.row_added(index, 1);
} else if is_contained && !should_be_contained {
mapping.remove(index);
drop(mapping);
self.notify.row_removed(index, 1);
}
}
fn row_added(self: Pin<&Self>, index: usize, count: usize) {
if count == 0 {
return;
}
let insertion: Vec<usize> = self
.wrapped_model
.iter()
.enumerate()
.skip(index)
.take(count)
.filter_map(|(i, e)| (self.filter_function)(&e).then_some(i))
.collect();
let mut mapping = self.mapping.borrow_mut();
let insertion_point = mapping.binary_search(&index).unwrap_or_else(|ip| ip);
mapping[insertion_point..].iter_mut().for_each(|i| *i += count);
if !insertion.is_empty() {
let insertion_len = insertion.len();
mapping.splice(insertion_point..insertion_point, insertion);
drop(mapping);
self.notify.row_added(insertion_point, insertion_len);
}
}
fn row_removed(self: Pin<&Self>, index: usize, count: usize) {
if count == 0 {
return;
}
let mut mapping = self.mapping.borrow_mut();
let start = mapping.binary_search(&index).unwrap_or_else(|s| s);
let end = mapping.binary_search(&(index + count)).unwrap_or_else(|e| e);
let range = start..end;
mapping[end..].iter_mut().for_each(|i| *i -= count);
if !range.is_empty() {
mapping.drain(range.clone());
drop(mapping);
self.notify.row_removed(start, range.len());
}
}
fn reset(self: Pin<&Self>) {
self.build_mapping_vec();
self.notify.reset();
}
}
/// Provides a filtered subset of rows by another [`Model`].
///
/// When the other Model is updated, the `FilterModel` is updated accordingly.
///
/// Generic parameters:
/// * `M` the type of the wrapped `Model`.
/// * `F` the filter function.
///
/// ## Example
///
/// Here we have a [`VecModel`] holding [`crate::SharedString`]s.
/// It is then filtered into a `FilterModel`.
///
/// ```
/// # use slint::{Model, VecModel, SharedString, FilterModel};
/// let model = VecModel::from(vec![
/// SharedString::from("Lorem"),
/// SharedString::from("ipsum"),
/// SharedString::from("dolor"),
/// ]);
///
/// let filtered_model = FilterModel::new(model, |s| s.contains('o'));
///
/// assert_eq!(filtered_model.row_data(0).unwrap(), SharedString::from("Lorem"));
/// assert_eq!(filtered_model.row_data(1).unwrap(), SharedString::from("dolor"));
/// ```
///
/// Alternatively you can use the shortcut [`ModelExt::filter`].
/// ```
/// # use slint::{Model, ModelExt, VecModel, SharedString, FilterModel};
/// let filtered_model = VecModel::from(vec![
/// SharedString::from("Lorem"),
/// SharedString::from("ipsum"),
/// SharedString::from("dolor"),
/// ]).filter(|s| s.contains('o'));
/// # assert_eq!(filtered_model.row_data(0).unwrap(), SharedString::from("Lorem"));
/// # assert_eq!(filtered_model.row_data(1).unwrap(), SharedString::from("dolor"));
/// ```
///
/// If you want to modify the underlying [`VecModel`] you can give it a [`Rc`] of the FilterModel:
/// ```
/// # use std::rc::Rc;
/// # use slint::{Model, VecModel, SharedString, FilterModel};
/// let model = Rc::new(VecModel::from(vec![
/// SharedString::from("Lorem"),
/// SharedString::from("ipsum"),
/// SharedString::from("dolor"),
/// ]));
///
/// let filtered_model = FilterModel::new(model.clone(), |s| s.contains('o'));
///
/// assert_eq!(filtered_model.row_data(0).unwrap(), SharedString::from("Lorem"));
/// assert_eq!(filtered_model.row_data(1).unwrap(), SharedString::from("dolor"));
///
/// model.set_row_data(1, SharedString::from("opsom"));
///
/// assert_eq!(filtered_model.row_data(0).unwrap(), SharedString::from("Lorem"));
/// assert_eq!(filtered_model.row_data(1).unwrap(), SharedString::from("opsom"));
/// assert_eq!(filtered_model.row_data(2).unwrap(), SharedString::from("dolor"));
/// ```
pub struct FilterModel<M, F>(Pin<Box<ModelChangeListenerContainer<FilterModelInner<M, F>>>>)
where
M: Model + 'static,
F: Fn(&M::Data) -> bool + 'static;
impl<M, F> FilterModel<M, F>
where
M: Model + 'static,
F: Fn(&M::Data) -> bool + 'static,
{
/// Creates a new FilterModel based on the given `wrapped_model` and filtered by `filter_function`.
/// Alternatively you can use [`ModelExt::filter`] on your Model.
pub fn new(wrapped_model: M, filter_function: F) -> Self {
let filter_model_inner = FilterModelInner {
wrapped_model,
filter_function,
mapping: RefCell::new(Vec::new()),
notify: Default::default(),
};
filter_model_inner.build_mapping_vec();
let container = Box::pin(ModelChangeListenerContainer::new(filter_model_inner));
container.wrapped_model.model_tracker().attach_peer(container.as_ref().model_peer());
Self(container)
}
/// Manually reapply the filter. You need to run this e.g. if the filtering function depends on
/// mutable state and it has changed.
pub fn reset(&self) {
self.0.as_ref().get().reset();
}
/// Gets the row index of the underlying unfiltered model for a given filtered row index.
pub fn unfiltered_row(&self, filtered_row: usize) -> usize {
self.0.mapping.borrow()[filtered_row]
}
/// Returns a reference to the inner model
pub fn source_model(&self) -> &M {
&self.0.as_ref().get().get_ref().wrapped_model
}
}
impl<M, F> Model for FilterModel<M, F>
where
M: Model + 'static,
F: Fn(&M::Data) -> bool + 'static,
{
type Data = M::Data;
fn row_count(&self) -> usize {
self.0.mapping.borrow().len()
}
fn row_data(&self, row: usize) -> Option<Self::Data> {
self.0
.mapping
.borrow()
.get(row)
.and_then(|&wrapped_row| self.0.wrapped_model.row_data(wrapped_row))
}
fn set_row_data(&self, row: usize, data: Self::Data) {
let wrapped_row = self.0.mapping.borrow()[row];
self.0.wrapped_model.set_row_data(wrapped_row, data);
}
fn model_tracker(&self) -> &dyn ModelTracker {
&self.0.notify
}
fn as_any(&self) -> &dyn core::any::Any {
self
}
}
#[test]
fn test_filter_model() {
let wrapped_rc = Rc::new(VecModel::from(std::vec![1, 2, 3, 4, 5, 6]));
let filter = Rc::new(FilterModel::new(wrapped_rc.clone(), |x| x % 2 == 0));
let _checker = ModelChecker::new(filter.clone());
assert_eq!(filter.row_data(0).unwrap(), 2);
assert_eq!(filter.row_data(1).unwrap(), 4);
assert_eq!(filter.row_data(2).unwrap(), 6);
assert_eq!(filter.row_count(), 3);
wrapped_rc.remove(1);
assert_eq!(filter.row_data(0).unwrap(), 4);
assert_eq!(filter.row_data(1).unwrap(), 6);
assert_eq!(filter.row_count(), 2);
wrapped_rc.push(8);
wrapped_rc.push(7);
assert_eq!(filter.row_data(0).unwrap(), 4);
assert_eq!(filter.row_data(1).unwrap(), 6);
assert_eq!(filter.row_data(2).unwrap(), 8);
assert_eq!(filter.row_count(), 3);
wrapped_rc.set_row_data(1, 2);
assert_eq!(filter.row_data(0).unwrap(), 2);
assert_eq!(filter.row_data(1).unwrap(), 4);
assert_eq!(filter.row_data(2).unwrap(), 6);
assert_eq!(filter.row_data(3).unwrap(), 8);
assert_eq!(filter.row_count(), 4);
wrapped_rc.insert(2, 12);
assert_eq!(filter.row_data(0).unwrap(), 2);
assert_eq!(filter.row_data(1).unwrap(), 12);
assert_eq!(filter.row_data(2).unwrap(), 4);
assert_eq!(filter.row_data(3).unwrap(), 6);
assert_eq!(filter.row_data(4).unwrap(), 8);
assert_eq!(filter.row_count(), 5);
}
#[test]
fn test_filter_model_source_model() {
let wrapped_rc = Rc::new(VecModel::from(std::vec![1, 2, 3, 4]));
let model = Rc::new(FilterModel::new(wrapped_rc.clone(), |x| x % 2 == 0));
let observer = Box::pin(ModelChangeListenerContainer::<TestView>::default());
model.model_tracker().attach_peer(Pin::as_ref(&observer).model_peer());
let _checker = ModelChecker::new(model.clone());
model.source_model().push(5);
model.source_model().push(6);
let expected = &[2, 4, 6];
assert_eq!(model.row_count(), expected.len());
for (i, v) in expected.iter().enumerate() {
assert_eq!(model.row_data(i), Some(*v), "Expected {} at index {}", v, i);
}
}
pub trait SortHelper<D> {
fn cmp(&mut self, lhs: &D, rhs: &D) -> core::cmp::Ordering;
}
pub struct AscendingSortHelper;
impl<D> SortHelper<D> for AscendingSortHelper
where
D: core::cmp::Ord,
{
fn cmp(&mut self, lhs: &D, rhs: &D) -> core::cmp::Ordering {
lhs.cmp(rhs)
}
}
impl<F, D> SortHelper<D> for F
where
F: FnMut(&D, &D) -> core::cmp::Ordering + 'static,
{
fn cmp(&mut self, lhs: &D, rhs: &D) -> core::cmp::Ordering {
(self)(lhs, rhs)
}
}
struct SortModelInner<M, S>
where
M: Model + 'static,
S: SortHelper<M::Data> + 'static,
{
wrapped_model: M,
sort_helper: RefCell<S>,
// This vector saves the indices of the elements in sorted order.
mapping: RefCell<Vec<usize>>,
notify: ModelNotify,
sorted_rows_dirty: Cell<bool>,
}
impl<M, S> SortModelInner<M, S>
where
M: Model + 'static,
S: SortHelper<M::Data>,
{
fn build_mapping_vec(&self) {
if !self.sorted_rows_dirty.get() {
return;
}
let mut mapping = self.mapping.borrow_mut();
mapping.clear();
mapping.extend(0..self.wrapped_model.row_count());
mapping.sort_by(|lhs, rhs| {
self.sort_helper.borrow_mut().cmp(
&self.wrapped_model.row_data(*lhs).unwrap(),
&self.wrapped_model.row_data(*rhs).unwrap(),
)
});
self.sorted_rows_dirty.set(false);
}
}
impl<M, S> ModelChangeListener for SortModelInner<M, S>
where
M: Model + 'static,
S: SortHelper<M::Data> + 'static,
{
fn row_changed(self: Pin<&Self>, row: usize) {
if self.sorted_rows_dirty.get() {
self.reset();
return;
}
let mut mapping = self.mapping.borrow_mut();
let removed_index = mapping.iter().position(|r| *r == row).unwrap();
mapping.remove(removed_index);
let changed_data = self.wrapped_model.row_data(row).unwrap();
let insertion_index = mapping.partition_point(|existing_row| {
self.sort_helper
.borrow_mut()
.cmp(&self.wrapped_model.row_data(*existing_row).unwrap(), &changed_data)
== core::cmp::Ordering::Less
});
mapping.insert(insertion_index, row);
drop(mapping);
if insertion_index == removed_index {
self.notify.row_changed(removed_index);
} else {
self.notify.row_removed(removed_index, 1);
self.notify.row_added(insertion_index, 1);
}
}
fn row_added(self: Pin<&Self>, index: usize, count: usize) {
if count == 0 {
return;
}
if self.sorted_rows_dirty.get() {
self.reset();
return;
}
// Adjust the existing sorted row indices to match the updated source model
for row in self.mapping.borrow_mut().iter_mut() {
if *row >= index {
*row += count;
}
}
for row in index..(index + count) {
let added_data = self.wrapped_model.row_data(row).unwrap();
let insertion_index = self.mapping.borrow().partition_point(|existing_row| {
self.sort_helper
.borrow_mut()
.cmp(&self.wrapped_model.row_data(*existing_row).unwrap(), &added_data)
== core::cmp::Ordering::Less
});
self.mapping.borrow_mut().insert(insertion_index, row);
self.notify.row_added(insertion_index, 1)
}
}
fn row_removed(self: Pin<&Self>, index: usize, count: usize) {
if count == 0 {
return;
}
if self.sorted_rows_dirty.get() {
self.reset();
return;
}
let mut removed_rows = Vec::new();
let mut i = 0;
loop {
if i >= self.mapping.borrow().len() {
break;
}
let sort_index = self.mapping.borrow()[i];
if sort_index >= index {
if sort_index < index + count {
removed_rows.push(i);
self.mapping.borrow_mut().remove(i);
continue;
} else {
self.mapping.borrow_mut()[i] -= count;
}
}
i += 1;
}
for removed_row in removed_rows {
self.notify.row_removed(removed_row, 1);
}
}
fn reset(self: Pin<&Self>) {
self.sorted_rows_dirty.set(true);
self.notify.reset();
}
}
/// Provides a sorted view of rows by another [`Model`].
///
/// When the other Model is updated, the `Sorted` is updated accordingly.
///
/// Generic parameters:
/// * `M` the type of the wrapped `Model`.
/// * `F` a type that provides an order to model rows. It is constrained by the internal trait `SortHelper`, which is used to sort the model in ascending order if the model data supports it, or by a given sort function.
///
/// ## Example
///
/// Here we have a [`VecModel`] holding [`crate::SharedString`]s.
/// It is then sorted into a `SortModel`.
///
/// ```
/// # use slint::{Model, VecModel, SharedString, SortModel};
/// let model = VecModel::from(vec![
/// SharedString::from("Lorem"),
/// SharedString::from("ipsum"),
/// SharedString::from("dolor"),
/// ]);
///
/// let sorted_model = SortModel::new(model, |lhs, rhs| lhs.to_lowercase().cmp(&rhs.to_lowercase()));
///
/// assert_eq!(sorted_model.row_data(0).unwrap(), SharedString::from("dolor"));
/// assert_eq!(sorted_model.row_data(1).unwrap(), SharedString::from("ipsum"));
/// assert_eq!(sorted_model.row_data(2).unwrap(), SharedString::from("Lorem"));
/// ```
///
/// Alternatively you can use the shortcut [`ModelExt::sort_by`].
/// ```
/// # use slint::{Model, ModelExt, VecModel, SharedString, SortModel};
/// let sorted_model = VecModel::from(vec![
/// SharedString::from("Lorem"),
/// SharedString::from("ipsum"),
/// SharedString::from("dolor"),
/// ]).sort_by(|lhs, rhs| lhs.to_lowercase().cmp(&rhs.to_lowercase()));
/// # assert_eq!(sorted_model.row_data(0).unwrap(), SharedString::from("dolor"));
/// # assert_eq!(sorted_model.row_data(1).unwrap(), SharedString::from("ipsum"));
/// # assert_eq!(sorted_model.row_data(2).unwrap(), SharedString::from("Lorem"));
/// ```
///
/// It is also possible to get a ascending sorted `SortModel` order for `core::cmp::Ord` type items.
///
/// ```
/// # use slint::{Model, VecModel, SortModel};
/// let model = VecModel::from(vec![
/// 5,
/// 1,
/// 3,
/// ]);
///
/// let sorted_model = SortModel::new_ascending(model);
///
/// assert_eq!(sorted_model.row_data(0).unwrap(), 1);
/// assert_eq!(sorted_model.row_data(1).unwrap(), 3);
/// assert_eq!(sorted_model.row_data(2).unwrap(), 5);
/// ```
///
/// Alternatively you can use the shortcut [`ModelExt::sort`].
/// ```
/// # use slint::{Model, ModelExt, VecModel, SharedString, SortModel};
/// let sorted_model = VecModel::from(vec![
/// 5,
/// 1,
/// 3,
/// ]).sort();
/// # assert_eq!(sorted_model.row_data(0).unwrap(), 1);
/// # assert_eq!(sorted_model.row_data(1).unwrap(), 3);
/// # assert_eq!(sorted_model.row_data(2).unwrap(), 5);
/// ```
///
/// If you want to modify the underlying [`VecModel`] you can give it a [`Rc`] of the SortModel:
/// ```
/// # use std::rc::Rc;
/// # use slint::{Model, VecModel, SharedString, SortModel};
/// let model = Rc::new(VecModel::from(vec![
/// SharedString::from("Lorem"),
/// SharedString::from("ipsum"),
/// SharedString::from("dolor"),
/// ]));
///
/// let sorted_model = SortModel::new(model.clone(), |lhs, rhs| lhs.to_lowercase().cmp(&rhs.to_lowercase()));
///
/// assert_eq!(sorted_model.row_data(0).unwrap(), SharedString::from("dolor"));
/// assert_eq!(sorted_model.row_data(1).unwrap(), SharedString::from("ipsum"));
/// assert_eq!(sorted_model.row_data(2).unwrap(), SharedString::from("Lorem"));
///
/// model.set_row_data(1, SharedString::from("opsom"));
///
/// assert_eq!(sorted_model.row_data(0).unwrap(), SharedString::from("dolor"));
/// assert_eq!(sorted_model.row_data(1).unwrap(), SharedString::from("Lorem"));
/// assert_eq!(sorted_model.row_data(2).unwrap(), SharedString::from("opsom"));
/// ```
pub struct SortModel<M, F>(Pin<Box<ModelChangeListenerContainer<SortModelInner<M, F>>>>)
where
M: Model + 'static,
F: SortHelper<M::Data> + 'static;
impl<M, F> SortModel<M, F>
where
M: Model + 'static,
F: FnMut(&M::Data, &M::Data) -> core::cmp::Ordering + 'static,
{
/// Creates a new SortModel based on the given `wrapped_model` and sorted by `sort_function`.
/// Alternatively you can use [`ModelExt::sort_by`] on your Model.
pub fn new(wrapped_model: M, sort_function: F) -> Self
where
F: FnMut(&M::Data, &M::Data) -> core::cmp::Ordering + 'static,
{
let sorted_model_inner = SortModelInner {
wrapped_model,
sort_helper: RefCell::new(sort_function),
mapping: RefCell::new(Vec::new()),
notify: Default::default(),
sorted_rows_dirty: Cell::new(true),
};
let container = Box::pin(ModelChangeListenerContainer::new(sorted_model_inner));
container.wrapped_model.model_tracker().attach_peer(container.as_ref().model_peer());
Self(container)
}
/// Returns a reference to the inner model
pub fn source_model(&self) -> &M {
&self.0.as_ref().get().get_ref().wrapped_model
}
}
impl<M> SortModel<M, AscendingSortHelper>
where
M: Model + 'static,
M::Data: core::cmp::Ord,
{
/// Creates a new SortModel based on the given `wrapped_model` and sorted in ascending order.
/// Alternatively you can use [`ModelExt::sort`] on your Model.
pub fn new_ascending(wrapped_model: M) -> Self
where
M::Data: core::cmp::Ord,
{
let sorted_model_inner = SortModelInner {
wrapped_model,
sort_helper: RefCell::new(AscendingSortHelper),
mapping: RefCell::new(Vec::new()),
notify: Default::default(),
sorted_rows_dirty: Cell::new(true),
};
let container = Box::pin(ModelChangeListenerContainer::new(sorted_model_inner));
container.wrapped_model.model_tracker().attach_peer(container.as_ref().model_peer());
Self(container)
}
/// Manually reapply the sorting. You need to run this e.g. if the sort function depends
/// on mutable state and it has changed.
pub fn reset(&self) {
self.0.as_ref().get().reset();
}
/// Gets the row index of the underlying unsorted model for a given sorted row index.
pub fn unsorted_row(&self, sorted_row: usize) -> usize {
self.0.build_mapping_vec();
self.0.mapping.borrow()[sorted_row]
}
}
impl<M, S> Model for SortModel<M, S>
where
M: Model + 'static,
S: SortHelper<M::Data>,
{
type Data = M::Data;
fn row_count(&self) -> usize {
self.0.wrapped_model.row_count()
}
fn row_data(&self, row: usize) -> Option<Self::Data> {
self.0.build_mapping_vec();
self.0
.mapping
.borrow()
.get(row)
.and_then(|&wrapped_row| self.0.wrapped_model.row_data(wrapped_row))
}
fn set_row_data(&self, row: usize, data: Self::Data) {
let wrapped_row = self.0.mapping.borrow()[row];
self.0.wrapped_model.set_row_data(wrapped_row, data);
}
fn model_tracker(&self) -> &dyn ModelTracker {
&self.0.notify
}
fn as_any(&self) -> &dyn core::any::Any {
self
}
}
#[cfg(test)]
mod sort_tests {
use super::*;
use std::vec;
#[test]
fn test_sorted_model_insert() {
let wrapped_rc = Rc::new(VecModel::from(std::vec![3, 4, 1, 2]));
let sorted_model = Rc::new(SortModel::new(wrapped_rc.clone(), |lhs, rhs| lhs.cmp(rhs)));
let _checker = ModelChecker::new(sorted_model.clone());
let observer = Box::pin(ModelChangeListenerContainer::<TestView>::default());
sorted_model.model_tracker().attach_peer(Pin::as_ref(&observer).model_peer());
assert_eq!(sorted_model.row_count(), 4);
assert_eq!(sorted_model.row_data(0).unwrap(), 1);
assert_eq!(sorted_model.row_data(1).unwrap(), 2);
assert_eq!(sorted_model.row_data(2).unwrap(), 3);
assert_eq!(sorted_model.row_data(3).unwrap(), 4);
wrapped_rc.insert(0, 10);
assert_eq!(observer.added_rows.borrow().len(), 1);
assert!(observer.added_rows.borrow().eq(&[(4, 1)]));
assert!(observer.changed_rows.borrow().is_empty());
assert!(observer.removed_rows.borrow().is_empty());
assert_eq!(*observer.reset.borrow(), 0);
observer.clear();
assert_eq!(sorted_model.row_count(), 5);
assert_eq!(sorted_model.row_data(0).unwrap(), 1);
assert_eq!(sorted_model.row_data(1).unwrap(), 2);
assert_eq!(sorted_model.row_data(2).unwrap(), 3);
assert_eq!(sorted_model.row_data(3).unwrap(), 4);
assert_eq!(sorted_model.row_data(4).unwrap(), 10);
}
#[test]
fn test_sorted_model_remove() {
let wrapped_rc = Rc::new(VecModel::from(vec![3, 4, 1, 2]));
let sorted_model = Rc::new(SortModel::new(wrapped_rc.clone(), |lhs, rhs| lhs.cmp(rhs)));
let _checker = ModelChecker::new(sorted_model.clone());
let observer = Box::pin(ModelChangeListenerContainer::<TestView>::default());
sorted_model.model_tracker().attach_peer(Pin::as_ref(&observer).model_peer());
assert_eq!(sorted_model.row_count(), 4);
assert_eq!(sorted_model.row_data(0).unwrap(), 1);
assert_eq!(sorted_model.row_data(1).unwrap(), 2);
assert_eq!(sorted_model.row_data(2).unwrap(), 3);
assert_eq!(sorted_model.row_data(3).unwrap(), 4);
// Remove the entry with the value 4
wrapped_rc.remove(1);
assert!(observer.added_rows.borrow().is_empty());
assert!(observer.changed_rows.borrow().is_empty());
assert_eq!(observer.removed_rows.borrow().len(), 1);
assert!(observer.removed_rows.borrow().eq(&[(3, 1)]));
assert_eq!(*observer.reset.borrow(), 0);
observer.clear();
assert_eq!(sorted_model.row_count(), 3);
assert_eq!(sorted_model.row_data(0).unwrap(), 1);
assert_eq!(sorted_model.row_data(1).unwrap(), 2);
assert_eq!(sorted_model.row_data(2).unwrap(), 3);
}
#[test]
fn test_sorted_model_changed() {
let wrapped_rc = Rc::new(VecModel::from(vec![3, 4, 1, 2]));
let sorted_model = Rc::new(SortModel::new(wrapped_rc.clone(), |lhs, rhs| lhs.cmp(rhs)));
let _checker = ModelChecker::new(sorted_model.clone());
let observer = Box::pin(ModelChangeListenerContainer::<TestView>::default());
sorted_model.model_tracker().attach_peer(Pin::as_ref(&observer).model_peer());
assert_eq!(sorted_model.row_count(), 4);
assert_eq!(sorted_model.row_data(0).unwrap(), 1);
assert_eq!(sorted_model.row_data(1).unwrap(), 2);
assert_eq!(sorted_model.row_data(2).unwrap(), 3);
assert_eq!(sorted_model.row_data(3).unwrap(), 4);
// Change the entry with the value 4 to 10 -> maintain order
wrapped_rc.set_row_data(1, 10);
assert!(observer.added_rows.borrow().is_empty());
assert_eq!(observer.changed_rows.borrow().len(), 1);
assert_eq!(*observer.changed_rows.borrow().first().unwrap(), 3);
assert!(observer.removed_rows.borrow().is_empty());
assert_eq!(*observer.reset.borrow(), 0);
observer.clear();
assert_eq!(sorted_model.row_count(), 4);
assert_eq!(sorted_model.row_data(0).unwrap(), 1);
assert_eq!(sorted_model.row_data(1).unwrap(), 2);
assert_eq!(sorted_model.row_data(2).unwrap(), 3);
assert_eq!(sorted_model.row_data(3).unwrap(), 10);
// Change the entry with the value 10 to 0 -> new order with remove and insert
wrapped_rc.set_row_data(1, 0);
assert_eq!(observer.added_rows.borrow().len(), 1);
assert!(observer.added_rows.borrow().first().unwrap().eq(&(0, 1)));
assert!(observer.changed_rows.borrow().is_empty());
assert_eq!(observer.removed_rows.borrow().len(), 1);
assert!(observer.removed_rows.borrow().first().unwrap().eq(&(3, 1)));
assert_eq!(*observer.reset.borrow(), 0);
observer.clear();
assert_eq!(sorted_model.row_count(), 4);
assert_eq!(sorted_model.row_data(0).unwrap(), 0);
assert_eq!(sorted_model.row_data(1).unwrap(), 1);
assert_eq!(sorted_model.row_data(2).unwrap(), 2);
assert_eq!(sorted_model.row_data(3).unwrap(), 3);
}
#[test]
fn test_sorted_model_source_model() {
let wrapped_rc = Rc::new(VecModel::from(vec![3, 4, 1, 2]));
let model = Rc::new(SortModel::new(wrapped_rc.clone(), |lhs, rhs| lhs.cmp(rhs)));
let _checker = ModelChecker::new(model.clone());
let observer = Box::pin(ModelChangeListenerContainer::<TestView>::default());
model.model_tracker().attach_peer(Pin::as_ref(&observer).model_peer());
model.source_model().push(6);
model.source_model().push(5);
let expected = &[1, 2, 3, 4, 5, 6];
assert_eq!(model.row_count(), expected.len());
for (i, v) in expected.iter().enumerate() {
assert_eq!(model.row_data(i), Some(*v), "Expected {} at index {}", v, i);
}
}
}
/// Provides a reversed view of another [`Model`].
///
/// When the other Model is updated, the `ReverseModel` is updated accordingly.
///
/// Generic parameters:
/// * `M` the type of the wrapped `Model`.
///
/// ## Example
///
/// Here we have a [`VecModel`] holding [`crate::SharedString`]s.
/// It is then reversed into a `ReverseModel`.
///
/// ```
/// # use slint::{Model, VecModel, SharedString, ReverseModel};
/// let model = VecModel::from(vec![
/// SharedString::from("Lorem"),
/// SharedString::from("ipsum"),
/// SharedString::from("dolor"),
/// ]);
///
/// let reverse_model = ReverseModel::new(model);
///
/// assert_eq!(reverse_model.row_data(0).unwrap(), SharedString::from("dolor"));
/// assert_eq!(reverse_model.row_data(1).unwrap(), SharedString::from("ipsum"));
/// assert_eq!(reverse_model.row_data(2).unwrap(), SharedString::from("Lorem"));
/// ```
///
/// Alternatively you can use the shortcut [`ModelExt::reverse`].
/// ```
/// # use slint::{Model, ModelExt, VecModel, SharedString};
/// let reverse_model = VecModel::from(vec![
/// SharedString::from("Lorem"),
/// SharedString::from("ipsum"),
/// SharedString::from("dolor"),
/// ]).reverse();
/// assert_eq!(reverse_model.row_data(0).unwrap(), SharedString::from("dolor"));
/// assert_eq!(reverse_model.row_data(1).unwrap(), SharedString::from("ipsum"));
/// assert_eq!(reverse_model.row_data(2).unwrap(), SharedString::from("Lorem"));
/// ```
///
/// If you want to modify the underlying [`VecModel`] you can give the ReverseModel a [`Rc`] of it:
/// ```
/// # use std::rc::Rc;
/// # use slint::{Model, VecModel, SharedString, ReverseModel};
/// let model = Rc::new(VecModel::from(vec![
/// SharedString::from("Lorem"),
/// SharedString::from("ipsum"),
/// SharedString::from("dolor"),
/// ]));
///
/// let reverse_model = ReverseModel::new(model.clone());
///
/// assert_eq!(reverse_model.row_data(0).unwrap(), SharedString::from("dolor"));
/// assert_eq!(reverse_model.row_data(1).unwrap(), SharedString::from("ipsum"));
/// assert_eq!(reverse_model.row_data(2).unwrap(), SharedString::from("Lorem"));
///
/// model.push(SharedString::from("opsom"));
///
/// assert_eq!(reverse_model.row_data(0).unwrap(), SharedString::from("opsom"));
/// assert_eq!(reverse_model.row_data(1).unwrap(), SharedString::from("dolor"));
/// assert_eq!(reverse_model.row_data(2).unwrap(), SharedString::from("ipsum"));
/// assert_eq!(reverse_model.row_data(3).unwrap(), SharedString::from("Lorem"));
/// ```
pub struct ReverseModel<M>(Pin<Box<ModelChangeListenerContainer<ReverseModelInner<M>>>>)
where
M: Model + 'static;
struct ReverseModelInner<M>
where
M: Model + 'static,
{
wrapped_model: M,
notify: ModelNotify,
}
impl<M> ModelChangeListener for ReverseModelInner<M>
where
M: Model + 'static,
{
fn row_changed(self: Pin<&Self>, row: usize) {
self.notify.row_changed(self.wrapped_model.row_count() - 1 - row);
}
fn row_added(self: Pin<&Self>, index: usize, count: usize) {
let row_count = self.wrapped_model.row_count();
let old_row_count = row_count - count;
let index = old_row_count - index;
self.notify.row_added(index, count);
}
fn row_removed(self: Pin<&Self>, index: usize, count: usize) {
let row_count = self.wrapped_model.row_count();
self.notify.row_removed(row_count - index, count);
}
fn reset(self: Pin<&Self>) {
self.notify.reset()
}
}
impl<M> ReverseModel<M>
where
M: Model + 'static,
{
/// Creates a new ReverseModel based on the given `wrapped_model`.
/// Alternatively you can use [`ModelExt::reverse`] on your Model.
pub fn new(wrapped_model: M) -> Self {
let inner = ReverseModelInner { wrapped_model, notify: Default::default() };
let container = Box::pin(ModelChangeListenerContainer::new(inner));
container.wrapped_model.model_tracker().attach_peer(container.as_ref().model_peer());
Self(container)
}
/// Returns a reference to the inner model
pub fn source_model(&self) -> &M {
&self.0.as_ref().get().get_ref().wrapped_model
}
}
impl<M> Model for ReverseModel<M>
where
M: Model + 'static,
{
type Data = M::Data;
fn row_count(&self) -> usize {
self.0.wrapped_model.row_count()
}
fn row_data(&self, row: usize) -> Option<Self::Data> {
let count = self.0.wrapped_model.row_count();
self.0.wrapped_model.row_data(count.checked_sub(row + 1)?)
}
fn set_row_data(&self, row: usize, data: Self::Data) {
let count = self.0.as_ref().wrapped_model.row_count();
self.0.wrapped_model.set_row_data(count - row - 1, data);
}
fn model_tracker(&self) -> &dyn ModelTracker {
&self.0.notify
}
fn as_any(&self) -> &dyn core::any::Any {
self
}
}
#[cfg(test)]
mod reversed_tests {
use super::*;
use std::vec;
#[track_caller]
fn check_content(model: &ReverseModel<Rc<VecModel<i32>>>, expected: &[i32]) {
assert_eq!(model.row_count(), expected.len());
for (i, v) in expected.iter().enumerate() {
assert_eq!(model.row_data(i), Some(*v), "Expected {} at index {}", v, i);
}
}
#[test]
fn test_reversed_model() {
let wrapped_rc = Rc::new(VecModel::from(vec![1, 2, 3, 4]));
let model = Rc::new(ReverseModel::new(wrapped_rc.clone()));
let _checker = ModelChecker::new(model.clone());
let observer = Box::pin(ModelChangeListenerContainer::<TestView>::default());
model.model_tracker().attach_peer(Pin::as_ref(&observer).model_peer());
check_content(&model, &[4, 3, 2, 1]);
}
#[test]
fn test_reversed_model_insert() {
for (idx, mapped_idx) in [(0, 4), (1, 3), (2, 2), (3, 1), (4, 0)] {
std::println!("Inserting at {} expecting mapped to {}", idx, mapped_idx);
let wrapped_rc = Rc::new(VecModel::from(vec![1, 2, 3, 4]));
let model = Rc::new(ReverseModel::new(wrapped_rc.clone()));
let _checker = ModelChecker::new(model.clone());
let observer = Box::pin(ModelChangeListenerContainer::<TestView>::default());
model.model_tracker().attach_peer(Pin::as_ref(&observer).model_peer());
wrapped_rc.insert(idx, 10);
assert_eq!(observer.added_rows.borrow().len(), 1);
assert!(
observer.added_rows.borrow().eq(&[(mapped_idx, 1)]),
"Added rows: {:?}",
observer.added_rows.borrow()
);
assert!(observer.changed_rows.borrow().is_empty());
assert!(observer.removed_rows.borrow().is_empty());
assert_eq!(*observer.reset.borrow(), 0);
assert_eq!(model.row_data(mapped_idx), Some(10));
}
}
#[test]
fn test_reversed_model_remove() {
for (idx, mapped_idx) in [(0, 3), (1, 2), (2, 1), (3, 0)] {
std::println!("Removing at {} expecting mapped to {}", idx, mapped_idx);
let wrapped_rc = Rc::new(VecModel::from(vec![1, 2, 3, 4]));
let model = Rc::new(ReverseModel::new(wrapped_rc.clone()));
let _checker = ModelChecker::new(model.clone());
let observer = Box::pin(ModelChangeListenerContainer::<TestView>::default());
model.model_tracker().attach_peer(Pin::as_ref(&observer).model_peer());
wrapped_rc.remove(idx);
assert_eq!(observer.removed_rows.borrow().len(), 1);
assert!(
observer.removed_rows.borrow().eq(&[(mapped_idx, 1)]),
"Remove rows: {:?}",
observer.removed_rows.borrow()
);
assert!(observer.added_rows.borrow().is_empty());
assert!(observer.changed_rows.borrow().is_empty());
assert_eq!(*observer.reset.borrow(), 0);
}
}
#[test]
fn test_reversed_model_changed() {
for (idx, mapped_idx) in [(0, 3), (1, 2), (2, 1), (3, 0)] {
std::println!("Changing at {} expecting mapped to {}", idx, mapped_idx);
let wrapped_rc = Rc::new(VecModel::from(std::vec![1, 2, 3, 4]));
let model = Rc::new(ReverseModel::new(wrapped_rc.clone()));
let _checker = ModelChecker::new(model.clone());
let observer = Box::pin(ModelChangeListenerContainer::<TestView>::default());
model.model_tracker().attach_peer(Pin::as_ref(&observer).model_peer());
wrapped_rc.set_row_data(idx, 10);
assert_eq!(observer.changed_rows.borrow().len(), 1);
assert!(
observer.changed_rows.borrow().eq(&[mapped_idx]),
"Changed rows: {:?}",
observer.changed_rows.borrow()
);
assert!(observer.added_rows.borrow().is_empty());
assert!(observer.removed_rows.borrow().is_empty());
assert_eq!(*observer.reset.borrow(), 0);
assert_eq!(model.row_data(mapped_idx), Some(10));
}
}
#[test]
fn test_reversed_model_source_model() {
let wrapped_rc = Rc::new(VecModel::from(std::vec![1, 2, 3, 4]));
let model = Rc::new(ReverseModel::new(wrapped_rc.clone()));
let _checker = ModelChecker::new(model.clone());
let observer = Box::pin(ModelChangeListenerContainer::<TestView>::default());
model.model_tracker().attach_peer(Pin::as_ref(&observer).model_peer());
model.source_model().push(5);
check_content(&model, &[5, 4, 3, 2, 1]);
}
}
#[test]
fn test_long_chain_integrity() {
use alloc::string::ToString;
let origin_model = Rc::new(VecModel::from((0..100).collect::<Vec<_>>()));
let checker1 = ModelChecker::new(origin_model.clone());
let fizzbuzz = Rc::new(MapModel::new(origin_model.clone(), |number| {
if (number % 3) == 0 && (number % 5) == 0 {
"FizzBuzz".to_string()
} else if (number % 3) == 0 {
"Fizz".to_string()
} else if (number % 5) == 0 {
"Buzz".to_string()
} else {
number.to_string()
}
}));
let checker2 = ModelChecker::new(fizzbuzz.clone());
let filter = Rc::new(FilterModel::new(fizzbuzz, |s| s != "FizzBuzz"));
let checker3 = ModelChecker::new(filter.clone());
let reverse = Rc::new(ReverseModel::new(filter));
let checker4 = ModelChecker::new(reverse.clone());
let sorted = Rc::new(SortModel::new_ascending(reverse));
let checker5 = ModelChecker::new(sorted.clone());
let filter2 = Rc::new(FilterModel::new(sorted, |s| s != "Fizz"));
let checker6 = ModelChecker::new(filter2.clone());
let check_all = || {
checker1.check();
checker2.check();
checker3.check();
checker4.check();
checker5.check();
checker6.check();
};
origin_model.extend(50..150);
check_all();
origin_model.insert(8, 1000);
check_all();
origin_model.remove(9);
check_all();
origin_model.remove(10);
origin_model.remove(11);
origin_model.set_row_data(55, 10001);
check_all();
origin_model.set_row_data(58, 10002);
origin_model.set_row_data(59, 10003);
origin_model.remove(28);
origin_model.remove(29);
origin_model.insert(100, 8888);
origin_model.remove(30);
origin_model.set_row_data(60, 10004);
origin_model.remove(130);
origin_model.set_row_data(61, 10005);
origin_model.remove(131);
check_all();
origin_model.remove(12);
origin_model.remove(13);
origin_model.remove(14);
origin_model.set_row_data(62, 10006);
origin_model.set_row_data(63, 10007);
origin_model.set_row_data(64, 10008);
origin_model.set_row_data(65, 10009);
check_all();
// Since VecModel don't have this as public API, just add some function that use row_removed on a wider range.
trait RemoveRange {
fn remove_range(&self, range: core::ops::Range<usize>);
}
impl<T> RemoveRange for VecModel<T> {
fn remove_range(&self, range: core::ops::Range<usize>) {
self.array.borrow_mut().drain(range.clone());
self.notify.row_removed(range.start, range.len())
}
}
origin_model.remove_range(25..110);
check_all();
origin_model.extend(900..910);
origin_model.set_row_data(45, 44444);
origin_model.remove_range(10..30);
origin_model.insert(45, 3000);
origin_model.insert(45, 3001);
origin_model.insert(45, 3002);
origin_model.insert(45, 3003);
origin_model.insert(45, 3004);
origin_model.insert(45, 3006);
origin_model.insert(45, 3007);
check_all();
}