diff options
author | Alexandru Croitor <alexandru.croitor@qt.io> | 2019-10-14 18:46:38 +0200 |
---|---|---|
committer | Alexandru Croitor <alexandru.croitor@qt.io> | 2019-10-14 19:02:37 +0200 |
commit | c2f8b9535d34da6948ccf45b7d5fd90de2f1bc9e (patch) | |
tree | c6f7e058a985d7c18b51cadc76283caf555071c9 /tests/auto/quick | |
parent | 9e633bbda7608ac0231809e2a6a97ae8f2d849d6 (diff) | |
parent | 803f18f02e5609a1ca00a5b78ea6d3613d44e1a0 (diff) |
Merge remote-tracking branch 'origin/dev' into wip/cmake
Removed dependencies.yaml because we don't use it yet in wip/cmake.
Fixed conflict in qmlcachegen.cpp.
Change-Id: Ie1060c737bee1daa85779903598e5b6d5020d922
Diffstat (limited to 'tests/auto/quick')
50 files changed, 1974 insertions, 135 deletions
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp index 66314f88a2..65c5ac9ef4 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp @@ -53,9 +53,12 @@ private slots: void initTestCase(); void defaultPropertyValues(); + void touchDrag_data(); void touchDrag(); void mouseDrag_data(); void mouseDrag(); + void mouseDragThreshold_data(); + void mouseDragThreshold(); void dragFromMargin(); void snapMode_data(); void snapMode(); @@ -131,9 +134,18 @@ void tst_DragHandler::defaultPropertyValues() QCOMPARE(dragHandler->centroid().sceneGrabPosition(), QPointF()); } +void tst_DragHandler::touchDrag_data() +{ + QTest::addColumn<int>("dragThreshold"); + QTest::newRow("threshold zero") << 0; + QTest::newRow("threshold one") << 1; + QTest::newRow("threshold 20") << 20; + QTest::newRow("threshold default") << -1; +} + void tst_DragHandler::touchDrag() { - const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QFETCH(int, dragThreshold); QScopedPointer<QQuickView> windowPtr; createView(windowPtr, "draggables.qml"); QQuickView * window = windowPtr.data(); @@ -142,6 +154,12 @@ void tst_DragHandler::touchDrag() QVERIFY(ball); QQuickDragHandler *dragHandler = ball->findChild<QQuickDragHandler*>(); QVERIFY(dragHandler); + if (dragThreshold < 0) { + dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QCOMPARE(dragHandler->dragThreshold(), dragThreshold); + } else { + dragHandler->setDragThreshold(dragThreshold); + } QSignalSpy translationChangedSpy(dragHandler, SIGNAL(translationChanged())); QSignalSpy centroidChangedSpy(dragHandler, SIGNAL(centroidChanged())); @@ -161,7 +179,9 @@ void tst_DragHandler::touchDrag() p1 += QPoint(dragThreshold, 0); QTest::touchEvent(window, touchDevice).move(1, p1, window); QQuickTouchUtils::flush(window); - QTRY_VERIFY(dragHandler->centroid().velocity().x() > 0); + qCDebug(lcPointerTests) << "velocity after drag" << dragHandler->centroid().velocity(); + if (dragThreshold > 0) + QTRY_VERIFY(!qFuzzyIsNull(dragHandler->centroid().velocity().x())); QCOMPARE(centroidChangedSpy.count(), 2); QVERIFY(!dragHandler->active()); p1 += QPoint(1, 0); @@ -282,6 +302,81 @@ void tst_DragHandler::mouseDrag() QCOMPARE(centroidChangedSpy.count(), shouldDrag ? 5 : 0); } +void tst_DragHandler::mouseDragThreshold_data() +{ + QTest::addColumn<int>("dragThreshold"); + QTest::newRow("threshold zero") << 0; + QTest::newRow("threshold one") << 1; + QTest::newRow("threshold 20") << 20; + QTest::newRow("threshold default") << -1; +} + +void tst_DragHandler::mouseDragThreshold() +{ + QFETCH(int, dragThreshold); + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "draggables.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *ball = window->rootObject()->childItems().first(); + QVERIFY(ball); + QQuickDragHandler *dragHandler = ball->findChild<QQuickDragHandler*>(); + QVERIFY(dragHandler); + if (dragThreshold < 0) { + dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QCOMPARE(dragHandler->dragThreshold(), dragThreshold); + } else { + dragHandler->setDragThreshold(dragThreshold); + } + + QSignalSpy translationChangedSpy(dragHandler, SIGNAL(translationChanged())); + QSignalSpy centroidChangedSpy(dragHandler, SIGNAL(centroidChanged())); + + QPointF ballCenter = ball->clipRect().center(); + QPointF scenePressPos = ball->mapToScene(ballCenter); + QPoint p1 = scenePressPos.toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QVERIFY(!dragHandler->active()); + QCOMPARE(dragHandler->centroid().position(), ballCenter); + QCOMPARE(dragHandler->centroid().pressPosition(), ballCenter); + QCOMPARE(dragHandler->centroid().scenePosition(), scenePressPos); + QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos); + QCOMPARE(dragHandler->centroid().velocity(), QVector2D()); + QCOMPARE(centroidChangedSpy.count(), 1); + p1 += QPoint(dragThreshold, 0); + QTest::mouseMove(window, p1); + if (dragThreshold > 0) + QTRY_VERIFY(dragHandler->centroid().velocity().x() > 0); + QCOMPARE(centroidChangedSpy.count(), 2); + QVERIFY(!dragHandler->active()); + p1 += QPoint(1, 0); + QTest::mouseMove(window, p1); + QTRY_VERIFY(dragHandler->active()); + QCOMPARE(translationChangedSpy.count(), 0); + QCOMPARE(centroidChangedSpy.count(), 3); + QCOMPARE(dragHandler->translation().x(), 0.0); + QPointF sceneGrabPos = p1; + QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos); + p1 += QPoint(19, 0); + QTest::mouseMove(window, p1); + QTRY_VERIFY(dragHandler->active()); + QCOMPARE(dragHandler->centroid().position(), ballCenter); + QCOMPARE(dragHandler->centroid().pressPosition(), ballCenter); + QCOMPARE(dragHandler->centroid().scenePosition(), ball->mapToScene(ballCenter)); + QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos); + QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos); + QCOMPARE(dragHandler->translation().x(), dragThreshold + 20.0); + QCOMPARE(dragHandler->translation().y(), 0.0); + QVERIFY(dragHandler->centroid().velocity().x() > 0); + QCOMPARE(centroidChangedSpy.count(), 4); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(!dragHandler->active()); + QCOMPARE(dragHandler->centroid().pressedButtons(), Qt::NoButton); + QCOMPARE(ball->mapToScene(ballCenter).toPoint(), p1); + QCOMPARE(translationChangedSpy.count(), 1); + QCOMPARE(centroidChangedSpy.count(), 5); +} + void tst_DragHandler::dragFromMargin() // QTBUG-74966 { const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); diff --git a/tests/auto/quick/qquickanimatedimage/data/currentframe.qml b/tests/auto/quick/qquickanimatedimage/data/currentframe.qml new file mode 100644 index 0000000000..b679da2a99 --- /dev/null +++ b/tests/auto/quick/qquickanimatedimage/data/currentframe.qml @@ -0,0 +1,13 @@ +import QtQuick 2.0 + +AnimatedImage { + property int currentFrameChangeCount: 0 + property int frameChangeCount: 0 + source: "stickman.gif" + onCurrentFrameChanged: if (currentFrame > 0) ++currentFrameChangeCount; + onFrameChanged: if (currentFrame > 0) ++frameChangeCount; + function scriptedSetCurrentFrame(frame) { + currentFrame = frame; + } +} + diff --git a/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp b/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp index 8026bafb9e..31c3fb9946 100644 --- a/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp +++ b/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp @@ -27,6 +27,7 @@ ****************************************************************************/ #include <qtest.h> #include <QtQml/qqmlengine.h> +#include <QtQml/qqmlexpression.h> #include <QtQml/qqmlcomponent.h> #include <QtQuick/qquickview.h> #include <QtQuick/private/qquickrectangle_p.h> @@ -40,6 +41,23 @@ Q_DECLARE_METATYPE(QQuickImageBase::Status) +template <typename T> static T evaluate(QObject *scope, const QString &expression) +{ + QQmlExpression expr(qmlContext(scope), scope, expression); + QVariant result = expr.evaluate(); + if (expr.hasError()) + qWarning() << expr.error().toString(); + return result.value<T>(); +} + +template <> void evaluate<void>(QObject *scope, const QString &expression) +{ + QQmlExpression expr(qmlContext(scope), scope, expression); + expr.evaluate(); + if (expr.hasError()) + qWarning() << expr.error().toString(); +} + class tst_qquickanimatedimage : public QQmlDataTest { Q_OBJECT @@ -68,6 +86,7 @@ private slots: void playingAndPausedChanges(); void noCaching(); void sourceChangesOnFrameChanged(); + void currentFrame(); }; void tst_qquickanimatedimage::cleanup() @@ -618,6 +637,33 @@ void tst_qquickanimatedimage::sourceChangesOnFrameChanged() qDeleteAll(images); } +void tst_qquickanimatedimage::currentFrame() +{ + QQuickView window; + window.setSource(testFileUrl("currentframe.qml")); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + QQuickAnimatedImage *anim = qobject_cast<QQuickAnimatedImage *>(window.rootObject()); + QVERIFY(anim); + QSignalSpy frameChangedSpy(anim, SIGNAL(frameChanged())); + QSignalSpy currentFrameChangedSpy(anim, SIGNAL(currentFrameChanged())); + + anim->setCurrentFrame(1); + QCOMPARE(anim->currentFrame(), 1); + QCOMPARE(frameChangedSpy.count(), 1); + QCOMPARE(currentFrameChangedSpy.count(), 1); + QCOMPARE(anim->property("currentFrameChangeCount"), 1); + QCOMPARE(anim->property("frameChangeCount"), 1); + + evaluate<void>(anim, "scriptedSetCurrentFrame(2)"); + QCOMPARE(anim->currentFrame(), 2); + QCOMPARE(frameChangedSpy.count(), 2); + QCOMPARE(currentFrameChangedSpy.count(), 2); + QCOMPARE(anim->property("currentFrameChangeCount"), 2); + QCOMPARE(anim->property("frameChangeCount"), 2); +} + QTEST_MAIN(tst_qquickanimatedimage) #include "tst_qquickanimatedimage.moc" diff --git a/tests/auto/quick/qquickboundaryrule/data/dragHandler.qml b/tests/auto/quick/qquickboundaryrule/data/dragHandler.qml index c66fd76ff1..769a5b2c7d 100644 --- a/tests/auto/quick/qquickboundaryrule/data/dragHandler.qml +++ b/tests/auto/quick/qquickboundaryrule/data/dragHandler.qml @@ -14,6 +14,7 @@ Rectangle { } BoundaryRule on x { + objectName: "boundaryRule" id: xbr minimum: -50 maximum: 100 diff --git a/tests/auto/quick/qquickboundaryrule/qquickboundaryrule.pro b/tests/auto/quick/qquickboundaryrule/qquickboundaryrule.pro index ef43f4526a..c41f798d33 100644 --- a/tests/auto/quick/qquickboundaryrule/qquickboundaryrule.pro +++ b/tests/auto/quick/qquickboundaryrule/qquickboundaryrule.pro @@ -9,4 +9,4 @@ include (../shared/util.pri) TESTDATA = data/* -QT += core-private gui-private qml-private quick-private testlib +QT += quick-private qml testlib diff --git a/tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp b/tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp index 44f1c9a2f9..75639dba49 100644 --- a/tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp +++ b/tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp @@ -30,7 +30,6 @@ #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcomponent.h> #include <QtQuick/qquickview.h> -#include <QtQuick/private/qquickboundaryrule_p.h> #include <QtQuick/private/qquickdraghandler_p.h> #include "../../shared/util.h" #include "../shared/viewtestutil.h" @@ -57,7 +56,7 @@ void tst_qquickboundaryrule::dragHandler() QVERIFY(target); QQuickDragHandler *dragHandler = target->findChild<QQuickDragHandler*>(); QVERIFY(dragHandler); - QQuickBoundaryRule *boundaryRule = target->findChild<QQuickBoundaryRule*>(); + QObject *boundaryRule = target->findChild<QObject *>(QLatin1String("boundaryRule")); QVERIFY(boundaryRule); QSignalSpy overshootChangedSpy(boundaryRule, SIGNAL(currentOvershootChanged())); @@ -68,29 +67,34 @@ void tst_qquickboundaryrule::dragHandler() QTest::mouseMove(&window, p1); QTRY_VERIFY(dragHandler->active()); QCOMPARE(target->position().x(), 100); - QCOMPARE(boundaryRule->currentOvershoot(), 0); - QCOMPARE(boundaryRule->peakOvershoot(), 0); + bool ok = false; + QCOMPARE(boundaryRule->property("currentOvershoot").toReal(&ok), 0); + QVERIFY(ok); + QCOMPARE(boundaryRule->property("peakOvershoot").toReal(&ok), 0); + QVERIFY(ok); QCOMPARE(overshootChangedSpy.count(), 0); // restricted drag: halfway into overshoot p1 += QPoint(20, 0); QTest::mouseMove(&window, p1); QCOMPARE(target->position().x(), 117.5); - QCOMPARE(boundaryRule->currentOvershoot(), 20); - QCOMPARE(boundaryRule->peakOvershoot(), 20); + QCOMPARE(boundaryRule->property("currentOvershoot").toReal(), 20); + QCOMPARE(boundaryRule->property("peakOvershoot").toReal(), 20); QCOMPARE(overshootChangedSpy.count(), 1); // restricted drag: maximum overshoot p1 += QPoint(80, 0); QTest::mouseMove(&window, p1); QCOMPARE(target->position().x(), 140); - QCOMPARE(boundaryRule->currentOvershoot(), 100); - QCOMPARE(boundaryRule->peakOvershoot(), 100); + QCOMPARE(boundaryRule->property("currentOvershoot").toReal(), 100); + QCOMPARE(boundaryRule->property("peakOvershoot").toReal(), 100); QCOMPARE(overshootChangedSpy.count(), 2); // release and let it return to bounds QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, p1); QTRY_COMPARE(dragHandler->active(), false); QTRY_COMPARE(overshootChangedSpy.count(), 3); - QCOMPARE(boundaryRule->currentOvershoot(), 0); - QCOMPARE(boundaryRule->peakOvershoot(), 0); + QCOMPARE(boundaryRule->property("currentOvershoot").toReal(&ok), 0); + QVERIFY(ok); + QCOMPARE(boundaryRule->property("peakOvershoot").toReal(&ok), 0); + QVERIFY(ok); QCOMPARE(target->position().x(), 100); } diff --git a/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp b/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp index 9d832066af..ee43e5e06a 100644 --- a/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp +++ b/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp @@ -598,7 +598,7 @@ void tst_QQuickDrag::move() QCoreApplication::processEvents(); QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&rightTarget)); QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&rightTarget)); - QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.moveEvents, 0); + QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 1); QCOMPARE(outerTarget.moveEvents, 0); QCOMPARE(leftTarget .enterEvents, 0); QCOMPARE(leftTarget .leaveEvents, 0); QCOMPARE(leftTarget .moveEvents, 0); QCOMPARE(rightTarget.enterEvents, 1); QCOMPARE(rightTarget.leaveEvents, 0); QCOMPARE(rightTarget.moveEvents, 0); QCOMPARE(rightTarget.position.x(), qreal(5)); QCOMPARE(rightTarget.position.y(), qreal(15)); @@ -620,10 +620,10 @@ void tst_QQuickDrag::move() QCoreApplication::processEvents(); QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&leftTarget)); QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&leftTarget)); - QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.moveEvents, 1); + QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.moveEvents, 0); QCOMPARE(leftTarget .enterEvents, 0); QCOMPARE(leftTarget .leaveEvents, 0); QCOMPARE(leftTarget .moveEvents, 1); QCOMPARE(rightTarget.enterEvents, 0); QCOMPARE(rightTarget.leaveEvents, 0); QCOMPARE(rightTarget.moveEvents, 0); - QCOMPARE(outerTarget.position.x(), qreal(25)); QCOMPARE(outerTarget.position.y(), qreal(40)); + QCOMPARE(outerTarget.position.x(), qreal(60)); QCOMPARE(outerTarget.position.y(), qreal(50)); QCOMPARE(leftTarget.position.x(), qreal(25)); QCOMPARE(leftTarget.position.y(), qreal(5)); // Move out of all targets. @@ -632,7 +632,7 @@ void tst_QQuickDrag::move() QCoreApplication::processEvents(); QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(nullptr)); QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(nullptr)); - QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 1); QCOMPARE(outerTarget.moveEvents, 0); + QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.moveEvents, 0); QCOMPARE(leftTarget .enterEvents, 0); QCOMPARE(leftTarget .leaveEvents, 1); QCOMPARE(leftTarget .moveEvents, 0); QCOMPARE(rightTarget.enterEvents, 0); QCOMPARE(rightTarget.leaveEvents, 0); QCOMPARE(rightTarget.moveEvents, 0); diff --git a/tests/auto/quick/qquickdroparea/data/nested1.qml b/tests/auto/quick/qquickdroparea/data/nested1.qml new file mode 100644 index 0000000000..de6ac70d08 --- /dev/null +++ b/tests/auto/quick/qquickdroparea/data/nested1.qml @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** 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.0 + +Item { + width: 200; height: 200 + property int outerEnterEvents: 0 + property int outerExitEvents: 0 + property int innerEnterEvents: 0 + property int innerExitEvents: 0 + + DropArea { + objectName: "outerDropArea" + x: 75; y: 75 + width: 100; height: 100 + Rectangle { + anchors.fill: parent + color: "green" + } + onEntered: ++outerEnterEvents + onExited: ++outerExitEvents + + DropArea { + objectName: "innerDropArea" + width: 50; height: 50 + Rectangle { + anchors.fill: parent + color: "blue" + } + onEntered: ++innerEnterEvents + onExited: ++innerExitEvents + } + } + + Rectangle { + width: 20; height: 20 + color: dragArea.pressed ? "red" : "brown" + Drag.active: dragArea.drag.active + MouseArea { + id: dragArea + objectName: "dragArea" + anchors.fill: parent + drag.target: parent + } + } +} diff --git a/tests/auto/quick/qquickdroparea/data/nested2.qml b/tests/auto/quick/qquickdroparea/data/nested2.qml new file mode 100644 index 0000000000..93630c3779 --- /dev/null +++ b/tests/auto/quick/qquickdroparea/data/nested2.qml @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** 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.0 + +Item { + width: 200; height: 200 + property int outerEnterEvents: 0 + property int outerExitEvents: 0 + property int innerEnterEvents: 0 + property int innerExitEvents: 0 + + Rectangle { + x: 75; y: 75 + width: 100; height: 100 + color: "green" + DropArea { + objectName: "outerDropArea" + anchors.fill: parent + onEntered: ++outerEnterEvents + onExited: ++outerExitEvents + } + + Rectangle { + width: 50; height: 50 + color: "blue" + DropArea { + objectName: "innerDropArea" + anchors.fill: parent + onEntered: ++innerEnterEvents + onExited: ++innerExitEvents + } + } + } + + Rectangle { + width: 20; height: 20 + color: dragArea.pressed ? "red" : "brown" + Drag.active: dragArea.drag.active + MouseArea { + id: dragArea + objectName: "dragArea" + anchors.fill: parent + drag.target: parent + } + } +} diff --git a/tests/auto/quick/qquickdroparea/qquickdroparea.pro b/tests/auto/quick/qquickdroparea/qquickdroparea.pro index a34d5ad009..7a8fdef7b9 100644 --- a/tests/auto/quick/qquickdroparea/qquickdroparea.pro +++ b/tests/auto/quick/qquickdroparea/qquickdroparea.pro @@ -4,4 +4,11 @@ macx:CONFIG -= app_bundle SOURCES += tst_qquickdroparea.cpp +OTHER_FILES += $$files(data/*.qml) + +include (../../shared/util.pri) +include (../shared/util.pri) + +TESTDATA = data/* + QT += core-private gui-private qml-private quick-private network testlib diff --git a/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp b/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp index cf01cc927b..dcba4c872e 100644 --- a/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp +++ b/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp @@ -28,6 +28,7 @@ #include <QtTest/QtTest> #include <QtTest/QSignalSpy> +#include <QtGui/qstylehints.h> #include <QtQuick/qquickitem.h> #include <QtQuick/qquickview.h> #include <QtQml/qqmlcontext.h> @@ -36,6 +37,8 @@ #include <qpa/qplatformdrag.h> #include <qpa/qwindowsysteminterface.h> +#include "../../shared/util.h" +#include "../shared/viewtestutil.h" template <typename T> static T evaluate(QObject *scope, const QString &expression) { @@ -54,13 +57,10 @@ template <> void evaluate<void>(QObject *scope, const QString &expression) qWarning() << expr.error().toString(); } -class tst_QQuickDropArea: public QObject +class tst_QQuickDropArea: public QQmlDataTest { Q_OBJECT private slots: - void initTestCase(); - void cleanupTestCase(); - void containsDrag_internal(); void containsDrag_external(); void keys_internal(); @@ -74,21 +74,13 @@ private slots: void competingDrags(); void simultaneousDrags(); void dropStuff(); + void nestedDropAreas_data(); + void nestedDropAreas(); private: QQmlEngine engine; }; -void tst_QQuickDropArea::initTestCase() -{ - -} - -void tst_QQuickDropArea::cleanupTestCase() -{ - -} - void tst_QQuickDropArea::containsDrag_internal() { QQuickWindow window; @@ -1224,6 +1216,74 @@ void tst_QQuickDropArea::dropStuff() QCOMPARE(evaluate<QByteArray>(dropArea, "array"), QByteArray("red")); } +void tst_QQuickDropArea::nestedDropAreas_data() +{ + QTest::addColumn<QString>("qmlFile"); + + QTest::newRow("dropRectDropRect") << "nested1.qml"; + QTest::newRow("rectDropRectDrop") << "nested2.qml"; +} + +void tst_QQuickDropArea::nestedDropAreas() +{ + QFETCH(QString, qmlFile); + + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QQuickView window; + QByteArray errorMessage; + QVERIFY2(QQuickTest::initView(window, testFileUrl(qmlFile.toLatin1().data()), true, &errorMessage), errorMessage.constData()); + + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + QVERIFY(window.rootObject() != nullptr); + + QQuickItem *dragArea = window.rootObject()->findChild<QQuickItem*>("dragArea"); + QVERIFY(dragArea); + QQuickItem *outerDropArea = window.rootObject()->findChild<QQuickItem*>("outerDropArea"); + QVERIFY(outerDropArea); + QQuickItem *innerDropArea = window.rootObject()->findChild<QQuickItem*>("innerDropArea"); + QVERIFY(innerDropArea); + + QPoint p = QPoint(10,10); + QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, p); + + // move the minimum distance to activate drag + p += QPoint(dragThreshold + 1, dragThreshold + 1); + QTest::mouseMove(&window, p); + + // drag the red rectangle into the inner DropArea + p += QPoint(100, 100); + QTest::mouseMove(&window, p); + QCOMPARE(window.rootObject()->property("outerEnterEvents"), 0); + QCOMPARE(window.rootObject()->property("outerExitEvents"), 0); + QCOMPARE(window.rootObject()->property("innerEnterEvents"), 1); + QCOMPARE(window.rootObject()->property("innerExitEvents"), 0); + + // drag the red rectangle into the outer DropArea + p += QPoint(0, 50); + QTest::mouseMove(&window, p); + QCOMPARE(window.rootObject()->property("outerEnterEvents"), 1); + QCOMPARE(window.rootObject()->property("outerExitEvents"), 0); + QCOMPARE(window.rootObject()->property("innerEnterEvents"), 1); + QCOMPARE(window.rootObject()->property("innerExitEvents"), 1); + + // drag the red rectangle into the inner DropArea + p -= QPoint(0, 50); + QTest::mouseMove(&window, p); + QCOMPARE(window.rootObject()->property("outerEnterEvents"), 1); + QCOMPARE(window.rootObject()->property("outerExitEvents"), 1); + QCOMPARE(window.rootObject()->property("innerEnterEvents"), 2); + QCOMPARE(window.rootObject()->property("innerExitEvents"), 1); + + // drag the red rectangle back out of both + p -= QPoint(100, 100); + QTest::mouseMove(&window, p); + QCOMPARE(window.rootObject()->property("outerEnterEvents"), 1); + QCOMPARE(window.rootObject()->property("outerExitEvents"), 1); + QCOMPARE(window.rootObject()->property("innerEnterEvents"), 2); + QCOMPARE(window.rootObject()->property("innerExitEvents"), 2); +} + QTEST_MAIN(tst_QQuickDropArea) #include "tst_qquickdroparea.moc" diff --git a/tests/auto/quick/qquickflickable/data/resize.qml b/tests/auto/quick/qquickflickable/data/resize.qml index 2f7ae7b8bb..131691d012 100644 --- a/tests/auto/quick/qquickflickable/data/resize.qml +++ b/tests/auto/quick/qquickflickable/data/resize.qml @@ -1,6 +1,7 @@ import QtQuick 2.0 Rectangle { + required property bool setRebound function resizeContent() { flick.resizeContent(600, 600, Qt.point(100, 100)) } diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index c104eecbcd..5364530ca8 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -226,7 +226,7 @@ void tst_qquickflickable::create() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("flickable01.qml")); - QQuickFlickable *obj = qobject_cast<QQuickFlickable*>(c.create()); + QQuickFlickable *obj = qobject_cast<QQuickFlickable*>(c.createWithInitialProperties({{"setRebound", false}})); QVERIFY(obj != nullptr); QCOMPARE(obj->isAtXBeginning(), true); @@ -782,9 +782,8 @@ void tst_qquickflickable::flickableDirection() void tst_qquickflickable::resizeContent() { QQmlEngine engine; - engine.rootContext()->setContextProperty("setRebound", QVariant::fromValue(false)); QQmlComponent c(&engine, testFileUrl("resize.qml")); - QQuickItem *root = qobject_cast<QQuickItem*>(c.create()); + QQuickItem *root = qobject_cast<QQuickItem*>(c.createWithInitialProperties({{"setRebound", false}})); QQuickFlickable *obj = findItem<QQuickFlickable>(root, "flick"); QVERIFY(obj != nullptr); @@ -816,7 +815,7 @@ void tst_qquickflickable::returnToBounds() QScopedPointer<QQuickView> window(new QQuickView); - window->rootContext()->setContextProperty("setRebound", setRebound); + window->setInitialProperties({{"setRebound", setRebound}}); window->setSource(testFileUrl("resize.qml")); window->show(); QVERIFY(QTest::qWaitForWindowActive(window.data())); diff --git a/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml b/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml index 6a1c0632ad..49838c4fd5 100644 --- a/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml +++ b/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml @@ -60,8 +60,8 @@ Item { id: testCase name: "Tests_GridLayout" when: windowShown - width: 200 - height: 200 + width: parent.width + height: parent.height Component { id: layout_flow_Component @@ -84,7 +84,7 @@ Item { function test_flow() { - var layout = layout_flow_Component.createObject(container); + var layout = createTemporaryObject(layout_flow_Component, container); tryCompare(layout.children[0], "itemRect", [ 0, 0, 10, 10]) tryCompare(layout.children[1], "itemRect", [10, 0, 10, 10]) tryCompare(layout.children[2], "itemRect", [20, 0, 10, 10]) @@ -102,8 +102,6 @@ Item { tryCompare(layout.children[4], "itemRect", [10, 0, 10, 10]) tryCompare(layout.children[5], "itemRect", [10, 10, 10, 10]) - - layout.destroy() } Component { @@ -178,7 +176,7 @@ Item { } function test_flowLeftToRight() { - var layout = layout_flowLeftToRight_Component.createObject(container); + var layout = createTemporaryObject(layout_flowLeftToRight_Component, container); compare(layout.implicitWidth, 80); compare(layout.children[0].x, 0); compare(layout.children[0].y, 0); @@ -208,8 +206,6 @@ Item { compare(layout.children[11].y, 60); compare(layout.children[12].x, 40); compare(layout.children[12].y, 80); - - layout.destroy(); } @@ -259,7 +255,7 @@ Item { function test_flowLeftToRightDefaultPositions() { ignoreWarning("QGridLayoutEngine::addItem: Cell (1, 0) already taken"); - var layout = layout_flowLeftToRightDefaultPositions_Component.createObject(container); + var layout = createTemporaryObject(layout_flowLeftToRightDefaultPositions_Component, container); compare(layout.implicitWidth, 40); compare(layout.children[0].x, 0); compare(layout.children[0].y, 0); @@ -267,7 +263,6 @@ Item { compare(layout.children[1].y, 20); compare(layout.children[2].x, 20); compare(layout.children[2].y, 20); - layout.destroy(); } @@ -342,7 +337,7 @@ Item { } function test_flowTopToBottom() { - var layout = layout_flowTopToBottom_Component.createObject(container); + var layout = createTemporaryObject(layout_flowTopToBottom_Component, container); compare(layout.children[0].x, 0); compare(layout.children[0].y, 0); compare(layout.children[1].x, 20); @@ -371,8 +366,6 @@ Item { compare(layout.children[11].y, 60); compare(layout.children[12].x, 80); compare(layout.children[12].y, 0); - - layout.destroy(); } Component { @@ -432,7 +425,7 @@ Item { } function test_spanAcrossEmptyRows() { - var layout = layout_spanAcrossEmptyRows_Component.createObject(container); + var layout = createTemporaryObject(layout_spanAcrossEmptyRows_Component, container); compare(layout.children[0].x, 0); compare(layout.children[0].y, 0); compare(layout.children[1].x, 20); @@ -442,8 +435,6 @@ Item { compare(layout.implicitWidth, 60); compare(layout.Layout.maximumWidth, 120); - - layout.destroy(); } Component { @@ -463,14 +454,13 @@ Item { } function test_spanIsMoreThanColumns() { - var layout = layout_spanIsMoreThanColumns_Component.createObject(container); + var layout = createTemporaryObject(layout_spanIsMoreThanColumns_Component, container); // item was not added, therefore implicit width is 0 compare(layout.implicitWidth, 0); - layout.destroy(); } function test_sizeHints() { - var layout = layout_spanAcrossEmptyRows_Component.createObject(container); + var layout = createTemporaryObject(layout_spanAcrossEmptyRows_Component, container); compare(layout.visible, true) var minWidth = layout.Layout.minimumWidth @@ -489,8 +479,6 @@ Item { compare(prefHeight, layout.implicitHeight) compare(maxWidth, layout.Layout.maximumWidth) compare(maxHeight, layout.Layout.maximumHeight) - - layout.destroy(); } Component { @@ -567,7 +555,7 @@ Item { function test_alignment() { - var layout = layout_alignment_Component.createObject(container); + var layout = createTemporaryObject(layout_alignment_Component, container); layout.width = 60; layout.height = 100; @@ -596,8 +584,6 @@ Item { layout.children[4].Layout.alignment = Qt.AlignLeft tryCompare(layout.children[4], "x", 0); tryCompare(layout.children[4], "y", 60); - - layout.destroy(); } @@ -648,7 +634,7 @@ Item { function test_rightToLeft() { - var layout = layout_rightToLeft_Component.createObject(container); + var layout = createTemporaryObject(layout_rightToLeft_Component, container); layout.width = 180; layout.height = 50; @@ -674,8 +660,6 @@ Item { layout.LayoutMirroring.enabled = true verifyIsRightToLeft(layout) - - layout.destroy(); } Component { @@ -698,7 +682,7 @@ Item { function test_columnsChanged() { - var layout = layout_columnsOrRowsChanged_Component.createObject(container); + var layout = createTemporaryObject(layout_columnsOrRowsChanged_Component, container); layout.width = 40; layout.height = 20; tryCompare(layout.children[0], "itemRect", [ 0, 5, 10, 10]) @@ -711,13 +695,11 @@ Item { tryCompare(layout.children[1], "itemRect", [20, 0, 10, 10]) tryCompare(layout.children[2], "itemRect", [ 0, 10, 10, 10]) tryCompare(layout.children[3], "itemRect", [20, 10, 10, 10]) - - layout.destroy() } function test_rowsChanged() { - var layout = layout_columnsOrRowsChanged_Component.createObject(container); + var layout = createTemporaryObject(layout_columnsOrRowsChanged_Component, container); layout.flow = GridLayout.TopToBottom layout.width = 20; layout.height = 40; @@ -731,8 +713,6 @@ Item { tryCompare(layout.children[1], "itemRect", [ 0, 25, 10, 10]) tryCompare(layout.children[2], "itemRect", [10, 5, 10, 10]) tryCompare(layout.children[3], "itemRect", [10, 25, 10, 10]) - - layout.destroy() } Component { @@ -767,7 +747,7 @@ Item { function test_columnOrRowChanged() { - var layout = layout_columnOrRowChanged_Component.createObject(container); + var layout = createTemporaryObject(layout_columnOrRowChanged_Component, container); layout.width = layout.implicitWidth layout.height = layout.implicitHeight // c0-c1-c2 @@ -795,8 +775,6 @@ Item { tryCompare(layout.children[0], "itemRect", [10, 10, 10, 10]) tryCompare(layout.children[1], "itemRect", [ 0, 0, 10, 10]) tryCompare(layout.children[2], "itemRect", [20, 0, 10, 10]) - - layout.destroy() } Component { @@ -819,7 +797,7 @@ Item { } function test_baselines() { - var layout = layout_baselines_Component.createObject(container); + var layout = createTemporaryObject(layout_baselines_Component, container); tryCompare(layout.children[0], "itemRect", [ 0, 0, 10, 10]) tryCompare(layout.children[1], "itemRect", [10, 0, 10, 10]) compare(layout.implicitWidth, 20) @@ -832,8 +810,6 @@ Item { tryCompare(layout.children[1], "itemRect", [10, 10, 10, 10]) compare(layout.implicitWidth, 20) compare(layout.implicitHeight, 20) - - layout.destroy(); } Component { @@ -851,30 +827,42 @@ Item { } } - function test_spacings() + function test_spacings_data() + { + let data = [ + { spacing: Number.NaN }, + { spacing: 0 }, + { spacing: 10 }, + { spacing: -5 }, + { spacing: -19 } + ] + for (let i = 0; i < data.length; ++i) { + data[i].tag = data[i].spacing.toString() + } + return data + } + + function test_spacings(data) { - var layout = layout_spacings_Component.createObject(container); + var layout = createTemporaryObject(layout_spacings_Component, container); // breaks down below -19. This is acceptable, since it means that the implicit size of the layout is negative var testSpacings = [Number.NaN, 0, 10, -5, -19] layout.rowSpacing = 0 - for (var i = 0; i < testSpacings.length; ++i) { - var sp = testSpacings[i] - if (isNaN(sp)) { - sp = 5 // Test defaults - } else { - layout.columnSpacing = sp - } - tryCompare(layout.children[0], "itemRect", [ 0, 0, 10, 10]) - tryCompare(layout.children[1], "itemRect", [10 + sp, 0, 10, 10]) - compare(layout.implicitWidth, 20 + sp) + var spacing = data.spacing + if (isNaN(spacing)) { + spacing = 5 // Test defaults + } else { + layout.columnSpacing = spacing } + tryCompare(layout.children[0], "itemRect", [ 0, 0, 10, 10]) + tryCompare(layout.children[1], "itemRect", [10 + spacing, 0, 10, 10]) + compare(layout.implicitWidth, 20 + spacing) // do not crash layout.columnSpacing = -100 waitForRendering(layout) verify(isFinite(layout.implicitWidth)) - layout.destroy(); } Component { @@ -1026,11 +1014,10 @@ Item { function test_invalidateWhileRearranging_QTBUG_44139() { - var layout = layout_invalidateWhileRearranging_Component.createObject(container) + var layout = createTemporaryObject(layout_invalidateWhileRearranging_Component, container) waitForRendering(layout); verify(layout.children[1].visible == false); - layout.destroy() } } } diff --git a/tests/auto/quick/qquicklistview/data/delegatesWithRequiredProperties.qml b/tests/auto/quick/qquicklistview/data/delegatesWithRequiredProperties.qml new file mode 100644 index 0000000000..f354517678 --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/delegatesWithRequiredProperties.qml @@ -0,0 +1,47 @@ +import QtQuick 2.12 +import Qt.fruit 1.0 + +Rectangle { + id: root + required property bool useCpp + width: 200; height: 200 + + + ListModel { + id: fruitModel + + ListElement { + name: "Apple" + cost: 2 + } + ListElement { + name: "Orange" + cost: 3 + } + ListElement { + name: "Banana" + cost: 1 + } + } + + + Component { + id: fruitDelegate + Row { + id: row + spacing: 10 + required property string name + required property int cost + Text { text: row.name } + Text { text: '$' + row.cost } + Component.onCompleted: () => { console.debug(row.name+row.cost) }; + } + } + + ListView { + anchors.fill: parent + model: root.useCpp ? FruitModelCpp : fruitModel + delegate: fruitDelegate + } + +} diff --git a/tests/auto/quick/qquicklistview/data/listview-sections_delegate_required.qml b/tests/auto/quick/qquicklistview/data/listview-sections_delegate_required.qml new file mode 100644 index 0000000000..18ce406e3f --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/listview-sections_delegate_required.qml @@ -0,0 +1,77 @@ +import QtQuick 2.0 + +Rectangle { + property string sectionProperty: "number" + property int sectionPositioning: ViewSection.InlineLabels + width: 240 + height: 320 + color: "#ffffff" + resources: [ + Component { + id: myDelegate + Item { + id: wrapper + objectName: "wrapper" + property string section: ListView.section + property string nextSection: ListView.nextSection + property string prevSection: ListView.previousSection + height: 20; + width: 240 + Rectangle { + height: 20 + width: parent.width + color: wrapper.ListView.isCurrentItem ? "lightsteelblue" : "white" + Text { + text: index + } + Text { + x: 30 + id: textName + objectName: "textName" + text: name + } + Text { + x: 100 + id: textNumber + objectName: "textNumber" + text: number + } + Text { + objectName: "nextSection" + x: 150 + text: wrapper.ListView.nextSection + } + Text { + x: 200 + text: wrapper.y + } + } + ListView.onRemove: SequentialAnimation { + PropertyAction { target: wrapper; property: "ListView.delayRemove"; value: true } + NumberAnimation { target: wrapper; property: "height"; to: 0; duration: 100; easing.type: Easing.InOutQuad } + PropertyAction { target: wrapper; property: "ListView.delayRemove"; value: false } + } + } + } + ] + ListView { + id: list + objectName: "list" + width: 240 + height: 320 + cacheBuffer: 60 + model: testModel + delegate: myDelegate + section.property: sectionProperty + section.delegate: Rectangle { + id: myDelegate + required property string section + objectName: "sect_" + section + color: "#99bb99" + height: 20 + width: list.width + Text { text: myDelegate.section + ", " + parent.y + ", " + parent.objectName } + } + section.labelPositioning: sectionPositioning + } +} diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index 08149a1786..fb5ae168e8 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -127,6 +127,7 @@ private slots: void qAbstractItemModel_package_sections(); void qAbstractItemModel_sections(); void sectionsPositioning(); + void sectionsDelegate_data(); void sectionsDelegate(); void sectionsDragOutsideBounds_data(); void sectionsDragOutsideBounds(); @@ -280,6 +281,8 @@ private slots: void touchCancel(); void resizeAfterComponentComplete(); + void delegateWithRequiredProperties(); + private: template <class T> void items(const QUrl &source); template <class T> void changed(const QUrl &source); @@ -389,7 +392,7 @@ void tst_QQuickListView::init() m_view = nullptr; } #endif - qmlRegisterType<QAbstractItemModel>(); + qmlRegisterAnonymousType<QAbstractItemModel>("Proxy", 1); qmlRegisterType<ProxyTestInnerModel>("Proxy", 1, 0, "ProxyTestInnerModel"); qmlRegisterType<QSortFilterProxyModel>("Proxy", 1, 0, "QSortFilterProxyModel"); } @@ -1943,6 +1946,31 @@ void tst_QQuickListView::enforceRange() QTRY_COMPARE(listview->currentIndex(), 6); + // Test for [QTBUG-77418] { + // explicit set current index + listview->setCurrentIndex(5); + QTRY_COMPARE(listview->contentY(), 0); + + // then check if contentY changes if the highlight range is changed + listview->setPreferredHighlightBegin(80); + listview->setPreferredHighlightEnd(80); + QTRY_COMPARE(listview->contentY(), 20); + + // verify that current index does not change with no highlight + listview->setHighlightRangeMode(QQuickListView::NoHighlightRange); + listview->setContentY(100); + QTRY_COMPARE(listview->currentIndex(), 5); + + // explicit set current index, contentY should not change now + listview->setCurrentIndex(6); + QTRY_COMPARE(listview->contentY(), 100); + QTest::qWait(50); // This was needed in order to reproduce a failure for the following test + + // verify that contentY changes if we turn on highlight again + listview->setHighlightRangeMode(QQuickListView::StrictlyEnforceRange); + QTRY_COMPARE(listview->contentY(), 40); + // } Test for [QTBUG-77418] + // change model QaimModel model2; for (int i = 0; i < 5; i++) @@ -2158,8 +2186,17 @@ void tst_QQuickListView::sections(const QUrl &source) QTRY_COMPARE(item->height(), 40.0); } +void tst_QQuickListView::sectionsDelegate_data() +{ + QTest::addColumn<QUrl>("path"); + QTest::addRow("implicit") << testFileUrl("listview-sections_delegate.qml"); + QTest::addRow("required") << testFileUrl("listview-sections_delegate_required.qml"); +} + void tst_QQuickListView::sectionsDelegate() { + QFETCH(QUrl, path); + QScopedPointer<QQuickView> window(createView()); QaimModel model; @@ -2169,7 +2206,7 @@ void tst_QQuickListView::sectionsDelegate() QQmlContext *ctxt = window->rootContext(); ctxt->setContextProperty("testModel", &model); - window->setSource(testFileUrl("listview-sections_delegate.qml")); + window->setSource(path); window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); @@ -9037,6 +9074,90 @@ void tst_QQuickListView::resizeAfterComponentComplete() // QTBUG-76487 QTRY_COMPARE(lastItem->property("y").toInt(), 9 * lastItem->property("height").toInt()); } +class Animal +{ +public: + Animal(const int cost, const QString &name) {m_name = name; m_cost = cost;} + + int cost() const {return m_cost;} + QString name() const {return m_name;} + + QString m_name; + int m_cost; +}; + +class FruitModel : public QAbstractListModel +{ + Q_OBJECT +public: + enum AnimalRoles { + NameRole = Qt::UserRole + 1, + CostRole + }; + + FruitModel(QObject* = nullptr) { + m_animals.push_back(Animal {4, QLatin1String("Melon")}); + m_animals.push_back(Animal {5, QLatin1String("Cherry")}); + } + + int rowCount(const QModelIndex & = QModelIndex()) const override {return m_animals.count();} + + QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override { + if (!checkIndex(index)) + return {}; + const Animal &animal = m_animals[index.row()]; + if (role == CostRole) + return animal.cost(); + else if (role == NameRole) + return animal.name(); + return QVariant(); + } + +protected: + QHash<int, QByteArray> roleNames() const override { + QHash<int, QByteArray> roles; + roles[CostRole] = "cost"; + roles[NameRole] = "name"; + return roles; + } +private: + QList<Animal> m_animals; +}; + +void tst_QQuickListView::delegateWithRequiredProperties() +{ + FruitModel myModel; + qmlRegisterSingletonInstance("Qt.fruit", 1, 0, "FruitModelCpp", &myModel); + { + // ListModel + QTest::ignoreMessage(QtMsgType::QtDebugMsg, "Apple2"); + QTest::ignoreMessage(QtMsgType::QtDebugMsg, "Orange3"); + QTest::ignoreMessage(QtMsgType::QtDebugMsg, "Banana1"); + QScopedPointer<QQuickView> window(createView()); + window->setInitialProperties({{QLatin1String("useCpp"), false}}); + window->setSource(testFileUrl("delegatesWithRequiredProperties.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QObject *listView = window->rootObject(); + QVERIFY(listView); + } + { + // C++ model + QTest::ignoreMessage(QtMsgType::QtDebugMsg, "Melon4"); + QTest::ignoreMessage(QtMsgType::QtDebugMsg, "Cherry5"); + QScopedPointer<QQuickView> window(createView()); + window->setInitialProperties({{QLatin1String("useCpp"), true}}); + window->setSource(testFileUrl("delegatesWithRequiredProperties.qml")); + + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QObject *listView = window->rootObject(); + QVERIFY(listView); + } +} + QTEST_MAIN(tst_QQuickListView) #include "tst_qquicklistview.moc" diff --git a/tests/auto/quick/qquickloader/data/RequiredPropertyValuesComponent.qml b/tests/auto/quick/qquickloader/data/RequiredPropertyValuesComponent.qml new file mode 100644 index 0000000000..7bb21e8b93 --- /dev/null +++ b/tests/auto/quick/qquickloader/data/RequiredPropertyValuesComponent.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +Item { + id: behaviorCounter + required property int i + required property string s + +} diff --git a/tests/auto/quick/qquickloader/data/initialPropertyValues.10.qml b/tests/auto/quick/qquickloader/data/initialPropertyValues.10.qml new file mode 100644 index 0000000000..4728346ca1 --- /dev/null +++ b/tests/auto/quick/qquickloader/data/initialPropertyValues.10.qml @@ -0,0 +1,20 @@ +import QtQuick 2.0 + +Item { + id: root + property int i: 0 + property string s: "" + + Loader { + id: loader + objectName: "loader" + onLoaded: { + root.i = loader.item.i; // should be 42 + root.s = loader.item.s; // should be 11 + } + } + + Component.onCompleted: { + loader.setSource("RequiredPropertyValuesComponent.qml", {"i": 42}); + } +} diff --git a/tests/auto/quick/qquickloader/data/initialPropertyValues.9.qml b/tests/auto/quick/qquickloader/data/initialPropertyValues.9.qml new file mode 100644 index 0000000000..5d6e3171a0 --- /dev/null +++ b/tests/auto/quick/qquickloader/data/initialPropertyValues.9.qml @@ -0,0 +1,20 @@ +import QtQuick 2.0 + +Item { + id: root + property int i: 0 + property string s: "" + + Loader { + id: loader + objectName: "loader" + onLoaded: { + root.i = loader.item.i; // should be 42 + root.s = loader.item.s; // should be 11 + } + } + + Component.onCompleted: { + loader.setSource("RequiredPropertyValuesComponent.qml", {"i": 42, "s": "hello world"}); + } +} diff --git a/tests/auto/quick/qquickloader/tst_qquickloader.cpp b/tests/auto/quick/qquickloader/tst_qquickloader.cpp index fbdd87905b..da923d4d41 100644 --- a/tests/auto/quick/qquickloader/tst_qquickloader.cpp +++ b/tests/auto/quick/qquickloader/tst_qquickloader.cpp @@ -681,6 +681,16 @@ void tst_QQuickLoader::initialPropertyValues_data() << QStringList() << (QStringList() << "initialValue") << (QVariantList() << 6); + + QTest::newRow("ensure required properties are set correctly") << testFileUrl("initialPropertyValues.9.qml") + << QStringList() + << (QStringList() << "i" << "s") + << (QVariantList() << 42 << QLatin1String("hello world")); + + QTest::newRow("required properties only partially set =") << testFileUrl("initialPropertyValues.10.qml") + << (QStringList() << QString(testFileUrl("RequiredPropertyValuesComponent.qml").toString() + QLatin1String(":6:5: Required property s was not initialized"))) + << (QStringList() << "i" << "s") + << (QVariantList() << 0 << QLatin1String("")); } void tst_QQuickLoader::initialPropertyValues() diff --git a/tests/auto/quick/qquickmousearea/BLACKLIST b/tests/auto/quick/qquickmousearea/BLACKLIST new file mode 100644 index 0000000000..f2cb00225b --- /dev/null +++ b/tests/auto/quick/qquickmousearea/BLACKLIST @@ -0,0 +1,4 @@ +# QTBUG-78153 +[nestedStopAtBounds] +opensuse-leap + diff --git a/tests/auto/quick/qquickmousearea/data/dragreset.qml b/tests/auto/quick/qquickmousearea/data/dragreset.qml index 10039f1fcb..bbe0160080 100644 --- a/tests/auto/quick/qquickmousearea/data/dragreset.qml +++ b/tests/auto/quick/qquickmousearea/data/dragreset.qml @@ -1,6 +1,7 @@ import QtQuick 2.0 Rectangle { id: whiteRect + required property bool haveTarget width: 200 height: 200 color: "white" diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 5844720aa4..54a29dbc7f 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -307,7 +307,7 @@ void tst_QQuickMouseArea::resetDrag() { QQuickView window; QByteArray errorMessage; - window.rootContext()->setContextProperty("haveTarget", QVariant(true)); + window.setInitialProperties({{"haveTarget", true}}); QVERIFY2(QQuickTest::initView(window, testFileUrl("dragreset.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); @@ -326,7 +326,9 @@ void tst_QQuickMouseArea::resetDrag() QVERIFY(rootItem != nullptr); QSignalSpy targetSpy(drag, SIGNAL(targetChanged())); QVERIFY(drag->target() != nullptr); - window.rootContext()->setContextProperty("haveTarget", QVariant(false)); + auto root = window.rootObject(); + QQmlProperty haveTarget {root, "haveTarget"}; + haveTarget.write(false); QCOMPARE(targetSpy.count(),1); QVERIFY(!drag->target()); } @@ -1359,6 +1361,34 @@ void tst_QQuickMouseArea::hoverVisible() QCOMPARE(enteredSpy.count(), 1); QCOMPARE(QPointF(mouseTracker->mouseX(), mouseTracker->mouseY()), QPointF(11,33)); + + // QTBUG-77983 + mouseTracker->setVisible(false); + mouseTracker->setEnabled(false); + + QCOMPARE(mouseTracker->hovered(), false); + mouseTracker->setVisible(true); + // if the enabled property is false, the containsMouse property shouldn't become true + // when an invisible mousearea become visible + QCOMPARE(mouseTracker->hovered(), false); + + mouseTracker->parentItem()->setEnabled(false); + mouseTracker->setVisible(false); + mouseTracker->setEnabled(true); + + QCOMPARE(mouseTracker->hovered(), false); + mouseTracker->setVisible(true); + // if the parent item is not enabled, the containsMouse property will be false, even if + // the mousearea is enabled + QCOMPARE(mouseTracker->hovered(), false); + + mouseTracker->parentItem()->setEnabled(true); + mouseTracker->setVisible(false); + mouseTracker->setEnabled(true); + + QCOMPARE(mouseTracker->hovered(), false); + mouseTracker->setVisible(true); + QCOMPARE(mouseTracker->hovered(), true); } void tst_QQuickMouseArea::hoverAfterPress() diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp index cd66fc4ede..e96b892b54 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp +++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp @@ -72,6 +72,7 @@ private slots: void mouseGestureStarted_data(); void mouseGestureStarted(); void cancel(); + void stationaryTouchWithChangingPressure(); private: QQuickView *createAndShowView(const QString &file); @@ -1305,6 +1306,42 @@ void tst_QQuickMultiPointTouchArea::cancel() } +void tst_QQuickMultiPointTouchArea::stationaryTouchWithChangingPressure() // QTBUG-77142 +{ + QScopedPointer<QQuickView> window(createAndShowView("basic.qml")); + QVERIFY(window->rootObject() != nullptr); + + QQuickTouchPoint *point1 = window->rootObject()->findChild<QQuickTouchPoint*>("point1"); + QCOMPARE(point1->pressed(), false); + + QPoint p1(20,100); + QTouchEvent::TouchPoint tp1(1); + + tp1.setScreenPos(window->mapToGlobal(p1)); + tp1.setState(Qt::TouchPointPressed); + tp1.setPressure(0.5); + qt_handleTouchEvent(window.data(), device, {tp1}); + QQuickTouchUtils::flush(window.data()); + + QCOMPARE(point1->pressed(), true); + QCOMPARE(point1->pressure(), 0.5); + + tp1.setState(Qt::TouchPointStationary); + tp1.setPressure(0.6); + qt_handleTouchEvent(window.data(), device, {tp1}); + QQuickTouchUtils::flush(window.data()); + + QCOMPARE(point1->pressure(), 0.6); + + tp1.setState(Qt::TouchPointReleased); + tp1.setPressure(0); + qt_handleTouchEvent(window.data(), device, {tp1}); + QQuickTouchUtils::flush(window.data()); + + QCOMPARE(point1->pressed(), false); + QCOMPARE(point1->pressure(), 0); +} + QTEST_MAIN(tst_QQuickMultiPointTouchArea) diff --git a/tests/auto/quick/qquickpathview/data/delegateWithRequiredProperties.2.qml b/tests/auto/quick/qquickpathview/data/delegateWithRequiredProperties.2.qml new file mode 100644 index 0000000000..ae8ca784bc --- /dev/null +++ b/tests/auto/quick/qquickpathview/data/delegateWithRequiredProperties.2.qml @@ -0,0 +1,59 @@ +import QtQuick 2.14 + +Item { + id: root + width: 800 + height: 600 + property bool working: false + + ListModel { + id: myModel + ListElement { + name: "Bill Jones" + place: "Berlin" + } + ListElement { + name: "Jane Doe" + place: "Oslo" + } + ListElement { + name: "John Smith" + place: "Oulo" + } + } + + Component { + id: delegateComponent + Rectangle { + id: myDelegate + height: 50 + width: 50 + required property string name + required property int index + onNameChanged: () => {if (myDelegate.name === "You-know-who") root.working = true} + Text { + text: myDelegate.name + font.pointSize: 10 + anchors.fill: myDelegate + } + } + } + + PathView { + anchors.fill: parent + model: myModel + delegate: delegateComponent + path: Path { + startX: 80; startY: 100 + PathQuad { x: 120; y: 25; controlX: 260; controlY: 75 } + PathQuad { x: 140; y: 100; controlX: -20; controlY: 75 } + } + } + Timer { + interval: 1 + running: true + repeat: false + onTriggered: () => { myModel.setProperty(1, "name", "You-know-who"); } + } + +} diff --git a/tests/auto/quick/qquickpathview/data/delegateWithRequiredProperties.3.qml b/tests/auto/quick/qquickpathview/data/delegateWithRequiredProperties.3.qml new file mode 100644 index 0000000000..2996ba18fd --- /dev/null +++ b/tests/auto/quick/qquickpathview/data/delegateWithRequiredProperties.3.qml @@ -0,0 +1,64 @@ +import QtQuick 2.14 + +Item { + id: root + width: 800 + height: 600 + property bool working: false + + ListModel { + id: myModel + ListElement { + name: "Bill Jones" + place: "Berlin" + } + ListElement { + name: "Jane Doe" + place: "Oslo" + } + ListElement { + name: "John Smith" + place: "Oulo" + } + } + + Component { + id: delegateComponent + Rectangle { + id: myDelegate + height: 50 + width: 50 + required property string name + required property int index + onNameChanged: () => {if (myDelegate.name === "You-know-who") root.working = false} + Text { + text: myDelegate.name + font.pointSize: 10 + anchors.fill: myDelegate + } + Component.onCompleted: () => {myDelegate.name = "break binding"} + } + } + + PathView { + anchors.fill: parent + model: myModel + delegate: delegateComponent + path: Path { + startX: 80; startY: 100 + PathQuad { x: 120; y: 25; controlX: 260; controlY: 75 } + PathQuad { x: 140; y: 100; controlX: -20; controlY: 75 } + } + } + Timer { + interval: 1 + running: true + repeat: false + onTriggered: () => { + myModel.setProperty(1, "name", "You-know-who") + myModel.setProperty(2, "name", "You-know-who") + root.working = true + } + } + +} diff --git a/tests/auto/quick/qquickpathview/data/delegateWithRequiredProperties.qml b/tests/auto/quick/qquickpathview/data/delegateWithRequiredProperties.qml new file mode 100644 index 0000000000..5d721fd0c4 --- /dev/null +++ b/tests/auto/quick/qquickpathview/data/delegateWithRequiredProperties.qml @@ -0,0 +1,53 @@ +import QtQuick 2.14 + +Item { + width: 400 + height: 200 + + ListModel { + id: myModel + ListElement { + name: "Bill Jones" + place: "Berlin" + } + ListElement { + name: "Jane Doe" + place: "Oslo" + } + ListElement { + name: "John Smith" + place: "Oulo" + } + } + + Component { + id: delegateComponent + Rectangle { + id: myDelegate + required property int index + required property string name + required property string place + height: 100 + width: 100 + Text { + text: myDelegate.name + " lives in " + myDelegate.place + myDelegate.index + font.pointSize: 16 + anchors.fill: myDelegate + + Component.onCompleted: () => {console.info(myDelegate.name+myDelegate.place+myDelegate.index)} + } + } + } + + PathView { + anchors.fill: parent + model: myModel + delegate: delegateComponent + path: Path { + startX: 120; startY: 100 + PathQuad { x: 120; y: 25; controlX: 260; controlY: 75 } + PathQuad { x: 120; y: 100; controlX: -20; controlY: 75 } + } + } + +} diff --git a/tests/auto/quick/qquickpathview/data/delegatewithUnrelatedRequiredPreventsAccessToModel.qml b/tests/auto/quick/qquickpathview/data/delegatewithUnrelatedRequiredPreventsAccessToModel.qml new file mode 100644 index 0000000000..bf130a2d73 --- /dev/null +++ b/tests/auto/quick/qquickpathview/data/delegatewithUnrelatedRequiredPreventsAccessToModel.qml @@ -0,0 +1,52 @@ +import QtQuick 2.14 + +Item { + width: 400 + height: 200 + + ListModel { + id: myModel + ListElement { + name: "Bill Jones" + place: "Berlin" + } + ListElement { + name: "Jane Doe" + place: "Oslo" + } + ListElement { + name: "John Smith" + place: "Oulo" + } + } + + Component { + id: delegateComponent + Rectangle { + id: myDelegate + required property int set + set: 42 + height: 100 + width: 100 + Text { + text: "Test" + font.pointSize: 16 + anchors.fill: myDelegate + + Component.onCompleted: () => { try {index; console.log(index); console.log(name)} catch(ex) {console.info(ex.name)} } + } + } + } + + PathView { + anchors.fill: parent + model: myModel + delegate: delegateComponent + path: Path { + startX: 120; startY: 100 + PathQuad { x: 120; y: 25; controlX: 260; controlY: 75 } + PathQuad { x: 120; y: 100; controlX: -20; controlY: 75 } + } + } + +} diff --git a/tests/auto/quick/qquickpathview/data/nestedmousearea2.qml b/tests/auto/quick/qquickpathview/data/nestedmousearea2.qml new file mode 100644 index 0000000000..ff11002552 --- /dev/null +++ b/tests/auto/quick/qquickpathview/data/nestedmousearea2.qml @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** 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.0 + +PathView { + id: view + width: 400; height: 240 + highlight: Rectangle { width: 80; height: 80; color: "lightsteelblue" } + preferredHighlightBegin: 0.5 + preferredHighlightEnd: 0.5 + model: ListModel { + id: appModel + ListElement { name: "Music" } + ListElement { name: "Movies" } + ListElement { name: "Camera" } + ListElement { name: "Calendar" } + ListElement { name: "Messaging" } + ListElement { name: "Todo List" } + ListElement { name: "Contacts" } + } + delegate: Rectangle { + width: 100; height: 100 + scale: PathView.iconScale + border.color: "lightgrey" + color: "transparent" + Text { + anchors { horizontalCenter: parent.horizontalCenter } + text: name + smooth: true + color: ma.pressed ? "red" : "black" + } + + MouseArea { + id: ma + anchors.fill: parent + onClicked: view.currentIndex = index + } + } + path: Path { + startX: 10 + startY: 50 + PathAttribute { name: "iconScale"; value: 0.5 } + PathQuad { x: 200; y: 150; controlX: 50; controlY: 200 } + PathAttribute { name: "iconScale"; value: 1.0 } + PathQuad { x: 390; y: 50; controlX: 350; controlY: 200 } + PathAttribute { name: "iconScale"; value: 0.5 } + } + Text { + anchors.horizontalCenter: parent.horizontalCenter + y: 20 + text: view.currentIndex + " @ " + offset.toFixed(2) + } +} diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp index 8b963117ed..a8e847a5c7 100644 --- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp +++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp @@ -120,6 +120,7 @@ private slots: void undefinedPath(); void mouseDrag(); void nestedMouseAreaDrag(); + void flickNClick(); void treeModel(); void changePreferredHighlight(); void missingPercent(); @@ -149,6 +150,8 @@ private slots: void movementDirection(); void removePath(); void objectModelMove(); + void requiredPropertiesInDelegate(); + void requiredPropertiesInDelegatePreventUnrelated(); }; class TestObject : public QObject @@ -1601,6 +1604,71 @@ void tst_QQuickPathView::nestedMouseAreaDrag() QVERIFY(pathview->isMoving()); } +void tst_QQuickPathView::flickNClick() // QTBUG-77173 +{ + QScopedPointer<QQuickView> window(createView()); + QQuickViewTestUtil::moveMouseAway(window.data()); + window->setSource(testFileUrl("nestedmousearea2.qml")); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QCOMPARE(window.data(), qGuiApp->focusWindow()); + + QQuickPathView *pathview = qobject_cast<QQuickPathView*>(window->rootObject()); + QVERIFY(pathview != nullptr); + QSignalSpy movingChangedSpy(pathview, SIGNAL(movingChanged())); + QSignalSpy draggingSpy(pathview, SIGNAL(draggingChanged())); + QSignalSpy dragStartedSpy(pathview, SIGNAL(dragStarted())); + QSignalSpy dragEndedSpy(pathview, SIGNAL(dragEnded())); + QSignalSpy currentIndexSpy(pathview, SIGNAL(currentIndexChanged())); + QSignalSpy moveStartedSpy(pathview, SIGNAL(movementStarted())); + QSignalSpy moveEndedSpy(pathview, SIGNAL(movementEnded())); + QSignalSpy flickingSpy(pathview, SIGNAL(flickingChanged())); + QSignalSpy flickStartedSpy(pathview, SIGNAL(flickStarted())); + QSignalSpy flickEndedSpy(pathview, SIGNAL(flickEnded())); + + for (int duration = 100; duration > 0; duration -= 20) { + movingChangedSpy.clear(); + draggingSpy.clear(); + dragStartedSpy.clear(); + dragEndedSpy.clear(); + currentIndexSpy.clear(); + moveStartedSpy.clear(); + moveEndedSpy.clear(); + flickingSpy.clear(); + flickStartedSpy.clear(); + flickEndedSpy.clear(); + // Dragging the child mouse area should animate the PathView (MA has no drag target) + flick(window.data(), QPoint(200,200), QPoint(400,200), duration); + QVERIFY(pathview->isMoving()); + QCOMPARE(movingChangedSpy.count(), 1); + QCOMPARE(draggingSpy.count(), 2); + QCOMPARE(dragStartedSpy.count(), 1); + QCOMPARE(dragEndedSpy.count(), 1); + QVERIFY(currentIndexSpy.count() > 0); + QCOMPARE(moveStartedSpy.count(), 1); + QCOMPARE(moveEndedSpy.count(), 0); + QCOMPARE(flickingSpy.count(), 1); + QCOMPARE(flickStartedSpy.count(), 1); + QCOMPARE(flickEndedSpy.count(), 0); + + // Now while it's still moving, click it. + // The PathView should stop at a position such that offset is a whole number. + QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, QPoint(200, 200)); + QTRY_VERIFY(!pathview->isMoving()); + QCOMPARE(movingChangedSpy.count(), 2); // QTBUG-78926 + QCOMPARE(draggingSpy.count(), 2); + QCOMPARE(dragStartedSpy.count(), 1); + QCOMPARE(dragEndedSpy.count(), 1); + QCOMPARE(moveStartedSpy.count(), 1); + QCOMPARE(moveEndedSpy.count(), 1); + QCOMPARE(flickingSpy.count(), 2); + QCOMPARE(flickStartedSpy.count(), 1); + QCOMPARE(flickEndedSpy.count(), 1); + QVERIFY(qFuzzyIsNull(pathview->offset() - int(pathview->offset()))); + } +} + void tst_QQuickPathView::treeModel() { QScopedPointer<QQuickView> window(createView()); @@ -2658,6 +2726,41 @@ void tst_QQuickPathView::objectModelMove() } } +void tst_QQuickPathView::requiredPropertiesInDelegate() +{ + { + QTest::ignoreMessage(QtMsgType::QtInfoMsg, "Bill JonesBerlin0"); + QTest::ignoreMessage(QtMsgType::QtInfoMsg, "Jane DoeOslo1"); + QTest::ignoreMessage(QtMsgType::QtInfoMsg, "John SmithOulo2"); + QScopedPointer<QQuickView> window(createView()); + window->setSource(testFileUrl("delegateWithRequiredProperties.qml")); + window->show(); + } + { + QScopedPointer<QQuickView> window(createView()); + window->setSource(testFileUrl("delegateWithRequiredProperties.2.qml")); + window->show(); + QTRY_VERIFY(window->rootObject()->property("working").toBool()); + } + { + QScopedPointer<QQuickView> window(createView()); + QTest::ignoreMessage(QtMsgType::QtWarningMsg, QRegularExpression("Writing to \"name\" broke the binding to the underlying model")); + window->setSource(testFileUrl("delegateWithRequiredProperties.3.qml")); + window->show(); + QTRY_VERIFY(window->rootObject()->property("working").toBool()); + } +} + +void tst_QQuickPathView::requiredPropertiesInDelegatePreventUnrelated() +{ + QTest::ignoreMessage(QtMsgType::QtInfoMsg, "ReferenceError"); + QTest::ignoreMessage(QtMsgType::QtInfoMsg, "ReferenceError"); + QTest::ignoreMessage(QtMsgType::QtInfoMsg, "ReferenceError"); + QScopedPointer<QQuickView> window(createView()); + window->setSource(testFileUrl("delegatewithUnrelatedRequiredPreventsAccessToModel.qml")); + window->show(); +} + QTEST_MAIN(tst_QQuickPathView) #include "tst_qquickpathview.moc" diff --git a/tests/auto/quick/qquickpositioners/data/transitions-padding.qml b/tests/auto/quick/qquickpositioners/data/transitions-padding.qml index e3175c480c..eda9ce628e 100644 --- a/tests/auto/quick/qquickpositioners/data/transitions-padding.qml +++ b/tests/auto/quick/qquickpositioners/data/transitions-padding.qml @@ -5,6 +5,16 @@ Rectangle { width: 500 height: 500 + required property bool usePopulateTransition + required property bool enableAddTransition + required property bool dynamicallyPopulate + required property var testModel + required property var model_targetItems_transitionFrom + required property var model_displacedItems_transitionVia + required property point targetItems_transitionFrom + required property point displacedItems_transitionVia + required property string testedPositioner + property int duration: 50 property real incrementalSize: 5 diff --git a/tests/auto/quick/qquickpositioners/data/transitions.qml b/tests/auto/quick/qquickpositioners/data/transitions.qml index a1f27bb06e..988a01e373 100644 --- a/tests/auto/quick/qquickpositioners/data/transitions.qml +++ b/tests/auto/quick/qquickpositioners/data/transitions.qml @@ -5,6 +5,16 @@ Rectangle { width: 500 height: 500 + required property bool usePopulateTransition + required property bool enableAddTransition + required property bool dynamicallyPopulate + required property var testModel + required property var model_targetItems_transitionFrom + required property var model_displacedItems_transitionVia + required property point targetItems_transitionFrom + required property point displacedItems_transitionVia + required property string testedPositioner + property int duration: 50 property real incrementalSize: 5 diff --git a/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp b/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp index d3c0f345b9..e6bbd8c215 100644 --- a/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp +++ b/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp @@ -1025,16 +1025,17 @@ void tst_qquickpositioners::populateTransitions(const QString &positionerObjectN QScopedPointer<QQuickView> window(QQuickViewTestUtil::createView()); - QQmlContext *ctxt = window->rootContext(); - ctxt->setContextProperty("usePopulateTransition", usePopulateTransition); - ctxt->setContextProperty("enableAddTransition", true); - ctxt->setContextProperty("dynamicallyPopulate", dynamicallyPopulate); - ctxt->setContextProperty("testModel", &model); - ctxt->setContextProperty("model_targetItems_transitionFrom", &model_targetItems_transitionFrom); - ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia); - ctxt->setContextProperty("targetItems_transitionFrom", targetItems_transitionFrom); - ctxt->setContextProperty("displacedItems_transitionVia", displacedItems_transitionVia); - ctxt->setContextProperty("testedPositioner", positionerObjectName); + window->setInitialProperties({ + {"usePopulateTransition", usePopulateTransition}, + {"enableAddTransition", true}, + {"dynamicallyPopulate", dynamicallyPopulate}, + {"testModel", QVariant::fromValue(&model)}, + {"model_targetItems_transitionFrom", QVariant::fromValue(&model_targetItems_transitionFrom)}, + {"model_displacedItems_transitionVia", QVariant::fromValue(&model_displacedItems_transitionVia)}, + {"targetItems_transitionFrom", targetItems_transitionFrom}, + {"displacedItems_transitionVia", displacedItems_transitionVia}, + {"testedPositioner", positionerObjectName} + }); window->setSource(testFileUrl(qmlFile)); QQuickItem *positioner = window->rootObject()->findChild<QQuickItem*>(positionerObjectName); @@ -1111,16 +1112,17 @@ void tst_qquickpositioners::addTransitions(const QString &positionerObjectName) QaimModel model_displacedItems_transitionVia; QScopedPointer<QQuickView> window(QQuickViewTestUtil::createView()); - QQmlContext *ctxt = window->rootContext(); - ctxt->setContextProperty("usePopulateTransition", QVariant(false)); - ctxt->setContextProperty("enableAddTransition", QVariant(true)); - ctxt->setContextProperty("dynamicallyPopulate", QVariant(false)); - ctxt->setContextProperty("testModel", &model); - ctxt->setContextProperty("model_targetItems_transitionFrom", &model_targetItems_transitionFrom); - ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia); - ctxt->setContextProperty("targetItems_transitionFrom", targetItems_transitionFrom); - ctxt->setContextProperty("displacedItems_transitionVia", displacedItems_transitionVia); - ctxt->setContextProperty("testedPositioner", QString()); + window->setInitialProperties({ + {"usePopulateTransition", QVariant(false)}, + {"enableAddTransition", QVariant(true)}, + {"dynamicallyPopulate", QVariant(false)}, + {"testModel", QVariant::fromValue(&model)}, + {"model_targetItems_transitionFrom", QVariant::fromValue(&model_targetItems_transitionFrom)}, + {"model_displacedItems_transitionVia", QVariant::fromValue(&model_displacedItems_transitionVia)}, + {"targetItems_transitionFrom", targetItems_transitionFrom}, + {"displacedItems_transitionVia", displacedItems_transitionVia}, + {"testedPositioner", QString()} + }); window->setSource(testFileUrl(qmlFile)); window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); @@ -1234,16 +1236,17 @@ void tst_qquickpositioners::moveTransitions(const QString &positionerObjectName) QaimModel model_displacedItems_transitionVia; QScopedPointer<QQuickView> window(QQuickViewTestUtil::createView()); - QQmlContext *ctxt = window->rootContext(); - ctxt->setContextProperty("usePopulateTransition", QVariant(false)); - ctxt->setContextProperty("enableAddTransition", QVariant(false)); - ctxt->setContextProperty("dynamicallyPopulate", QVariant(false)); - ctxt->setContextProperty("testModel", &model); - ctxt->setContextProperty("model_targetItems_transitionFrom", &model_targetItems_transitionFrom); - ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia); - ctxt->setContextProperty("targetItems_transitionFrom", targetItems_transitionFrom); - ctxt->setContextProperty("displacedItems_transitionVia", displacedItems_transitionVia); - ctxt->setContextProperty("testedPositioner", QString()); + window->setInitialProperties({ + {"usePopulateTransition", QVariant(false)}, + {"enableAddTransition", QVariant(false)}, + {"dynamicallyPopulate", QVariant(false)}, + {"testModel", QVariant::fromValue(&model)}, + {"model_targetItems_transitionFrom", QVariant::fromValue(&model_targetItems_transitionFrom)}, + {"model_displacedItems_transitionVia", QVariant::fromValue(&model_displacedItems_transitionVia)}, + {"targetItems_transitionFrom", targetItems_transitionFrom}, + {"displacedItems_transitionVia", displacedItems_transitionVia}, + {"testedPositioner", QString()} + }); window->setSource(testFileUrl(qmlFile)); window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); diff --git a/tests/auto/quick/qquickrepeater/data/objlist_required.qml b/tests/auto/quick/qquickrepeater/data/objlist_required.qml new file mode 100644 index 0000000000..cc9dd9566c --- /dev/null +++ b/tests/auto/quick/qquickrepeater/data/objlist_required.qml @@ -0,0 +1,23 @@ +import QtQuick 2.0 + +Rectangle { + id: container + objectName: "container" + width: 240 + height: 320 + color: "white" + Repeater { + id: repeater + objectName: "repeater" + model: testData + property int errors: 0 + property int instantiated: 0 + Component { + Item{ + required property int index + required property int idx + Component.onCompleted: {if (index != idx) repeater.errors += 1; repeater.instantiated++} + } + } + } +} diff --git a/tests/auto/quick/qquickrepeater/data/requiredProperty.qml b/tests/auto/quick/qquickrepeater/data/requiredProperty.qml new file mode 100644 index 0000000000..80eb3c28ee --- /dev/null +++ b/tests/auto/quick/qquickrepeater/data/requiredProperty.qml @@ -0,0 +1,16 @@ +import QtQuick 2.14 + +Item { + Column { + Repeater { + model: ["apples", "oranges", "pears"] + Text { + id: txt + required property string modelData + required property int index + text: modelData + index + Component.onCompleted: () => {console.info(txt.text)} + } + } + } +} diff --git a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp index 65e7d29595..ccfef63902 100644 --- a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp +++ b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp @@ -55,6 +55,7 @@ public: private slots: void numberModel(); + void objectList_data(); void objectList(); void stringList(); void dataModel_adding(); @@ -79,6 +80,7 @@ private slots: void QTBUG54859_asynchronousMove(); void package(); void ownership(); + void requiredProperties(); }; class TestObject : public QObject @@ -143,6 +145,14 @@ void tst_QQuickRepeater::numberModel() delete window; } +void tst_QQuickRepeater::objectList_data() +{ + QTest::addColumn<QUrl>("filename"); + + QTest::newRow("normal") << testFileUrl("objlist.qml"); + QTest::newRow("required") << testFileUrl("objlist_required.qml"); +} + class MyObject : public QObject { Q_OBJECT @@ -157,6 +167,7 @@ public: void tst_QQuickRepeater::objectList() { + QFETCH(QUrl, filename); QQuickView *window = createView(); QObjectList data; for (int i=0; i<100; i++) @@ -165,7 +176,7 @@ void tst_QQuickRepeater::objectList() QQmlContext *ctxt = window->rootContext(); ctxt->setContextProperty("testData", QVariant::fromValue(data)); - window->setSource(testFileUrl("objlist.qml")); + window->setSource(filename); qApp->processEvents(); QQuickRepeater *repeater = findItem<QQuickRepeater>(window->rootObject(), "repeater"); @@ -1108,6 +1119,18 @@ void tst_QQuickRepeater::ownership() QVERIFY(!modelGuard); } +void tst_QQuickRepeater::requiredProperties() +{ + QTest::ignoreMessage(QtMsgType::QtInfoMsg, "apples0"); + QTest::ignoreMessage(QtMsgType::QtInfoMsg, "oranges1"); + QTest::ignoreMessage(QtMsgType::QtInfoMsg, "pears2"); + QQmlEngine engine; + + QQmlComponent component(&engine, testFileUrl("requiredProperty.qml")); + QScopedPointer<QObject> o {component.create()}; + QVERIFY(o); +} + QTEST_MAIN(tst_QQuickRepeater) #include "tst_qquickrepeater.moc" diff --git a/tests/auto/quick/qquickstates/data/parentChangeCorrectReversal.qml b/tests/auto/quick/qquickstates/data/parentChangeCorrectReversal.qml new file mode 100644 index 0000000000..3d38fa4046 --- /dev/null +++ b/tests/auto/quick/qquickstates/data/parentChangeCorrectReversal.qml @@ -0,0 +1,72 @@ +import QtQuick 2.10 +import QtQuick.Layouts 1.3 + +Item { + id: root + + visible: true + + width: 400 + height: 200 + property bool switchToRight: false + property alias stayingRectX: stayingRect.x + + RowLayout { + id: topLayout + + anchors.fill: parent + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + + Rectangle { + id: leftRect + + width: parent.width*(2/3) + height: width + anchors.centerIn: parent + + color: "red" + + Rectangle { + id: stayingRect + + x: 70; y: 70 + width: 50; height: 50 + + color: "yellow" + } + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + + Rectangle { + id: rightRect + + width: parent.height*(2/3) + height: width + anchors.centerIn: parent + + color: "green" + rotation: 45 + } + } + } + + states: State { + name: "switchToRight" + + ParentChange { + target: stayingRect + parent: rightRect + width: 70 + } + + } + + state: root.switchToRight ? "switchToRight" : "" +} diff --git a/tests/auto/quick/qquickstates/tst_qquickstates.cpp b/tests/auto/quick/qquickstates/tst_qquickstates.cpp index 1eb797f54f..d5fea3cb28 100644 --- a/tests/auto/quick/qquickstates/tst_qquickstates.cpp +++ b/tests/auto/quick/qquickstates/tst_qquickstates.cpp @@ -139,6 +139,7 @@ private slots: void revertListMemoryLeak(); void duplicateStateName(); void trivialWhen(); + void parentChangeCorrectReversal(); }; void tst_qquickstates::initTestCase() @@ -1675,6 +1676,22 @@ void tst_qquickstates::trivialWhen() QVERIFY(c.create()); } +void tst_qquickstates::parentChangeCorrectReversal() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("parentChangeCorrectReversal.qml")); + QScopedPointer<QObject> root {c.create()}; + QVERIFY(root); + QQmlProperty stayingRectX(root.get(), "stayingRectX"); + qreal oldX = stayingRectX.read().toDouble(); + QQmlProperty switchToRight(root.get(), "switchToRight"); + switchToRight.write(true); + qreal newX = stayingRectX.read().toDouble(); + QVERIFY(newX != oldX); + switchToRight.write(false); + QCOMPARE(oldX, stayingRectX.read().toDouble()); +} + QTEST_MAIN(tst_qquickstates) diff --git a/tests/auto/quick/qquicktableview/data/delegateWithRequired.qml b/tests/auto/quick/qquicktableview/data/delegateWithRequired.qml new file mode 100644 index 0000000000..bebfd86931 --- /dev/null +++ b/tests/auto/quick/qquicktableview/data/delegateWithRequired.qml @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** 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 + + TableView { + id: tableView + width: 600 + height: 400 + delegate: tableViewDelegate + } + + Component { + id: tableViewDelegate + Rectangle { + id: rect + required property string position + required property bool hasModelChildren + required property QtObject model + Text {text: rect.position} + implicitWidth: 100 + implicitHeight: 100 + Component.onCompleted: () => {if (rect.position === "R1:C1" && rect.model.hasModelChildren == rect.hasModelChildren) console.info("success")} + } + } + +} diff --git a/tests/auto/quick/qquicktableview/data/delegatewithRequiredUnset.qml b/tests/auto/quick/qquicktableview/data/delegatewithRequiredUnset.qml new file mode 100644 index 0000000000..0c685cd49e --- /dev/null +++ b/tests/auto/quick/qquicktableview/data/delegatewithRequiredUnset.qml @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** 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 + + TableView { + id: tableView + width: 600 + height: 400 + delegate: tableViewDelegate + } + + Component { + id: tableViewDelegate + Rectangle { + id: rect + required property string position + required property bool unset + required property bool hasModelChildren + required property QtObject model + Text {text: rect.position} + implicitWidth: 100 + implicitHeight: 100 + Component.onCompleted: () => {if (rect.position === "R1:C1" && rect.model.hasModelChildren == rect.hasModelChildren) console.info("success")} + } + } + +} diff --git a/tests/auto/quick/qquicktableview/data/tweakimplicitsize.qml b/tests/auto/quick/qquicktableview/data/tweakimplicitsize.qml new file mode 100644 index 0000000000..ecc79a9368 --- /dev/null +++ b/tests/auto/quick/qquicktableview/data/tweakimplicitsize.qml @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** 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 delegateSize: 10 + property int hideRow: -1 + + TableView { + id: tableView + width: 600 + height: 400 + anchors.margins: 1 + clip: true + delegate: tableViewDelegate + columnSpacing: 1 + rowSpacing: 1 + columnWidthProvider: function(column) { + return -1 + } + rowHeightProvider: function(row) { + if (row === hideRow) + return 0 + return -1 + } + } + + Component { + id: tableViewDelegate + Rectangle { + objectName: "tableViewDelegate" + implicitWidth: row === 0 ? 10 : delegateSize + implicitHeight: column === 0 ? 10 : delegateSize + color: "lightgray" + border.width: 1 + + Text { + id: textItem + anchors.centerIn: parent + text: model.display + renderType: Text.NativeRendering + } + } + } + +} + diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp index 19967efcd9..230dcc9446 100644 --- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp +++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp @@ -119,6 +119,7 @@ private slots: void checkRowHeightProviderNegativeReturnValue(); void checkRowHeightProviderNotCallable(); void checkForceLayoutFunction(); + void checkForceLayoutEndUpDoingALayout(); void checkContentWidthAndHeight(); void checkPageFlicking(); void checkExplicitContentWidthAndHeight(); @@ -172,6 +173,7 @@ private slots: void checkSyncView_differentSizedModels(); void checkSyncView_connect_late_data(); void checkSyncView_connect_late(); + void delegateWithRequiredProperties(); }; tst_QQuickTableView::tst_QQuickTableView() @@ -560,6 +562,32 @@ void tst_QQuickTableView::checkForceLayoutFunction() QCOMPARE(fxItem->item->width(), newColumnWidth); } +void tst_QQuickTableView::checkForceLayoutEndUpDoingALayout() +{ + // QTBUG-77074 + // Check that we change the implicit size of the delegate after + // the initial loading, and at the same time hide some rows or + // columns, and then do a forceLayout(), we end up with a + // complete relayout that respects the new implicit size. + LOAD_TABLEVIEW("tweakimplicitsize.qml"); + + auto model = TestModelAsVariant(10, 10); + + tableView->setModel(model); + + WAIT_UNTIL_POLISHED; + + const qreal newDelegateSize = 20; + view->rootObject()->setProperty("delegateSize", newDelegateSize); + // Hide a row, just to force the following relayout to + // do a complete reload (and not just a relayout) + view->rootObject()->setProperty("hideRow", 1); + tableView->forceLayout(); + + for (auto fxItem : tableViewPrivate->loadedItems) + QCOMPARE(fxItem->item->height(), newDelegateSize); +} + void tst_QQuickTableView::checkContentWidthAndHeight() { // Check that contentWidth/Height reports the correct size of the @@ -2592,6 +2620,50 @@ void tst_QQuickTableView::checkSyncView_connect_late() } +void tst_QQuickTableView::delegateWithRequiredProperties() +{ + constexpr static int PositionRole = Qt::UserRole+1; + struct MyTable : QAbstractTableModel { + + + using QAbstractTableModel::QAbstractTableModel; + + int rowCount(const QModelIndex& = QModelIndex()) const override { + return 3; + } + + int columnCount(const QModelIndex& = QModelIndex()) const override { + return 3; + } + + QVariant data(const QModelIndex &index, int = Qt::DisplayRole) const override { + return QVariant::fromValue(QString::asprintf("R%d:C%d", index.row(), index.column())); + } + + QHash<int, QByteArray> roleNames() const override { + return QHash<int, QByteArray> { {PositionRole, "position"} }; + } + }; + + auto model = QVariant::fromValue(QSharedPointer<MyTable>(new MyTable)); + { + QTest::ignoreMessage(QtMsgType::QtInfoMsg, "success"); + LOAD_TABLEVIEW("delegateWithRequired.qml") + QVERIFY(tableView); + tableView->setModel(model); + WAIT_UNTIL_POLISHED; + QVERIFY(view->errors().empty()); + } + { + QTest::ignoreMessage(QtMsgType::QtWarningMsg, QRegularExpression(R"|(TableView: failed loading index: \d)|")); + LOAD_TABLEVIEW("delegatewithRequiredUnset.qml") + QVERIFY(tableView); + tableView->setModel(model); + WAIT_UNTIL_POLISHED; + QTRY_VERIFY(view->errors().empty()); + } +} + QTEST_MAIN(tst_QQuickTableView) #include "tst_qquicktableview.moc" diff --git a/tests/auto/quick/qquicktext/data/transparentBackground.qml b/tests/auto/quick/qquicktext/data/transparentBackground.qml new file mode 100644 index 0000000000..a10a1779bb --- /dev/null +++ b/tests/auto/quick/qquicktext/data/transparentBackground.qml @@ -0,0 +1,16 @@ +import QtQuick 2.0 + +Rectangle { + width: 200 + height: 200 + color: "white" + Text { + objectName: "text" + textFormat: Text.RichText + anchors.fill: parent + color: "black" + text: "<h1 style=\"background-color:rgba(255,255,255,0.00)\">foo</h1>" + verticalAlignment: Text.AlignTop + horizontalAlignment: Text.AlignLeft + } +} diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp index 97107694bd..e62db81d27 100644 --- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp +++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp @@ -164,6 +164,8 @@ private slots: void verticallyAlignedImageInTable(); + void transparentBackground(); + private: QStringList standard; QStringList richText; @@ -4429,6 +4431,26 @@ void tst_qquicktext::verticallyAlignedImageInTable() // Don't crash } +void tst_qquicktext::transparentBackground() +{ + if ((QGuiApplication::platformName() == QLatin1String("offscreen")) + || (QGuiApplication::platformName() == QLatin1String("minimal"))) + QSKIP("Skipping due to grabToImage not functional on offscreen/minimimal platforms"); + + QScopedPointer<QQuickView> window(new QQuickView); + window->setSource(testFileUrl("transparentBackground.qml")); + QTRY_COMPARE(window->status(), QQuickView::Ready); + + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + QImage img = window->grabWindow(); + QCOMPARE(img.isNull(), false); + + QColor color = img.pixelColor(0, 0); + QCOMPARE(color.red(), 255); + QCOMPARE(color.blue(), 255); + QCOMPARE(color.green(), 255); +} QTEST_MAIN(tst_qquicktext) #include "tst_qquicktext.moc" diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp index 33a6b829bc..facd63027e 100644 --- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp +++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp @@ -2106,30 +2106,31 @@ void tst_qquicktextedit::mouseSelection() textEditObject->setFocus(focus); textEditObject->setFocusOnPress(focusOnPress); + // Avoid that the last click from the previous test data and the first click in the + // current test data happens so close in time that they are interpreted as a double click. + static const int moreThanDoubleClickInterval = QGuiApplication::styleHints()->mouseDoubleClickInterval() + 1; + // press-and-drag-and-release from x1 to x2 QPoint p1 = textEditObject->positionToRectangle(from).center().toPoint(); QPoint p2 = textEditObject->positionToRectangle(to).center().toPoint(); if (clicks == 2) - QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, p1); + QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, p1, moreThanDoubleClickInterval); else if (clicks == 3) - QTest::mouseDClick(&window, Qt::LeftButton, Qt::NoModifier, p1); + QTest::mouseDClick(&window, Qt::LeftButton, Qt::NoModifier, p1, moreThanDoubleClickInterval); + // cancel the 500ms delta QTestLib adds in order to properly synthesize a triple click within the required interval + QTest::lastMouseTimestamp -= QTest::mouseDoubleClickInterval; QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, p1); - if (clicks == 2) { - // QTBUG-50022: Since qtbase commit beef975, QTestLib avoids generating - // double click events by adding 500ms delta to release event timestamps. - // Send a double click event by hand to ensure the correct sequence: - // press, release, press, _dbl click_, move, release. - QMouseEvent dblClickEvent(QEvent::MouseButtonDblClick, p1, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); - QGuiApplication::sendEvent(textEditObject, &dblClickEvent); - } QTest::mouseMove(&window, p2); QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, p2); QTRY_COMPARE(textEditObject->selectedText(), selectedText); // Clicking and shift to clicking between the same points should select the same text. textEditObject->setCursorPosition(0); - if (clicks > 1) + if (clicks > 1) { QTest::mouseDClick(&window, Qt::LeftButton, Qt::NoModifier, p1); + // cancel the 500ms delta QTestLib adds in order to properly synthesize a triple click within the required interval + QTest::lastMouseTimestamp -= QTest::mouseDoubleClickInterval; + } if (clicks != 2) QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, p1); QTest::mouseClick(&window, Qt::LeftButton, Qt::ShiftModifier, p2); diff --git a/tests/auto/quick/qquicktextinput/BLACKLIST b/tests/auto/quick/qquicktextinput/BLACKLIST new file mode 100644 index 0000000000..ada7c57c75 --- /dev/null +++ b/tests/auto/quick/qquicktextinput/BLACKLIST @@ -0,0 +1,3 @@ +# QTBUG-78162 +[mouseSelectionMode] +opensuse-leap diff --git a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp index 32008f675a..fe56cad018 100644 --- a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp +++ b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp @@ -432,6 +432,7 @@ private slots: void asynchronousCancel(); void invalidContext(); void externalManagedModel(); + void delegateModelChangeDelegate(); private: template <int N> void groups_verify( @@ -4302,6 +4303,45 @@ void tst_qquickvisualdatamodel::externalManagedModel() QTRY_VERIFY(!object->property("running").toBool()); } +void tst_qquickvisualdatamodel::delegateModelChangeDelegate() +{ + // Verify that QTBUG-63477 is fixed. + // Changing the delegate would not update existing items. + QQmlEngine engine; + QScopedPointer<QQmlContext> context(new QQmlContext(engine.rootContext())); + + QQmlComponent c(&engine); + c.setData("import QtQml.Models 2.2\nDelegateModel {}\n", QUrl()); + QCOMPARE(c.status(), QQmlComponent::Ready); + + QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel*>(c.create(context.data())); + QVERIFY(visualModel); + visualModel->setModel(QVariant(3)); + + QQmlComponent first(&engine); + first.setData("import QtQuick 2.0\nItem { objectName: \"old\" }\n", QUrl()); + QCOMPARE(first.status(), QQmlComponent::Ready); + + // Without delegate, claim to have an item count of 0 + QCOMPARE(visualModel->count(), 0); + + visualModel->setDelegate(&first); + // The first delegate has been set, verify we get it + QObject* old = visualModel->object(0, QQmlIncubator::Synchronous); + QVERIFY(old); + QCOMPARE(visualModel->object(0, QQmlIncubator::Synchronous)->objectName(), QStringLiteral("old")); + QCOMPARE(visualModel->count(), 3); + + QQmlComponent second(&engine); + second.setData("import QtQuick 2.0\nItem { objectName: \"new\" }\n", QUrl()); + QCOMPARE(second.status(), QQmlComponent::Ready); + + visualModel->setDelegate(&second); + // After changing the delegate, expect the existing item to have the new delegate + QCOMPARE(visualModel->object(0, QQmlIncubator::Synchronous)->objectName(), QStringLiteral("new")); + QCOMPARE(visualModel->count(), 3); +} + QTEST_MAIN(tst_qquickvisualdatamodel) #include "tst_qquickvisualdatamodel.moc" diff --git a/tests/auto/quick/qquickwindow/data/shortcut.qml b/tests/auto/quick/qquickwindow/data/shortcut.qml new file mode 100644 index 0000000000..2632e27859 --- /dev/null +++ b/tests/auto/quick/qquickwindow/data/shortcut.qml @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** 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:GPL-EXCEPT$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.9 +import QtQuick.Window 2.2 + +Window { + id: root + visible: true + width: 200 + height: 200 + property bool received: false + Item { + focus: true + Shortcut { + sequence: "B" + onActivated: { + root.received = true + } + } + } +} + diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index f08b9207d1..7faa621e86 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -482,6 +482,10 @@ private slots: void testChildMouseEventFilter_data(); void cleanupGrabsOnRelease(); +#if QT_CONFIG(shortcut) + void testShortCut(); +#endif + private: QTouchDevice *touchDevice; QTouchDevice *touchDeviceWithVelocity; @@ -3579,6 +3583,30 @@ void tst_qquickwindow::cleanupGrabsOnRelease() QCOMPARE(parent->mouseUngrabEventCount, 1); } +#if QT_CONFIG(shortcut) +void tst_qquickwindow::testShortCut() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("shortcut.qml")); + + QObject *created = component.create(); + QScopedPointer<QObject> cleanup(created); + QVERIFY(created); + + QQuickWindow *window = qobject_cast<QQuickWindow *>(created); + QVERIFY(QTest::qWaitForWindowActive(window)); + + EventFilter eventFilter; + window->activeFocusItem()->installEventFilter(&eventFilter); + //Send non-spontaneous key press event + QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_B, Qt::NoModifier); + QCoreApplication::sendEvent(window, &keyEvent); + QVERIFY(eventFilter.events.contains(int(QEvent::ShortcutOverride))); + QVERIFY(window->property("received").value<bool>()); +} +#endif + QTEST_MAIN(tst_qquickwindow) #include "tst_qquickwindow.moc" |