aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@qt.io>2019-09-02 22:28:43 +0200
committerShawn Rutledge <shawn.rutledge@qt.io>2019-09-03 17:33:32 +0200
commite81ff5732f7884c5c1d9efb1f63ac7ad0dabbcb5 (patch)
treed62afc29c40181e9818f760142bcf31fd6368431
parent45b1a3f97953fac65c6aef8e46abad865a0d0bc3 (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.cpp20
-rw-r--r--tests/auto/quick/qquickshape/data/multilineStronglyTyped.qml59
-rw-r--r--tests/auto/quick/qquickshape/tst_qquickshape.cpp72
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"