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 QtQml 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/qquickstate_p.h>
47 #include <private/qqmlglobal_p.h>
48 #include <private/qqmlopenmetaobject_p.h>
49 #include <private/qlistmodelinterface_p.h>
50 #include <private/qquickchangeset_p.h>
52 #include <QtGui/qevent.h>
53 #include <QtGui/qevent.h>
54 #include <QtGui/qguiapplication.h>
55 #include <QtGui/qstylehints.h>
56 #include <QtCore/qmath.h>
59 // The number of samples to use in calculating the velocity of a flick
60 #ifndef QML_FLICK_SAMPLEBUFFER
61 #define QML_FLICK_SAMPLEBUFFER 3
64 // The number of samples to discard when calculating the flick velocity.
65 // Touch panels often produce inaccurate results as the finger is lifted.
66 #ifndef QML_FLICK_DISCARDSAMPLES
67 #define QML_FLICK_DISCARDSAMPLES 1
72 inline qreal qmlMod(qreal x, qreal y)
74 #ifdef QT_USE_MATH_H_FLOATS
75 if (sizeof(qreal) == sizeof(float))
76 return fmodf(float(x), float(y));
82 static QQmlOpenMetaObjectType *qPathViewAttachedType = 0;
84 QQuickPathViewAttached::QQuickPathViewAttached(QObject *parent)
85 : QObject(parent), m_percent(-1), m_view(0), m_onPath(false), m_isCurrent(false)
87 if (qPathViewAttachedType) {
88 m_metaobject = new QQmlOpenMetaObject(this, qPathViewAttachedType);
89 m_metaobject->setCached(true);
91 m_metaobject = new QQmlOpenMetaObject(this);
95 QQuickPathViewAttached::~QQuickPathViewAttached()
99 QVariant QQuickPathViewAttached::value(const QByteArray &name) const
101 return m_metaobject->value(name);
103 void QQuickPathViewAttached::setValue(const QByteArray &name, const QVariant &val)
105 m_metaobject->setValue(name, val);
109 void QQuickPathViewPrivate::init()
113 q->setAcceptedMouseButtons(Qt::LeftButton);
114 q->setFlag(QQuickItem::ItemIsFocusScope);
115 q->setFiltersChildMouseEvents(true);
116 FAST_CONNECT(&tl, SIGNAL(updated()), q, SLOT(ticked()))
117 lastPosTime.invalidate();
118 FAST_CONNECT(&tl, SIGNAL(completed()), q, SLOT(movementEnding()))
121 QQuickItem *QQuickPathViewPrivate::getItem(int modelIndex, qreal z, bool onPath)
124 requestedIndex = modelIndex;
125 requestedOnPath = onPath;
128 QQuickItem *item = model->item(modelIndex, false);
130 QQml_setParent_noEvent(item, q);
131 item->setParentItem(q);
133 qPathViewAttachedType = attType;
134 QQuickPathViewAttached *att = static_cast<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(item));
135 qPathViewAttachedType = 0;
137 att->setOnPath(onPath);
138 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
139 itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
145 void QQuickPathView::createdItem(int index, QQuickItem *item)
148 if (d->requestedIndex != index) {
149 qPathViewAttachedType = d->attachedType();
150 QQuickPathViewAttached *att = static_cast<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(item));
151 qPathViewAttachedType = 0;
154 att->setOnPath(false);
156 item->setParentItem(this);
157 QQml_setParent_noEvent(item, this);
158 d->updateItem(item, index < d->firstIndex ? 0.0 : 1.0);
160 d->requestedIndex = -1;
166 void QQuickPathView::initItem(int index, QQuickItem *item)
169 if (d->requestedIndex == index) {
170 item->setParentItem(this);
171 qPathViewAttachedType = d->attachedType();
172 QQuickPathViewAttached *att = static_cast<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(item));
173 qPathViewAttachedType = 0;
176 qreal percent = d->positionOfIndex(index);
177 foreach (const QString &attr, d->path->attributes())
178 att->setValue(attr.toUtf8(), d->path->attributeAt(attr, percent));
179 item->setZ(d->requestedZ);
181 att->setOnPath(d->requestedOnPath);
186 void QQuickPathViewPrivate::releaseItem(QQuickItem *item)
190 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
191 itemPrivate->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
192 if (model->release(item) == 0) {
193 // item was not destroyed, and we no longer reference it.
194 if (QQuickPathViewAttached *att = attached(item))
195 att->setOnPath(false);
199 QQuickPathViewAttached *QQuickPathViewPrivate::attached(QQuickItem *item)
201 return static_cast<QQuickPathViewAttached *>(qmlAttachedPropertiesObject<QQuickPathView>(item, false));
204 QQmlOpenMetaObjectType *QQuickPathViewPrivate::attachedType()
208 // pre-create one metatype to share with all attached objects
209 attType = new QQmlOpenMetaObjectType(&QQuickPathViewAttached::staticMetaObject, qmlEngine(q));
210 foreach (const QString &attr, path->attributes())
211 attType->createProperty(attr.toUtf8());
217 void QQuickPathViewPrivate::clear()
220 releaseItem(currentItem);
223 for (int i=0; i<items.count(); i++){
224 QQuickItem *p = items[i];
230 void QQuickPathViewPrivate::updateMappedRange()
232 if (model && pathItems != -1 && pathItems < modelCount)
233 mappedRange = qreal(pathItems)/modelCount;
238 qreal QQuickPathViewPrivate::positionOfIndex(qreal index) const
242 if (model && index >= 0 && index < modelCount) {
244 if (haveHighlightRange && highlightRangeMode != QQuickPathView::NoHighlightRange)
245 start = highlightRangeStart;
246 qreal globalPos = index + offset;
247 globalPos = qmlMod(globalPos, qreal(modelCount)) / modelCount;
248 if (pathItems != -1 && pathItems < modelCount) {
249 globalPos += start * mappedRange;
250 globalPos = qmlMod(globalPos, 1.0);
251 if (globalPos < mappedRange)
252 pos = globalPos / mappedRange;
254 pos = qmlMod(globalPos + start, 1.0);
261 void QQuickPathViewPrivate::createHighlight()
264 if (!q->isComponentComplete())
267 bool changed = false;
269 highlightItem->setParentItem(0);
270 highlightItem->deleteLater();
275 QQuickItem *item = 0;
276 if (highlightComponent) {
277 QQmlContext *creationContext = highlightComponent->creationContext();
278 QQmlContext *highlightContext = new QQmlContext(
279 creationContext ? creationContext : qmlContext(q));
280 QObject *nobj = highlightComponent->create(highlightContext);
282 QQml_setParent_noEvent(highlightContext, nobj);
283 item = qobject_cast<QQuickItem *>(nobj);
287 delete highlightContext;
290 item = new QQuickItem;
293 QQml_setParent_noEvent(item, q);
294 item->setParentItem(q);
295 highlightItem = item;
299 emit q->highlightItemChanged();
302 void QQuickPathViewPrivate::updateHighlight()
305 if (!q->isComponentComplete() || !isValid())
308 if (haveHighlightRange && highlightRangeMode == QQuickPathView::StrictlyEnforceRange) {
309 updateItem(highlightItem, highlightRangeStart);
311 qreal target = currentIndex;
314 tl.reset(moveHighlight);
315 moveHighlight.setValue(highlightPosition);
317 const int duration = highlightMoveDuration;
319 if (target - highlightPosition > modelCount/2) {
321 qreal distance = modelCount - target + highlightPosition;
322 tl.move(moveHighlight, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * highlightPosition / distance));
323 tl.set(moveHighlight, modelCount-0.01);
324 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * (modelCount-target) / distance));
325 } else if (target - highlightPosition <= -modelCount/2) {
327 qreal distance = modelCount - highlightPosition + target;
328 tl.move(moveHighlight, modelCount-0.01, QEasingCurve(QEasingCurve::InQuad), int(duration * (modelCount-highlightPosition) / distance));
329 tl.set(moveHighlight, 0.0);
330 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * target / distance));
332 highlightUp = highlightPosition - target < 0;
333 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::InOutQuad), duration);
339 void QQuickPathViewPrivate::setHighlightPosition(qreal pos)
341 if (pos != highlightPosition) {
344 if (haveHighlightRange && highlightRangeMode != QQuickPathView::NoHighlightRange) {
345 start = highlightRangeStart;
346 end = highlightRangeEnd;
349 qreal range = qreal(modelCount);
350 // calc normalized position of highlight relative to offset
351 qreal relativeHighlight = qmlMod(pos + offset, range) / range;
353 if (!highlightUp && relativeHighlight > end * mappedRange) {
354 qreal diff = 1.0 - relativeHighlight;
355 setOffset(offset + diff * range);
356 } else if (highlightUp && relativeHighlight >= (end - start) * mappedRange) {
357 qreal diff = relativeHighlight - (end - start) * mappedRange;
358 setOffset(offset - diff * range - 0.00001);
361 highlightPosition = pos;
362 qreal pathPos = positionOfIndex(pos);
363 updateItem(highlightItem, pathPos);
364 if (QQuickPathViewAttached *att = attached(highlightItem))
365 att->setOnPath(pathPos != -1.0);
369 void QQuickPathView::pathUpdated()
372 QList<QQuickItem*>::iterator it = d->items.begin();
373 while (it != d->items.end()) {
374 QQuickItem *item = *it;
375 if (QQuickPathViewAttached *att = d->attached(item))
382 void QQuickPathViewPrivate::updateItem(QQuickItem *item, qreal percent)
384 if (QQuickPathViewAttached *att = attached(item)) {
385 if (qFuzzyCompare(att->m_percent, percent))
387 att->m_percent = percent;
388 foreach (const QString &attr, path->attributes())
389 att->setValue(attr.toUtf8(), path->attributeAt(attr, percent));
391 QPointF pf = path->pointAt(percent);
392 item->setX(pf.x() - item->width()/2);
393 item->setY(pf.y() - item->height()/2);
396 void QQuickPathViewPrivate::regenerate()
399 if (!q->isComponentComplete())
413 \qmlclass PathView QQuickPathView
414 \inqmlmodule QtQuick 2
415 \ingroup qml-view-elements
416 \brief The PathView element lays out model-provided items on a path.
419 A PathView displays data from models created from built-in QML elements like ListModel
420 and XmlListModel, or custom model classes defined in C++ that inherit from
423 The view has a \l model, which defines the data to be displayed, and
424 a \l delegate, which defines how the data should be displayed.
425 The \l delegate is instantiated for each item on the \l path.
426 The items may be flicked to move them along the path.
428 For example, if there is a simple list model defined in a file \c ContactModel.qml like this:
430 \snippet doc/src/snippets/qml/pathview/ContactModel.qml 0
432 This data can be represented as a PathView, like this:
434 \snippet doc/src/snippets/qml/pathview/pathview.qml 0
438 (Note the above example uses PathAttribute to scale and modify the
439 opacity of the items as they rotate. This additional code can be seen in the
440 PathAttribute documentation.)
442 PathView does not automatically handle keyboard navigation. This is because
443 the keys to use for navigation will depend upon the shape of the path. Navigation
444 can be added quite simply by setting \c focus to \c true and calling
445 \l decrementCurrentIndex() or \l incrementCurrentIndex(), for example to navigate
446 using the left and right arrow keys:
452 Keys.onLeftPressed: decrementCurrentIndex()
453 Keys.onRightPressed: incrementCurrentIndex()
457 The path view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
459 Delegates are instantiated as needed and may be destroyed at any time.
460 State should \e never be stored in a delegate.
462 PathView attaches a number of properties to the root item of the delegate, for example
463 \c {PathView.isCurrentItem}. In the following example, the root delegate item can access
464 this attached property directly as \c PathView.isCurrentItem, while the child
465 \c nameText object must refer to this property as \c wrapper.PathView.isCurrentItem.
467 \snippet doc/src/snippets/qml/pathview/pathview.qml 1
469 \bold Note that views do not enable \e clip automatically. If the view
470 is not clipped by another item or the screen, it will be necessary
471 to set \e {clip: true} in order to have the out of view items clipped
474 \sa Path, {declarative/modelviews/pathview}{PathView example}
477 QQuickPathView::QQuickPathView(QQuickItem *parent)
478 : QQuickItem(*(new QQuickPathViewPrivate), parent)
484 QQuickPathView::~QQuickPathView()
489 d->attType->release();
495 \qmlattachedproperty PathView QtQuick2::PathView::view
496 This attached property holds the view that manages this delegate instance.
498 It is attached to each instance of the delegate.
502 \qmlattachedproperty bool QtQuick2::PathView::onPath
503 This attached property holds whether the item is currently on the path.
505 If a pathItemCount has been set, it is possible that some items may
506 be instantiated, but not considered to be currently on the path.
507 Usually, these items would be set invisible, for example:
512 visible: PathView.onPath
518 It is attached to each instance of the delegate.
522 \qmlattachedproperty bool QtQuick2::PathView::isCurrentItem
523 This attached property is true if this delegate is the current item; otherwise false.
525 It is attached to each instance of the delegate.
527 This property may be used to adjust the appearance of the current item.
529 \snippet doc/src/snippets/qml/pathview/pathview.qml 1
533 \qmlproperty model QtQuick2::PathView::model
534 This property holds the model providing data for the view.
536 The model provides a set of data that is used to create the items for the view.
537 For large or dynamic datasets the model is usually provided by a C++ model object.
538 Models can also be created directly in QML, using the ListModel element.
540 \sa {qmlmodels}{Data Models}
542 QVariant QQuickPathView::model() const
544 Q_D(const QQuickPathView);
545 return d->modelVariant;
548 void QQuickPathView::setModel(const QVariant &model)
551 if (d->modelVariant == model)
555 disconnect(d->model, SIGNAL(modelUpdated(QQuickChangeSet,bool)),
556 this, SLOT(modelUpdated(QQuickChangeSet,bool)));
557 disconnect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*)));
558 disconnect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*)));
559 for (int i=0; i<d->items.count(); i++){
560 QQuickItem *p = d->items[i];
566 d->modelVariant = model;
567 QObject *object = qvariant_cast<QObject*>(model);
568 QQuickVisualModel *vim = 0;
569 if (object && (vim = qobject_cast<QQuickVisualModel *>(object))) {
577 d->model = new QQuickVisualDataModel(qmlContext(this));
579 if (isComponentComplete())
580 static_cast<QQuickVisualDataModel *>(d->model.data())->componentComplete();
582 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
583 dataModel->setModel(model);
587 connect(d->model, SIGNAL(modelUpdated(QQuickChangeSet,bool)),
588 this, SLOT(modelUpdated(QQuickChangeSet,bool)));
589 connect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*)));
590 connect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*)));
591 d->modelCount = d->model->count();
592 if (d->model->count())
593 d->offset = qmlMod(d->offset, qreal(d->model->count()));
595 d->offset = d->model->count() + d->offset;
598 if (d->currentIndex < d->modelCount)
599 setOffset(qmlMod(d->modelCount - d->currentIndex, d->modelCount));
607 \qmlproperty int QtQuick2::PathView::count
608 This property holds the number of items in the model.
610 int QQuickPathView::count() const
612 Q_D(const QQuickPathView);
613 return d->model ? d->modelCount : 0;
617 \qmlproperty Path QtQuick2::PathView::path
618 This property holds the path used to lay out the items.
619 For more information see the \l Path documentation.
621 QQuickPath *QQuickPathView::path() const
623 Q_D(const QQuickPathView);
627 void QQuickPathView::setPath(QQuickPath *path)
633 disconnect(d->path, SIGNAL(changed()), this, SLOT(pathUpdated()));
635 connect(d->path, SIGNAL(changed()), this, SLOT(pathUpdated()));
636 if (d->isValid() && isComponentComplete()) {
639 d->attType->release();
648 \qmlproperty int QtQuick2::PathView::currentIndex
649 This property holds the index of the current item.
651 int QQuickPathView::currentIndex() const
653 Q_D(const QQuickPathView);
654 return d->currentIndex;
657 void QQuickPathView::setCurrentIndex(int idx)
660 if (d->model && d->modelCount)
661 idx = qAbs(idx % d->modelCount);
662 if (d->model && (idx != d->currentIndex || !d->currentItem)) {
663 if (d->currentItem) {
664 if (QQuickPathViewAttached *att = d->attached(d->currentItem))
665 att->setIsCurrentItem(false);
666 d->releaseItem(d->currentItem);
668 int oldCurrentIdx = d->currentIndex;
669 QQuickItem *oldCurrentItem = d->currentItem;
671 d->moveReason = QQuickPathViewPrivate::SetIndex;
672 d->currentIndex = idx;
674 d->createCurrentItem();
675 if (d->haveHighlightRange && d->highlightRangeMode == QQuickPathView::StrictlyEnforceRange)
677 d->currentItemOffset = d->positionOfIndex(d->currentIndex);
678 d->updateHighlight();
680 if (oldCurrentIdx != d->currentIndex)
681 emit currentIndexChanged();
682 if (oldCurrentItem != d->currentItem)
683 emit currentItemChanged();
687 QQuickItem *QQuickPathView::currentItem() const
689 Q_D(const QQuickPathView);
690 return d->currentItem;
694 \qmlmethod QtQuick2::PathView::incrementCurrentIndex()
696 Increments the current index.
698 \bold Note: methods should only be called after the Component has completed.
700 void QQuickPathView::incrementCurrentIndex()
703 d->moveDirection = QQuickPathViewPrivate::Positive;
704 setCurrentIndex(currentIndex()+1);
708 \qmlmethod QtQuick2::PathView::decrementCurrentIndex()
710 Decrements the current index.
712 \bold Note: methods should only be called after the Component has completed.
714 void QQuickPathView::decrementCurrentIndex()
717 if (d->model && d->modelCount) {
718 int idx = currentIndex()-1;
720 idx = d->modelCount - 1;
721 d->moveDirection = QQuickPathViewPrivate::Negative;
722 setCurrentIndex(idx);
727 \qmlproperty real QtQuick2::PathView::offset
729 The offset specifies how far along the path the items are from their initial positions.
730 This is a real number that ranges from 0.0 to the count of items in the model.
732 qreal QQuickPathView::offset() const
734 Q_D(const QQuickPathView);
738 void QQuickPathView::setOffset(qreal offset)
741 d->setOffset(offset);
745 void QQuickPathViewPrivate::setOffset(qreal o)
749 if (isValid() && q->isComponentComplete()) {
750 offset = qmlMod(o, qreal(modelCount));
752 offset += qreal(modelCount);
757 emit q->offsetChanged();
761 void QQuickPathViewPrivate::setAdjustedOffset(qreal o)
763 setOffset(o+offsetAdj);
767 \qmlproperty Component QtQuick2::PathView::highlight
768 This property holds the component to use as the highlight.
770 An instance of the highlight component will be created for each view.
771 The geometry of the resultant component instance will be managed by the view
772 so as to stay with the current item.
774 The below example demonstrates how to make a simple highlight. Note the use
775 of the \l{PathView::onPath}{PathView.onPath} attached property to ensure that
776 the highlight is hidden when flicked away from the path.
781 visible: PathView.onPath
787 \sa highlightItem, highlightRangeMode
790 QQmlComponent *QQuickPathView::highlight() const
792 Q_D(const QQuickPathView);
793 return d->highlightComponent;
796 void QQuickPathView::setHighlight(QQmlComponent *highlight)
799 if (highlight != d->highlightComponent) {
800 d->highlightComponent = highlight;
801 d->createHighlight();
802 d->updateHighlight();
803 emit highlightChanged();
808 \qmlproperty Item QtQuick2::PathView::highlightItem
810 \c highlightItem holds the highlight item, which was created
811 from the \l highlight component.
815 QQuickItem *QQuickPathView::highlightItem()
817 Q_D(const QQuickPathView);
818 return d->highlightItem;
821 \qmlproperty real QtQuick2::PathView::preferredHighlightBegin
822 \qmlproperty real QtQuick2::PathView::preferredHighlightEnd
823 \qmlproperty enumeration QtQuick2::PathView::highlightRangeMode
825 These properties set the preferred range of the highlight (current item)
826 within the view. The preferred values must be in the range 0.0-1.0.
828 If highlightRangeMode is set to \e PathView.NoHighlightRange
830 If highlightRangeMode is set to \e PathView.ApplyRange the view will
831 attempt to maintain the highlight within the range, however
832 the highlight can move outside of the range at the ends of the path
833 or due to a mouse interaction.
835 If highlightRangeMode is set to \e PathView.StrictlyEnforceRange the highlight will never
836 move outside of the range. This means that the current item will change
837 if a keyboard or mouse action would cause the highlight to move
838 outside of the range.
840 Note that this is the correct way to influence where the
841 current item ends up when the view moves. For example, if you want the
842 currently selected item to be in the middle of the path, then set the
843 highlight range to be 0.5,0.5 and highlightRangeMode to PathView.StrictlyEnforceRange.
844 Then, when the path scrolls,
845 the currently selected item will be the item at that position. This also applies to
846 when the currently selected item changes - it will scroll to within the preferred
847 highlight range. Furthermore, the behaviour of the current item index will occur
848 whether or not a highlight exists.
850 The default value is \e PathView.StrictlyEnforceRange.
852 Note that a valid range requires preferredHighlightEnd to be greater
853 than or equal to preferredHighlightBegin.
855 qreal QQuickPathView::preferredHighlightBegin() const
857 Q_D(const QQuickPathView);
858 return d->highlightRangeStart;
861 void QQuickPathView::setPreferredHighlightBegin(qreal start)
864 if (d->highlightRangeStart == start || start < 0 || start > 1.0)
866 d->highlightRangeStart = start;
867 d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
869 emit preferredHighlightBeginChanged();
872 qreal QQuickPathView::preferredHighlightEnd() const
874 Q_D(const QQuickPathView);
875 return d->highlightRangeEnd;
878 void QQuickPathView::setPreferredHighlightEnd(qreal end)
881 if (d->highlightRangeEnd == end || end < 0 || end > 1.0)
883 d->highlightRangeEnd = end;
884 d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
886 emit preferredHighlightEndChanged();
889 QQuickPathView::HighlightRangeMode QQuickPathView::highlightRangeMode() const
891 Q_D(const QQuickPathView);
892 return d->highlightRangeMode;
895 void QQuickPathView::setHighlightRangeMode(HighlightRangeMode mode)
898 if (d->highlightRangeMode == mode)
900 d->highlightRangeMode = mode;
901 d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
902 if (d->haveHighlightRange) {
906 emit highlightRangeModeChanged();
910 \qmlproperty int QtQuick2::PathView::highlightMoveDuration
911 This property holds the move animation duration of the highlight delegate.
913 If the highlightRangeMode is StrictlyEnforceRange then this property
914 determines the speed that the items move along the path.
916 The default value for the duration is 300ms.
918 int QQuickPathView::highlightMoveDuration() const
920 Q_D(const QQuickPathView);
921 return d->highlightMoveDuration;
924 void QQuickPathView::setHighlightMoveDuration(int duration)
927 if (d->highlightMoveDuration == duration)
929 d->highlightMoveDuration = duration;
930 emit highlightMoveDurationChanged();
934 \qmlproperty real QtQuick2::PathView::dragMargin
935 This property holds the maximum distance from the path that initiate mouse dragging.
937 By default the path can only be dragged by clicking on an item. If
938 dragMargin is greater than zero, a drag can be initiated by clicking
939 within dragMargin pixels of the path.
941 qreal QQuickPathView::dragMargin() const
943 Q_D(const QQuickPathView);
944 return d->dragMargin;
947 void QQuickPathView::setDragMargin(qreal dragMargin)
950 if (d->dragMargin == dragMargin)
952 d->dragMargin = dragMargin;
953 emit dragMarginChanged();
957 \qmlproperty real QtQuick2::PathView::flickDeceleration
958 This property holds the rate at which a flick will decelerate.
962 qreal QQuickPathView::flickDeceleration() const
964 Q_D(const QQuickPathView);
965 return d->deceleration;
968 void QQuickPathView::setFlickDeceleration(qreal dec)
971 if (d->deceleration == dec)
973 d->deceleration = dec;
974 emit flickDecelerationChanged();
978 \qmlproperty bool QtQuick2::PathView::interactive
980 A user cannot drag or flick a PathView that is not interactive.
982 This property is useful for temporarily disabling flicking. This allows
983 special interaction with PathView's children.
985 bool QQuickPathView::isInteractive() const
987 Q_D(const QQuickPathView);
988 return d->interactive;
991 void QQuickPathView::setInteractive(bool interactive)
994 if (interactive != d->interactive) {
995 d->interactive = interactive;
998 emit interactiveChanged();
1003 \qmlproperty bool QtQuick2::PathView::moving
1005 This property holds whether the view is currently moving
1006 due to the user either dragging or flicking the view.
1008 bool QQuickPathView::isMoving() const
1010 Q_D(const QQuickPathView);
1015 \qmlproperty bool QtQuick2::PathView::flicking
1017 This property holds whether the view is currently moving
1018 due to the user flicking the view.
1020 bool QQuickPathView::isFlicking() const
1022 Q_D(const QQuickPathView);
1027 \qmlsignal QtQuick2::PathView::onMovementStarted()
1029 This handler is called when the view begins moving due to user
1034 \qmlsignal QtQuick2::PathView::onMovementEnded()
1036 This handler is called when the view stops moving due to user
1037 interaction. If a flick was generated, this handler will
1038 be triggered once the flick stops. If a flick was not
1039 generated, the handler will be triggered when the
1040 user stops dragging - i.e. a mouse or touch release.
1044 \qmlsignal QtQuick2::PathView::onFlickStarted()
1046 This handler is called when the view is flicked. A flick
1047 starts from the point that the mouse or touch is released,
1048 while still in motion.
1052 \qmlsignal QtQuick2::PathView::onFlickEnded()
1054 This handler is called when the view stops moving due to a flick.
1058 \qmlproperty Component QtQuick2::PathView::delegate
1060 The delegate provides a template defining each item instantiated by the view.
1061 The index is exposed as an accessible \c index property. Properties of the
1062 model are also available depending upon the type of \l {qmlmodels}{Data Model}.
1064 The number of elements in the delegate has a direct effect on the
1065 flicking performance of the view when pathItemCount is specified. If at all possible, place functionality
1066 that is not needed for the normal display of the delegate in a \l Loader which
1067 can load additional elements when needed.
1069 Note that the PathView will layout the items based on the size of the root
1070 item in the delegate.
1072 Here is an example delegate:
1073 \snippet doc/src/snippets/qml/pathview/pathview.qml 1
1075 QQmlComponent *QQuickPathView::delegate() const
1077 Q_D(const QQuickPathView);
1079 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
1080 return dataModel->delegate();
1086 void QQuickPathView::setDelegate(QQmlComponent *delegate)
1088 Q_D(QQuickPathView);
1089 if (delegate == this->delegate())
1092 d->model = new QQuickVisualDataModel(qmlContext(this));
1095 if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model)) {
1096 int oldCount = dataModel->count();
1097 dataModel->setDelegate(delegate);
1098 d->modelCount = dataModel->count();
1100 if (oldCount != dataModel->count())
1101 emit countChanged();
1102 emit delegateChanged();
1107 \qmlproperty int QtQuick2::PathView::pathItemCount
1108 This property holds the number of items visible on the path at any one time.
1110 int QQuickPathView::pathItemCount() const
1112 Q_D(const QQuickPathView);
1113 return d->pathItems;
1116 void QQuickPathView::setPathItemCount(int i)
1118 Q_D(QQuickPathView);
1119 if (i == d->pathItems)
1124 d->updateMappedRange();
1125 if (d->isValid() && isComponentComplete()) {
1128 emit pathItemCountChanged();
1131 QPointF QQuickPathViewPrivate::pointNear(const QPointF &point, qreal *nearPercent) const
1133 qreal samples = qMin(path->path().length()/5, qreal(500.0));
1134 qreal res = path->path().length()/samples;
1136 qreal mindist = 1e10; // big number
1137 QPointF nearPoint = path->pointAt(0);
1141 for (qreal i=1; i < samples; i++) {
1142 QPointF pt = path->pointAt(i/samples);
1143 QPointF diff = pt - point;
1144 qreal dist = diff.x()*diff.x() + diff.y()*diff.y();
1145 if (dist < mindist) {
1153 qreal approxPc = nearPc;
1154 for (qreal i = approxPc-1.0; i < approxPc+1.0; i += 1/(2*res)) {
1155 QPointF pt = path->pointAt(i/samples);
1156 QPointF diff = pt - point;
1157 qreal dist = diff.x()*diff.x() + diff.y()*diff.y();
1158 if (dist < mindist) {
1166 *nearPercent = nearPc / samples;
1171 void QQuickPathViewPrivate::addVelocitySample(qreal v)
1173 velocityBuffer.append(v);
1174 if (velocityBuffer.count() > QML_FLICK_SAMPLEBUFFER)
1175 velocityBuffer.remove(0);
1178 qreal QQuickPathViewPrivate::calcVelocity() const
1181 if (velocityBuffer.count() > QML_FLICK_DISCARDSAMPLES) {
1182 int count = velocityBuffer.count()-QML_FLICK_DISCARDSAMPLES;
1183 for (int i = 0; i < count; ++i) {
1184 qreal v = velocityBuffer.at(i);
1192 void QQuickPathView::mousePressEvent(QMouseEvent *event)
1194 Q_D(QQuickPathView);
1195 if (d->interactive) {
1196 d->handleMousePressEvent(event);
1199 QQuickItem::mousePressEvent(event);
1203 void QQuickPathViewPrivate::handleMousePressEvent(QMouseEvent *event)
1205 Q_Q(QQuickPathView);
1206 if (!interactive || !items.count())
1208 velocityBuffer.clear();
1209 QPointF scenePoint = q->mapToScene(event->localPos());
1211 for (; idx < items.count(); ++idx) {
1212 QRectF rect = items.at(idx)->boundingRect();
1213 rect = items.at(idx)->mapRectToScene(rect);
1214 if (rect.contains(scenePoint))
1217 if (idx == items.count() && dragMargin == 0.) // didn't click on an item
1220 startPoint = pointNear(event->localPos(), &startPc);
1221 if (idx == items.count()) {
1222 qreal distance = qAbs(event->localPos().x() - startPoint.x()) + qAbs(event->localPos().y() - startPoint.y());
1223 if (distance > dragMargin)
1228 if (tl.isActive() && flicking && flickDuration && qreal(tl.time())/flickDuration < 0.8)
1229 stealMouse = true; // If we've been flicked then steal the click.
1235 QQuickItemPrivate::start(lastPosTime);
1239 void QQuickPathView::mouseMoveEvent(QMouseEvent *event)
1241 Q_D(QQuickPathView);
1242 if (d->interactive) {
1243 d->handleMouseMoveEvent(event);
1245 setKeepMouseGrab(true);
1248 QQuickItem::mouseMoveEvent(event);
1252 void QQuickPathViewPrivate::handleMouseMoveEvent(QMouseEvent *event)
1254 Q_Q(QQuickPathView);
1255 if (!interactive || !lastPosTime.isValid())
1259 QPointF pathPoint = pointNear(event->localPos(), &newPc);
1261 QPointF delta = pathPoint - startPoint;
1262 if (qAbs(delta.x()) > qApp->styleHints()->startDragDistance() || qAbs(delta.y()) > qApp->styleHints()->startDragDistance()) {
1269 moveReason = QQuickPathViewPrivate::Mouse;
1270 qreal diff = (newPc - startPc)*modelCount*mappedRange;
1272 q->setOffset(offset + diff);
1274 if (diff > modelCount/2)
1276 else if (diff < -modelCount/2)
1279 lastElapsed = QQuickItemPrivate::restart(lastPosTime);
1282 addVelocitySample(diff / (qreal(lastElapsed) / 1000.));
1286 emit q->movingChanged();
1287 emit q->movementStarted();
1292 void QQuickPathView::mouseReleaseEvent(QMouseEvent *event)
1294 Q_D(QQuickPathView);
1295 if (d->interactive) {
1296 d->handleMouseReleaseEvent(event);
1300 QQuickItem::mouseReleaseEvent(event);
1304 void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *)
1306 Q_Q(QQuickPathView);
1308 q->setKeepMouseGrab(false);
1309 if (!interactive || !lastPosTime.isValid())
1312 qreal velocity = calcVelocity();
1313 if (model && modelCount && qAbs(velocity) > 0.5) {
1314 qreal count = pathItems == -1 ? modelCount : pathItems;
1315 if (qAbs(velocity) > count * 2) // limit velocity
1316 velocity = (velocity > 0 ? count : -count) * 2;
1317 // Calculate the distance to be travelled
1318 qreal v2 = velocity*velocity;
1319 qreal accel = deceleration/10;
1321 if (haveHighlightRange && highlightRangeMode == QQuickPathView::StrictlyEnforceRange) {
1322 // + 0.25 to encourage moving at least one item in the flick direction
1323 dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2.0) + 0.25));
1324 // round to nearest item.
1326 dist = qRound(dist + offset) - offset;
1328 dist = qRound(dist - offset) + offset;
1329 // Calculate accel required to stop on item boundary
1334 accel = v2 / (2.0f * qAbs(dist));
1337 dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2.0)));
1339 flickDuration = static_cast<int>(1000 * qAbs(velocity) / accel);
1341 moveOffset.setValue(offset);
1342 tl.accel(moveOffset, velocity, accel, dist);
1343 tl.callback(QQuickTimeLineCallback(&moveOffset, fixOffsetCallback, this));
1346 emit q->flickingChanged();
1347 emit q->flickStarted();
1353 lastPosTime.invalidate();
1355 q->movementEnding();
1358 bool QQuickPathView::sendMouseEvent(QMouseEvent *event)
1360 Q_D(QQuickPathView);
1361 QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
1362 QQuickCanvas *c = canvas();
1363 QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
1364 bool stealThisEvent = d->stealMouse;
1365 if ((stealThisEvent || myRect.contains(event->windowPos())) && (!grabber || !grabber->keepMouseGrab())) {
1366 QMouseEvent mouseEvent(event->type(), mapFromScene(event->windowPos()), event->windowPos(), event->screenPos(),
1367 event->button(), event->buttons(), event->modifiers());
1368 mouseEvent.setAccepted(false);
1370 switch (mouseEvent.type()) {
1371 case QEvent::MouseMove:
1372 d->handleMouseMoveEvent(&mouseEvent);
1374 case QEvent::MouseButtonPress:
1375 d->handleMousePressEvent(&mouseEvent);
1376 stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
1378 case QEvent::MouseButtonRelease:
1379 d->handleMouseReleaseEvent(&mouseEvent);
1384 grabber = c->mouseGrabberItem();
1385 if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this)
1388 return d->stealMouse;
1389 } else if (d->lastPosTime.isValid()) {
1390 d->lastPosTime.invalidate();
1393 if (event->type() == QEvent::MouseButtonRelease)
1394 d->stealMouse = false;
1398 bool QQuickPathView::childMouseEventFilter(QQuickItem *i, QEvent *e)
1400 Q_D(QQuickPathView);
1401 if (!isVisible() || !d->interactive)
1402 return QQuickItem::childMouseEventFilter(i, e);
1404 switch (e->type()) {
1405 case QEvent::MouseButtonPress:
1406 case QEvent::MouseMove:
1407 case QEvent::MouseButtonRelease:
1408 return sendMouseEvent(static_cast<QMouseEvent *>(e));
1413 return QQuickItem::childMouseEventFilter(i, e);
1416 void QQuickPathView::mouseUngrabEvent()
1418 Q_D(QQuickPathView);
1419 if (d->stealMouse) {
1420 // if our mouse grab has been removed (probably by a Flickable),
1422 d->stealMouse = false;
1423 setKeepMouseGrab(false);
1424 d->lastPosTime.invalidate();
1428 void QQuickPathView::updatePolish()
1430 QQuickItem::updatePolish();
1434 void QQuickPathView::componentComplete()
1436 Q_D(QQuickPathView);
1437 if (d->model && d->ownModel)
1438 static_cast<QQuickVisualDataModel *>(d->model.data())->componentComplete();
1440 QQuickItem::componentComplete();
1442 d->createHighlight();
1443 // It is possible that a refill has already happended to to Path
1444 // bindings being handled in the componentComplete(). If so
1445 // don't do it again.
1446 if (d->items.count() == 0 && d->model) {
1447 d->modelCount = d->model->count();
1450 d->updateHighlight();
1453 emit countChanged();
1456 void QQuickPathView::refill()
1458 Q_D(QQuickPathView);
1459 if (!d->isValid() || !isComponentComplete())
1462 d->layoutScheduled = false;
1463 bool currentVisible = false;
1465 // first move existing items and remove items off path
1466 int idx = d->firstIndex;
1467 QList<QQuickItem*>::iterator it = d->items.begin();
1468 while (it != d->items.end()) {
1469 qreal pos = d->positionOfIndex(idx);
1470 QQuickItem *item = *it;
1472 d->updateItem(item, pos);
1473 if (idx == d->currentIndex) {
1474 currentVisible = true;
1475 d->currentItemOffset = pos;
1479 // qDebug() << "release";
1480 d->updateItem(item, 1.0);
1481 d->releaseItem(item);
1482 if (it == d->items.begin()) {
1483 if (++d->firstIndex >= d->modelCount)
1486 it = d->items.erase(it);
1489 if (idx >= d->modelCount)
1492 if (!d->items.count())
1495 bool waiting = false;
1496 if (d->modelCount) {
1497 // add items to beginning and end
1498 int count = d->pathItems == -1 ? d->modelCount : qMin(d->pathItems, d->modelCount);
1499 if (d->items.count() < count) {
1500 int idx = qRound(d->modelCount - d->offset) % d->modelCount;
1501 qreal startPos = 0.0;
1502 if (d->haveHighlightRange && d->highlightRangeMode != QQuickPathView::NoHighlightRange)
1503 startPos = d->highlightRangeStart;
1504 if (d->firstIndex >= 0) {
1505 startPos = d->positionOfIndex(d->firstIndex);
1506 idx = (d->firstIndex + d->items.count()) % d->modelCount;
1508 qreal pos = d->positionOfIndex(idx);
1509 while ((pos > startPos || !d->items.count()) && d->items.count() < count) {
1510 // qDebug() << "append" << idx;
1511 QQuickItem *item = d->getItem(idx, idx+1);
1516 if (d->currentIndex == idx) {
1517 currentVisible = true;
1518 d->currentItemOffset = pos;
1520 if (d->items.count() == 0)
1521 d->firstIndex = idx;
1522 d->items.append(item);
1523 d->updateItem(item, pos);
1525 if (idx >= d->modelCount)
1527 pos = d->positionOfIndex(idx);
1530 idx = d->firstIndex - 1;
1532 idx = d->modelCount - 1;
1533 pos = d->positionOfIndex(idx);
1534 while (!waiting && (pos >= 0.0 && pos < startPos) && d->items.count() < count) {
1535 // qDebug() << "prepend" << idx;
1536 QQuickItem *item = d->getItem(idx, idx+1);
1541 if (d->currentIndex == idx) {
1542 currentVisible = true;
1543 d->currentItemOffset = pos;
1545 d->items.prepend(item);
1546 d->updateItem(item, pos);
1547 d->firstIndex = idx;
1548 idx = d->firstIndex - 1;
1550 idx = d->modelCount - 1;
1551 pos = d->positionOfIndex(idx);
1556 if (!currentVisible) {
1557 d->currentItemOffset = 1.0;
1558 if (d->currentItem) {
1559 if (QQuickPathViewAttached *att = d->attached(d->currentItem))
1560 att->setOnPath(false);
1561 } else if (!waiting && d->currentIndex >= 0 && d->currentIndex < d->modelCount) {
1562 if ((d->currentItem = d->getItem(d->currentIndex, d->currentIndex, false))) {
1563 d->updateItem(d->currentItem, d->currentIndex < d->firstIndex ? 0.0 : 1.0);
1564 if (QQuickPathViewAttached *att = d->attached(d->currentItem))
1565 att->setIsCurrentItem(true);
1568 } else if (!waiting && !d->currentItem) {
1569 if ((d->currentItem = d->getItem(d->currentIndex, d->currentIndex, true))) {
1570 d->currentItem->setFocus(true);
1571 if (QQuickPathViewAttached *att = d->attached(d->currentItem))
1572 att->setIsCurrentItem(true);
1576 if (d->highlightItem && d->haveHighlightRange && d->highlightRangeMode == QQuickPathView::StrictlyEnforceRange) {
1577 d->updateItem(d->highlightItem, d->highlightRangeStart);
1578 if (QQuickPathViewAttached *att = d->attached(d->highlightItem))
1579 att->setOnPath(true);
1580 } else if (d->highlightItem && d->moveReason != QQuickPathViewPrivate::SetIndex) {
1581 d->updateItem(d->highlightItem, d->currentItemOffset);
1582 if (QQuickPathViewAttached *att = d->attached(d->highlightItem))
1583 att->setOnPath(currentVisible);
1585 while (d->itemCache.count())
1586 d->releaseItem(d->itemCache.takeLast());
1589 void QQuickPathView::modelUpdated(const QQuickChangeSet &changeSet, bool reset)
1591 Q_D(QQuickPathView);
1592 if (!d->model || !d->model->isValid() || !d->path || !isComponentComplete())
1596 d->modelCount = d->model->count();
1598 emit countChanged();
1602 if (changeSet.removes().isEmpty() && changeSet.inserts().isEmpty())
1605 const int modelCount = d->modelCount;
1608 bool currentChanged = false;
1609 bool changedOffset = false;
1610 foreach (const QQuickChangeSet::Remove &r, changeSet.removes()) {
1611 if (moveId == -1 && d->currentIndex >= r.index + r.count) {
1612 d->currentIndex -= r.count;
1613 currentChanged = true;
1614 } else if (moveId == -1 && d->currentIndex >= r.index && d->currentIndex < r.index + r.count) {
1615 // current item has been removed.
1616 d->currentIndex = qMin(r.index, d->modelCount - r.count - 1);
1619 moveOffset = d->currentIndex - r.index;
1620 } else if (d->currentItem) {
1621 if (QQuickPathViewAttached *att = d->attached(d->currentItem))
1622 att->setIsCurrentItem(true);
1623 d->releaseItem(d->currentItem);
1626 currentChanged = true;
1629 if (r.index > d->currentIndex) {
1630 changedOffset = true;
1631 d->offset -= r.count;
1632 d->offsetAdj -= r.count;
1634 d->modelCount -= r.count;
1636 foreach (const QQuickChangeSet::Insert &i, changeSet.inserts()) {
1637 if (d->modelCount) {
1638 if (moveId == -1 && i.index <= d->currentIndex) {
1639 d->currentIndex += i.count;
1640 currentChanged = true;
1642 if (moveId != -1 && moveId == i.moveId) {
1643 d->currentIndex = i.index + moveOffset;
1644 currentChanged = true;
1646 if (i.index > d->currentIndex) {
1647 d->offset += i.count;
1648 d->offsetAdj += i.count;
1649 changedOffset = true;
1653 d->modelCount += i.count;
1656 d->offset = qmlMod(d->offset, d->modelCount);
1658 d->offset += d->modelCount;
1660 d->itemCache += d->items;
1663 if (!d->modelCount) {
1664 while (d->itemCache.count())
1665 d->releaseItem(d->itemCache.takeLast());
1667 changedOffset = true;
1668 d->tl.reset(d->moveOffset);
1670 if (!d->flicking && !d->moving && d->haveHighlightRange && d->highlightRangeMode == QQuickPathView::StrictlyEnforceRange) {
1671 d->offset = qmlMod(d->modelCount - d->currentIndex, d->modelCount);
1672 changedOffset = true;
1675 d->updateMappedRange();
1676 d->scheduleLayout();
1679 emit offsetChanged();
1681 emit currentIndexChanged();
1682 if (d->modelCount != modelCount)
1683 emit countChanged();
1686 void QQuickPathView::destroyingItem(QQuickItem *item)
1691 void QQuickPathView::ticked()
1693 Q_D(QQuickPathView);
1697 void QQuickPathView::movementEnding()
1699 Q_D(QQuickPathView);
1701 d->flicking = false;
1702 emit flickingChanged();
1705 if (d->moving && !d->stealMouse) {
1707 emit movingChanged();
1708 emit movementEnded();
1712 // find the item closest to the snap position
1713 int QQuickPathViewPrivate::calcCurrentIndex()
1716 if (modelCount && model && items.count()) {
1717 offset = qmlMod(offset, modelCount);
1719 offset += modelCount;
1720 current = qRound(qAbs(qmlMod(modelCount - offset, modelCount)));
1721 current = current % modelCount;
1727 void QQuickPathViewPrivate::createCurrentItem()
1729 if (requestedIndex != -1)
1731 int itemIndex = (currentIndex - firstIndex + modelCount) % modelCount;
1732 if (itemIndex < items.count()) {
1733 if ((currentItem = getItem(currentIndex, currentIndex, true))) {
1734 currentItem->setFocus(true);
1735 if (QQuickPathViewAttached *att = attached(currentItem))
1736 att->setIsCurrentItem(true);
1738 } else if (currentIndex >= 0 && currentIndex < modelCount) {
1739 if ((currentItem = getItem(currentIndex, currentIndex, false))) {
1740 updateItem(currentItem, currentIndex < firstIndex ? 0.0 : 1.0);
1741 if (QQuickPathViewAttached *att = attached(currentItem))
1742 att->setIsCurrentItem(true);
1747 void QQuickPathViewPrivate::updateCurrent()
1749 Q_Q(QQuickPathView);
1750 if (moveReason != Mouse)
1752 if (!modelCount || !haveHighlightRange || highlightRangeMode != QQuickPathView::StrictlyEnforceRange)
1755 int idx = calcCurrentIndex();
1756 if (model && idx != currentIndex) {
1758 if (QQuickPathViewAttached *att = attached(currentItem))
1759 att->setIsCurrentItem(false);
1760 releaseItem(currentItem);
1764 createCurrentItem();
1765 emit q->currentIndexChanged();
1769 void QQuickPathViewPrivate::fixOffsetCallback(void *d)
1771 ((QQuickPathViewPrivate *)d)->fixOffset();
1774 void QQuickPathViewPrivate::fixOffset()
1776 Q_Q(QQuickPathView);
1777 if (model && items.count()) {
1778 if (haveHighlightRange && highlightRangeMode == QQuickPathView::StrictlyEnforceRange) {
1779 int curr = calcCurrentIndex();
1780 if (curr != currentIndex)
1781 q->setCurrentIndex(curr);
1788 void QQuickPathViewPrivate::snapToCurrent()
1790 if (!model || modelCount <= 0)
1793 qreal targetOffset = qmlMod(modelCount - currentIndex, modelCount);
1795 if (offset == targetOffset)
1800 tl.reset(moveOffset);
1801 moveOffset.setValue(offset);
1803 const int duration = highlightMoveDuration;
1806 tl.set(moveOffset, targetOffset);
1807 } else if (moveDirection == Positive || (moveDirection == Shortest && targetOffset - offset > modelCount/2)) {
1808 qreal distance = modelCount - targetOffset + offset;
1809 if (targetOffset > moveOffset) {
1810 tl.move(moveOffset, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * offset / distance));
1811 tl.set(moveOffset, modelCount);
1812 tl.move(moveOffset, targetOffset, QEasingCurve(offset == 0.0 ? QEasingCurve::InOutQuad : QEasingCurve::OutQuad), int(duration * (modelCount-targetOffset) / distance));
1814 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
1816 } else if (moveDirection == Negative || targetOffset - offset <= -modelCount/2) {
1817 qreal distance = modelCount - offset + targetOffset;
1818 if (targetOffset < moveOffset) {
1819 tl.move(moveOffset, modelCount, QEasingCurve(targetOffset == 0 ? QEasingCurve::InOutQuad : QEasingCurve::InQuad), int(duration * (modelCount-offset) / distance));
1820 tl.set(moveOffset, 0.0);
1821 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::OutQuad), int(duration * targetOffset / distance));
1823 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
1826 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
1828 moveDirection = Shortest;
1831 QQuickPathViewAttached *QQuickPathView::qmlAttachedProperties(QObject *obj)
1833 return new QQuickPathViewAttached(obj);