1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "QtQuick1/private/qdeclarativepath_p.h"
43 #include "QtQuick1/private/qdeclarativepath_p_p.h"
48 #include <private/qbezier_p.h>
49 #include <QtCore/qmath.h>
50 #include <QtCore/qnumeric.h>
57 \qmlclass PathElement QDeclarative1PathElement
58 \inqmlmodule QtQuick 1
59 \ingroup qml-view-elements
61 \brief PathElement is the base path type.
63 This type is the base for all path types. It cannot
66 \sa Path, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic
70 \qmlclass Path QDeclarative1Path
71 \inqmlmodule QtQuick 1
72 \ingroup qml-view-elements
74 \brief A Path object defines a path for use by \l PathView.
76 A Path is composed of one or more path segments - PathLine, PathQuad,
79 The spacing of the items along the Path can be adjusted via a
82 PathAttribute allows named attributes with values to be defined
85 \sa PathView, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic
87 QDeclarative1Path::QDeclarative1Path(QObject *parent)
88 : QObject(*(new QDeclarative1PathPrivate), parent)
92 QDeclarative1Path::~QDeclarative1Path()
97 \qmlproperty real QtQuick1::Path::startX
98 \qmlproperty real QtQuick1::Path::startY
99 These properties hold the starting position of the path.
101 qreal QDeclarative1Path::startX() const
103 Q_D(const QDeclarative1Path);
107 void QDeclarative1Path::setStartX(qreal x)
109 Q_D(QDeclarative1Path);
110 if (qFuzzyCompare(x, d->startX))
113 emit startXChanged();
117 qreal QDeclarative1Path::startY() const
119 Q_D(const QDeclarative1Path);
123 void QDeclarative1Path::setStartY(qreal y)
125 Q_D(QDeclarative1Path);
126 if (qFuzzyCompare(y, d->startY))
129 emit startYChanged();
134 \qmlproperty bool QtQuick1::Path::closed
135 This property holds whether the start and end of the path are identical.
137 bool QDeclarative1Path::isClosed() const
139 Q_D(const QDeclarative1Path);
144 \qmlproperty list<PathElement> QtQuick1::Path::pathElements
145 This property holds the objects composing the path.
149 A path can contain the following path objects:
151 \i \l PathLine - a straight line to a given position.
152 \i \l PathQuad - a quadratic Bezier curve to a given position with a control point.
153 \i \l PathCubic - a cubic Bezier curve to a given position with two control points.
154 \i \l PathAttribute - an attribute at a given position in the path.
155 \i \l PathPercent - a way to spread out items along various segments of the path.
158 \snippet doc/src/snippets/qtquick1/pathview/pathattributes.qml 2
161 QDeclarativeListProperty<QDeclarative1PathElement> QDeclarative1Path::pathElements()
163 Q_D(QDeclarative1Path);
164 return QDeclarativeListProperty<QDeclarative1PathElement>(this, d->_pathElements);
167 void QDeclarative1Path::interpolate(int idx, const QString &name, qreal value)
169 Q_D(QDeclarative1Path);
174 qreal lastPercent = 0;
175 int search = idx - 1;
177 const AttributePoint &point = d->_attributePoints.at(search);
178 if (point.values.contains(name)) {
179 lastValue = point.values.value(name);
180 lastPercent = point.origpercent;
188 const AttributePoint &curPoint = d->_attributePoints.at(idx);
190 for (int ii = search; ii < idx; ++ii) {
191 AttributePoint &point = d->_attributePoints[ii];
193 qreal val = lastValue + (value - lastValue) * (point.origpercent - lastPercent) / (curPoint.origpercent - lastPercent);
194 point.values.insert(name, val);
198 void QDeclarative1Path::endpoint(const QString &name)
200 Q_D(QDeclarative1Path);
201 const AttributePoint &first = d->_attributePoints.first();
202 qreal val = first.values.value(name);
203 for (int ii = d->_attributePoints.count() - 1; ii >= 0; ii--) {
204 const AttributePoint &point = d->_attributePoints.at(ii);
205 if (point.values.contains(name)) {
206 for (int jj = ii + 1; jj < d->_attributePoints.count(); ++jj) {
207 AttributePoint &setPoint = d->_attributePoints[jj];
208 setPoint.values.insert(name, val);
215 void QDeclarative1Path::processPath()
217 Q_D(QDeclarative1Path);
219 if (!d->componentComplete)
222 d->_pointCache.clear();
223 d->_attributePoints.clear();
224 d->_path = QPainterPath();
226 AttributePoint first;
227 for (int ii = 0; ii < d->_attributes.count(); ++ii)
228 first.values[d->_attributes.at(ii)] = 0;
229 d->_attributePoints << first;
231 d->_path.moveTo(d->startX, d->startY);
233 QDeclarative1Curve *lastCurve = 0;
234 foreach (QDeclarative1PathElement *pathElement, d->_pathElements) {
235 if (QDeclarative1Curve *curve = qobject_cast<QDeclarative1Curve *>(pathElement)) {
236 curve->addToPath(d->_path);
238 p.origpercent = d->_path.length();
239 d->_attributePoints << p;
241 } else if (QDeclarative1PathAttribute *attribute = qobject_cast<QDeclarative1PathAttribute *>(pathElement)) {
242 AttributePoint &point = d->_attributePoints.last();
243 point.values[attribute->name()] = attribute->value();
244 interpolate(d->_attributePoints.count() - 1, attribute->name(), attribute->value());
245 } else if (QDeclarative1PathPercent *percent = qobject_cast<QDeclarative1PathPercent *>(pathElement)) {
246 AttributePoint &point = d->_attributePoints.last();
247 point.values[QLatin1String("_qfx_percent")] = percent->value();
248 interpolate(d->_attributePoints.count() - 1, QLatin1String("_qfx_percent"), percent->value());
253 const AttributePoint &last = d->_attributePoints.last();
254 for (int ii = 0; ii < d->_attributes.count(); ++ii) {
255 if (!last.values.contains(d->_attributes.at(ii)))
256 endpoint(d->_attributes.at(ii));
260 qreal length = d->_path.length();
261 qreal prevpercent = 0;
262 qreal prevorigpercent = 0;
263 for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
264 const AttributePoint &point = d->_attributePoints.at(ii);
265 if (point.values.contains(QLatin1String("_qfx_percent"))) { //special string for QDeclarative1PathPercent
267 qreal scale = (d->_attributePoints[ii].origpercent/length - prevorigpercent) /
268 (point.values.value(QLatin1String("_qfx_percent"))-prevpercent);
269 d->_attributePoints[ii].scale = scale;
271 d->_attributePoints[ii].origpercent /= length;
272 d->_attributePoints[ii].percent = point.values.value(QLatin1String("_qfx_percent"));
273 prevorigpercent = d->_attributePoints[ii].origpercent;
274 prevpercent = d->_attributePoints[ii].percent;
276 d->_attributePoints[ii].origpercent /= length;
277 d->_attributePoints[ii].percent = d->_attributePoints[ii].origpercent;
281 d->closed = lastCurve && d->startX == lastCurve->x() && d->startY == lastCurve->y();
286 void QDeclarative1Path::classBegin()
288 Q_D(QDeclarative1Path);
289 d->componentComplete = false;
292 void QDeclarative1Path::componentComplete()
294 Q_D(QDeclarative1Path);
296 d->componentComplete = true;
298 // First gather up all the attributes
299 foreach (QDeclarative1PathElement *pathElement, d->_pathElements) {
300 if (QDeclarative1PathAttribute *attribute =
301 qobject_cast<QDeclarative1PathAttribute *>(pathElement))
302 attrs.insert(attribute->name());
304 d->_attributes = attrs.toList();
308 foreach (QDeclarative1PathElement *pathElement, d->_pathElements)
309 connect(pathElement, SIGNAL(changed()), this, SLOT(processPath()));
312 QPainterPath QDeclarative1Path::path() const
314 Q_D(const QDeclarative1Path);
318 QStringList QDeclarative1Path::attributes() const
320 Q_D(const QDeclarative1Path);
321 if (!d->componentComplete) {
324 // First gather up all the attributes
325 foreach (QDeclarative1PathElement *pathElement, d->_pathElements) {
326 if (QDeclarative1PathAttribute *attribute =
327 qobject_cast<QDeclarative1PathAttribute *>(pathElement))
328 attrs.insert(attribute->name());
330 return attrs.toList();
332 return d->_attributes;
335 static inline QBezier nextBezier(const QPainterPath &path, int *from, qreal *bezLength)
337 const int lastElement = path.elementCount() - 1;
338 for (int i=*from; i <= lastElement; ++i) {
339 const QPainterPath::Element &e = path.elementAt(i);
342 case QPainterPath::MoveToElement:
344 case QPainterPath::LineToElement:
346 QLineF line(path.elementAt(i-1), e);
347 *bezLength = line.length();
348 QPointF a = path.elementAt(i-1);
349 QPointF delta = e - a;
351 return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
353 case QPainterPath::CurveToElement:
355 QBezier b = QBezier::fromPoints(path.elementAt(i-1),
358 path.elementAt(i+2));
359 *bezLength = b.length();
372 void QDeclarative1Path::createPointCache() const
374 Q_D(const QDeclarative1Path);
375 qreal pathLength = d->_path.length();
376 if (pathLength <= 0 || qIsNaN(pathLength))
378 // more points means less jitter between items as they move along the
379 // path, but takes longer to generate
380 const int points = qCeil(pathLength*5);
381 const int lastElement = d->_path.elementCount() - 1;
382 d->_pointCache.resize(points+1);
386 QBezier currBez = nextBezier(d->_path, &currElement, &bezLength);
387 qreal currLength = bezLength;
388 qreal epc = currLength / pathLength;
390 for (int i = 0; i < d->_pointCache.size(); i++) {
391 //find which set we are in
392 qreal prevPercent = 0;
393 qreal prevOrigPercent = 0;
394 for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
395 qreal percent = qreal(i)/points;
396 const AttributePoint &point = d->_attributePoints.at(ii);
397 if (percent < point.percent || ii == d->_attributePoints.count() - 1) { //### || is special case for very last item
398 qreal elementPercent = (percent - prevPercent);
400 qreal spc = prevOrigPercent + elementPercent * point.scale;
403 if (currElement > lastElement)
405 currBez = nextBezier(d->_path, &currElement, &bezLength);
406 if (bezLength == 0.0) {
407 currLength = pathLength;
411 currLength += bezLength;
412 epc = currLength / pathLength;
414 qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength;
415 d->_pointCache[i] = currBez.pointAt(qBound(qreal(0), realT, qreal(1)));
418 prevOrigPercent = point.origpercent;
419 prevPercent = point.percent;
424 QPointF QDeclarative1Path::pointAt(qreal p) const
426 Q_D(const QDeclarative1Path);
427 if (d->_pointCache.isEmpty()) {
429 if (d->_pointCache.isEmpty())
432 int idx = qRound(p*d->_pointCache.size());
433 if (idx >= d->_pointCache.size())
434 idx = d->_pointCache.size() - 1;
437 return d->_pointCache.at(idx);
440 qreal QDeclarative1Path::attributeAt(const QString &name, qreal percent) const
442 Q_D(const QDeclarative1Path);
443 if (percent < 0 || percent > 1)
446 for (int ii = 0; ii < d->_attributePoints.count(); ++ii) {
447 const AttributePoint &point = d->_attributePoints.at(ii);
449 if (point.percent == percent) {
450 return point.values.value(name);
451 } else if (point.percent > percent) {
453 ii?(d->_attributePoints.at(ii - 1).values.value(name)):0;
455 ii?(d->_attributePoints.at(ii - 1).percent):0;
456 qreal curValue = point.values.value(name);
457 qreal curPercent = point.percent;
459 return lastValue + (curValue - lastValue) * (percent - lastPercent) / (curPercent - lastPercent);
466 /****************************************************************************/
468 qreal QDeclarative1Curve::x() const
473 void QDeclarative1Curve::setX(qreal x)
482 qreal QDeclarative1Curve::y() const
487 void QDeclarative1Curve::setY(qreal y)
496 /****************************************************************************/
499 \qmlclass PathAttribute QDeclarative1PathAttribute
500 \inqmlmodule QtQuick 1
501 \ingroup qml-view-elements
503 \brief The PathAttribute allows setting an attribute at a given position in a Path.
505 The PathAttribute object allows attributes consisting of a name and
506 a value to be specified for various points along a path. The
507 attributes are exposed to the delegate as
508 \l{qdeclarativeintroduction.html#attached-properties} {Attached Properties}.
509 The value of an attribute at any particular point along the path is interpolated
510 from the PathAttributes bounding that point.
512 The example below shows a path with the items scaled to 30% with
513 opacity 50% at the top of the path and scaled 100% with opacity
514 100% at the bottom. Note the use of the PathView.iconScale and
515 PathView.iconOpacity attached properties to set the scale and opacity
520 \o \image declarative-pathattribute.png
522 \snippet doc/src/snippets/qtquick1/pathview/pathattributes.qml 0
523 (see the PathView documentation for the specification of ContactModel.qml
524 used for ContactModel above.)
532 \qmlproperty string QtQuick1::PathAttribute::name
533 This property holds the name of the attribute to change.
535 This attribute will be available to the delegate as PathView.<name>
537 Note that using an existing Item property name such as "opacity" as an
538 attribute is allowed. This is because path attributes add a new
539 \l{qdeclarativeintroduction.html#attached-properties} {Attached Property}
540 which in no way clashes with existing properties.
544 the name of the attribute to change.
547 QString QDeclarative1PathAttribute::name() const
552 void QDeclarative1PathAttribute::setName(const QString &name)
561 \qmlproperty real QtQuick1::PathAttribute::value
562 This property holds the value for the attribute.
564 The value specified can be used to influence the visual appearance
565 of an item along the path. For example, the following Path specifies
566 an attribute named \e itemRotation, which has the value \e 0 at the
567 beginning of the path, and the value 90 at the end of the path.
573 PathAttribute { name: "itemRotation"; value: 0 }
574 PathLine { x: 100; y: 100 }
575 PathAttribute { name: "itemRotation"; value: 90 }
579 In our delegate, we can then bind the \e rotation property to the
580 \l{qdeclarativeintroduction.html#attached-properties} {Attached Property}
581 \e PathView.itemRotation created for this attribute.
585 width: 10; height: 10
586 rotation: PathView.itemRotation
590 As each item is positioned along the path, it will be rotated accordingly:
591 an item at the beginning of the path with be not be rotated, an item at
592 the end of the path will be rotated 90 degrees, and an item mid-way along
593 the path will be rotated 45 degrees.
597 the new value of the attribute.
599 qreal QDeclarative1PathAttribute::value() const
604 void QDeclarative1PathAttribute::setValue(qreal value)
606 if (_value != value) {
613 /****************************************************************************/
616 \qmlclass PathLine QDeclarative1PathLine
617 \inqmlmodule QtQuick 1
618 \ingroup qml-view-elements
620 \brief The PathLine defines a straight line.
622 The example below creates a path consisting of a straight line from
627 startX: 0; startY: 100
628 PathLine { x: 200; y: 100 }
632 \sa Path, PathQuad, PathCubic
636 \qmlproperty real QtQuick1::PathLine::x
637 \qmlproperty real QtQuick1::PathLine::y
639 Defines the end point of the line.
642 void QDeclarative1PathLine::addToPath(QPainterPath &path)
644 path.lineTo(x(), y());
647 /****************************************************************************/
650 \qmlclass PathQuad QDeclarative1PathQuad
651 \inqmlmodule QtQuick 1
652 \ingroup qml-view-elements
654 \brief The PathQuad defines a quadratic Bezier curve with a control point.
656 The following QML produces the path shown below:
659 \o \image declarative-pathquad.png
664 PathQuad { x: 200; y: 0; controlX: 100; controlY: 150 }
669 \sa Path, PathCubic, PathLine
673 \qmlproperty real QtQuick1::PathQuad::x
674 \qmlproperty real QtQuick1::PathQuad::y
676 Defines the end point of the curve.
680 \qmlproperty real QtQuick1::PathQuad::controlX
681 \qmlproperty real QtQuick1::PathQuad::controlY
683 Defines the position of the control point.
687 the x position of the control point.
689 qreal QDeclarative1PathQuad::controlX() const
694 void QDeclarative1PathQuad::setControlX(qreal x)
696 if (_controlX != x) {
698 emit controlXChanged();
705 the y position of the control point.
707 qreal QDeclarative1PathQuad::controlY() const
712 void QDeclarative1PathQuad::setControlY(qreal y)
714 if (_controlY != y) {
716 emit controlYChanged();
721 void QDeclarative1PathQuad::addToPath(QPainterPath &path)
723 path.quadTo(controlX(), controlY(), x(), y());
726 /****************************************************************************/
729 \qmlclass PathCubic QDeclarative1PathCubic
730 \inqmlmodule QtQuick 1
731 \ingroup qml-view-elements
733 \brief The PathCubic defines a cubic Bezier curve with two control points.
735 The following QML produces the path shown below:
738 \o \image declarative-pathcubic.png
742 startX: 20; startY: 0
745 control1X: -10; control1Y: 90
746 control2X: 210; control2Y: 90
752 \sa Path, PathQuad, PathLine
756 \qmlproperty real QtQuick1::PathCubic::x
757 \qmlproperty real QtQuick1::PathCubic::y
759 Defines the end point of the curve.
763 \qmlproperty real QtQuick1::PathCubic::control1X
764 \qmlproperty real QtQuick1::PathCubic::control1Y
766 Defines the position of the first control point.
768 qreal QDeclarative1PathCubic::control1X() const
773 void QDeclarative1PathCubic::setControl1X(qreal x)
775 if (_control1X != x) {
777 emit control1XChanged();
782 qreal QDeclarative1PathCubic::control1Y() const
787 void QDeclarative1PathCubic::setControl1Y(qreal y)
789 if (_control1Y != y) {
791 emit control1YChanged();
797 \qmlproperty real QtQuick1::PathCubic::control2X
798 \qmlproperty real QtQuick1::PathCubic::control2Y
800 Defines the position of the second control point.
802 qreal QDeclarative1PathCubic::control2X() const
807 void QDeclarative1PathCubic::setControl2X(qreal x)
809 if (_control2X != x) {
811 emit control2XChanged();
816 qreal QDeclarative1PathCubic::control2Y() const
821 void QDeclarative1PathCubic::setControl2Y(qreal y)
823 if (_control2Y != y) {
825 emit control2YChanged();
830 void QDeclarative1PathCubic::addToPath(QPainterPath &path)
832 path.cubicTo(control1X(), control1Y(), control2X(), control2Y(), x(), y());
835 /****************************************************************************/
838 \qmlclass PathPercent QDeclarative1PathPercent
839 \inqmlmodule QtQuick 1
840 \ingroup qml-view-elements
842 \brief The PathPercent manipulates the way a path is interpreted.
844 PathPercent allows you to manipulate the spacing between items on a
845 PathView's path. You can use it to bunch together items on part of
846 the path, and spread them out on other parts of the path.
848 The examples below show the normal distrubution of items along a path
849 compared to a distribution which places 50% of the items along the
850 PathLine section of the path.
853 \o \image declarative-nopercent.png
859 startX: 20; startY: 0
860 PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
861 PathLine { x: 150; y: 80 }
862 PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
867 \o \image declarative-percent.png
873 startX: 20; startY: 0
874 PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
875 PathPercent { value: 0.25 }
876 PathLine { x: 150; y: 80 }
877 PathPercent { value: 0.75 }
878 PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 }
879 PathPercent { value: 1 }
889 \qmlproperty real QtQuick1::PathPercent::value
890 The proporation of items that should be laid out up to this point.
892 This value should always be higher than the last value specified
893 by a PathPercent at a previous position in the Path.
895 In the following example we have a Path made up of three PathLines.
896 Normally, the items of the PathView would be laid out equally along
897 this path, with an equal number of items per line segment. PathPercent
898 allows us to specify that the first and third lines should each hold
899 10% of the laid out items, while the second line should hold the remaining
907 PathLine { x:100; y: 0; }
908 PathPercent { value: 0.1 }
909 PathLine { x: 100; y: 100 }
910 PathPercent { value: 0.9 }
911 PathLine { x: 100; y: 0 }
912 PathPercent { value: 1 }
918 qreal QDeclarative1PathPercent::value() const
923 void QDeclarative1PathPercent::setValue(qreal value)
925 if (_value != value) {