aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/util/qquickpath.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/util/qquickpath.cpp')
-rw-r--r--src/quick/util/qquickpath.cpp1047
1 files changed, 936 insertions, 111 deletions
diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp
index 60d725d650..c33111039f 100644
--- a/src/quick/util/qquickpath.cpp
+++ b/src/quick/util/qquickpath.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 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$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickpath_p.h"
#include "qquickpath_p_p.h"
@@ -60,8 +24,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,
- PathAngleArc, PathCurve, PathSvg
+ \sa Path, PathAttribute, PathPercent, PathLine, PathPolyline, PathQuad, PathCubic, PathArc,
+ PathAngleArc, PathCurve, PathSvg, PathRectangle
*/
/*!
@@ -71,7 +35,7 @@ QT_BEGIN_NAMESPACE
\ingroup qtquick-animation-paths
\brief Defines a path for use by \l PathView and \l Shape.
- A Path is composed of one or more path segments - PathLine, PathQuad,
+ A Path is composed of one or more path segments - PathLine, PathPolyline, PathQuad,
PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg.
The spacing of the items along the Path can be adjusted via a
@@ -89,74 +53,78 @@ QT_BEGIN_NAMESPACE
\li Element
\li PathView
\li Shape
- \li Shape, GL_NV_path_rendering
\li Shape, software
\row
\li PathMove
\li N/A
\li Yes
\li Yes
- \li Yes
\row
\li PathLine
\li Yes
\li Yes
\li Yes
+ \row
+ \li PathPolyline
+ \li Yes
+ \li Yes
\li Yes
\row
- \li PathQuad
+ \li PathMultiline
\li Yes
\li Yes
\li Yes
+ \row
+ \li PathQuad
+ \li Yes
+ \li Yes
\li Yes
\row
\li PathCubic
\li Yes
\li Yes
\li Yes
- \li Yes
\row
\li PathArc
\li Yes
\li Yes
\li Yes
- \li Yes
\row
\li PathAngleArc
\li Yes
\li Yes
\li Yes
- \li Yes
\row
\li PathSvg
\li Yes
\li Yes
\li Yes
+ \row
+ \li PathRectangle
+ \li Yes
+ \li Yes
\li Yes
\row
\li PathAttribute
\li Yes
\li N/A
\li N/A
- \li N/A
\row
\li PathPercent
\li Yes
\li N/A
\li N/A
- \li N/A
\row
\li PathCurve
\li Yes
\li No
\li No
- \li No
\endtable
\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, PathAngleArc, PathCurve, PathSvg
+ \sa PathView, Shape, PathAttribute, PathPercent, PathLine, PathPolyline, PathMove, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathRectangle
*/
QQuickPath::QQuickPath(QObject *parent)
: QObject(*(new QQuickPathPrivate), parent)
@@ -180,7 +148,7 @@ QQuickPath::~QQuickPath()
qreal QQuickPath::startX() const
{
Q_D(const QQuickPath);
- return d->startX.isNull ? 0 : d->startX.value;
+ return d->startX.isValid() ? d->startX.value() : 0;
}
void QQuickPath::setStartX(qreal x)
@@ -202,7 +170,7 @@ bool QQuickPath::hasStartX() const
qreal QQuickPath::startY() const
{
Q_D(const QQuickPath);
- return d->startY.isNull ? 0 : d->startY.value;
+ return d->startY.isValid() ? d->startY.value() : 0;
}
void QQuickPath::setStartY(qreal y)
@@ -235,16 +203,19 @@ bool QQuickPath::isClosed() const
\qmlproperty list<PathElement> QtQuick::Path::pathElements
This property holds the objects composing the path.
- \default
+ \qmldefault
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 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.
\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 PathRectangle - a rectangle with a given position and size
\li \l PathCurve - a point on a Catmull-Rom curve.
\li \l PathAttribute - an attribute at a given position in the path.
\li \l PathPercent - a way to spread out items along various segments of the path.
@@ -270,7 +241,7 @@ static QQuickPathPrivate *privatePath(QObject *object)
return QQuickPathPrivate::get(path);
}
-QQuickPathElement *QQuickPath::pathElements_at(QQmlListProperty<QQuickPathElement> *property, int index)
+QQuickPathElement *QQuickPath::pathElements_at(QQmlListProperty<QQuickPathElement> *property, qsizetype index)
{
QQuickPathPrivate *d = privatePath(property->object);
@@ -288,6 +259,8 @@ void QQuickPath::pathElements_append(QQmlListProperty<QQuickPathElement> *proper
QQuickCurve *curve = qobject_cast<QQuickCurve *>(pathElement);
if (curve)
d->_pathCurves.append(curve);
+ else if (QQuickPathText *text = qobject_cast<QQuickPathText *>(pathElement))
+ d->_pathTexts.append(text);
else {
QQuickPathAttribute *attribute = qobject_cast<QQuickPathAttribute *>(pathElement);
if (attribute && !d->_attributes.contains(attribute->name()))
@@ -300,11 +273,11 @@ void QQuickPath::pathElements_append(QQmlListProperty<QQuickPathElement> *proper
}
}
-int QQuickPath::pathElements_count(QQmlListProperty<QQuickPathElement> *property)
+qsizetype QQuickPath::pathElements_count(QQmlListProperty<QQuickPathElement> *property)
{
QQuickPathPrivate *d = privatePath(property->object);
- return d->_pathElements.count();
+ return d->_pathElements.size();
}
void QQuickPath::pathElements_clear(QQmlListProperty<QQuickPathElement> *property)
@@ -316,6 +289,7 @@ void QQuickPath::pathElements_clear(QQmlListProperty<QQuickPathElement> *propert
d->_pathElements.clear();
d->_pathCurves.clear();
d->_pointCache.clear();
+ d->_pathTexts.clear();
}
void QQuickPath::interpolate(int idx, const QString &name, qreal value)
@@ -359,10 +333,10 @@ void QQuickPath::endpoint(const QString &name)
Q_D(QQuickPath);
const AttributePoint &first = d->_attributePoints.first();
qreal val = first.values.value(name);
- for (int ii = d->_attributePoints.count() - 1; ii >= 0; ii--) {
+ for (int ii = d->_attributePoints.size() - 1; ii >= 0; ii--) {
const AttributePoint &point = d->_attributePoints.at(ii);
if (point.values.contains(name)) {
- for (int jj = ii + 1; jj < d->_attributePoints.count(); ++jj) {
+ for (int jj = ii + 1; jj < d->_attributePoints.size(); ++jj) {
AttributePoint &setPoint = d->_attributePoints[jj];
setPoint.values.insert(name, val);
}
@@ -375,10 +349,10 @@ void QQuickPath::endpoint(QList<AttributePoint> &attributePoints, const QString
{
const AttributePoint &first = attributePoints.first();
qreal val = first.values.value(name);
- for (int ii = attributePoints.count() - 1; ii >= 0; ii--) {
+ for (int ii = attributePoints.size() - 1; ii >= 0; ii--) {
const AttributePoint &point = attributePoints.at(ii);
if (point.values.contains(name)) {
- for (int jj = ii + 1; jj < attributePoints.count(); ++jj) {
+ for (int jj = ii + 1; jj < attributePoints.size(); ++jj) {
AttributePoint &setPoint = attributePoints[jj];
setPoint.values.insert(name, val);
}
@@ -404,9 +378,25 @@ void QQuickPath::processPath()
d->_path = createPath(QPointF(), QPointF(), d->_attributes, d->pathLength, d->_attributePoints, &d->closed);
}
+ if (d->simplify)
+ d->_path = d->_path.simplified();
+
emit changed();
}
+inline static void scalePath(QPainterPath &path, const QSizeF &scale)
+{
+ const qreal xscale = scale.width();
+ const qreal yscale = scale.height();
+ if (xscale == 1 && yscale == 1)
+ return;
+
+ for (int i = 0; i < path.elementCount(); ++i) {
+ const QPainterPath::Element &element = path.elementAt(i);
+ path.setElementPositionAt(i, element.x * xscale, element.y * yscale);
+ }
+}
+
QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &endPoint, const QStringList &attributes, qreal &pathLength, QList<AttributePoint> &attributePoints, bool *closed)
{
Q_D(QQuickPath);
@@ -420,19 +410,19 @@ QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &en
QPainterPath path;
AttributePoint first;
- for (int ii = 0; ii < attributes.count(); ++ii)
+ for (int ii = 0; ii < attributes.size(); ++ii)
first.values[attributes.at(ii)] = 0;
attributePoints << first;
- qreal startX = d->startX.isValid() ? d->startX.value : startPoint.x();
- qreal startY = d->startY.isValid() ? d->startY.value : startPoint.y();
+ qreal startX = d->startX.isValid() ? d->startX.value() : startPoint.x();
+ qreal startY = d->startY.isValid() ? d->startY.value() : startPoint.y();
path.moveTo(startX, startY);
const QString percentString = QStringLiteral("_qfx_percent");
bool usesPercent = false;
int index = 0;
- for (QQuickPathElement *pathElement : qAsConst(d->_pathElements)) {
+ for (QQuickPathElement *pathElement : std::as_const(d->_pathElements)) {
if (QQuickCurve *curve = qobject_cast<QQuickCurve *>(pathElement)) {
QQuickPathData data;
data.index = index;
@@ -446,32 +436,34 @@ QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &en
} else if (QQuickPathAttribute *attribute = qobject_cast<QQuickPathAttribute *>(pathElement)) {
AttributePoint &point = attributePoints.last();
point.values[attribute->name()] = attribute->value();
- interpolate(attributePoints, attributePoints.count() - 1, attribute->name(), attribute->value());
+ interpolate(attributePoints, attributePoints.size() - 1, attribute->name(), attribute->value());
} else if (QQuickPathPercent *percent = qobject_cast<QQuickPathPercent *>(pathElement)) {
AttributePoint &point = attributePoints.last();
point.values[percentString] = percent->value();
- interpolate(attributePoints, attributePoints.count() - 1, percentString, percent->value());
+ interpolate(attributePoints, attributePoints.size() - 1, percentString, percent->value());
usesPercent = true;
+ } else if (QQuickPathText *text = qobject_cast<QQuickPathText *>(pathElement)) {
+ text->addToPath(path);
}
}
// Fixup end points
const AttributePoint &last = attributePoints.constLast();
- for (int ii = 0; ii < attributes.count(); ++ii) {
+ for (int ii = 0; ii < attributes.size(); ++ii) {
if (!last.values.contains(attributes.at(ii)))
endpoint(attributePoints, attributes.at(ii));
}
if (usesPercent && !last.values.contains(percentString)) {
d->_attributePoints.last().values[percentString] = 1;
- interpolate(d->_attributePoints.count() - 1, percentString, 1);
+ interpolate(d->_attributePoints.size() - 1, percentString, 1);
}
-
+ scalePath(path, d->scale);
// Adjust percent
qreal length = path.length();
qreal prevpercent = 0;
qreal prevorigpercent = 0;
- for (int ii = 0; ii < attributePoints.count(); ++ii) {
+ for (int ii = 0; ii < attributePoints.size(); ++ii) {
const AttributePoint &point = attributePoints.at(ii);
if (point.values.contains(percentString)) { //special string for QQuickPathPercent
if ( ii > 0) {
@@ -491,7 +483,7 @@ QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &en
if (closed) {
QPointF end = path.currentPosition();
- *closed = length > 0 && startX == end.x() && startY == end.y();
+ *closed = length > 0 && startX * d->scale.width() == end.x() && startY * d->scale.height() == end.y();
}
pathLength = length;
@@ -507,12 +499,12 @@ QPainterPath QQuickPath::createShapePath(const QPointF &startPoint, const QPoint
QPainterPath path;
- qreal startX = d->startX.isValid() ? d->startX.value : startPoint.x();
- qreal startY = d->startY.isValid() ? d->startY.value : startPoint.y();
+ qreal startX = d->startX.isValid() ? d->startX.value() : startPoint.x();
+ qreal startY = d->startY.isValid() ? d->startY.value() : startPoint.y();
path.moveTo(startX, startY);
int index = 0;
- for (QQuickCurve *curve : qAsConst(d->_pathCurves)) {
+ for (QQuickCurve *curve : std::as_const(d->_pathCurves)) {
QQuickPathData data;
data.index = index;
data.endPoint = endPoint;
@@ -521,10 +513,14 @@ QPainterPath QQuickPath::createShapePath(const QPointF &startPoint, const QPoint
++index;
}
+ for (QQuickPathText *text : std::as_const(d->_pathTexts))
+ text->addToPath(path);
+
if (closed) {
QPointF end = path.currentPosition();
*closed = startX == end.x() && startY == end.y();
}
+ scalePath(path, d->scale);
// Note: Length of paths inside ShapePath is not used, so currently
// length is always 0. This avoids potentially heavy path.length()
@@ -563,14 +559,16 @@ void QQuickPath::gatherAttributes()
QSet<QString> attributes;
// First gather up all the attributes
- for (QQuickPathElement *pathElement : qAsConst(d->_pathElements)) {
+ for (QQuickPathElement *pathElement : std::as_const(d->_pathElements)) {
if (QQuickCurve *curve = qobject_cast<QQuickCurve *>(pathElement))
d->_pathCurves.append(curve);
+ else if (QQuickPathText *text = qobject_cast<QQuickPathText *>(pathElement))
+ d->_pathTexts.append(text);
else if (QQuickPathAttribute *attribute = qobject_cast<QQuickPathAttribute *>(pathElement))
attributes.insert(attribute->name());
}
- d->_attributes = attributes.toList();
+ d->_attributes = attributes.values();
}
void QQuickPath::componentComplete()
@@ -603,7 +601,7 @@ QStringList QQuickPath::attributes() const
qobject_cast<QQuickPathAttribute *>(pathElement))
attrs.insert(attribute->name());
}
- return attrs.toList();
+ return attrs.values();
}
return d->_attributes;
}
@@ -687,10 +685,10 @@ void QQuickPath::createPointCache() const
//find which set we are in
qreal prevPercent = 0;
qreal prevOrigPercent = 0;
- for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
+ for (int ii = 0; ii < d->_attributePoints.size(); ++ii) {
qreal percent = qreal(i)/segments;
const AttributePoint &point = d->_attributePoints.at(ii);
- if (percent < point.percent || ii == d->_attributePoints.count() - 1) { //### || is special case for very last item
+ if (percent < point.percent || ii == d->_attributePoints.size() - 1) { //### || is special case for very last item
qreal elementPercent = (percent - prevPercent);
qreal spc = prevOrigPercent + elementPercent * point.scale;
@@ -723,6 +721,59 @@ void QQuickPath::invalidateSequentialHistory() const
d->prevBez.isValid = false;
}
+/*! \qmlproperty bool QtQuick::Path::simplify
+ \since 6.6
+
+ When set to true, the path will be simplified. This implies merging all subpaths that intersect,
+ creating a path where there are no self-intersections. Consecutive parallel lines will also be
+ merged. The simplified path is intended to be used with ShapePath.OddEvenFill. Bezier curves may
+ be flattened to line segments due to numerical instability of doing bezier curve intersections.
+*/
+void QQuickPath::setSimplify(bool s)
+{
+ Q_D(QQuickPath);
+ if (d->simplify == s)
+ return;
+
+ d->simplify = s;
+ processPath();
+
+ emit simplifyChanged();
+}
+
+bool QQuickPath::simplify() const
+{
+ Q_D(const QQuickPath);
+ return d->simplify;
+}
+
+/*!
+ \qmlproperty size QtQuick::Path::scale
+
+ This property holds the scale factor for the path.
+ The width and height of \a scale can be different, to
+ achieve anisotropic scaling.
+
+ \note Setting this property will not affect the border width.
+
+ \since QtQuick 2.14
+*/
+QSizeF QQuickPath::scale() const
+{
+ Q_D(const QQuickPath);
+ return d->scale;
+}
+
+void QQuickPath::setScale(const QSizeF &scale)
+{
+ Q_D(QQuickPath);
+ if (scale == d->scale)
+ return;
+ d->scale = scale;
+ emit scaleChanged();
+ processPath();
+}
+
QPointF QQuickPath::sequentialPointAt(qreal p, qreal *angle) const
{
Q_D(const QQuickPath);
@@ -757,10 +808,10 @@ QPointF QQuickPath::forwardsPointAt(const QPainterPath &path, const qreal &pathL
//find which set we are in
qreal prevPercent = 0;
qreal prevOrigPercent = 0;
- for (int ii = 0; ii < attributePoints.count(); ++ii) {
+ for (int ii = 0; ii < attributePoints.size(); ++ii) {
qreal percent = p;
const AttributePoint &point = attributePoints.at(ii);
- if (percent < point.percent || ii == attributePoints.count() - 1) {
+ if (percent < point.percent || ii == attributePoints.size() - 1) {
qreal elementPercent = (percent - prevPercent);
qreal spc = prevOrigPercent + elementPercent * point.scale;
@@ -811,7 +862,7 @@ QPointF QQuickPath::backwardsPointAt(const QPainterPath &path, const qreal &path
qreal prevLength = currLength - bezLength;
qreal epc = prevLength / pathLength;
- for (int ii = attributePoints.count() - 1; ii > 0; --ii) {
+ for (int ii = attributePoints.size() - 1; ii > 0; --ii) {
qreal percent = p;
const AttributePoint &point = attributePoints.at(ii);
const AttributePoint &prevPoint = attributePoints.at(ii-1);
@@ -852,9 +903,28 @@ QPointF QQuickPath::backwardsPointAt(const QPainterPath &path, const qreal &path
return QPointF(0,0);
}
-QPointF QQuickPath::pointAt(qreal p) const
+/*!
+ \qmlmethod point Path::pointAtPercent(real t)
+
+ Returns the point at the percentage \a t of the current path.
+ The argument \a t has to be between 0 and 1.
+
+ \note Similarly to other percent methods in \l QPainterPath,
+ the percentage measurement is not linear with regards to the length,
+ if curves are present in the path.
+ When curves are present, the percentage argument is mapped to the \c t
+ parameter of the Bezier equations.
+
+ \sa QPainterPath::pointAtPercent()
+
+ \since QtQuick 2.14
+*/
+QPointF QQuickPath::pointAtPercent(qreal t) const
{
Q_D(const QQuickPath);
+ if (d->isShapePath) // this since ShapePath does not calculate the length at all,
+ return d->_path.pointAtPercent(t); // in order to be faster.
+
if (d->_pointCache.isEmpty()) {
createPointCache();
if (d->_pointCache.isEmpty())
@@ -862,7 +932,7 @@ QPointF QQuickPath::pointAt(qreal p) const
}
const int segmentCount = d->_pointCache.size() - 1;
- qreal idxf = p*segmentCount;
+ qreal idxf = t*segmentCount;
int idx1 = qFloor(idxf);
qreal delta = idxf - idx1;
if (idx1 > segmentCount)
@@ -893,7 +963,7 @@ qreal QQuickPath::attributeAt(const QString &name, qreal percent) const
if (percent < 0 || percent > 1)
return 0;
- for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
+ for (int ii = 0; ii < d->_attributePoints.size(); ++ii) {
const AttributePoint &point = d->_attributePoints.at(ii);
if (point.percent == percent) {
@@ -917,12 +987,12 @@ qreal QQuickPath::attributeAt(const QString &name, qreal percent) const
qreal QQuickCurve::x() const
{
- return _x.isNull ? 0 : _x.value;
+ return _x.isValid() ? _x.value() : 0;
}
void QQuickCurve::setX(qreal x)
{
- if (_x.isNull || _x != x) {
+ if (!_x.isValid() || _x != x) {
_x = x;
emit xChanged();
emit changed();
@@ -936,12 +1006,12 @@ bool QQuickCurve::hasX()
qreal QQuickCurve::y() const
{
- return _y.isNull ? 0 : _y.value;
+ return _y.isValid() ? _y.value() : 0;
}
void QQuickCurve::setY(qreal y)
{
- if (_y.isNull || _y != y) {
+ if (!_y.isValid() || _y != y) {
_y = y;
emit yChanged();
emit changed();
@@ -960,7 +1030,7 @@ qreal QQuickCurve::relativeX() const
void QQuickCurve::setRelativeX(qreal x)
{
- if (_relativeX.isNull || _relativeX != x) {
+ if (!_relativeX.isValid() || _relativeX != x) {
_relativeX = x;
emit relativeXChanged();
emit changed();
@@ -979,7 +1049,7 @@ qreal QQuickCurve::relativeY() const
void QQuickCurve::setRelativeY(qreal y)
{
- if (_relativeY.isNull || _relativeY != y) {
+ if (!_relativeY.isValid() || _relativeY != y) {
_relativeY = y;
emit relativeYChanged();
emit changed();
@@ -1127,7 +1197,7 @@ void QQuickPathAttribute::setValue(qreal value)
}
\endqml
- \sa Path, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove
+ \sa Path, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove, PathPolyline, PathRectangle
*/
/*!
@@ -1338,7 +1408,7 @@ qreal QQuickPathQuad::relativeControlX() const
void QQuickPathQuad::setRelativeControlX(qreal x)
{
- if (_relativeControlX.isNull || _relativeControlX != x) {
+ if (!_relativeControlX.isValid() || _relativeControlX != x) {
_relativeControlX = x;
emit relativeControlXChanged();
emit changed();
@@ -1357,7 +1427,7 @@ qreal QQuickPathQuad::relativeControlY() const
void QQuickPathQuad::setRelativeControlY(qreal y)
{
- if (_relativeControlY.isNull || _relativeControlY != y) {
+ if (!_relativeControlY.isValid() || _relativeControlY != y) {
_relativeControlY = y;
emit relativeControlYChanged();
emit changed();
@@ -1403,7 +1473,7 @@ void QQuickPathQuad::addToPath(QPainterPath &path, const QQuickPathData &data)
\endqml
\endtable
- \sa Path, PathQuad, PathLine, PathArc, PathAngleArc, PathCurve, PathSvg
+ \sa Path, PathQuad, PathLine, PathArc, PathAngleArc, PathCurve, PathSvg, PathRectangle
*/
/*!
@@ -1522,7 +1592,7 @@ qreal QQuickPathCubic::relativeControl1X() const
void QQuickPathCubic::setRelativeControl1X(qreal x)
{
- if (_relativeControl1X.isNull || _relativeControl1X != x) {
+ if (!_relativeControl1X.isValid() || _relativeControl1X != x) {
_relativeControl1X = x;
emit relativeControl1XChanged();
emit changed();
@@ -1541,7 +1611,7 @@ qreal QQuickPathCubic::relativeControl1Y() const
void QQuickPathCubic::setRelativeControl1Y(qreal y)
{
- if (_relativeControl1Y.isNull || _relativeControl1Y != y) {
+ if (!_relativeControl1Y.isValid() || _relativeControl1Y != y) {
_relativeControl1Y = y;
emit relativeControl1YChanged();
emit changed();
@@ -1560,7 +1630,7 @@ qreal QQuickPathCubic::relativeControl2X() const
void QQuickPathCubic::setRelativeControl2X(qreal x)
{
- if (_relativeControl2X.isNull || _relativeControl2X != x) {
+ if (!_relativeControl2X.isValid() || _relativeControl2X != x) {
_relativeControl2X = x;
emit relativeControl2XChanged();
emit changed();
@@ -1579,7 +1649,7 @@ qreal QQuickPathCubic::relativeControl2Y() const
void QQuickPathCubic::setRelativeControl2Y(qreal y)
{
- if (_relativeControl2Y.isNull || _relativeControl2Y != y) {
+ if (!_relativeControl2Y.isValid() || _relativeControl2Y != y) {
_relativeControl2Y = y;
emit relativeControl2YChanged();
emit changed();
@@ -1676,17 +1746,17 @@ void QQuickPathCatmullRomCurve::addToPath(QPainterPath &path, const QQuickPathDa
} else {
prev = path.currentPosition();
bool prevFarSet = false;
- if (index == -1 && data.curves.count() > 1) {
- if (qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(data.curves.count()-1))) {
+ if (index == -1 && data.curves.size() > 1) {
+ if (qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(data.curves.size()-1))) {
//TODO: profile and optimize
QPointF pos = prev;
QQuickPathData loopData;
loopData.endPoint = data.endPoint;
loopData.curves = data.curves;
- for (int i = data.index; i < data.curves.count(); ++i) {
+ for (int i = data.index; i < data.curves.size(); ++i) {
loopData.index = i;
pos = positionForCurve(loopData, pos);
- if (i == data.curves.count()-2)
+ if (i == data.curves.size()-2)
prevFar = pos;
}
if (pos == QPointF(path.elementAt(0))) {
@@ -1705,7 +1775,7 @@ void QQuickPathCatmullRomCurve::addToPath(QPainterPath &path, const QQuickPathDa
//get next point
index = data.index + 1;
- if (index < data.curves.count() && qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(index))) {
+ if (index < data.curves.size() && qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(index))) {
QQuickPathData nextData;
nextData.index = index;
nextData.endPoint = data.endPoint;
@@ -1971,7 +2041,7 @@ void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data)
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
+ \sa Path, PathLine, PathQuad, PathCubic, PathCurve, PathSvg, PathArc, PathRectangle
*/
/*!
@@ -2153,11 +2223,6 @@ void QQuickPathAngleArc::addToPath(QPainterPath &path, const QQuickPathData &)
\endqml
\endtable
- \note Mixing PathSvg with other type of elements is not always supported.
- For example, when \l Shape is backed by \c{GL_NV_path_rendering}, a
- 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, PathAngleArc, PathCurve
*/
@@ -2193,6 +2258,268 @@ void QQuickPathSvg::addToPath(QPainterPath &path, const QQuickPathData &)
/****************************************************************************/
/*!
+ \qmltype PathRectangle
+ \instantiates QQuickPathRectangle
+ \inqmlmodule QtQuick
+ \ingroup qtquick-animation-paths
+ \brief Defines a rectangle with optionally rounded corners.
+ \since QtQuick 6.8
+
+ PathRectangle provides an easy way to specify a rectangle, optionally with rounded corners. The
+ API corresponds to that of the \l Rectangle item.
+
+ \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg
+*/
+
+/*!
+ \qmlproperty real QtQuick::PathRectangle::x
+ \qmlproperty real QtQuick::PathRectangle::y
+
+ Defines the top left corner of the rectangle.
+
+ Unless that corner is rounded, this will also be the start and end point of the path.
+
+ \sa relativeX, relativeY
+*/
+
+/*!
+ \qmlproperty real QtQuick::PathRectangle::relativeX
+ \qmlproperty real QtQuick::PathRectangle::relativeY
+
+ Defines the top left corner of the rectangle relative to the path's start point.
+
+ If both a relative and absolute end position are specified for a single axis, the relative
+ position will be used.
+
+ Relative and absolute positions can be mixed, for example it is valid to set a relative x
+ and an absolute y.
+
+ \sa x, y
+*/
+
+/*!
+ \qmlproperty real QtQuick::PathRectangle::width
+ \qmlproperty real QtQuick::PathRectangle::height
+
+ Defines the width and height of the rectangle.
+
+ \sa x, y
+*/
+
+qreal QQuickPathRectangle::width() const
+{
+ return _width;
+}
+
+void QQuickPathRectangle::setWidth(qreal width)
+{
+ if (_width == width)
+ return;
+
+ _width = width;
+ emit widthChanged();
+ emit changed();
+}
+
+qreal QQuickPathRectangle::height() const
+{
+ return _height;
+}
+
+void QQuickPathRectangle::setHeight(qreal height)
+{
+ if (_height == height)
+ return;
+
+ _height = height;
+ emit heightChanged();
+ emit changed();
+}
+
+/*!
+ \qmlproperty real QtQuick::PathRectangle::strokeAdjustment
+
+ This property defines the stroke width adjustment to the rectangle coordinates.
+
+ When used in a \l ShapePath with stroking enabled, the actual stroked rectangle will by default
+ extend beyond the defined rectangle by half the stroke width on all sides. This is the expected
+ behavior since the path defines the midpoint line of the stroking, and corresponds to QPainter
+ and SVG rendering.
+
+ If one instead wants the defined rectangle to be the outer edge of the stroked rectangle, like
+ a \l Rectangle item with a border, one can set strokeAdjustment to the stroke width. This will
+ effectively shift all edges inwards by half the stroke width. Like in the following example:
+
+ \qml
+ ShapePath {
+ id: myRec
+ fillColor: "white"
+ strokeColor: "black"
+ strokeWidth: 16
+ joinStyle: ShapePath.MiterJoin
+
+ PathRectangle { x: 10; y: 10; width: 200; height: 100; strokeAdjustment: myRec.strokeWidth }
+ }
+ \endqml
+*/
+
+qreal QQuickPathRectangle::strokeAdjustment() const
+{
+ return _strokeAdjustment;
+}
+
+void QQuickPathRectangle::setStrokeAdjustment(qreal newStrokeAdjustment)
+{
+ if (_strokeAdjustment == newStrokeAdjustment)
+ return;
+ _strokeAdjustment = newStrokeAdjustment;
+ emit strokeAdjustmentChanged();
+ emit changed();
+}
+
+/*!
+ \qmlproperty real QtQuick::PathRectangle::radius
+
+ This property defines the corner radius used to define a rounded rectangle.
+
+ If radius is a positive value, the rectangle path will be defined as a rounded rectangle,
+ otherwise it will be defined as a normal rectangle.
+
+ This property may be overridden by the individual corner radius properties.
+
+ \sa topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius
+*/
+
+qreal QQuickPathRectangle::radius() const
+{
+ return _extra.isAllocated() ? _extra->radius : 0;
+}
+
+void QQuickPathRectangle::setRadius(qreal newRadius)
+{
+ if (_extra.value().radius == newRadius)
+ return;
+ _extra->radius = newRadius;
+ emit radiusChanged();
+ if (_extra->cornerRadii[Qt::TopLeftCorner] < 0)
+ emit topLeftRadiusChanged();
+ if (_extra->cornerRadii[Qt::TopRightCorner] < 0)
+ emit topRightRadiusChanged();
+ if (_extra->cornerRadii[Qt::BottomLeftCorner] < 0)
+ emit bottomLeftRadiusChanged();
+ if (_extra->cornerRadii[Qt::BottomRightCorner] < 0)
+ emit bottomRightRadiusChanged();
+ emit changed();
+}
+
+/*!
+ \qmlproperty real QtQuick::PathRectangle::topLeftRadius
+ \qmlproperty real QtQuick::PathRectangle::topRightRadius
+ \qmlproperty real QtQuick::PathRectangle::bottomLeftRadius
+ \qmlproperty real QtQuick::PathRectangle::bottomRightRadius
+
+ If set, these properties define the individual corner radii. A zero value defines that corner
+ to be sharp, while a positive value defines it to be rounded. When unset, the value of \l
+ radius is used instead.
+
+ These properties are unset by default. Assign \c undefined to them to return them to the unset
+ state.
+
+ \sa radius
+*/
+
+qreal QQuickPathRectangle::cornerRadius(Qt::Corner corner) const
+{
+ if (_extra.isAllocated())
+ return _extra->cornerRadii[corner] < 0 ? _extra->radius : _extra->cornerRadii[corner];
+ else
+ return 0;
+}
+
+void QQuickPathRectangle::setCornerRadius(Qt::Corner corner, qreal newCornerRadius)
+{
+ if (newCornerRadius < 0 || _extra.value().cornerRadii[corner] == newCornerRadius)
+ return;
+ _extra->cornerRadii[corner] = newCornerRadius;
+ emitCornerRadiusChanged(corner);
+}
+
+void QQuickPathRectangle::resetCornerRadius(Qt::Corner corner)
+{
+ if (!_extra.isAllocated() || _extra->cornerRadii[corner] < 0)
+ return;
+ _extra->cornerRadii[corner] = -1;
+ emitCornerRadiusChanged(corner);
+}
+
+void QQuickPathRectangle::emitCornerRadiusChanged(Qt::Corner corner)
+{
+ switch (corner) {
+ case Qt::TopLeftCorner:
+ emit topLeftRadiusChanged();
+ break;
+ case Qt::TopRightCorner:
+ emit topRightRadiusChanged();
+ break;
+ case Qt::BottomLeftCorner:
+ emit bottomLeftRadiusChanged();
+ break;
+ case Qt::BottomRightCorner:
+ emit bottomRightRadiusChanged();
+ break;
+ }
+ emit changed();
+}
+
+void QQuickPathRectangle::addToPath(QPainterPath &path, const QQuickPathData &data)
+{
+ QRectF rect(positionForCurve(data, path.currentPosition()), QSizeF(_width, _height));
+
+ qreal halfStroke = _strokeAdjustment * 0.5;
+ rect.adjust(halfStroke, halfStroke, -halfStroke, -halfStroke);
+ if (rect.isEmpty())
+ return;
+
+ if (!_extra.isAllocated()) {
+ // No rounded corners
+ path.addRect(rect);
+ } else {
+ // Radii must not exceed half of the width or half of the height
+ const qreal maxDiameter = qMin(rect.width(), rect.height());
+ const qreal generalDiameter = qMax(qreal(0), qMin(maxDiameter, 2 * _extra->radius));
+ auto effectiveDiameter = [&](Qt::Corner corner) {
+ qreal radius = _extra->cornerRadii[corner];
+ return radius < 0 ? generalDiameter : qMin(maxDiameter, 2 * radius);
+ };
+ const qreal diamTL = effectiveDiameter(Qt::TopLeftCorner);
+ const qreal diamTR = effectiveDiameter(Qt::TopRightCorner);
+ const qreal diamBL = effectiveDiameter(Qt::BottomLeftCorner);
+ const qreal diamBR = effectiveDiameter(Qt::BottomRightCorner);
+
+ path.moveTo(rect.left() + diamTL * 0.5, rect.top());
+ if (diamTR)
+ path.arcTo(QRectF(QPointF(rect.right() - diamTR, rect.top()), QSizeF(diamTR, diamTR)), 90, -90);
+ else
+ path.lineTo(rect.topRight());
+ if (diamBR)
+ path.arcTo(QRectF(QPointF(rect.right() - diamBR, rect.bottom() - diamBR), QSizeF(diamBR, diamBR)), 0, -90);
+ else
+ path.lineTo(rect.bottomRight());
+ if (diamBL)
+ path.arcTo(QRectF(QPointF(rect.left(), rect.bottom() - diamBL), QSizeF(diamBL, diamBL)), 270, -90);
+ else
+ path.lineTo(rect.bottomLeft());
+ if (diamTL)
+ path.arcTo(QRectF(rect.topLeft(), QSizeF(diamTL, diamTL)), 180, -90);
+ else
+ path.lineTo(rect.topLeft());
+ path.closeSubpath();
+ }
+}
+
+/****************************************************************************/
+
+/*!
\qmltype PathPercent
\instantiates QQuickPathPercent
\inqmlmodule QtQuick
@@ -2286,6 +2613,504 @@ void QQuickPathPercent::setValue(qreal value)
emit changed();
}
}
+
+/*!
+ \qmltype PathPolyline
+ \instantiates QQuickPathPolyline
+ \inqmlmodule QtQuick
+ \ingroup qtquick-animation-paths
+ \brief Defines a polyline through a list of coordinates.
+ \since QtQuick 2.14
+
+ The example below creates a triangular path consisting of four vertices
+ on the edge of the containing Shape's bounding box.
+ Through the containing shape's \l {QtQuick::Path::}{scale} property,
+ the path will be rescaled together with its containing shape.
+
+ \qml
+ PathPolyline {
+ id: ppl
+ path: [ Qt.point(0.0, 0.0),
+ Qt.point(1.0, 0.0),
+ Qt.point(0.5, 1.0),
+ Qt.point(0.0, 0.0)
+ ]
+ }
+ \endqml
+
+ \sa Path, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove, PathPolyline
+*/
+
+/*!
+ \qmlproperty point QtQuick::PathPolyline::start
+
+ This read-only property contains the beginning of the polyline.
+*/
+
+/*!
+ \qmlproperty list<point> QtQuick::PathPolyline::path
+
+ This property defines the vertices of the polyline.
+
+ It can be a JS array of points constructed with \c Qt.point(),
+ a QList or QVector of QPointF, or QPolygonF.
+ If you are binding this to a custom property in some C++ object,
+ QPolygonF is the most appropriate type to use.
+*/
+
+QQuickPathPolyline::QQuickPathPolyline(QObject *parent) : QQuickCurve(parent)
+{
+}
+
+QVariant QQuickPathPolyline::path() const
+{
+ return QVariant::fromValue(m_path);
+}
+
+void QQuickPathPolyline::setPath(const QVariant &path)
+{
+ if (path.userType() == QMetaType::QPolygonF) {
+ setPath(path.value<QPolygonF>());
+ } else if (path.canConvert<QVector<QPointF>>()) {
+ setPath(path.value<QVector<QPointF>>());
+ } else if (path.canConvert<QVariantList>()) {
+ // This handles cases other than QPolygonF or QVector<QPointF>, such as
+ // QList<QPointF>, QVector<QPoint>, QVariantList of QPointF, QVariantList of QPoint.
+ QVector<QPointF> pathList;
+ QVariantList vl = path.value<QVariantList>();
+ // If path is a QJSValue, e.g. coming from a JS array of Qt.point() in QML,
+ // then path.value<QVariantList>() is inefficient.
+ // TODO We should be able to iterate over path.value<QSequentialIterable>() eventually
+ for (const QVariant &v : vl)
+ pathList.append(v.toPointF());
+ setPath(pathList);
+ } else {
+ qWarning() << "PathPolyline: path of type" << path.userType() << "not supported";
+ }
+}
+
+void QQuickPathPolyline::setPath(const QVector<QPointF> &path)
+{
+ if (m_path != path) {
+ const QPointF &oldStart = start();
+ m_path = path;
+ const QPointF &newStart = start();
+ emit pathChanged();
+ if (oldStart != newStart)
+ emit startChanged();
+ emit changed();
+ }
+}
+
+QPointF QQuickPathPolyline::start() const
+{
+ if (m_path.size()) {
+ const QPointF &p = m_path.first();
+ return p;
+ }
+ return QPointF();
+}
+
+void QQuickPathPolyline::addToPath(QPainterPath &path, const QQuickPathData &/*data*/)
+{
+ if (m_path.size() < 2)
+ return;
+
+ path.moveTo(m_path.first());
+ for (int i = 1; i < m_path.size(); ++i)
+ 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 {QtQuick::Path::}{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.
+
+ It can be a JS array of JS arrays of points constructed with \c Qt.point(),
+ a QList or QVector of QPolygonF, or QVector<QVector<QPointF>>.
+ If you are binding this to a custom property in some C++ object,
+ QVector<QPolygonF> or QVector<QVector<QPointF>> is the most
+ appropriate type to use.
+*/
+
+QQuickPathMultiline::QQuickPathMultiline(QObject *parent) : QQuickCurve(parent)
+{
+}
+
+QVariant QQuickPathMultiline::paths() const
+{
+ return QVariant::fromValue(m_paths);
+}
+
+void QQuickPathMultiline::setPaths(const QVariant &paths)
+{
+ if (paths.canConvert<QVector<QPolygonF>>()) {
+ const QVector<QPolygonF> pathPolygons = paths.value<QVector<QPolygonF>>();
+ QVector<QVector<QPointF>> pathVectors;
+ for (const QPolygonF &p : pathPolygons)
+ pathVectors << p;
+ setPaths(pathVectors);
+ } else if (paths.canConvert<QVector<QVector<QPointF>>>()) {
+ setPaths(paths.value<QVector<QVector<QPointF>>>());
+ } else if (paths.canConvert<QVariantList>()) {
+ // This handles cases other than QVector<QPolygonF> or QVector<QVector<QPointF>>, such as
+ // QList<QVector<QPointF>>, QList<QList<QPointF>>, QVariantList of QVector<QPointF>,
+ // QVariantList of QVariantList of QPointF, QVector<QList<QPoint>> etc.
+ QVector<QVector<QPointF>> pathsList;
+ QVariantList vll = paths.value<QVariantList>();
+ for (const QVariant &v : vll) {
+ // 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);
+ }
+ }
+ setPaths(pathsList);
+ } else {
+ qWarning() << "PathMultiline: paths of type" << paths.userType() << "not supported";
+ setPaths(QVector<QVector<QPointF>>());
+ }
+}
+
+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));
+ }
+}
+
+/*!
+ \qmltype PathText
+ \instantiates QQuickPathText
+ \inqmlmodule QtQuick
+ \ingroup qtquick-animation-paths
+ \brief Defines a string in a specified font.
+ \since QtQuick 2.15
+
+ This element defines the shape of a specified string in a specified font. The text's
+ baseline will be translated to the x and y coordinates, and the outlines from the font
+ will be added to the path accordingly.
+
+ \qml
+ PathText {
+ x: 0
+ y: font.pixelSize
+ font.family: "Arial"
+ font.pixelSize: 100
+ text: "Foobar"
+ }
+ \endqml
+
+ \sa Path, QPainterPath::setFillRule, PathPolyline, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove
+*/
+
+/*!
+ \qmlproperty real QtQuick::PathText::x
+
+ The horizontal position of the PathText's baseline.
+*/
+
+/*!
+ \qmlproperty real QtQuick::PathText::y
+
+ The vertical position of the PathText's baseline.
+
+ \note This property refers to the position of the baseline of the text, not the top of its bounding box. This may
+ cause some confusion, e.g. when using the PathText with Qt Quick Shapes. See \l FontMetrics for information on how to
+ get the ascent of a font, which can be used to translate the text into the expected position.
+*/
+
+/*!
+ \qmlproperty string QtQuick::PathText::text
+
+ The text for which this PathText should contain the outlines.
+*/
+
+/*!
+ \qmlproperty string QtQuick::PathText::font.family
+
+ Sets the family name of the font.
+
+ The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
+ If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
+ If the family isn't available a family will be set using the font matching algorithm.
+*/
+
+/*!
+ \qmlproperty string QtQuick::PathText::font.styleName
+
+ Sets the style name of the font.
+
+ The style name is case insensitive. If set, the font will be matched against style name instead
+ of the font properties \l font.weight, \l font.bold and \l font.italic.
+*/
+
+/*!
+ \qmlproperty bool QtQuick::PathText::font.bold
+
+ Sets whether the font weight is bold.
+*/
+
+/*!
+ \qmlproperty int QtQuick::PathText::font.weight
+
+ Sets the font's weight.
+
+ The weight can be one of:
+
+ \value Font.Thin 100
+ \value Font.ExtraLight 200
+ \value Font.Light 300
+ \value Font.Normal 400 (default)
+ \value Font.Medium 500
+ \value Font.DemiBold 600
+ \value Font.Bold 700
+ \value Font.ExtraBold 800
+ \value Font.Black 900
+
+ \qml
+ PathText { text: "Hello"; font.weight: Font.DemiBold }
+ \endqml
+*/
+
+/*!
+ \qmlproperty bool QtQuick::PathText::font.italic
+
+ Sets whether the font has an italic style.
+*/
+
+/*!
+ \qmlproperty bool QtQuick::PathText::font.underline
+
+ Sets whether the text is underlined.
+*/
+
+/*!
+ \qmlproperty bool QtQuick::PathText::font.strikeout
+
+ Sets whether the font has a strikeout style.
+*/
+
+/*!
+ \qmlproperty real QtQuick::PathText::font.pointSize
+
+ Sets the font size in points. The point size must be greater than zero.
+*/
+
+/*!
+ \qmlproperty int QtQuick::PathText::font.pixelSize
+
+ Sets the font size in pixels.
+
+ Using this function makes the font device dependent.
+ Use \c pointSize to set the size of the font in a device independent manner.
+*/
+
+/*!
+ \qmlproperty real QtQuick::PathText::font.letterSpacing
+
+ Sets the letter spacing for the font.
+
+ Letter spacing changes the default spacing between individual letters in the font.
+ A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
+*/
+
+/*!
+ \qmlproperty real QtQuick::PathText::font.wordSpacing
+
+ Sets the word spacing for the font.
+
+ Word spacing changes the default spacing between individual words.
+ A positive value increases the word spacing by a corresponding amount of pixels,
+ while a negative value decreases the inter-word spacing accordingly.
+*/
+
+/*!
+ \qmlproperty enumeration QtQuick::PathText::font.capitalization
+
+ Sets the capitalization for the text.
+
+ \value Font.MixedCase no capitalization change is applied
+ \value Font.AllUppercase alters the text to be rendered in all uppercase type
+ \value Font.AllLowercase alters the text to be rendered in all lowercase type
+ \value Font.SmallCaps alters the text to be rendered in small-caps type
+ \value Font.Capitalize alters the text to be rendered with the first character of
+ each word as an uppercase character
+
+ \qml
+ PathText { text: "Hello"; font.capitalization: Font.AllLowercase }
+ \endqml
+*/
+
+/*!
+ \qmlproperty bool QtQuick::PathText::font.kerning
+
+ Enables or disables the kerning OpenType feature when shaping the text. Disabling this may
+ improve performance when creating or changing the text, at the expense of some cosmetic
+ features. The default value is true.
+
+ \qml
+ PathText { text: "OATS FLAVOUR WAY"; font.kerning: false }
+ \endqml
+*/
+
+/*!
+ \qmlproperty bool QtQuick::PathText::font.preferShaping
+
+ Sometimes, a font will apply complex rules to a set of characters in order to
+ display them correctly. In some writing systems, such as Brahmic scripts, this is
+ required in order for the text to be legible, but in e.g. Latin script, it is merely
+ a cosmetic feature. Setting the \c preferShaping property to false will disable all
+ such features when they are not required, which will improve performance in most cases.
+
+ The default value is true.
+
+ \qml
+ PathText { text: "Some text"; font.preferShaping: false }
+ \endqml
+*/
+
+/*!
+ \qmlproperty object QtQuick::PathText::font.variableAxes
+ \since 6.7
+
+ \include qquicktext.cpp qml-font-variable-axes
+*/
+
+/*!
+ \qmlproperty object QtQuick::PathText::font.features
+ \since 6.6
+
+ \include qquicktext.cpp qml-font-features
+*/
+
+/*!
+ \qmlproperty bool QtQuick::PathText::font.contextFontMerging
+ \since 6.8
+
+ \include qquicktext.cpp qml-font-context-font-merging
+*/
+
+/*!
+ \qmlproperty bool QtQuick::PathText::font.preferTypoLineMetrics
+ \since 6.8
+
+ \include qquicktext.cpp qml-font-prefer-typo-line-metrics
+*/
+void QQuickPathText::updatePath() const
+{
+ if (!_path.isEmpty())
+ return;
+
+ _path.addText(0.0, 0.0, _font, _text);
+
+ // Account for distance from baseline to top, since addText() takes baseline position
+ QRectF brect = _path.boundingRect();
+ _path.translate(_x, _y - brect.y());
+}
+
+void QQuickPathText::addToPath(QPainterPath &path)
+{
+ if (_text.isEmpty())
+ return;
+ updatePath();
+ path.addPath(_path);
+}
+
QT_END_NAMESPACE
#include "moc_qquickpath_p.cpp"