diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2019-12-05 01:00:07 +0100 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2019-12-05 10:09:17 +0100 |
commit | 88490da44e8afa0f4d03ca79bcc928a14412ef99 (patch) | |
tree | 41b40fe0f36c5ed49d0b8a2ce54421eb4dfbfd3b | |
parent | 6ad3445f1e159d9beea936b66d267dcaacdc5d6c (diff) | |
parent | e2af7c3b37095e601a84cc52de69a99af8e5d3a2 (diff) |
Merge remote-tracking branch 'origin/5.14' into 5.15
Conflicts:
tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
Change-Id: Ib46bc1c717cf524eea2fb3d876810c8d55747c91
12 files changed, 463 insertions, 7 deletions
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index e88f60db6d..7f228e1c05 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -2441,7 +2441,11 @@ bool QQuickItemViewPrivate::releaseItem(FxViewItem *item, QQmlInstanceModel::Reu flags = model->release(item->item, reusableFlag); if (!flags) { // item was not destroyed, and we no longer reference it. - QQuickItemPrivate::get(item->item)->setCulled(true); + if (item->item->parentItem() == contentItem) { + // Only cull the item if its parent item is still our contentItem. + // One case where this can happen is moving an item out of one ObjectModel and into another. + QQuickItemPrivate::get(item->item)->setCulled(true); + } unrequestedItems.insert(item->item, model->indexOf(item->item, q)); } else if (flags & QQmlInstanceModel::Destroyed) { item->item->setParentItem(nullptr); diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index 6976665134..7ebe174a9e 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -1922,9 +1922,10 @@ bool QQuickPathView::childMouseEventFilter(QQuickItem *i, QEvent *e) void QQuickPathView::mouseUngrabEvent() { Q_D(QQuickPathView); - if (d->stealMouse) { + if (d->stealMouse || + (!d->flicking && d->snapMode != NoSnap && !qFuzzyCompare(qRound(d->offset), d->offset))) { // if our mouse grab has been removed (probably by a Flickable), - // fix our state + // or if we should snap but haven't done it, fix our state d->stealMouse = false; setKeepMouseGrab(false); d->timer.invalidate(); diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index ec2fe75d9b..f1002badd1 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -1249,9 +1249,9 @@ qreal QQuickTableViewPrivate::getColumnLayoutWidth(int column) if (qIsNaN(columnWidth) || columnWidth <= 0) { if (!layoutWarningIssued) { layoutWarningIssued = true; - qmlWarning(q_func()) << "the delegate's implicitHeight needs to be greater than zero"; + qmlWarning(q_func()) << "the delegate's implicitWidth needs to be greater than zero"; } - columnWidth = kDefaultRowHeight; + columnWidth = kDefaultColumnWidth; } return columnWidth; @@ -2242,6 +2242,7 @@ void QQuickTableViewPrivate::syncModel() if (instanceModel) { if (tableModel) { + releaseLoadedItems(QQmlTableInstanceModel::NotReusable); delete tableModel; tableModel = nullptr; } diff --git a/tests/auto/qml/ecmascripttests/qjstest/main.cpp b/tests/auto/qml/ecmascripttests/qjstest/main.cpp index 4a3541d892..38736b083d 100644 --- a/tests/auto/qml/ecmascripttests/qjstest/main.cpp +++ b/tests/auto/qml/ecmascripttests/qjstest/main.cpp @@ -92,6 +92,7 @@ int main(int argc, char **argv) int flags = 0; if (parser.isSet(verbose)) + flags |= Test262Runner::Verbose; if (parser.isSet(parallel)) flags |= Test262Runner::Parallel; diff --git a/tests/auto/quick/qquicklistview/data/moveObjectModelItemToAnotherObjectModel.qml b/tests/auto/quick/qquicklistview/data/moveObjectModelItemToAnotherObjectModel.qml new file mode 100644 index 0000000000..ebdebeb449 --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/moveObjectModelItemToAnotherObjectModel.qml @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.14 +import QtQml.Models 2.14 + +Item { + id: root + width: 400 + height: 400 + + readonly property int rectCount: 3 + property var rectColors: ["red", "green", "blue"] + + property alias listView1: listView1 + property alias listView2: listView2 + + function moveRedRectToModel2() { + var appItem = objectModel1.get(0) + objectModel1.remove(0, 1) + objectModel2.insert(0, appItem) + } + + function moveRedRectToModel1() { + var appItem = objectModel2.get(0) + objectModel2.remove(0, 1) + objectModel1.insert(0, appItem) + } + + ObjectModel { + id: objectModel1 + objectName: "objectModel1" + + Component.onCompleted: { + for (var i = 0; i < root.rectCount; i++) { + var outerRect = rectComponent.createObject(null, { + "objectName": root.rectColors[i] + "Rect", + "color": root.rectColors[i] + }) + objectModel1.append(outerRect) + } + } + } + + ObjectModel { + id: objectModel2 + objectName: "objectModel2" + } + + ListView { + id: listView1 + objectName: "listView1" + anchors.left: parent.left + anchors.top: parent.top + height: 100 + width: 100 + anchors.margins: 20 + clip: true + cacheBuffer: 0 + model: objectModel1 + orientation: ListView.Horizontal + spacing: 20 + + Component.onCompleted: contentItem.objectName = "listView1ContentItem" + } + + ListView { + id: listView2 + objectName: "listView2" + anchors.right: parent.right + anchors.top: parent.top + height: 100 + width: 100 + anchors.margins: 20 + clip: true + cacheBuffer: 0 + model: objectModel2 + orientation: ListView.Horizontal + spacing: 20 + + Component.onCompleted: contentItem.objectName = "listView2ContentItem" + } + + Component { + id: rectComponent + + Rectangle { + height: 100 + width: 100 + opacity: 0.2 + } + } +} diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index 4e21edbcb4..4c147b753b 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -288,6 +288,7 @@ private slots: void reuse_reuseIsOffByDefault(); void reuse_checkThatItemsAreReused(); + void moveObjectModelItemToAnotherObjectModel(); private: template <class T> void items(const QUrl &source); @@ -9376,6 +9377,40 @@ void tst_QQuickListView::dragOverFloatingHeaderOrFooter() // QTBUG-74046 releaseView(window); } +void tst_QQuickListView::moveObjectModelItemToAnotherObjectModel() +{ + QScopedPointer<QQuickView> window(createView()); + window->setSource(testFileUrl("moveObjectModelItemToAnotherObjectModel.qml")); + QCOMPARE(window->status(), QQuickView::Ready); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QObject *root = window->rootObject(); + QVERIFY(root); + + const QQuickListView *listView1 = root->property("listView1").value<QQuickListView*>(); + QVERIFY(listView1); + + const QQuickListView *listView2 = root->property("listView2").value<QQuickListView*>(); + QVERIFY(listView2); + + const QQuickItem *redRect = listView1->itemAtIndex(0); + QVERIFY(redRect); + QCOMPARE(redRect->objectName(), QString::fromLatin1("redRect")); + + QVERIFY(QMetaObject::invokeMethod(root, "moveRedRectToModel2")); + QVERIFY(QQuickTest::qIsPolishScheduled(listView2)); + QVERIFY(QQuickTest::qWaitForItemPolished(listView2)); + QVERIFY(redRect->isVisible()); + QVERIFY(!QQuickItemPrivate::get(redRect)->culled); + + QVERIFY(QMetaObject::invokeMethod(root, "moveRedRectToModel1")); + QVERIFY(QQuickTest::qIsPolishScheduled(listView1)); + QVERIFY(QQuickTest::qWaitForItemPolished(listView1)); + QVERIFY(redRect->isVisible()); + QVERIFY(!QQuickItemPrivate::get(redRect)->culled); +} + QTEST_MAIN(tst_QQuickListView) #include "tst_qquicklistview.moc" diff --git a/tests/auto/quick/qquickpathview/data/ungrabNestedinFlickable.qml b/tests/auto/quick/qquickpathview/data/ungrabNestedinFlickable.qml new file mode 100644 index 0000000000..4258d8fef4 --- /dev/null +++ b/tests/auto/quick/qquickpathview/data/ungrabNestedinFlickable.qml @@ -0,0 +1,75 @@ +import QtQuick 2.9 + +Flickable { + width: 480 + height: 480 + contentX: 0 + contentWidth: width + contentHeight: height + leftMargin: 408 + rightMargin: 36 + maximumFlickVelocity: 0 + boundsBehavior: Flickable.StopAtBounds + flickableDirection: Flickable.HorizontalFlick + + PathView { + id:pathView + objectName: "pathView" + + property int countclick: 0 + + readonly property int contentsWidth: 348 + readonly property int contentsHeight: 480 + + width: contentsWidth + height: contentsHeight + + interactive: true + + cacheItemCount: 10 + currentIndex: 2 + pathItemCount: 4 + highlightMoveDuration: 300 + highlightRangeMode : PathView.StrictlyEnforceRange + preferredHighlightBegin: 0.5 + preferredHighlightEnd: 0.5 + snapMode : PathView.SnapOneItem + + path: Path { + startX: pathView.contentsWidth / 2 - 800 + startY: pathView.contentsHeight / 2 - 800 + + PathArc { + x: pathView.contentsWidth / 2 - 800 + y: pathView.contentsHeight / 2 + 800 + radiusX: 800 + radiusY: 800 + direction: PathArc.Clockwise + } + } + + model: ListModel { + ListElement { objectName:"aqua"; name: "aqua" ;mycolor:"aqua"} + ListElement { objectName:"blue"; name: "blue" ;mycolor:"blue"} + ListElement { objectName:"blueviolet"; name: "blueviolet" ;mycolor:"blueviolet"} + ListElement { objectName:"brown"; name: "brown" ;mycolor:"brown"} + ListElement { objectName:"chartreuse"; name: "chartreuse" ;mycolor:"chartreuse"} + } + + delegate: Item { + id: revolveritem + objectName: model.objectName + + width: pathView.contentsWidth + height: pathView.contentsHeight + + Rectangle + { + id:myRectangle + color: mycolor + width: pathView.contentsWidth -20 + height: pathView.contentsHeight -20 + } + } + } +} diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp index a8e847a5c7..8ba09fa509 100644 --- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp +++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp @@ -140,6 +140,7 @@ private slots: void cacheItemCount(); void changePathDuringRefill(); void nestedinFlickable(); + void ungrabNestedinFlickable(); void flickableDelegate(); void jsArrayChange(); void qtbug37815(); @@ -2413,6 +2414,40 @@ void tst_QQuickPathView::nestedinFlickable() } +void tst_QQuickPathView::ungrabNestedinFlickable() +{ + QScopedPointer<QQuickView> window(createView()); + QQuickViewTestUtil::moveMouseAway(window.data()); + window->setSource(testFileUrl("ungrabNestedinFlickable.qml")); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QCOMPARE(window.data(), qGuiApp->focusWindow()); + + QQuickPathView *pathview = findItem<QQuickPathView>(window->rootObject(), "pathView"); + QVERIFY(pathview != nullptr); + + double pathviewOffsetBefore = pathview->offset(); + + // Drag slowly upwards so that it does not flick, release, and let it start snapping back + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(200, 350)); + for (int i = 0; i < 4; ++i) + QTest::mouseMove(window.data(), QPoint(200, 325 - i * 25), 500); + QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(200, 250)); + QCOMPARE(pathview->isMoving(), true); + + // Press again to stop moving + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(200, 350)); + QTRY_COMPARE(pathview->isMoving(), false); + + // Cancel the grab, wait for movement to stop, and expect it to snap to + // the nearest delegate, which should be at the same offset as where we started + pathview->ungrabMouse(); + QTRY_COMPARE(pathview->offset(), pathviewOffsetBefore); + QCOMPARE(pathview->isMoving(), false); + QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(200, 350)); +} + void tst_QQuickPathView::flickableDelegate() { QScopedPointer<QQuickView> window(createView()); diff --git a/tests/auto/quick/qquicktableview/data/checkalwaysemit.qml b/tests/auto/quick/qquicktableview/data/checkalwaysemit.qml new file mode 100644 index 0000000000..33db6f6d02 --- /dev/null +++ b/tests/auto/quick/qquicktableview/data/checkalwaysemit.qml @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Window 2.3 + +Item { + width: 640 + height: 450 + + property alias tableView: tableView + property real delegateWidth: 100 + property real delegateHeight: 50 + property Component delegate: tableViewDelegate + property bool delegateParentSetBeforeCompleted: false + + TableView { + id: tableView + width: 600 + height: 400 + anchors.margins: 1 + clip: true + delegate: tableViewDelegate + columnSpacing: 1 + rowSpacing: 1 + } + + Component { + id: tableViewDelegate + Rectangle { + objectName: "tableViewDelegate" + implicitWidth: delegateWidth + implicitHeight: delegateHeight + color: "lightgray" + border.width: 1 + + property string modelDataFromIndex: tableView.model.dataFromSerializedIndex(index) + property string modelDataBinding: modelData + + Text { + anchors.centerIn: parent + text: modelData + } + + Component.onCompleted: { + delegateParentSetBeforeCompleted = parent != null; + } + } + } + +} diff --git a/tests/auto/quick/qquicktableview/data/plaintableview.qml b/tests/auto/quick/qquicktableview/data/plaintableview.qml index 33db6f6d02..90271eda71 100644 --- a/tests/auto/quick/qquicktableview/data/plaintableview.qml +++ b/tests/auto/quick/qquicktableview/data/plaintableview.qml @@ -70,7 +70,6 @@ Item { color: "lightgray" border.width: 1 - property string modelDataFromIndex: tableView.model.dataFromSerializedIndex(index) property string modelDataBinding: modelData Text { diff --git a/tests/auto/quick/qquicktableview/data/replaceModelTableView.qml b/tests/auto/quick/qquicktableview/data/replaceModelTableView.qml new file mode 100644 index 0000000000..2b17e055a7 --- /dev/null +++ b/tests/auto/quick/qquicktableview/data/replaceModelTableView.qml @@ -0,0 +1,63 @@ +import QtQuick 2.14 +import QtQml.Models 2.14 + +Item { + id: root + visible: true + width: 640 + height: 480 + + property alias tableView: tv + + ObjectModel { + id: om + Rectangle { height: 30; width: 80; color: "red" } + Rectangle { height: 30; width: 80; color: "green" } + Rectangle { height: 30; width: 80; color: "blue" } + } + + ListModel { + id: lm + ListElement { name: "1" } + ListElement { name: "44"} + } + + DelegateModel { + id: dm + model: ListModel { + ListElement { name: "Apple" } + ListElement { name: "Orange" } + } + delegate: Rectangle { + height: 25 + width: 100 + Text { text: "Name: " + name} + } + } + TableView { + id: tv + visible: true + anchors.fill: parent + property int modelId: 0 + + model: { + switch (modelId) { + case 0: return lm; + case 1: return om; + case 2: return dm; + default: return null; + } + } + + delegate: Rectangle { + id: dlg + implicitWidth: 40 + implicitHeight: 20 + color: "red" + Text { + text: qsTr("name: " + name) + } + border.color: "green" + } + } +} diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp index 6ae095a7d6..10004bb775 100644 --- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp +++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp @@ -177,6 +177,7 @@ private slots: void checkSyncView_connect_late(); void delegateWithRequiredProperties(); void checkThatFetchMoreIsCalledWhenScrolledToTheEndOfTable(); + void replaceModel(); }; tst_QQuickTableView::tst_QQuickTableView() @@ -2094,7 +2095,7 @@ void tst_QQuickTableView::checkThatWeAlwaysEmitChangedUponItemReused() // any data referred to by the index property inside the delegate // will change too. So we need to refresh any bindings to index. // QTBUG-79209 - LOAD_TABLEVIEW("plaintableview.qml"); + LOAD_TABLEVIEW("checkalwaysemit.qml"); TestModel model(1, 1); tableView->setModel(QVariant::fromValue(&model)); @@ -2745,6 +2746,20 @@ void tst_QQuickTableView::delegateWithRequiredProperties() } } +void tst_QQuickTableView::replaceModel() +{ + LOAD_TABLEVIEW("replaceModelTableView.qml"); + + tableView->setProperty("modelId", 0); + QTRY_COMPARE(tableView->rows(), 2); + tableView->setProperty("modelId", 1); + QTRY_COMPARE(tableView->rows(), 0); + tableView->setProperty("modelId", 2); + QTRY_COMPARE(tableView->rows(), 0); + tableView->setProperty("modelId", 0); + QTRY_COMPARE(tableView->rows(), 2); +} + QTEST_MAIN(tst_QQuickTableView) #include "tst_qquicktableview.moc" |