1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qdeclarativepath_p.h"
43 #include "qdeclarativepath_p_p.h"
44 #include "qdeclarativesvgparser_p.h"
49 #include <private/qbezier_p.h>
50 #include <QtCore/qmath.h>
51 #include <QtCore/qnumeric.h>
56 \qmlclass PathElement QDeclarativePathElement
57 \inqmlmodule QtQuick 2
58 \ingroup qml-view-elements
59 \brief PathElement is the base path type.
61 This type is the base for all path types. It cannot
64 \sa Path, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
68 \qmlclass Path QDeclarativePath
69 \inqmlmodule QtQuick 2
70 \ingroup qml-view-elements
71 \brief A Path object defines a path for use by \l PathView.
73 A Path is composed of one or more path segments - PathLine, PathQuad,
74 PathCubic, PathArc, PathCurve, PathSvg.
76 The spacing of the items along the Path can be adjusted via a
79 PathAttribute allows named attributes with values to be defined
82 \sa PathView, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
84 QDeclarativePath::QDeclarativePath(QObject *parent)
85 : QObject(*(new QDeclarativePathPrivate), parent)
89 QDeclarativePath::~QDeclarativePath()
94 \qmlproperty real QtQuick2::Path::startX
95 \qmlproperty real QtQuick2::Path::startY
96 These properties hold the starting position of the path.
98 qreal QDeclarativePath::startX() const
100 Q_D(const QDeclarativePath);
101 return d->startX.isNull ? 0 : d->startX.value;
104 void QDeclarativePath::setStartX(qreal x)
106 Q_D(QDeclarativePath);
107 if (d->startX.isValid() && qFuzzyCompare(x, d->startX))
110 emit startXChanged();
114 bool QDeclarativePath::hasStartX() const
116 Q_D(const QDeclarativePath);
117 return d->startX.isValid();
120 qreal QDeclarativePath::startY() const
122 Q_D(const QDeclarativePath);
123 return d->startY.isNull ? 0 : d->startY.value;
126 void QDeclarativePath::setStartY(qreal y)
128 Q_D(QDeclarativePath);
129 if (d->startY.isValid() && qFuzzyCompare(y, d->startY))
132 emit startYChanged();
136 bool QDeclarativePath::hasStartY() const
138 Q_D(const QDeclarativePath);
139 return d->startY.isValid();
143 \qmlproperty bool QtQuick2::Path::closed
144 This property holds whether the start and end of the path are identical.
146 bool QDeclarativePath::isClosed() const
148 Q_D(const QDeclarativePath);
152 bool QDeclarativePath::hasEnd() const
154 Q_D(const QDeclarativePath);
155 for (int i = d->_pathElements.count() - 1; i > -1; --i) {
156 if (QDeclarativeCurve *curve = qobject_cast<QDeclarativeCurve *>(d->_pathElements.at(i))) {
157 if ((!curve->hasX() && !curve->hasRelativeX()) || (!curve->hasY() && !curve->hasRelativeY()))
163 return hasStartX() && hasStartY();
167 \qmlproperty list<PathElement> QtQuick2::Path::pathElements
168 This property holds the objects composing the path.
172 A path can contain the following path objects:
174 \i \l PathLine - a straight line to a given position.
175 \i \l PathQuad - a quadratic Bezier curve to a given position with a control point.
176 \i \l PathCubic - a cubic Bezier curve to a given position with two control points.
177 \i \l PathArc - an arc to a given position with a radius.
178 \i \l PathSvg - a path specified as an SVG path data string.
179 \i \l PathCurve - a point on a Catmull-Rom curve.
180 \i \l PathAttribute - an attribute at a given position in the path.
181 \i \l PathPercent - a way to spread out items along various segments of the path.
184 \snippet doc/src/snippets/declarative/pathview/pathattributes.qml 2
187 QDeclarativeListProperty<QDeclarativePathElement> QDeclarativePath::pathElements()
189 Q_D(QDeclarativePath);
190 return QDeclarativeListProperty<QDeclarativePathElement>(this, d->_pathElements);
193 void QDeclarativePath::interpolate(int idx, const QString &name, qreal value)
195 Q_D(QDeclarativePath);
196 interpolate(d->_attributePoints, idx, name, value);
199 void QDeclarativePath::interpolate(QList<AttributePoint> &attributePoints, int idx, const QString &name, qreal value)
205 qreal lastPercent = 0;
206 int search = idx - 1;
208 const AttributePoint &point = attributePoints.at(search);
209 if (point.values.contains(name)) {
210 lastValue = point.values.value(name);
211 lastPercent = point.origpercent;
219 const AttributePoint &curPoint = attributePoints.at(idx);
221 for (int ii = search; ii < idx; ++ii) {
222 AttributePoint &point = attributePoints[ii];
224 qreal val = lastValue + (value - lastValue) * (point.origpercent - lastPercent) / (curPoint.origpercent - lastPercent);
225 point.values.insert(name, val);
229 void QDeclarativePath::endpoint(const QString &name)
231 Q_D(QDeclarativePath);
232 const AttributePoint &first = d->_attributePoints.first();
233 qreal val = first.values.value(name);
234 for (int ii = d->_attributePoints.count() - 1; ii >= 0; ii--) {
235 const AttributePoint &point = d->_attributePoints.at(ii);
236 if (point.values.contains(name)) {
237 for (int jj = ii + 1; jj < d->_attributePoints.count(); ++jj) {
238 AttributePoint &setPoint = d->_attributePoints[jj];
239 setPoint.values.insert(name, val);
246 void QDeclarativePath::endpoint(QList<AttributePoint> &attributePoints, const QString &name)
248 const AttributePoint &first = attributePoints.first();
249 qreal val = first.values.value(name);
250 for (int ii = attributePoints.count() - 1; ii >= 0; ii--) {
251 const AttributePoint &point = attributePoints.at(ii);
252 if (point.values.contains(name)) {
253 for (int jj = ii + 1; jj < attributePoints.count(); ++jj) {
254 AttributePoint &setPoint = attributePoints[jj];
255 setPoint.values.insert(name, val);
262 static QString percentString(QLatin1String("_qfx_percent"));
264 void QDeclarativePath::processPath()
266 Q_D(QDeclarativePath);
268 if (!d->componentComplete)
271 d->_pointCache.clear();
272 d->prevBez.isValid = false;
274 d->_path = createPath(QPointF(), QPointF(), d->_attributes, d->pathLength, d->_attributePoints, &d->closed);
279 QPainterPath QDeclarativePath::createPath(const QPointF &startPoint, const QPointF &endPoint, const QStringList &attributes, qreal &pathLength, QList<AttributePoint> &attributePoints, bool *closed)
281 Q_D(QDeclarativePath);
284 attributePoints.clear();
286 if (!d->componentComplete)
287 return QPainterPath();
291 AttributePoint first;
292 for (int ii = 0; ii < attributes.count(); ++ii)
293 first.values[attributes.at(ii)] = 0;
294 attributePoints << first;
296 qreal startX = d->startX.isValid() ? d->startX.value : startPoint.x();
297 qreal startY = d->startY.isValid() ? d->startY.value : startPoint.y();
298 path.moveTo(startX, startY);
300 bool usesPercent = false;
302 foreach (QDeclarativePathElement *pathElement, d->_pathElements) {
303 if (QDeclarativeCurve *curve = qobject_cast<QDeclarativeCurve *>(pathElement)) {
304 QDeclarativePathData data;
306 data.endPoint = endPoint;
307 data.curves = d->_pathCurves;
308 curve->addToPath(path, data);
310 p.origpercent = path.length();
311 attributePoints << p;
313 } else if (QDeclarativePathAttribute *attribute = qobject_cast<QDeclarativePathAttribute *>(pathElement)) {
314 AttributePoint &point = attributePoints.last();
315 point.values[attribute->name()] = attribute->value();
316 interpolate(attributePoints, attributePoints.count() - 1, attribute->name(), attribute->value());
317 } else if (QDeclarativePathPercent *percent = qobject_cast<QDeclarativePathPercent *>(pathElement)) {
318 AttributePoint &point = attributePoints.last();
319 point.values[percentString] = percent->value();
320 interpolate(attributePoints, attributePoints.count() - 1, percentString, percent->value());
326 const AttributePoint &last = attributePoints.last();
327 for (int ii = 0; ii < attributes.count(); ++ii) {
328 if (!last.values.contains(attributes.at(ii)))
329 endpoint(attributePoints, attributes.at(ii));
331 if (usesPercent && !last.values.contains(percentString)) {
332 d->_attributePoints.last().values[percentString] = 1;
333 interpolate(d->_attributePoints.count() - 1, percentString, 1);
338 qreal length = path.length();
339 qreal prevpercent = 0;
340 qreal prevorigpercent = 0;
341 for (int ii = 0; ii < attributePoints.count(); ++ii) {
342 const AttributePoint &point = attributePoints.at(ii);
343 if (point.values.contains(percentString)) { //special string for QDeclarativePathPercent
345 qreal scale = (attributePoints[ii].origpercent/length - prevorigpercent) /
346 (point.values.value(percentString)-prevpercent);
347 attributePoints[ii].scale = scale;
349 attributePoints[ii].origpercent /= length;
350 attributePoints[ii].percent = point.values.value(percentString);
351 prevorigpercent = attributePoints[ii].origpercent;
352 prevpercent = attributePoints[ii].percent;
354 attributePoints[ii].origpercent /= length;
355 attributePoints[ii].percent = attributePoints[ii].origpercent;
360 QPointF end = path.currentPosition();
361 *closed = length > 0 && startX == end.x() && startY == end.y();
368 void QDeclarativePath::classBegin()
370 Q_D(QDeclarativePath);
371 d->componentComplete = false;
374 void QDeclarativePath::componentComplete()
376 Q_D(QDeclarativePath);
378 d->componentComplete = true;
380 // First gather up all the attributes
381 foreach (QDeclarativePathElement *pathElement, d->_pathElements) {
382 if (QDeclarativeCurve *curve =
383 qobject_cast<QDeclarativeCurve *>(pathElement))
384 d->_pathCurves.append(curve);
385 else if (QDeclarativePathAttribute *attribute =
386 qobject_cast<QDeclarativePathAttribute *>(pathElement))
387 attrs.insert(attribute->name());
389 d->_attributes = attrs.toList();
393 foreach (QDeclarativePathElement *pathElement, d->_pathElements)
394 connect(pathElement, SIGNAL(changed()), this, SLOT(processPath()));
397 QPainterPath QDeclarativePath::path() const
399 Q_D(const QDeclarativePath);
403 QStringList QDeclarativePath::attributes() const
405 Q_D(const QDeclarativePath);
406 if (!d->componentComplete) {
409 // First gather up all the attributes
410 foreach (QDeclarativePathElement *pathElement, d->_pathElements) {
411 if (QDeclarativePathAttribute *attribute =
412 qobject_cast<QDeclarativePathAttribute *>(pathElement))
413 attrs.insert(attribute->name());
415 return attrs.toList();
417 return d->_attributes;
420 static inline QBezier nextBezier(const QPainterPath &path, int *current, qreal *bezLength, bool reverse = false)
422 const int lastElement = reverse ? 0 : path.elementCount() - 1;
423 const int start = reverse ? *current - 1 : *current + 1;
424 for (int i=start; reverse ? i >= lastElement : i <= lastElement; reverse ? --i : ++i) {
425 const QPainterPath::Element &e = path.elementAt(i);
428 case QPainterPath::MoveToElement:
430 case QPainterPath::LineToElement:
432 QLineF line(path.elementAt(i-1), e);
433 *bezLength = line.length();
434 QPointF a = path.elementAt(i-1);
435 QPointF delta = e - a;
437 return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
439 case QPainterPath::CurveToElement:
441 QBezier b = QBezier::fromPoints(path.elementAt(i-1),
444 path.elementAt(i+2));
445 *bezLength = b.length();
453 *current = lastElement;
458 //derivative of the equation
459 static inline qreal slopeAt(qreal t, qreal a, qreal b, qreal c, qreal d)
461 return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
464 void QDeclarativePath::createPointCache() const
466 Q_D(const QDeclarativePath);
467 qreal pathLength = d->pathLength;
468 if (pathLength <= 0 || qIsNaN(pathLength))
470 // more points means less jitter between items as they move along the
471 // path, but takes longer to generate
472 const int points = qCeil(pathLength*5);
473 const int lastElement = d->_path.elementCount() - 1;
474 d->_pointCache.resize(points+1);
476 int currElement = -1;
478 QBezier currBez = nextBezier(d->_path, &currElement, &bezLength);
479 qreal currLength = bezLength;
480 qreal epc = currLength / pathLength;
482 for (int i = 0; i < d->_pointCache.size(); i++) {
483 //find which set we are in
484 qreal prevPercent = 0;
485 qreal prevOrigPercent = 0;
486 for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
487 qreal percent = qreal(i)/points;
488 const AttributePoint &point = d->_attributePoints.at(ii);
489 if (percent < point.percent || ii == d->_attributePoints.count() - 1) { //### || is special case for very last item
490 qreal elementPercent = (percent - prevPercent);
492 qreal spc = prevOrigPercent + elementPercent * point.scale;
495 if (currElement > lastElement)
497 currBez = nextBezier(d->_path, &currElement, &bezLength);
498 if (bezLength == 0.0) {
499 currLength = pathLength;
503 currLength += bezLength;
504 epc = currLength / pathLength;
506 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
507 d->_pointCache[i] = currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
510 prevOrigPercent = point.origpercent;
511 prevPercent = point.percent;
516 QPointF QDeclarativePath::sequentialPointAt(qreal p, qreal *angle) const
518 Q_D(const QDeclarativePath);
519 return sequentialPointAt(d->_path, d->pathLength, d->_attributePoints, d->prevBez, p, angle);
522 QPointF QDeclarativePath::sequentialPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QDeclarativeCachedBezier &prevBez, qreal p, qreal *angle)
524 if (!prevBez.isValid)
525 return p > .5 ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
526 forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
528 return p < prevBez.p ? backwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle) :
529 forwardsPointAt(path, pathLength, attributePoints, prevBez, p, angle);
532 QPointF QDeclarativePath::forwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QDeclarativeCachedBezier &prevBez, qreal p, qreal *angle)
534 if (pathLength <= 0 || qIsNaN(pathLength))
535 return path.pointAtPercent(0); //expensive?
537 const int lastElement = path.elementCount() - 1;
538 bool haveCachedBez = prevBez.isValid;
539 int currElement = haveCachedBez ? prevBez.element : -1;
540 qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
541 QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength);
542 qreal currLength = haveCachedBez ? prevBez.currLength : bezLength;
543 qreal epc = currLength / pathLength;
545 //find which set we are in
546 qreal prevPercent = 0;
547 qreal prevOrigPercent = 0;
548 for (int ii = 0; ii < attributePoints.count(); ++ii) {
550 const AttributePoint &point = attributePoints.at(ii);
551 if (percent < point.percent || ii == attributePoints.count() - 1) {
552 qreal elementPercent = (percent - prevPercent);
554 qreal spc = prevOrigPercent + elementPercent * point.scale;
557 Q_ASSERT(!(currElement > lastElement));
558 Q_UNUSED(lastElement);
559 currBez = nextBezier(path, &currElement, &bezLength);
560 currLength += bezLength;
561 epc = currLength / pathLength;
563 prevBez.element = currElement;
564 prevBez.bezLength = bezLength;
565 prevBez.currLength = currLength;
566 prevBez.bezier = currBez;
568 prevBez.isValid = true;
570 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
573 qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4);
574 qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4);
575 *angle = QLineF(0, 0, m1, m2).angle();
578 return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
580 prevOrigPercent = point.origpercent;
581 prevPercent = point.percent;
587 //ideally this should be merged with forwardsPointAt
588 QPointF QDeclarativePath::backwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QDeclarativeCachedBezier &prevBez, qreal p, qreal *angle)
590 if (pathLength <= 0 || qIsNaN(pathLength))
591 return path.pointAtPercent(0);
593 const int firstElement = 0;
594 bool haveCachedBez = prevBez.isValid;
595 int currElement = haveCachedBez ? prevBez.element : path.elementCount();
596 qreal bezLength = haveCachedBez ? prevBez.bezLength : 0;
597 QBezier currBez = haveCachedBez ? prevBez.bezier : nextBezier(path, &currElement, &bezLength, true /*reverse*/);
598 qreal currLength = haveCachedBez ? prevBez.currLength : pathLength;
599 qreal prevLength = currLength - bezLength;
600 qreal epc = prevLength / pathLength;
602 for (int ii = attributePoints.count() - 1; ii > 0; --ii) {
604 const AttributePoint &point = attributePoints.at(ii);
605 const AttributePoint &prevPoint = attributePoints.at(ii-1);
606 if (percent > prevPoint.percent || ii == 1) {
607 qreal elementPercent = (percent - prevPoint.percent);
609 qreal spc = prevPoint.origpercent + elementPercent * point.scale;
612 Q_ASSERT(!(currElement < firstElement));
613 Q_UNUSED(firstElement);
614 currBez = nextBezier(path, &currElement, &bezLength, true /*reverse*/);
615 currLength = prevLength;
616 prevLength = currLength - bezLength;
617 epc = prevLength / pathLength;
619 prevBez.element = currElement;
620 prevBez.bezLength = bezLength;
621 prevBez.currLength = currLength;
622 prevBez.bezier = currBez;
624 prevBez.isValid = true;
626 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
629 qreal m1 = slopeAt(realT, currBez.x1, currBez.x2, currBez.x3, currBez.x4);
630 qreal m2 = slopeAt(realT, currBez.y1, currBez.y2, currBez.y3, currBez.y4);
631 *angle = QLineF(0, 0, m1, m2).angle();
634 return currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
641 QPointF QDeclarativePath::pointAt(qreal p) const
643 Q_D(const QDeclarativePath);
644 if (d->_pointCache.isEmpty()) {
646 if (d->_pointCache.isEmpty())
649 int idx = qRound(p*d->_pointCache.size());
650 if (idx >= d->_pointCache.size())
651 idx = d->_pointCache.size() - 1;
654 return d->_pointCache.at(idx);
657 qreal QDeclarativePath::attributeAt(const QString &name, qreal percent) const
659 Q_D(const QDeclarativePath);
660 if (percent < 0 || percent > 1)
663 for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
664 const AttributePoint &point = d->_attributePoints.at(ii);
666 if (point.percent == percent) {
667 return point.values.value(name);
668 } else if (point.percent > percent) {
670 ii?(d->_attributePoints.at(ii - 1).values.value(name)):0;
672 ii?(d->_attributePoints.at(ii - 1).percent):0;
673 qreal curValue = point.values.value(name);
674 qreal curPercent = point.percent;
676 return lastValue + (curValue - lastValue) * (percent - lastPercent) / (curPercent - lastPercent);
683 /****************************************************************************/
685 qreal QDeclarativeCurve::x() const
687 return _x.isNull ? 0 : _x.value;
690 void QDeclarativeCurve::setX(qreal x)
692 if (_x.isNull || _x != x) {
699 bool QDeclarativeCurve::hasX()
704 qreal QDeclarativeCurve::y() const
706 return _y.isNull ? 0 : _y.value;
709 void QDeclarativeCurve::setY(qreal y)
711 if (_y.isNull || _y != y) {
718 bool QDeclarativeCurve::hasY()
723 qreal QDeclarativeCurve::relativeX() const
728 void QDeclarativeCurve::setRelativeX(qreal x)
730 if (_relativeX.isNull || _relativeX != x) {
732 emit relativeXChanged();
737 bool QDeclarativeCurve::hasRelativeX()
739 return _relativeX.isValid();
742 qreal QDeclarativeCurve::relativeY() const
747 void QDeclarativeCurve::setRelativeY(qreal y)
749 if (_relativeY.isNull || _relativeY != y) {
751 emit relativeYChanged();
756 bool QDeclarativeCurve::hasRelativeY()
758 return _relativeY.isValid();
761 /****************************************************************************/
764 \qmlclass PathAttribute QDeclarativePathAttribute
765 \inqmlmodule QtQuick 2
766 \ingroup qml-view-elements
767 \brief The PathAttribute allows setting an attribute at a given position in a Path.
769 The PathAttribute object allows attributes consisting of a name and
770 a value to be specified for various points along a path. The
771 attributes are exposed to the delegate as
772 \l{qdeclarativeintroduction.html#attached-properties} {Attached Properties}.
773 The value of an attribute at any particular point along the path is interpolated
774 from the PathAttributes bounding that point.
776 The example below shows a path with the items scaled to 30% with
777 opacity 50% at the top of the path and scaled 100% with opacity
778 100% at the bottom. Note the use of the PathView.iconScale and
779 PathView.iconOpacity attached properties to set the scale and opacity
784 \o \image declarative-pathattribute.png
786 \snippet doc/src/snippets/declarative/pathview/pathattributes.qml 0
787 (see the PathView documentation for the specification of ContactModel.qml
788 used for ContactModel above.)
796 \qmlproperty string QtQuick2::PathAttribute::name
797 This property holds the name of the attribute to change.
799 This attribute will be available to the delegate as PathView.<name>
801 Note that using an existing Item property name such as "opacity" as an
802 attribute is allowed. This is because path attributes add a new
803 \l{qdeclarativeintroduction.html#attached-properties} {Attached Property}
804 which in no way clashes with existing properties.
808 the name of the attribute to change.
811 QString QDeclarativePathAttribute::name() const
816 void QDeclarativePathAttribute::setName(const QString &name)
825 \qmlproperty real QtQuick2::PathAttribute::value
826 This property holds the value for the attribute.
828 The value specified can be used to influence the visual appearance
829 of an item along the path. For example, the following Path specifies
830 an attribute named \e itemRotation, which has the value \e 0 at the
831 beginning of the path, and the value 90 at the end of the path.
837 PathAttribute { name: "itemRotation"; value: 0 }
838 PathLine { x: 100; y: 100 }
839 PathAttribute { name: "itemRotation"; value: 90 }
843 In our delegate, we can then bind the \e rotation property to the
844 \l{qdeclarativeintroduction.html#attached-properties} {Attached Property}
845 \e PathView.itemRotation created for this attribute.
849 width: 10; height: 10
850 rotation: PathView.itemRotation
854 As each item is positioned along the path, it will be rotated accordingly:
855 an item at the beginning of the path with be not be rotated, an item at
856 the end of the path will be rotated 90 degrees, and an item mid-way along
857 the path will be rotated 45 degrees.
861 the new value of the attribute.
863 qreal QDeclarativePathAttribute::value() const
868 void QDeclarativePathAttribute::setValue(qreal value)
870 if (_value != value) {
877 /****************************************************************************/
880 \qmlclass PathLine QDeclarativePathLine
881 \inqmlmodule QtQuick 2
882 \ingroup qml-view-elements
883 \brief The PathLine defines a straight line.
885 The example below creates a path consisting of a straight line from
890 startX: 0; startY: 100
891 PathLine { x: 200; y: 100 }
895 \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
899 \qmlproperty real QtQuick2::PathLine::x
900 \qmlproperty real QtQuick2::PathLine::y
902 Defines the end point of the line.
904 \sa relativeX, relativeY
908 \qmlproperty real QtQuick2::PathLine::relativeX
909 \qmlproperty real QtQuick2::PathLine::relativeY
911 Defines the end point of the line relative to its start.
913 If both a relative and absolute end position are specified for a single axis, the relative
914 position will be used.
916 Relative and absolute positions can be mixed, for example it is valid to set a relative x
922 inline QPointF positionForCurve(const QDeclarativePathData &data, const QPointF &prevPoint)
924 QDeclarativeCurve *curve = data.curves.at(data.index);
925 bool isEnd = data.index == data.curves.size() - 1;
926 return QPointF(curve->hasRelativeX() ? prevPoint.x() + curve->relativeX() : !isEnd || curve->hasX() ? curve->x() : data.endPoint.x(),
927 curve->hasRelativeY() ? prevPoint.y() + curve->relativeY() : !isEnd || curve->hasY() ? curve->y() : data.endPoint.y());
930 void QDeclarativePathLine::addToPath(QPainterPath &path, const QDeclarativePathData &data)
932 path.lineTo(positionForCurve(data, path.currentPosition()));
935 /****************************************************************************/
938 \qmlclass PathQuad QDeclarativePathQuad
939 \inqmlmodule QtQuick 2
940 \ingroup qml-view-elements
941 \brief The PathQuad defines a quadratic Bezier curve with a control point.
943 The following QML produces the path shown below:
946 \o \image declarative-pathquad.png
951 PathQuad { x: 200; y: 0; controlX: 100; controlY: 150 }
956 \sa Path, PathCubic, PathLine, PathArc, PathCurve, PathSvg
960 \qmlproperty real QtQuick2::PathQuad::x
961 \qmlproperty real QtQuick2::PathQuad::y
963 Defines the end point of the curve.
965 \sa relativeX, relativeY
969 \qmlproperty real QtQuick2::PathQuad::relativeX
970 \qmlproperty real QtQuick2::PathQuad::relativeY
972 Defines the end point of the curve relative to its start.
974 If both a relative and absolute end position are specified for a single axis, the relative
975 position will be used.
977 Relative and absolute positions can be mixed, for example it is valid to set a relative x
984 \qmlproperty real QtQuick2::PathQuad::controlX
985 \qmlproperty real QtQuick2::PathQuad::controlY
987 Defines the position of the control point.
991 the x position of the control point.
993 qreal QDeclarativePathQuad::controlX() const
998 void QDeclarativePathQuad::setControlX(qreal x)
1000 if (_controlX != x) {
1002 emit controlXChanged();
1009 the y position of the control point.
1011 qreal QDeclarativePathQuad::controlY() const
1016 void QDeclarativePathQuad::setControlY(qreal y)
1018 if (_controlY != y) {
1020 emit controlYChanged();
1026 \qmlproperty real QtQuick2::PathCubic::relativeControlX
1027 \qmlproperty real QtQuick2::PathCubic::relativeControlY
1029 Defines the position of the control point relative to the curve's start.
1031 If both a relative and absolute control position are specified for a single axis, the relative
1032 position will be used.
1034 Relative and absolute positions can be mixed, for example it is valid to set a relative control x
1035 and an absolute control y.
1037 \sa controlX, controlY
1040 qreal QDeclarativePathQuad::relativeControlX() const
1042 return _relativeControlX;
1045 void QDeclarativePathQuad::setRelativeControlX(qreal x)
1047 if (_relativeControlX.isNull || _relativeControlX != x) {
1048 _relativeControlX = x;
1049 emit relativeControlXChanged();
1054 bool QDeclarativePathQuad::hasRelativeControlX()
1056 return _relativeControlX.isValid();
1059 qreal QDeclarativePathQuad::relativeControlY() const
1061 return _relativeControlY;
1064 void QDeclarativePathQuad::setRelativeControlY(qreal y)
1066 if (_relativeControlY.isNull || _relativeControlY != y) {
1067 _relativeControlY = y;
1068 emit relativeControlYChanged();
1073 bool QDeclarativePathQuad::hasRelativeControlY()
1075 return _relativeControlY.isValid();
1078 void QDeclarativePathQuad::addToPath(QPainterPath &path, const QDeclarativePathData &data)
1080 const QPointF &prevPoint = path.currentPosition();
1081 QPointF controlPoint(hasRelativeControlX() ? prevPoint.x() + relativeControlX() : controlX(),
1082 hasRelativeControlY() ? prevPoint.y() + relativeControlY() : controlY());
1083 path.quadTo(controlPoint, positionForCurve(data, path.currentPosition()));
1086 /****************************************************************************/
1089 \qmlclass PathCubic QDeclarativePathCubic
1090 \inqmlmodule QtQuick 2
1091 \ingroup qml-view-elements
1092 \brief The PathCubic defines a cubic Bezier curve with two control points.
1094 The following QML produces the path shown below:
1097 \o \image declarative-pathcubic.png
1101 startX: 20; startY: 0
1104 control1X: -10; control1Y: 90
1105 control2X: 210; control2Y: 90
1111 \sa Path, PathQuad, PathLine, PathArc, PathCurve, PathSvg
1115 \qmlproperty real QtQuick2::PathCubic::x
1116 \qmlproperty real QtQuick2::PathCubic::y
1118 Defines the end point of the curve.
1120 \sa relativeX, relativeY
1124 \qmlproperty real QtQuick2::PathCubic::relativeX
1125 \qmlproperty real QtQuick2::PathCubic::relativeY
1127 Defines the end point of the curve relative to its start.
1129 If both a relative and absolute end position are specified for a single axis, the relative
1130 position will be used.
1132 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1139 \qmlproperty real QtQuick2::PathCubic::control1X
1140 \qmlproperty real QtQuick2::PathCubic::control1Y
1142 Defines the position of the first control point.
1144 qreal QDeclarativePathCubic::control1X() const
1149 void QDeclarativePathCubic::setControl1X(qreal x)
1151 if (_control1X != x) {
1153 emit control1XChanged();
1158 qreal QDeclarativePathCubic::control1Y() const
1163 void QDeclarativePathCubic::setControl1Y(qreal y)
1165 if (_control1Y != y) {
1167 emit control1YChanged();
1173 \qmlproperty real QtQuick2::PathCubic::control2X
1174 \qmlproperty real QtQuick2::PathCubic::control2Y
1176 Defines the position of the second control point.
1178 qreal QDeclarativePathCubic::control2X() const
1183 void QDeclarativePathCubic::setControl2X(qreal x)
1185 if (_control2X != x) {
1187 emit control2XChanged();
1192 qreal QDeclarativePathCubic::control2Y() const
1197 void QDeclarativePathCubic::setControl2Y(qreal y)
1199 if (_control2Y != y) {
1201 emit control2YChanged();
1207 \qmlproperty real QtQuick2::PathCubic::relativeControl1X
1208 \qmlproperty real QtQuick2::PathCubic::relativeControl1Y
1209 \qmlproperty real QtQuick2::PathCubic::relativeControl2X
1210 \qmlproperty real QtQuick2::PathCubic::relativeControl2Y
1212 Defines the positions of the control points relative to the curve's start.
1214 If both a relative and absolute control position are specified for a control point's axis, the relative
1215 position will be used.
1217 Relative and absolute positions can be mixed, for example it is valid to set a relative control1 x
1218 and an absolute control1 y.
1220 \sa control1X, control1Y, control2X, control2Y
1223 qreal QDeclarativePathCubic::relativeControl1X() const
1225 return _relativeControl1X;
1228 void QDeclarativePathCubic::setRelativeControl1X(qreal x)
1230 if (_relativeControl1X.isNull || _relativeControl1X != x) {
1231 _relativeControl1X = x;
1232 emit relativeControl1XChanged();
1237 bool QDeclarativePathCubic::hasRelativeControl1X()
1239 return _relativeControl1X.isValid();
1242 qreal QDeclarativePathCubic::relativeControl1Y() const
1244 return _relativeControl1Y;
1247 void QDeclarativePathCubic::setRelativeControl1Y(qreal y)
1249 if (_relativeControl1Y.isNull || _relativeControl1Y != y) {
1250 _relativeControl1Y = y;
1251 emit relativeControl1YChanged();
1256 bool QDeclarativePathCubic::hasRelativeControl1Y()
1258 return _relativeControl1Y.isValid();
1261 qreal QDeclarativePathCubic::relativeControl2X() const
1263 return _relativeControl2X;
1266 void QDeclarativePathCubic::setRelativeControl2X(qreal x)
1268 if (_relativeControl2X.isNull || _relativeControl2X != x) {
1269 _relativeControl2X = x;
1270 emit relativeControl2XChanged();
1275 bool QDeclarativePathCubic::hasRelativeControl2X()
1277 return _relativeControl2X.isValid();
1280 qreal QDeclarativePathCubic::relativeControl2Y() const
1282 return _relativeControl2Y;
1285 void QDeclarativePathCubic::setRelativeControl2Y(qreal y)
1287 if (_relativeControl2Y.isNull || _relativeControl2Y != y) {
1288 _relativeControl2Y = y;
1289 emit relativeControl2YChanged();
1294 bool QDeclarativePathCubic::hasRelativeControl2Y()
1296 return _relativeControl2Y.isValid();
1299 void QDeclarativePathCubic::addToPath(QPainterPath &path, const QDeclarativePathData &data)
1301 const QPointF &prevPoint = path.currentPosition();
1302 QPointF controlPoint1(hasRelativeControl1X() ? prevPoint.x() + relativeControl1X() : control1X(),
1303 hasRelativeControl1Y() ? prevPoint.y() + relativeControl1Y() : control1Y());
1304 QPointF controlPoint2(hasRelativeControl2X() ? prevPoint.x() + relativeControl2X() : control2X(),
1305 hasRelativeControl2Y() ? prevPoint.y() + relativeControl2Y() : control2Y());
1306 path.cubicTo(controlPoint1, controlPoint2, positionForCurve(data, path.currentPosition()));
1309 /****************************************************************************/
1312 \qmlclass PathCurve QDeclarativePathCurve
1313 \inqmlmodule QtQuick 2
1314 \ingroup qml-view-elements
1315 \brief The PathCurve defines a point on a Catmull-Rom curve.
1317 PathCurve provides an easy way to specify a curve passing directly through a set of points.
1318 Typically multiple PathCurves are used in a series, as the following example demonstrates:
1320 \snippet doc/src/snippets/declarative/path/basiccurve.qml 0
1322 This example produces the following path (with the starting point and PathCurve points
1323 highlighted in red):
1325 \image declarative-pathcurve.png
1327 \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathSvg
1331 \qmlproperty real QtQuick2::PathCurve::x
1332 \qmlproperty real QtQuick2::PathCurve::y
1334 Defines the end point of the curve.
1336 \sa relativeX, relativeY
1340 \qmlproperty real QtQuick2::PathCurve::relativeX
1341 \qmlproperty real QtQuick2::PathCurve::relativeY
1343 Defines the end point of the curve relative to its start.
1345 If both a relative and absolute end position are specified for a single axis, the relative
1346 position will be used.
1348 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1354 inline QPointF previousPathPosition(const QPainterPath &path)
1356 int count = path.elementCount();
1360 int index = path.elementAt(count-1).type == QPainterPath::CurveToDataElement ? count - 4 : count - 2;
1361 return index > -1 ? QPointF(path.elementAt(index)) : path.pointAtPercent(0);
1364 void QDeclarativePathCatmullRomCurve::addToPath(QPainterPath &path, const QDeclarativePathData &data)
1366 //here we convert catmull-rom spline to bezier for use in QPainterPath.
1367 //basic conversion algorithm:
1368 // catmull-rom points * inverse bezier matrix * catmull-rom matrix = bezier points
1369 //each point in the catmull-rom spline produces a bezier endpoint + 2 control points
1370 //calculations for each point use a moving window of 4 points
1371 // (previous 2 points + current point + next point)
1372 QPointF prevFar, prev, point, next;
1374 //get previous points
1375 int index = data.index - 1;
1376 QDeclarativeCurve *curve = index == -1 ? 0 : data.curves.at(index);
1377 if (qobject_cast<QDeclarativePathCatmullRomCurve*>(curve)) {
1378 prev = path.currentPosition();
1379 prevFar = previousPathPosition(path);
1381 prev = path.currentPosition();
1382 bool prevFarSet = false;
1383 if (index == -1 && data.curves.count() > 1) {
1384 if (qobject_cast<QDeclarativePathCatmullRomCurve*>(data.curves.at(data.curves.count()-1))) {
1385 //TODO: profile and optimize
1387 QDeclarativePathData loopData;
1388 loopData.endPoint = data.endPoint;
1389 loopData.curves = data.curves;
1390 for (int i = data.index; i < data.curves.count(); ++i) {
1392 pos = positionForCurve(loopData, pos);
1393 if (i == data.curves.count()-2)
1396 if (pos == QPointF(path.elementAt(0))) {
1397 //this is a closed path starting and ending with catmull-rom segments.
1398 //we try to smooth the join point
1408 point = positionForCurve(data, path.currentPosition());
1411 index = data.index + 1;
1412 if (index < data.curves.count() && qobject_cast<QDeclarativePathCatmullRomCurve*>(data.curves.at(index))) {
1413 QDeclarativePathData nextData;
1414 nextData.index = index;
1415 nextData.endPoint = data.endPoint;
1416 nextData.curves = data.curves;
1417 next = positionForCurve(nextData, point);
1419 if (point == QPointF(path.elementAt(0)) && qobject_cast<QDeclarativePathCatmullRomCurve*>(data.curves.at(0))) {
1420 //this is a closed path starting and ending with catmull-rom segments.
1421 //we try to smooth the join point
1422 next = QPointF(path.elementAt(3)); //the first catmull-rom point
1428 full conversion matrix (inverse bezier * catmull-rom):
1429 0.000, 1.000, 0.000, 0.000,
1430 -0.167, 1.000, 0.167, 0.000,
1431 0.000, 0.167, 1.000, -0.167,
1432 0.000, 0.000, 1.000, 0.000
1434 conversion doesn't require full matrix multiplication,
1435 so below we simplify
1437 QPointF control1(prevFar.x() * qreal(-0.167) +
1439 point.x() * qreal(0.167),
1440 prevFar.y() * qreal(-0.167) +
1442 point.y() * qreal(0.167));
1444 QPointF control2(prev.x() * qreal(0.167) +
1446 next.x() * qreal(-0.167),
1447 prev.y() * qreal(0.167) +
1449 next.y() * qreal(-0.167));
1451 path.cubicTo(control1, control2, point);
1454 /****************************************************************************/
1457 \qmlclass PathArc QDeclarativePathArc
1458 \inqmlmodule QtQuick 2
1459 \ingroup qml-view-elements
1460 \brief The PathArc defines an arc with the given radius.
1462 PathArc provides a simple way of specifying an arc that ends at a given position
1463 and uses the specified radius. It is modeled after the SVG elliptical arc command.
1465 The following QML produces the path shown below:
1468 \o \image declarative-patharc.png
1469 \o \snippet doc/src/snippets/declarative/path/basicarc.qml 0
1472 Note that a single PathArc cannot be used to specify a circle. Instead, you can
1473 use two PathArc elements, each specifying half of the circle.
1475 \sa Path, PathLine, PathQuad, PathCubic, PathCurve, PathSvg
1479 \qmlproperty real QtQuick2::PathArc::x
1480 \qmlproperty real QtQuick2::PathArc::y
1482 Defines the end point of the arc.
1484 \sa relativeX, relativeY
1488 \qmlproperty real QtQuick2::PathArc::relativeX
1489 \qmlproperty real QtQuick2::PathArc::relativeY
1491 Defines the end point of the arc relative to its start.
1493 If both a relative and absolute end position are specified for a single axis, the relative
1494 position will be used.
1496 Relative and absolute positions can be mixed, for example it is valid to set a relative x
1503 \qmlproperty real QtQuick2::PathArc::radiusX
1504 \qmlproperty real QtQuick2::PathArc::radiusY
1506 Defines the radius of the arc.
1508 The following QML demonstrates how different radius values can be used to change
1509 the shape of the arc:
1512 \o \image declarative-arcradius.png
1513 \o \snippet doc/src/snippets/declarative/path/arcradius.qml 0
1517 qreal QDeclarativePathArc::radiusX() const
1522 void QDeclarativePathArc::setRadiusX(qreal radius)
1524 if (_radiusX == radius)
1528 emit radiusXChanged();
1531 qreal QDeclarativePathArc::radiusY() const
1536 void QDeclarativePathArc::setRadiusY(qreal radius)
1538 if (_radiusY == radius)
1542 emit radiusYChanged();
1546 \qmlproperty bool QtQuick2::PathArc::useLargeArc
1547 Whether to use a large arc as defined by the arc points.
1549 Given fixed start and end positions, radius, and direction,
1550 there are two possible arcs that can fit the data. useLargeArc
1551 is used to distinguish between these. For example, the following
1552 QML can produce either of the two illustrated arcs below by
1553 changing the value of useLargeArc.
1557 \o \image declarative-largearc.png
1558 \o \snippet doc/src/snippets/declarative/path/largearc.qml 0
1561 The default value is false.
1564 bool QDeclarativePathArc::useLargeArc() const
1566 return _useLargeArc;
1569 void QDeclarativePathArc::setUseLargeArc(bool largeArc)
1571 if (_useLargeArc == largeArc)
1574 _useLargeArc = largeArc;
1575 emit useLargeArcChanged();
1579 \qmlproperty enum QtQuick2::PathArc::direction
1581 Defines the direction of the arc. Possible values are
1582 PathArc.Clockwise (default) and PathArc.Counterclockwise.
1584 The following QML can produce either of the two illustrated arcs below
1585 by changing the value of direction.
1588 \o \image declarative-arcdirection.png
1589 \o \snippet doc/src/snippets/declarative/path/arcdirection.qml 0
1595 QDeclarativePathArc::ArcDirection QDeclarativePathArc::direction() const
1600 void QDeclarativePathArc::setDirection(ArcDirection direction)
1602 if (_direction == direction)
1605 _direction = direction;
1606 emit directionChanged();
1609 void QDeclarativePathArc::addToPath(QPainterPath &path, const QDeclarativePathData &data)
1611 const QPointF &startPoint = path.currentPosition();
1612 const QPointF &endPoint = positionForCurve(data, startPoint);
1613 QDeclarativeSvgParser::pathArc(path,
1618 _direction == Clockwise ? 1 : 0,
1621 startPoint.x(), startPoint.y());
1624 /****************************************************************************/
1627 \qmlclass PathSvg QDeclarativePathSvg
1628 \inqmlmodule QtQuick 2
1629 \ingroup qml-view-elements
1630 \brief The PathSvg defines a path using an SVG path data string.
1632 The following QML produces the path shown below:
1635 \o \image declarative-pathsvg.png
1639 startX: 50; startY: 50
1640 PathSvg { path: "L 150 50 L 100 150 z" }
1645 \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathCurve
1649 \qmlproperty string QtQuick2::PathSvg::path
1651 The SVG path data string specifying the path.
1653 See \l {http://www.w3.org/TR/SVG/paths.html#PathData}{W3C SVG Path Data}
1654 for more details on this format.
1657 QString QDeclarativePathSvg::path() const
1662 void QDeclarativePathSvg::setPath(const QString &path)
1671 void QDeclarativePathSvg::addToPath(QPainterPath &path, const QDeclarativePathData &)
1673 QDeclarativeSvgParser::parsePathDataFast(_path, path);
1676 /****************************************************************************/
1679 \qmlclass PathPercent QDeclarativePathPercent
1680 \inqmlmodule QtQuick 2
1681 \ingroup qml-view-elements
1682 \brief The PathPercent manipulates the way a path is interpreted.
1684 PathPercent allows you to manipulate the spacing between items on a
1685 PathView's path. You can use it to bunch together items on part of
1686 the path, and spread them out on other parts of the path.
1688 The examples below show the normal distribution of items along a path
1689 compared to a distribution which places 50% of the items along the
1690 PathLine section of the path.
1693 \o \image declarative-nopercent.png
1699 startX: 20; startY: 0
1700 PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
1701 PathLine { x: 150; y: 80 }
1702 PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
1707 \o \image declarative-percent.png
1713 startX: 20; startY: 0
1714 PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
1715 PathPercent { value: 0.25 }
1716 PathLine { x: 150; y: 80 }
1717 PathPercent { value: 0.75 }
1718 PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
1719 PathPercent { value: 1 }
1729 \qmlproperty real QtQuick2::PathPercent::value
1730 The proportion of items that should be laid out up to this point.
1732 This value should always be higher than the last value specified
1733 by a PathPercent at a previous position in the Path.
1735 In the following example we have a Path made up of three PathLines.
1736 Normally, the items of the PathView would be laid out equally along
1737 this path, with an equal number of items per line segment. PathPercent
1738 allows us to specify that the first and third lines should each hold
1739 10% of the laid out items, while the second line should hold the remaining
1746 startX: 0; startY: 0
1747 PathLine { x:100; y: 0; }
1748 PathPercent { value: 0.1 }
1749 PathLine { x: 100; y: 100 }
1750 PathPercent { value: 0.9 }
1751 PathLine { x: 100; y: 0 }
1752 PathPercent { value: 1 }
1758 qreal QDeclarativePathPercent::value() const
1763 void QDeclarativePathPercent::setValue(qreal value)
1765 if (_value != value) {
1767 emit valueChanged();