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/qdeclarativepathview_p.h"
43 #include "private/qdeclarativepathview_p_p.h"
45 #include <qdeclarativestate_p.h>
46 #include <qdeclarativeopenmetaobject_p.h>
49 #include <qlistmodelinterface_p.h>
50 #include <QGraphicsSceneEvent>
57 inline qreal qmlMod(qreal x, qreal y)
59 #ifdef QT_USE_MATH_H_FLOATS
60 if(sizeof(qreal) == sizeof(float))
61 return fmodf(float(x), float(y));
67 static QDeclarativeOpenMetaObjectType *qPathViewAttachedType = 0;
69 QDeclarativePathViewAttached::QDeclarativePathViewAttached(QObject *parent)
70 : QObject(parent), m_percent(-1), m_view(0), m_onPath(false), m_isCurrent(false)
72 if (qPathViewAttachedType) {
73 m_metaobject = new QDeclarativeOpenMetaObject(this, qPathViewAttachedType);
74 m_metaobject->setCached(true);
76 m_metaobject = new QDeclarativeOpenMetaObject(this);
80 QDeclarativePathViewAttached::~QDeclarativePathViewAttached()
84 QVariant QDeclarativePathViewAttached::value(const QByteArray &name) const
86 return m_metaobject->value(name);
88 void QDeclarativePathViewAttached::setValue(const QByteArray &name, const QVariant &val)
90 m_metaobject->setValue(name, val);
94 void QDeclarativePathViewPrivate::init()
96 Q_Q(QDeclarativePathView);
98 q->setAcceptedMouseButtons(Qt::LeftButton);
99 q->setFlag(QGraphicsItem::ItemIsFocusScope);
100 q->setFiltersChildEvents(true);
101 q->connect(&tl, SIGNAL(updated()), q, SLOT(ticked()));
102 lastPosTime.invalidate();
103 static int timelineCompletedIdx = -1;
104 static int movementEndingIdx = -1;
105 if (timelineCompletedIdx == -1) {
106 timelineCompletedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("completed()");
107 movementEndingIdx = QDeclarativePathView::staticMetaObject.indexOfSlot("movementEnding()");
109 QMetaObject::connect(&tl, timelineCompletedIdx,
110 q, movementEndingIdx, Qt::DirectConnection);
113 QDeclarativeItem *QDeclarativePathViewPrivate::getItem(int modelIndex)
115 Q_Q(QDeclarativePathView);
116 requestedIndex = modelIndex;
117 QDeclarativeItem *item = model->item(modelIndex, false);
120 // pre-create one metatype to share with all attached objects
121 attType = new QDeclarativeOpenMetaObjectType(&QDeclarativePathViewAttached::staticMetaObject, qmlEngine(q));
122 foreach(const QString &attr, path->attributes())
123 attType->createProperty(attr.toUtf8());
125 qPathViewAttachedType = attType;
126 QDeclarativePathViewAttached *att = static_cast<QDeclarativePathViewAttached *>(qmlAttachedPropertiesObject<QDeclarativePathView>(item));
127 qPathViewAttachedType = 0;
130 att->setOnPath(true);
132 item->setParentItem(q);
133 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
134 itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
140 void QDeclarativePathViewPrivate::releaseItem(QDeclarativeItem *item)
144 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
145 itemPrivate->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
146 if (model->release(item) == 0) {
147 // item was not destroyed, and we no longer reference it.
148 if (QDeclarativePathViewAttached *att = attached(item))
149 att->setOnPath(false);
153 QDeclarativePathViewAttached *QDeclarativePathViewPrivate::attached(QDeclarativeItem *item)
155 return static_cast<QDeclarativePathViewAttached *>(qmlAttachedPropertiesObject<QDeclarativePathView>(item, false));
158 void QDeclarativePathViewPrivate::clear()
160 for (int i=0; i<items.count(); i++){
161 QDeclarativeItem *p = items[i];
167 void QDeclarativePathViewPrivate::updateMappedRange()
169 if (model && pathItems != -1 && pathItems < modelCount)
170 mappedRange = qreal(pathItems)/modelCount;
175 qreal QDeclarativePathViewPrivate::positionOfIndex(qreal index) const
179 if (model && index >= 0 && index < modelCount) {
181 if (haveHighlightRange && highlightRangeMode != QDeclarativePathView::NoHighlightRange)
182 start = highlightRangeStart;
183 qreal globalPos = index + offset;
184 globalPos = qmlMod(globalPos, qreal(modelCount)) / modelCount;
185 if (pathItems != -1 && pathItems < modelCount) {
186 globalPos += start * mappedRange;
187 globalPos = qmlMod(globalPos, 1.0);
188 if (globalPos < mappedRange)
189 pos = globalPos / mappedRange;
191 pos = qmlMod(globalPos + start, 1.0);
198 void QDeclarativePathViewPrivate::createHighlight()
200 Q_Q(QDeclarativePathView);
201 if (!q->isComponentComplete())
204 bool changed = false;
206 delete highlightItem;
211 QDeclarativeItem *item = 0;
212 if (highlightComponent) {
213 QDeclarativeContext *highlightContext = new QDeclarativeContext(qmlContext(q));
214 QObject *nobj = highlightComponent->create(highlightContext);
216 QDeclarative_setParent_noEvent(highlightContext, nobj);
217 item = qobject_cast<QDeclarativeItem *>(nobj);
221 delete highlightContext;
224 item = new QDeclarativeItem;
227 QDeclarative_setParent_noEvent(item, q);
228 item->setParentItem(q);
229 highlightItem = item;
233 emit q->highlightItemChanged();
236 void QDeclarativePathViewPrivate::updateHighlight()
238 Q_Q(QDeclarativePathView);
239 if (!q->isComponentComplete() || !isValid())
242 if (haveHighlightRange && highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) {
243 updateItem(highlightItem, highlightRangeStart);
245 qreal target = currentIndex;
248 tl.reset(moveHighlight);
249 moveHighlight.setValue(highlightPosition);
251 const int duration = highlightMoveDuration;
253 if (target - highlightPosition > modelCount/2) {
255 qreal distance = modelCount - target + highlightPosition;
256 tl.move(moveHighlight, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * highlightPosition / distance));
257 tl.set(moveHighlight, modelCount-0.01);
258 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * (modelCount-target) / distance));
259 } else if (target - highlightPosition <= -modelCount/2) {
261 qreal distance = modelCount - highlightPosition + target;
262 tl.move(moveHighlight, modelCount-0.01, QEasingCurve(QEasingCurve::InQuad), int(duration * (modelCount-highlightPosition) / distance));
263 tl.set(moveHighlight, 0.0);
264 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * target / distance));
266 highlightUp = highlightPosition - target < 0;
267 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::InOutQuad), duration);
273 void QDeclarativePathViewPrivate::setHighlightPosition(qreal pos)
275 if (pos != highlightPosition) {
278 if (haveHighlightRange && highlightRangeMode != QDeclarativePathView::NoHighlightRange) {
279 start = highlightRangeStart;
280 end = highlightRangeEnd;
283 qreal range = qreal(modelCount);
284 // calc normalized position of highlight relative to offset
285 qreal relativeHighlight = qmlMod(pos + offset, range) / range;
287 if (!highlightUp && relativeHighlight > end * mappedRange) {
288 qreal diff = 1.0 - relativeHighlight;
289 setOffset(offset + diff * range);
290 } else if (highlightUp && relativeHighlight >= (end - start) * mappedRange) {
291 qreal diff = relativeHighlight - (end - start) * mappedRange;
292 setOffset(offset - diff * range - 0.00001);
295 highlightPosition = pos;
296 qreal pathPos = positionOfIndex(pos);
297 updateItem(highlightItem, pathPos);
298 if (QDeclarativePathViewAttached *att = attached(highlightItem))
299 att->setOnPath(pathPos != -1.0);
303 void QDeclarativePathView::pathUpdated()
305 Q_D(QDeclarativePathView);
306 QList<QDeclarativeItem*>::iterator it = d->items.begin();
307 while (it != d->items.end()) {
308 QDeclarativeItem *item = *it;
309 if (QDeclarativePathViewAttached *att = d->attached(item))
316 void QDeclarativePathViewPrivate::updateItem(QDeclarativeItem *item, qreal percent)
318 if (QDeclarativePathViewAttached *att = attached(item)) {
319 if (qFuzzyCompare(att->m_percent, percent))
321 att->m_percent = percent;
322 foreach(const QString &attr, path->attributes())
323 att->setValue(attr.toUtf8(), path->attributeAt(attr, percent));
325 QPointF pf = path->pointAt(percent);
326 item->setX(qRound(pf.x() - item->width()/2));
327 item->setY(qRound(pf.y() - item->height()/2));
330 void QDeclarativePathViewPrivate::regenerate()
332 Q_Q(QDeclarativePathView);
333 if (!q->isComponentComplete())
347 \qmlclass PathView QDeclarativePathView
348 \ingroup qml-view-elements
350 \brief The PathView element lays out model-provided items on a path.
353 A PathView displays data from models created from built-in QML elements like ListModel
354 and XmlListModel, or custom model classes defined in C++ that inherit from
357 The view has a \l model, which defines the data to be displayed, and
358 a \l delegate, which defines how the data should be displayed.
359 The \l delegate is instantiated for each item on the \l path.
360 The items may be flicked to move them along the path.
362 For example, if there is a simple list model defined in a file \c ContactModel.qml like this:
364 \snippet doc/src/snippets/declarative/pathview/ContactModel.qml 0
366 This data can be represented as a PathView, like this:
368 \snippet doc/src/snippets/declarative/pathview/pathview.qml 0
372 (Note the above example uses PathAttribute to scale and modify the
373 opacity of the items as they rotate. This additional code can be seen in the
374 PathAttribute documentation.)
376 PathView does not automatically handle keyboard navigation. This is because
377 the keys to use for navigation will depend upon the shape of the path. Navigation
378 can be added quite simply by setting \c focus to \c true and calling
379 \l decrementCurrentIndex() or \l incrementCurrentIndex(), for example to navigate
380 using the left and right arrow keys:
386 Keys.onLeftPressed: decrementCurrentIndex()
387 Keys.onRightPressed: incrementCurrentIndex()
391 The path view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
393 Delegates are instantiated as needed and may be destroyed at any time.
394 State should \e never be stored in a delegate.
396 PathView attaches a number of properties to the root item of the delegate, for example
397 \c {PathView.isCurrentItem}. In the following example, the root delegate item can access
398 this attached property directly as \c PathView.isCurrentItem, while the child
399 \c nameText object must refer to this property as \c wrapper.PathView.isCurrentItem.
401 \snippet doc/src/snippets/declarative/pathview/pathview.qml 1
403 \bold Note that views do not enable \e clip automatically. If the view
404 is not clipped by another item or the screen, it will be necessary
405 to set \e {clip: true} in order to have the out of view items clipped
408 \sa Path, {declarative/modelviews/pathview}{PathView example}
411 QDeclarativePathView::QDeclarativePathView(QDeclarativeItem *parent)
412 : QDeclarativeItem(*(new QDeclarativePathViewPrivate), parent)
414 Q_D(QDeclarativePathView);
418 QDeclarativePathView::~QDeclarativePathView()
420 Q_D(QDeclarativePathView);
423 d->attType->release();
429 \qmlattachedproperty PathView PathView::view
430 This attached property holds the view that manages this delegate instance.
432 It is attached to each instance of the delegate.
436 \qmlattachedproperty bool PathView::onPath
437 This attached property holds whether the item is currently on the path.
439 If a pathItemCount has been set, it is possible that some items may
440 be instantiated, but not considered to be currently on the path.
441 Usually, these items would be set invisible, for example:
446 visible: PathView.onPath
452 It is attached to each instance of the delegate.
456 \qmlattachedproperty bool PathView::isCurrentItem
457 This attached property is true if this delegate is the current item; otherwise false.
459 It is attached to each instance of the delegate.
461 This property may be used to adjust the appearance of the current item.
463 \snippet doc/src/snippets/declarative/pathview/pathview.qml 1
467 \qmlproperty model PathView::model
468 This property holds the model providing data for the view.
470 The model provides a set of data that is used to create the items for the view.
471 For large or dynamic datasets the model is usually provided by a C++ model object.
472 Models can also be created directly in QML, using the ListModel element.
474 \sa {qmlmodels}{Data Models}
476 QVariant QDeclarativePathView::model() const
478 Q_D(const QDeclarativePathView);
479 return d->modelVariant;
482 void QDeclarativePathView::setModel(const QVariant &model)
484 Q_D(QDeclarativePathView);
485 if (d->modelVariant == model)
489 disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
490 disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
491 disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
492 disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
493 disconnect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*)));
494 for (int i=0; i<d->items.count(); i++){
495 QDeclarativeItem *p = d->items[i];
496 d->model->release(p);
501 d->modelVariant = model;
502 QObject *object = qvariant_cast<QObject*>(model);
503 QDeclarativeVisualModel *vim = 0;
504 if (object && (vim = qobject_cast<QDeclarativeVisualModel *>(object))) {
512 d->model = new QDeclarativeVisualDataModel(qmlContext(this), this);
515 if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model))
516 dataModel->setModel(model);
520 connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
521 connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
522 connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
523 connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
524 connect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*)));
525 d->modelCount = d->model->count();
526 if (d->model->count())
527 d->offset = qmlMod(d->offset, qreal(d->model->count()));
529 d->offset = d->model->count() + d->offset;
538 \qmlproperty int PathView::count
539 This property holds the number of items in the model.
541 int QDeclarativePathView::count() const
543 Q_D(const QDeclarativePathView);
544 return d->model ? d->modelCount : 0;
548 \qmlproperty Path PathView::path
549 This property holds the path used to lay out the items.
550 For more information see the \l Path documentation.
552 QDeclarativePath *QDeclarativePathView::path() const
554 Q_D(const QDeclarativePathView);
558 void QDeclarativePathView::setPath(QDeclarativePath *path)
560 Q_D(QDeclarativePathView);
564 disconnect(d->path, SIGNAL(changed()), this, SLOT(pathUpdated()));
566 connect(d->path, SIGNAL(changed()), this, SLOT(pathUpdated()));
567 if (d->isValid() && isComponentComplete()) {
570 d->attType->release();
579 \qmlproperty int PathView::currentIndex
580 This property holds the index of the current item.
582 int QDeclarativePathView::currentIndex() const
584 Q_D(const QDeclarativePathView);
585 return d->currentIndex;
588 void QDeclarativePathView::setCurrentIndex(int idx)
590 Q_D(QDeclarativePathView);
591 if (d->model && d->modelCount)
592 idx = qAbs(idx % d->modelCount);
593 if (d->model && idx != d->currentIndex) {
595 int itemIndex = (d->currentIndex - d->firstIndex + d->modelCount) % d->modelCount;
596 if (itemIndex < d->items.count()) {
597 if (QDeclarativeItem *item = d->items.at(itemIndex)) {
598 if (QDeclarativePathViewAttached *att = d->attached(item))
599 att->setIsCurrentItem(false);
604 d->moveReason = QDeclarativePathViewPrivate::SetIndex;
605 d->currentIndex = idx;
607 if (d->haveHighlightRange && d->highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange)
609 int itemIndex = (idx - d->firstIndex + d->modelCount) % d->modelCount;
610 if (itemIndex < d->items.count()) {
611 d->currentItem = d->items.at(itemIndex);
612 d->currentItem->setFocus(true);
613 if (QDeclarativePathViewAttached *att = d->attached(d->currentItem))
614 att->setIsCurrentItem(true);
616 d->currentItemOffset = d->positionOfIndex(d->currentIndex);
617 d->updateHighlight();
619 emit currentIndexChanged();
624 \qmlmethod PathView::incrementCurrentIndex()
626 Increments the current index.
628 \bold Note: methods should only be called after the Component has completed.
630 void QDeclarativePathView::incrementCurrentIndex()
632 Q_D(QDeclarativePathView);
633 d->moveDirection = QDeclarativePathViewPrivate::Positive;
634 setCurrentIndex(currentIndex()+1);
639 \qmlmethod PathView::decrementCurrentIndex()
641 Decrements the current index.
643 \bold Note: methods should only be called after the Component has completed.
645 void QDeclarativePathView::decrementCurrentIndex()
647 Q_D(QDeclarativePathView);
648 if (d->model && d->modelCount) {
649 int idx = currentIndex()-1;
651 idx = d->modelCount - 1;
652 d->moveDirection = QDeclarativePathViewPrivate::Negative;
653 setCurrentIndex(idx);
658 \qmlproperty real PathView::offset
660 The offset specifies how far along the path the items are from their initial positions.
661 This is a real number that ranges from 0.0 to the count of items in the model.
663 qreal QDeclarativePathView::offset() const
665 Q_D(const QDeclarativePathView);
669 void QDeclarativePathView::setOffset(qreal offset)
671 Q_D(QDeclarativePathView);
672 d->setOffset(offset);
676 void QDeclarativePathViewPrivate::setOffset(qreal o)
678 Q_Q(QDeclarativePathView);
680 if (isValid() && q->isComponentComplete()) {
681 offset = qmlMod(o, qreal(modelCount));
683 offset += qreal(modelCount);
688 emit q->offsetChanged();
692 void QDeclarativePathViewPrivate::setAdjustedOffset(qreal o)
694 setOffset(o+offsetAdj);
698 \qmlproperty Component PathView::highlight
699 This property holds the component to use as the highlight.
701 An instance of the highlight component will be created for each view.
702 The geometry of the resultant component instance will be managed by the view
703 so as to stay with the current item.
705 The below example demonstrates how to make a simple highlight. Note the use
706 of the \l{PathView::onPath}{PathView.onPath} attached property to ensure that
707 the highlight is hidden when flicked away from the path.
712 visible: PathView.onPath
718 \sa highlightItem, highlightRangeMode
721 QDeclarativeComponent *QDeclarativePathView::highlight() const
723 Q_D(const QDeclarativePathView);
724 return d->highlightComponent;
727 void QDeclarativePathView::setHighlight(QDeclarativeComponent *highlight)
729 Q_D(QDeclarativePathView);
730 if (highlight != d->highlightComponent) {
731 d->highlightComponent = highlight;
732 d->createHighlight();
733 d->updateHighlight();
734 emit highlightChanged();
739 \qmlproperty Item PathView::highlightItem
741 \c highlightItem holds the highlight item, which was created
742 from the \l highlight component.
746 QDeclarativeItem *QDeclarativePathView::highlightItem()
748 Q_D(const QDeclarativePathView);
749 return d->highlightItem;
752 \qmlproperty real PathView::preferredHighlightBegin
753 \qmlproperty real PathView::preferredHighlightEnd
754 \qmlproperty enumeration PathView::highlightRangeMode
756 These properties set the preferred range of the highlight (current item)
757 within the view. The preferred values must be in the range 0.0-1.0.
759 If highlightRangeMode is set to \e PathView.NoHighlightRange
761 If highlightRangeMode is set to \e PathView.ApplyRange the view will
762 attempt to maintain the highlight within the range, however
763 the highlight can move outside of the range at the ends of the path
764 or due to a mouse interaction.
766 If highlightRangeMode is set to \e PathView.StrictlyEnforceRange the highlight will never
767 move outside of the range. This means that the current item will change
768 if a keyboard or mouse action would cause the highlight to move
769 outside of the range.
771 Note that this is the correct way to influence where the
772 current item ends up when the view moves. For example, if you want the
773 currently selected item to be in the middle of the path, then set the
774 highlight range to be 0.5,0.5 and highlightRangeMode to PathView.StrictlyEnforceRange.
775 Then, when the path scrolls,
776 the currently selected item will be the item at that position. This also applies to
777 when the currently selected item changes - it will scroll to within the preferred
778 highlight range. Furthermore, the behaviour of the current item index will occur
779 whether or not a highlight exists.
781 The default value is \e PathView.StrictlyEnforceRange.
783 Note that a valid range requires preferredHighlightEnd to be greater
784 than or equal to preferredHighlightBegin.
786 qreal QDeclarativePathView::preferredHighlightBegin() const
788 Q_D(const QDeclarativePathView);
789 return d->highlightRangeStart;
792 void QDeclarativePathView::setPreferredHighlightBegin(qreal start)
794 Q_D(QDeclarativePathView);
795 if (d->highlightRangeStart == start || start < 0 || start > 1.0)
797 d->highlightRangeStart = start;
798 d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
800 emit preferredHighlightBeginChanged();
803 qreal QDeclarativePathView::preferredHighlightEnd() const
805 Q_D(const QDeclarativePathView);
806 return d->highlightRangeEnd;
809 void QDeclarativePathView::setPreferredHighlightEnd(qreal end)
811 Q_D(QDeclarativePathView);
812 if (d->highlightRangeEnd == end || end < 0 || end > 1.0)
814 d->highlightRangeEnd = end;
815 d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
817 emit preferredHighlightEndChanged();
820 QDeclarativePathView::HighlightRangeMode QDeclarativePathView::highlightRangeMode() const
822 Q_D(const QDeclarativePathView);
823 return d->highlightRangeMode;
826 void QDeclarativePathView::setHighlightRangeMode(HighlightRangeMode mode)
828 Q_D(QDeclarativePathView);
829 if (d->highlightRangeMode == mode)
831 d->highlightRangeMode = mode;
832 d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
833 emit highlightRangeModeChanged();
838 \qmlproperty int PathView::highlightMoveDuration
839 This property holds the move animation duration of the highlight delegate.
841 If the highlightRangeMode is StrictlyEnforceRange then this property
842 determines the speed that the items move along the path.
844 The default value for the duration is 300ms.
846 int QDeclarativePathView::highlightMoveDuration() const
848 Q_D(const QDeclarativePathView);
849 return d->highlightMoveDuration;
852 void QDeclarativePathView::setHighlightMoveDuration(int duration)
854 Q_D(QDeclarativePathView);
855 if (d->highlightMoveDuration == duration)
857 d->highlightMoveDuration = duration;
858 emit highlightMoveDurationChanged();
862 \qmlproperty real PathView::dragMargin
863 This property holds the maximum distance from the path that initiate mouse dragging.
865 By default the path can only be dragged by clicking on an item. If
866 dragMargin is greater than zero, a drag can be initiated by clicking
867 within dragMargin pixels of the path.
869 qreal QDeclarativePathView::dragMargin() const
871 Q_D(const QDeclarativePathView);
872 return d->dragMargin;
875 void QDeclarativePathView::setDragMargin(qreal dragMargin)
877 Q_D(QDeclarativePathView);
878 if (d->dragMargin == dragMargin)
880 d->dragMargin = dragMargin;
881 emit dragMarginChanged();
885 \qmlproperty real PathView::flickDeceleration
886 This property holds the rate at which a flick will decelerate.
890 qreal QDeclarativePathView::flickDeceleration() const
892 Q_D(const QDeclarativePathView);
893 return d->deceleration;
896 void QDeclarativePathView::setFlickDeceleration(qreal dec)
898 Q_D(QDeclarativePathView);
899 if (d->deceleration == dec)
901 d->deceleration = dec;
902 emit flickDecelerationChanged();
906 \qmlproperty bool PathView::interactive
908 A user cannot drag or flick a PathView that is not interactive.
910 This property is useful for temporarily disabling flicking. This allows
911 special interaction with PathView's children.
913 bool QDeclarativePathView::isInteractive() const
915 Q_D(const QDeclarativePathView);
916 return d->interactive;
919 void QDeclarativePathView::setInteractive(bool interactive)
921 Q_D(QDeclarativePathView);
922 if (interactive != d->interactive) {
923 d->interactive = interactive;
926 emit interactiveChanged();
931 \qmlproperty bool PathView::moving
933 This property holds whether the view is currently moving
934 due to the user either dragging or flicking the view.
936 bool QDeclarativePathView::isMoving() const
938 Q_D(const QDeclarativePathView);
943 \qmlproperty bool PathView::flicking
945 This property holds whether the view is currently moving
946 due to the user flicking the view.
948 bool QDeclarativePathView::isFlicking() const
950 Q_D(const QDeclarativePathView);
955 \qmlsignal PathView::onMovementStarted()
957 This handler is called when the view begins moving due to user
962 \qmlsignal PathView::onMovementEnded()
964 This handler is called when the view stops moving due to user
965 interaction. If a flick was generated, this handler will
966 be triggered once the flick stops. If a flick was not
967 generated, the handler will be triggered when the
968 user stops dragging - i.e. a mouse or touch release.
972 \qmlsignal PathView::onFlickStarted()
974 This handler is called when the view is flicked. A flick
975 starts from the point that the mouse or touch is released,
976 while still in motion.
980 \qmlsignal PathView::onFlickEnded()
982 This handler is called when the view stops moving due to a flick.
986 \qmlproperty Component PathView::delegate
988 The delegate provides a template defining each item instantiated by the view.
989 The index is exposed as an accessible \c index property. Properties of the
990 model are also available depending upon the type of \l {qmlmodels}{Data Model}.
992 The number of elements in the delegate has a direct effect on the
993 flicking performance of the view when pathItemCount is specified. If at all possible, place functionality
994 that is not needed for the normal display of the delegate in a \l Loader which
995 can load additional elements when needed.
997 Note that the PathView will layout the items based on the size of the root
998 item in the delegate.
1000 Here is an example delegate:
1001 \snippet doc/src/snippets/declarative/pathview/pathview.qml 1
1003 QDeclarativeComponent *QDeclarativePathView::delegate() const
1005 Q_D(const QDeclarativePathView);
1007 if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model))
1008 return dataModel->delegate();
1014 void QDeclarativePathView::setDelegate(QDeclarativeComponent *delegate)
1016 Q_D(QDeclarativePathView);
1017 if (delegate == this->delegate())
1020 d->model = new QDeclarativeVisualDataModel(qmlContext(this));
1023 if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model)) {
1024 int oldCount = dataModel->count();
1025 dataModel->setDelegate(delegate);
1026 d->modelCount = dataModel->count();
1028 if (oldCount != dataModel->count())
1029 emit countChanged();
1030 emit delegateChanged();
1035 \qmlproperty int PathView::pathItemCount
1036 This property holds the number of items visible on the path at any one time.
1038 int QDeclarativePathView::pathItemCount() const
1040 Q_D(const QDeclarativePathView);
1041 return d->pathItems;
1044 void QDeclarativePathView::setPathItemCount(int i)
1046 Q_D(QDeclarativePathView);
1047 if (i == d->pathItems)
1052 d->updateMappedRange();
1053 if (d->isValid() && isComponentComplete()) {
1056 emit pathItemCountChanged();
1059 QPointF QDeclarativePathViewPrivate::pointNear(const QPointF &point, qreal *nearPercent) const
1061 //XXX maybe do recursively at increasing resolution.
1062 qreal mindist = 1e10; // big number
1063 QPointF nearPoint = path->pointAt(0);
1065 for (qreal i=1; i < 1000; i++) {
1066 QPointF pt = path->pointAt(i/1000.0);
1067 QPointF diff = pt - point;
1068 qreal dist = diff.x()*diff.x() + diff.y()*diff.y();
1069 if (dist < mindist) {
1077 *nearPercent = nearPc / 1000.0;
1082 void QDeclarativePathView::mousePressEvent(QGraphicsSceneMouseEvent *event)
1084 Q_D(QDeclarativePathView);
1085 if (d->interactive) {
1086 d->handleMousePressEvent(event);
1089 QDeclarativeItem::mousePressEvent(event);
1093 void QDeclarativePathViewPrivate::handleMousePressEvent(QGraphicsSceneMouseEvent *event)
1095 Q_Q(QDeclarativePathView);
1096 if (!interactive || !items.count())
1098 QPointF scenePoint = q->mapToScene(event->pos());
1100 for (; idx < items.count(); ++idx) {
1101 QRectF rect = items.at(idx)->boundingRect();
1102 rect = items.at(idx)->mapToScene(rect).boundingRect();
1103 if (rect.contains(scenePoint))
1106 if (idx == items.count() && dragMargin == 0.) // didn't click on an item
1109 startPoint = pointNear(event->pos(), &startPc);
1110 if (idx == items.count()) {
1111 qreal distance = qAbs(event->pos().x() - startPoint.x()) + qAbs(event->pos().y() - startPoint.y());
1112 if (distance > dragMargin)
1116 if (tl.isActive() && flicking)
1117 stealMouse = true; // If we've been flicked then steal the click.
1123 QDeclarativeItemPrivate::start(lastPosTime);
1127 void QDeclarativePathView::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
1129 Q_D(QDeclarativePathView);
1130 if (d->interactive) {
1131 d->handleMouseMoveEvent(event);
1133 setKeepMouseGrab(true);
1136 QDeclarativeItem::mouseMoveEvent(event);
1140 void QDeclarativePathViewPrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent *event)
1142 Q_Q(QDeclarativePathView);
1143 if (!interactive || !lastPosTime.isValid())
1147 QPointF pathPoint = pointNear(event->pos(), &newPc);
1149 QPointF delta = pathPoint - startPoint;
1150 if (qAbs(delta.x()) > QApplication::startDragDistance() || qAbs(delta.y()) > QApplication::startDragDistance()) {
1157 moveReason = QDeclarativePathViewPrivate::Mouse;
1158 qreal diff = (newPc - startPc)*modelCount*mappedRange;
1160 q->setOffset(offset + diff);
1162 if (diff > modelCount/2)
1164 else if (diff < -modelCount/2)
1167 lastElapsed = QDeclarativeItemPrivate::restart(lastPosTime);
1173 emit q->movingChanged();
1174 emit q->movementStarted();
1179 void QDeclarativePathView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
1181 Q_D(QDeclarativePathView);
1182 if (d->interactive) {
1183 d->handleMouseReleaseEvent(event);
1187 QDeclarativeItem::mouseReleaseEvent(event);
1191 void QDeclarativePathViewPrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEvent *)
1193 Q_Q(QDeclarativePathView);
1195 q->setKeepMouseGrab(false);
1196 if (!interactive || !lastPosTime.isValid())
1199 qreal elapsed = qreal(lastElapsed + QDeclarativeItemPrivate::elapsed(lastPosTime)) / 1000.;
1200 qreal velocity = elapsed > 0. ? lastDist / elapsed : 0;
1201 if (model && modelCount && qAbs(velocity) > 1.) {
1202 qreal count = pathItems == -1 ? modelCount : pathItems;
1203 if (qAbs(velocity) > count * 2) // limit velocity
1204 velocity = (velocity > 0 ? count : -count) * 2;
1205 // Calculate the distance to be travelled
1206 qreal v2 = velocity*velocity;
1207 qreal accel = deceleration/10;
1208 // + 0.25 to encourage moving at least one item in the flick direction
1209 qreal dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2.0) + 0.25));
1210 if (haveHighlightRange && highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) {
1211 // round to nearest item.
1213 dist = qRound(dist + offset) - offset;
1215 dist = qRound(dist - offset) + offset;
1216 // Calculate accel required to stop on item boundary
1221 accel = v2 / (2.0f * qAbs(dist));
1225 moveOffset.setValue(offset);
1226 tl.accel(moveOffset, velocity, accel, dist);
1227 tl.callback(QDeclarativeTimeLineCallback(&moveOffset, fixOffsetCallback, this));
1230 emit q->flickingChanged();
1231 emit q->flickStarted();
1237 lastPosTime.invalidate();
1239 q->movementEnding();
1242 bool QDeclarativePathView::sendMouseEvent(QGraphicsSceneMouseEvent *event)
1244 Q_D(QDeclarativePathView);
1245 QGraphicsSceneMouseEvent mouseEvent(event->type());
1246 QRectF myRect = mapToScene(QRectF(0, 0, width(), height())).boundingRect();
1247 QGraphicsScene *s = scene();
1248 QDeclarativeItem *grabber = s ? qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem()) : 0;
1249 bool stealThisEvent = d->stealMouse;
1250 if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) {
1251 mouseEvent.setAccepted(false);
1252 for (int i = 0x1; i <= 0x10; i <<= 1) {
1253 if (event->buttons() & i) {
1254 Qt::MouseButton button = Qt::MouseButton(i);
1255 mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button)));
1258 mouseEvent.setScenePos(event->scenePos());
1259 mouseEvent.setLastScenePos(event->lastScenePos());
1260 mouseEvent.setPos(mapFromScene(event->scenePos()));
1261 mouseEvent.setLastPos(mapFromScene(event->lastScenePos()));
1263 switch(mouseEvent.type()) {
1264 case QEvent::GraphicsSceneMouseMove:
1265 d->handleMouseMoveEvent(&mouseEvent);
1267 case QEvent::GraphicsSceneMousePress:
1268 d->handleMousePressEvent(&mouseEvent);
1269 stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
1271 case QEvent::GraphicsSceneMouseRelease:
1272 d->handleMouseReleaseEvent(&mouseEvent);
1277 grabber = qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem());
1278 if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this)
1281 return d->stealMouse;
1282 } else if (d->lastPosTime.isValid()) {
1283 d->lastPosTime.invalidate();
1285 if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease)
1286 d->stealMouse = false;
1290 bool QDeclarativePathView::sceneEventFilter(QGraphicsItem *i, QEvent *e)
1292 Q_D(QDeclarativePathView);
1293 if (!isVisible() || !d->interactive)
1294 return QDeclarativeItem::sceneEventFilter(i, e);
1296 switch (e->type()) {
1297 case QEvent::GraphicsSceneMousePress:
1298 case QEvent::GraphicsSceneMouseMove:
1299 case QEvent::GraphicsSceneMouseRelease:
1300 return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e));
1305 return QDeclarativeItem::sceneEventFilter(i, e);
1308 bool QDeclarativePathView::event(QEvent *event)
1310 if (event->type() == QEvent::User) {
1315 return QDeclarativeItem::event(event);
1318 void QDeclarativePathView::componentComplete()
1320 Q_D(QDeclarativePathView);
1321 QDeclarativeItem::componentComplete();
1322 d->createHighlight();
1323 // It is possible that a refill has already happended to to Path
1324 // bindings being handled in the componentComplete(). If so
1325 // don't do it again.
1326 if (d->items.count() == 0 && d->model) {
1327 d->modelCount = d->model->count();
1330 d->updateHighlight();
1333 void QDeclarativePathView::refill()
1335 Q_D(QDeclarativePathView);
1336 if (!d->isValid() || !isComponentComplete())
1339 d->layoutScheduled = false;
1340 bool currentVisible = false;
1342 // first move existing items and remove items off path
1343 int idx = d->firstIndex;
1344 QList<QDeclarativeItem*>::iterator it = d->items.begin();
1345 while (it != d->items.end()) {
1346 qreal pos = d->positionOfIndex(idx);
1347 QDeclarativeItem *item = *it;
1349 d->updateItem(item, pos);
1350 if (idx == d->currentIndex) {
1351 currentVisible = true;
1352 d->currentItemOffset = pos;
1356 // qDebug() << "release";
1357 d->updateItem(item, 1.0);
1358 d->releaseItem(item);
1359 if (it == d->items.begin()) {
1360 if (++d->firstIndex >= d->modelCount)
1363 it = d->items.erase(it);
1366 if (idx >= d->modelCount)
1369 if (!d->items.count())
1372 if (d->modelCount) {
1373 // add items to beginning and end
1374 int count = d->pathItems == -1 ? d->modelCount : qMin(d->pathItems, d->modelCount);
1375 if (d->items.count() < count) {
1376 int idx = qRound(d->modelCount - d->offset) % d->modelCount;
1377 qreal startPos = 0.0;
1378 if (d->haveHighlightRange && d->highlightRangeMode != QDeclarativePathView::NoHighlightRange)
1379 startPos = d->highlightRangeStart;
1380 if (d->firstIndex >= 0) {
1381 startPos = d->positionOfIndex(d->firstIndex);
1382 idx = (d->firstIndex + d->items.count()) % d->modelCount;
1384 qreal pos = d->positionOfIndex(idx);
1385 while ((pos > startPos || !d->items.count()) && d->items.count() < count) {
1386 // qDebug() << "append" << idx;
1387 QDeclarativeItem *item = d->getItem(idx);
1388 if (d->model->completePending())
1389 item->setZValue(idx+1);
1390 if (d->currentIndex == idx) {
1391 item->setFocus(true);
1392 if (QDeclarativePathViewAttached *att = d->attached(item))
1393 att->setIsCurrentItem(true);
1394 currentVisible = true;
1395 d->currentItemOffset = pos;
1396 d->currentItem = item;
1398 if (d->items.count() == 0)
1399 d->firstIndex = idx;
1400 d->items.append(item);
1401 d->updateItem(item, pos);
1402 if (d->model->completePending())
1403 d->model->completeItem();
1405 if (idx >= d->modelCount)
1407 pos = d->positionOfIndex(idx);
1410 idx = d->firstIndex - 1;
1412 idx = d->modelCount - 1;
1413 pos = d->positionOfIndex(idx);
1414 while (pos >= 0.0 && pos < startPos) {
1415 // qDebug() << "prepend" << idx;
1416 QDeclarativeItem *item = d->getItem(idx);
1417 if (d->model->completePending())
1418 item->setZValue(idx+1);
1419 if (d->currentIndex == idx) {
1420 item->setFocus(true);
1421 if (QDeclarativePathViewAttached *att = d->attached(item))
1422 att->setIsCurrentItem(true);
1423 currentVisible = true;
1424 d->currentItemOffset = pos;
1425 d->currentItem = item;
1427 d->items.prepend(item);
1428 d->updateItem(item, pos);
1429 if (d->model->completePending())
1430 d->model->completeItem();
1431 d->firstIndex = idx;
1432 idx = d->firstIndex - 1;
1434 idx = d->modelCount - 1;
1435 pos = d->positionOfIndex(idx);
1440 if (!currentVisible)
1441 d->currentItemOffset = 1.0;
1443 if (d->highlightItem && d->haveHighlightRange && d->highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) {
1444 d->updateItem(d->highlightItem, d->highlightRangeStart);
1445 if (QDeclarativePathViewAttached *att = d->attached(d->highlightItem))
1446 att->setOnPath(true);
1447 } else if (d->highlightItem && d->moveReason != QDeclarativePathViewPrivate::SetIndex) {
1448 d->updateItem(d->highlightItem, d->currentItemOffset);
1449 if (QDeclarativePathViewAttached *att = d->attached(d->highlightItem))
1450 att->setOnPath(currentVisible);
1452 while (d->itemCache.count())
1453 d->releaseItem(d->itemCache.takeLast());
1456 void QDeclarativePathView::itemsInserted(int modelIndex, int count)
1458 //XXX support animated insertion
1459 Q_D(QDeclarativePathView);
1460 if (!d->isValid() || !isComponentComplete())
1463 if (d->modelCount) {
1464 d->itemCache += d->items;
1466 if (modelIndex <= d->currentIndex) {
1467 d->currentIndex += count;
1468 emit currentIndexChanged();
1469 } else if (d->offset != 0) {
1471 d->offsetAdj += count;
1474 d->modelCount += count;
1475 if (d->flicking || d->moving) {
1480 d->updateMappedRange();
1481 d->scheduleLayout();
1483 emit countChanged();
1486 void QDeclarativePathView::itemsRemoved(int modelIndex, int count)
1488 //XXX support animated removal
1489 Q_D(QDeclarativePathView);
1490 if (!d->model || !d->modelCount || !d->model->isValid() || !d->path || !isComponentComplete())
1494 bool currentChanged = false;
1495 if (d->currentIndex >= modelIndex + count) {
1496 d->currentIndex -= count;
1497 currentChanged = true;
1498 } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) {
1499 // current item has been removed.
1500 d->currentIndex = qMin(modelIndex, d->modelCount-count-1);
1501 if (d->currentItem) {
1502 if (QDeclarativePathViewAttached *att = d->attached(d->currentItem))
1503 att->setIsCurrentItem(true);
1505 currentChanged = true;
1508 d->itemCache += d->items;
1511 bool changedOffset = false;
1512 if (modelIndex > d->currentIndex) {
1513 if (d->offset >= count) {
1514 changedOffset = true;
1516 d->offsetAdj -= count;
1520 d->modelCount -= count;
1521 if (!d->modelCount) {
1522 while (d->itemCache.count())
1523 d->releaseItem(d->itemCache.takeLast());
1525 changedOffset = true;
1526 d->tl.reset(d->moveOffset);
1531 if (!d->flicking && !d->moving && d->haveHighlightRange && d->highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange)
1535 emit offsetChanged();
1537 emit currentIndexChanged();
1538 emit countChanged();
1541 void QDeclarativePathView::itemsMoved(int /*from*/, int /*to*/, int /*count*/)
1543 Q_D(QDeclarativePathView);
1544 if (!d->isValid() || !isComponentComplete())
1547 QList<QDeclarativeItem *> removedItems = d->items;
1550 while (removedItems.count())
1551 d->releaseItem(removedItems.takeLast());
1553 // Fix current index
1554 if (d->currentIndex >= 0 && d->currentItem) {
1555 int oldCurrent = d->currentIndex;
1556 d->currentIndex = d->model->indexOf(d->currentItem, this);
1557 if (oldCurrent != d->currentIndex)
1558 emit currentIndexChanged();
1563 void QDeclarativePathView::modelReset()
1565 Q_D(QDeclarativePathView);
1566 d->modelCount = d->model->count();
1568 emit countChanged();
1571 void QDeclarativePathView::createdItem(int index, QDeclarativeItem *item)
1573 Q_D(QDeclarativePathView);
1574 if (d->requestedIndex != index) {
1576 // pre-create one metatype to share with all attached objects
1577 d->attType = new QDeclarativeOpenMetaObjectType(&QDeclarativePathViewAttached::staticMetaObject, qmlEngine(this));
1578 foreach(const QString &attr, d->path->attributes())
1579 d->attType->createProperty(attr.toUtf8());
1581 qPathViewAttachedType = d->attType;
1582 QDeclarativePathViewAttached *att = static_cast<QDeclarativePathViewAttached *>(qmlAttachedPropertiesObject<QDeclarativePathView>(item));
1583 qPathViewAttachedType = 0;
1586 att->setOnPath(false);
1588 item->setParentItem(this);
1589 d->updateItem(item, index < d->firstIndex ? 0.0 : 1.0);
1593 void QDeclarativePathView::destroyingItem(QDeclarativeItem *item)
1598 void QDeclarativePathView::ticked()
1600 Q_D(QDeclarativePathView);
1604 void QDeclarativePathView::movementEnding()
1606 Q_D(QDeclarativePathView);
1608 d->flicking = false;
1609 emit flickingChanged();
1612 if (d->moving && !d->stealMouse) {
1614 emit movingChanged();
1615 emit movementEnded();
1619 // find the item closest to the snap position
1620 int QDeclarativePathViewPrivate::calcCurrentIndex()
1623 if (modelCount && model && items.count()) {
1624 offset = qmlMod(offset, modelCount);
1626 offset += modelCount;
1627 current = qRound(qAbs(qmlMod(modelCount - offset, modelCount)));
1628 current = current % modelCount;
1634 void QDeclarativePathViewPrivate::updateCurrent()
1636 Q_Q(QDeclarativePathView);
1637 if (moveReason != Mouse)
1639 if (!modelCount || !haveHighlightRange || highlightRangeMode != QDeclarativePathView::StrictlyEnforceRange)
1642 int idx = calcCurrentIndex();
1643 if (model && idx != currentIndex) {
1644 int itemIndex = (currentIndex - firstIndex + modelCount) % modelCount;
1645 if (itemIndex < items.count()) {
1646 if (QDeclarativeItem *item = items.at(itemIndex)) {
1647 if (QDeclarativePathViewAttached *att = attached(item))
1648 att->setIsCurrentItem(false);
1653 itemIndex = (idx - firstIndex + modelCount) % modelCount;
1654 if (itemIndex < items.count()) {
1655 currentItem = items.at(itemIndex);
1656 currentItem->setFocus(true);
1657 if (QDeclarativePathViewAttached *att = attached(currentItem))
1658 att->setIsCurrentItem(true);
1660 emit q->currentIndexChanged();
1664 void QDeclarativePathViewPrivate::fixOffsetCallback(void *d)
1666 ((QDeclarativePathViewPrivate *)d)->fixOffset();
1669 void QDeclarativePathViewPrivate::fixOffset()
1671 Q_Q(QDeclarativePathView);
1672 if (model && items.count()) {
1673 if (haveHighlightRange && highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) {
1674 int curr = calcCurrentIndex();
1675 if (curr != currentIndex)
1676 q->setCurrentIndex(curr);
1683 void QDeclarativePathViewPrivate::snapToCurrent()
1685 if (!model || modelCount <= 0)
1688 qreal targetOffset = qmlMod(modelCount - currentIndex, modelCount);
1692 tl.reset(moveOffset);
1693 moveOffset.setValue(offset);
1695 const int duration = highlightMoveDuration;
1697 if (moveDirection == Positive || (moveDirection == Shortest && targetOffset - offset > modelCount/2)) {
1698 qreal distance = modelCount - targetOffset + offset;
1699 if (targetOffset > moveOffset) {
1700 tl.move(moveOffset, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * offset / distance));
1701 tl.set(moveOffset, modelCount);
1702 tl.move(moveOffset, targetOffset, QEasingCurve(offset == 0.0 ? QEasingCurve::InOutQuad : QEasingCurve::OutQuad), int(duration * (modelCount-targetOffset) / distance));
1704 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
1706 } else if (moveDirection == Negative || targetOffset - offset <= -modelCount/2) {
1707 qreal distance = modelCount - offset + targetOffset;
1708 if (targetOffset < moveOffset) {
1709 tl.move(moveOffset, modelCount, QEasingCurve(targetOffset == 0 ? QEasingCurve::InOutQuad : QEasingCurve::InQuad), int(duration * (modelCount-offset) / distance));
1710 tl.set(moveOffset, 0.0);
1711 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::OutQuad), int(duration * targetOffset / distance));
1713 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
1716 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
1718 moveDirection = Shortest;
1721 QDeclarativePathViewAttached *QDeclarativePathView::qmlAttachedProperties(QObject *obj)
1723 return new QDeclarativePathViewAttached(obj);