Models: fix writing data doesn't update ListView model data property

This fixes a couple of bug:
 - Bug #3740 happens because `Repeater::model_set_row_data` did not use
   the inner.offset to get the instance (So that's the `val == 106` part
   of the test)
 - But I went ahead and also tested what happenned if you changed the
   model from the "outside" using the `model[i] = `, and that was not
   implemented, hence the move of the code from
   `Repeater::model_set_row_data` to `RepeaterTracker::row_changed`,
   That does need Pin though, so Pin was added everywhere
 - C++ is not affected by bug #3740, because because the C++ listview
   don't do the "allocate only visible" optimization. But the
   "val == 1106" part of the test would fail so this patch also moces
   the update from  `model_set_row_data` to `row_changed`.
   But following that we don't set the state as Dirty in `row_changed`,
   the write_to_model test started filling  because the `row_added`
   function was missing an update of the index on every further items

The change in the interpreter prevent a borrow_mut from causing trouble.
The Value really don't need to be in a RefCell anyway

Fix #3740
This commit is contained in:
Olivier Goffart 2023-10-31 09:18:18 +01:00
parent 924b2c8625
commit d55803d290
7 changed files with 145 additions and 65 deletions

View file

@ -1091,17 +1091,27 @@ class Repeater
};
std::vector<RepeatedInstanceWithState> data;
private_api::Property<bool> is_dirty { true };
std::shared_ptr<Model<ModelData>> model;
void row_added(size_t index, size_t count) override
{
is_dirty.set(true);
data.resize(data.size() + count);
std::rotate(data.begin() + index, data.end() - count, data.end());
for (std::size_t i = index; i < data.size(); ++i) {
// all the indexes are dirty
data[i].state = State::Dirty;
}
}
void row_changed(size_t index) override
{
is_dirty.set(true);
data[index].state = State::Dirty;
auto &c = data[index];
if (model && c.ptr) {
(*c.ptr)->update_data(index, *model->row_data(index));
c.state = State::Clean;
} else {
c.state = State::Dirty;
}
}
void row_removed(size_t index, size_t count) override
{
@ -1135,6 +1145,7 @@ public:
if (model.is_dirty()) {
inner = std::make_shared<RepeaterInner>();
if (auto m = model.get()) {
inner->model = m;
m->attach_peer(inner);
}
}
@ -1236,12 +1247,6 @@ public:
if (auto m = model.get()) {
if (row < m->row_count()) {
m->set_row_data(row, data);
if (inner && inner->is_dirty.get()) {
auto &c = inner->data[row];
if (c.state == RepeaterInner::State::Dirty && c.ptr) {
(*c.ptr)->update_data(row, *m->row_data(row));
}
}
}
}
}