1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtQuick module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include "qquickpath_p.h"
41 #include "qquickpath_p_p.h"
42 #include "qquicksvgparser_p.h"
43
44 #include <QSet>
45 #include <QTime>
46
47 #include <private/qbezier_p.h>
48 #include <QtCore/qmath.h>
49 #include <QtCore/private/qnumeric_p.h>
50
51 QT_BEGIN_NAMESPACE
52
53 /*!
54 \qmltype PathElement
55 \instantiates QQuickPathElement
56 \inqmlmodule QtQuick
57 \ingroup qtquick-animation-paths
58 \brief PathElement is the base path type.
59
60 This type is the base for all path types. It cannot
61 be instantiated.
62
63 \sa Path, PathAttribute, PathPercent, PathLine, PathPolyline, PathQuad, PathCubic, PathArc,
64 PathAngleArc, PathCurve, PathSvg
65 */
66
67 /*!
68 \qmltype Path
69 \instantiates QQuickPath
70 \inqmlmodule QtQuick
71 \ingroup qtquick-animation-paths
72 \brief Defines a path for use by \l PathView and \l Shape.
73
74 A Path is composed of one or more path segments - PathLine, PathPolyline, PathQuad,
75 PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg.
76
77 The spacing of the items along the Path can be adjusted via a
78 PathPercent object.
79
80 PathAttribute allows named attributes with values to be defined
81 along the path.
82
83 Path and the other types for specifying path elements are shared between
84 \l PathView and \l Shape. The following table provides an overview of the
85 applicability of the various path elements:
86
87 \table
88 \header
89 \li Element
90 \li PathView
91 \li Shape
92 \li Shape, GL_NV_path_rendering
93 \li Shape, software
94 \row
95 \li PathMove
96 \li N/A
97 \li Yes
98 \li Yes
99 \li Yes
100 \row
101 \li PathLine
102 \li Yes
103 \li Yes
104 \li Yes
105 \li Yes
106 \row
107 \li PathPolyline
108 \li Yes
109 \li Yes
110 \li Yes
111 \li Yes
112 \li PathMultiLine
113 \li Yes
114 \li Yes
115 \li Yes
116 \li Yes
117 \row
118 \li PathQuad
119 \li Yes
120 \li Yes
121 \li Yes
122 \li Yes
123 \row
124 \li PathCubic
125 \li Yes
126 \li Yes
127 \li Yes
128 \li Yes
129 \row
130 \li PathArc
131 \li Yes
132 \li Yes
133 \li Yes
134 \li Yes
135 \row
136 \li PathAngleArc
137 \li Yes
138 \li Yes
139 \li Yes
140 \li Yes
141 \row
142 \li PathSvg
143 \li Yes
144 \li Yes
145 \li Yes
146 \li Yes
147 \row
148 \li PathAttribute
149 \li Yes
150 \li N/A
151 \li N/A
152 \li N/A
153 \row
154 \li PathPercent
155 \li Yes
156 \li N/A
157 \li N/A
158 \li N/A
159 \row
160 \li PathCurve
161 \li Yes
162 \li No
163 \li No
164 \li No
165 \endtable
166
167 \note Path is a non-visual type; it does not display anything on its own.
168 To draw a path, use \l Shape.
169
170 \sa PathView, Shape, PathAttribute, PathPercent, PathLine, PathPolyline, PathMove, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg
171 */
QQuickPath(QObject * parent)172 QQuickPath::QQuickPath(QObject *parent)
173 : QObject(*(new QQuickPathPrivate), parent)
174 {
175 }
176
QQuickPath(QQuickPathPrivate & dd,QObject * parent)177 QQuickPath::QQuickPath(QQuickPathPrivate &dd, QObject *parent)
178 : QObject(dd, parent)
179 {
180 }
181
~QQuickPath()182 QQuickPath::~QQuickPath()
183 {
184 }
185
186 /*!
187 \qmlproperty real QtQuick::Path::startX
188 \qmlproperty real QtQuick::Path::startY
189 These properties hold the starting position of the path.
190 */
startX() const191 qreal QQuickPath::startX() const
192 {
193 Q_D(const QQuickPath);
194 return d->startX.isNull ? 0 : d->startX.value;
195 }
196
setStartX(qreal x)197 void QQuickPath::setStartX(qreal x)
198 {
199 Q_D(QQuickPath);
200 if (d->startX.isValid() && qFuzzyCompare(x, d->startX))
201 return;
202 d->startX = x;
203 emit startXChanged();
204 processPath();
205 }
206
hasStartX() const207 bool QQuickPath::hasStartX() const
208 {
209 Q_D(const QQuickPath);
210 return d->startX.isValid();
211 }
212
startY() const213 qreal QQuickPath::startY() const
214 {
215 Q_D(const QQuickPath);
216 return d->startY.isNull ? 0 : d->startY.value;
217 }
218
setStartY(qreal y)219 void QQuickPath::setStartY(qreal y)
220 {
221 Q_D(QQuickPath);
222 if (d->startY.isValid() && qFuzzyCompare(y, d->startY))
223 return;
224 d->startY = y;
225 emit startYChanged();
226 processPath();
227 }
228
hasStartY() const229 bool QQuickPath::hasStartY() const
230 {
231 Q_D(const QQuickPath);
232 return d->startY.isValid();
233 }
234
235 /*!
236 \qmlproperty bool QtQuick::Path::closed
237 This property holds whether the start and end of the path are identical.
238 */
isClosed() const239 bool QQuickPath::isClosed() const
240 {
241 Q_D(const QQuickPath);
242 return d->closed;
243 }
244
245 /*!
246 \qmlproperty list<PathElement> QtQuick::Path::pathElements
247 This property holds the objects composing the path.
248
249 \default
250
251 A path can contain the following path objects:
252 \list
253 \li \l PathLine - a straight line to a given position.
254 \li \l PathPolyline - a polyline specified as a list of coordinates.
255 \li \l PathMultiline - a list of polylines specified as a list of lists of coordinates.
256 \li \l PathQuad - a quadratic Bezier curve to a given position with a control point.
257 \li \l PathCubic - a cubic Bezier curve to a given position with two control points.
258 \li \l PathArc - an arc to a given position with a radius.
259 \li \l PathAngleArc - an arc specified by center point, radii, and angles.
260 \li \l PathSvg - a path specified as an SVG path data string.
261 \li \l PathCurve - a point on a Catmull-Rom curve.
262 \li \l PathAttribute - an attribute at a given position in the path.
263 \li \l PathPercent - a way to spread out items along various segments of the path.
264 \endlist
265
266 \snippet qml/pathview/pathattributes.qml 2
267 */
268
pathElements()269 QQmlListProperty<QQuickPathElement> QQuickPath::pathElements()
270 {
271 return QQmlListProperty<QQuickPathElement>(this,
272 nullptr,
273 pathElements_append,
274 pathElements_count,
275 pathElements_at,
276 pathElements_clear);
277 }
278
privatePath(QObject * object)279 static QQuickPathPrivate *privatePath(QObject *object)
280 {
281 QQuickPath *path = static_cast<QQuickPath*>(object);
282
283 return QQuickPathPrivate::get(path);
284 }
285
pathElements_at(QQmlListProperty<QQuickPathElement> * property,int index)286 QQuickPathElement *QQuickPath::pathElements_at(QQmlListProperty<QQuickPathElement> *property, int index)
287 {
288 QQuickPathPrivate *d = privatePath(property->object);
289
290 return d->_pathElements.at(index);
291 }
292
pathElements_append(QQmlListProperty<QQuickPathElement> * property,QQuickPathElement * pathElement)293 void QQuickPath::pathElements_append(QQmlListProperty<QQuickPathElement> *property, QQuickPathElement *pathElement)
294 {
295 QQuickPathPrivate *d = privatePath(property->object);
296 QQuickPath *path = static_cast<QQuickPath*>(property->object);
297
298 d->_pathElements.append(pathElement);
299
300 if (d->componentComplete) {
301 QQuickCurve *curve = qobject_cast<QQuickCurve *>(pathElement);
302 if (curve)
303 d->_pathCurves.append(curve);
304 else if (QQuickPathText *text = qobject_cast<QQuickPathText *>(pathElement))
305 d->_pathTexts.append(text);
306 else {
307 QQuickPathAttribute *attribute = qobject_cast<QQuickPathAttribute *>(pathElement);
308 if (attribute && !d->_attributes.contains(attribute->name()))
309 d->_attributes.append(attribute->name());
310 }
311
312 path->processPath();
313
314 connect(pathElement, SIGNAL(changed()), path, SLOT(processPath()));
315 }
316 }
317
pathElements_count(QQmlListProperty<QQuickPathElement> * property)318 int QQuickPath::pathElements_count(QQmlListProperty<QQuickPathElement> *property)
319 {
320 QQuickPathPrivate *d = privatePath(property->object);
321
322 return d->_pathElements.count();
323 }
324
pathElements_clear(QQmlListProperty<QQuickPathElement> * property)325 void QQuickPath::pathElements_clear(QQmlListProperty<QQuickPathElement> *property)
326 {
327 QQuickPathPrivate *d = privatePath(property->object);
328 QQuickPath *path = static_cast<QQuickPath*>(property->object);
329
330 path->disconnectPathElements();
331 d->_pathElements.clear();
332 d->_pathCurves.clear();
333 d->_pointCache.clear();
334 d->_pathTexts.clear();
335 }
336
interpolate(int idx,const QString & name,qreal value)337 void QQuickPath::interpolate(int idx, const QString &name, qreal value)
338 {
339 Q_D(QQuickPath);
340 interpolate(d->_attributePoints, idx, name, value);
341 }
342
interpolate(QList<AttributePoint> & attributePoints,int idx,const QString & name,qreal value)343 void QQuickPath::interpolate(QList<AttributePoint> &attributePoints, int idx, const QString &name, qreal value)
344 {
345 if (!idx)
346 return;
347
348 qreal lastValue = 0;
349 qreal lastPercent = 0;
350 int search = idx - 1;
351 while(search >= 0) {
352 const AttributePoint &point = attributePoints.at(search);
353 if (point.values.contains(name)) {
354 lastValue = point.values.value(name);
355 lastPercent = point.origpercent;
356 break;
357 }
358 --search;
359 }
360
361 ++search;
362
363 const AttributePoint &curPoint = attributePoints.at(idx);
364
365 for (int ii = search; ii < idx; ++ii) {
366 AttributePoint &point = attributePoints[ii];
367
368 qreal val = lastValue + (value - lastValue) * (point.origpercent - lastPercent) / (curPoint.origpercent - lastPercent);
369 point.values.insert(name, val);
370 }
371 }
372
endpoint(const QString & name)373 void QQuickPath::endpoint(const QString &name)
374 {
375 Q_D(QQuickPath);
376 const AttributePoint &first = d->_attributePoints.first();
377 qreal val = first.values.value(name);
378 for (int ii = d->_attributePoints.count() - 1; ii >= 0; ii--) {
379 const AttributePoint &point = d->_attributePoints.at(ii);
380 if (point.values.contains(name)) {
381 for (int jj = ii + 1; jj < d->_attributePoints.count(); ++jj) {
382 AttributePoint &setPoint = d->_attributePoints[jj];
383 setPoint.values.insert(name, val);
384 }
385 return;
386 }
387 }
388 }
389
endpoint(QList<AttributePoint> & attributePoints,const QString & name)390 void QQuickPath::endpoint(QList<AttributePoint> &attributePoints, const QString &name)
391 {
392 const AttributePoint &first = attributePoints.first();
393 qreal val = first.values.value(name);
394 for (int ii = attributePoints.count() - 1; ii >= 0; ii--) {
395 const AttributePoint &point = attributePoints.at(ii);
396 if (point.values.contains(name)) {
397 for (int jj = ii + 1; jj < attributePoints.count(); ++jj) {
398 AttributePoint &setPoint = attributePoints[jj];
399 setPoint.values.insert(name, val);
400 }
401 return;
402 }
403 }
404 }
405
processPath()406 void QQuickPath::processPath()
407 {
408 Q_D(QQuickPath);
409
410 if (!d->componentComplete)
411 return;
412
413 d->_pointCache.clear();
414 d->prevBez.isValid = false;
415
416 if (d->isShapePath) {
417 // This path is a ShapePath, so avoid extra overhead
418 d->_path = createShapePath(QPointF(), QPointF(), d->pathLength, &d->closed);
419 } else {
420 d->_path = createPath(QPointF(), QPointF(), d->_attributes, d->pathLength, d->_attributePoints, &d->closed);
421 }
422
423 emit changed();
424 }
425
scalePath(QPainterPath & path,const QSizeF & scale)426 inline static void scalePath(QPainterPath &path, const QSizeF &scale)
427 {
428 const qreal xscale = scale.width();
429 const qreal yscale = scale.height();
430 if (xscale == 1 && yscale == 1)
431 return;
432
433 for (int i = 0; i < path.elementCount(); ++i) {
434 const QPainterPath::Element &element = path.elementAt(i);
435 path.setElementPositionAt(i, element.x * xscale, element.y * yscale);
436 }
437 }
438
createPath(const QPointF & startPoint,const QPointF & endPoint,const QStringList & attributes,qreal & pathLength,QList<AttributePoint> & attributePoints,bool * closed)439 QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &endPoint, const QStringList &attributes, qreal &pathLength, QList<AttributePoint> &attributePoints, bool *closed)
440 {
441 Q_D(QQuickPath);
442
443 pathLength = 0;
444 attributePoints.clear();
445
446 if (!d->componentComplete)
447 return QPainterPath();
448
449 QPainterPath path;
450
451 AttributePoint first;
452 for (int ii = 0; ii < attributes.count(); ++ii)
453 first.values[attributes.at(ii)] = 0;
454 attributePoints << first;
455
456 qreal startX = d->startX.isValid() ? d->startX.value : startPoint.x();
457 qreal startY = d->startY.isValid() ? d->startY.value : startPoint.y();
458 path.moveTo(startX, startY);
459
460 const QString percentString = QStringLiteral("_qfx_percent");
461
462 bool usesPercent = false;
463 int index = 0;
464 for (QQuickPathElement *pathElement : qAsConst(d->_pathElements)) {
465 if (QQuickCurve *curve = qobject_cast<QQuickCurve *>(pathElement)) {
466 QQuickPathData data;
467 data.index = index;
468 data.endPoint = endPoint;
469 data.curves = d->_pathCurves;
470 curve->addToPath(path, data);
471 AttributePoint p;
472 p.origpercent = path.length();
473 attributePoints << p;
474 ++index;
475 } else if (QQuickPathAttribute *attribute = qobject_cast<QQuickPathAttribute *>(pathElement)) {
476 AttributePoint &point = attributePoints.last();
477 point.values[attribute->name()] = attribute->value();
478 interpolate(attributePoints, attributePoints.count() - 1, attribute->name(), attribute->value());
479 } else if (QQuickPathPercent *percent = qobject_cast<QQuickPathPercent *>(pathElement)) {
480 AttributePoint &point = attributePoints.last();
481 point.values[percentString] = percent->value();
482 interpolate(attributePoints, attributePoints.count() - 1, percentString, percent->value());
483 usesPercent = true;
484 } else if (QQuickPathText *text = qobject_cast<QQuickPathText *>(pathElement)) {
485 text->addToPath(path);
486 }
487 }
488
489 // Fixup end points
490 const AttributePoint &last = attributePoints.constLast();
491 for (int ii = 0; ii < attributes.count(); ++ii) {
492 if (!last.values.contains(attributes.at(ii)))
493 endpoint(attributePoints, attributes.at(ii));
494 }
495 if (usesPercent && !last.values.contains(percentString)) {
496 d->_attributePoints.last().values[percentString] = 1;
497 interpolate(d->_attributePoints.count() - 1, percentString, 1);
498 }
499 scalePath(path, d->scale);
500
501 // Adjust percent
502 qreal length = path.length();
503 qreal prevpercent = 0;
504 qreal prevorigpercent = 0;
505 for (int ii = 0; ii < attributePoints.count(); ++ii) {
506 const AttributePoint &point = attributePoints.at(ii);
507 if (point.values.contains(percentString)) { //special string for QQuickPathPercent
508 if ( ii > 0) {
509 qreal scale = (attributePoints[ii].origpercent/length - prevorigpercent) /
510 (point.values.value(percentString)-prevpercent);
511 attributePoints[ii].scale = scale;
512 }
513 attributePoints[ii].origpercent /= length;
514 attributePoints[ii].percent = point.values.value(percentString);
515 prevorigpercent = attributePoints.at(ii).origpercent;
516 prevpercent = attributePoints.at(ii).percent;
517 } else {
518 attributePoints[ii].origpercent /= length;
519 attributePoints[ii].percent = attributePoints.at(ii).origpercent;
520 }
521 }
522
523 if (closed) {
524 QPointF end = path.currentPosition();
525 *closed = length > 0 && startX * d->scale.width() == end.x() && startY * d->scale.height() == end.y();
526 }
527 pathLength = length;
528
529 return path;
530 }
531
createShapePath(const QPointF & startPoint,const QPointF & endPoint,qreal & pathLength,bool * closed)532 QPainterPath QQuickPath::createShapePath(const QPointF &startPoint, const QPointF &endPoint, qreal &pathLength, bool *closed)
533 {
534 Q_D(QQuickPath);
535
536 if (!d->componentComplete)
537 return QPainterPath();
538
539 QPainterPath path;
540
541 qreal startX = d->startX.isValid() ? d->startX.value : startPoint.x();
542 qreal startY = d->startY.isValid() ? d->startY.value : startPoint.y();
543 path.moveTo(startX, startY);
544
545 int index = 0;
546 for (QQuickCurve *curve : qAsConst(d->_pathCurves)) {
547 QQuickPathData data;
548 data.index = index;
549 data.endPoint = endPoint;
550 data.curves = d->_pathCurves;
551 curve->addToPath(path, data);
552 ++index;
553 }
554
555 for (QQuickPathText *text : qAsConst(d->_pathTexts))
556 text->addToPath(path);
557
558 if (closed) {
559 QPointF end = path.currentPosition();
560 *closed = startX == end.x() && startY == end.y();
561 }
562 scalePath(path, d->scale);
563
564 // Note: Length of paths inside ShapePath is not used, so currently
565 // length is always 0. This avoids potentially heavy path.length()
566 //pathLength = path.length();
567 pathLength = 0;
568
569 return path;
570 }
571
classBegin()572 void QQuickPath::classBegin()
573 {
574 Q_D(QQuickPath);
575 d->componentComplete = false;
576 }
577
disconnectPathElements()578 void QQuickPath::disconnectPathElements()
579 {
580 Q_D(const QQuickPath);
581
582 for (QQuickPathElement *pathElement : d->_pathElements)
583 disconnect(pathElement, SIGNAL(changed()), this, SLOT(processPath()));
584 }
585
connectPathElements()586 void QQuickPath::connectPathElements()
587 {
588 Q_D(const QQuickPath);
589
590 for (QQuickPathElement *pathElement : d->_pathElements)
591 connect(pathElement, SIGNAL(changed()), this, SLOT(processPath()));
592 }
593
gatherAttributes()594 void QQuickPath::gatherAttributes()
595 {
596 Q_D(QQuickPath);
597
598 QSet<QString> attributes;
599
600 // First gather up all the attributes
601 for (QQuickPathElement *pathElement : qAsConst(d->_pathElements)) {
602 if (QQuickCurve *curve = qobject_cast<QQuickCurve *>(pathElement))
603 d->_pathCurves.append(curve);
604 else if (QQuickPathText *text = qobject_cast<QQuickPathText *>(pathElement))
605 d->_pathTexts.append(text);
606 else if (QQuickPathAttribute *attribute = qobject_cast<QQuickPathAttribute *>(pathElement))
607 attributes.insert(attribute->name());
608 }
609
610 d->_attributes = attributes.values();
611 }
612
componentComplete()613 void QQuickPath::componentComplete()
614 {
615 Q_D(QQuickPath);
616 d->componentComplete = true;
617
618 gatherAttributes();
619
620 processPath();
621
622 connectPathElements();
623 }
624
path() const625 QPainterPath QQuickPath::path() const
626 {
627 Q_D(const QQuickPath);
628 return d->_path;
629 }
630
attributes() const631 QStringList QQuickPath::attributes() const
632 {
633 Q_D(const QQuickPath);
634 if (!d->componentComplete) {
635 QSet<QString> attrs;
636
637 // First gather up all the attributes
638 for (QQuickPathElement *pathElement : d->_pathElements) {
639 if (QQuickPathAttribute *attribute =
640 qobject_cast<QQuickPathAttribute *>(pathElement))
641 attrs.insert(attribute->name());
642 }
643 return attrs.values();
644 }
645 return d->_attributes;
646 }
647
nextBezier(const QPainterPath & path,int * current,qreal * bezLength,bool reverse=false)648 static inline QBezier nextBezier(const QPainterPath &path, int *current, qreal *bezLength, bool reverse = false)
649 {
650 const int lastElement = reverse ? 0 : path.elementCount() - 1;
651 const int start = reverse ? *current - 1 : *current + 1;
652 for (int i=start; reverse ? i >= lastElement : i <= lastElement; reverse ? --i : ++i) {
653 const QPainterPath::Element &e = path.elementAt(i);
654
655 switch (e.type) {
656 case QPainterPath::MoveToElement:
657 break;
658 case QPainterPath::LineToElement:
659 {
660 QLineF line(path.elementAt(i-1), e);
661 *bezLength = line.length();
662 QPointF a = path.elementAt(i-1);
663 QPointF delta = e - a;
664 *current = i;
665 return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
666 }
667 case QPainterPath::CurveToElement:
668 {
669 QBezier b = QBezier::fromPoints(path.elementAt(i-1),
670 e,
671 path.elementAt(i+1),
672 path.elementAt(i+2));
673 *bezLength = b.length();
674 *current = i;
675 return b;
676 }
677 default:
678 break;
679 }
680 }
681 *current = lastElement;
682 *bezLength = 0;
683 return QBezier();
684 }
685
segmentCount(const QPainterPath & path,qreal pathLength)686 static inline int segmentCount(const QPainterPath &path, qreal pathLength)
687 {
688 // In the really simple case of a single straight line we can interpolate without jitter
689 // between just two points.
690 if (path.elementCount() == 2
691 && path.elementAt(0).type == QPainterPath::MoveToElement
692 && path.elementAt(1).type == QPainterPath::LineToElement) {
693 return 1;
694 }
695 // more points means less jitter between items as they move along the
696 // path, but takes longer to generate
697 return qCeil(pathLength*5);
698 }
699
700 //derivative of the equation
slopeAt(qreal t,qreal a,qreal b,qreal c,qreal d)701 static inline qreal slopeAt(qreal t, qreal a, qreal b, qreal c, qreal d)
702 {
703 return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
704 }
705
createPointCache() const706 void QQuickPath::createPointCache() const
707 {
708 Q_D(const QQuickPath);
709 qreal pathLength = d->pathLength;
710 if (pathLength <= 0 || qt_is_nan(pathLength))
711 return;
712
713 const int segments = segmentCount(d->_path, pathLength);
714 const int lastElement = d->_path.elementCount() - 1;
715 d->_pointCache.resize(segments+1);
716
717 int currElement = -1;
718 qreal bezLength = 0;
719 QBezier currBez = nextBezier(d->_path, &currElement, &bezLength);
720 qreal currLength = bezLength;
721 qreal epc = currLength / pathLength;
722
723 for (int i = 0; i < d->_pointCache.size(); i++) {
724 //find which set we are in
725 qreal prevPercent = 0;
726 qreal prevOrigPercent = 0;
727 for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
728 qreal percent = qreal(i)/segments;
729 const AttributePoint &point = d->_attributePoints.at(ii);
730 if (percent < point.percent || ii == d->_attributePoints.count() - 1) { //### || is special case for very last item
731 qreal elementPercent = (percent - prevPercent);
732
733 qreal spc = prevOrigPercent + elementPercent * point.scale;
734
735 while (spc > epc) {
736 if (currElement > lastElement)
737 break;
738 currBez = nextBezier(d->_path, &currElement, &bezLength);
739 if (bezLength == 0.0) {
740 currLength = pathLength;
741 epc = 1.0;
742 break;
743 }
744 currLength += bezLength;
745 epc = currLength / pathLength;
746 }
747 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
748 d->_pointCache[i] = currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
749 break;
750 }
751 prevOrigPercent = point.origpercent;
752 prevPercent = point.percent;
753 }
754 }
755 }
756
invalidateSequentialHistory() const757 void QQuickPath::invalidateSequentialHistory() const
758 {
759 Q_D(const QQuickPath);
760 d->prevBez.isValid = false;
761 }
762
763 /*!
764 \qmlproperty size QtQuick::Path::scale
765
766 This property holds the scale factor for the path.
767 The width and height of \a scale can be different, to
768 achieve anisotropic scaling.
769
770 \note Setting this property will not affect the border width.
771
772 \since QtQuick 2.14
773 */
scale() const774 QSizeF QQuickPath::scale() const
775 {
776 Q_D(const QQuickPath);
777 return d->scale;
778 }
779
setScale(const QSizeF & scale)780 void QQuickPath::setScale(const QSizeF &scale)
781 {
782 Q_D(QQuickPath);
783 if (scale == d->scale)
784 return;
785 d->scale = scale;
786 emit scaleChanged();
787 processPath();
788 }
789
sequentialPointAt(qreal p,qreal * angle) const790 QPointF QQuickPath::sequentialPointAt(qreal p, qreal *angle) const
791 {
792 Q_D(const QQuickPath);
793 return sequentialPointAt(d->_path, d->pathLength, d->_attributePoints, d->prevBez, p, angle);
794 }
795
sequentialPointAt(const QPainterPath & path,const qreal & pathLength,const QList<AttributePoint> & attributePoints,QQuickCachedBezier & prevBez,qreal p,qreal * angle)796 QPointF QQuickPath::sequentialPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
797 {
798 Q_ASSERT(p >= 0.0 && p <= 1.0);
799
800 if (!prevBez.isValid)
801 return p > .5 ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
802 forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
803
804 return p < prevBez.p ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
805 forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
806 }
807
forwardsPointAt(const QPainterPath & path,const qreal & pathLength,const QList<AttributePoint> & attributePoints,QQuickCachedBezier & prevBez,qreal p,qreal * angle)808 QPointF QQuickPath::forwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
809 {
810 if (pathLength <= 0 || qt_is_nan(pathLength))
811 return path.pointAtPercent(0); //expensive?
812
813 const int lastElement = path.elementCount() - 1;
814 bool haveCachedBez = prevBez.isValid;
815 int currElement = haveCachedBez ? prevBez.element : -1;
816 qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
817 QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength);
818 qreal currLength = haveCachedBez ? prevBez.currLength : bezLength;
819 qreal epc = currLength / pathLength;
820
821 //find which set we are in
822 qreal prevPercent = 0;
823 qreal prevOrigPercent = 0;
824 for (int ii = 0; ii < attributePoints.count(); ++ii) {
825 qreal percent = p;
826 const AttributePoint &point = attributePoints.at(ii);
827 if (percent < point.percent || ii == attributePoints.count() - 1) {
828 qreal elementPercent = (percent - prevPercent);
829
830 qreal spc = prevOrigPercent + elementPercent * point.scale;
831
832 while (spc > epc) {
833 Q_ASSERT(!(currElement > lastElement));
834 Q_UNUSED(lastElement);
835 currBez = nextBezier(path, &currElement, &bezLength);
836 currLength += bezLength;
837 epc = currLength / pathLength;
838 }
839 prevBez.element = currElement;
840 prevBez.bezLength = bezLength;
841 prevBez.currLength = currLength;
842 prevBez.bezier = currBez;
843 prevBez.p = p;
844 prevBez.isValid = true;
845
846 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
847
848 if (angle) {
849 qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4);
850 qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4);
851 *angle = QLineF(0, 0, m1, m2).angle();
852 }
853
854 return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
855 }
856 prevOrigPercent = point.origpercent;
857 prevPercent = point.percent;
858 }
859
860 return QPointF(0,0);
861 }
862
863 //ideally this should be merged with forwardsPointAt
backwardsPointAt(const QPainterPath & path,const qreal & pathLength,const QList<AttributePoint> & attributePoints,QQuickCachedBezier & prevBez,qreal p,qreal * angle)864 QPointF QQuickPath::backwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle)
865 {
866 if (pathLength <= 0 || qt_is_nan(pathLength))
867 return path.pointAtPercent(0);
868
869 const int firstElement = 1; //element 0 is always a MoveTo, which we ignore
870 bool haveCachedBez = prevBez.isValid;
871 int currElement = haveCachedBez ? prevBez.element : path.elementCount();
872 qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
873 QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength, true /*reverse*/);
874 qreal currLength = haveCachedBez ? prevBez.currLength : pathLength;
875 qreal prevLength = currLength - bezLength;
876 qreal epc = prevLength / pathLength;
877
878 for (int ii = attributePoints.count() - 1; ii > 0; --ii) {
879 qreal percent = p;
880 const AttributePoint &point = attributePoints.at(ii);
881 const AttributePoint &prevPoint = attributePoints.at(ii-1);
882 if (percent > prevPoint.percent || ii == 1) {
883 qreal elementPercent = (percent - prevPoint.percent);
884
885 qreal spc = prevPoint.origpercent + elementPercent * point.scale;
886
887 while (spc < epc) {
888 Q_ASSERT(!(currElement < firstElement));
889 Q_UNUSED(firstElement);
890 currBez = nextBezier(path, &currElement, &bezLength, true /*reverse*/);
891 //special case for first element is to avoid floating point math
892 //causing an epc that never hits 0.
893 currLength = (currElement == firstElement) ? bezLength : prevLength;
894 prevLength = currLength - bezLength;
895 epc = prevLength / pathLength;
896 }
897 prevBez.element = currElement;
898 prevBez.bezLength = bezLength;
899 prevBez.currLength = currLength;
900 prevBez.bezier = currBez;
901 prevBez.p = p;
902 prevBez.isValid = true;
903
904 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
905
906 if (angle) {
907 qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4);
908 qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4);
909 *angle = QLineF(0, 0, m1, m2).angle();
910 }
911
912 return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
913 }
914 }
915
916 return QPointF(0,0);
917 }
918
919 /*!
920 \qmlmethod point Path::pointAtPercent(real t)
921
922 Returns the point at the percentage \a t of the current path.
923 The argument \a t has to be between 0 and 1.
924
925 \note Similarly to other percent methods in \l QPainterPath,
926 the percentage measurement is not linear with regards to the length,
927 if curves are present in the path.
928 When curves are present, the percentage argument is mapped to the \c t
929 parameter of the Bezier equations.
930
931 \sa QPainterPath::pointAtPercent()
932
933 \since QtQuick 2.14
934 */
pointAtPercent(qreal t) const935 QPointF QQuickPath::pointAtPercent(qreal t) const
936 {
937 Q_D(const QQuickPath);
938 if (d->isShapePath) // this since ShapePath does not calculate the length at all,
939 return d->_path.pointAtPercent(t); // in order to be faster.
940
941 if (d->_pointCache.isEmpty()) {
942 createPointCache();
943 if (d->_pointCache.isEmpty())
944 return QPointF();
945 }
946
947 const int segmentCount = d->_pointCache.size() - 1;
948 qreal idxf = t*segmentCount;
949 int idx1 = qFloor(idxf);
950 qreal delta = idxf - idx1;
951 if (idx1 > segmentCount)
952 idx1 = segmentCount;
953 else if (idx1 < 0)
954 idx1 = 0;
955
956 if (delta == 0.0)
957 return d->_pointCache.at(idx1);
958
959 // interpolate between the two points.
960 int idx2 = qCeil(idxf);
961 if (idx2 > segmentCount)
962 idx2 = segmentCount;
963 else if (idx2 < 0)
964 idx2 = 0;
965
966 QPointF p1 = d->_pointCache.at(idx1);
967 QPointF p2 = d->_pointCache.at(idx2);
968 QPointF pos = p1 * (1.0-delta) + p2 * delta;
969
970 return pos;
971 }
972
attributeAt(const QString & name,qreal percent) const973 qreal QQuickPath::attributeAt(const QString &name, qreal percent) const
974 {
975 Q_D(const QQuickPath);
976 if (percent < 0 || percent > 1)
977 return 0;
978
979 for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
980 const AttributePoint &point = d->_attributePoints.at(ii);
981
982 if (point.percent == percent) {
983 return point.values.value(name);
984 } else if (point.percent > percent) {
985 qreal lastValue =
986 ii?(d->_attributePoints.at(ii - 1).values.value(name)):0;
987 qreal lastPercent =
988 ii?(d->_attributePoints.at(ii - 1).percent):0;
989 qreal curValue = point.values.value(name);
990 qreal curPercent = point.percent;
991
992 return lastValue + (curValue - lastValue) * (percent - lastPercent) / (curPercent - lastPercent);
993 }
994 }
995
996 return 0;
997 }
998
999 /****************************************************************************/
1000
x() const1001 qreal QQuickCurve::x() const
1002 {
1003 return _x.isNull ? 0 : _x.value;
1004 }
1005
setX(qreal x)1006 void QQuickCurve::setX(qreal x)
1007 {
1008 if (_x.isNull || _x != x) {
1009 _x = x;
1010 emit xChanged();
1011 emit changed();
1012 }
1013 }
1014
hasX()1015 bool QQuickCurve::hasX()
1016 {
1017 return _x.isValid();
1018 }
1019
y() const1020 qreal QQuickCurve::y() const
1021 {
1022 return _y.isNull ? 0 : _y.value;
1023 }
1024
setY(qreal y)1025 void QQuickCurve::setY(qreal y)
1026 {
1027 if (_y.isNull || _y != y) {
1028 _y = y;
1029 emit yChanged();
1030 emit changed();
1031 }
1032 }
1033
hasY()1034 bool QQuickCurve::hasY()
1035 {
1036 return _y.isValid();
1037 }
1038
relativeX() const1039 qreal QQuickCurve::relativeX() const
1040 {
1041 return _relativeX;
1042 }
1043
setRelativeX(qreal x)1044 void QQuickCurve::setRelativeX(qreal x)
1045 {
1046 if (_relativeX.isNull || _relativeX != x) {
1047 _relativeX = x;
1048 emit relativeXChanged();
1049 emit changed();
1050 }
1051 }
1052
hasRelativeX()1053 bool QQuickCurve::hasRelativeX()
1054 {
1055 return _relativeX.isValid();
1056 }
1057
relativeY() const1058 qreal QQuickCurve::relativeY() const
1059 {
1060 return _relativeY;
1061 }
1062
setRelativeY(qreal y)1063 void QQuickCurve::setRelativeY(qreal y)
1064 {
1065 if (_relativeY.isNull || _relativeY != y) {
1066 _relativeY = y;
1067 emit relativeYChanged();
1068 emit changed();
1069 }
1070 }
1071
hasRelativeY()1072 bool QQuickCurve::hasRelativeY()
1073 {
1074 return _relativeY.isValid();
1075 }
1076
1077 /****************************************************************************/
1078
1079 /*!
1080 \qmltype PathAttribute
1081 \instantiates QQuickPathAttribute
1082 \inqmlmodule QtQuick
1083 \ingroup qtquick-animation-paths
1084 \brief Specifies how to set an attribute at a given position in a Path.
1085
1086 The PathAttribute object allows attributes consisting of a name and
1087 a value to be specified for various points along a path. The
1088 attributes are exposed to the delegate as
1089 \l{Attached Properties and Attached Signal Handlers} {Attached Properties}.
1090 The value of an attribute at any particular point along the path is interpolated
1091 from the PathAttributes bounding that point.
1092
1093 The example below shows a path with the items scaled to 30% with
1094 opacity 50% at the top of the path and scaled 100% with opacity
1095 100% at the bottom. Note the use of the PathView.iconScale and
1096 PathView.iconOpacity attached properties to set the scale and opacity
1097 of the delegate.
1098
1099 \table
1100 \row
1101 \li \image declarative-pathattribute.png
1102 \li
1103 \snippet qml/pathview/pathattributes.qml 0
1104 (see the PathView documentation for the specification of ContactModel.qml
1105 used for ContactModel above.)
1106 \endtable
1107
1108
1109 \sa Path
1110 */
1111
1112 /*!
1113 \qmlproperty string QtQuick::PathAttribute::name
1114 This property holds the name of the attribute to change.
1115
1116 This attribute will be available to the delegate as PathView.<name>
1117
1118 Note that using an existing Item property name such as "opacity" as an
1119 attribute is allowed. This is because path attributes add a new
1120 \l{Attached Properties and Attached Signal Handlers} {Attached Property}
1121 which in no way clashes with existing properties.
1122 */
1123
1124 /*!
1125 the name of the attribute to change.
1126 */
1127
name() const1128 QString QQuickPathAttribute::name() const
1129 {
1130 return _name;
1131 }
1132
setName(const QString & name)1133 void QQuickPathAttribute::setName(const QString &name)
1134 {
1135 if (_name == name)
1136 return;
1137 _name = name;
1138 emit nameChanged();
1139 }
1140
1141 /*!
1142 \qmlproperty real QtQuick::PathAttribute::value
1143 This property holds the value for the attribute.
1144
1145 The value specified can be used to influence the visual appearance
1146 of an item along the path. For example, the following Path specifies
1147 an attribute named \e itemRotation, which has the value \e 0 at the
1148 beginning of the path, and the value 90 at the end of the path.
1149
1150 \qml
1151 Path {
1152 startX: 0
1153 startY: 0
1154 PathAttribute { name: "itemRotation"; value: 0 }
1155 PathLine { x: 100; y: 100 }
1156 PathAttribute { name: "itemRotation"; value: 90 }
1157 }
1158 \endqml
1159
1160 In our delegate, we can then bind the \e rotation property to the
1161 \l{Attached Properties and Attached Signal Handlers} {Attached Property}
1162 \e PathView.itemRotation created for this attribute.
1163
1164 \qml
1165 Rectangle {
1166 width: 10; height: 10
1167 rotation: PathView.itemRotation
1168 }
1169 \endqml
1170
1171 As each item is positioned along the path, it will be rotated accordingly:
1172 an item at the beginning of the path with be not be rotated, an item at
1173 the end of the path will be rotated 90 degrees, and an item mid-way along
1174 the path will be rotated 45 degrees.
1175 */
1176
1177 /*!
1178 the new value of the attribute.
1179 */
value() const1180 qreal QQuickPathAttribute::value() const
1181 {
1182 return _value;
1183 }
1184
setValue(qreal value)1185 void QQuickPathAttribute::setValue(qreal value)
1186 {
1187 if (_value != value) {
1188 _value = value;
1189 emit valueChanged();
1190 emit changed();
1191 }
1192 }
1193
1194 /****************************************************************************/
1195
1196 /*!
1197 \qmltype PathLine
1198 \instantiates QQuickPathLine
1199 \inqmlmodule QtQuick
1200 \ingroup qtquick-animation-paths
1201 \brief Defines a straight line.
1202
1203 The example below creates a path consisting of a straight line from
1204 0,100 to 200,100:
1205
1206 \qml
1207 Path {
1208 startX: 0; startY: 100
1209 PathLine { x: 200; y: 100 }
1210 }
1211 \endqml
1212
1213 \sa Path, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove, PathPolyline
1214 */
1215
1216 /*!
1217 \qmlproperty real QtQuick::PathLine::x
1218 \qmlproperty real QtQuick::PathLine::y
1219
1220 Defines the end point of the line.
1221
1222 \sa relativeX, relativeY
1223 */
1224
1225 /*!
1226 \qmlproperty real QtQuick::PathLine::relativeX
1227 \qmlproperty real QtQuick::PathLine::relativeY
1228
1229 Defines the end point of the line relative to its start.
1230
1231 If both a relative and absolute end position are specified for a single axis, the relative
1232 position will be used.
1233
1234 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1235 and an absolute y.
1236
1237 \sa x, y
1238 */
1239
positionForCurve(const QQuickPathData & data,const QPointF & prevPoint)1240 inline QPointF positionForCurve(const QQuickPathData &data, const QPointF &prevPoint)
1241 {
1242 QQuickCurve *curve = data.curves.at(data.index);
1243 bool isEnd = data.index == data.curves.size() - 1;
1244 return QPointF(curve->hasRelativeX() ? prevPoint.x() + curve->relativeX() : !isEnd || curve->hasX() ? curve->x() : data.endPoint.x(),
1245 curve->hasRelativeY() ? prevPoint.y() + curve->relativeY() : !isEnd || curve->hasY() ? curve->y() : data.endPoint.y());
1246 }
1247
addToPath(QPainterPath & path,const QQuickPathData & data)1248 void QQuickPathLine::addToPath(QPainterPath &path, const QQuickPathData &data)
1249 {
1250 path.lineTo(positionForCurve(data, path.currentPosition()));
1251 }
1252
1253 /****************************************************************************/
1254
1255 /*!
1256 \qmltype PathMove
1257 \instantiates QQuickPathMove
1258 \inqmlmodule QtQuick
1259 \ingroup qtquick-animation-paths
1260 \brief Moves the Path's position.
1261
1262 The example below creates a path consisting of two horizontal lines with
1263 some empty space between them. All three segments have a width of 100:
1264
1265 \qml
1266 Path {
1267 startX: 0; startY: 100
1268 PathLine { relativeX: 100; y: 100 }
1269 PathMove { relativeX: 100; y: 100 }
1270 PathLine { relativeX: 100; y: 100 }
1271 }
1272 \endqml
1273
1274 \note PathMove should not be used in a Path associated with a PathView. Use
1275 PathLine instead. For ShapePath however it is important to distinguish
1276 between the operations of drawing a straight line and moving the path
1277 position without drawing anything.
1278
1279 \sa Path, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathLine
1280 */
1281
1282 /*!
1283 \qmlproperty real QtQuick::PathMove::x
1284 \qmlproperty real QtQuick::PathMove::y
1285
1286 Defines the position to move to.
1287
1288 \sa relativeX, relativeY
1289 */
1290
1291 /*!
1292 \qmlproperty real QtQuick::PathMove::relativeX
1293 \qmlproperty real QtQuick::PathMove::relativeY
1294
1295 Defines the position to move to relative to its start.
1296
1297 If both a relative and absolute end position are specified for a single axis, the relative
1298 position will be used.
1299
1300 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1301 and an absolute y.
1302
1303 \sa x, y
1304 */
1305
addToPath(QPainterPath & path,const QQuickPathData & data)1306 void QQuickPathMove::addToPath(QPainterPath &path, const QQuickPathData &data)
1307 {
1308 path.moveTo(positionForCurve(data, path.currentPosition()));
1309 }
1310
1311 /****************************************************************************/
1312
1313 /*!
1314 \qmltype PathQuad
1315 \instantiates QQuickPathQuad
1316 \inqmlmodule QtQuick
1317 \ingroup qtquick-animation-paths
1318 \brief Defines a quadratic Bezier curve with a control point.
1319
1320 The following QML produces the path shown below:
1321 \table
1322 \row
1323 \li \image declarative-pathquad.png
1324 \li
1325 \qml
1326 Path {
1327 startX: 0; startY: 0
1328 PathQuad { x: 200; y: 0; controlX: 100; controlY: 150 }
1329 }
1330 \endqml
1331 \endtable
1332
1333 \sa Path, PathCubic, PathLine, PathArc, PathAngleArc, PathCurve, PathSvg
1334 */
1335
1336 /*!
1337 \qmlproperty real QtQuick::PathQuad::x
1338 \qmlproperty real QtQuick::PathQuad::y
1339
1340 Defines the end point of the curve.
1341
1342 \sa relativeX, relativeY
1343 */
1344
1345 /*!
1346 \qmlproperty real QtQuick::PathQuad::relativeX
1347 \qmlproperty real QtQuick::PathQuad::relativeY
1348
1349 Defines the end point of the curve relative to its start.
1350
1351 If both a relative and absolute end position are specified for a single axis, the relative
1352 position will be used.
1353
1354 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1355 and an absolute y.
1356
1357 \sa x, y
1358 */
1359
1360 /*!
1361 \qmlproperty real QtQuick::PathQuad::controlX
1362 \qmlproperty real QtQuick::PathQuad::controlY
1363
1364 Defines the position of the control point.
1365 */
1366
1367 /*!
1368 the x position of the control point.
1369 */
controlX() const1370 qreal QQuickPathQuad::controlX() const
1371 {
1372 return _controlX;
1373 }
1374
setControlX(qreal x)1375 void QQuickPathQuad::setControlX(qreal x)
1376 {
1377 if (_controlX != x) {
1378 _controlX = x;
1379 emit controlXChanged();
1380 emit changed();
1381 }
1382 }
1383
1384
1385 /*!
1386 the y position of the control point.
1387 */
controlY() const1388 qreal QQuickPathQuad::controlY() const
1389 {
1390 return _controlY;
1391 }
1392
setControlY(qreal y)1393 void QQuickPathQuad::setControlY(qreal y)
1394 {
1395 if (_controlY != y) {
1396 _controlY = y;
1397 emit controlYChanged();
1398 emit changed();
1399 }
1400 }
1401
1402 /*!
1403 \qmlproperty real QtQuick::PathQuad::relativeControlX
1404 \qmlproperty real QtQuick::PathQuad::relativeControlY
1405
1406 Defines the position of the control point relative to the curve's start.
1407
1408 If both a relative and absolute control position are specified for a single axis, the relative
1409 position will be used.
1410
1411 Relative and absolute positions can be mixed, for example it is valid to set a relative control x
1412 and an absolute control y.
1413
1414 \sa controlX, controlY
1415 */
1416
relativeControlX() const1417 qreal QQuickPathQuad::relativeControlX() const
1418 {
1419 return _relativeControlX;
1420 }
1421
setRelativeControlX(qreal x)1422 void QQuickPathQuad::setRelativeControlX(qreal x)
1423 {
1424 if (_relativeControlX.isNull || _relativeControlX != x) {
1425 _relativeControlX = x;
1426 emit relativeControlXChanged();
1427 emit changed();
1428 }
1429 }
1430
hasRelativeControlX()1431 bool QQuickPathQuad::hasRelativeControlX()
1432 {
1433 return _relativeControlX.isValid();
1434 }
1435
relativeControlY() const1436 qreal QQuickPathQuad::relativeControlY() const
1437 {
1438 return _relativeControlY;
1439 }
1440
setRelativeControlY(qreal y)1441 void QQuickPathQuad::setRelativeControlY(qreal y)
1442 {
1443 if (_relativeControlY.isNull || _relativeControlY != y) {
1444 _relativeControlY = y;
1445 emit relativeControlYChanged();
1446 emit changed();
1447 }
1448 }
1449
hasRelativeControlY()1450 bool QQuickPathQuad::hasRelativeControlY()
1451 {
1452 return _relativeControlY.isValid();
1453 }
1454
addToPath(QPainterPath & path,const QQuickPathData & data)1455 void QQuickPathQuad::addToPath(QPainterPath &path, const QQuickPathData &data)
1456 {
1457 const QPointF &prevPoint = path.currentPosition();
1458 QPointF controlPoint(hasRelativeControlX() ? prevPoint.x() + relativeControlX() : controlX(),
1459 hasRelativeControlY() ? prevPoint.y() + relativeControlY() : controlY());
1460 path.quadTo(controlPoint, positionForCurve(data, path.currentPosition()));
1461 }
1462
1463 /****************************************************************************/
1464
1465 /*!
1466 \qmltype PathCubic
1467 \instantiates QQuickPathCubic
1468 \inqmlmodule QtQuick
1469 \ingroup qtquick-animation-paths
1470 \brief Defines a cubic Bezier curve with two control points.
1471
1472 The following QML produces the path shown below:
1473 \table
1474 \row
1475 \li \image declarative-pathcubic.png
1476 \li
1477 \qml
1478 Path {
1479 startX: 20; startY: 0
1480 PathCubic {
1481 x: 180; y: 0
1482 control1X: -10; control1Y: 90
1483 control2X: 210; control2Y: 90
1484 }
1485 }
1486 \endqml
1487 \endtable
1488
1489 \sa Path, PathQuad, PathLine, PathArc, PathAngleArc, PathCurve, PathSvg
1490 */
1491
1492 /*!
1493 \qmlproperty real QtQuick::PathCubic::x
1494 \qmlproperty real QtQuick::PathCubic::y
1495
1496 Defines the end point of the curve.
1497
1498 \sa relativeX, relativeY
1499 */
1500
1501 /*!
1502 \qmlproperty real QtQuick::PathCubic::relativeX
1503 \qmlproperty real QtQuick::PathCubic::relativeY
1504
1505 Defines the end point of the curve relative to its start.
1506
1507 If both a relative and absolute end position are specified for a single axis, the relative
1508 position will be used.
1509
1510 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1511 and an absolute y.
1512
1513 \sa x, y
1514 */
1515
1516 /*!
1517 \qmlproperty real QtQuick::PathCubic::control1X
1518 \qmlproperty real QtQuick::PathCubic::control1Y
1519
1520 Defines the position of the first control point.
1521 */
control1X() const1522 qreal QQuickPathCubic::control1X() const
1523 {
1524 return _control1X;
1525 }
1526
setControl1X(qreal x)1527 void QQuickPathCubic::setControl1X(qreal x)
1528 {
1529 if (_control1X != x) {
1530 _control1X = x;
1531 emit control1XChanged();
1532 emit changed();
1533 }
1534 }
1535
control1Y() const1536 qreal QQuickPathCubic::control1Y() const
1537 {
1538 return _control1Y;
1539 }
1540
setControl1Y(qreal y)1541 void QQuickPathCubic::setControl1Y(qreal y)
1542 {
1543 if (_control1Y != y) {
1544 _control1Y = y;
1545 emit control1YChanged();
1546 emit changed();
1547 }
1548 }
1549
1550 /*!
1551 \qmlproperty real QtQuick::PathCubic::control2X
1552 \qmlproperty real QtQuick::PathCubic::control2Y
1553
1554 Defines the position of the second control point.
1555 */
control2X() const1556 qreal QQuickPathCubic::control2X() const
1557 {
1558 return _control2X;
1559 }
1560
setControl2X(qreal x)1561 void QQuickPathCubic::setControl2X(qreal x)
1562 {
1563 if (_control2X != x) {
1564 _control2X = x;
1565 emit control2XChanged();
1566 emit changed();
1567 }
1568 }
1569
control2Y() const1570 qreal QQuickPathCubic::control2Y() const
1571 {
1572 return _control2Y;
1573 }
1574
setControl2Y(qreal y)1575 void QQuickPathCubic::setControl2Y(qreal y)
1576 {
1577 if (_control2Y != y) {
1578 _control2Y = y;
1579 emit control2YChanged();
1580 emit changed();
1581 }
1582 }
1583
1584 /*!
1585 \qmlproperty real QtQuick::PathCubic::relativeControl1X
1586 \qmlproperty real QtQuick::PathCubic::relativeControl1Y
1587 \qmlproperty real QtQuick::PathCubic::relativeControl2X
1588 \qmlproperty real QtQuick::PathCubic::relativeControl2Y
1589
1590 Defines the positions of the control points relative to the curve's start.
1591
1592 If both a relative and absolute control position are specified for a control point's axis, the relative
1593 position will be used.
1594
1595 Relative and absolute positions can be mixed, for example it is valid to set a relative control1 x
1596 and an absolute control1 y.
1597
1598 \sa control1X, control1Y, control2X, control2Y
1599 */
1600
relativeControl1X() const1601 qreal QQuickPathCubic::relativeControl1X() const
1602 {
1603 return _relativeControl1X;
1604 }
1605
setRelativeControl1X(qreal x)1606 void QQuickPathCubic::setRelativeControl1X(qreal x)
1607 {
1608 if (_relativeControl1X.isNull || _relativeControl1X != x) {
1609 _relativeControl1X = x;
1610 emit relativeControl1XChanged();
1611 emit changed();
1612 }
1613 }
1614
hasRelativeControl1X()1615 bool QQuickPathCubic::hasRelativeControl1X()
1616 {
1617 return _relativeControl1X.isValid();
1618 }
1619
relativeControl1Y() const1620 qreal QQuickPathCubic::relativeControl1Y() const
1621 {
1622 return _relativeControl1Y;
1623 }
1624
setRelativeControl1Y(qreal y)1625 void QQuickPathCubic::setRelativeControl1Y(qreal y)
1626 {
1627 if (_relativeControl1Y.isNull || _relativeControl1Y != y) {
1628 _relativeControl1Y = y;
1629 emit relativeControl1YChanged();
1630 emit changed();
1631 }
1632 }
1633
hasRelativeControl1Y()1634 bool QQuickPathCubic::hasRelativeControl1Y()
1635 {
1636 return _relativeControl1Y.isValid();
1637 }
1638
relativeControl2X() const1639 qreal QQuickPathCubic::relativeControl2X() const
1640 {
1641 return _relativeControl2X;
1642 }
1643
setRelativeControl2X(qreal x)1644 void QQuickPathCubic::setRelativeControl2X(qreal x)
1645 {
1646 if (_relativeControl2X.isNull || _relativeControl2X != x) {
1647 _relativeControl2X = x;
1648 emit relativeControl2XChanged();
1649 emit changed();
1650 }
1651 }
1652
hasRelativeControl2X()1653 bool QQuickPathCubic::hasRelativeControl2X()
1654 {
1655 return _relativeControl2X.isValid();
1656 }
1657
relativeControl2Y() const1658 qreal QQuickPathCubic::relativeControl2Y() const
1659 {
1660 return _relativeControl2Y;
1661 }
1662
setRelativeControl2Y(qreal y)1663 void QQuickPathCubic::setRelativeControl2Y(qreal y)
1664 {
1665 if (_relativeControl2Y.isNull || _relativeControl2Y != y) {
1666 _relativeControl2Y = y;
1667 emit relativeControl2YChanged();
1668 emit changed();
1669 }
1670 }
1671
hasRelativeControl2Y()1672 bool QQuickPathCubic::hasRelativeControl2Y()
1673 {
1674 return _relativeControl2Y.isValid();
1675 }
1676
addToPath(QPainterPath & path,const QQuickPathData & data)1677 void QQuickPathCubic::addToPath(QPainterPath &path, const QQuickPathData &data)
1678 {
1679 const QPointF &prevPoint = path.currentPosition();
1680 QPointF controlPoint1(hasRelativeControl1X() ? prevPoint.x() + relativeControl1X() : control1X(),
1681 hasRelativeControl1Y() ? prevPoint.y() + relativeControl1Y() : control1Y());
1682 QPointF controlPoint2(hasRelativeControl2X() ? prevPoint.x() + relativeControl2X() : control2X(),
1683 hasRelativeControl2Y() ? prevPoint.y() + relativeControl2Y() : control2Y());
1684 path.cubicTo(controlPoint1, controlPoint2, positionForCurve(data, path.currentPosition()));
1685 }
1686
1687 /****************************************************************************/
1688
1689 /*!
1690 \qmltype PathCurve
1691 \instantiates QQuickPathCatmullRomCurve
1692 \inqmlmodule QtQuick
1693 \ingroup qtquick-animation-paths
1694 \brief Defines a point on a Catmull-Rom curve.
1695
1696 PathCurve provides an easy way to specify a curve passing directly through a set of points.
1697 Typically multiple PathCurves are used in a series, as the following example demonstrates:
1698
1699 \snippet qml/path/basiccurve.qml 0
1700
1701 This example produces the following path (with the starting point and PathCurve points
1702 highlighted in red):
1703
1704 \image declarative-pathcurve.png
1705
1706 \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathSvg
1707 */
1708
1709 /*!
1710 \qmlproperty real QtQuick::PathCurve::x
1711 \qmlproperty real QtQuick::PathCurve::y
1712
1713 Defines the end point of the curve.
1714
1715 \sa relativeX, relativeY
1716 */
1717
1718 /*!
1719 \qmlproperty real QtQuick::PathCurve::relativeX
1720 \qmlproperty real QtQuick::PathCurve::relativeY
1721
1722 Defines the end point of the curve relative to its start.
1723
1724 If both a relative and absolute end position are specified for a single axis, the relative
1725 position will be used.
1726
1727 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1728 and an absolute y.
1729
1730 \sa x, y
1731 */
1732
previousPathPosition(const QPainterPath & path)1733 inline QPointF previousPathPosition(const QPainterPath &path)
1734 {
1735 int count = path.elementCount();
1736 if (count < 1)
1737 return QPointF();
1738
1739 int index = path.elementAt(count-1).type == QPainterPath::CurveToDataElement ? count - 4 : count - 2;
1740 return index > -1 ? QPointF(path.elementAt(index)) : path.pointAtPercent(0);
1741 }
1742
addToPath(QPainterPath & path,const QQuickPathData & data)1743 void QQuickPathCatmullRomCurve::addToPath(QPainterPath &path, const QQuickPathData &data)
1744 {
1745 //here we convert catmull-rom spline to bezier for use in QPainterPath.
1746 //basic conversion algorithm:
1747 // catmull-rom points * inverse bezier matrix * catmull-rom matrix = bezier points
1748 //each point in the catmull-rom spline produces a bezier endpoint + 2 control points
1749 //calculations for each point use a moving window of 4 points
1750 // (previous 2 points + current point + next point)
1751 QPointF prevFar, prev, point, next;
1752
1753 //get previous points
1754 int index = data.index - 1;
1755 QQuickCurve *curve = index == -1 ? 0 : data.curves.at(index);
1756 if (qobject_cast<QQuickPathCatmullRomCurve*>(curve)) {
1757 prev = path.currentPosition();
1758 prevFar = previousPathPosition(path);
1759 } else {
1760 prev = path.currentPosition();
1761 bool prevFarSet = false;
1762 if (index == -1 && data.curves.count() > 1) {
1763 if (qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(data.curves.count()-1))) {
1764 //TODO: profile and optimize
1765 QPointF pos = prev;
1766 QQuickPathData loopData;
1767 loopData.endPoint = data.endPoint;
1768 loopData.curves = data.curves;
1769 for (int i = data.index; i < data.curves.count(); ++i) {
1770 loopData.index = i;
1771 pos = positionForCurve(loopData, pos);
1772 if (i == data.curves.count()-2)
1773 prevFar = pos;
1774 }
1775 if (pos == QPointF(path.elementAt(0))) {
1776 //this is a closed path starting and ending with catmull-rom segments.
1777 //we try to smooth the join point
1778 prevFarSet = true;
1779 }
1780 }
1781 }
1782 if (!prevFarSet)
1783 prevFar = prev;
1784 }
1785
1786 //get current point
1787 point = positionForCurve(data, path.currentPosition());
1788
1789 //get next point
1790 index = data.index + 1;
1791 if (index < data.curves.count() && qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(index))) {
1792 QQuickPathData nextData;
1793 nextData.index = index;
1794 nextData.endPoint = data.endPoint;
1795 nextData.curves = data.curves;
1796 next = positionForCurve(nextData, point);
1797 } else {
1798 if (point == QPointF(path.elementAt(0)) && qobject_cast<QQuickPathCatmullRomCurve*>(data.curves.at(0)) && path.elementCount() >= 3) {
1799 //this is a closed path starting and ending with catmull-rom segments.
1800 //we try to smooth the join point
1801 next = QPointF(path.elementAt(3)); //the first catmull-rom point
1802 } else
1803 next = point;
1804 }
1805
1806 /*
1807 full conversion matrix (inverse bezier * catmull-rom):
1808 0.000, 1.000, 0.000, 0.000,
1809 -0.167, 1.000, 0.167, 0.000,
1810 0.000, 0.167, 1.000, -0.167,
1811 0.000, 0.000, 1.000, 0.000
1812
1813 conversion doesn't require full matrix multiplication,
1814 so below we simplify
1815 */
1816 QPointF control1(prevFar.x() * qreal(-0.167) +
1817 prev.x() +
1818 point.x() * qreal(0.167),
1819 prevFar.y() * qreal(-0.167) +
1820 prev.y() +
1821 point.y() * qreal(0.167));
1822
1823 QPointF control2(prev.x() * qreal(0.167) +
1824 point.x() +
1825 next.x() * qreal(-0.167),
1826 prev.y() * qreal(0.167) +
1827 point.y() +
1828 next.y() * qreal(-0.167));
1829
1830 path.cubicTo(control1, control2, point);
1831 }
1832
1833 /****************************************************************************/
1834
1835 /*!
1836 \qmltype PathArc
1837 \instantiates QQuickPathArc
1838 \inqmlmodule QtQuick
1839 \ingroup qtquick-animation-paths
1840 \brief Defines an arc with the given radius.
1841
1842 PathArc provides a simple way of specifying an arc that ends at a given position
1843 and uses the specified radius. It is modeled after the SVG elliptical arc command.
1844
1845 The following QML produces the path shown below:
1846 \table
1847 \row
1848 \li \image declarative-patharc.png
1849 \li \snippet qml/path/basicarc.qml 0
1850 \endtable
1851
1852 Note that a single PathArc cannot be used to specify a circle. Instead, you can
1853 use two PathArc elements, each specifying half of the circle.
1854
1855 \sa Path, PathLine, PathQuad, PathCubic, PathAngleArc, PathCurve, PathSvg
1856 */
1857
1858 /*!
1859 \qmlproperty real QtQuick::PathArc::x
1860 \qmlproperty real QtQuick::PathArc::y
1861
1862 Defines the end point of the arc.
1863
1864 \sa relativeX, relativeY
1865 */
1866
1867 /*!
1868 \qmlproperty real QtQuick::PathArc::relativeX
1869 \qmlproperty real QtQuick::PathArc::relativeY
1870
1871 Defines the end point of the arc relative to its start.
1872
1873 If both a relative and absolute end position are specified for a single axis, the relative
1874 position will be used.
1875
1876 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1877 and an absolute y.
1878
1879 \sa x, y
1880 */
1881
1882 /*!
1883 \qmlproperty real QtQuick::PathArc::radiusX
1884 \qmlproperty real QtQuick::PathArc::radiusY
1885
1886 Defines the radius of the arc.
1887
1888 The following QML demonstrates how different radius values can be used to change
1889 the shape of the arc:
1890 \table
1891 \row
1892 \li \image declarative-arcradius.png
1893 \li \snippet qml/path/arcradius.qml 0
1894 \endtable
1895 */
1896
radiusX() const1897 qreal QQuickPathArc::radiusX() const
1898 {
1899 return _radiusX;
1900 }
1901
setRadiusX(qreal radius)1902 void QQuickPathArc::setRadiusX(qreal radius)
1903 {
1904 if (_radiusX == radius)
1905 return;
1906
1907 _radiusX = radius;
1908 emit radiusXChanged();
1909 emit changed();
1910 }
1911
radiusY() const1912 qreal QQuickPathArc::radiusY() const
1913 {
1914 return _radiusY;
1915 }
1916
setRadiusY(qreal radius)1917 void QQuickPathArc::setRadiusY(qreal radius)
1918 {
1919 if (_radiusY == radius)
1920 return;
1921
1922 _radiusY = radius;
1923 emit radiusYChanged();
1924 emit changed();
1925 }
1926
1927 /*!
1928 \qmlproperty bool QtQuick::PathArc::useLargeArc
1929 Whether to use a large arc as defined by the arc points.
1930
1931 Given fixed start and end positions, radius, and direction,
1932 there are two possible arcs that can fit the data. useLargeArc
1933 is used to distinguish between these. For example, the following
1934 QML can produce either of the two illustrated arcs below by
1935 changing the value of useLargeArc.
1936
1937 \table
1938 \row
1939 \li \image declarative-largearc.png
1940 \li \snippet qml/path/largearc.qml 0
1941 \endtable
1942
1943 The default value is false.
1944 */
1945
useLargeArc() const1946 bool QQuickPathArc::useLargeArc() const
1947 {
1948 return _useLargeArc;
1949 }
1950
setUseLargeArc(bool largeArc)1951 void QQuickPathArc::setUseLargeArc(bool largeArc)
1952 {
1953 if (_useLargeArc == largeArc)
1954 return;
1955
1956 _useLargeArc = largeArc;
1957 emit useLargeArcChanged();
1958 emit changed();
1959 }
1960
1961 /*!
1962 \qmlproperty enumeration QtQuick::PathArc::direction
1963
1964 Defines the direction of the arc. Possible values are
1965 PathArc.Clockwise (default) and PathArc.Counterclockwise.
1966
1967 The following QML can produce either of the two illustrated arcs below
1968 by changing the value of direction.
1969 \table
1970 \row
1971 \li \image declarative-arcdirection.png
1972 \li \snippet qml/path/arcdirection.qml 0
1973 \endtable
1974
1975 \sa useLargeArc
1976 */
1977
direction() const1978 QQuickPathArc::ArcDirection QQuickPathArc::direction() const
1979 {
1980 return _direction;
1981 }
1982
setDirection(ArcDirection direction)1983 void QQuickPathArc::setDirection(ArcDirection direction)
1984 {
1985 if (_direction == direction)
1986 return;
1987
1988 _direction = direction;
1989 emit directionChanged();
1990 emit changed();
1991 }
1992
1993 /*!
1994 \qmlproperty real QtQuick::PathArc::xAxisRotation
1995
1996 Defines the rotation of the arc, in degrees. The default value is 0.
1997
1998 An arc is a section of circles or ellipses. Given the radius and the start
1999 and end points, there are two ellipses that connect the points. This
2000 property defines the rotation of the X axis of these ellipses.
2001
2002 \note The value is only useful when the x and y radius differ, meaning the
2003 arc is a section of ellipses.
2004
2005 The following QML demonstrates how different radius values can be used to change
2006 the shape of the arc:
2007 \table
2008 \row
2009 \li \image declarative-arcrotation.png
2010 \li \snippet qml/path/arcrotation.qml 0
2011 \endtable
2012 */
2013
xAxisRotation() const2014 qreal QQuickPathArc::xAxisRotation() const
2015 {
2016 return _xAxisRotation;
2017 }
2018
setXAxisRotation(qreal rotation)2019 void QQuickPathArc::setXAxisRotation(qreal rotation)
2020 {
2021 if (_xAxisRotation == rotation)
2022 return;
2023
2024 _xAxisRotation = rotation;
2025 emit xAxisRotationChanged();
2026 emit changed();
2027 }
2028
addToPath(QPainterPath & path,const QQuickPathData & data)2029 void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data)
2030 {
2031 const QPointF &startPoint = path.currentPosition();
2032 const QPointF &endPoint = positionForCurve(data, startPoint);
2033 QQuickSvgParser::pathArc(path,
2034 _radiusX,
2035 _radiusY,
2036 _xAxisRotation,
2037 _useLargeArc,
2038 _direction == Clockwise ? 1 : 0,
2039 endPoint.x(),
2040 endPoint.y(),
2041 startPoint.x(), startPoint.y());
2042 }
2043
2044 /****************************************************************************/
2045
2046 /*!
2047 \qmltype PathAngleArc
2048 \instantiates QQuickPathAngleArc
2049 \inqmlmodule QtQuick
2050 \ingroup qtquick-animation-paths
2051 \brief Defines an arc with the given radii and center.
2052
2053 PathAngleArc provides a simple way of specifying an arc. While PathArc is designed
2054 to work as part of a larger path (specifying start and end), PathAngleArc is designed
2055 to make a path where the arc is primary (such as a circular progress indicator) more intuitive.
2056
2057 \sa Path, PathLine, PathQuad, PathCubic, PathCurve, PathSvg, PathArc
2058 */
2059
2060 /*!
2061 \qmlproperty real QtQuick::PathAngleArc::centerX
2062 \qmlproperty real QtQuick::PathAngleArc::centerY
2063
2064 Defines the center of the arc.
2065 */
2066
centerX() const2067 qreal QQuickPathAngleArc::centerX() const
2068 {
2069 return _centerX;
2070 }
2071
setCenterX(qreal centerX)2072 void QQuickPathAngleArc::setCenterX(qreal centerX)
2073 {
2074 if (_centerX == centerX)
2075 return;
2076
2077 _centerX = centerX;
2078 emit centerXChanged();
2079 emit changed();
2080 }
2081
centerY() const2082 qreal QQuickPathAngleArc::centerY() const
2083 {
2084 return _centerY;
2085 }
2086
setCenterY(qreal centerY)2087 void QQuickPathAngleArc::setCenterY(qreal centerY)
2088 {
2089 if (_centerY == centerY)
2090 return;
2091
2092 _centerY = centerY;
2093 emit centerYChanged();
2094 emit changed();
2095 }
2096
2097 /*!
2098 \qmlproperty real QtQuick::PathAngleArc::radiusX
2099 \qmlproperty real QtQuick::PathAngleArc::radiusY
2100
2101 Defines the radii of the ellipse of which the arc is part.
2102 */
2103
radiusX() const2104 qreal QQuickPathAngleArc::radiusX() const
2105 {
2106 return _radiusX;
2107 }
2108
setRadiusX(qreal radius)2109 void QQuickPathAngleArc::setRadiusX(qreal radius)
2110 {
2111 if (_radiusX == radius)
2112 return;
2113
2114 _radiusX = radius;
2115 emit radiusXChanged();
2116 emit changed();
2117 }
2118
radiusY() const2119 qreal QQuickPathAngleArc::radiusY() const
2120 {
2121 return _radiusY;
2122 }
2123
setRadiusY(qreal radius)2124 void QQuickPathAngleArc::setRadiusY(qreal radius)
2125 {
2126 if (_radiusY == radius)
2127 return;
2128
2129 _radiusY = radius;
2130 emit radiusYChanged();
2131 emit changed();
2132 }
2133
2134 /*!
2135 \qmlproperty real QtQuick::PathAngleArc::startAngle
2136
2137 Defines the start angle of the arc.
2138
2139 The start angle is reported clockwise, with zero degrees at the 3 o'clock position.
2140 */
2141
startAngle() const2142 qreal QQuickPathAngleArc::startAngle() const
2143 {
2144 return _startAngle;
2145 }
2146
setStartAngle(qreal angle)2147 void QQuickPathAngleArc::setStartAngle(qreal angle)
2148 {
2149 if (_startAngle == angle)
2150 return;
2151
2152 _startAngle = angle;
2153 emit startAngleChanged();
2154 emit changed();
2155 }
2156
2157 /*!
2158 \qmlproperty real QtQuick::PathAngleArc::sweepAngle
2159
2160 Defines the sweep angle of the arc.
2161
2162 The arc will begin at startAngle and continue sweepAngle degrees, with a value of 360
2163 resulting in a full circle. Positive numbers are clockwise and negative numbers are counterclockwise.
2164 */
2165
sweepAngle() const2166 qreal QQuickPathAngleArc::sweepAngle() const
2167 {
2168 return _sweepAngle;
2169 }
2170
setSweepAngle(qreal angle)2171 void QQuickPathAngleArc::setSweepAngle(qreal angle)
2172 {
2173 if (_sweepAngle == angle)
2174 return;
2175
2176 _sweepAngle = angle;
2177 emit sweepAngleChanged();
2178 emit changed();
2179 }
2180
2181 /*!
2182 \qmlproperty bool QtQuick::PathAngleArc::moveToStart
2183
2184 Whether this element should be disconnected from the previous Path element (or startX/Y).
2185
2186 The default value is true. If set to false, the previous element's end-point
2187 (or startX/Y if PathAngleArc is the first element) will be connected to the arc's
2188 start-point with a straight line.
2189 */
2190
moveToStart() const2191 bool QQuickPathAngleArc::moveToStart() const
2192 {
2193 return _moveToStart;
2194 }
2195
setMoveToStart(bool move)2196 void QQuickPathAngleArc::setMoveToStart(bool move)
2197 {
2198 if (_moveToStart == move)
2199 return;
2200
2201 _moveToStart = move;
2202 emit moveToStartChanged();
2203 emit changed();
2204 }
2205
addToPath(QPainterPath & path,const QQuickPathData &)2206 void QQuickPathAngleArc::addToPath(QPainterPath &path, const QQuickPathData &)
2207 {
2208 qreal x = _centerX - _radiusX;
2209 qreal y = _centerY - _radiusY;
2210 qreal width = _radiusX * 2;
2211 qreal height = _radiusY * 2;
2212 if (_moveToStart)
2213 path.arcMoveTo(x, y, width, height, -_startAngle);
2214 path.arcTo(x, y, width, height, -_startAngle, -_sweepAngle);
2215 }
2216
2217 /****************************************************************************/
2218
2219 /*!
2220 \qmltype PathSvg
2221 \instantiates QQuickPathSvg
2222 \inqmlmodule QtQuick
2223 \ingroup qtquick-animation-paths
2224 \brief Defines a path using an SVG path data string.
2225
2226 The following QML produces the path shown below:
2227 \table
2228 \row
2229 \li \image declarative-pathsvg.png
2230 \li
2231 \qml
2232 Path {
2233 startX: 50; startY: 50
2234 PathSvg { path: "L 150 50 L 100 150 z" }
2235 }
2236 \endqml
2237 \endtable
2238
2239 \note Mixing PathSvg with other type of elements is not always supported.
2240 For example, when \l Shape is backed by \c{GL_NV_path_rendering}, a
2241 ShapePath can contain one or more PathSvg elements, or one or more other
2242 type of elements, but not both.
2243
2244 \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve
2245 */
2246
2247 /*!
2248 \qmlproperty string QtQuick::PathSvg::path
2249
2250 The SVG path data string specifying the path.
2251
2252 See \l {http://www.w3.org/TR/SVG/paths.html#PathData}{W3C SVG Path Data}
2253 for more details on this format.
2254 */
2255
path() const2256 QString QQuickPathSvg::path() const
2257 {
2258 return _path;
2259 }
2260
setPath(const QString & path)2261 void QQuickPathSvg::setPath(const QString &path)
2262 {
2263 if (_path == path)
2264 return;
2265
2266 _path = path;
2267 emit pathChanged();
2268 emit changed();
2269 }
2270
addToPath(QPainterPath & path,const QQuickPathData &)2271 void QQuickPathSvg::addToPath(QPainterPath &path, const QQuickPathData &)
2272 {
2273 QQuickSvgParser::parsePathDataFast(_path, path);
2274 }
2275
2276 /****************************************************************************/
2277
2278 /*!
2279 \qmltype PathPercent
2280 \instantiates QQuickPathPercent
2281 \inqmlmodule QtQuick
2282 \ingroup qtquick-animation-paths
2283 \brief Manipulates the way a path is interpreted.
2284
2285 PathPercent allows you to manipulate the spacing between items on a
2286 PathView's path. You can use it to bunch together items on part of
2287 the path, and spread them out on other parts of the path.
2288
2289 The examples below show the normal distribution of items along a path
2290 compared to a distribution which places 50% of the items along the
2291 PathLine section of the path.
2292 \table
2293 \row
2294 \li \image declarative-nopercent.png
2295 \li
2296 \qml
2297 PathView {
2298 // ...
2299 Path {
2300 startX: 20; startY: 0
2301 PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
2302 PathLine { x: 150; y: 80 }
2303 PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
2304 }
2305 }
2306 \endqml
2307 \row
2308 \li \image declarative-percent.png
2309 \li
2310 \qml
2311 PathView {
2312 // ...
2313 Path {
2314 startX: 20; startY: 0
2315 PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
2316 PathPercent { value: 0.25 }
2317 PathLine { x: 150; y: 80 }
2318 PathPercent { value: 0.75 }
2319 PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
2320 PathPercent { value: 1 }
2321 }
2322 }
2323 \endqml
2324 \endtable
2325
2326 \sa Path
2327 */
2328
2329 /*!
2330 \qmlproperty real QtQuick::PathPercent::value
2331 The proportion of items that should be laid out up to this point.
2332
2333 This value should always be higher than the last value specified
2334 by a PathPercent at a previous position in the Path.
2335
2336 In the following example we have a Path made up of three PathLines.
2337 Normally, the items of the PathView would be laid out equally along
2338 this path, with an equal number of items per line segment. PathPercent
2339 allows us to specify that the first and third lines should each hold
2340 10% of the laid out items, while the second line should hold the remaining
2341 80%.
2342
2343 \qml
2344 PathView {
2345 // ...
2346 Path {
2347 startX: 0; startY: 0
2348 PathLine { x:100; y: 0; }
2349 PathPercent { value: 0.1 }
2350 PathLine { x: 100; y: 100 }
2351 PathPercent { value: 0.9 }
2352 PathLine { x: 100; y: 0 }
2353 PathPercent { value: 1 }
2354 }
2355 }
2356 \endqml
2357 */
2358
value() const2359 qreal QQuickPathPercent::value() const
2360 {
2361 return _value;
2362 }
2363
setValue(qreal value)2364 void QQuickPathPercent::setValue(qreal value)
2365 {
2366 if (_value != value) {
2367 _value = value;
2368 emit valueChanged();
2369 emit changed();
2370 }
2371 }
2372
2373 /*!
2374 \qmltype PathPolyline
2375 \instantiates QQuickPathPolyline
2376 \inqmlmodule QtQuick
2377 \ingroup qtquick-animation-paths
2378 \brief Defines a polyline through a list of coordinates.
2379 \since QtQuick 2.14
2380
2381 The example below creates a triangular path consisting of four vertices
2382 on the edge of the containing Shape's bounding box.
2383 Through the containing shape's \l {QtQuick::Path::}{scale} property,
2384 the path will be rescaled together with its containing shape.
2385
2386 \qml
2387 PathPolyline {
2388 id: ppl
2389 path: [ Qt.point(0.0, 0.0),
2390 Qt.point(1.0, 0.0),
2391 Qt.point(0.5, 1.0),
2392 Qt.point(0.0, 0.0)
2393 ]
2394 }
2395 \endqml
2396
2397 \sa Path, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove, PathPolyline
2398 */
2399
2400 /*!
2401 \qmlproperty point QtQuick::PathPolyline::start
2402
2403 This read-only property contains the beginning of the polyline.
2404 */
2405
2406 /*!
2407 \qmlproperty list<point> QtQuick::PathPolyline::path
2408
2409 This property defines the vertices of the polyline.
2410
2411 It can be a JS array of points constructed with \c Qt.point(),
2412 a QList or QVector of QPointF, or QPolygonF.
2413 If you are binding this to a custom property in some C++ object,
2414 QPolygonF is the most appropriate type to use.
2415 */
2416
QQuickPathPolyline(QObject * parent)2417 QQuickPathPolyline::QQuickPathPolyline(QObject *parent) : QQuickCurve(parent)
2418 {
2419 }
2420
path() const2421 QVariant QQuickPathPolyline::path() const
2422 {
2423 return QVariant::fromValue(m_path);
2424 }
2425
setPath(const QVariant & path)2426 void QQuickPathPolyline::setPath(const QVariant &path)
2427 {
2428 if (path.userType() == QMetaType::QPolygonF) {
2429 setPath(path.value<QPolygonF>());
2430 } else if (path.canConvert<QVector<QPointF>>()) {
2431 setPath(path.value<QVector<QPointF>>());
2432 } else if (path.canConvert<QVariantList>()) {
2433 // This handles cases other than QPolygonF or QVector<QPointF>, such as
2434 // QList<QPointF>, QVector<QPoint>, QVariantList of QPointF, QVariantList of QPoint.
2435 QVector<QPointF> pathList;
2436 QVariantList vl = path.value<QVariantList>();
2437 // If path is a QJSValue, e.g. coming from a JS array of Qt.point() in QML,
2438 // then path.value<QVariantList>() is inefficient.
2439 // TODO We should be able to iterate over path.value<QSequentialIterable>() eventually
2440 for (const QVariant &v : vl)
2441 pathList.append(v.toPointF());
2442 setPath(pathList);
2443 } else {
2444 qWarning() << "PathPolyline: path of type" << path.userType() << "not supported";
2445 }
2446 }
2447
setPath(const QVector<QPointF> & path)2448 void QQuickPathPolyline::setPath(const QVector<QPointF> &path)
2449 {
2450 if (m_path != path) {
2451 const QPointF &oldStart = start();
2452 m_path = path;
2453 const QPointF &newStart = start();
2454 emit pathChanged();
2455 if (oldStart != newStart)
2456 emit startChanged();
2457 emit changed();
2458 }
2459 }
2460
start() const2461 QPointF QQuickPathPolyline::start() const
2462 {
2463 if (m_path.size()) {
2464 const QPointF &p = m_path.first();
2465 return p;
2466 }
2467 return QPointF();
2468 }
2469
addToPath(QPainterPath & path,const QQuickPathData &)2470 void QQuickPathPolyline::addToPath(QPainterPath &path, const QQuickPathData &/*data*/)
2471 {
2472 if (m_path.size() < 2)
2473 return;
2474
2475 path.moveTo(m_path.first());
2476 for (int i = 1; i < m_path.size(); ++i)
2477 path.lineTo(m_path.at(i));
2478 }
2479
2480
2481 /*!
2482 \qmltype PathMultiline
2483 \instantiates QQuickPathMultiline
2484 \inqmlmodule QtQuick
2485 \ingroup qtquick-animation-paths
2486 \brief Defines a set of polylines through a list of lists of coordinates.
2487 \since QtQuick 2.14
2488
2489 This element allows to define a list of polylines at once.
2490 Each polyline in the list will be preceded by a \l{QPainterPath::moveTo}{moveTo}
2491 command, effectively making each polyline a separate one.
2492 The polylines in this list are supposed to be non-intersecting with each other.
2493 In any case, when used in conjunction with a \l ShapePath, the containing ShapePath's
2494 \l ShapePath::fillRule applies.
2495 That is, with the default \c OddEvenFill and non intersecting shapes, the largest shape in the list defines an area to be filled;
2496 areas where two shapes overlap are holes; areas where three shapes overlap are filled areas inside holes, etc.
2497
2498 The example below creates a high voltage symbol by adding each path
2499 of the symbol to the list of paths.
2500 The coordinates of the vertices are normalized, and through the containing shape's
2501 \l {QtQuick::Path::}{scale} property, the path will be rescaled together with its containing shape.
2502
2503 \qml
2504 PathMultiline {
2505 paths: [
2506 [Qt.point(0.5, 0.06698),
2507 Qt.point(1, 0.93301),
2508 Qt.point(0, 0.93301),
2509 Qt.point(0.5, 0.06698)],
2510
2511 [Qt.point(0.5, 0.12472),
2512 Qt.point(0.95, 0.90414),
2513 Qt.point(0.05, 0.90414),
2514 Qt.point(0.5, 0.12472)],
2515
2516 [Qt.point(0.47131, 0.32986),
2517 Qt.point(0.36229, 0.64789),
2518 Qt.point(0.51492, 0.58590),
2519 Qt.point(0.47563, 0.76014),
2520 Qt.point(0.44950, 0.73590),
2521 Qt.point(0.46292, 0.83392),
2522 Qt.point(0.52162, 0.75190),
2523 Qt.point(0.48531, 0.76230),
2524 Qt.point(0.57529, 0.53189),
2525 Qt.point(0.41261, 0.59189),
2526 Qt.point(0.53001, 0.32786),
2527 Qt.point(0.47131, 0.32986)]
2528 ]
2529 }
2530 \endqml
2531
2532 \sa Path, QPainterPath::setFillRule, PathPolyline, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove
2533 */
2534
2535 /*!
2536 \qmlproperty point QtQuick::PathMultiline::start
2537
2538 This read-only property contains the beginning of the polylines.
2539 */
2540
2541 /*!
2542 \qmlproperty list<list<point>> QtQuick::PathMultiline::paths
2543
2544 This property defines the vertices of the polylines.
2545
2546 It can be a JS array of JS arrays of points constructed with \c Qt.point(),
2547 a QList or QVector of QPolygonF, or QVector<QVector<QPointF>>.
2548 If you are binding this to a custom property in some C++ object,
2549 QVector<QPolygonF> or QVector<QVector<QPointF>> is the most
2550 appropriate type to use.
2551 */
2552
QQuickPathMultiline(QObject * parent)2553 QQuickPathMultiline::QQuickPathMultiline(QObject *parent) : QQuickCurve(parent)
2554 {
2555 }
2556
paths() const2557 QVariant QQuickPathMultiline::paths() const
2558 {
2559 return QVariant::fromValue(m_paths);
2560 }
2561
setPaths(const QVariant & paths)2562 void QQuickPathMultiline::setPaths(const QVariant &paths)
2563 {
2564 if (paths.canConvert<QVector<QPolygonF>>()) {
2565 const QVector<QPolygonF> pathPolygons = paths.value<QVector<QPolygonF>>();
2566 QVector<QVector<QPointF>> pathVectors;
2567 for (const QPolygonF &p : pathPolygons)
2568 pathVectors << p;
2569 setPaths(pathVectors);
2570 } else if (paths.canConvert<QVector<QVector<QPointF>>>()) {
2571 setPaths(paths.value<QVector<QVector<QPointF>>>());
2572 } else if (paths.canConvert<QVariantList>()) {
2573 // This handles cases other than QVector<QPolygonF> or QVector<QVector<QPointF>>, such as
2574 // QList<QVector<QPointF>>, QList<QList<QPointF>>, QVariantList of QVector<QPointF>,
2575 // QVariantList of QVariantList of QPointF, QVector<QList<QPoint>> etc.
2576 QVector<QVector<QPointF>> pathsList;
2577 QVariantList vll = paths.value<QVariantList>();
2578 for (const QVariant &v : vll) {
2579 // If we bind a QVector<QPolygonF> property directly, rather than via QVariant,
2580 // it will come through as QJSValue that can be converted to QVariantList of QPolygonF.
2581 if (v.canConvert<QPolygonF>()) {
2582 pathsList.append(v.value<QPolygonF>());
2583 } else {
2584 QVariantList vl = v.value<QVariantList>();
2585 QVector<QPointF> l;
2586 for (const QVariant &point : vl) {
2587 if (point.canConvert<QPointF>())
2588 l.append(point.toPointF());
2589 }
2590 if (l.size() >= 2)
2591 pathsList.append(l);
2592 }
2593 }
2594 setPaths(pathsList);
2595 } else {
2596 qWarning() << "PathMultiline: paths of type" << paths.userType() << "not supported";
2597 setPaths(QVector<QVector<QPointF>>());
2598 }
2599 }
2600
setPaths(const QVector<QVector<QPointF>> & paths)2601 void QQuickPathMultiline::setPaths(const QVector<QVector<QPointF>> &paths)
2602 {
2603 if (m_paths != paths) {
2604 const QPointF &oldStart = start();
2605 m_paths = paths;
2606 const QPointF &newStart = start();
2607 emit pathsChanged();
2608 if (oldStart != newStart)
2609 emit startChanged();
2610 emit changed();
2611 }
2612 }
2613
start() const2614 QPointF QQuickPathMultiline::start() const
2615 {
2616 if (m_paths.size())
2617 return m_paths.first().first();
2618 return QPointF();
2619 }
2620
addToPath(QPainterPath & path,const QQuickPathData &)2621 void QQuickPathMultiline::addToPath(QPainterPath &path, const QQuickPathData &)
2622 {
2623 if (!m_paths.size())
2624 return;
2625 for (const QVector<QPointF> &p: m_paths) {
2626 path.moveTo(p.first());
2627 for (int i = 1; i < p.size(); ++i)
2628 path.lineTo(p.at(i));
2629 }
2630 }
2631
2632 /*!
2633 \qmltype PathText
2634 \instantiates QQuickPathText
2635 \inqmlmodule QtQuick
2636 \ingroup qtquick-animation-paths
2637 \brief Defines a string in a specified font.
2638 \since QtQuick 2.15
2639
2640 This element defines the shape of a specified string in a specified font. The text's
2641 baseline will be translated to the x and y coordinates, and the outlines from the font
2642 will be added to the path accordingly.
2643
2644 \qml
2645 PathText {
2646 x: 0
2647 y: font.pixelSize
2648 font.family: "Arial"
2649 font.pixelSize: 100
2650 text: "Foobar"
2651 }
2652 \endqml
2653
2654 \sa Path, QPainterPath::setFillRule, PathPolyline, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove
2655 */
2656
2657 /*!
2658 \qmlproperty real QtQuick::PathText::x
2659
2660 The horizontal position of the PathText's baseline.
2661 */
2662
2663 /*!
2664 \qmlproperty real QtQuick::PathText::y
2665
2666 The vertical position of the PathText's baseline.
2667
2668 \note This property refers to the position of the baseline of the text, not the top of its bounding box. This may
2669 cause some confusion, e.g. when using the PathText with Qt Quick Shapes. See \l FontMetrics for information on how to
2670 get the ascent of a font, which can be used to translate the text into the expected position.
2671 */
2672
2673 /*!
2674 \qmlproperty string QtQuick::PathText::text
2675
2676 The text for which this PathText should contain the outlines.
2677 */
2678
2679 /*!
2680 \qmlproperty string QtQuick::PathText::font.family
2681
2682 Sets the family name of the font.
2683
2684 The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
2685 If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
2686 If the family isn't available a family will be set using the font matching algorithm.
2687 */
2688
2689 /*!
2690 \qmlproperty string QtQuick::PathText::font.styleName
2691
2692 Sets the style name of the font.
2693
2694 The style name is case insensitive. If set, the font will be matched against style name instead
2695 of the font properties \l font.weight, \l font.bold and \l font.italic.
2696 */
2697
2698 /*!
2699 \qmlproperty bool QtQuick::PathText::font.bold
2700
2701 Sets whether the font weight is bold.
2702 */
2703
2704 /*!
2705 \qmlproperty enumeration QtQuick::PathText::font.weight
2706
2707 Sets the font's weight.
2708
2709 The weight can be one of:
2710 \list
2711 \li Font.Thin
2712 \li Font.Light
2713 \li Font.ExtraLight
2714 \li Font.Normal - the default
2715 \li Font.Medium
2716 \li Font.DemiBold
2717 \li Font.Bold
2718 \li Font.ExtraBold
2719 \li Font.Black
2720 \endlist
2721
2722 \qml
2723 PathText { text: "Hello"; font.weight: Font.DemiBold }
2724 \endqml
2725 */
2726
2727 /*!
2728 \qmlproperty bool QtQuick::PathText::font.italic
2729
2730 Sets whether the font has an italic style.
2731 */
2732
2733 /*!
2734 \qmlproperty bool QtQuick::PathText::font.underline
2735
2736 Sets whether the text is underlined.
2737 */
2738
2739 /*!
2740 \qmlproperty bool QtQuick::PathText::font.strikeout
2741
2742 Sets whether the font has a strikeout style.
2743 */
2744
2745 /*!
2746 \qmlproperty real QtQuick::PathText::font.pointSize
2747
2748 Sets the font size in points. The point size must be greater than zero.
2749 */
2750
2751 /*!
2752 \qmlproperty int QtQuick::PathText::font.pixelSize
2753
2754 Sets the font size in pixels.
2755
2756 Using this function makes the font device dependent.
2757 Use \c pointSize to set the size of the font in a device independent manner.
2758 */
2759
2760 /*!
2761 \qmlproperty real QtQuick::PathText::font.letterSpacing
2762
2763 Sets the letter spacing for the font.
2764
2765 Letter spacing changes the default spacing between individual letters in the font.
2766 A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
2767 */
2768
2769 /*!
2770 \qmlproperty real QtQuick::PathText::font.wordSpacing
2771
2772 Sets the word spacing for the font.
2773
2774 Word spacing changes the default spacing between individual words.
2775 A positive value increases the word spacing by a corresponding amount of pixels,
2776 while a negative value decreases the inter-word spacing accordingly.
2777 */
2778
2779 /*!
2780 \qmlproperty enumeration QtQuick::PathText::font.capitalization
2781
2782 Sets the capitalization for the text.
2783
2784 \list
2785 \li Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
2786 \li Font.AllUppercase - This alters the text to be rendered in all uppercase type.
2787 \li Font.AllLowercase - This alters the text to be rendered in all lowercase type.
2788 \li Font.SmallCaps - This alters the text to be rendered in small-caps type.
2789 \li Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
2790 \endlist
2791
2792 \qml
2793 PathText { text: "Hello"; font.capitalization: Font.AllLowercase }
2794 \endqml
2795 */
2796
2797 /*!
2798 \qmlproperty bool QtQuick::PathText::font.kerning
2799
2800 Enables or disables the kerning OpenType feature when shaping the text. Disabling this may
2801 improve performance when creating or changing the text, at the expense of some cosmetic
2802 features. The default value is true.
2803
2804 \qml
2805 PathText { text: "OATS FLAVOUR WAY"; font.kerning: false }
2806 \endqml
2807 */
2808
2809 /*!
2810 \qmlproperty bool QtQuick::PathText::font.preferShaping
2811
2812 Sometimes, a font will apply complex rules to a set of characters in order to
2813 display them correctly. In some writing systems, such as Brahmic scripts, this is
2814 required in order for the text to be legible, but in e.g. Latin script, it is merely
2815 a cosmetic feature. Setting the \c preferShaping property to false will disable all
2816 such features when they are not required, which will improve performance in most cases.
2817
2818 The default value is true.
2819
2820 \qml
2821 PathText { text: "Some text"; font.preferShaping: false }
2822 \endqml
2823 */
2824
updatePath() const2825 void QQuickPathText::updatePath() const
2826 {
2827 if (!_path.isEmpty())
2828 return;
2829
2830 _path.addText(0.0, 0.0, _font, _text);
2831
2832 // Account for distance from baseline to top, since addText() takes baseline position
2833 QRectF brect = _path.boundingRect();
2834 _path.translate(_x, _y - brect.y());
2835 }
2836
addToPath(QPainterPath & path)2837 void QQuickPathText::addToPath(QPainterPath &path)
2838 {
2839 if (_text.isEmpty())
2840 return;
2841 updatePath();
2842 path.addPath(_path);
2843 }
2844
2845 QT_END_NAMESPACE
2846
2847 #include "moc_qquickpath_p.cpp"
2848