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 "qquickpathview_p.h"
43 #include "qquickpathview_p_p.h"
44 #include "qquickcanvas.h"
46 #include <QtQuick/private/qdeclarativestate_p.h>
47 #include <private/qdeclarativeopenmetaobject_p.h>
48 #include <private/qlistmodelinterface_p.h>
49 #include <private/qdeclarativechangeset_p.h>
51 #include <QtGui/qevent.h>
52 #include <QtGui/qevent.h>
53 #include <QtGui/qguiapplication.h>
54 #include <QtGui/qstylehints.h>
55 #include <QtCore/qmath.h>
60 inline qreal qmlMod(qreal x, qreal y)
62 #ifdef QT_USE_MATH_H_FLOATS
63 if (sizeof(qreal) == sizeof(float))
64 return fmodf(float(x), float(y));
70 static QDeclarativeOpenMetaObjectType *qPathViewAttachedType = 0;
72 QQuickPathViewAttached::QQuickPathViewAttached(QObject *parent)
73 : QObject(parent), m_percent(-1), m_view(0), m_onPath(false), m_isCurrent(false)
75 if (qPathViewAttachedType) {
76 m_metaobject = new QDeclarativeOpenMetaObject(this, qPathViewAttachedType);
77 m_metaobject->setCached(true);
79 m_metaobject = new QDeclarativeOpenMetaObject(this);
83 QQuickPathViewAttached::~QQuickPathViewAttached()
87 QVariant QQuickPathViewAttached::value(const QByteArray &name) const
89 return m_metaobject->value(name);
91 void QQuickPathViewAttached::setValue(const QByteArray &name, const QVariant &val)
93 m_metaobject->setValue(name, val);
97 void QQuickPathViewPrivate::init()
101 q->setAcceptedMouseButtons(Qt::LeftButton);
102 q->setFlag(QQuickItem::ItemIsFocusScope);
103 q->setFiltersChildMouseEvents(true);
104 FAST_CONNECT(&tl, SIGNAL(updated()), q, SLOT(ticked()))
105 lastPosTime.invalidate();
106 FAST_CONNECT(&tl, SIGNAL(completed()), q, SLOT(movementEnding()))
109 QQuickItem *QQuickPathViewPrivate::getItem(int modelIndex, qreal z, bool onPath)
112 requestedIndex = modelIndex;
113 requestedOnPath = onPath;
116 QQuickItem *item = model->item(modelIndex, false);
118 QDeclarative_setParent_noEvent(item, q);
119 item->setParentItem(q);
121 qPathViewAttachedType = attType;
122 QQuickPathViewAttached *att = static_cast<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(item));
123 qPathViewAttachedType = 0;
125 att->setOnPath(onPath);
126 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
127 itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
133 void QQuickPathView::createdItem(int index, QQuickItem *item)
136 if (d->requestedIndex != index) {
137 qPathViewAttachedType = d->attachedType();
138 QQuickPathViewAttached *att = static_cast<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(item));
139 qPathViewAttachedType = 0;
142 att->setOnPath(false);
144 item->setParentItem(this);
145 QDeclarative_setParent_noEvent(item, this);
146 d->updateItem(item, index < d->firstIndex ? 0.0 : 1.0);
148 d->requestedIndex = -1;
154 void QQuickPathView::initItem(int index, QQuickItem *item)
157 if (d->requestedIndex == index) {
158 item->setParentItem(this);
159 qPathViewAttachedType = d->attachedType();
160 QQuickPathViewAttached *att = static_cast<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(item));
161 qPathViewAttachedType = 0;
164 qreal percent = d->positionOfIndex(index);
165 foreach (const QString &attr, d->path->attributes())
166 att->setValue(attr.toUtf8(), d->path->attributeAt(attr, percent));
167 item->setZ(d->requestedZ);
169 att->setOnPath(d->requestedOnPath);
174 void QQuickPathViewPrivate::releaseItem(QQuickItem *item)
178 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
179 itemPrivate->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
180 if (model->release(item) == 0) {
181 // item was not destroyed, and we no longer reference it.
182 if (QQuickPathViewAttached *att = attached(item))
183 att->setOnPath(false);
187 QQuickPathViewAttached *QQuickPathViewPrivate::attached(QQuickItem *item)
189 return static_cast<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(item, false));
192 QDeclarativeOpenMetaObjectType *QQuickPathViewPrivate::attachedType()
196 // pre-create one metatype to share with all attached objects
197 attType = new QDeclarativeOpenMetaObjectType(&QQuickPathViewAttached::staticMetaObject, qmlEngine(q));
198 foreach (const QString &attr, path->attributes())
199 attType->createProperty(attr.toUtf8());
205 void QQuickPathViewPrivate::clear()
208 releaseItem(currentItem);
211 for (int i=0; i<items.count(); i++){
212 QQuickItem *p = items[i];
218 void QQuickPathViewPrivate::updateMappedRange()
220 if (model && pathItems != -1 && pathItems < modelCount)
221 mappedRange = qreal(pathItems)/modelCount;
226 qreal QQuickPathViewPrivate::positionOfIndex(qreal index) const
230 if (model && index >= 0 && index < modelCount) {
232 if (haveHighlightRange && highlightRangeMode != QQuickPathView::NoHighlightRange)
233 start = highlightRangeStart;
234 qreal globalPos = index + offset;
235 globalPos = qmlMod(globalPos, qreal(modelCount)) / modelCount;
236 if (pathItems != -1 && pathItems < modelCount) {
237 globalPos += start * mappedRange;
238 globalPos = qmlMod(globalPos, 1.0);
239 if (globalPos < mappedRange)
240 pos = globalPos / mappedRange;
242 pos = qmlMod(globalPos + start, 1.0);
249 void QQuickPathViewPrivate::createHighlight()
252 if (!q->isComponentComplete())
255 bool changed = false;
257 highlightItem->setParentItem(0);
258 highlightItem->deleteLater();
263 QQuickItem *item = 0;
264 if (highlightComponent) {
265 QDeclarativeContext *creationContext = highlightComponent->creationContext();
266 QDeclarativeContext *highlightContext = new QDeclarativeContext(
267 creationContext ? creationContext : qmlContext(q));
268 QObject *nobj = highlightComponent->create(highlightContext);
270 QDeclarative_setParent_noEvent(highlightContext, nobj);
271 item = qobject_cast<QQuickItem *>(nobj);
275 delete highlightContext;
278 item = new QQuickItem;
281 QDeclarative_setParent_noEvent(item, q);
282 item->setParentItem(q);
283 highlightItem = item;
287 emit q->highlightItemChanged();
290 void QQuickPathViewPrivate::updateHighlight()
293 if (!q->isComponentComplete() || !isValid())
296 if (haveHighlightRange && highlightRangeMode == QQuickPathView::StrictlyEnforceRange) {
297 updateItem(highlightItem, highlightRangeStart);
299 qreal target = currentIndex;
302 tl.reset(moveHighlight);
303 moveHighlight.setValue(highlightPosition);
305 const int duration = highlightMoveDuration;
307 if (target - highlightPosition > modelCount/2) {
309 qreal distance = modelCount - target + highlightPosition;
310 tl.move(moveHighlight, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * highlightPosition / distance));
311 tl.set(moveHighlight, modelCount-0.01);
312 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * (modelCount-target) / distance));
313 } else if (target - highlightPosition <= -modelCount/2) {
315 qreal distance = modelCount - highlightPosition + target;
316 tl.move(moveHighlight, modelCount-0.01, QEasingCurve(QEasingCurve::InQuad), int(duration * (modelCount-highlightPosition) / distance));
317 tl.set(moveHighlight, 0.0);
318 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * target / distance));
320 highlightUp = highlightPosition - target < 0;
321 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::InOutQuad), duration);
327 void QQuickPathViewPrivate::setHighlightPosition(qreal pos)
329 if (pos != highlightPosition) {
332 if (haveHighlightRange && highlightRangeMode != QQuickPathView::NoHighlightRange) {
333 start = highlightRangeStart;
334 end = highlightRangeEnd;
337 qreal range = qreal(modelCount);
338 // calc normalized position of highlight relative to offset
339 qreal relativeHighlight = qmlMod(pos + offset, range) / range;
341 if (!highlightUp && relativeHighlight > end * mappedRange) {
342 qreal diff = 1.0 - relativeHighlight;
343 setOffset(offset + diff * range);
344 } else if (highlightUp && relativeHighlight >= (end - start) * mappedRange) {
345 qreal diff = relativeHighlight - (end - start) * mappedRange;
346 setOffset(offset - diff * range - 0.00001);
349 highlightPosition = pos;
350 qreal pathPos = positionOfIndex(pos);
351 updateItem(highlightItem, pathPos);
352 if (QQuickPathViewAttached *att = attached(highlightItem))
353 att->setOnPath(pathPos != -1.0);
357 void QQuickPathView::pathUpdated()
360 QList<QQuickItem*>::iterator it = d->items.begin();
361 while (it != d->items.end()) {
362 QQuickItem *item = *it;
363 if (QQuickPathViewAttached *att = d->attached(item))
370 void QQuickPathViewPrivate::updateItem(QQuickItem *item, qreal percent)
372 if (QQuickPathViewAttached *att = attached(item)) {
373 if (qFuzzyCompare(att->m_percent, percent))
375 att->m_percent = percent;
376 foreach (const QString &attr, path->attributes())
377 att->setValue(attr.toUtf8(), path->attributeAt(attr, percent));
379 QPointF pf = path->pointAt(percent);
380 item->setX(qRound(pf.x() - item->width()/2));
381 item->setY(qRound(pf.y() - item->height()/2));
384 void QQuickPathViewPrivate::regenerate()
387 if (!q->isComponentComplete())
401 \qmlclass PathView QQuickPathView
402 \inqmlmodule QtQuick 2
403 \ingroup qml-view-elements
404 \brief The PathView element lays out model-provided items on a path.
407 A PathView displays data from models created from built-in QML elements like ListModel
408 and XmlListModel, or custom model classes defined in C++ that inherit from
411 The view has a \l model, which defines the data to be displayed, and
412 a \l delegate, which defines how the data should be displayed.
413 The \l delegate is instantiated for each item on the \l path.
414 The items may be flicked to move them along the path.
416 For example, if there is a simple list model defined in a file \c ContactModel.qml like this:
418 \snippet doc/src/snippets/declarative/pathview/ContactModel.qml 0
420 This data can be represented as a PathView, like this:
422 \snippet doc/src/snippets/declarative/pathview/pathview.qml 0
426 (Note the above example uses PathAttribute to scale and modify the
427 opacity of the items as they rotate. This additional code can be seen in the
428 PathAttribute documentation.)
430 PathView does not automatically handle keyboard navigation. This is because
431 the keys to use for navigation will depend upon the shape of the path. Navigation
432 can be added quite simply by setting \c focus to \c true and calling
433 \l decrementCurrentIndex() or \l incrementCurrentIndex(), for example to navigate
434 using the left and right arrow keys:
440 Keys.onLeftPressed: decrementCurrentIndex()
441 Keys.onRightPressed: incrementCurrentIndex()
445 The path view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
447 Delegates are instantiated as needed and may be destroyed at any time.
448 State should \e never be stored in a delegate.
450 PathView attaches a number of properties to the root item of the delegate, for example
451 \c {PathView.isCurrentItem}. In the following example, the root delegate item can access
452 this attached property directly as \c PathView.isCurrentItem, while the child
453 \c nameText object must refer to this property as \c wrapper.PathView.isCurrentItem.
455 \snippet doc/src/snippets/declarative/pathview/pathview.qml 1
457 \bold Note that views do not enable \e clip automatically. If the view
458 is not clipped by another item or the screen, it will be necessary
459 to set \e {clip: true} in order to have the out of view items clipped
462 \sa Path, {declarative/modelviews/pathview}{PathView example}
465 QQuickPathView::QQuickPathView(QQuickItem *parent)
466 : QQuickItem(*(new QQuickPathViewPrivate), parent)
472 QQuickPathView::~QQuickPathView()
477 d->attType->release();
483 \qmlattachedproperty PathView QtQuick2::PathView::view
484 This attached property holds the view that manages this delegate instance.
486 It is attached to each instance of the delegate.
490 \qmlattachedproperty bool QtQuick2::PathView::onPath
491 This attached property holds whether the item is currently on the path.
493 If a pathItemCount has been set, it is possible that some items may
494 be instantiated, but not considered to be currently on the path.
495 Usually, these items would be set invisible, for example:
500 visible: PathView.onPath
506 It is attached to each instance of the delegate.
510 \qmlattachedproperty bool QtQuick2::PathView::isCurrentItem
511 This attached property is true if this delegate is the current item; otherwise false.
513 It is attached to each instance of the delegate.
515 This property may be used to adjust the appearance of the current item.
517 \snippet doc/src/snippets/declarative/pathview/pathview.qml 1
521 \qmlproperty model QtQuick2::PathView::model
522 This property holds the model providing data for the view.
524 The model provides a set of data that is used to create the items for the view.
525 For large or dynamic datasets the model is usually provided by a C++ model object.
526 Models can also be created directly in QML, using the ListModel element.
528 \sa {qmlmodels}{Data Models}
530 QVariant QQuickPathView::model() const
532 Q_D(const QQuickPathView);
533 return d->modelVariant;
536 void QQuickPathView::setModel(const QVariant &model)
539 if (d->modelVariant == model)
543 disconnect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)),
544 this, SLOT(modelUpdated(QDeclarativeChangeSet,bool)));
545 disconnect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*)));
546 disconnect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*)));
547 for (int i=0; i<d->items.count(); i++){
548 QQuickItem *p = d->items[i];
554 d->modelVariant = model;
555 QObject *object = qvariant_cast<QObject*>(model);
556 QQuickVisualModel *vim = 0;
557 if (object && (vim = qobject_cast<QQuickVisualModel *>(object))) {
565 d->model = new QQuickVisualDataModel(qmlContext(this));
567 if (isComponentComplete())
568 static_cast<QQuickVisualDataModel *>(d->model.data())->componentComplete();
570 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
571 dataModel->setModel(model);
575 connect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)),
576 this, SLOT(modelUpdated(QDeclarativeChangeSet,bool)));
577 connect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*)));
578 connect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*)));
579 d->modelCount = d->model->count();
580 if (d->model->count())
581 d->offset = qmlMod(d->offset, qreal(d->model->count()));
583 d->offset = d->model->count() + d->offset;
586 if (d->currentIndex < d->modelCount)
587 setOffset(qmlMod(d->modelCount - d->currentIndex, d->modelCount));
595 \qmlproperty int QtQuick2::PathView::count
596 This property holds the number of items in the model.
598 int QQuickPathView::count() const
600 Q_D(const QQuickPathView);
601 return d->model ? d->modelCount : 0;
605 \qmlproperty Path QtQuick2::PathView::path
606 This property holds the path used to lay out the items.
607 For more information see the \l Path documentation.
609 QDeclarativePath *QQuickPathView::path() const
611 Q_D(const QQuickPathView);
615 void QQuickPathView::setPath(QDeclarativePath *path)
621 disconnect(d->path, SIGNAL(changed()), this, SLOT(pathUpdated()));
623 connect(d->path, SIGNAL(changed()), this, SLOT(pathUpdated()));
624 if (d->isValid() && isComponentComplete()) {
627 d->attType->release();
636 \qmlproperty int QtQuick2::PathView::currentIndex
637 This property holds the index of the current item.
639 int QQuickPathView::currentIndex() const
641 Q_D(const QQuickPathView);
642 return d->currentIndex;
645 void QQuickPathView::setCurrentIndex(int idx)
648 if (d->model && d->modelCount)
649 idx = qAbs(idx % d->modelCount);
650 if (d->model && (idx != d->currentIndex || !d->currentItem)) {
651 if (d->currentItem) {
652 if (QQuickPathViewAttached *att = d->attached(d->currentItem))
653 att->setIsCurrentItem(false);
654 d->releaseItem(d->currentItem);
656 int oldCurrentIdx = d->currentIndex;
657 QQuickItem *oldCurrentItem = d->currentItem;
659 d->moveReason = QQuickPathViewPrivate::SetIndex;
660 d->currentIndex = idx;
662 d->createCurrentItem();
663 if (d->haveHighlightRange && d->highlightRangeMode == QQuickPathView::StrictlyEnforceRange)
665 d->currentItemOffset = d->positionOfIndex(d->currentIndex);
666 d->updateHighlight();
668 if (oldCurrentIdx != d->currentIndex)
669 emit currentIndexChanged();
670 if (oldCurrentItem != d->currentItem)
671 emit currentItemChanged();
675 QQuickItem *QQuickPathView::currentItem() const
677 Q_D(const QQuickPathView);
678 return d->currentItem;
682 \qmlmethod QtQuick2::PathView::incrementCurrentIndex()
684 Increments the current index.
686 \bold Note: methods should only be called after the Component has completed.
688 void QQuickPathView::incrementCurrentIndex()
691 d->moveDirection = QQuickPathViewPrivate::Positive;
692 setCurrentIndex(currentIndex()+1);
696 \qmlmethod QtQuick2::PathView::decrementCurrentIndex()
698 Decrements the current index.
700 \bold Note: methods should only be called after the Component has completed.
702 void QQuickPathView::decrementCurrentIndex()
705 if (d->model && d->modelCount) {
706 int idx = currentIndex()-1;
708 idx = d->modelCount - 1;
709 d->moveDirection = QQuickPathViewPrivate::Negative;
710 setCurrentIndex(idx);
715 \qmlproperty real QtQuick2::PathView::offset
717 The offset specifies how far along the path the items are from their initial positions.
718 This is a real number that ranges from 0.0 to the count of items in the model.
720 qreal QQuickPathView::offset() const
722 Q_D(const QQuickPathView);
726 void QQuickPathView::setOffset(qreal offset)
729 d->setOffset(offset);
733 void QQuickPathViewPrivate::setOffset(qreal o)
737 if (isValid() && q->isComponentComplete()) {
738 offset = qmlMod(o, qreal(modelCount));
740 offset += qreal(modelCount);
745 emit q->offsetChanged();
749 void QQuickPathViewPrivate::setAdjustedOffset(qreal o)
751 setOffset(o+offsetAdj);
755 \qmlproperty Component QtQuick2::PathView::highlight
756 This property holds the component to use as the highlight.
758 An instance of the highlight component will be created for each view.
759 The geometry of the resultant component instance will be managed by the view
760 so as to stay with the current item.
762 The below example demonstrates how to make a simple highlight. Note the use
763 of the \l{PathView::onPath}{PathView.onPath} attached property to ensure that
764 the highlight is hidden when flicked away from the path.
769 visible: PathView.onPath
775 \sa highlightItem, highlightRangeMode
778 QDeclarativeComponent *QQuickPathView::highlight() const
780 Q_D(const QQuickPathView);
781 return d->highlightComponent;
784 void QQuickPathView::setHighlight(QDeclarativeComponent *highlight)
787 if (highlight != d->highlightComponent) {
788 d->highlightComponent = highlight;
789 d->createHighlight();
790 d->updateHighlight();
791 emit highlightChanged();
796 \qmlproperty Item QtQuick2::PathView::highlightItem
798 \c highlightItem holds the highlight item, which was created
799 from the \l highlight component.
803 QQuickItem *QQuickPathView::highlightItem()
805 Q_D(const QQuickPathView);
806 return d->highlightItem;
809 \qmlproperty real QtQuick2::PathView::preferredHighlightBegin
810 \qmlproperty real QtQuick2::PathView::preferredHighlightEnd
811 \qmlproperty enumeration QtQuick2::PathView::highlightRangeMode
813 These properties set the preferred range of the highlight (current item)
814 within the view. The preferred values must be in the range 0.0-1.0.
816 If highlightRangeMode is set to \e PathView.NoHighlightRange
818 If highlightRangeMode is set to \e PathView.ApplyRange the view will
819 attempt to maintain the highlight within the range, however
820 the highlight can move outside of the range at the ends of the path
821 or due to a mouse interaction.
823 If highlightRangeMode is set to \e PathView.StrictlyEnforceRange the highlight will never
824 move outside of the range. This means that the current item will change
825 if a keyboard or mouse action would cause the highlight to move
826 outside of the range.
828 Note that this is the correct way to influence where the
829 current item ends up when the view moves. For example, if you want the
830 currently selected item to be in the middle of the path, then set the
831 highlight range to be 0.5,0.5 and highlightRangeMode to PathView.StrictlyEnforceRange.
832 Then, when the path scrolls,
833 the currently selected item will be the item at that position. This also applies to
834 when the currently selected item changes - it will scroll to within the preferred
835 highlight range. Furthermore, the behaviour of the current item index will occur
836 whether or not a highlight exists.
838 The default value is \e PathView.StrictlyEnforceRange.
840 Note that a valid range requires preferredHighlightEnd to be greater
841 than or equal to preferredHighlightBegin.
843 qreal QQuickPathView::preferredHighlightBegin() const
845 Q_D(const QQuickPathView);
846 return d->highlightRangeStart;
849 void QQuickPathView::setPreferredHighlightBegin(qreal start)
852 if (d->highlightRangeStart == start || start < 0 || start > 1.0)
854 d->highlightRangeStart = start;
855 d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
857 emit preferredHighlightBeginChanged();
860 qreal QQuickPathView::preferredHighlightEnd() const
862 Q_D(const QQuickPathView);
863 return d->highlightRangeEnd;
866 void QQuickPathView::setPreferredHighlightEnd(qreal end)
869 if (d->highlightRangeEnd == end || end < 0 || end > 1.0)
871 d->highlightRangeEnd = end;
872 d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
874 emit preferredHighlightEndChanged();
877 QQuickPathView::HighlightRangeMode QQuickPathView::highlightRangeMode() const
879 Q_D(const QQuickPathView);
880 return d->highlightRangeMode;
883 void QQuickPathView::setHighlightRangeMode(HighlightRangeMode mode)
886 if (d->highlightRangeMode == mode)
888 d->highlightRangeMode = mode;
889 d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
890 if (d->haveHighlightRange) {
894 emit highlightRangeModeChanged();
898 \qmlproperty int QtQuick2::PathView::highlightMoveDuration
899 This property holds the move animation duration of the highlight delegate.
901 If the highlightRangeMode is StrictlyEnforceRange then this property
902 determines the speed that the items move along the path.
904 The default value for the duration is 300ms.
906 int QQuickPathView::highlightMoveDuration() const
908 Q_D(const QQuickPathView);
909 return d->highlightMoveDuration;
912 void QQuickPathView::setHighlightMoveDuration(int duration)
915 if (d->highlightMoveDuration == duration)
917 d->highlightMoveDuration = duration;
918 emit highlightMoveDurationChanged();
922 \qmlproperty real QtQuick2::PathView::dragMargin
923 This property holds the maximum distance from the path that initiate mouse dragging.
925 By default the path can only be dragged by clicking on an item. If
926 dragMargin is greater than zero, a drag can be initiated by clicking
927 within dragMargin pixels of the path.
929 qreal QQuickPathView::dragMargin() const
931 Q_D(const QQuickPathView);
932 return d->dragMargin;
935 void QQuickPathView::setDragMargin(qreal dragMargin)
938 if (d->dragMargin == dragMargin)
940 d->dragMargin = dragMargin;
941 emit dragMarginChanged();
945 \qmlproperty real QtQuick2::PathView::flickDeceleration
946 This property holds the rate at which a flick will decelerate.
950 qreal QQuickPathView::flickDeceleration() const
952 Q_D(const QQuickPathView);
953 return d->deceleration;
956 void QQuickPathView::setFlickDeceleration(qreal dec)
959 if (d->deceleration == dec)
961 d->deceleration = dec;
962 emit flickDecelerationChanged();
966 \qmlproperty bool QtQuick2::PathView::interactive
968 A user cannot drag or flick a PathView that is not interactive.
970 This property is useful for temporarily disabling flicking. This allows
971 special interaction with PathView's children.
973 bool QQuickPathView::isInteractive() const
975 Q_D(const QQuickPathView);
976 return d->interactive;
979 void QQuickPathView::setInteractive(bool interactive)
982 if (interactive != d->interactive) {
983 d->interactive = interactive;
986 emit interactiveChanged();
991 \qmlproperty bool QtQuick2::PathView::moving
993 This property holds whether the view is currently moving
994 due to the user either dragging or flicking the view.
996 bool QQuickPathView::isMoving() const
998 Q_D(const QQuickPathView);
1003 \qmlproperty bool QtQuick2::PathView::flicking
1005 This property holds whether the view is currently moving
1006 due to the user flicking the view.
1008 bool QQuickPathView::isFlicking() const
1010 Q_D(const QQuickPathView);
1015 \qmlsignal QtQuick2::PathView::onMovementStarted()
1017 This handler is called when the view begins moving due to user
1022 \qmlsignal QtQuick2::PathView::onMovementEnded()
1024 This handler is called when the view stops moving due to user
1025 interaction. If a flick was generated, this handler will
1026 be triggered once the flick stops. If a flick was not
1027 generated, the handler will be triggered when the
1028 user stops dragging - i.e. a mouse or touch release.
1032 \qmlsignal QtQuick2::PathView::onFlickStarted()
1034 This handler is called when the view is flicked. A flick
1035 starts from the point that the mouse or touch is released,
1036 while still in motion.
1040 \qmlsignal QtQuick2::PathView::onFlickEnded()
1042 This handler is called when the view stops moving due to a flick.
1046 \qmlproperty Component QtQuick2::PathView::delegate
1048 The delegate provides a template defining each item instantiated by the view.
1049 The index is exposed as an accessible \c index property. Properties of the
1050 model are also available depending upon the type of \l {qmlmodels}{Data Model}.
1052 The number of elements in the delegate has a direct effect on the
1053 flicking performance of the view when pathItemCount is specified. If at all possible, place functionality
1054 that is not needed for the normal display of the delegate in a \l Loader which
1055 can load additional elements when needed.
1057 Note that the PathView will layout the items based on the size of the root
1058 item in the delegate.
1060 Here is an example delegate:
1061 \snippet doc/src/snippets/declarative/pathview/pathview.qml 1
1063 QDeclarativeComponent *QQuickPathView::delegate() const
1065 Q_D(const QQuickPathView);
1067 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
1068 return dataModel->delegate();
1074 void QQuickPathView::setDelegate(QDeclarativeComponent *delegate)
1076 Q_D(QQuickPathView);
1077 if (delegate == this->delegate())
1080 d->model = new QQuickVisualDataModel(qmlContext(this));
1083 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model)) {
1084 int oldCount = dataModel->count();
1085 dataModel->setDelegate(delegate);
1086 d->modelCount = dataModel->count();
1088 if (oldCount != dataModel->count())
1089 emit countChanged();
1090 emit delegateChanged();
1095 \qmlproperty int QtQuick2::PathView::pathItemCount
1096 This property holds the number of items visible on the path at any one time.
1098 int QQuickPathView::pathItemCount() const
1100 Q_D(const QQuickPathView);
1101 return d->pathItems;
1104 void QQuickPathView::setPathItemCount(int i)
1106 Q_D(QQuickPathView);
1107 if (i == d->pathItems)
1112 d->updateMappedRange();
1113 if (d->isValid() && isComponentComplete()) {
1116 emit pathItemCountChanged();
1119 QPointF QQuickPathViewPrivate::pointNear(const QPointF &point, qreal *nearPercent) const
1121 //XXX maybe do recursively at increasing resolution.
1122 qreal mindist = 1e10; // big number
1123 QPointF nearPoint = path->pointAt(0);
1125 for (qreal i=1; i < 1000; i++) {
1126 QPointF pt = path->pointAt(i/1000.0);
1127 QPointF diff = pt - point;
1128 qreal dist = diff.x()*diff.x() + diff.y()*diff.y();
1129 if (dist < mindist) {
1137 *nearPercent = nearPc / 1000.0;
1142 void QQuickPathView::mousePressEvent(QMouseEvent *event)
1144 Q_D(QQuickPathView);
1145 if (d->interactive) {
1146 d->handleMousePressEvent(event);
1149 QQuickItem::mousePressEvent(event);
1153 void QQuickPathViewPrivate::handleMousePressEvent(QMouseEvent *event)
1155 Q_Q(QQuickPathView);
1156 if (!interactive || !items.count())
1158 QPointF scenePoint = q->mapToScene(event->localPos());
1160 for (; idx < items.count(); ++idx) {
1161 QRectF rect = items.at(idx)->boundingRect();
1162 rect = items.at(idx)->mapRectToScene(rect);
1163 if (rect.contains(scenePoint))
1166 if (idx == items.count() && dragMargin == 0.) // didn't click on an item
1169 startPoint = pointNear(event->localPos(), &startPc);
1170 if (idx == items.count()) {
1171 qreal distance = qAbs(event->localPos().x() - startPoint.x()) + qAbs(event->localPos().y() - startPoint.y());
1172 if (distance > dragMargin)
1176 if (tl.isActive() && flicking)
1177 stealMouse = true; // If we've been flicked then steal the click.
1183 QQuickItemPrivate::start(lastPosTime);
1187 void QQuickPathView::mouseMoveEvent(QMouseEvent *event)
1189 Q_D(QQuickPathView);
1190 if (d->interactive) {
1191 d->handleMouseMoveEvent(event);
1193 setKeepMouseGrab(true);
1196 QQuickItem::mouseMoveEvent(event);
1200 void QQuickPathViewPrivate::handleMouseMoveEvent(QMouseEvent *event)
1202 Q_Q(QQuickPathView);
1203 if (!interactive || !lastPosTime.isValid())
1207 QPointF pathPoint = pointNear(event->localPos(), &newPc);
1209 QPointF delta = pathPoint - startPoint;
1210 if (qAbs(delta.x()) > qApp->styleHints()->startDragDistance() || qAbs(delta.y()) > qApp->styleHints()->startDragDistance()) {
1217 moveReason = QQuickPathViewPrivate::Mouse;
1218 qreal diff = (newPc - startPc)*modelCount*mappedRange;
1220 q->setOffset(offset + diff);
1222 if (diff > modelCount/2)
1224 else if (diff < -modelCount/2)
1227 lastElapsed = QQuickItemPrivate::restart(lastPosTime);
1233 emit q->movingChanged();
1234 emit q->movementStarted();
1239 void QQuickPathView::mouseReleaseEvent(QMouseEvent *event)
1241 Q_D(QQuickPathView);
1242 if (d->interactive) {
1243 d->handleMouseReleaseEvent(event);
1247 QQuickItem::mouseReleaseEvent(event);
1251 void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *)
1253 Q_Q(QQuickPathView);
1255 q->setKeepMouseGrab(false);
1256 if (!interactive || !lastPosTime.isValid())
1259 qreal elapsed = qreal(lastElapsed + QQuickItemPrivate::elapsed(lastPosTime)) / 1000.;
1260 qreal velocity = elapsed > 0. ? lastDist / elapsed : 0;
1261 if (model && modelCount && qAbs(velocity) > 1.) {
1262 qreal count = pathItems == -1 ? modelCount : pathItems;
1263 if (qAbs(velocity) > count * 2) // limit velocity
1264 velocity = (velocity > 0 ? count : -count) * 2;
1265 // Calculate the distance to be travelled
1266 qreal v2 = velocity*velocity;
1267 qreal accel = deceleration/10;
1268 // + 0.25 to encourage moving at least one item in the flick direction
1269 qreal dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2.0) + 0.25));
1270 if (haveHighlightRange && highlightRangeMode == QQuickPathView::StrictlyEnforceRange) {
1271 // round to nearest item.
1273 dist = qRound(dist + offset) - offset;
1275 dist = qRound(dist - offset) + offset;
1276 // Calculate accel required to stop on item boundary
1281 accel = v2 / (2.0f * qAbs(dist));
1285 moveOffset.setValue(offset);
1286 tl.accel(moveOffset, velocity, accel, dist);
1287 tl.callback(QDeclarativeTimeLineCallback(&moveOffset, fixOffsetCallback, this));
1290 emit q->flickingChanged();
1291 emit q->flickStarted();
1297 lastPosTime.invalidate();
1299 q->movementEnding();
1302 bool QQuickPathView::sendMouseEvent(QMouseEvent *event)
1304 Q_D(QQuickPathView);
1305 QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
1306 QQuickCanvas *c = canvas();
1307 QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
1308 bool stealThisEvent = d->stealMouse;
1309 if ((stealThisEvent || myRect.contains(event->windowPos())) && (!grabber || !grabber->keepMouseGrab())) {
1310 QMouseEvent mouseEvent(event->type(), mapFromScene(event->windowPos()), event->windowPos(), event->screenPos(),
1311 event->button(), event->buttons(), event->modifiers());
1312 mouseEvent.setAccepted(false);
1314 switch (mouseEvent.type()) {
1315 case QEvent::MouseMove:
1316 d->handleMouseMoveEvent(&mouseEvent);
1318 case QEvent::MouseButtonPress:
1319 d->handleMousePressEvent(&mouseEvent);
1320 stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
1322 case QEvent::MouseButtonRelease:
1323 d->handleMouseReleaseEvent(&mouseEvent);
1328 grabber = c->mouseGrabberItem();
1329 if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this)
1332 return d->stealMouse;
1333 } else if (d->lastPosTime.isValid()) {
1334 d->lastPosTime.invalidate();
1337 if (event->type() == QEvent::MouseButtonRelease)
1338 d->stealMouse = false;
1342 bool QQuickPathView::childMouseEventFilter(QQuickItem *i, QEvent *e)
1344 Q_D(QQuickPathView);
1345 if (!isVisible() || !d->interactive)
1346 return QQuickItem::childMouseEventFilter(i, e);
1348 switch (e->type()) {
1349 case QEvent::MouseButtonPress:
1350 case QEvent::MouseMove:
1351 case QEvent::MouseButtonRelease:
1352 return sendMouseEvent(static_cast<QMouseEvent *>(e));
1357 return QQuickItem::childMouseEventFilter(i, e);
1360 void QQuickPathView::mouseUngrabEvent()
1362 Q_D(QQuickPathView);
1363 if (d->stealMouse) {
1364 // if our mouse grab has been removed (probably by a Flickable),
1366 d->stealMouse = false;
1367 setKeepMouseGrab(false);
1368 d->lastPosTime.invalidate();
1372 void QQuickPathView::updatePolish()
1374 QQuickItem::updatePolish();
1378 void QQuickPathView::componentComplete()
1380 Q_D(QQuickPathView);
1381 if (d->model && d->ownModel)
1382 static_cast<QQuickVisualDataModel *>(d->model.data())->componentComplete();
1384 QQuickItem::componentComplete();
1386 d->createHighlight();
1387 // It is possible that a refill has already happended to to Path
1388 // bindings being handled in the componentComplete(). If so
1389 // don't do it again.
1390 if (d->items.count() == 0 && d->model) {
1391 d->modelCount = d->model->count();
1394 d->updateHighlight();
1397 emit countChanged();
1400 void QQuickPathView::refill()
1402 Q_D(QQuickPathView);
1403 if (!d->isValid() || !isComponentComplete())
1406 d->layoutScheduled = false;
1407 bool currentVisible = false;
1409 // first move existing items and remove items off path
1410 int idx = d->firstIndex;
1411 QList<QQuickItem*>::iterator it = d->items.begin();
1412 while (it != d->items.end()) {
1413 qreal pos = d->positionOfIndex(idx);
1414 QQuickItem *item = *it;
1416 d->updateItem(item, pos);
1417 if (idx == d->currentIndex) {
1418 currentVisible = true;
1419 d->currentItemOffset = pos;
1423 // qDebug() << "release";
1424 d->updateItem(item, 1.0);
1425 d->releaseItem(item);
1426 if (it == d->items.begin()) {
1427 if (++d->firstIndex >= d->modelCount)
1430 it = d->items.erase(it);
1433 if (idx >= d->modelCount)
1436 if (!d->items.count())
1439 bool waiting = false;
1440 if (d->modelCount) {
1441 // add items to beginning and end
1442 int count = d->pathItems == -1 ? d->modelCount : qMin(d->pathItems, d->modelCount);
1443 if (d->items.count() < count) {
1444 int idx = qRound(d->modelCount - d->offset) % d->modelCount;
1445 qreal startPos = 0.0;
1446 if (d->haveHighlightRange && d->highlightRangeMode != QQuickPathView::NoHighlightRange)
1447 startPos = d->highlightRangeStart;
1448 if (d->firstIndex >= 0) {
1449 startPos = d->positionOfIndex(d->firstIndex);
1450 idx = (d->firstIndex + d->items.count()) % d->modelCount;
1452 qreal pos = d->positionOfIndex(idx);
1453 while ((pos > startPos || !d->items.count()) && d->items.count() < count) {
1454 // qDebug() << "append" << idx;
1455 QQuickItem *item = d->getItem(idx, idx+1);
1460 if (d->currentIndex == idx) {
1461 currentVisible = true;
1462 d->currentItemOffset = pos;
1464 if (d->items.count() == 0)
1465 d->firstIndex = idx;
1466 d->items.append(item);
1467 d->updateItem(item, pos);
1469 if (idx >= d->modelCount)
1471 pos = d->positionOfIndex(idx);
1474 idx = d->firstIndex - 1;
1476 idx = d->modelCount - 1;
1477 pos = d->positionOfIndex(idx);
1478 while (!waiting && (pos >= 0.0 && pos < startPos) && d->items.count() < count) {
1479 // qDebug() << "prepend" << idx;
1480 QQuickItem *item = d->getItem(idx, idx+1);
1485 if (d->currentIndex == idx) {
1486 currentVisible = true;
1487 d->currentItemOffset = pos;
1489 d->items.prepend(item);
1490 d->updateItem(item, pos);
1491 d->firstIndex = idx;
1492 idx = d->firstIndex - 1;
1494 idx = d->modelCount - 1;
1495 pos = d->positionOfIndex(idx);
1500 if (!currentVisible) {
1501 d->currentItemOffset = 1.0;
1502 if (d->currentItem) {
1503 if (QQuickPathViewAttached *att = d->attached(d->currentItem))
1504 att->setOnPath(false);
1505 } else if (!waiting && d->currentIndex >= 0 && d->currentIndex < d->modelCount) {
1506 if ((d->currentItem = d->getItem(d->currentIndex, d->currentIndex, false))) {
1507 d->updateItem(d->currentItem, d->currentIndex < d->firstIndex ? 0.0 : 1.0);
1508 if (QQuickPathViewAttached *att = d->attached(d->currentItem))
1509 att->setIsCurrentItem(true);
1512 } else if (!waiting && !d->currentItem) {
1513 if ((d->currentItem = d->getItem(d->currentIndex, d->currentIndex, true))) {
1514 d->currentItem->setFocus(true);
1515 if (QQuickPathViewAttached *att = d->attached(d->currentItem))
1516 att->setIsCurrentItem(true);
1520 if (d->highlightItem && d->haveHighlightRange && d->highlightRangeMode == QQuickPathView::StrictlyEnforceRange) {
1521 d->updateItem(d->highlightItem, d->highlightRangeStart);
1522 if (QQuickPathViewAttached *att = d->attached(d->highlightItem))
1523 att->setOnPath(true);
1524 } else if (d->highlightItem && d->moveReason != QQuickPathViewPrivate::SetIndex) {
1525 d->updateItem(d->highlightItem, d->currentItemOffset);
1526 if (QQuickPathViewAttached *att = d->attached(d->highlightItem))
1527 att->setOnPath(currentVisible);
1529 while (d->itemCache.count())
1530 d->releaseItem(d->itemCache.takeLast());
1533 void QQuickPathView::modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset)
1535 Q_D(QQuickPathView);
1536 if (!d->model || !d->model->isValid() || !d->path || !isComponentComplete())
1540 d->modelCount = d->model->count();
1542 emit countChanged();
1546 if (changeSet.removes().isEmpty() && changeSet.inserts().isEmpty())
1549 const int modelCount = d->modelCount;
1552 bool currentChanged = false;
1553 bool changedOffset = false;
1554 foreach (const QDeclarativeChangeSet::Remove &r, changeSet.removes()) {
1555 if (moveId == -1 && d->currentIndex >= r.index + r.count) {
1556 d->currentIndex -= r.count;
1557 currentChanged = true;
1558 } else if (moveId == -1 && d->currentIndex >= r.index && d->currentIndex < r.index + r.count) {
1559 // current item has been removed.
1560 d->currentIndex = qMin(r.index, d->modelCount - r.count - 1);
1563 moveOffset = d->currentIndex - r.index;
1564 } else if (d->currentItem) {
1565 if (QQuickPathViewAttached *att = d->attached(d->currentItem))
1566 att->setIsCurrentItem(true);
1567 d->releaseItem(d->currentItem);
1570 currentChanged = true;
1573 if (r.index > d->currentIndex) {
1574 changedOffset = true;
1575 d->offset -= r.count;
1576 d->offsetAdj -= r.count;
1578 d->modelCount -= r.count;
1580 foreach (const QDeclarativeChangeSet::Insert &i, changeSet.inserts()) {
1581 if (d->modelCount) {
1582 if (moveId == -1 && i.index <= d->currentIndex) {
1583 d->currentIndex += i.count;
1584 currentChanged = true;
1586 if (moveId != -1 && moveId == i.moveId) {
1587 d->currentIndex = i.index + moveOffset;
1588 currentChanged = true;
1590 if (i.index > d->currentIndex) {
1591 d->offset += i.count;
1592 d->offsetAdj += i.count;
1593 changedOffset = true;
1597 d->modelCount += i.count;
1600 d->offset = qmlMod(d->offset, d->modelCount);
1602 d->offset += d->modelCount;
1604 d->itemCache += d->items;
1607 if (!d->modelCount) {
1608 while (d->itemCache.count())
1609 d->releaseItem(d->itemCache.takeLast());
1611 changedOffset = true;
1612 d->tl.reset(d->moveOffset);
1614 if (!d->flicking && !d->moving && d->haveHighlightRange && d->highlightRangeMode == QQuickPathView::StrictlyEnforceRange) {
1615 d->offset = qmlMod(d->modelCount - d->currentIndex, d->modelCount);
1616 changedOffset = true;
1619 d->updateMappedRange();
1620 d->scheduleLayout();
1623 emit offsetChanged();
1625 emit currentIndexChanged();
1626 if (d->modelCount != modelCount)
1627 emit countChanged();
1630 void QQuickPathView::destroyingItem(QQuickItem *item)
1635 void QQuickPathView::ticked()
1637 Q_D(QQuickPathView);
1641 void QQuickPathView::movementEnding()
1643 Q_D(QQuickPathView);
1645 d->flicking = false;
1646 emit flickingChanged();
1649 if (d->moving && !d->stealMouse) {
1651 emit movingChanged();
1652 emit movementEnded();
1656 // find the item closest to the snap position
1657 int QQuickPathViewPrivate::calcCurrentIndex()
1660 if (modelCount && model && items.count()) {
1661 offset = qmlMod(offset, modelCount);
1663 offset += modelCount;
1664 current = qRound(qAbs(qmlMod(modelCount - offset, modelCount)));
1665 current = current % modelCount;
1671 void QQuickPathViewPrivate::createCurrentItem()
1673 if (requestedIndex != -1)
1675 int itemIndex = (currentIndex - firstIndex + modelCount) % modelCount;
1676 if (itemIndex < items.count()) {
1677 if ((currentItem = getItem(currentIndex, currentIndex, true))) {
1678 currentItem->setFocus(true);
1679 if (QQuickPathViewAttached *att = attached(currentItem))
1680 att->setIsCurrentItem(true);
1682 } else if (currentIndex >= 0 && currentIndex < modelCount) {
1683 if ((currentItem = getItem(currentIndex, currentIndex, false))) {
1684 updateItem(currentItem, currentIndex < firstIndex ? 0.0 : 1.0);
1685 if (QQuickPathViewAttached *att = attached(currentItem))
1686 att->setIsCurrentItem(true);
1691 void QQuickPathViewPrivate::updateCurrent()
1693 Q_Q(QQuickPathView);
1694 if (moveReason != Mouse)
1696 if (!modelCount || !haveHighlightRange || highlightRangeMode != QQuickPathView::StrictlyEnforceRange)
1699 int idx = calcCurrentIndex();
1700 if (model && idx != currentIndex) {
1702 if (QQuickPathViewAttached *att = attached(currentItem))
1703 att->setIsCurrentItem(false);
1704 releaseItem(currentItem);
1708 createCurrentItem();
1709 emit q->currentIndexChanged();
1713 void QQuickPathViewPrivate::fixOffsetCallback(void *d)
1715 ((QQuickPathViewPrivate *)d)->fixOffset();
1718 void QQuickPathViewPrivate::fixOffset()
1720 Q_Q(QQuickPathView);
1721 if (model && items.count()) {
1722 if (haveHighlightRange && highlightRangeMode == QQuickPathView::StrictlyEnforceRange) {
1723 int curr = calcCurrentIndex();
1724 if (curr != currentIndex)
1725 q->setCurrentIndex(curr);
1732 void QQuickPathViewPrivate::snapToCurrent()
1734 if (!model || modelCount <= 0)
1737 qreal targetOffset = qmlMod(modelCount - currentIndex, modelCount);
1739 if (offset == targetOffset)
1744 tl.reset(moveOffset);
1745 moveOffset.setValue(offset);
1747 const int duration = highlightMoveDuration;
1750 tl.set(moveOffset, targetOffset);
1751 } else if (moveDirection == Positive || (moveDirection == Shortest && targetOffset - offset > modelCount/2)) {
1752 qreal distance = modelCount - targetOffset + offset;
1753 if (targetOffset > moveOffset) {
1754 tl.move(moveOffset, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * offset / distance));
1755 tl.set(moveOffset, modelCount);
1756 tl.move(moveOffset, targetOffset, QEasingCurve(offset == 0.0 ? QEasingCurve::InOutQuad : QEasingCurve::OutQuad), int(duration * (modelCount-targetOffset) / distance));
1758 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
1760 } else if (moveDirection == Negative || targetOffset - offset <= -modelCount/2) {
1761 qreal distance = modelCount - offset + targetOffset;
1762 if (targetOffset < moveOffset) {
1763 tl.move(moveOffset, modelCount, QEasingCurve(targetOffset == 0 ? QEasingCurve::InOutQuad : QEasingCurve::InQuad), int(duration * (modelCount-offset) / distance));
1764 tl.set(moveOffset, 0.0);
1765 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::OutQuad), int(duration * targetOffset / distance));
1767 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
1770 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
1772 moveDirection = Shortest;
1775 QQuickPathViewAttached *QQuickPathView::qmlAttachedProperties(QObject *obj)
1777 return new QQuickPathViewAttached(obj);