ListView: position delegate items at zero on the inactive axis

FxListItemSG::setPosition() only cares about a single axis, which is
based on the current orientation. If the orientation is Vertical, then
the y axis will be used with positioning the delegate item, and if the
orientation is Horizontal, then the x axis will be used instead.

This would cause problems when the ListView is changing its orientation.
Because if the ListView's orientation suddently changes from Vertical to
Horizontal, then all the delegate items will keep their old y value,
which FxListItemSG::setPosition() won't do anything about since the new
"active" axis is now the x axis.

This patch fixes the issue by resetting the value for the "inactive"
axis back to zero, in pointForPosition() which is used by
FxListItemSG::setPosition().

The problem wasn't noticeable when using a normal QQmlDelegateModel,
since the delegate items would be created lazily when they needed to
appear inside the viewport, at which point they'd be given the correct x
and y values. (The position is first (0, 0), and then layoutVisibleItems
will updated one of those values based on the orientation).
But if the user decides to use a QQmlObjectModel based model instead,
then the delegate items would all be created immediately with a
persistent lifetime, which would never have their position reset as a
natural result of the item view delegate lifecycle mechanism.

Fixes: QTBUG-115696
Change-Id: I5377aeb556be2f536794f489b8232c5b9675ab78
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
(cherry picked from commit f03a9839b6)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
(cherry picked from commit 27beeb7ccb)
This commit is contained in:
Oliver Eftevaag 2023-11-16 13:31:19 +01:00 committed by Qt Cherry-pick Bot
parent 150eae8cde
commit a4cc75c3bf
4 changed files with 142 additions and 4 deletions

View File

@ -326,21 +326,21 @@ private:
if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop) {
if (section())
pos += section()->height();
return QPointF(itemX(), -itemHeight() - pos);
return QPointF(0, -itemHeight() - pos);
} else {
if (section())
pos += section()->height();
return QPointF(itemX(), pos);
return QPointF(0, pos);
}
} else {
if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
if (section())
pos += section()->width();
return QPointF(-itemWidth() - pos, itemY());
return QPointF(-itemWidth() - pos, 0);
} else {
if (section())
pos += section()->width();
return QPointF(pos, itemY());
return QPointF(pos, 0);
}
}
}

View File

@ -0,0 +1,47 @@
import QtQuick
ListView {
id: root
function allDelegates(valueSelector) {
let sum = 0;
for (let i = 0; i < root.count; i++)
sum += valueSelector(root.itemAtIndex(i));
return sum;
}
readonly property bool isXReset: allDelegates(function(item) { return item?.x ?? 0; }) === 0
readonly property bool isYReset: allDelegates(function(item) { return item?.y ?? 0; }) === 0
width: 500
height: 500
delegate: Rectangle {
width: root.width
height: root.height
color: c
}
model: ListModel {
ListElement {
c: "red"
}
ListElement {
c: "green"
}
ListElement {
c: "blue"
}
ListElement {
c: "cyan"
}
ListElement {
c: "magenta"
}
ListElement {
c: "teal"
}
}
clip: true
orientation: ListView.Vertical
snapMode: ListView.SnapOneItem
highlightRangeMode: ListView.StrictlyEnforceRange
}

View File

@ -0,0 +1,54 @@
import QtQuick
ListView {
id: root
readonly property bool isXReset: red.x === 0 && green.x === 0 && blue.x === 0 && cyan.x === 0 && magenta.x === 0 && teal.x === 0
readonly property bool isYReset: red.y === 0 && green.y === 0 && blue.y === 0 && cyan.y === 0 && magenta.y === 0 && teal.y === 0
width: 500
height: 500
model: ObjectModel {
Rectangle {
id: red
width: root.width
height: root.height
color: "red"
}
Rectangle {
id: green
width: root.width
height: root.height
color: "green"
}
Rectangle {
id: blue
width: root.width
height: root.height
color: "blue"
}
Rectangle {
id: cyan
width: root.width
height: root.height
color: "cyan"
}
Rectangle {
id: magenta
width: root.width
height: root.height
color: "magenta"
}
Rectangle {
id: teal
width: root.width
height: root.height
color: "teal"
}
}
clip: true
orientation: ListView.Vertical
snapMode: ListView.SnapOneItem
highlightRangeMode: ListView.StrictlyEnforceRange
}

View File

@ -60,6 +60,9 @@ private slots:
void fetchMore_data();
void fetchMore();
void changingOrientationResetsPreviousAxisValues_data();
void changingOrientationResetsPreviousAxisValues();
private:
void flickWithTouch(QQuickWindow *window, const QPoint &from, const QPoint &to);
QScopedPointer<QPointingDevice> touchDevice = QScopedPointer<QPointingDevice>(QTest::createTouchDevice());
@ -1116,6 +1119,40 @@ void tst_QQuickListView2::fetchMore() // QTBUG-95107
QCOMPARE_GE(model.m_lines, listView->count()); // fetchMore() was called
}
}
void tst_QQuickListView2::changingOrientationResetsPreviousAxisValues_data()
{
QTest::addColumn<QByteArray>("sourceFile");
QTest::newRow("ObjectModel") << QByteArray("changingOrientationWithObjectModel.qml");
QTest::newRow("ListModel") << QByteArray("changingOrientationWithListModel.qml");
}
void tst_QQuickListView2::changingOrientationResetsPreviousAxisValues() // QTBUG-115696
{
QFETCH(QByteArray, sourceFile);
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl(QString::fromLatin1(sourceFile))));
auto *listView = qobject_cast<QQuickListView *>(window.rootObject());
QVERIFY(listView);
// Starts of with vertical orientation. X should be 0 for all delegates, but not Y.
QVERIFY(listView->property("isXReset").toBool());
QVERIFY(!listView->property("isYReset").toBool());
listView->setOrientation(QQuickListView::Orientation::Horizontal);
// Y should be 0 for all delegates, but not X.
QVERIFY(!listView->property("isXReset").toBool());
QVERIFY(listView->property("isYReset").toBool());
listView->setOrientation(QQuickListView::Orientation::Vertical);
// X should be 0 for all delegates, but not Y.
QVERIFY(listView->property("isXReset").toBool());
QVERIFY(!listView->property("isYReset").toBool());
}
QTEST_MAIN(tst_QQuickListView2)
#include "tst_qquicklistview2.moc"