diff options
Diffstat (limited to 'src/quick/util/qquickpath.cpp')
-rw-r--r-- | src/quick/util/qquickpath.cpp | 401 |
1 files changed, 358 insertions, 43 deletions
diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp index a38de75753..5cd158b191 100644 --- a/src/quick/util/qquickpath.cpp +++ b/src/quick/util/qquickpath.cpp @@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE be instantiated. \sa Path, PathAttribute, PathPercent, PathLine, PathPolyline, PathQuad, PathCubic, PathArc, - PathAngleArc, PathCurve, PathSvg + PathAngleArc, PathCurve, PathSvg, PathRectangle */ /*! @@ -70,7 +70,7 @@ QT_BEGIN_NAMESPACE \li Yes \li Yes \row - \li PathMultiLine + \li PathMultiline \li Yes \li Yes \li Yes @@ -100,6 +100,11 @@ QT_BEGIN_NAMESPACE \li Yes \li Yes \row + \li PathRectangle + \li Yes + \li Yes + \li Yes + \row \li PathAttribute \li Yes \li N/A @@ -119,7 +124,7 @@ QT_BEGIN_NAMESPACE \note Path is a non-visual type; it does not display anything on its own. To draw a path, use \l Shape. - \sa PathView, Shape, PathAttribute, PathPercent, PathLine, PathPolyline, 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) @@ -143,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) @@ -165,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) @@ -210,6 +215,7 @@ bool QQuickPath::isClosed() const \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. @@ -372,6 +378,9 @@ 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(); } @@ -405,8 +414,8 @@ QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &en 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"); @@ -490,8 +499,8 @@ 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; @@ -712,6 +721,32 @@ 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 @@ -952,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(); @@ -971,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(); @@ -995,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(); @@ -1014,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(); @@ -1162,7 +1197,7 @@ void QQuickPathAttribute::setValue(qreal value) } \endqml - \sa Path, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove, PathPolyline + \sa Path, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove, PathPolyline, PathRectangle */ /*! @@ -1373,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(); @@ -1392,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(); @@ -1438,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 */ /*! @@ -1557,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(); @@ -1576,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(); @@ -1595,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(); @@ -1614,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(); @@ -2006,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 */ /*! @@ -2223,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 @@ -2649,22 +2946,21 @@ void QQuickPathMultiline::addToPath(QPainterPath &path, const QQuickPathData &) */ /*! - \qmlproperty enumeration QtQuick::PathText::font.weight + \qmlproperty int QtQuick::PathText::font.weight Sets the font's weight. The weight can be one of: - \list - \li Font.Thin - \li Font.Light - \li Font.ExtraLight - \li Font.Normal - the default - \li Font.Medium - \li Font.DemiBold - \li Font.Bold - \li Font.ExtraBold - \li Font.Black - \endlist + + \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 } @@ -2728,13 +3024,12 @@ void QQuickPathMultiline::addToPath(QPainterPath &path, const QQuickPathData &) Sets the capitalization for the text. - \list - \li Font.MixedCase - This is the normal text rendering option where no capitalization change is applied. - \li Font.AllUppercase - This alters the text to be rendered in all uppercase type. - \li Font.AllLowercase - This alters the text to be rendered in all lowercase type. - \li Font.SmallCaps - This alters the text to be rendered in small-caps type. - \li Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character. - \endlist + \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 } @@ -2769,6 +3064,26 @@ void QQuickPathMultiline::addToPath(QPainterPath &path, const QQuickPathData &) \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 object QtQuick::PathText::font.contextFontMerging + \since 6.8 + + \include qquicktext.cpp qml-font-context-font-merging +*/ void QQuickPathText::updatePath() const { if (!_path.isEmpty()) |