C++: Fix and test the reset function on model adapter

They were causing infinite recursion because they were calling
themselves.

Also add the missing MapModel::reset

Yet another motivation for https://github.com/slint-ui/slint/issues/3888
as the code was mixing the `reset` function on the adapter meaning
"please re-apply the adapter filter/map/sort function" with the `reset`
function on Model which means "the subclass has changed and we should
notify listeners".

Fixes #4968
This commit is contained in:
Olivier Goffart 2024-03-28 09:47:35 +01:00
parent dc5004f9c4
commit fe38a1e97d
2 changed files with 100 additions and 5 deletions

View file

@ -632,7 +632,7 @@ struct FilterModelInner : private_api::ModelChangeListener
void reset() override
{
update_mapping();
target_model.reset();
target_model.Model<ModelData>::reset();
}
void update_mapping()
@ -721,7 +721,7 @@ struct MapModelInner : private_api::ModelChangeListener
{
target_model.row_removed(index, count);
}
void reset() override { target_model.reset(); }
void reset() override { target_model.Model<SourceModelData>::reset(); }
slint::MapModel<SourceModelData, MappedModelData> &target_model;
};
@ -767,6 +767,10 @@ public:
/// Returns the source model of this filter model.
std::shared_ptr<Model<SourceModelData>> source_model() const { return model; }
/// Re-applies the model's mapping function on each row of the source model. Use this if state
/// external to the mapping function has changed.
void reset() { inner->reset(); }
private:
std::shared_ptr<private_api::MapModelInner<SourceModelData, MappedModelData>> inner;
std::shared_ptr<slint::Model<SourceModelData>> model;
@ -874,7 +878,7 @@ struct SortModelInner : private_api::ModelChangeListener
void reset() override
{
sorted_rows_dirty = true;
target_model.reset();
target_model.Model<ModelData>::reset();
}
void ensure_sorted()
@ -987,7 +991,7 @@ struct ReverseModelInner : private_api::ModelChangeListener
target_model.row_removed(source_model->row_count() - first_removed_row, count);
}
void reset() override { source_model.reset(); }
void reset() override { target_model.reset(); }
std::shared_ptr<slint::Model<ModelData>> source_model;
slint::ReverseModel<ModelData> &target_model;

View file

@ -221,12 +221,47 @@ SCENARIO("Filtering Model Remove")
REQUIRE(even_rows->row_data(1) == 4);
}
SCENARIO("Filtering Model Reset")
{
auto vec_model =
std::make_shared<slint::VectorModel<int>>(std::vector<int> { 1, 2, 3, 4, 5, 6 });
bool even = true;
auto even_rows = std::make_shared<slint::FilterModel<int>>(
vec_model, [&even](auto value) { return value % 2 == !even; });
auto observer = std::make_shared<ModelObserver>();
even_rows->attach_peer(observer);
REQUIRE(even_rows->row_count() == 3);
REQUIRE(even_rows->row_data(0) == 2);
REQUIRE(even_rows->row_data(1) == 4);
REQUIRE(even_rows->row_data(2) == 6);
even = false;
even_rows->reset();
REQUIRE(observer->added_rows.empty());
REQUIRE(observer->changed_rows.empty());
REQUIRE(observer->removed_rows.empty());
REQUIRE(observer->model_reset);
observer->clear();
REQUIRE(even_rows->row_count() == 3);
REQUIRE(even_rows->row_data(0) == 1);
REQUIRE(even_rows->row_data(1) == 3);
REQUIRE(even_rows->row_data(2) == 5);
}
SCENARIO("Mapped Model")
{
auto vec_model = std::make_shared<slint::VectorModel<int>>(std::vector<int> { 1, 2, 3, 4 });
int to_add = 1;
auto plus_one_model = std::make_shared<slint::MapModel<int, int>>(
vec_model, [](auto value) { return value + 1; });
vec_model, [&to_add](auto value) { return value + to_add; });
auto observer = std::make_shared<ModelObserver>();
plus_one_model->attach_peer(observer);
@ -283,6 +318,21 @@ SCENARIO("Mapped Model")
REQUIRE(plus_one_model->row_data(1) == 4);
REQUIRE(plus_one_model->row_data(2) == 3);
REQUIRE(plus_one_model->row_data(3) == 5);
to_add = 51;
plus_one_model->reset();
REQUIRE(observer->added_rows.empty());
REQUIRE(observer->changed_rows.empty());
REQUIRE(observer->removed_rows.empty());
REQUIRE(observer->model_reset);
observer->clear();
REQUIRE(plus_one_model->row_count() == 4);
REQUIRE(plus_one_model->row_data(0) == 151);
REQUIRE(plus_one_model->row_data(1) == 54);
REQUIRE(plus_one_model->row_data(2) == 53);
REQUIRE(plus_one_model->row_data(3) == 55);
}
SCENARIO("Sorted Model Insert")
@ -400,6 +450,38 @@ SCENARIO("Sorted Model Change")
REQUIRE(sorted_model->row_data(3) == 3);
}
SCENARIO("Sorted Model Reset")
{
auto vec_model = std::make_shared<slint::VectorModel<int>>(std::vector<int> { 3, 4, 1, 2 });
bool ascending = true;
auto sorted_model =
std::make_shared<slint::SortModel<int>>(vec_model, [&ascending](auto lhs, auto rhs) {
return ascending ? lhs < rhs : rhs < lhs;
});
auto observer = std::make_shared<ModelObserver>();
sorted_model->attach_peer(observer);
REQUIRE(sorted_model->row_count() == 4);
REQUIRE(sorted_model->row_data(0) == 1);
REQUIRE(sorted_model->row_data(1) == 2);
REQUIRE(sorted_model->row_data(2) == 3);
REQUIRE(sorted_model->row_data(3) == 4);
ascending = false;
sorted_model->reset();
REQUIRE(sorted_model->row_count() == 4);
REQUIRE(sorted_model->row_data(0) == 4);
REQUIRE(sorted_model->row_data(1) == 3);
REQUIRE(sorted_model->row_data(2) == 2);
REQUIRE(sorted_model->row_data(3) == 1);
REQUIRE(observer->model_reset);
}
SCENARIO("Reverse Model Insert")
{
auto vec_model = std::make_shared<slint::VectorModel<int>>(std::vector<int> { 3, 4, 1, 2 });
@ -493,6 +575,15 @@ SCENARIO("Reverse Model Change")
REQUIRE(reverse_model->row_data(1) == 1);
REQUIRE(reverse_model->row_data(2) == 10);
REQUIRE(reverse_model->row_data(3) == 3);
vec_model->clear();
REQUIRE(observer->added_rows.empty());
REQUIRE(observer->changed_rows.empty());
REQUIRE(observer->removed_rows.empty());
REQUIRE(observer->model_reset);
observer->clear();
REQUIRE(reverse_model->row_count() == 0);
}
TEST_CASE("VectorModel clear and replace")