diff options
author | Michael Brasser <mbrasser@ford.com> | 2017-09-20 15:15:06 -0500 |
---|---|---|
committer | Michael Brasser <michael.brasser@live.com> | 2017-11-07 16:29:16 +0000 |
commit | 7bedd55551fbe95355b0db11f9d576924e829f9d (patch) | |
tree | fdef6c4ec81b46161162abacefe350c87a373830 | |
parent | a10f154e3b46cc004ab6f7d5319f550c450987d4 (diff) |
Add new PathAngleArc type
This type allows working with arcs in different ways (based
on angles rather than start/end positions) that can be more
intuitive for certain use cases (such as a circular
progress indicator).
[ChangeLog][QtQuick][Path] Add new PathAngleArc type
Change-Id: Icbe5fc0450edd9a4d92f9a8d03438842b72a312d
Task-number: QTBUG-62684
Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
-rw-r--r-- | examples/quick/shapes/content/item11.qml | 14 | ||||
-rw-r--r-- | src/imports/shapes/qquickshapenvprrenderer.cpp | 25 | ||||
-rw-r--r-- | src/imports/shapes/shapes.pro | 2 | ||||
-rw-r--r-- | src/quick/items/qquickitemsmodule.cpp | 4 | ||||
-rw-r--r-- | src/quick/util/qquickpath.cpp | 199 | ||||
-rw-r--r-- | src/quick/util/qquickpath_p.h | 58 | ||||
-rw-r--r-- | tests/auto/quick/qquickpath/data/anglearc.qml | 12 | ||||
-rw-r--r-- | tests/auto/quick/qquickpath/tst_qquickpath.cpp | 40 |
8 files changed, 339 insertions, 15 deletions
diff --git a/examples/quick/shapes/content/item11.qml b/examples/quick/shapes/content/item11.qml index 0cfe73d5c9..bdd08339e3 100644 --- a/examples/quick/shapes/content/item11.qml +++ b/examples/quick/shapes/content/item11.qml @@ -48,7 +48,7 @@ ** ****************************************************************************/ -import QtQuick 2.9 +import QtQuick 2.11 import QtQuick.Shapes 1.0 Rectangle { @@ -100,11 +100,15 @@ Rectangle { strokeWidth: 20 capStyle: ShapePath.RoundCap - startX: 20; startY: 50 - PathArc { - x: 20; y: 90 + PathAngleArc { + centerX: 65; centerY: 95 radiusX: 45; radiusY: 45 - useLargeArc: true + startAngle: -180 + SequentialAnimation on sweepAngle { + loops: Animation.Infinite + NumberAnimation { to: 360; duration: 2000 } + NumberAnimation { to: 0; duration: 2000 } + } } } } diff --git a/src/imports/shapes/qquickshapenvprrenderer.cpp b/src/imports/shapes/qquickshapenvprrenderer.cpp index c0a918bda5..88f367fe70 100644 --- a/src/imports/shapes/qquickshapenvprrenderer.cpp +++ b/src/imports/shapes/qquickshapenvprrenderer.cpp @@ -43,6 +43,7 @@ #include <QOpenGLShaderProgram> #include <QOpenGLBuffer> #include <qmath.h> +#include <private/qpainterpath_p.h> #include <private/qquickpath_p_p.h> QT_BEGIN_NAMESPACE @@ -287,6 +288,30 @@ void QQuickShapeNvprRenderer::convertPath(const QQuickPath *path, ShapePathGuiDa if (d->path.str.isEmpty()) d->path.str = QString(QStringLiteral("M %1 %2 ")).arg(pos.x()).arg(pos.y()).toUtf8(); d->path.str.append(o->path().toUtf8()); + } else if (QQuickPathAngleArc *o = qobject_cast<QQuickPathAngleArc *>(e)) { + QRectF rect(o->centerX() - o->radiusX(), o->centerY() - o->radiusY(), o->radiusX() * 2, o->radiusY() * 2); + QPointF startPoint; + QPointF endPoint; + qt_find_ellipse_coords(rect, o->startAngle(), -o->sweepAngle(), &startPoint, &endPoint); + + // get to our starting position + if (o->moveToStart()) + d->path.cmd.append(GL_MOVE_TO_NV); + else + d->path.cmd.append(GL_LINE_TO_NV); // ### should we check if startPoint == pos? + d->path.coord.append(startPoint.x()); + d->path.coord.append(startPoint.y()); + + const bool sweepFlag = o->sweepAngle() > 0; // maps to CCW, not a typo + d->path.cmd.append(qAbs(o->sweepAngle()) > 180.0 + ? (sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV) + : (sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV)); + d->path.coord.append(o->radiusX()); + d->path.coord.append(o->radiusY()); + d->path.coord.append(0); // xAxisRotation + d->path.coord.append(endPoint.x()); + d->path.coord.append(endPoint.y()); + pos = endPoint; } else { qWarning() << "Shape/NVPR: unsupported Path element" << e; } diff --git a/src/imports/shapes/shapes.pro b/src/imports/shapes/shapes.pro index 4406474c3f..60cc61e974 100644 --- a/src/imports/shapes/shapes.pro +++ b/src/imports/shapes/shapes.pro @@ -3,7 +3,7 @@ TARGET = qmlshapesplugin TARGETPATH = QtQuick/Shapes IMPORT_VERSION = 1.0 -QT = core gui qml quick quick-private +QT = core gui-private qml quick-private HEADERS += \ qquickshape_p.h \ diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 3c87496c83..869fdeadc8 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -402,6 +402,10 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType<QQuickFlickable, 10>(uri, 2, 10, "Flickable"); qmlRegisterType<QQuickTextEdit, 10>(uri, 2, 10, "TextEdit"); qmlRegisterType<QQuickText, 10>(uri, 2, 10, "Text"); + +#if QT_CONFIG(quick_path) + qmlRegisterType<QQuickPathAngleArc, 2>(uri, 2, 11, "PathAngleArc"); +#endif } static void initResources() diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp index b19eec6fb3..0323dc9286 100644 --- a/src/quick/util/qquickpath.cpp +++ b/src/quick/util/qquickpath.cpp @@ -60,7 +60,8 @@ QT_BEGIN_NAMESPACE This type is the base for all path types. It cannot be instantiated. - \sa Path, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic, PathArc, PathCurve, PathSvg + \sa Path, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic, PathArc, + PathAngleArc, PathCurve, PathSvg */ /*! @@ -71,7 +72,7 @@ QT_BEGIN_NAMESPACE \brief Defines a path for use by \l PathView and \l Shape A Path is composed of one or more path segments - PathLine, PathQuad, - PathCubic, PathArc, PathCurve, PathSvg. + PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg. The spacing of the items along the Path can be adjusted via a PathPercent object. @@ -121,6 +122,12 @@ QT_BEGIN_NAMESPACE \li Yes \li Yes \row + \li PathAngleArc + \li Yes + \li Yes + \li Yes + \li Yes + \row \li PathSvg \li Yes \li Yes @@ -149,7 +156,7 @@ QT_BEGIN_NAMESPACE \note Path is a non-visual type; it does not display anything on its own. To draw a path, use \l Shape. - \sa PathView, Shape, PathAttribute, PathPercent, PathLine, PathMove, PathQuad, PathCubic, PathArc, PathCurve, PathSvg + \sa PathView, Shape, PathAttribute, PathPercent, PathLine, PathMove, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg */ QQuickPath::QQuickPath(QObject *parent) : QObject(*(new QQuickPathPrivate), parent) @@ -236,6 +243,7 @@ bool QQuickPath::isClosed() const \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. + \li \l PathAngleArc - an arc specified by center point, radii, and angles. \li \l PathSvg - a path specified as an SVG path data string. \li \l PathCurve - a point on a Catmull-Rom curve. \li \l PathAttribute - an attribute at a given position in the path. @@ -1078,7 +1086,7 @@ void QQuickPathAttribute::setValue(qreal value) } \endqml - \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg, PathMove + \sa Path, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove */ /*! @@ -1144,7 +1152,7 @@ void QQuickPathLine::addToPath(QPainterPath &path, const QQuickPathData &data) between the operations of drawing a straight line and moving the path position without drawing anything. - \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg, PathLine + \sa Path, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathLine */ /*! @@ -1198,7 +1206,7 @@ void QQuickPathMove::addToPath(QPainterPath &path, const QQuickPathData &data) \endqml \endtable - \sa Path, PathCubic, PathLine, PathArc, PathCurve, PathSvg + \sa Path, PathCubic, PathLine, PathArc, PathAngleArc, PathCurve, PathSvg */ /*! @@ -1354,7 +1362,7 @@ void QQuickPathQuad::addToPath(QPainterPath &path, const QQuickPathData &data) \endqml \endtable - \sa Path, PathQuad, PathLine, PathArc, PathCurve, PathSvg + \sa Path, PathQuad, PathLine, PathArc, PathAngleArc, PathCurve, PathSvg */ /*! @@ -1720,7 +1728,7 @@ void QQuickPathCatmullRomCurve::addToPath(QPainterPath &path, const QQuickPathDa Note that a single PathArc cannot be used to specify a circle. Instead, you can use two PathArc elements, each specifying half of the circle. - \sa Path, PathLine, PathQuad, PathCubic, PathCurve, PathSvg + \sa Path, PathLine, PathQuad, PathCubic, PathAngleArc, PathCurve, PathSvg */ /*! @@ -1912,6 +1920,179 @@ void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data) /****************************************************************************/ /*! + \qmltype PathAngleArc + \instantiates QQuickPathAngleArc + \inqmlmodule QtQuick + \ingroup qtquick-animation-paths + \brief Defines an arc with the given radii and center + + PathAngleArc provides a simple way of specifying an arc. While PathArc is designed + to work as part of a larger path (specifying start and end), PathAngleArc is designed + to make a path where the arc is primary (such as a circular progress indicator) more intuitive. + + \sa Path, PathLine, PathQuad, PathCubic, PathCurve, PathSvg, PathArc +*/ + +/*! + \qmlproperty real QtQuick::PathAngleArc::centerX + \qmlproperty real QtQuick::PathAngleArc::centerY + + Defines the center of the arc. +*/ + +qreal QQuickPathAngleArc::centerX() const +{ + return _centerX; +} + +void QQuickPathAngleArc::setCenterX(qreal centerX) +{ + if (_centerX == centerX) + return; + + _centerX = centerX; + emit centerXChanged(); + emit changed(); +} + +qreal QQuickPathAngleArc::centerY() const +{ + return _centerY; +} + +void QQuickPathAngleArc::setCenterY(qreal centerY) +{ + if (_centerY == centerY) + return; + + _centerY = centerY; + emit centerXChanged(); + emit changed(); +} + +/*! + \qmlproperty real QtQuick::PathAngleArc::radiusX + \qmlproperty real QtQuick::PathAngleArc::radiusY + + Defines the radii of the ellipse of which the arc is part. +*/ + +qreal QQuickPathAngleArc::radiusX() const +{ + return _radiusX; +} + +void QQuickPathAngleArc::setRadiusX(qreal radius) +{ + if (_radiusX == radius) + return; + + _radiusX = radius; + emit radiusXChanged(); + emit changed(); +} + +qreal QQuickPathAngleArc::radiusY() const +{ + return _radiusY; +} + +void QQuickPathAngleArc::setRadiusY(qreal radius) +{ + if (_radiusY == radius) + return; + + _radiusY = radius; + emit radiusYChanged(); + emit changed(); +} + +/*! + \qmlproperty real QtQuick::PathAngleArc::startAngle + + Defines the start angle of the arc. + + The start angle is reported clockwise, with zero degrees at the 3 o'clock position. +*/ + +qreal QQuickPathAngleArc::startAngle() const +{ + return _startAngle; +} + +void QQuickPathAngleArc::setStartAngle(qreal angle) +{ + if (_startAngle == angle) + return; + + _startAngle = angle; + emit startAngleChanged(); + emit changed(); +} + +/*! + \qmlproperty real QtQuick::PathAngleArc::sweepAngle + + Defines the sweep angle of the arc. + + The arc will begin at startAngle and continue sweepAngle degrees, with a value of 360 + resulting in a full circle. Positive numbers are clockwise and negative numbers are counterclockwise. +*/ + +qreal QQuickPathAngleArc::sweepAngle() const +{ + return _sweepAngle; +} + +void QQuickPathAngleArc::setSweepAngle(qreal angle) +{ + if (_sweepAngle == angle) + return; + + _sweepAngle = angle; + emit sweepAngleChanged(); + emit changed(); +} + +/*! + \qmlproperty bool QtQuick::PathAngleArc::moveToStart + + Whether this element should be disconnected from the previous Path element (or startX/Y). + + The default value is true. If set to false, the previous element's end-point + (or startX/Y if PathAngleArc is the first element) will be connected to the arc's + start-point with a straight line. +*/ + +bool QQuickPathAngleArc::moveToStart() const +{ + return _moveToStart; +} + +void QQuickPathAngleArc::setMoveToStart(bool move) +{ + if (_moveToStart == move) + return; + + _moveToStart = move; + emit moveToStartChanged(); + emit changed(); +} + +void QQuickPathAngleArc::addToPath(QPainterPath &path, const QQuickPathData &) +{ + qreal x = _centerX - _radiusX; + qreal y = _centerY - _radiusY; + qreal width = _radiusX * 2; + qreal height = _radiusY * 2; + if (_moveToStart) + path.arcMoveTo(x, y, width, height, -_startAngle); + path.arcTo(x, y, width, height, -_startAngle, -_sweepAngle); +} + +/****************************************************************************/ + +/*! \qmltype PathSvg \instantiates QQuickPathSvg \inqmlmodule QtQuick @@ -1936,7 +2117,7 @@ void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data) ShapePath can contain one or more PathSvg elements, or one or more other type of elements, but not both. - \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathCurve + \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve */ /*! diff --git a/src/quick/util/qquickpath_p.h b/src/quick/util/qquickpath_p.h index b7fde5c272..d5474e2d30 100644 --- a/src/quick/util/qquickpath_p.h +++ b/src/quick/util/qquickpath_p.h @@ -331,6 +331,63 @@ private: qreal _xAxisRotation; }; +class Q_QUICK_PRIVATE_EXPORT QQuickPathAngleArc : public QQuickCurve +{ + Q_OBJECT + Q_PROPERTY(qreal centerX READ centerX WRITE setCenterX NOTIFY centerXChanged) + Q_PROPERTY(qreal centerY READ centerY WRITE setCenterY NOTIFY centerYChanged) + Q_PROPERTY(qreal radiusX READ radiusX WRITE setRadiusX NOTIFY radiusXChanged) + Q_PROPERTY(qreal radiusY READ radiusY WRITE setRadiusY NOTIFY radiusYChanged) + Q_PROPERTY(qreal startAngle READ startAngle WRITE setStartAngle NOTIFY startAngleChanged) + Q_PROPERTY(qreal sweepAngle READ sweepAngle WRITE setSweepAngle NOTIFY sweepAngleChanged) + Q_PROPERTY(bool moveToStart READ moveToStart WRITE setMoveToStart NOTIFY moveToStartChanged) + +public: + QQuickPathAngleArc(QObject *parent=0) + : QQuickCurve(parent), _centerX(0), _centerY(0), _radiusX(0), _radiusY(0), _startAngle(0), _sweepAngle(0), _moveToStart(true) {} + + qreal centerX() const; + void setCenterX(qreal); + + qreal centerY() const; + void setCenterY(qreal); + + qreal radiusX() const; + void setRadiusX(qreal); + + qreal radiusY() const; + void setRadiusY(qreal); + + qreal startAngle() const; + void setStartAngle(qreal); + + qreal sweepAngle() const; + void setSweepAngle(qreal); + + bool moveToStart() const; + void setMoveToStart(bool); + + void addToPath(QPainterPath &path, const QQuickPathData &) override; + +Q_SIGNALS: + void centerXChanged(); + void centerYChanged(); + void radiusXChanged(); + void radiusYChanged(); + void startAngleChanged(); + void sweepAngleChanged(); + void moveToStartChanged(); + +private: + qreal _centerX; + qreal _centerY; + qreal _radiusX; + qreal _radiusY; + qreal _startAngle; + qreal _sweepAngle; + bool _moveToStart; +}; + class Q_QUICK_PRIVATE_EXPORT QQuickPathSvg : public QQuickCurve { Q_OBJECT @@ -479,6 +536,7 @@ QML_DECLARE_TYPE(QQuickPathQuad) QML_DECLARE_TYPE(QQuickPathCubic) QML_DECLARE_TYPE(QQuickPathCatmullRomCurve) QML_DECLARE_TYPE(QQuickPathArc) +QML_DECLARE_TYPE(QQuickPathAngleArc) QML_DECLARE_TYPE(QQuickPathSvg) QML_DECLARE_TYPE(QQuickPathPercent) QML_DECLARE_TYPE(QQuickPath) diff --git a/tests/auto/quick/qquickpath/data/anglearc.qml b/tests/auto/quick/qquickpath/data/anglearc.qml new file mode 100644 index 0000000000..cbe41c1ac8 --- /dev/null +++ b/tests/auto/quick/qquickpath/data/anglearc.qml @@ -0,0 +1,12 @@ +import QtQuick 2.11 + +Path { + PathAngleArc { + centerX: 100 + centerY: 100 + radiusX: 50 + radiusY: 50 + startAngle: 45 + sweepAngle: 90 + } +} diff --git a/tests/auto/quick/qquickpath/tst_qquickpath.cpp b/tests/auto/quick/qquickpath/tst_qquickpath.cpp index 2ec95840e1..106d7afd85 100644 --- a/tests/auto/quick/qquickpath/tst_qquickpath.cpp +++ b/tests/auto/quick/qquickpath/tst_qquickpath.cpp @@ -41,6 +41,7 @@ public: private slots: void arc(); + void angleArc(); void catmullromCurve(); void closedCatmullromCurve(); void svg(); @@ -82,6 +83,45 @@ void tst_QuickPath::arc() QCOMPARE(pos, QPointF(100,100)); } +void tst_QuickPath::angleArc() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("anglearc.qml")); + QQuickPath *obj = qobject_cast<QQuickPath*>(c.create()); + QVERIFY(obj != 0); + + QQmlListReference list(obj, "pathElements"); + QCOMPARE(list.count(), 1); + + QQuickPathAngleArc* arc = qobject_cast<QQuickPathAngleArc*>(list.at(0)); + QVERIFY(arc != 0); + QCOMPARE(arc->centerX(), 100.); + QCOMPARE(arc->centerY(), 100.); + QCOMPARE(arc->radiusX(), 50.); + QCOMPARE(arc->radiusY(), 50.); + QCOMPARE(arc->startAngle(), 45.); + QCOMPARE(arc->sweepAngle(), 90.); + QCOMPARE(arc->moveToStart(), true); + + QPainterPath path = obj->path(); + QVERIFY(path != QPainterPath()); + + // using QPoint to do fuzzy compare + QPointF pos = obj->pointAt(0); + QCOMPARE(pos.toPoint(), QPoint(135,135)); + pos = obj->pointAt(.25); + QCOMPARE(pos.toPoint(), QPoint(119,146)); + pos = obj->pointAt(.75); + QCOMPARE(pos.toPoint(), QPoint(81,146)); + pos = obj->pointAt(1); + QCOMPARE(pos.toPoint(), QPoint(65,135)); + + // if moveToStart is false, we should have a line starting from startX/Y + arc->setMoveToStart(false); + pos = obj->pointAt(0); + QCOMPARE(pos, QPointF(0,0)); +} + void tst_QuickPath::catmullromCurve() { QQmlEngine engine; |