Fix ListView panic when setting a new model

.. that has less items and the ListView is scrolled.

We should not have an offset that is higher than the current count
otherwise we're going to access invalid item in the model

Fix #2780
This commit is contained in:
Olivier Goffart 2023-06-05 13:54:23 +02:00 committed by Olivier Goffart
parent 69cec30748
commit bef2e3617d
2 changed files with 66 additions and 0 deletions

View file

@ -962,6 +962,10 @@ impl<C: RepeatedComponent + 'static> Repeater<C> {
let data = self.data();
let mut inner = data.inner.borrow_mut();
if inner.offset >= row_count {
inner.offset = row_count - 1;
}
let one_and_a_half_screen = listview_height * 3 as Coord / 2 as Coord;
let first_item_y = inner.anchor_y;
let last_item_bottom = first_item_y + element_height * inner.components.len() as Coord;

View file

@ -0,0 +1,62 @@
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
// This test case verifies that the listview updates its layout / geometry when the
// entire model changes. This test works by triggering layout updates by simulating
// mouse clicks, which results in item tree traversal and ensure_updated_listview
// calls, similar to when painting. The actual model change is triggered via simulated
// mouse clicks into the touch area further down.
// Note: The C++ test uses the same test method, but due to its differing implementation
// it's not testing the same code path.
import { ListView } from "std-widgets.slint";
export component TestCase inherits Window {
width: 300px;
height: 300px;
in-out property pos <=> lv.viewport-y;
out property <int> clicked-idx: -1;
in-out property<[color]> the_model: [Colors.red, Colors.blue, Colors.yellow, Colors.pink, Colors.orange, Colors.aliceblue, Colors.limegreen];
lv := ListView {
for col[idx] in the_model: Rectangle {
height: 100px;
background: col;
TouchArea {
clicked => {
clicked-idx = idx;
the_model = [Colors.gray];
}
}
}
}
}
/*
```cpp
auto handle = TestCase::create();
const TestCase &instance = *handle;
instance.set_pos(-400.);
slint_testing::send_mouse_click(&instance, 5., 5.);
assert_eq(instance.get_clicked_idx(), 4);
slint_testing::send_mouse_click(&instance, 5., 5.);
```
```rust
let instance = TestCase::new().unwrap();
instance.set_pos(-400.);
slint_testing::send_mouse_click(&instance, 5., 5.);
assert_eq!(instance.get_clicked_idx(), 4);
slint_testing::send_mouse_click(&instance, 5., 5.);
assert_eq!(instance.get_clicked_idx(), 0);
```
*/