diff options
| author | Santhosh Kumar <santhosh.kumar.selvaraj@qt.io> | 2025-07-31 11:56:28 +0200 |
|---|---|---|
| committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2025-08-14 22:22:45 +0000 |
| commit | 82cbc476ebbdcbcbff8ca4675731ea2fdfd1524c (patch) | |
| tree | 689653e3e8b70240a6cfaf5a9697d5743fa5d4ab | |
| parent | a645673b48ba2098fc87857d3dc69da6500eefc8 (diff) | |
Skip reordering content model if items aren't reparented to item view
The container reorders the content model items after component
completion. The content model can be assigned as a model to the item
views to visualise through a container content item. The content item
can have item views anywhere in the object hierarchy. If the item
views are somewhere down in the line and not at the top, there is
a chance during component completion that some of the items are
reparented to the item views, and others are with the container
content item itself. Thus, reordering at this stage (having partial
items) can lead to changing the order of elements within the content
model, which further causes repositioning of items within the item
views.
This patch resolves this issue by skipping reordering the content
model, if any of the items are not reparented to the item views.
Fixes: QTBUG-138490
Change-Id: I1673e600774e2821653542c003d2d573f62d024d
Reviewed-by: Oliver Eftevaag <oliver.eftevaag@qt.io>
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
(cherry picked from commit 0d1730681dd2cd6ef0638c94d7bddf9e22bd37e0)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
(cherry picked from commit 9b25239d7f2b0a7c85bee7e0520888f71bc7e8aa)
(cherry picked from commit dfa694cca0ac64494749f44e198ed09c14512c58)
(cherry picked from commit f04c0d65fbefedb9c4a8fc86dc23ba9e6d5cebc6)
3 files changed, 119 insertions, 9 deletions
diff --git a/src/quicktemplates/qquickcontainer.cpp b/src/quicktemplates/qquickcontainer.cpp index 890e72f5bc..06390e4175 100644 --- a/src/quicktemplates/qquickcontainer.cpp +++ b/src/quicktemplates/qquickcontainer.cpp @@ -299,15 +299,37 @@ void QQuickContainerPrivate::reorderItems() if (!contentItem) return; - QList<QQuickItem *> siblings = effectiveContentItem(contentItem)->childItems(); - - int to = 0; - for (int i = 0; i < siblings.size(); ++i) { - QQuickItem* sibling = siblings.at(i); - if (QQuickItemPrivate::get(sibling)->isTransparentForPositioner()) - continue; - int index = contentModel->indexOf(sibling, nullptr); - q->moveItem(index, to++); + // The item view eventually reparents all the items of the content model + // from the container. At this stage (during component complete), however, due + // to optimisation strategies in the item views, this doesn't happen when the + // visible area of the item view is less than the total content width of the items + // within the content model. This can cause issues while reordering. Thus, it's + // better to skip reordering the item within the content model once it's known + // that it will be reparented to the item views. + bool allowReorder = true; + if (!qobject_cast<QQuickFlickable *>(contentItem)) { + for (int index = 0; index < contentModel->count(); index++) { + if (const auto *item = qobject_cast<QQuickItem *>(contentModel->get(index))) { + const auto *parentItem = item->parentItem(); + if (parentItem && !qobject_cast<QQuickItemView *>(parentItem->parentItem())) { + allowReorder = false; + break; + } + } + } + } + + if (allowReorder) { + QList<QQuickItem *> siblings = effectiveContentItem(contentItem)->childItems(); + int to = 0; + for (int i = 0; i < siblings.size(); ++i) { + QQuickItem* sibling = siblings.at(i); + if (QQuickItemPrivate::get(sibling)->isTransparentForPositioner()) + continue; + const int index = contentModel->indexOf(sibling, nullptr); + if (index >= 0) + q->moveItem(index, to++); + } } } diff --git a/tests/auto/quickcontrols/qquickcontainer/data/skipReorderContentModelItem.qml b/tests/auto/quickcontrols/qquickcontainer/data/skipReorderContentModelItem.qml new file mode 100644 index 0000000000..9074904026 --- /dev/null +++ b/tests/auto/quickcontrols/qquickcontainer/data/skipReorderContentModelItem.qml @@ -0,0 +1,57 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick +import QtQuick.Controls +import QtQml.Models + +Window { + width: 640 + height: 480 + + property alias navigationBar: navigationbar + + component NavigationBar: Container { + id: container + + property alias model: repeater.model + property alias listView: list + + implicitHeight: 20 + + contentItem: Item { + width: parent.width + height: parent.height + + ListView { + id: list + model: container.contentModel + snapMode: ListView.SnapToItem + orientation: ListView.Horizontal + interactive: width < contentWidth + clip: interactive + spacing: 20 + + anchors { + right: parent.right + top: parent.top + bottom: parent.bottom + left: parent.left + } + } + } + + Repeater { + id: repeater + delegate: Text { + text: modelData + } + } + } + + NavigationBar { + id: navigationbar + width: 100 + model: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"] + } +} diff --git a/tests/auto/quickcontrols/qquickcontainer/tst_qquickcontainer.cpp b/tests/auto/quickcontrols/qquickcontainer/tst_qquickcontainer.cpp index ce95f2cc37..b61717e4c9 100644 --- a/tests/auto/quickcontrols/qquickcontainer/tst_qquickcontainer.cpp +++ b/tests/auto/quickcontrols/qquickcontainer/tst_qquickcontainer.cpp @@ -8,6 +8,8 @@ #include <QtQuickTestUtils/private/qmlutils_p.h> #include <QtQuickTestUtils/private/visualtestutils_p.h> #include <QtQuickControlsTestUtils/private/controlstestutils_p.h> +#include <QtQuick/private/qquicklistview_p.h> +#include <QtQmlModels/private/qqmlobjectmodel_p.h> using namespace QQuickVisualTestUtils; using namespace QQuickControlsTestUtils; @@ -22,6 +24,7 @@ public: private slots: void zeroSize_data(); void zeroSize(); + void skipReorderContentModelItem(); }; tst_qquickcontainer::tst_qquickcontainer() @@ -102,6 +105,34 @@ void tst_qquickcontainer::zeroSize() QCOMPARE(QQuickItemPrivate::get(text4)->culled, isItemView); } +void tst_qquickcontainer::skipReorderContentModelItem() +{ + QQuickControlsApplicationHelper helper(this, "skipReorderContentModelItem.qml"); + QVERIFY2(helper.ready, helper.failureMessage()); + centerOnScreen(helper.window); + helper.window->show(); + QVERIFY(QTest::qWaitForWindowExposed(helper.window)); + + const auto *container = helper.window->property("navigationBar").value<QQuickContainer *>(); + QVERIFY(container); + const auto *listView = container->property("listView").value<QQuickListView *>(); + QVERIFY(listView); + const auto *contentModel = container->property("contentModel").value<QQmlObjectModel *>(); + QVERIFY(contentModel); + + QCOMPARE(listView->count(), contentModel->count()); + + int totalWidth = listView->spacing() * (contentModel->count() - 1); + for (int index = 0; index < contentModel->count(); index++) + totalWidth += qobject_cast<QQuickText *>(listView->itemAtIndex(index))->width(); + QVERIFY(totalWidth > listView->width()); + + for (int index = 0; index < listView->count(); index++) { + const auto *textItem = qobject_cast<QQuickText *>(listView->itemAtIndex(index)); + QCOMPARE(textItem->text().toInt(), index + 1); + } +} + QTEST_MAIN(tst_qquickcontainer) #include "tst_qquickcontainer.moc" |
