diff options
-rw-r--r-- | src/quick/items/qquickitemsmodule.cpp | 1 | ||||
-rw-r--r-- | src/quick/util/qquickpath.cpp | 157 | ||||
-rw-r--r-- | src/quick/util/qquickpath_p.h | 25 | ||||
-rw-r--r-- | tests/auto/quick/qquickshape/data/pathitem8.png | bin | 0 -> 7323 bytes | |||
-rw-r--r-- | tests/auto/quick/qquickshape/data/pathitem8.qml | 88 | ||||
-rw-r--r-- | tests/auto/quick/qquickshape/tst_qquickshape.cpp | 30 | ||||
-rw-r--r-- | tests/manual/scenegraph_lancelot/data/shape/shape_multiline.qml | 74 |
7 files changed, 372 insertions, 3 deletions
diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 15a417a8b4..4168903205 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -228,6 +228,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType<QQuickPathSvg>("QtQuick",2,0,"PathSvg"); qmlRegisterType<QQuickPath, 14>(uri, 2, 14, "Path"); qmlRegisterType<QQuickPathPolyline>("QtQuick", 2, 14, "PathPolyline"); + qmlRegisterType<QQuickPathMultiline>("QtQuick", 2, 14, "PathMultiline"); #endif #if QT_CONFIG(quick_pathview) qmlRegisterType<QQuickPathView>(uri,major,minor,"PathView"); diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp index 840a8c6a2c..d246ee7910 100644 --- a/src/quick/util/qquickpath.cpp +++ b/src/quick/util/qquickpath.cpp @@ -109,6 +109,11 @@ QT_BEGIN_NAMESPACE \li Yes \li Yes \li Yes + \li PathMultiLine + \li Yes + \li Yes + \li Yes + \li Yes \row \li PathQuad \li Yes @@ -246,7 +251,8 @@ bool QQuickPath::isClosed() const A path can contain the following path objects: \list \li \l PathLine - a straight line to a given position. - \li \l PathPolyline - a polyline specified as a list of normalized coordinates. + \li \l PathPolyline - a polyline specified as a list of coordinates. + \li \l PathMultiline - a list of polylines specified as a list of lists of coordinates. \li \l PathQuad - a quadratic Bezier curve to a given position with a control point. \li \l PathCubic - a cubic Bezier curve to a given position with two control points. \li \l PathArc - an arc to a given position with a radius. @@ -2416,9 +2422,14 @@ void QQuickPathPolyline::setPath(const QVariantList &path) pathList.append(c); } - if (m_path != pathList) { + setPath(pathList); +} + +void QQuickPathPolyline::setPath(const QVector<QPointF> &path) +{ + if (m_path != path) { const QPointF &oldStart = start(); - m_path = pathList; + m_path = path; const QPointF &newStart = start(); emit pathChanged(); if (oldStart != newStart) @@ -2446,6 +2457,146 @@ void QQuickPathPolyline::addToPath(QPainterPath &path, const QQuickPathData &/*d path.lineTo(m_path.at(i)); } + +/*! + \qmltype PathMultiline + \instantiates QQuickPathMultiline + \inqmlmodule QtQuick + \ingroup qtquick-animation-paths + \brief Defines a set of polylines through a list of lists of coordinates. + \since QtQuick 2.14 + + This element allows to define a list of polylines at once. + Each polyline in the list will be preceded by a \l{QPainterPath::moveTo}{moveTo} + command, effectively making each polyline a separate one. + The polylines in this list are supposed to be non-intersecting with each other. + In any case, when used in conjunction with a \l ShapePath, the containing ShapePath's + \l ShapePath::fillRule applies. + That is, with the default \c OddEvenFill and non intersecting shapes, the largest shape in the list defines an area to be filled; + areas where two shapes overlap are holes; areas where three shapes overlap are filled areas inside holes, etc. + + The example below creates a high voltage symbol by adding each path + of the symbol to the list of paths. + The coordinates of the vertices are normalized, and through the containing shape's + \l scale property, the path will be rescaled together with its containing shape. + + \qml + PathMultiline { + paths: [ + [Qt.point(0.5, 0.06698), + Qt.point(1, 0.93301), + Qt.point(0, 0.93301), + Qt.point(0.5, 0.06698)], + + [Qt.point(0.5, 0.12472), + Qt.point(0.95, 0.90414), + Qt.point(0.05, 0.90414), + Qt.point(0.5, 0.12472)], + + [Qt.point(0.47131, 0.32986), + Qt.point(0.36229, 0.64789), + Qt.point(0.51492, 0.58590), + Qt.point(0.47563, 0.76014), + Qt.point(0.44950, 0.73590), + Qt.point(0.46292, 0.83392), + Qt.point(0.52162, 0.75190), + Qt.point(0.48531, 0.76230), + Qt.point(0.57529, 0.53189), + Qt.point(0.41261, 0.59189), + Qt.point(0.53001, 0.32786), + Qt.point(0.47131, 0.32986)] + ] + } + \endqml + + \sa Path, QPainterPath::setFillRule, PathPolyline, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove +*/ + +/*! + \qmlproperty point QtQuick::PathMultiline::start + + This read-only property contains the beginning of the polylines. +*/ + +/*! + \qmlproperty list<list<point>> QtQuick::PathMultiline::paths + + This property defines the vertices of the polylines. +*/ + +QQuickPathMultiline::QQuickPathMultiline(QObject *parent) : QQuickCurve(parent) +{ +} + +QVariantList QQuickPathMultiline::paths() const +{ + QVariantList res; + for (int j = 0; j < m_paths.length(); ++j) { + const QVector<QPointF> &path = m_paths.at(j); + QVariantList p; + for (int i = 0; i < path.length(); ++i) { + const QPointF &c = path.at(i); + p.append(QVariant::fromValue(c)); + } + res.append(p); + } + return res; +} + +void QQuickPathMultiline::setPaths(const QVariantList &paths) +{ + QVector<QVector<QPointF>> pathsList; + for (int j = 0; j < paths.length(); ++j) { + if (paths.at(j).type() != QVariant::List) + qWarning() << "QQuickPathMultiLine::setPaths: elements in argument not of type List"; + QVariantList path = paths.at(j).toList(); + QVector<QPointF> l; + for (int i = 0; i < path.length(); ++i) { + const QVariant &element = path.at(i); + const QVariant::Type elementType = element.type(); + if (elementType == QVariant::PointF || elementType == QVariant::Point) { + const QPointF c = element.toPointF(); + l.append(c); + } + } + if (l.size() >= 2) + pathsList.append(l); + } + + setPaths(pathsList); +} + +void QQuickPathMultiline::setPaths(const QVector<QVector<QPointF>> &paths) +{ + if (m_paths != paths) { + const QPointF &oldStart = start(); + m_paths = paths; + const QPointF &newStart = start(); + emit pathsChanged(); + if (oldStart != newStart) + emit startChanged(); + emit changed(); + } +} + +QPointF QQuickPathMultiline::start() const +{ + if (m_paths.size()) + return m_paths.first().first(); + return QPointF(); +} + +void QQuickPathMultiline::addToPath(QPainterPath &path, const QQuickPathData &) +{ + if (!m_paths.size()) + return; + for (const QVector<QPointF> &p: m_paths) { + path.moveTo(p.first()); + for (int i = 1; i < p.size(); ++i) + path.lineTo(p.at(i)); + } +} + QT_END_NAMESPACE #include "moc_qquickpath_p.cpp" diff --git a/src/quick/util/qquickpath_p.h b/src/quick/util/qquickpath_p.h index 998f4e3123..aa3425ff6c 100644 --- a/src/quick/util/qquickpath_p.h +++ b/src/quick/util/qquickpath_p.h @@ -434,6 +434,7 @@ public: QVariantList path() const; void setPath(const QVariantList &path); + void setPath(const QVector<QPointF> &path); QPointF start() const; void addToPath(QPainterPath &path, const QQuickPathData &data) override; @@ -445,6 +446,30 @@ private: QVector<QPointF> m_path; }; +class Q_QUICK_PRIVATE_EXPORT QQuickPathMultiline : public QQuickCurve +{ + Q_OBJECT + Q_PROPERTY(QPointF start READ start NOTIFY startChanged) + Q_PROPERTY(QVariantList paths READ paths WRITE setPaths NOTIFY pathsChanged) +public: + QQuickPathMultiline(QObject *parent=nullptr); + + QVariantList paths() const; + void setPaths(const QVariantList &paths); + void setPaths(const QVector<QVector<QPointF>> &paths); + QPointF start() const; + void addToPath(QPainterPath &path, const QQuickPathData &) override; + +Q_SIGNALS: + void pathsChanged(); + void startChanged(); + +private: + QPointF absolute(const QPointF &relative) const; + + QVector<QVector<QPointF>> m_paths; +}; + struct QQuickCachedBezier { QQuickCachedBezier() {} diff --git a/tests/auto/quick/qquickshape/data/pathitem8.png b/tests/auto/quick/qquickshape/data/pathitem8.png Binary files differnew file mode 100644 index 0000000000..ee585958ec --- /dev/null +++ b/tests/auto/quick/qquickshape/data/pathitem8.png diff --git a/tests/auto/quick/qquickshape/data/pathitem8.qml b/tests/auto/quick/qquickshape/data/pathitem8.qml new file mode 100644 index 0000000000..9789ff90e0 --- /dev/null +++ b/tests/auto/quick/qquickshape/data/pathitem8.qml @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** 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 + +Item { + id: item + width: 200 + height: 150 + + Shape { + vendorExtensionsEnabled: false + objectName: "shape" + id: shape + anchors.fill: parent + + ShapePath { + strokeWidth: 4 + strokeColor: "red" + scale: Qt.size(shape.width - 1, shape.height - 1) + fillGradient: LinearGradient { + x1: 20; y1: 20 + x2: 180; y2: 130 + GradientStop { position: 0; color: "blue" } + GradientStop { position: 0.2; color: "green" } + GradientStop { position: 0.4; color: "red" } + GradientStop { position: 0.6; color: "yellow" } + GradientStop { position: 1; color: "cyan" } + } + strokeStyle: ShapePath.DashLine + dashPattern: [ 1, 4 ] + PathMultiline { + paths: [[Qt.point(20.0 / (item.width - 1.0), 20.0 / (item.height - 1.0)), + Qt.point(180.0 / (item.width - 1.0), 130.0 / (item.height - 1.0)), + Qt.point(20.0 / (item.width - 1.0), 130.0 / (item.height - 1.0)), + Qt.point(20.0 / (item.width - 1.0), 20.0 / (item.height - 1.0)) ], + [Qt.point( 0.45 , 0.67 ), + Qt.point( 0.414906666468 , 0.573581858547 ), + Qt.point( 0.32604722665 , 0.522278837048 ), + Qt.point( 0.225 , 0.540096189432 ), + Qt.point( 0.159046106882 , 0.618696978501 ), + Qt.point( 0.159046106882 , 0.721303021499 ), + Qt.point( 0.225 , 0.799903810568 ), + Qt.point( 0.32604722665 , 0.817721162952 ), + Qt.point( 0.414906666468 , 0.766418141453 ), + Qt.point( 0.45 , 0.67 ), + ], + [Qt.point( 0.69 , 0.75 ), + Qt.point( 0.668943999881 , 0.692149115128 ), + Qt.point( 0.61562833599 , 0.661367302229 ), + Qt.point( 0.555 , 0.672057713659 ), + Qt.point( 0.515427664129 , 0.719218187101 ), + Qt.point( 0.515427664129 , 0.780781812899 ), + Qt.point( 0.555 , 0.827942286341 ), + Qt.point( 0.61562833599 , 0.838632697771 ), + Qt.point( 0.668943999881 , 0.807850884872 ), + Qt.point( 0.69 , 0.75 ), + ]] + } + } + } +} diff --git a/tests/auto/quick/qquickshape/tst_qquickshape.cpp b/tests/auto/quick/qquickshape/tst_qquickshape.cpp index 174ada65a5..b3b8d2d148 100644 --- a/tests/auto/quick/qquickshape/tst_qquickshape.cpp +++ b/tests/auto/quick/qquickshape/tst_qquickshape.cpp @@ -59,6 +59,7 @@ private slots: void radialGrad(); void conicalGrad(); void renderPolyline(); + void renderMultiline(); }; tst_QQuickShape::tst_QQuickShape() @@ -343,6 +344,35 @@ void tst_QQuickShape::renderPolyline() QVERIFY2(res, qPrintable(errorMessage)); } +void tst_QQuickShape::renderMultiline() +{ + QScopedPointer<QQuickView> window(createView()); + + window->setSource(testFileUrl("pathitem8.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + 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("pathitem8.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("/pathitem8.png")); + } + QVERIFY2(res, qPrintable(errorMessage)); +} + QTEST_MAIN(tst_QQuickShape) #include "tst_qquickshape.moc" diff --git a/tests/manual/scenegraph_lancelot/data/shape/shape_multiline.qml b/tests/manual/scenegraph_lancelot/data/shape/shape_multiline.qml new file mode 100644 index 0000000000..8524915bc4 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/shape/shape_multiline.qml @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** 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 + +Item { + id: root + width: 320 + height: 320 + + Shape { + vendorExtensionsEnabled: false + anchors.fill: parent + + ShapePath { + strokeWidth: 1 + strokeColor: "red" + fillColor: Qt.rgba(1,0,0,0.3) + scale: Qt.size(root.width - 1, root.height - 1) + PathMultiline { + paths: [ + [Qt.point(0.5, 0.06698), + Qt.point(1, 0.93301), + Qt.point(0, 0.93301), + Qt.point(0.5, 0.06698)], + + [Qt.point(0.5, 0.12472), + Qt.point(0.95, 0.90414), + Qt.point(0.05, 0.90414), + Qt.point(0.5, 0.12472)], + + [Qt.point(0.47131, 0.32986), + Qt.point(0.36229, 0.64789), + Qt.point(0.51492, 0.58590), + Qt.point(0.47563, 0.76014), + Qt.point(0.44950, 0.73590), + Qt.point(0.46292, 0.83392), + Qt.point(0.52162, 0.75190), + Qt.point(0.48531, 0.76230), + Qt.point(0.57529, 0.53189), + Qt.point(0.41261, 0.59189), + Qt.point(0.53001, 0.32786), + Qt.point(0.47131, 0.32986)] + ] + } + } + } +} |