Reduce the amount of re-creation of cells in repeaters when the model changes (#1954)

Re-use the cells but mark them as dirty, instead of re-creating them every time.
In the included test-case that provides behavior that's
more intuitive.
This commit is contained in:
Simon Hausmann 2022-12-06 18:43:55 +01:00 committed by GitHub
parent 958cafc357
commit 1162ebbb79
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 126 additions and 7 deletions

View file

@ -1299,7 +1299,14 @@ public:
void ensure_updated(const Parent *parent) const
{
if (model.is_dirty()) {
auto preserved_data = inner ? std::make_optional(std::move(inner->data)) : std::nullopt;
inner = std::make_shared<RepeaterInner>();
if (auto data = preserved_data) {
inner->data = std::move(*data);
for (auto &&compo_with_state : inner->data) {
compo_with_state.state = RepeaterInner::State::Dirty;
}
}
if (auto m = model.get()) {
m->attach_peer(inner);
}

View file

@ -773,7 +773,13 @@ impl<C: RepeatedComponent + 'static> Repeater<C> {
let model = self.data().project_ref().model;
if model.is_dirty() {
*self.data().inner.borrow_mut() = RepeaterInner::default();
self.data()
.inner
.borrow_mut()
.components
.iter_mut()
.for_each(|c| c.0 = RepeatedComponentState::Dirty);
self.data().is_dirty.set(true);
let m = model.get();
let peer = self.project_ref().0.model_peer();

View file

@ -72,7 +72,8 @@ assert_eq!(instance.get_clicked_index(), 2);
slint_testing::send_mouse_click(&instance, 15., 5.);
assert_eq!(instance.get_clicked_score(), 222000);
assert_eq!(instance.get_clicked_name(), slint::SharedString::from("cruel"));
assert_eq!(instance.get_clicked_internal_state(), 1);
// Rectangle was re-used!
assert_eq!(instance.get_clicked_internal_state(), 2);
another_model.push(("a4".into(), "!".into(), 444.));
slint_testing::send_mouse_click(&instance, 35., 5.);
@ -85,7 +86,8 @@ another_model.set_row_data(1, ("a2".into(), "idyllic".into(), 555.));
slint_testing::send_mouse_click(&instance, 15., 5.);
assert_eq!(instance.get_clicked_score(), 555000);
assert_eq!(instance.get_clicked_name(), slint::SharedString::from("idyllic"));
assert_eq!(instance.get_clicked_internal_state(), 2);
// Rectangle was re-used!
assert_eq!(instance.get_clicked_internal_state(), 3);
assert_eq!(instance.get_clicked_index(), 1);
another_model.remove(1);
@ -126,7 +128,8 @@ assert_eq(instance.get_clicked_index(), 2);
slint_testing::send_mouse_click(&instance, 15., 5.);
assert_eq(instance.get_clicked_score(), 222000);
assert_eq(instance.get_clicked_name(), "cruel");
assert_eq(instance.get_clicked_internal_state(), 1);
// Rectangle was re-used!
assert_eq(instance.get_clicked_internal_state(), 2);
another_model->push_back({"a4", "!", 444.});
slint_testing::send_mouse_click(&instance, 35., 5.);
@ -138,7 +141,8 @@ another_model->set_row_data(1, {"a2", "idyllic", 555.});
slint_testing::send_mouse_click(&instance, 15., 5.);
assert_eq(instance.get_clicked_score(), 555000);
assert_eq(instance.get_clicked_name(), "idyllic");
assert_eq(instance.get_clicked_internal_state(), 2);
// Rectangle was re-used!
assert_eq(instance.get_clicked_internal_state(), 3);
assert_eq(instance.get_clicked_index(), 1);
another_model->erase(1);
@ -179,7 +183,8 @@ assert.equal(instance.clicked_index, 2);
instance.send_mouse_click(15., 5.);
assert.equal(instance.clicked_score, 222000);
assert.equal(instance.clicked_name, "cruel");
assert.equal(instance.clicked_internal_state, 1);
// Rectangle was re-used!
assert.equal(instance.clicked_internal_state, 2);
another_model.push({account: "a4", name: "!", score: 444.});
instance.send_mouse_click(35., 5.);
@ -191,7 +196,8 @@ another_model.setRowData(1, {account: "a2", name: "idyllic", score: 555.});
instance.send_mouse_click(15., 5.);
assert.equal(instance.clicked_score, 555000);
assert.equal(instance.clicked_name, "idyllic");
assert.equal(instance.clicked_internal_state, 2);
// Rectangle was re-used!
assert.equal(instance.clicked_internal_state, 3);
assert.equal(instance.clicked_index, 1);
another_model.remove(1, 1);

View file

@ -0,0 +1,100 @@
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
TestCase := Rectangle {
width: 100phx;
height: 100phx;
property <int> creation-count: 0;
property <bool> condition;
property <length> test-width: preferred-width;
HorizontalLayout {
if condition: Rectangle {
preferred-width: 10px;
init => {
creation-count += 1;
}
}
}
}
/*
```cpp
auto handle = TestCase::create();
const TestCase &instance = *handle;
assert_eq(instance.get_test_width(), 0.);
assert_eq(instance.get_creation_count(), 0);
instance.set_condition(true);
assert_eq(instance.get_test_width(), 10.);
assert_eq(instance.get_creation_count(), 1);
instance.set_condition(false);
assert_eq(instance.get_test_width(), 0.);
assert_eq(instance.get_creation_count(), 1);
instance.set_condition(true);
assert_eq(instance.get_test_width(), 10.);
assert_eq(instance.get_creation_count(), 2);
instance.set_condition(false);
instance.set_condition(true);
assert_eq(instance.get_test_width(), 10.);
assert_eq(instance.get_creation_count(), 2);
```
```rust
let instance = TestCase::new();
assert_eq!(instance.get_test_width(), 0.);
assert_eq!(instance.get_creation_count(), 0);
instance.set_condition(true);
assert_eq!(instance.get_test_width(), 10.);
assert_eq!(instance.get_creation_count(), 1);
instance.set_condition(false);
assert_eq!(instance.get_test_width(), 0.);
assert_eq!(instance.get_creation_count(), 1);
instance.set_condition(true);
assert_eq!(instance.get_test_width(), 10.);
assert_eq!(instance.get_creation_count(), 2);
instance.set_condition(false);
instance.set_condition(true);
assert_eq!(instance.get_test_width(), 10.);
assert_eq!(instance.get_creation_count(), 2);
```
```js
var instance = new slint.TestCase();
assert.equal(instance.test_width, 0.);
assert.equal(instance.creation_count, 0);
instance.condition = true;
assert.equal(instance.test_width, 10.);
assert.equal(instance.creation_count, 1);
instance.condition = false;
assert.equal(instance.test_width, 0.);
assert.equal(instance.creation_count, 1);
instance.condition = true;
assert.equal(instance.test_width, 10.);
assert.equal(instance.creation_count, 2);
instance.condition = false;
instance.condition = true;
assert.equal(instance.test_width, 10.);
assert.equal(instance.creation_count, 2);
```
*/