diff options
author | Shawn Rutledge <shawn.rutledge@qt.io> | 2019-09-02 22:28:43 +0200 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2019-09-03 17:33:32 +0200 |
commit | e81ff5732f7884c5c1d9efb1f63ac7ad0dabbcb5 (patch) | |
tree | d62afc29c40181e9818f760142bcf31fd6368431 | |
parent | 45b1a3f97953fac65c6aef8e46abad865a0d0bc3 (diff) |
PathMultiline: handle directly-bound QVector<QPolygonF>
The autotest in 811b15bd161d12e5c85e093f9f492a0c4fa278d6 only tested
what happens if the vector of polygons is passed via a QVariant to the
PathMultiline paths property. But the intention (as documented) was to
literally support an object with Q_PROPERTY(QVector<QPolygonF> paths ...)
and binding that paths property to PathMultiline.paths. In that case
it appears in QQuickPathMultiline::setPaths() as a QVariant<QJSValue>,
canConvert<QVector<QPolygonF>>() returns false, then
canConvert<QVariantList>() returns true. Nevertheless each variant
in the QVariantList is a QPolygonF, as expected. So we need another
check to detect this case. Also added a test specifically for that.
Fixes: QTBUG-77929
Change-Id: I84d0a45326d5f007b8ba3cc9bb1fbccf0345d812
Reviewed-by: Paolo Angelelli <paolo.angelelli@qt.io>
-rw-r--r-- | src/quick/util/qquickpath.cpp | 20 | ||||
-rw-r--r-- | tests/auto/quick/qquickshape/data/multilineStronglyTyped.qml | 59 | ||||
-rw-r--r-- | tests/auto/quick/qquickshape/tst_qquickshape.cpp | 72 |
3 files changed, 144 insertions, 7 deletions
diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp index bebe9b2563..61319c388c 100644 --- a/src/quick/util/qquickpath.cpp +++ b/src/quick/util/qquickpath.cpp @@ -2566,14 +2566,20 @@ void QQuickPathMultiline::setPaths(const QVariant &paths) QVector<QVector<QPointF>> pathsList; QVariantList vll = paths.value<QVariantList>(); for (const QVariant &v : vll) { - QVariantList vl = v.value<QVariantList>(); - QVector<QPointF> l; - for (const QVariant &point : vl) { - if (point.canConvert<QPointF>()) - l.append(point.toPointF()); + // If we bind a QVector<QPolygonF> property directly, rather than via QVariant, + // it will come through as QJSValue that can be converted to QVariantList of QPolygonF. + if (v.canConvert<QPolygonF>()) { + pathsList.append(v.value<QPolygonF>()); + } else { + QVariantList vl = v.value<QVariantList>(); + QVector<QPointF> l; + for (const QVariant &point : vl) { + if (point.canConvert<QPointF>()) + l.append(point.toPointF()); + } + if (l.size() >= 2) + pathsList.append(l); } - if (l.size() >= 2) - pathsList.append(l); } setPaths(pathsList); } else { diff --git a/tests/auto/quick/qquickshape/data/multilineStronglyTyped.qml b/tests/auto/quick/qquickshape/data/multilineStronglyTyped.qml new file mode 100644 index 0000000000..46d650e053 --- /dev/null +++ b/tests/auto/quick/qquickshape/data/multilineStronglyTyped.qml @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** 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.14 +import QtQuick.Shapes 1.14 +import Qt.test 1.0 + +Shape { + width: 200 + height: 150 + vendorExtensionsEnabled: false + objectName: "shape" + id: shape + property alias paths: multiline.paths + property point vertexBeingChecked; + + function checkVertexAt(i, j) { + vertexBeingChecked = multiline.paths[i][j] + } + + PolygonProvider { + id: provider + objectName: "provider" + } + + ShapePath { + strokeWidth: 4 + strokeColor: "green" + PathMultiline { + id: multiline + paths: provider.paths + } + } +} diff --git a/tests/auto/quick/qquickshape/tst_qquickshape.cpp b/tests/auto/quick/qquickshape/tst_qquickshape.cpp index 6bd8562823..851475e2cd 100644 --- a/tests/auto/quick/qquickshape/tst_qquickshape.cpp +++ b/tests/auto/quick/qquickshape/tst_qquickshape.cpp @@ -43,6 +43,28 @@ using namespace QQuickViewTestUtil; using namespace QQuickVisualTestUtil; +class PolygonProvider : public QObject +{ + Q_OBJECT + Q_PROPERTY(QVector<QPolygonF> paths READ paths WRITE setPaths NOTIFY pathsChanged) + +public: + QVector<QPolygonF> paths() const { return m_paths; } + void setPaths(QVector<QPolygonF> paths) + { + if (m_paths == paths) + return; + m_paths = paths; + emit pathsChanged(); + } + +signals: + void pathsChanged(); + +private: + QVector<QPolygonF> m_paths; +}; + class tst_QQuickShape : public QQmlDataTest { Q_OBJECT @@ -64,6 +86,7 @@ private slots: void polylineDataTypes(); void multilineDataTypes_data(); void multilineDataTypes(); + void multilineStronglyTyped(); private: QVector<QPolygonF> m_lowPolyLogo; @@ -82,6 +105,7 @@ tst_QQuickShape::tst_QQuickShape() qmlRegisterType<QQuickShapeRadialGradient>(uri, 1, 0, "RadialGradient"); qmlRegisterType<QQuickShapeConicalGradient>(uri, 1, 0, "ConicalGradient"); qmlRegisterType<QQuickPathPolyline>(uri, 1, 0, "PathPolyline"); + qmlRegisterType<PolygonProvider>("Qt.test", 1, 0, "PolygonProvider"); m_lowPolyLogo << (QPolygonF() << QPointF(20, 0) << @@ -635,6 +659,54 @@ void tst_QQuickShape::multilineDataTypes() } } +void tst_QQuickShape::multilineStronglyTyped() +{ + QScopedPointer<QQuickView> window(createView()); + window->setSource(testFileUrl("multilineStronglyTyped.qml")); + QQuickShape *shape = qobject_cast<QQuickShape *>(window->rootObject()); + QVERIFY(shape); + PolygonProvider *provider = shape->findChild<PolygonProvider*>("provider"); + QVERIFY(provider); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + provider->setPaths(m_lowPolyLogo); + + if ((QGuiApplication::platformName() == QLatin1String("offscreen")) + || (QGuiApplication::platformName() == QLatin1String("minimal"))) + QEXPECT_FAIL("", "Failure due to grabWindow not functional on offscreen/minimimal platforms", Abort); + + QImage img = window->grabWindow(); + QVERIFY(!img.isNull()); + + QImage refImg(testFileUrl("multiline.png").toLocalFile()); + QVERIFY(!refImg.isNull()); + + QString errorMessage; + const QImage actualImg = img.convertToFormat(refImg.format()); + const bool res = QQuickVisualTestUtil::compareImages(actualImg, refImg, &errorMessage); + if (!res) { // For visual inspection purposes. + QTest::qWait(5000); + const QString &tempLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation); + actualImg.save(tempLocation + QLatin1String("/multilineStronglyTyped.png")); + } + QVERIFY2(res, qPrintable(errorMessage)); + + QVector<QVector<QPointF>> pointVectors; + for (auto v : m_lowPolyLogo) + pointVectors << v; + QCOMPARE(shape->property("paths").value<QVector<QVector<QPointF>>>(), pointVectors); + // Verify that QML sees it as an array of arrays of points + int i = 0; + for (auto pv : m_lowPolyLogo) { + int j = 0; + for (QPointF p : pv) { + QMetaObject::invokeMethod(shape, "checkVertexAt", Q_ARG(QVariant, QVariant::fromValue<int>(i)), Q_ARG(QVariant, QVariant::fromValue<int>(j++))); + QCOMPARE(shape->property("vertexBeingChecked").toPointF(), p); + } + ++i; + } +} + QTEST_MAIN(tst_QQuickShape) #include "tst_qquickshape.moc" |