1 /****************************************************************************
3 ** Copyright (C) 2011 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 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "private/qdeclarativepath_p.h"
43 #include "private/qdeclarativepath_p_p.h"
48 #include <private/qbezier_p.h>
49 #include <QtCore/qmath.h>
50 #include <QtCore/qnumeric.h>
55 \qmlclass PathElement QDeclarativePathElement
56 \ingroup qml-view-elements
58 \brief PathElement is the base path type.
60 This type is the base for all path types. It cannot
63 \sa Path, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic
67 \qmlclass Path QDeclarativePath
68 \ingroup qml-view-elements
70 \brief A Path object defines a path for use by \l PathView.
72 A Path is composed of one or more path segments - PathLine, PathQuad,
75 The spacing of the items along the Path can be adjusted via a
78 PathAttribute allows named attributes with values to be defined
81 \sa PathView, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic
83 QDeclarativePath::QDeclarativePath(QObject *parent)
84 : QObject(*(new QDeclarativePathPrivate), parent)
88 QDeclarativePath::~QDeclarativePath()
93 \qmlproperty real Path::startX
94 \qmlproperty real Path::startY
95 These properties hold the starting position of the path.
97 qreal QDeclarativePath::startX() const
99 Q_D(const QDeclarativePath);
103 void QDeclarativePath::setStartX(qreal x)
105 Q_D(QDeclarativePath);
106 if (qFuzzyCompare(x, d->startX))
109 emit startXChanged();
113 qreal QDeclarativePath::startY() const
115 Q_D(const QDeclarativePath);
119 void QDeclarativePath::setStartY(qreal y)
121 Q_D(QDeclarativePath);
122 if (qFuzzyCompare(y, d->startY))
125 emit startYChanged();
130 \qmlproperty bool Path::closed
131 This property holds whether the start and end of the path are identical.
133 bool QDeclarativePath::isClosed() const
135 Q_D(const QDeclarativePath);
140 \qmlproperty list<PathElement> Path::pathElements
141 This property holds the objects composing the path.
145 A path can contain the following path objects:
147 \i \l PathLine - a straight line to a given position.
148 \i \l PathQuad - a quadratic Bezier curve to a given position with a control point.
149 \i \l PathCubic - a cubic Bezier curve to a given position with two control points.
150 \i \l PathAttribute - an attribute at a given position in the path.
151 \i \l PathPercent - a way to spread out items along various segments of the path.
154 \snippet doc/src/snippets/declarative/pathview/pathattributes.qml 2
157 QDeclarativeListProperty<QDeclarativePathElement> QDeclarativePath::pathElements()
159 Q_D(QDeclarativePath);
160 return QDeclarativeListProperty<QDeclarativePathElement>(this, d->_pathElements);
163 void QDeclarativePath::interpolate(int idx, const QString &name, qreal value)
165 Q_D(QDeclarativePath);
170 qreal lastPercent = 0;
171 int search = idx - 1;
173 const AttributePoint &point = d->_attributePoints.at(search);
174 if (point.values.contains(name)) {
175 lastValue = point.values.value(name);
176 lastPercent = point.origpercent;
184 const AttributePoint &curPoint = d->_attributePoints.at(idx);
186 for (int ii = search; ii < idx; ++ii) {
187 AttributePoint &point = d->_attributePoints[ii];
189 qreal val = lastValue + (value - lastValue) * (point.origpercent - lastPercent) / (curPoint.origpercent - lastPercent);
190 point.values.insert(name, val);
194 void QDeclarativePath::endpoint(const QString &name)
196 Q_D(QDeclarativePath);
197 const AttributePoint &first = d->_attributePoints.first();
198 qreal val = first.values.value(name);
199 for (int ii = d->_attributePoints.count() - 1; ii >= 0; ii--) {
200 const AttributePoint &point = d->_attributePoints.at(ii);
201 if (point.values.contains(name)) {
202 for (int jj = ii + 1; jj < d->_attributePoints.count(); ++jj) {
203 AttributePoint &setPoint = d->_attributePoints[jj];
204 setPoint.values.insert(name, val);
211 void QDeclarativePath::processPath()
213 Q_D(QDeclarativePath);
215 if (!d->componentComplete)
218 d->_pointCache.clear();
219 d->_attributePoints.clear();
220 d->_path = QPainterPath();
222 AttributePoint first;
223 for (int ii = 0; ii < d->_attributes.count(); ++ii)
224 first.values[d->_attributes.at(ii)] = 0;
225 d->_attributePoints << first;
227 d->_path.moveTo(d->startX, d->startY);
229 QDeclarativeCurve *lastCurve = 0;
230 foreach (QDeclarativePathElement *pathElement, d->_pathElements) {
231 if (QDeclarativeCurve *curve = qobject_cast<QDeclarativeCurve *>(pathElement)) {
232 curve->addToPath(d->_path);
234 p.origpercent = d->_path.length();
235 d->_attributePoints << p;
237 } else if (QDeclarativePathAttribute *attribute = qobject_cast<QDeclarativePathAttribute *>(pathElement)) {
238 AttributePoint &point = d->_attributePoints.last();
239 point.values[attribute->name()] = attribute->value();
240 interpolate(d->_attributePoints.count() - 1, attribute->name(), attribute->value());
241 } else if (QDeclarativePathPercent *percent = qobject_cast<QDeclarativePathPercent *>(pathElement)) {
242 AttributePoint &point = d->_attributePoints.last();
243 point.values[QLatin1String("_qfx_percent")] = percent->value();
244 interpolate(d->_attributePoints.count() - 1, QLatin1String("_qfx_percent"), percent->value());
249 const AttributePoint &last = d->_attributePoints.last();
250 for (int ii = 0; ii < d->_attributes.count(); ++ii) {
251 if (!last.values.contains(d->_attributes.at(ii)))
252 endpoint(d->_attributes.at(ii));
256 qreal length = d->_path.length();
257 qreal prevpercent = 0;
258 qreal prevorigpercent = 0;
259 for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
260 const AttributePoint &point = d->_attributePoints.at(ii);
261 if (point.values.contains(QLatin1String("_qfx_percent"))) { //special string for QDeclarativePathPercent
263 qreal scale = (d->_attributePoints[ii].origpercent/length - prevorigpercent) /
264 (point.values.value(QLatin1String("_qfx_percent"))-prevpercent);
265 d->_attributePoints[ii].scale = scale;
267 d->_attributePoints[ii].origpercent /= length;
268 d->_attributePoints[ii].percent = point.values.value(QLatin1String("_qfx_percent"));
269 prevorigpercent = d->_attributePoints[ii].origpercent;
270 prevpercent = d->_attributePoints[ii].percent;
272 d->_attributePoints[ii].origpercent /= length;
273 d->_attributePoints[ii].percent = d->_attributePoints[ii].origpercent;
277 d->closed = lastCurve && d->startX == lastCurve->x() && d->startY == lastCurve->y();
282 void QDeclarativePath::classBegin()
284 Q_D(QDeclarativePath);
285 d->componentComplete = false;
288 void QDeclarativePath::componentComplete()
290 Q_D(QDeclarativePath);
292 d->componentComplete = true;
294 // First gather up all the attributes
295 foreach (QDeclarativePathElement *pathElement, d->_pathElements) {
296 if (QDeclarativePathAttribute *attribute =
297 qobject_cast<QDeclarativePathAttribute *>(pathElement))
298 attrs.insert(attribute->name());
300 d->_attributes = attrs.toList();
304 foreach (QDeclarativePathElement *pathElement, d->_pathElements)
305 connect(pathElement, SIGNAL(changed()), this, SLOT(processPath()));
308 QPainterPath QDeclarativePath::path() const
310 Q_D(const QDeclarativePath);
314 QStringList QDeclarativePath::attributes() const
316 Q_D(const QDeclarativePath);
317 if (!d->componentComplete) {
320 // First gather up all the attributes
321 foreach (QDeclarativePathElement *pathElement, d->_pathElements) {
322 if (QDeclarativePathAttribute *attribute =
323 qobject_cast<QDeclarativePathAttribute *>(pathElement))
324 attrs.insert(attribute->name());
326 return attrs.toList();
328 return d->_attributes;
331 static inline QBezier nextBezier(const QPainterPath &path, int *from, qreal *bezLength)
333 const int lastElement = path.elementCount() - 1;
334 for (int i=*from; i <= lastElement; ++i) {
335 const QPainterPath::Element &e = path.elementAt(i);
338 case QPainterPath::MoveToElement:
340 case QPainterPath::LineToElement:
342 QLineF line(path.elementAt(i-1), e);
343 *bezLength = line.length();
344 QPointF a = path.elementAt(i-1);
345 QPointF delta = e - a;
347 return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
349 case QPainterPath::CurveToElement:
351 QBezier b = QBezier::fromPoints(path.elementAt(i-1),
354 path.elementAt(i+2));
355 *bezLength = b.length();
368 void QDeclarativePath::createPointCache() const
370 Q_D(const QDeclarativePath);
371 qreal pathLength = d->_path.length();
372 if (pathLength <= 0 || qIsNaN(pathLength))
374 // more points means less jitter between items as they move along the
375 // path, but takes longer to generate
376 const int points = qCeil(pathLength*5);
377 const int lastElement = d->_path.elementCount() - 1;
378 d->_pointCache.resize(points+1);
382 QBezier currBez = nextBezier(d->_path, &currElement, &bezLength);
383 qreal currLength = bezLength;
384 qreal epc = currLength / pathLength;
386 for (int i = 0; i < d->_pointCache.size(); i++) {
387 //find which set we are in
388 qreal prevPercent = 0;
389 qreal prevOrigPercent = 0;
390 for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
391 qreal percent = qreal(i)/points;
392 const AttributePoint &point = d->_attributePoints.at(ii);
393 if (percent < point.percent || ii == d->_attributePoints.count() - 1) { //### || is special case for very last item
394 qreal elementPercent = (percent - prevPercent);
396 qreal spc = prevOrigPercent + elementPercent * point.scale;
399 if (currElement > lastElement)
401 currBez = nextBezier(d->_path, &currElement, &bezLength);
402 if (bezLength == 0.0) {
403 currLength = pathLength;
407 currLength += bezLength;
408 epc = currLength / pathLength;
410 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
411 d->_pointCache[i] = currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
414 prevOrigPercent = point.origpercent;
415 prevPercent = point.percent;
420 QPointF QDeclarativePath::pointAt(qreal p) const
422 Q_D(const QDeclarativePath);
423 if (d->_pointCache.isEmpty()) {
425 if (d->_pointCache.isEmpty())
428 int idx = qRound(p*d->_pointCache.size());
429 if (idx >= d->_pointCache.size())
430 idx = d->_pointCache.size() - 1;
433 return d->_pointCache.at(idx);
436 qreal QDeclarativePath::attributeAt(const QString &name, qreal percent) const
438 Q_D(const QDeclarativePath);
439 if (percent < 0 || percent > 1)
442 for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
443 const AttributePoint &point = d->_attributePoints.at(ii);
445 if (point.percent == percent) {
446 return point.values.value(name);
447 } else if (point.percent > percent) {
449 ii?(d->_attributePoints.at(ii - 1).values.value(name)):0;
451 ii?(d->_attributePoints.at(ii - 1).percent):0;
452 qreal curValue = point.values.value(name);
453 qreal curPercent = point.percent;
455 return lastValue + (curValue - lastValue) * (percent - lastPercent) / (curPercent - lastPercent);
462 /****************************************************************************/
464 qreal QDeclarativeCurve::x() const
469 void QDeclarativeCurve::setX(qreal x)
478 qreal QDeclarativeCurve::y() const
483 void QDeclarativeCurve::setY(qreal y)
492 /****************************************************************************/
495 \qmlclass PathAttribute QDeclarativePathAttribute
496 \ingroup qml-view-elements
498 \brief The PathAttribute allows setting an attribute at a given position in a Path.
500 The PathAttribute object allows attributes consisting of a name and
501 a value to be specified for various points along a path. The
502 attributes are exposed to the delegate as
503 \l{qdeclarativeintroduction.html#attached-properties} {Attached Properties}.
504 The value of an attribute at any particular point along the path is interpolated
505 from the PathAttributes bounding that point.
507 The example below shows a path with the items scaled to 30% with
508 opacity 50% at the top of the path and scaled 100% with opacity
509 100% at the bottom. Note the use of the PathView.iconScale and
510 PathView.iconOpacity attached properties to set the scale and opacity
515 \o \image declarative-pathattribute.png
517 \snippet doc/src/snippets/declarative/pathview/pathattributes.qml 0
518 (see the PathView documentation for the specification of ContactModel.qml
519 used for ContactModel above.)
527 \qmlproperty string PathAttribute::name
528 This property holds the name of the attribute to change.
530 This attribute will be available to the delegate as PathView.<name>
532 Note that using an existing Item property name such as "opacity" as an
533 attribute is allowed. This is because path attributes add a new
534 \l{qdeclarativeintroduction.html#attached-properties} {Attached Property}
535 which in no way clashes with existing properties.
539 the name of the attribute to change.
542 QString QDeclarativePathAttribute::name() const
547 void QDeclarativePathAttribute::setName(const QString &name)
556 \qmlproperty real PathAttribute::value
557 This property holds the value for the attribute.
559 The value specified can be used to influence the visual appearance
560 of an item along the path. For example, the following Path specifies
561 an attribute named \e itemRotation, which has the value \e 0 at the
562 beginning of the path, and the value 90 at the end of the path.
568 PathAttribute { name: "itemRotation"; value: 0 }
569 PathLine { x: 100; y: 100 }
570 PathAttribute { name: "itemRotation"; value: 90 }
574 In our delegate, we can then bind the \e rotation property to the
575 \l{qdeclarativeintroduction.html#attached-properties} {Attached Property}
576 \e PathView.itemRotation created for this attribute.
580 width: 10; height: 10
581 rotation: PathView.itemRotation
585 As each item is positioned along the path, it will be rotated accordingly:
586 an item at the beginning of the path with be not be rotated, an item at
587 the end of the path will be rotated 90 degrees, and an item mid-way along
588 the path will be rotated 45 degrees.
592 the new value of the attribute.
594 qreal QDeclarativePathAttribute::value() const
599 void QDeclarativePathAttribute::setValue(qreal value)
601 if (_value != value) {
608 /****************************************************************************/
611 \qmlclass PathLine QDeclarativePathLine
612 \ingroup qml-view-elements
614 \brief The PathLine defines a straight line.
616 The example below creates a path consisting of a straight line from
621 startX: 0; startY: 100
622 PathLine { x: 200; y: 100 }
626 \sa Path, PathQuad, PathCubic
630 \qmlproperty real PathLine::x
631 \qmlproperty real PathLine::y
633 Defines the end point of the line.
636 void QDeclarativePathLine::addToPath(QPainterPath &path)
638 path.lineTo(x(), y());
641 /****************************************************************************/
644 \qmlclass PathQuad QDeclarativePathQuad
645 \ingroup qml-view-elements
647 \brief The PathQuad defines a quadratic Bezier curve with a control point.
649 The following QML produces the path shown below:
652 \o \image declarative-pathquad.png
657 PathQuad { x: 200; y: 0; controlX: 100; controlY: 150 }
662 \sa Path, PathCubic, PathLine
666 \qmlproperty real PathQuad::x
667 \qmlproperty real PathQuad::y
669 Defines the end point of the curve.
673 \qmlproperty real PathQuad::controlX
674 \qmlproperty real PathQuad::controlY
676 Defines the position of the control point.
680 the x position of the control point.
682 qreal QDeclarativePathQuad::controlX() const
687 void QDeclarativePathQuad::setControlX(qreal x)
689 if (_controlX != x) {
691 emit controlXChanged();
698 the y position of the control point.
700 qreal QDeclarativePathQuad::controlY() const
705 void QDeclarativePathQuad::setControlY(qreal y)
707 if (_controlY != y) {
709 emit controlYChanged();
714 void QDeclarativePathQuad::addToPath(QPainterPath &path)
716 path.quadTo(controlX(), controlY(), x(), y());
719 /****************************************************************************/
722 \qmlclass PathCubic QDeclarativePathCubic
723 \ingroup qml-view-elements
725 \brief The PathCubic defines a cubic Bezier curve with two control points.
727 The following QML produces the path shown below:
730 \o \image declarative-pathcubic.png
734 startX: 20; startY: 0
737 control1X: -10; control1Y: 90
738 control2X: 210; control2Y: 90
744 \sa Path, PathQuad, PathLine
748 \qmlproperty real PathCubic::x
749 \qmlproperty real PathCubic::y
751 Defines the end point of the curve.
755 \qmlproperty real PathCubic::control1X
756 \qmlproperty real PathCubic::control1Y
758 Defines the position of the first control point.
760 qreal QDeclarativePathCubic::control1X() const
765 void QDeclarativePathCubic::setControl1X(qreal x)
767 if (_control1X != x) {
769 emit control1XChanged();
774 qreal QDeclarativePathCubic::control1Y() const
779 void QDeclarativePathCubic::setControl1Y(qreal y)
781 if (_control1Y != y) {
783 emit control1YChanged();
789 \qmlproperty real PathCubic::control2X
790 \qmlproperty real PathCubic::control2Y
792 Defines the position of the second control point.
794 qreal QDeclarativePathCubic::control2X() const
799 void QDeclarativePathCubic::setControl2X(qreal x)
801 if (_control2X != x) {
803 emit control2XChanged();
808 qreal QDeclarativePathCubic::control2Y() const
813 void QDeclarativePathCubic::setControl2Y(qreal y)
815 if (_control2Y != y) {
817 emit control2YChanged();
822 void QDeclarativePathCubic::addToPath(QPainterPath &path)
824 path.cubicTo(control1X(), control1Y(), control2X(), control2Y(), x(), y());
827 /****************************************************************************/
830 \qmlclass PathPercent QDeclarativePathPercent
831 \ingroup qml-view-elements
833 \brief The PathPercent manipulates the way a path is interpreted.
835 PathPercent allows you to manipulate the spacing between items on a
836 PathView's path. You can use it to bunch together items on part of
837 the path, and spread them out on other parts of the path.
839 The examples below show the normal distrubution of items along a path
840 compared to a distribution which places 50% of the items along the
841 PathLine section of the path.
844 \o \image declarative-nopercent.png
850 startX: 20; startY: 0
851 PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
852 PathLine { x: 150; y: 80 }
853 PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
858 \o \image declarative-percent.png
864 startX: 20; startY: 0
865 PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
866 PathPercent { value: 0.25 }
867 PathLine { x: 150; y: 80 }
868 PathPercent { value: 0.75 }
869 PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
870 PathPercent { value: 1 }
880 \qmlproperty real PathPercent::value
881 The proporation of items that should be laid out up to this point.
883 This value should always be higher than the last value specified
884 by a PathPercent at a previous position in the Path.
886 In the following example we have a Path made up of three PathLines.
887 Normally, the items of the PathView would be laid out equally along
888 this path, with an equal number of items per line segment. PathPercent
889 allows us to specify that the first and third lines should each hold
890 10% of the laid out items, while the second line should hold the remaining
898 PathLine { x:100; y: 0; }
899 PathPercent { value: 0.1 }
900 PathLine { x: 100; y: 100 }
901 PathPercent { value: 0.9 }
902 PathLine { x: 100; y: 0 }
903 PathPercent { value: 1 }
909 qreal QDeclarativePathPercent::value() const
914 void QDeclarativePathPercent::setValue(qreal value)
916 if (_value != value) {