diff options
Diffstat (limited to 'src/quick/util/qquickpath.cpp')
-rw-r--r-- | src/quick/util/qquickpath.cpp | 1047 |
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" |