Compare commits
No commits in common. "factory" and "factory" have entirely different histories.
@ -1,45 +0,0 @@
|
||||
From e6e3da4de8fac7f52eb2591cb95a04ab413f8ace Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Olivier=20De=20Canni=C3=A8re?= <olivier.decanniere@qt.io>
|
||||
Date: Wed, 9 Oct 2024 09:41:27 +0200
|
||||
Subject: [PATCH] Compiler: Wrap raw string literals in QStringLiteral in
|
||||
generated code
|
||||
|
||||
Amends e7146cd98700c29802214a39a4cefd9db146c97a
|
||||
|
||||
Fixes: QTBUG-129797
|
||||
Change-Id: I5dc48412cf29bd2de877dd681ea309a6e74d75c5
|
||||
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
|
||||
(cherry picked from commit d63c560ec4ce79c1bb24e1a01a20de41706dd039)
|
||||
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
|
||||
---
|
||||
src/qmlcompiler/qqmljscodegenerator.cpp | 8 ++++----
|
||||
1 file changed, 4 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp
|
||||
index 807168ca09..e5622fe6f9 100644
|
||||
--- a/src/qmlcompiler/qqmljscodegenerator.cpp
|
||||
+++ b/src/qmlcompiler/qqmljscodegenerator.cpp
|
||||
@@ -77,16 +77,16 @@ QString QQmlJSCodeGenerator::compositeListMetaType(const QString &elementName) c
|
||||
{
|
||||
return u"QQmlPrivate::compositeListMetaType(aotContext->compilationUnit, "_s
|
||||
+ (m_jsUnitGenerator->hasStringId(elementName)
|
||||
- ? QString::number(m_jsUnitGenerator->getStringId(elementName))
|
||||
- : u'"' + elementName + u'"') + u")"_s;
|
||||
+ ? QString::number(m_jsUnitGenerator->getStringId(elementName)) + u')'
|
||||
+ : u"QStringLiteral(\"%1\"))"_s.arg(elementName));
|
||||
}
|
||||
|
||||
QString QQmlJSCodeGenerator::compositeMetaType(const QString &elementName) const
|
||||
{
|
||||
return u"QQmlPrivate::compositeMetaType(aotContext->compilationUnit, "_s
|
||||
+ (m_jsUnitGenerator->hasStringId(elementName)
|
||||
- ? QString::number(m_jsUnitGenerator->getStringId(elementName))
|
||||
- : u'"' + elementName + u'"') + u")"_s;
|
||||
+ ? QString::number(m_jsUnitGenerator->getStringId(elementName)) + u')'
|
||||
+ : u"QStringLiteral(\"%1\"))"_s.arg(elementName));
|
||||
}
|
||||
|
||||
QString QQmlJSCodeGenerator::metaObject(const QQmlJSScope::ConstPtr &objectType)
|
||||
--
|
||||
2.47.0
|
||||
|
@ -1,116 +0,0 @@
|
||||
From 71a9938f4e093c77fc7b9f1cf8043e6d3552aff3 Mon Sep 17 00:00:00 2001
|
||||
From: Aleix Pol <aleixpol@kde.org>
|
||||
Date: Tue, 22 Oct 2024 02:47:48 +0200
|
||||
Subject: [PATCH] QQuickAccessibleAttached: Let implicit names work when
|
||||
there's a proxy
|
||||
|
||||
QQuickAccessibleAttached has infrastructure to provide an implicit name
|
||||
that we were bypassing now that we have proxies (introduced in 6.8).
|
||||
We should only use the name of the proxy when it's being set, otherwise
|
||||
we better provide the implicit name.
|
||||
|
||||
Amends 7bdeea2c309150c8b49558b135232926d6a89c50.
|
||||
|
||||
Fixes: QTBUG-130360
|
||||
Pick-to: 6.8
|
||||
Change-Id: I37470e1f28e837bdbb304726ee85f45ba6577621
|
||||
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
||||
---
|
||||
src/quick/items/qquickaccessibleattached_p.h | 2 +-
|
||||
.../data/actionAccessibility/button2.qml | 9 +++++
|
||||
.../accessibility/tst_accessibility.cpp | 36 +++++++++++++++++++
|
||||
3 files changed, 46 insertions(+), 1 deletion(-)
|
||||
create mode 100644 tests/auto/quickcontrols/accessibility/data/actionAccessibility/button2.qml
|
||||
|
||||
diff --git a/src/quick/items/qquickaccessibleattached_p.h b/src/quick/items/qquickaccessibleattached_p.h
|
||||
index aa15b43..2d44000 100644
|
||||
--- a/src/quick/items/qquickaccessibleattached_p.h
|
||||
+++ b/src/quick/items/qquickaccessibleattached_p.h
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
QString name() const {
|
||||
if (m_state.passwordEdit)
|
||||
return QString();
|
||||
- if (m_proxying)
|
||||
+ if (!m_nameExplicitlySet && m_proxying && m_proxying->wasNameExplicitlySet())
|
||||
return m_proxying->name();
|
||||
return m_name;
|
||||
}
|
||||
diff --git a/tests/auto/quickcontrols/accessibility/data/actionAccessibility/button2.qml b/tests/auto/quickcontrols/accessibility/data/actionAccessibility/button2.qml
|
||||
new file mode 100644
|
||||
index 0000000..1cd1c81
|
||||
--- /dev/null
|
||||
+++ b/tests/auto/quickcontrols/accessibility/data/actionAccessibility/button2.qml
|
||||
@@ -0,0 +1,9 @@
|
||||
+import QtQuick
|
||||
+import QtQuick.Controls
|
||||
+
|
||||
+Button {
|
||||
+ action: Action {
|
||||
+ text: "Pears"
|
||||
+ Accessible.description: "Show pears some love"
|
||||
+ }
|
||||
+}
|
||||
diff --git a/tests/auto/quickcontrols/accessibility/tst_accessibility.cpp b/tests/auto/quickcontrols/accessibility/tst_accessibility.cpp
|
||||
index 8bdd945..2018c0e 100644
|
||||
--- a/tests/auto/quickcontrols/accessibility/tst_accessibility.cpp
|
||||
+++ b/tests/auto/quickcontrols/accessibility/tst_accessibility.cpp
|
||||
@@ -33,6 +33,8 @@ private slots:
|
||||
void ordering();
|
||||
|
||||
void actionAccessibility();
|
||||
+ void actionAccessibilityImplicitName();
|
||||
+
|
||||
private:
|
||||
QQmlEngine engine;
|
||||
};
|
||||
@@ -279,6 +281,13 @@ void tst_accessibility::ordering()
|
||||
void tst_accessibility::actionAccessibility()
|
||||
{
|
||||
#if QT_CONFIG(accessibility)
|
||||
+ if (!QAccessible::isActive()) {
|
||||
+ QPlatformAccessibility *accessibility = platformAccessibility();
|
||||
+ if (!accessibility)
|
||||
+ QSKIP("No QPlatformAccessibility available.");
|
||||
+ accessibility->setActive(true);
|
||||
+ }
|
||||
+
|
||||
QQmlComponent component(&engine);
|
||||
component.loadUrl(testFileUrl("actionAccessibility/button.qml"));
|
||||
|
||||
@@ -296,6 +305,33 @@ void tst_accessibility::actionAccessibility()
|
||||
#endif
|
||||
}
|
||||
|
||||
+void tst_accessibility::actionAccessibilityImplicitName()
|
||||
+{
|
||||
+#if QT_CONFIG(accessibility)
|
||||
+ if (!QAccessible::isActive()) {
|
||||
+ QPlatformAccessibility *accessibility = platformAccessibility();
|
||||
+ if (!accessibility)
|
||||
+ QSKIP("No QPlatformAccessibility available.");
|
||||
+ accessibility->setActive(true);
|
||||
+ }
|
||||
+
|
||||
+ QQmlComponent component(&engine);
|
||||
+ component.loadUrl(testFileUrl("actionAccessibility/button2.qml"));
|
||||
+
|
||||
+ QScopedPointer<QObject> object(component.create());
|
||||
+ QVERIFY2(!object.isNull(), qPrintable(component.errorString()));
|
||||
+
|
||||
+ QQuickItem *item = qobject_cast<QQuickItem *>(object.data());
|
||||
+ QVERIFY(item);
|
||||
+ const QString description = "Show pears some love";
|
||||
+ QCOMPARE(item->property("text"), "Pears");
|
||||
+ QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(item);
|
||||
+ QVERIFY(iface);
|
||||
+ QCOMPARE(iface->text(QAccessible::Name), "Pears"); // We get the action.text implicitly
|
||||
+ QCOMPARE(iface->text(QAccessible::Description), description);
|
||||
+#endif
|
||||
+}
|
||||
+
|
||||
QTEST_MAIN(tst_accessibility)
|
||||
|
||||
#include "tst_accessibility.moc"
|
||||
--
|
||||
2.47.0
|
||||
|
@ -1,126 +0,0 @@
|
||||
From 0ae3697cf40bcd3ae1de20621abad17cf6c5f52d Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= <tor.arne.vestbo@qt.io>
|
||||
Date: Wed, 2 Oct 2024 17:04:25 +0200
|
||||
Subject: [PATCH] QQuickItem::map{To,From}Item: Account for not having a window
|
||||
yet
|
||||
|
||||
Prior to 06ace3e226b2394362e27d1bc4743c1170777af1 we would not
|
||||
account for items in different scenes, and would map them as if
|
||||
they were part of the same scene.
|
||||
|
||||
After 06ace3e226b2394362e27d1bc4743c1170777af1 we took the scenes
|
||||
into account, but failed to guard against one or both of the items
|
||||
not having a scene (yet), causing potential crashes.
|
||||
|
||||
We now properly check that both items have a scene before trying
|
||||
any scene to scene mapping.
|
||||
|
||||
The semantics if one or both of the items are not in a scene is
|
||||
kept as it was before 06ace3e226b2394362e27d1bc4743c1170777af1,
|
||||
where we assume the two items are part of the same scene, and an
|
||||
item without a parent is assumed to be the root of its scene.
|
||||
|
||||
Fixes: QTBUG-129500
|
||||
Change-Id: I897faf73d04dddd68a7a8797e5238743efdd4f73
|
||||
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
|
||||
(cherry picked from commit 08b0e3e39e899bea013057a7ac038b8878809712)
|
||||
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
|
||||
---
|
||||
src/quick/items/qquickitem.cpp | 14 +++++-----
|
||||
.../data/mapCoordinatesWithWindows.qml | 11 ++++++++
|
||||
.../auto/quick/qquickitem2/tst_qquickitem.cpp | 27 +++++++++++++++++++
|
||||
3 files changed, 46 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
|
||||
index 9288df17a4..802f4dad55 100644
|
||||
--- a/src/quick/items/qquickitem.cpp
|
||||
+++ b/src/quick/items/qquickitem.cpp
|
||||
@@ -8755,9 +8755,10 @@ QPointF QQuickItem::mapToItem(const QQuickItem *item, const QPointF &point) cons
|
||||
{
|
||||
QPointF p = mapToScene(point);
|
||||
if (item) {
|
||||
- const QQuickWindow *itemWindow = item->window();
|
||||
- if (itemWindow != nullptr && itemWindow != window())
|
||||
- p = itemWindow->mapFromGlobal(window()->mapToGlobal(p));
|
||||
+ const auto *itemWindow = item->window();
|
||||
+ const auto *thisWindow = window();
|
||||
+ if (thisWindow && itemWindow && itemWindow != thisWindow)
|
||||
+ p = itemWindow->mapFromGlobal(thisWindow->mapToGlobal(p));
|
||||
|
||||
p = item->mapFromScene(p);
|
||||
}
|
||||
@@ -8862,9 +8863,10 @@ QPointF QQuickItem::mapFromItem(const QQuickItem *item, const QPointF &point) co
|
||||
QPointF p = point;
|
||||
if (item) {
|
||||
p = item->mapToScene(point);
|
||||
-
|
||||
- if (item->window() != window())
|
||||
- p = window()->mapFromGlobal(item->window()->mapToGlobal(p));
|
||||
+ const auto *itemWindow = item->window();
|
||||
+ const auto *thisWindow = window();
|
||||
+ if (thisWindow && itemWindow && itemWindow != thisWindow)
|
||||
+ p = thisWindow->mapFromGlobal(itemWindow->mapToGlobal(p));
|
||||
}
|
||||
return mapFromScene(p);
|
||||
}
|
||||
diff --git a/tests/auto/quick/qquickitem2/data/mapCoordinatesWithWindows.qml b/tests/auto/quick/qquickitem2/data/mapCoordinatesWithWindows.qml
|
||||
index 98fc0e77af..1696699a75 100644
|
||||
--- a/tests/auto/quick/qquickitem2/data/mapCoordinatesWithWindows.qml
|
||||
+++ b/tests/auto/quick/qquickitem2/data/mapCoordinatesWithWindows.qml
|
||||
@@ -48,4 +48,15 @@ QtObject {
|
||||
color: "cyan"
|
||||
}
|
||||
}
|
||||
+
|
||||
+ property Item itemWithoutWindowA: Item {
|
||||
+ x: 20; y: 20
|
||||
+ }
|
||||
+ property Item itemWithoutWindowB: Item {
|
||||
+ x: 40; y: 40
|
||||
+ Item {
|
||||
+ objectName: "childItemWithoutWindow"
|
||||
+ x: 30; y: 30
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
|
||||
index 154c4a661f..eb4f62e65f 100644
|
||||
--- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
|
||||
+++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
|
||||
@@ -2945,6 +2945,33 @@ void tst_QQuickItem::mapCoordinatesWithWindows()
|
||||
globalItemOffset(childItemInChildWindow, childItemInOtherWindow));
|
||||
QCOMPARE(childItemInChildWindow->mapFromItem(childItemInOtherWindow, {0, 0}),
|
||||
globalItemOffset(childItemInOtherWindow, childItemInChildWindow));
|
||||
+
|
||||
+ // If one or both of the items are not in a scene (yet), they are assumed
|
||||
+ // to eventually be in the same scene.
|
||||
+
|
||||
+ auto *itemWithoutWindowA = root->property("itemWithoutWindowA").value<QQuickItem*>();
|
||||
+ QVERIFY(itemWithoutWindowA);
|
||||
+ auto *itemWithoutWindowB = root->property("itemWithoutWindowB").value<QQuickItem*>();
|
||||
+ QVERIFY(itemWithoutWindowB);
|
||||
+ auto *childItemWithoutWindow = itemWithoutWindowB->findChild<QQuickItem*>("childItemWithoutWindow");
|
||||
+ QVERIFY(childItemWithoutWindow);
|
||||
+
|
||||
+ QPoint itemWithoutWindowAPos = itemWithoutWindowA->position().toPoint();
|
||||
+ QPoint itemWithoutWindowBPos = itemWithoutWindowB->position().toPoint();
|
||||
+
|
||||
+ QCOMPARE(itemWithoutWindowA->mapToItem(childItemWithoutWindow, {0, 0}),
|
||||
+ itemWithoutWindowAPos - (itemWithoutWindowBPos + childItemWithoutWindow->position()));
|
||||
+ QCOMPARE(itemWithoutWindowA->mapFromItem(childItemWithoutWindow, {0, 0}),
|
||||
+ (itemWithoutWindowBPos + childItemWithoutWindow->position()) - itemWithoutWindowAPos);
|
||||
+
|
||||
+ QCOMPARE(itemWithoutWindowA->mapToItem(childItem, {0, 0}),
|
||||
+ itemWithoutWindowAPos - itemPos);
|
||||
+ QCOMPARE(itemWithoutWindowA->mapFromItem(childItem, {0, 0}),
|
||||
+ itemPos - itemWithoutWindowAPos);
|
||||
+ QCOMPARE(childItem->mapToItem(itemWithoutWindowA, {0, 0}),
|
||||
+ itemPos - itemWithoutWindowAPos);
|
||||
+ QCOMPARE(childItem->mapFromItem(itemWithoutWindowA, {0, 0}),
|
||||
+ itemWithoutWindowAPos - itemPos);
|
||||
}
|
||||
|
||||
void tst_QQuickItem::transforms_data()
|
||||
--
|
||||
2.47.0
|
||||
|
@ -1,127 +0,0 @@
|
||||
From c343a0bb2a8f87758bf97241223224770e5c4687 Mon Sep 17 00:00:00 2001
|
||||
From: Mitch Curtis <mitch.curtis@qt.io>
|
||||
Date: Tue, 22 Oct 2024 13:26:08 +0800
|
||||
Subject: [PATCH] QQuickItemView: fix crash with zero-size SwipeView that uses
|
||||
Repeater
|
||||
|
||||
When running the minimal example from the bug report, the order of
|
||||
events is:
|
||||
|
||||
1. deletables are iterated in QQuickRepeater::clear(), and deleteLater
|
||||
is called on the delegate item.
|
||||
2. deletables are unparented. This causes Container (SwipeView) to be
|
||||
notified of the parent change and it removes the item. Part of this
|
||||
involves changing the currentIndex, since the removed item was current.
|
||||
3. SwipeView's contentItem (ListView) has its currentIndex bound to the
|
||||
container's, so QQuickItemView::setCurrentIndex is called.
|
||||
4. setCurrentIndex calls updateCurrent, which detects that the
|
||||
currentIndex (which is -1, because there was only one item) is out of
|
||||
range and so releases the item. This causes it to be added to
|
||||
unrequestedItems, even though it has been scheduled for deletion.
|
||||
|
||||
This patch makes QQuickItemView detect that the item is going to be
|
||||
deleted and not add it to the list of deletables.
|
||||
|
||||
Fixes: QTBUG-129622
|
||||
Pick-to: 6.5 6.8
|
||||
Change-Id: I999aedbdfafc61ff6d33eb6579331f470e9c1454
|
||||
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
|
||||
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
||||
---
|
||||
src/quick/items/qquickitemview.cpp | 4 +-
|
||||
.../controls/data/tst_swipeview.qml | 59 +++++++++++++++++++
|
||||
2 files changed, 62 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp
|
||||
index 19e5b7f..dabd563 100644
|
||||
--- a/src/quick/items/qquickitemview.cpp
|
||||
+++ b/src/quick/items/qquickitemview.cpp
|
||||
@@ -2499,7 +2499,9 @@ bool QQuickItemViewPrivate::releaseItem(FxViewItem *item, QQmlInstanceModel::Reu
|
||||
// One case where this can happen is moving an item out of one ObjectModel and into another.
|
||||
QQuickItemPrivate::get(item->item)->setCulled(true);
|
||||
}
|
||||
- if (!isClearing)
|
||||
+ // If deleteLater was called, the item isn't long for this world and so we shouldn't store references to it.
|
||||
+ // This can happen when a Repeater is used to populate items in SwipeView's ListView contentItem.
|
||||
+ if (!isClearing && !QObjectPrivate::get(item->item)->deleteLaterCalled)
|
||||
unrequestedItems.insert(item->item, model->indexOf(item->item, q));
|
||||
} else if (flags & QQmlInstanceModel::Destroyed) {
|
||||
item->item->setParentItem(nullptr);
|
||||
diff --git a/tests/auto/quickcontrols/controls/data/tst_swipeview.qml b/tests/auto/quickcontrols/controls/data/tst_swipeview.qml
|
||||
index 3a7558c..a3dd16c 100644
|
||||
--- a/tests/auto/quickcontrols/controls/data/tst_swipeview.qml
|
||||
+++ b/tests/auto/quickcontrols/controls/data/tst_swipeview.qml
|
||||
@@ -4,6 +4,7 @@
|
||||
import QtQuick
|
||||
import QtTest
|
||||
import QtQuick.Controls
|
||||
+import QtQuick.Layouts
|
||||
|
||||
TestCase {
|
||||
id: testCase
|
||||
@@ -760,4 +761,62 @@ TestCase {
|
||||
tryCompare(swipeListView, "contentX", swipeListView.width, 1000)
|
||||
compare(item2.x, swipeListView.width)
|
||||
}
|
||||
+
|
||||
+ Component {
|
||||
+ id: zeroSizeSwipeViewWithRepeatersComponent
|
||||
+
|
||||
+ Item {
|
||||
+ objectName: "rootItem"
|
||||
+ anchors.fill: parent
|
||||
+
|
||||
+ property alias swipeView: swipeView
|
||||
+ property int d
|
||||
+
|
||||
+ Timer {
|
||||
+ interval: 2
|
||||
+ running: true
|
||||
+ repeat: false
|
||||
+ onTriggered: d = 2
|
||||
+ }
|
||||
+
|
||||
+ SwipeView {
|
||||
+ id: swipeView
|
||||
+ contentItem.objectName: "swipeViewListView"
|
||||
+
|
||||
+ Repeater {
|
||||
+ objectName: "swipeViewContentItemRepeater"
|
||||
+ model: [
|
||||
+ {
|
||||
+ title: d
|
||||
+ }
|
||||
+ ]
|
||||
+
|
||||
+ delegate: GridLayout {
|
||||
+ objectName: "gridLayoutDelegate"
|
||||
+
|
||||
+ Repeater {
|
||||
+ id: repeater
|
||||
+ objectName: "delegateRepeater"
|
||||
+ model: d
|
||||
+ delegate: Item {
|
||||
+ objectName: "delegate" + index
|
||||
+
|
||||
+ required property int index
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // QTBUG-129622
|
||||
+ function test_zeroSizeSwipeViewWithRepeaters() {
|
||||
+ let root = createTemporaryObject(zeroSizeSwipeViewWithRepeatersComponent, testCase)
|
||||
+ verify(root)
|
||||
+
|
||||
+ let swipeView = root.swipeView
|
||||
+ tryCompare(root, "d", 2)
|
||||
+ // Shouldn't crash when the model is changed.
|
||||
+ }
|
||||
}
|
||||
--
|
||||
2.47.0
|
||||
|
@ -1,420 +0,0 @@
|
||||
From 3330731d0cb221477ab3d856db032126403ae6a0 Mon Sep 17 00:00:00 2001
|
||||
From: Mitch Curtis <mitch.curtis@qt.io>
|
||||
Date: Tue, 24 Sep 2024 08:18:14 +0800
|
||||
Subject: [PATCH 1/2] Revert "QQmlDelegateModel: fix delegates not being
|
||||
created in certain cases"
|
||||
|
||||
This reverts commit 6561344dd2d1ba69abe6edec4fe340b256da9e13. It needs
|
||||
to be fixed in a different way.
|
||||
|
||||
Fixes: QTBUG-127340
|
||||
Pick-to: 6.7 6.5
|
||||
Change-Id: I8503b22a5257e0fb5ee11a1bdf83d3dcab4a600a
|
||||
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
|
||||
(cherry picked from commit 281f620ceea03e7a222d796ae0cca917a9778368)
|
||||
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
|
||||
---
|
||||
src/qmlmodels/qqmldelegatemodel.cpp | 61 +++-----
|
||||
src/qmlmodels/qqmldelegatemodel_p_p.h | 2 -
|
||||
.../auto/qml/qqmldelegatemodel/CMakeLists.txt | 1 -
|
||||
.../auto/qml/qqmldelegatemodel/data/reset.qml | 28 ----
|
||||
.../data/resetInQAIMConstructor.qml | 28 ----
|
||||
.../tst_qqmldelegatemodel.cpp | 135 ++----------------
|
||||
6 files changed, 29 insertions(+), 226 deletions(-)
|
||||
delete mode 100644 tests/auto/qml/qqmldelegatemodel/data/reset.qml
|
||||
delete mode 100644 tests/auto/qml/qqmldelegatemodel/data/resetInQAIMConstructor.qml
|
||||
|
||||
diff --git a/src/qmlmodels/qqmldelegatemodel.cpp b/src/qmlmodels/qqmldelegatemodel.cpp
|
||||
index 9af58d7a22..7cfa662aa6 100644
|
||||
--- a/src/qmlmodels/qqmldelegatemodel.cpp
|
||||
+++ b/src/qmlmodels/qqmldelegatemodel.cpp
|
||||
@@ -172,7 +172,6 @@ QQmlDelegateModelPrivate::QQmlDelegateModelPrivate(QQmlContext *ctxt)
|
||||
, m_transaction(false)
|
||||
, m_incubatorCleanupScheduled(false)
|
||||
, m_waitingToFetchMore(false)
|
||||
- , m_maybeResetRoleNames(false)
|
||||
, m_cacheItems(nullptr)
|
||||
, m_items(nullptr)
|
||||
, m_persistedItems(nullptr)
|
||||
@@ -366,7 +365,6 @@ void QQmlDelegateModelPrivate::connectToAbstractItemModel()
|
||||
QObject::connect(aim, &QAbstractItemModel::dataChanged, q, &QQmlDelegateModel::_q_dataChanged);
|
||||
QObject::connect(aim, &QAbstractItemModel::rowsMoved, q, &QQmlDelegateModel::_q_rowsMoved);
|
||||
QObject::connect(aim, &QAbstractItemModel::modelAboutToBeReset, q, &QQmlDelegateModel::_q_modelAboutToBeReset);
|
||||
- QObject::connect(aim, &QAbstractItemModel::modelReset, q, &QQmlDelegateModel::handleModelReset);
|
||||
QObject::connect(aim, &QAbstractItemModel::layoutChanged, q, &QQmlDelegateModel::_q_layoutChanged);
|
||||
}
|
||||
|
||||
@@ -387,7 +385,6 @@ void QQmlDelegateModelPrivate::disconnectFromAbstractItemModel()
|
||||
QObject::disconnect(aim, &QAbstractItemModel::dataChanged, q, &QQmlDelegateModel::_q_dataChanged);
|
||||
QObject::disconnect(aim, &QAbstractItemModel::rowsMoved, q, &QQmlDelegateModel::_q_rowsMoved);
|
||||
QObject::disconnect(aim, &QAbstractItemModel::modelAboutToBeReset, q, &QQmlDelegateModel::_q_modelAboutToBeReset);
|
||||
- QObject::disconnect(aim, &QAbstractItemModel::modelReset, q, &QQmlDelegateModel::handleModelReset);
|
||||
QObject::disconnect(aim, &QAbstractItemModel::layoutChanged, q, &QQmlDelegateModel::_q_layoutChanged);
|
||||
}
|
||||
|
||||
@@ -1898,28 +1895,25 @@ void QQmlDelegateModel::_q_modelAboutToBeReset()
|
||||
Q_D(QQmlDelegateModel);
|
||||
if (!d->m_adaptorModel.adaptsAim())
|
||||
return;
|
||||
-
|
||||
- /*
|
||||
- roleNames are generally guaranteed to be stable (given that QAIM has no
|
||||
- change signal for them), except that resetting the model is allowed to
|
||||
- invalidate them (QTBUG-32132). DelegateModel must take this into account by
|
||||
- snapshotting the current roleNames before the model is reset.
|
||||
- Afterwards, if we detect that roleNames has changed, we throw the
|
||||
- current model set up away and rebuild everything from scratch – it is
|
||||
- unlikely that a more efficient implementation would be worth it.
|
||||
-
|
||||
- If we detect no changes, we simply use the existing logic to handle the
|
||||
- model reset.
|
||||
-
|
||||
- This (role name resetting) logic relies on the fact that
|
||||
- modelAboutToBeReset must be followed by a modelReset signal before any
|
||||
- further modelAboutToBeReset can occur. However, it's possible for user
|
||||
- code to begin the reset before connectToAbstractItemModel is called
|
||||
- (QTBUG-125053), in which case we don't attempt to reset the role names.
|
||||
- */
|
||||
- Q_ASSERT(!d->m_maybeResetRoleNames);
|
||||
- d->m_maybeResetRoleNames = true;
|
||||
- d->m_roleNamesBeforeReset = d->m_adaptorModel.aim()->roleNames();
|
||||
+ auto aim = d->m_adaptorModel.aim();
|
||||
+ auto oldRoleNames = aim->roleNames();
|
||||
+ // this relies on the fact that modelAboutToBeReset must be followed
|
||||
+ // by a modelReset signal before any further modelAboutToBeReset can occur
|
||||
+ QObject::connect(aim, &QAbstractItemModel::modelReset, this, [this, d, oldRoleNames, aim](){
|
||||
+ if (!d->m_adaptorModel.adaptsAim() || d->m_adaptorModel.aim() != aim)
|
||||
+ return;
|
||||
+ if (oldRoleNames == aim->roleNames()) {
|
||||
+ // if the rolenames stayed the same (most common case), then we don't have
|
||||
+ // to throw away all the setup that we did
|
||||
+ handleModelReset();
|
||||
+ } else {
|
||||
+ // If they did change, we give up and just start from scratch via setMode
|
||||
+ setModel(QVariant::fromValue(model()));
|
||||
+ // but we still have to call handleModelReset, otherwise views will
|
||||
+ // not refresh
|
||||
+ handleModelReset();
|
||||
+ }
|
||||
+ }, Qt::SingleShotConnection);
|
||||
}
|
||||
|
||||
void QQmlDelegateModel::handleModelReset()
|
||||
@@ -1929,23 +1923,6 @@ void QQmlDelegateModel::handleModelReset()
|
||||
return;
|
||||
|
||||
int oldCount = d->m_count;
|
||||
-
|
||||
- if (d->m_maybeResetRoleNames) {
|
||||
- auto aim = d->m_adaptorModel.aim();
|
||||
- if (!d->m_adaptorModel.adaptsAim() || d->m_adaptorModel.aim() != aim)
|
||||
- return;
|
||||
-
|
||||
- // If the role names stayed the same (most common case), then we don't have
|
||||
- // to throw away all the setup that we did.
|
||||
- // If they did change, we give up and just start from scratch via setModel.
|
||||
- // We do this before handling the reset to ensure that views refresh.
|
||||
- if (aim->roleNames() != d->m_roleNamesBeforeReset)
|
||||
- setModel(QVariant::fromValue(model()));
|
||||
-
|
||||
- d->m_maybeResetRoleNames = false;
|
||||
- d->m_roleNamesBeforeReset.clear();
|
||||
- }
|
||||
-
|
||||
d->m_adaptorModel.rootIndex = QModelIndex();
|
||||
|
||||
if (d->m_complete) {
|
||||
diff --git a/src/qmlmodels/qqmldelegatemodel_p_p.h b/src/qmlmodels/qqmldelegatemodel_p_p.h
|
||||
index bae8fc8a23..3c7ab9281d 100644
|
||||
--- a/src/qmlmodels/qqmldelegatemodel_p_p.h
|
||||
+++ b/src/qmlmodels/qqmldelegatemodel_p_p.h
|
||||
@@ -334,7 +334,6 @@ public:
|
||||
QQmlReusableDelegateModelItemsPool m_reusableItemsPool;
|
||||
QList<QQDMIncubationTask *> m_finishedIncubating;
|
||||
QList<QByteArray> m_watchedRoles;
|
||||
- QHash<int, QByteArray> m_roleNamesBeforeReset;
|
||||
|
||||
QString m_filterGroup;
|
||||
|
||||
@@ -348,7 +347,6 @@ public:
|
||||
bool m_transaction : 1;
|
||||
bool m_incubatorCleanupScheduled : 1;
|
||||
bool m_waitingToFetchMore : 1;
|
||||
- bool m_maybeResetRoleNames : 1;
|
||||
|
||||
union {
|
||||
struct {
|
||||
diff --git a/tests/auto/qml/qqmldelegatemodel/CMakeLists.txt b/tests/auto/qml/qqmldelegatemodel/CMakeLists.txt
|
||||
index 966f5229df..8d8a90e0a7 100644
|
||||
--- a/tests/auto/qml/qqmldelegatemodel/CMakeLists.txt
|
||||
+++ b/tests/auto/qml/qqmldelegatemodel/CMakeLists.txt
|
||||
@@ -29,7 +29,6 @@ qt_internal_add_test(tst_qqmldelegatemodel
|
||||
Qt::QmlModelsPrivate
|
||||
Qt::QmlPrivate
|
||||
Qt::Quick
|
||||
- Qt::QuickPrivate
|
||||
Qt::QuickTestUtilsPrivate
|
||||
TESTDATA ${test_data}
|
||||
)
|
||||
diff --git a/tests/auto/qml/qqmldelegatemodel/data/reset.qml b/tests/auto/qml/qqmldelegatemodel/data/reset.qml
|
||||
deleted file mode 100644
|
||||
index 0fcd5e8afa..0000000000
|
||||
--- a/tests/auto/qml/qqmldelegatemodel/data/reset.qml
|
||||
+++ /dev/null
|
||||
@@ -1,28 +0,0 @@
|
||||
-import QtQuick
|
||||
-import Test
|
||||
-
|
||||
-Window {
|
||||
- id: root
|
||||
- width: 200
|
||||
- height: 200
|
||||
-
|
||||
- property alias listView: listView
|
||||
-
|
||||
- ResettableModel {
|
||||
- id: resetModel
|
||||
- }
|
||||
-
|
||||
- ListView {
|
||||
- id: listView
|
||||
- anchors.fill: parent
|
||||
- model: resetModel
|
||||
-
|
||||
- delegate: Rectangle {
|
||||
- implicitWidth: 100
|
||||
- implicitHeight: 50
|
||||
- color: "olivedrab"
|
||||
-
|
||||
- required property string display
|
||||
- }
|
||||
- }
|
||||
-}
|
||||
diff --git a/tests/auto/qml/qqmldelegatemodel/data/resetInQAIMConstructor.qml b/tests/auto/qml/qqmldelegatemodel/data/resetInQAIMConstructor.qml
|
||||
deleted file mode 100644
|
||||
index cb1f226737..0000000000
|
||||
--- a/tests/auto/qml/qqmldelegatemodel/data/resetInQAIMConstructor.qml
|
||||
+++ /dev/null
|
||||
@@ -1,28 +0,0 @@
|
||||
-import QtQuick
|
||||
-import Test
|
||||
-
|
||||
-Window {
|
||||
- id: root
|
||||
- width: 200
|
||||
- height: 200
|
||||
-
|
||||
- property alias listView: listView
|
||||
-
|
||||
- ResetInConstructorModel {
|
||||
- id: resetInConstructorModel
|
||||
- }
|
||||
-
|
||||
- ListView {
|
||||
- id: listView
|
||||
- anchors.fill: parent
|
||||
- model: resetInConstructorModel
|
||||
-
|
||||
- delegate: Rectangle {
|
||||
- implicitWidth: 100
|
||||
- implicitHeight: 50
|
||||
- color: "olivedrab"
|
||||
-
|
||||
- required property string display
|
||||
- }
|
||||
- }
|
||||
-}
|
||||
diff --git a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
|
||||
index d9f8b7aeba..2cacda5513 100644
|
||||
--- a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
|
||||
+++ b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <QtTest/qtest.h>
|
||||
#include <QtCore/qjsonobject.h>
|
||||
#include <QtCore/QConcatenateTablesProxyModel>
|
||||
-#include <QtCore/qtimer.h>
|
||||
#include <QtGui/QStandardItemModel>
|
||||
#include <QtQml/qqmlcomponent.h>
|
||||
#include <QtQml/qqmlapplicationengine.h>
|
||||
@@ -12,17 +11,11 @@
|
||||
#include <QtQmlModels/private/qqmllistmodel_p.h>
|
||||
#include <QtQuick/qquickview.h>
|
||||
#include <QtQuick/qquickitem.h>
|
||||
-#include <QtQuick/private/qquickitemview_p_p.h>
|
||||
-#include <QtQuick/private/qquicklistview_p.h>
|
||||
-#include <QtQuickTest/quicktest.h>
|
||||
#include <QtQuickTestUtils/private/qmlutils_p.h>
|
||||
-#include <QtQuickTestUtils/private/visualtestutils_p.h>
|
||||
#include <QtTest/QSignalSpy>
|
||||
|
||||
#include <forward_list>
|
||||
|
||||
-using namespace QQuickVisualTestUtils;
|
||||
-
|
||||
class tst_QQmlDelegateModel : public QQmlDataTest
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -32,8 +25,6 @@ public:
|
||||
|
||||
private slots:
|
||||
void resettingRolesRespected();
|
||||
- void resetInQAIMConstructor();
|
||||
- void reset();
|
||||
void valueWithoutCallingObjectFirst_data();
|
||||
void valueWithoutCallingObjectFirst();
|
||||
void qtbug_86017();
|
||||
@@ -53,9 +44,16 @@ private slots:
|
||||
void viewUpdatedOnDelegateChoiceAffectingRoleChange();
|
||||
};
|
||||
|
||||
-class BaseAbstractItemModel : public QAbstractItemModel
|
||||
+class AbstractItemModel : public QAbstractItemModel
|
||||
{
|
||||
+ Q_OBJECT
|
||||
public:
|
||||
+ AbstractItemModel()
|
||||
+ {
|
||||
+ for (int i = 0; i < 3; ++i)
|
||||
+ mValues.append(QString::fromLatin1("Item %1").arg(i));
|
||||
+ }
|
||||
+
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override
|
||||
{
|
||||
if (parent.isValid())
|
||||
@@ -93,21 +91,10 @@ public:
|
||||
return mValues.at(index.row());
|
||||
}
|
||||
|
||||
-protected:
|
||||
+private:
|
||||
QVector<QString> mValues;
|
||||
};
|
||||
|
||||
-class AbstractItemModel : public BaseAbstractItemModel
|
||||
-{
|
||||
- Q_OBJECT
|
||||
-public:
|
||||
- AbstractItemModel()
|
||||
- {
|
||||
- for (int i = 0; i < 3; ++i)
|
||||
- mValues.append(QString::fromLatin1("Item %1").arg(i));
|
||||
- }
|
||||
-};
|
||||
-
|
||||
tst_QQmlDelegateModel::tst_QQmlDelegateModel()
|
||||
: QQmlDataTest(QT_QMLTEST_DATADIR)
|
||||
{
|
||||
@@ -166,109 +153,7 @@ void tst_QQmlDelegateModel::resettingRolesRespected()
|
||||
QObject *root = engine.rootObjects().constFirst();
|
||||
QVERIFY(!root->property("success").toBool());
|
||||
model->change();
|
||||
- QTRY_VERIFY_WITH_TIMEOUT(root->property("success").toBool(), 100);
|
||||
-}
|
||||
-
|
||||
-class ResetInConstructorModel : public BaseAbstractItemModel
|
||||
-{
|
||||
- Q_OBJECT
|
||||
- QML_ELEMENT
|
||||
-
|
||||
-public:
|
||||
- ResetInConstructorModel()
|
||||
- {
|
||||
- beginResetModel();
|
||||
- QTimer::singleShot(0, this, &ResetInConstructorModel::finishReset);
|
||||
- }
|
||||
-
|
||||
-private:
|
||||
- void finishReset()
|
||||
- {
|
||||
- mValues.append("First");
|
||||
- endResetModel();
|
||||
- }
|
||||
-};
|
||||
-
|
||||
-void tst_QQmlDelegateModel::resetInQAIMConstructor()
|
||||
-{
|
||||
- qmlRegisterTypesAndRevisions<ResetInConstructorModel>("Test", 1);
|
||||
-
|
||||
- QQuickApplicationHelper helper(this, "resetInQAIMConstructor.qml");
|
||||
- QVERIFY2(helper.ready, helper.failureMessage());
|
||||
- QQuickWindow *window = helper.window;
|
||||
- window->show();
|
||||
- QVERIFY(QTest::qWaitForWindowExposed(window));
|
||||
-
|
||||
- auto *listView = window->property("listView").value<QQuickListView *>();
|
||||
- QVERIFY(listView);
|
||||
- QTRY_VERIFY_WITH_TIMEOUT(listView->itemAtIndex(0), 100);
|
||||
- QQuickItem *firstDelegateItem = listView->itemAtIndex(0);
|
||||
- QVERIFY(firstDelegateItem);
|
||||
- QCOMPARE(firstDelegateItem->property("display").toString(), "First");
|
||||
-}
|
||||
-
|
||||
-class ResettableModel : public BaseAbstractItemModel
|
||||
-{
|
||||
- Q_OBJECT
|
||||
- QML_ELEMENT
|
||||
-
|
||||
-public:
|
||||
- ResettableModel()
|
||||
- {
|
||||
- mValues.append("First");
|
||||
- }
|
||||
-
|
||||
- void callBeginResetModel()
|
||||
- {
|
||||
- beginResetModel();
|
||||
- mValues.clear();
|
||||
- }
|
||||
-
|
||||
- void appendData()
|
||||
- {
|
||||
- mValues.append(QString::fromLatin1("Item %1").arg(mValues.size()));
|
||||
- }
|
||||
-
|
||||
- void callEndResetModel()
|
||||
- {
|
||||
- endResetModel();
|
||||
- }
|
||||
-};
|
||||
-
|
||||
-// Tests that everything works as expected when calling beginResetModel/endResetModel
|
||||
-// after the QAIM subclass constructor has run.
|
||||
-void tst_QQmlDelegateModel::reset()
|
||||
-{
|
||||
- qmlRegisterTypesAndRevisions<ResettableModel>("Test", 1);
|
||||
-
|
||||
- QQuickApplicationHelper helper(this, "reset.qml");
|
||||
- QVERIFY2(helper.ready, helper.failureMessage());
|
||||
- QQuickWindow *window = helper.window;
|
||||
- window->show();
|
||||
- QVERIFY(QTest::qWaitForWindowExposed(window));
|
||||
-
|
||||
- auto *listView = window->property("listView").value<QQuickListView *>();
|
||||
- QVERIFY(listView);
|
||||
- QQuickItem *firstDelegateItem = listView->itemAtIndex(0);
|
||||
- QVERIFY(firstDelegateItem);
|
||||
- QCOMPARE(firstDelegateItem->property("display").toString(), "First");
|
||||
-
|
||||
- const auto delegateModel = QQuickItemViewPrivate::get(listView)->model;
|
||||
- QSignalSpy rootIndexChangedSpy(delegateModel, SIGNAL(rootIndexChanged()));
|
||||
- QVERIFY(rootIndexChangedSpy.isValid());
|
||||
-
|
||||
- auto *model = listView->model().value<ResettableModel *>();
|
||||
- model->callBeginResetModel();
|
||||
- model->appendData();
|
||||
- model->callEndResetModel();
|
||||
- // This is verifies that handleModelReset isn't called
|
||||
- // more than once during this process, since it unconditionally emits rootIndexChanged.
|
||||
- QCOMPARE(rootIndexChangedSpy.count(), 1);
|
||||
-
|
||||
- QTRY_VERIFY_WITH_TIMEOUT(listView->itemAtIndex(0), 100);
|
||||
- firstDelegateItem = listView->itemAtIndex(0);
|
||||
- QVERIFY(firstDelegateItem);
|
||||
- QCOMPARE(firstDelegateItem->property("display").toString(), "Item 0");
|
||||
+ QTRY_VERIFY(root->property("success").toBool());
|
||||
}
|
||||
|
||||
void tst_QQmlDelegateModel::valueWithoutCallingObjectFirst_data()
|
||||
--
|
||||
2.46.1
|
||||
|
@ -1,473 +0,0 @@
|
||||
From 2aefbca84d2f3dca2c2697f13710b6907c0c7e59 Mon Sep 17 00:00:00 2001
|
||||
From: Mitch Curtis <mitch.curtis@qt.io>
|
||||
Date: Tue, 24 Sep 2024 10:22:12 +0800
|
||||
Subject: [PATCH 2/2] QQmlDelegateModel: fix delegates not being created in
|
||||
certain cases v2
|
||||
|
||||
Since 837c2f18cd223707e7cedb213257b0158ea07146, we connect to
|
||||
modelAboutToBeReset rather than modelReset so that we can handle role
|
||||
name changes. _q_modelAboutToBeReset now connects modelReset to
|
||||
handleModelReset with a single shot connection instead.
|
||||
|
||||
However, it's possible for user code to begin the reset before
|
||||
connectToAbstractItemModel is called (QTBUG-125053), in which case we
|
||||
connect to modelReset too late and handleModelReset is never called,
|
||||
resulting in delegates not being created in certain cases.
|
||||
|
||||
So, we check at the earliest point we can if the model is in the
|
||||
process of being reset, and if so, connect modelReset to
|
||||
handleModelReset.
|
||||
|
||||
This is a less intrusive alternative to
|
||||
6561344dd2d1ba69abe6edec4fe340b256da9e13, which caused regressions and
|
||||
was reverted.
|
||||
|
||||
Fixes: QTBUG-125053
|
||||
Task-number: QTBUG-127340
|
||||
Pick-to: 6.7 6.5
|
||||
Change-Id: I2bfe192ed61eddaa481de4b1e14b1fa5d07a51c1
|
||||
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
|
||||
(cherry picked from commit 4bb16ce2c8ea94f768991593a581c8838d48f3a3)
|
||||
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
|
||||
---
|
||||
src/qmlmodels/qqmldelegatemodel.cpp | 17 ++
|
||||
.../auto/qml/qqmldelegatemodel/CMakeLists.txt | 1 +
|
||||
...yModelWithDelayedSourceModelInListView.qml | 30 +++
|
||||
.../auto/qml/qqmldelegatemodel/data/reset.qml | 28 +++
|
||||
.../data/resetInQAIMConstructor.qml | 28 +++
|
||||
.../tst_qqmldelegatemodel.cpp | 210 +++++++++++++++++-
|
||||
6 files changed, 304 insertions(+), 10 deletions(-)
|
||||
create mode 100644 tests/auto/qml/qqmldelegatemodel/data/proxyModelWithDelayedSourceModelInListView.qml
|
||||
create mode 100644 tests/auto/qml/qqmldelegatemodel/data/reset.qml
|
||||
create mode 100644 tests/auto/qml/qqmldelegatemodel/data/resetInQAIMConstructor.qml
|
||||
|
||||
diff --git a/src/qmlmodels/qqmldelegatemodel.cpp b/src/qmlmodels/qqmldelegatemodel.cpp
|
||||
index 7cfa662aa6..f8de5cba82 100644
|
||||
--- a/src/qmlmodels/qqmldelegatemodel.cpp
|
||||
+++ b/src/qmlmodels/qqmldelegatemodel.cpp
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include "qqmldelegatemodel_p_p.h"
|
||||
|
||||
+#include <QtCore/private/qabstractitemmodel_p.h>
|
||||
+
|
||||
#include <QtQml/qqmlinfo.h>
|
||||
|
||||
#include <private/qqmlabstractdelegatecomponent_p.h>
|
||||
@@ -409,6 +411,21 @@ void QQmlDelegateModel::setModel(const QVariant &model)
|
||||
_q_itemsInserted(0, d->adaptorModelCount());
|
||||
d->requestMoreIfNecessary();
|
||||
}
|
||||
+
|
||||
+ // Since 837c2f18cd223707e7cedb213257b0158ea07146, we connect to modelAboutToBeReset
|
||||
+ // rather than modelReset so that we can handle role name changes. _q_modelAboutToBeReset
|
||||
+ // now connects modelReset to handleModelReset with a single shot connection instead.
|
||||
+ // However, it's possible for user code to begin the reset before connectToAbstractItemModel is called
|
||||
+ // (QTBUG-125053), in which case we connect to modelReset too late and handleModelReset is never called,
|
||||
+ // resulting in delegates not being created in certain cases.
|
||||
+ // So, we check at the earliest point we can if the model is in the process of being reset,
|
||||
+ // and if so, connect modelReset to handleModelReset.
|
||||
+ if (d->m_adaptorModel.adaptsAim()) {
|
||||
+ auto *aim = d->m_adaptorModel.aim();
|
||||
+ auto *aimPrivate = QAbstractItemModelPrivate::get(aim);
|
||||
+ if (aimPrivate->resetting)
|
||||
+ QObject::connect(aim, &QAbstractItemModel::modelReset, this, &QQmlDelegateModel::handleModelReset, Qt::SingleShotConnection);
|
||||
+ }
|
||||
}
|
||||
|
||||
/*!
|
||||
diff --git a/tests/auto/qml/qqmldelegatemodel/CMakeLists.txt b/tests/auto/qml/qqmldelegatemodel/CMakeLists.txt
|
||||
index 8d8a90e0a7..966f5229df 100644
|
||||
--- a/tests/auto/qml/qqmldelegatemodel/CMakeLists.txt
|
||||
+++ b/tests/auto/qml/qqmldelegatemodel/CMakeLists.txt
|
||||
@@ -29,6 +29,7 @@ qt_internal_add_test(tst_qqmldelegatemodel
|
||||
Qt::QmlModelsPrivate
|
||||
Qt::QmlPrivate
|
||||
Qt::Quick
|
||||
+ Qt::QuickPrivate
|
||||
Qt::QuickTestUtilsPrivate
|
||||
TESTDATA ${test_data}
|
||||
)
|
||||
diff --git a/tests/auto/qml/qqmldelegatemodel/data/proxyModelWithDelayedSourceModelInListView.qml b/tests/auto/qml/qqmldelegatemodel/data/proxyModelWithDelayedSourceModelInListView.qml
|
||||
new file mode 100644
|
||||
index 0000000000..b6733bd38c
|
||||
--- /dev/null
|
||||
+++ b/tests/auto/qml/qqmldelegatemodel/data/proxyModelWithDelayedSourceModelInListView.qml
|
||||
@@ -0,0 +1,30 @@
|
||||
+import QtQuick
|
||||
+import Test
|
||||
+
|
||||
+Window {
|
||||
+ id: root
|
||||
+ title: listView.count
|
||||
+
|
||||
+ property alias listView: listView
|
||||
+ property ProxySourceModel connectionModel: null
|
||||
+
|
||||
+ Component {
|
||||
+ id: modelComponent
|
||||
+ ProxySourceModel {}
|
||||
+ }
|
||||
+
|
||||
+ ListView {
|
||||
+ id: listView
|
||||
+ anchors.fill: parent
|
||||
+
|
||||
+ delegate: Text {
|
||||
+ text: model.Name
|
||||
+ }
|
||||
+
|
||||
+ model: ProxyModel {
|
||||
+ sourceModel: root.connectionModel
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ Component.onCompleted: root.connectionModel = modelComponent.createObject(root)
|
||||
+}
|
||||
diff --git a/tests/auto/qml/qqmldelegatemodel/data/reset.qml b/tests/auto/qml/qqmldelegatemodel/data/reset.qml
|
||||
new file mode 100644
|
||||
index 0000000000..0fcd5e8afa
|
||||
--- /dev/null
|
||||
+++ b/tests/auto/qml/qqmldelegatemodel/data/reset.qml
|
||||
@@ -0,0 +1,28 @@
|
||||
+import QtQuick
|
||||
+import Test
|
||||
+
|
||||
+Window {
|
||||
+ id: root
|
||||
+ width: 200
|
||||
+ height: 200
|
||||
+
|
||||
+ property alias listView: listView
|
||||
+
|
||||
+ ResettableModel {
|
||||
+ id: resetModel
|
||||
+ }
|
||||
+
|
||||
+ ListView {
|
||||
+ id: listView
|
||||
+ anchors.fill: parent
|
||||
+ model: resetModel
|
||||
+
|
||||
+ delegate: Rectangle {
|
||||
+ implicitWidth: 100
|
||||
+ implicitHeight: 50
|
||||
+ color: "olivedrab"
|
||||
+
|
||||
+ required property string display
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/tests/auto/qml/qqmldelegatemodel/data/resetInQAIMConstructor.qml b/tests/auto/qml/qqmldelegatemodel/data/resetInQAIMConstructor.qml
|
||||
new file mode 100644
|
||||
index 0000000000..cb1f226737
|
||||
--- /dev/null
|
||||
+++ b/tests/auto/qml/qqmldelegatemodel/data/resetInQAIMConstructor.qml
|
||||
@@ -0,0 +1,28 @@
|
||||
+import QtQuick
|
||||
+import Test
|
||||
+
|
||||
+Window {
|
||||
+ id: root
|
||||
+ width: 200
|
||||
+ height: 200
|
||||
+
|
||||
+ property alias listView: listView
|
||||
+
|
||||
+ ResetInConstructorModel {
|
||||
+ id: resetInConstructorModel
|
||||
+ }
|
||||
+
|
||||
+ ListView {
|
||||
+ id: listView
|
||||
+ anchors.fill: parent
|
||||
+ model: resetInConstructorModel
|
||||
+
|
||||
+ delegate: Rectangle {
|
||||
+ implicitWidth: 100
|
||||
+ implicitHeight: 50
|
||||
+ color: "olivedrab"
|
||||
+
|
||||
+ required property string display
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
|
||||
index 2cacda5513..3f08d8fc85 100644
|
||||
--- a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
|
||||
+++ b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp
|
||||
@@ -3,7 +3,9 @@
|
||||
|
||||
#include <QtTest/qtest.h>
|
||||
#include <QtCore/qjsonobject.h>
|
||||
+#include <QtCore/qsortfilterproxymodel.h>
|
||||
#include <QtCore/QConcatenateTablesProxyModel>
|
||||
+#include <QtCore/qtimer.h>
|
||||
#include <QtGui/QStandardItemModel>
|
||||
#include <QtQml/qqmlcomponent.h>
|
||||
#include <QtQml/qqmlapplicationengine.h>
|
||||
@@ -11,11 +13,17 @@
|
||||
#include <QtQmlModels/private/qqmllistmodel_p.h>
|
||||
#include <QtQuick/qquickview.h>
|
||||
#include <QtQuick/qquickitem.h>
|
||||
+#include <QtQuick/private/qquickitemview_p_p.h>
|
||||
+#include <QtQuick/private/qquicklistview_p.h>
|
||||
+#include <QtQuickTest/quicktest.h>
|
||||
#include <QtQuickTestUtils/private/qmlutils_p.h>
|
||||
+#include <QtQuickTestUtils/private/visualtestutils_p.h>
|
||||
#include <QtTest/QSignalSpy>
|
||||
|
||||
#include <forward_list>
|
||||
|
||||
+using namespace QQuickVisualTestUtils;
|
||||
+
|
||||
class tst_QQmlDelegateModel : public QQmlDataTest
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -25,6 +33,8 @@ public:
|
||||
|
||||
private slots:
|
||||
void resettingRolesRespected();
|
||||
+ void resetInQAIMConstructor();
|
||||
+ void reset();
|
||||
void valueWithoutCallingObjectFirst_data();
|
||||
void valueWithoutCallingObjectFirst();
|
||||
void qtbug_86017();
|
||||
@@ -42,18 +52,12 @@ private slots:
|
||||
void doNotUnrefObjectUnderConstruction();
|
||||
void clearCacheDuringInsertion();
|
||||
void viewUpdatedOnDelegateChoiceAffectingRoleChange();
|
||||
+ void proxyModelWithDelayedSourceModelInListView();
|
||||
};
|
||||
|
||||
-class AbstractItemModel : public QAbstractItemModel
|
||||
+class BaseAbstractItemModel : public QAbstractItemModel
|
||||
{
|
||||
- Q_OBJECT
|
||||
public:
|
||||
- AbstractItemModel()
|
||||
- {
|
||||
- for (int i = 0; i < 3; ++i)
|
||||
- mValues.append(QString::fromLatin1("Item %1").arg(i));
|
||||
- }
|
||||
-
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override
|
||||
{
|
||||
if (parent.isValid())
|
||||
@@ -91,10 +95,21 @@ public:
|
||||
return mValues.at(index.row());
|
||||
}
|
||||
|
||||
-private:
|
||||
+protected:
|
||||
QVector<QString> mValues;
|
||||
};
|
||||
|
||||
+class AbstractItemModel : public BaseAbstractItemModel
|
||||
+{
|
||||
+ Q_OBJECT
|
||||
+public:
|
||||
+ AbstractItemModel()
|
||||
+ {
|
||||
+ for (int i = 0; i < 3; ++i)
|
||||
+ mValues.append(QString::fromLatin1("Item %1").arg(i));
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
tst_QQmlDelegateModel::tst_QQmlDelegateModel()
|
||||
: QQmlDataTest(QT_QMLTEST_DATADIR)
|
||||
{
|
||||
@@ -153,7 +168,109 @@ void tst_QQmlDelegateModel::resettingRolesRespected()
|
||||
QObject *root = engine.rootObjects().constFirst();
|
||||
QVERIFY(!root->property("success").toBool());
|
||||
model->change();
|
||||
- QTRY_VERIFY(root->property("success").toBool());
|
||||
+ QTRY_VERIFY_WITH_TIMEOUT(root->property("success").toBool(), 100);
|
||||
+}
|
||||
+
|
||||
+class ResetInConstructorModel : public BaseAbstractItemModel
|
||||
+{
|
||||
+ Q_OBJECT
|
||||
+ QML_ELEMENT
|
||||
+
|
||||
+public:
|
||||
+ ResetInConstructorModel()
|
||||
+ {
|
||||
+ beginResetModel();
|
||||
+ QTimer::singleShot(0, this, &ResetInConstructorModel::finishReset);
|
||||
+ }
|
||||
+
|
||||
+private:
|
||||
+ void finishReset()
|
||||
+ {
|
||||
+ mValues.append("First");
|
||||
+ endResetModel();
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
+void tst_QQmlDelegateModel::resetInQAIMConstructor()
|
||||
+{
|
||||
+ qmlRegisterTypesAndRevisions<ResetInConstructorModel>("Test", 1);
|
||||
+
|
||||
+ QQuickApplicationHelper helper(this, "resetInQAIMConstructor.qml");
|
||||
+ QVERIFY2(helper.ready, helper.failureMessage());
|
||||
+ QQuickWindow *window = helper.window;
|
||||
+ window->show();
|
||||
+ QVERIFY(QTest::qWaitForWindowExposed(window));
|
||||
+
|
||||
+ auto *listView = window->property("listView").value<QQuickListView *>();
|
||||
+ QVERIFY(listView);
|
||||
+ QTRY_VERIFY_WITH_TIMEOUT(listView->itemAtIndex(0), 100);
|
||||
+ QQuickItem *firstDelegateItem = listView->itemAtIndex(0);
|
||||
+ QVERIFY(firstDelegateItem);
|
||||
+ QCOMPARE(firstDelegateItem->property("display").toString(), "First");
|
||||
+}
|
||||
+
|
||||
+class ResettableModel : public BaseAbstractItemModel
|
||||
+{
|
||||
+ Q_OBJECT
|
||||
+ QML_ELEMENT
|
||||
+
|
||||
+public:
|
||||
+ ResettableModel()
|
||||
+ {
|
||||
+ mValues.append("First");
|
||||
+ }
|
||||
+
|
||||
+ void callBeginResetModel()
|
||||
+ {
|
||||
+ beginResetModel();
|
||||
+ mValues.clear();
|
||||
+ }
|
||||
+
|
||||
+ void appendData()
|
||||
+ {
|
||||
+ mValues.append(QString::fromLatin1("Item %1").arg(mValues.size()));
|
||||
+ }
|
||||
+
|
||||
+ void callEndResetModel()
|
||||
+ {
|
||||
+ endResetModel();
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
+// Tests that everything works as expected when calling beginResetModel/endResetModel
|
||||
+// after the QAIM subclass constructor has run.
|
||||
+void tst_QQmlDelegateModel::reset()
|
||||
+{
|
||||
+ qmlRegisterTypesAndRevisions<ResettableModel>("Test", 1);
|
||||
+
|
||||
+ QQuickApplicationHelper helper(this, "reset.qml");
|
||||
+ QVERIFY2(helper.ready, helper.failureMessage());
|
||||
+ QQuickWindow *window = helper.window;
|
||||
+ window->show();
|
||||
+ QVERIFY(QTest::qWaitForWindowExposed(window));
|
||||
+
|
||||
+ auto *listView = window->property("listView").value<QQuickListView *>();
|
||||
+ QVERIFY(listView);
|
||||
+ QQuickItem *firstDelegateItem = listView->itemAtIndex(0);
|
||||
+ QVERIFY(firstDelegateItem);
|
||||
+ QCOMPARE(firstDelegateItem->property("display").toString(), "First");
|
||||
+
|
||||
+ const auto delegateModel = QQuickItemViewPrivate::get(listView)->model;
|
||||
+ QSignalSpy rootIndexChangedSpy(delegateModel, SIGNAL(rootIndexChanged()));
|
||||
+ QVERIFY(rootIndexChangedSpy.isValid());
|
||||
+
|
||||
+ auto *model = listView->model().value<ResettableModel *>();
|
||||
+ model->callBeginResetModel();
|
||||
+ model->appendData();
|
||||
+ model->callEndResetModel();
|
||||
+ // This is verifies that handleModelReset isn't called
|
||||
+ // more than once during this process, since it unconditionally emits rootIndexChanged.
|
||||
+ QCOMPARE(rootIndexChangedSpy.count(), 1);
|
||||
+
|
||||
+ QTRY_VERIFY_WITH_TIMEOUT(listView->itemAtIndex(0), 100);
|
||||
+ firstDelegateItem = listView->itemAtIndex(0);
|
||||
+ QVERIFY(firstDelegateItem);
|
||||
+ QCOMPARE(firstDelegateItem->property("display").toString(), "Item 0");
|
||||
}
|
||||
|
||||
void tst_QQmlDelegateModel::valueWithoutCallingObjectFirst_data()
|
||||
@@ -616,6 +733,79 @@ void tst_QQmlDelegateModel::viewUpdatedOnDelegateChoiceAffectingRoleChange()
|
||||
QVERIFY(returnedValue);
|
||||
}
|
||||
|
||||
+class ProxySourceModel : public QAbstractListModel
|
||||
+{
|
||||
+ Q_OBJECT
|
||||
+ QML_ELEMENT
|
||||
+public:
|
||||
+ explicit ProxySourceModel(QObject *parent = nullptr)
|
||||
+ : QAbstractListModel(parent)
|
||||
+ {
|
||||
+ for (int i = 0; i < rows; ++i) {
|
||||
+ beginInsertRows(QModelIndex(), i, i);
|
||||
+ endInsertRows();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ ~ProxySourceModel() override = default;
|
||||
+
|
||||
+ int rowCount(const QModelIndex &) const override
|
||||
+ {
|
||||
+ return rows;
|
||||
+ }
|
||||
+
|
||||
+ QVariant data(const QModelIndex &, int ) const override
|
||||
+ {
|
||||
+ return "Hello";
|
||||
+ }
|
||||
+
|
||||
+ QHash<int, QByteArray> roleNames() const override
|
||||
+ {
|
||||
+ QHash<int, QByteArray> roles = QAbstractListModel::roleNames();
|
||||
+ roles[Qt::UserRole + 1] = "Name";
|
||||
+
|
||||
+ return roles;
|
||||
+ }
|
||||
+
|
||||
+ static const int rows = 1;
|
||||
+};
|
||||
+
|
||||
+class ProxyModel : public QSortFilterProxyModel
|
||||
+{
|
||||
+ Q_OBJECT
|
||||
+ QML_ELEMENT
|
||||
+ Q_PROPERTY(QAbstractItemModel *sourceModel READ sourceModel WRITE setSourceModel)
|
||||
+
|
||||
+public:
|
||||
+ explicit ProxyModel(QObject *parent = nullptr)
|
||||
+ : QSortFilterProxyModel(parent)
|
||||
+ {
|
||||
+ }
|
||||
+
|
||||
+ ~ProxyModel() override = default;
|
||||
+};
|
||||
+
|
||||
+// Checks that the correct amount of delegates are created when using a proxy
|
||||
+// model whose source model is set after a delay.
|
||||
+void tst_QQmlDelegateModel::proxyModelWithDelayedSourceModelInListView()
|
||||
+{
|
||||
+ QTest::failOnWarning();
|
||||
+
|
||||
+ qmlRegisterTypesAndRevisions<ProxySourceModel>("Test", 1);
|
||||
+ qmlRegisterTypesAndRevisions<ProxyModel>("Test", 1);
|
||||
+
|
||||
+ QQuickApplicationHelper helper(this, "proxyModelWithDelayedSourceModelInListView.qml");
|
||||
+ QVERIFY2(helper.ready, helper.failureMessage());
|
||||
+ QQuickWindow *window = helper.window;
|
||||
+ window->show();
|
||||
+ QVERIFY(QTest::qWaitForWindowExposed(window));
|
||||
+
|
||||
+ auto *listView = window->property("listView").value<QQuickListView *>();
|
||||
+ QVERIFY(listView);
|
||||
+ const auto delegateModel = QQuickItemViewPrivate::get(listView)->model;
|
||||
+ QTRY_COMPARE(listView->count(), 1);
|
||||
+}
|
||||
+
|
||||
QTEST_MAIN(tst_QQmlDelegateModel)
|
||||
|
||||
#include "tst_qqmldelegatemodel.moc"
|
||||
--
|
||||
2.46.1
|
||||
|
14
_constraints
14
_constraints
@ -2,20 +2,10 @@
|
||||
<constraints>
|
||||
<hardware>
|
||||
<disk>
|
||||
<size unit="G">25</size>
|
||||
<size unit="G">20</size>
|
||||
</disk>
|
||||
<processors>4</processors>
|
||||
</hardware>
|
||||
<overwrite>
|
||||
<conditions>
|
||||
<arch>s390x</arch>
|
||||
</conditions>
|
||||
<hardware>
|
||||
<memory>
|
||||
<size unit="G">9</size>
|
||||
</memory>
|
||||
</hardware>
|
||||
</overwrite>
|
||||
<overwrite>
|
||||
<conditions>
|
||||
<arch>ppc64</arch>
|
||||
@ -45,7 +35,7 @@
|
||||
</conditions>
|
||||
<hardware>
|
||||
<disk>
|
||||
<size unit="G">3</size>
|
||||
<size unit="G">2</size>
|
||||
</disk>
|
||||
<memory>
|
||||
<size unit="G">2</size>
|
||||
|
@ -1,52 +1,3 @@
|
||||
-------------------------------------------------------------------
|
||||
Mon Nov 4 17:50:07 UTC 2024 - Christophe Marin <christophe@krop.fr>
|
||||
|
||||
- Add upstream change (QTBUG-129500, kde#495089):
|
||||
* 0001-QQuickItem-map-To-From-Item-Account-for-not-having-a.patch
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Fri Oct 25 11:42:28 UTC 2024 - Christophe Marin <christophe@krop.fr>
|
||||
|
||||
- Add upstream changes:
|
||||
* 0001-QQuickItemView-fix-crash-with-zero-size-SwipeView-th.patch
|
||||
(QTBUG-129622, kde#493854)
|
||||
* 0001-QQuickAccessibleAttached-Let-implicit-names-work-whe.patch
|
||||
(QTBUG-130360)
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Tue Oct 15 09:37:16 UTC 2024 - Antonio Larrosa <alarrosa@suse.com>
|
||||
|
||||
- Update memory constraints for s390x and use %limit_build
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Tue Oct 15 07:52:36 UTC 2024 - Christophe Marin <christophe@krop.fr>
|
||||
|
||||
- Update disk constraints
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Wed Oct 9 16:52:34 UTC 2024 - Christophe Marin <christophe@krop.fr>
|
||||
|
||||
- Add upstream change to fix compilation failures (kde#494281, QTBUG-129797)
|
||||
* 0001-Compiler-Wrap-raw-string-literals-in-QStringLiteral-.patch
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Tue Oct 8 10:17:37 UTC 2024 - Christophe Marin <christophe@krop.fr>
|
||||
|
||||
- Update to 6.8.0:
|
||||
* https://www.qt.io/blog/qt-6.8-released
|
||||
- Add upstream changes:
|
||||
* 0001-Revert-QQmlDelegateModel-fix-delegates-not-being-cre.patch
|
||||
* 0002-QQmlDelegateModel-fix-delegates-not-being-created-in.patch
|
||||
(kde#493116, QTBUG-127340)
|
||||
- Merge some -devel and -private-devel packages that only have
|
||||
private API.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Sat Sep 28 08:22:56 UTC 2024 - Christophe Marin <christophe@krop.fr>
|
||||
|
||||
- Update to 6.7.3
|
||||
* https://www.qt.io/blog/qt-6.7.3-released
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Wed Jun 19 07:25:41 UTC 2024 - Christophe Marin <christophe@krop.fr>
|
||||
|
||||
|
1327
qt6-declarative.spec
1327
qt6-declarative.spec
File diff suppressed because it is too large
Load Diff
BIN
qtdeclarative-everywhere-src-6.7.2.tar.xz
(Stored with Git LFS)
Normal file
BIN
qtdeclarative-everywhere-src-6.7.2.tar.xz
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3b41a36b42e919a3aa0da1f71107591504200f41707bee2ad8e8d4f99b5644c2
|
||||
size 37029068
|
Loading…
Reference in New Issue
Block a user