1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "QtQuick1/private/qdeclarativepathview_p.h"
43 #include "QtQuick1/private/qdeclarativepathview_p_p.h"
45 #include <QtQuick1/private/qdeclarativestate_p.h>
46 #include <QtQuick1/private/qdeclarativeopenmetaobject_p.h>
49 #include <QGraphicsSceneEvent>
58 inline qreal qmlMod(qreal x, qreal y)
60 #ifdef QT_USE_MATH_H_FLOATS
61 if(sizeof(qreal) == sizeof(float))
62 return fmodf(float(x), float(y));
68 static QDeclarative1OpenMetaObjectType *qPathViewAttachedType = 0;
70 QDeclarative1PathViewAttached::QDeclarative1PathViewAttached(QObject *parent)
71 : QObject(parent), m_percent(-1), m_view(0), m_onPath(false), m_isCurrent(false)
73 if (qPathViewAttachedType) {
74 m_metaobject = new QDeclarative1OpenMetaObject(this, qPathViewAttachedType);
75 m_metaobject->setCached(true);
77 m_metaobject = new QDeclarative1OpenMetaObject(this);
81 QDeclarative1PathViewAttached::~QDeclarative1PathViewAttached()
85 QVariant QDeclarative1PathViewAttached::value(const QByteArray &name) const
87 return m_metaobject->value(name);
89 void QDeclarative1PathViewAttached::setValue(const QByteArray &name, const QVariant &val)
91 m_metaobject->setValue(name, val);
95 void QDeclarative1PathViewPrivate::init()
97 Q_Q(QDeclarative1PathView);
99 q->setAcceptedMouseButtons(Qt::LeftButton);
100 q->setFlag(QGraphicsItem::ItemIsFocusScope);
101 q->setFiltersChildEvents(true);
102 q->connect(&tl, SIGNAL(updated()), q, SLOT(ticked()));
103 lastPosTime.invalidate();
104 static int timelineCompletedIdx = -1;
105 static int movementEndingIdx = -1;
106 if (timelineCompletedIdx == -1) {
107 timelineCompletedIdx = QDeclarative1TimeLine::staticMetaObject.indexOfSignal("completed()");
108 movementEndingIdx = QDeclarative1PathView::staticMetaObject.indexOfSlot("movementEnding()");
110 QMetaObject::connect(&tl, timelineCompletedIdx,
111 q, movementEndingIdx, Qt::DirectConnection);
114 QDeclarativeItem *QDeclarative1PathViewPrivate::getItem(int modelIndex)
116 Q_Q(QDeclarative1PathView);
117 requestedIndex = modelIndex;
118 QDeclarativeItem *item = model->item(modelIndex, false);
121 // pre-create one metatype to share with all attached objects
122 attType = new QDeclarative1OpenMetaObjectType(&QDeclarative1PathViewAttached::staticMetaObject, qmlEngine(q));
123 foreach(const QString &attr, path->attributes())
124 attType->createProperty(attr.toUtf8());
126 qPathViewAttachedType = attType;
127 QDeclarative1PathViewAttached *att = static_cast<QDeclarative1PathViewAttached *>(qmlAttachedPropertiesObject<QDeclarative1PathView>(item));
128 qPathViewAttachedType = 0;
131 att->setOnPath(true);
133 item->setParentItem(q);
134 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
135 itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
141 void QDeclarative1PathViewPrivate::releaseItem(QDeclarativeItem *item)
145 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
146 itemPrivate->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
147 if (model->release(item) == 0) {
148 // item was not destroyed, and we no longer reference it.
149 if (QDeclarative1PathViewAttached *att = attached(item))
150 att->setOnPath(false);
154 QDeclarative1PathViewAttached *QDeclarative1PathViewPrivate::attached(QDeclarativeItem *item)
156 return static_cast<QDeclarative1PathViewAttached *>(qmlAttachedPropertiesObject<QDeclarative1PathView>(item, false));
159 void QDeclarative1PathViewPrivate::clear()
161 for (int i=0; i<items.count(); i++){
162 QDeclarativeItem *p = items[i];
168 void QDeclarative1PathViewPrivate::updateMappedRange()
170 if (model && pathItems != -1 && pathItems < modelCount)
171 mappedRange = qreal(pathItems)/modelCount;
176 qreal QDeclarative1PathViewPrivate::positionOfIndex(qreal index) const
180 if (model && index >= 0 && index < modelCount) {
182 if (haveHighlightRange && highlightRangeMode != QDeclarative1PathView::NoHighlightRange)
183 start = highlightRangeStart;
184 qreal globalPos = index + offset;
185 globalPos = qmlMod(globalPos, qreal(modelCount)) / modelCount;
186 if (pathItems != -1 && pathItems < modelCount) {
187 globalPos += start * mappedRange;
188 globalPos = qmlMod(globalPos, 1.0);
189 if (globalPos < mappedRange)
190 pos = globalPos / mappedRange;
192 pos = qmlMod(globalPos + start, 1.0);
199 void QDeclarative1PathViewPrivate::createHighlight()
201 Q_Q(QDeclarative1PathView);
202 if (!q->isComponentComplete())
205 bool changed = false;
207 if (highlightItem->scene())
208 highlightItem->scene()->removeItem(highlightItem);
209 highlightItem->deleteLater();
214 QDeclarativeItem *item = 0;
215 if (highlightComponent) {
216 QDeclarativeContext *highlightContext = new QDeclarativeContext(qmlContext(q));
217 QObject *nobj = highlightComponent->create(highlightContext);
219 QDeclarative_setParent_noEvent(highlightContext, nobj);
220 item = qobject_cast<QDeclarativeItem *>(nobj);
224 delete highlightContext;
227 item = new QDeclarativeItem;
230 QDeclarative_setParent_noEvent(item, q);
231 item->setParentItem(q);
232 highlightItem = item;
236 emit q->highlightItemChanged();
239 void QDeclarative1PathViewPrivate::updateHighlight()
241 Q_Q(QDeclarative1PathView);
242 if (!q->isComponentComplete() || !isValid())
245 if (haveHighlightRange && highlightRangeMode == QDeclarative1PathView::StrictlyEnforceRange) {
246 updateItem(highlightItem, highlightRangeStart);
248 qreal target = currentIndex;
251 tl.reset(moveHighlight);
252 moveHighlight.setValue(highlightPosition);
254 const int duration = highlightMoveDuration;
256 if (target - highlightPosition > modelCount/2) {
258 qreal distance = modelCount - target + highlightPosition;
259 tl.move(moveHighlight, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * highlightPosition / distance));
260 tl.set(moveHighlight, modelCount-0.01);
261 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * (modelCount-target) / distance));
262 } else if (target - highlightPosition <= -modelCount/2) {
264 qreal distance = modelCount - highlightPosition + target;
265 tl.move(moveHighlight, modelCount-0.01, QEasingCurve(QEasingCurve::InQuad), int(duration * (modelCount-highlightPosition) / distance));
266 tl.set(moveHighlight, 0.0);
267 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * target / distance));
269 highlightUp = highlightPosition - target < 0;
270 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::InOutQuad), duration);
276 void QDeclarative1PathViewPrivate::setHighlightPosition(qreal pos)
278 if (pos != highlightPosition) {
281 if (haveHighlightRange && highlightRangeMode != QDeclarative1PathView::NoHighlightRange) {
282 start = highlightRangeStart;
283 end = highlightRangeEnd;
286 qreal range = qreal(modelCount);
287 // calc normalized position of highlight relative to offset
288 qreal relativeHighlight = qmlMod(pos + offset, range) / range;
290 if (!highlightUp && relativeHighlight > end * mappedRange) {
291 qreal diff = 1.0 - relativeHighlight;
292 setOffset(offset + diff * range);
293 } else if (highlightUp && relativeHighlight >= (end - start) * mappedRange) {
294 qreal diff = relativeHighlight - (end - start) * mappedRange;
295 setOffset(offset - diff * range - 0.00001);
298 highlightPosition = pos;
299 qreal pathPos = positionOfIndex(pos);
300 updateItem(highlightItem, pathPos);
301 if (QDeclarative1PathViewAttached *att = attached(highlightItem))
302 att->setOnPath(pathPos != -1.0);
306 void QDeclarative1PathView::pathUpdated()
308 Q_D(QDeclarative1PathView);
309 QList<QDeclarativeItem*>::iterator it = d->items.begin();
310 while (it != d->items.end()) {
311 QDeclarativeItem *item = *it;
312 if (QDeclarative1PathViewAttached *att = d->attached(item))
319 void QDeclarative1PathViewPrivate::updateItem(QDeclarativeItem *item, qreal percent)
321 if (QDeclarative1PathViewAttached *att = attached(item)) {
322 if (qFuzzyCompare(att->m_percent, percent))
324 att->m_percent = percent;
325 foreach(const QString &attr, path->attributes())
326 att->setValue(attr.toUtf8(), path->attributeAt(attr, percent));
328 QPointF pf = path->pointAt(percent);
329 item->setX(qRound(pf.x() - item->width()/2));
330 item->setY(qRound(pf.y() - item->height()/2));
333 void QDeclarative1PathViewPrivate::regenerate()
335 Q_Q(QDeclarative1PathView);
336 if (!q->isComponentComplete())
350 \qmlclass PathView QDeclarative1PathView
351 \inqmlmodule QtQuick 1
352 \ingroup qml-view-elements
354 \brief The PathView element lays out model-provided items on a path.
357 A PathView displays data from models created from built-in QML elements like ListModel
358 and XmlListModel, or custom model classes defined in C++ that inherit from
361 The view has a \l model, which defines the data to be displayed, and
362 a \l delegate, which defines how the data should be displayed.
363 The \l delegate is instantiated for each item on the \l path.
364 The items may be flicked to move them along the path.
366 For example, if there is a simple list model defined in a file \c ContactModel.qml like this:
368 \snippet doc/src/snippets/qtquick1/pathview/ContactModel.qml 0
370 This data can be represented as a PathView, like this:
372 \snippet doc/src/snippets/qtquick1/pathview/pathview.qml 0
376 (Note the above example uses PathAttribute to scale and modify the
377 opacity of the items as they rotate. This additional code can be seen in the
378 PathAttribute documentation.)
380 PathView does not automatically handle keyboard navigation. This is because
381 the keys to use for navigation will depend upon the shape of the path. Navigation
382 can be added quite simply by setting \c focus to \c true and calling
383 \l decrementCurrentIndex() or \l incrementCurrentIndex(), for example to navigate
384 using the left and right arrow keys:
390 Keys.onLeftPressed: decrementCurrentIndex()
391 Keys.onRightPressed: incrementCurrentIndex()
395 The path view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
397 Delegates are instantiated as needed and may be destroyed at any time.
398 State should \e never be stored in a delegate.
400 PathView attaches a number of properties to the root item of the delegate, for example
401 \c {PathView.isCurrentItem}. In the following example, the root delegate item can access
402 this attached property directly as \c PathView.isCurrentItem, while the child
403 \c nameText object must refer to this property as \c wrapper.PathView.isCurrentItem.
405 \snippet doc/src/snippets/qtquick1/pathview/pathview.qml 1
407 \bold Note that views do not enable \e clip automatically. If the view
408 is not clipped by another item or the screen, it will be necessary
409 to set \e {clip: true} in order to have the out of view items clipped
412 \sa Path, {declarative/modelviews/pathview}{PathView example}
415 QDeclarative1PathView::QDeclarative1PathView(QDeclarativeItem *parent)
416 : QDeclarativeItem(*(new QDeclarative1PathViewPrivate), parent)
418 Q_D(QDeclarative1PathView);
422 QDeclarative1PathView::~QDeclarative1PathView()
424 Q_D(QDeclarative1PathView);
427 d->attType->release();
433 \qmlattachedproperty PathView PathView::view
434 This attached property holds the view that manages this delegate instance.
436 It is attached to each instance of the delegate.
440 \qmlattachedproperty bool PathView::onPath
441 This attached property holds whether the item is currently on the path.
443 If a pathItemCount has been set, it is possible that some items may
444 be instantiated, but not considered to be currently on the path.
445 Usually, these items would be set invisible, for example:
450 visible: PathView.onPath
456 It is attached to each instance of the delegate.
460 \qmlattachedproperty bool PathView::isCurrentItem
461 This attached property is true if this delegate is the current item; otherwise false.
463 It is attached to each instance of the delegate.
465 This property may be used to adjust the appearance of the current item.
467 \snippet doc/src/snippets/qtquick1/pathview/pathview.qml 1
471 \qmlproperty model QtQuick1::PathView::model
472 This property holds the model providing data for the view.
474 The model provides a set of data that is used to create the items for the view.
475 For large or dynamic datasets the model is usually provided by a C++ model object.
476 Models can also be created directly in QML, using the ListModel element.
478 \sa {qmlmodels}{Data Models}
480 QVariant QDeclarative1PathView::model() const
482 Q_D(const QDeclarative1PathView);
483 return d->modelVariant;
486 void QDeclarative1PathView::setModel(const QVariant &model)
488 Q_D(QDeclarative1PathView);
489 if (d->modelVariant == model)
493 disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
494 disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
495 disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
496 disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
497 disconnect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*)));
498 for (int i=0; i<d->items.count(); i++){
499 QDeclarativeItem *p = d->items[i];
500 d->model->release(p);
505 d->modelVariant = model;
506 QObject *object = qvariant_cast<QObject*>(model);
507 QDeclarative1VisualModel *vim = 0;
508 if (object && (vim = qobject_cast<QDeclarative1VisualModel *>(object))) {
516 d->model = new QDeclarative1VisualDataModel(qmlContext(this), this);
519 if (QDeclarative1VisualDataModel *dataModel = qobject_cast<QDeclarative1VisualDataModel*>(d->model))
520 dataModel->setModel(model);
524 connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
525 connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
526 connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
527 connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
528 connect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*)));
529 d->modelCount = d->model->count();
530 if (d->model->count())
531 d->offset = qmlMod(d->offset, qreal(d->model->count()));
533 d->offset = d->model->count() + d->offset;
542 \qmlproperty int QtQuick1::PathView::count
543 This property holds the number of items in the model.
545 int QDeclarative1PathView::count() const
547 Q_D(const QDeclarative1PathView);
548 return d->model ? d->modelCount : 0;
552 \qmlproperty Path QtQuick1::PathView::path
553 This property holds the path used to lay out the items.
554 For more information see the \l Path documentation.
556 QDeclarative1Path *QDeclarative1PathView::path() const
558 Q_D(const QDeclarative1PathView);
562 void QDeclarative1PathView::setPath(QDeclarative1Path *path)
564 Q_D(QDeclarative1PathView);
568 disconnect(d->path, SIGNAL(changed()), this, SLOT(pathUpdated()));
570 connect(d->path, SIGNAL(changed()), this, SLOT(pathUpdated()));
571 if (d->isValid() && isComponentComplete()) {
574 d->attType->release();
583 \qmlproperty int QtQuick1::PathView::currentIndex
584 This property holds the index of the current item.
586 int QDeclarative1PathView::currentIndex() const
588 Q_D(const QDeclarative1PathView);
589 return d->currentIndex;
592 void QDeclarative1PathView::setCurrentIndex(int idx)
594 Q_D(QDeclarative1PathView);
595 if (d->model && d->modelCount)
596 idx = qAbs(idx % d->modelCount);
597 if (d->model && idx != d->currentIndex) {
599 int itemIndex = (d->currentIndex - d->firstIndex + d->modelCount) % d->modelCount;
600 if (itemIndex < d->items.count()) {
601 if (QDeclarativeItem *item = d->items.at(itemIndex)) {
602 if (QDeclarative1PathViewAttached *att = d->attached(item))
603 att->setIsCurrentItem(false);
608 d->moveReason = QDeclarative1PathViewPrivate::SetIndex;
609 d->currentIndex = idx;
611 if (d->haveHighlightRange && d->highlightRangeMode == QDeclarative1PathView::StrictlyEnforceRange)
613 int itemIndex = (idx - d->firstIndex + d->modelCount) % d->modelCount;
614 if (itemIndex < d->items.count()) {
615 d->currentItem = d->items.at(itemIndex);
616 d->currentItem->setFocus(true);
617 if (QDeclarative1PathViewAttached *att = d->attached(d->currentItem))
618 att->setIsCurrentItem(true);
620 d->currentItemOffset = d->positionOfIndex(d->currentIndex);
621 d->updateHighlight();
623 emit currentIndexChanged();
628 \qmlmethod QtQuick1::PathView::incrementCurrentIndex()
630 Increments the current index.
632 \bold Note: methods should only be called after the Component has completed.
634 void QDeclarative1PathView::incrementCurrentIndex()
636 Q_D(QDeclarative1PathView);
637 d->moveDirection = QDeclarative1PathViewPrivate::Positive;
638 setCurrentIndex(currentIndex()+1);
643 \qmlmethod QtQuick1::PathView::decrementCurrentIndex()
645 Decrements the current index.
647 \bold Note: methods should only be called after the Component has completed.
649 void QDeclarative1PathView::decrementCurrentIndex()
651 Q_D(QDeclarative1PathView);
652 if (d->model && d->modelCount) {
653 int idx = currentIndex()-1;
655 idx = d->modelCount - 1;
656 d->moveDirection = QDeclarative1PathViewPrivate::Negative;
657 setCurrentIndex(idx);
662 \qmlproperty real QtQuick1::PathView::offset
664 The offset specifies how far along the path the items are from their initial positions.
665 This is a real number that ranges from 0.0 to the count of items in the model.
667 qreal QDeclarative1PathView::offset() const
669 Q_D(const QDeclarative1PathView);
673 void QDeclarative1PathView::setOffset(qreal offset)
675 Q_D(QDeclarative1PathView);
676 d->setOffset(offset);
680 void QDeclarative1PathViewPrivate::setOffset(qreal o)
682 Q_Q(QDeclarative1PathView);
684 if (isValid() && q->isComponentComplete()) {
685 offset = qmlMod(o, qreal(modelCount));
687 offset += qreal(modelCount);
692 emit q->offsetChanged();
696 void QDeclarative1PathViewPrivate::setAdjustedOffset(qreal o)
698 setOffset(o+offsetAdj);
702 \qmlproperty Component QtQuick1::PathView::highlight
703 This property holds the component to use as the highlight.
705 An instance of the highlight component will be created for each view.
706 The geometry of the resultant component instance will be managed by the view
707 so as to stay with the current item.
709 The below example demonstrates how to make a simple highlight. Note the use
710 of the \l{PathView::onPath}{PathView.onPath} attached property to ensure that
711 the highlight is hidden when flicked away from the path.
716 visible: PathView.onPath
722 \sa highlightItem, highlightRangeMode
725 QDeclarativeComponent *QDeclarative1PathView::highlight() const
727 Q_D(const QDeclarative1PathView);
728 return d->highlightComponent;
731 void QDeclarative1PathView::setHighlight(QDeclarativeComponent *highlight)
733 Q_D(QDeclarative1PathView);
734 if (highlight != d->highlightComponent) {
735 d->highlightComponent = highlight;
736 d->createHighlight();
737 d->updateHighlight();
738 emit highlightChanged();
743 \qmlproperty Item QtQuick1::PathView::highlightItem
745 \c highlightItem holds the highlight item, which was created
746 from the \l highlight component.
750 QDeclarativeItem *QDeclarative1PathView::highlightItem()
752 Q_D(const QDeclarative1PathView);
753 return d->highlightItem;
756 \qmlproperty real QtQuick1::PathView::preferredHighlightBegin
757 \qmlproperty real QtQuick1::PathView::preferredHighlightEnd
758 \qmlproperty enumeration QtQuick1::PathView::highlightRangeMode
760 These properties set the preferred range of the highlight (current item)
761 within the view. The preferred values must be in the range 0.0-1.0.
763 If highlightRangeMode is set to \e PathView.NoHighlightRange
765 If highlightRangeMode is set to \e PathView.ApplyRange the view will
766 attempt to maintain the highlight within the range, however
767 the highlight can move outside of the range at the ends of the path
768 or due to a mouse interaction.
770 If highlightRangeMode is set to \e PathView.StrictlyEnforceRange the highlight will never
771 move outside of the range. This means that the current item will change
772 if a keyboard or mouse action would cause the highlight to move
773 outside of the range.
775 Note that this is the correct way to influence where the
776 current item ends up when the view moves. For example, if you want the
777 currently selected item to be in the middle of the path, then set the
778 highlight range to be 0.5,0.5 and highlightRangeMode to PathView.StrictlyEnforceRange.
779 Then, when the path scrolls,
780 the currently selected item will be the item at that position. This also applies to
781 when the currently selected item changes - it will scroll to within the preferred
782 highlight range. Furthermore, the behaviour of the current item index will occur
783 whether or not a highlight exists.
785 The default value is \e PathView.StrictlyEnforceRange.
787 Note that a valid range requires preferredHighlightEnd to be greater
788 than or equal to preferredHighlightBegin.
790 qreal QDeclarative1PathView::preferredHighlightBegin() const
792 Q_D(const QDeclarative1PathView);
793 return d->highlightRangeStart;
796 void QDeclarative1PathView::setPreferredHighlightBegin(qreal start)
798 Q_D(QDeclarative1PathView);
799 if (d->highlightRangeStart == start || start < 0 || start > 1.0)
801 d->highlightRangeStart = start;
802 d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
804 emit preferredHighlightBeginChanged();
807 qreal QDeclarative1PathView::preferredHighlightEnd() const
809 Q_D(const QDeclarative1PathView);
810 return d->highlightRangeEnd;
813 void QDeclarative1PathView::setPreferredHighlightEnd(qreal end)
815 Q_D(QDeclarative1PathView);
816 if (d->highlightRangeEnd == end || end < 0 || end > 1.0)
818 d->highlightRangeEnd = end;
819 d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
821 emit preferredHighlightEndChanged();
824 QDeclarative1PathView::HighlightRangeMode QDeclarative1PathView::highlightRangeMode() const
826 Q_D(const QDeclarative1PathView);
827 return d->highlightRangeMode;
830 void QDeclarative1PathView::setHighlightRangeMode(HighlightRangeMode mode)
832 Q_D(QDeclarative1PathView);
833 if (d->highlightRangeMode == mode)
835 d->highlightRangeMode = mode;
836 d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
837 emit highlightRangeModeChanged();
842 \qmlproperty int QtQuick1::PathView::highlightMoveDuration
843 This property holds the move animation duration of the highlight delegate.
845 If the highlightRangeMode is StrictlyEnforceRange then this property
846 determines the speed that the items move along the path.
848 The default value for the duration is 300ms.
850 int QDeclarative1PathView::highlightMoveDuration() const
852 Q_D(const QDeclarative1PathView);
853 return d->highlightMoveDuration;
856 void QDeclarative1PathView::setHighlightMoveDuration(int duration)
858 Q_D(QDeclarative1PathView);
859 if (d->highlightMoveDuration == duration)
861 d->highlightMoveDuration = duration;
862 emit highlightMoveDurationChanged();
866 \qmlproperty real QtQuick1::PathView::dragMargin
867 This property holds the maximum distance from the path that initiate mouse dragging.
869 By default the path can only be dragged by clicking on an item. If
870 dragMargin is greater than zero, a drag can be initiated by clicking
871 within dragMargin pixels of the path.
873 qreal QDeclarative1PathView::dragMargin() const
875 Q_D(const QDeclarative1PathView);
876 return d->dragMargin;
879 void QDeclarative1PathView::setDragMargin(qreal dragMargin)
881 Q_D(QDeclarative1PathView);
882 if (d->dragMargin == dragMargin)
884 d->dragMargin = dragMargin;
885 emit dragMarginChanged();
889 \qmlproperty real QtQuick1::PathView::flickDeceleration
890 This property holds the rate at which a flick will decelerate.
894 qreal QDeclarative1PathView::flickDeceleration() const
896 Q_D(const QDeclarative1PathView);
897 return d->deceleration;
900 void QDeclarative1PathView::setFlickDeceleration(qreal dec)
902 Q_D(QDeclarative1PathView);
903 if (d->deceleration == dec)
905 d->deceleration = dec;
906 emit flickDecelerationChanged();
910 \qmlproperty bool QtQuick1::PathView::interactive
912 A user cannot drag or flick a PathView that is not interactive.
914 This property is useful for temporarily disabling flicking. This allows
915 special interaction with PathView's children.
917 bool QDeclarative1PathView::isInteractive() const
919 Q_D(const QDeclarative1PathView);
920 return d->interactive;
923 void QDeclarative1PathView::setInteractive(bool interactive)
925 Q_D(QDeclarative1PathView);
926 if (interactive != d->interactive) {
927 d->interactive = interactive;
930 emit interactiveChanged();
935 \qmlproperty bool QtQuick1::PathView::moving
937 This property holds whether the view is currently moving
938 due to the user either dragging or flicking the view.
940 bool QDeclarative1PathView::isMoving() const
942 Q_D(const QDeclarative1PathView);
947 \qmlproperty bool QtQuick1::PathView::flicking
949 This property holds whether the view is currently moving
950 due to the user flicking the view.
952 bool QDeclarative1PathView::isFlicking() const
954 Q_D(const QDeclarative1PathView);
959 \qmlsignal QtQuick1::PathView::onMovementStarted()
961 This handler is called when the view begins moving due to user
966 \qmlsignal QtQuick1::PathView::onMovementEnded()
968 This handler is called when the view stops moving due to user
969 interaction. If a flick was generated, this handler will
970 be triggered once the flick stops. If a flick was not
971 generated, the handler will be triggered when the
972 user stops dragging - i.e. a mouse or touch release.
976 \qmlsignal QtQuick1::PathView::onFlickStarted()
978 This handler is called when the view is flicked. A flick
979 starts from the point that the mouse or touch is released,
980 while still in motion.
984 \qmlsignal QtQuick1::PathView::onFlickEnded()
986 This handler is called when the view stops moving due to a flick.
990 \qmlproperty Component QtQuick1::PathView::delegate
992 The delegate provides a template defining each item instantiated by the view.
993 The index is exposed as an accessible \c index property. Properties of the
994 model are also available depending upon the type of \l {qmlmodels}{Data Model}.
996 The number of elements in the delegate has a direct effect on the
997 flicking performance of the view when pathItemCount is specified. If at all possible, place functionality
998 that is not needed for the normal display of the delegate in a \l Loader which
999 can load additional elements when needed.
1001 Note that the PathView will layout the items based on the size of the root
1002 item in the delegate.
1004 Here is an example delegate:
1005 \snippet doc/src/snippets/qtquick1/pathview/pathview.qml 1
1007 QDeclarativeComponent *QDeclarative1PathView::delegate() const
1009 Q_D(const QDeclarative1PathView);
1011 if (QDeclarative1VisualDataModel *dataModel = qobject_cast<QDeclarative1VisualDataModel*>(d->model))
1012 return dataModel->delegate();
1018 void QDeclarative1PathView::setDelegate(QDeclarativeComponent *delegate)
1020 Q_D(QDeclarative1PathView);
1021 if (delegate == this->delegate())
1024 d->model = new QDeclarative1VisualDataModel(qmlContext(this));
1027 if (QDeclarative1VisualDataModel *dataModel = qobject_cast<QDeclarative1VisualDataModel*>(d->model)) {
1028 int oldCount = dataModel->count();
1029 dataModel->setDelegate(delegate);
1030 d->modelCount = dataModel->count();
1032 if (oldCount != dataModel->count())
1033 emit countChanged();
1034 emit delegateChanged();
1039 \qmlproperty int QtQuick1::PathView::pathItemCount
1040 This property holds the number of items visible on the path at any one time.
1042 int QDeclarative1PathView::pathItemCount() const
1044 Q_D(const QDeclarative1PathView);
1045 return d->pathItems;
1048 void QDeclarative1PathView::setPathItemCount(int i)
1050 Q_D(QDeclarative1PathView);
1051 if (i == d->pathItems)
1056 d->updateMappedRange();
1057 if (d->isValid() && isComponentComplete()) {
1060 emit pathItemCountChanged();
1063 QPointF QDeclarative1PathViewPrivate::pointNear(const QPointF &point, qreal *nearPercent) const
1065 //XXX maybe do recursively at increasing resolution.
1066 qreal mindist = 1e10; // big number
1067 QPointF nearPoint = path->pointAt(0);
1069 for (qreal i=1; i < 1000; i++) {
1070 QPointF pt = path->pointAt(i/1000.0);
1071 QPointF diff = pt - point;
1072 qreal dist = diff.x()*diff.x() + diff.y()*diff.y();
1073 if (dist < mindist) {
1081 *nearPercent = nearPc / 1000.0;
1086 void QDeclarative1PathView::mousePressEvent(QGraphicsSceneMouseEvent *event)
1088 Q_D(QDeclarative1PathView);
1089 if (d->interactive) {
1090 d->handleMousePressEvent(event);
1093 QDeclarativeItem::mousePressEvent(event);
1097 void QDeclarative1PathViewPrivate::handleMousePressEvent(QGraphicsSceneMouseEvent *event)
1099 Q_Q(QDeclarative1PathView);
1100 if (!interactive || !items.count())
1102 QPointF scenePoint = q->mapToScene(event->pos());
1104 for (; idx < items.count(); ++idx) {
1105 QRectF rect = items.at(idx)->boundingRect();
1106 rect = items.at(idx)->mapToScene(rect).boundingRect();
1107 if (rect.contains(scenePoint))
1110 if (idx == items.count() && dragMargin == 0.) // didn't click on an item
1113 startPoint = pointNear(event->pos(), &startPc);
1114 if (idx == items.count()) {
1115 qreal distance = qAbs(event->pos().x() - startPoint.x()) + qAbs(event->pos().y() - startPoint.y());
1116 if (distance > dragMargin)
1120 if (tl.isActive() && flicking)
1121 stealMouse = true; // If we've been flicked then steal the click.
1127 QDeclarativeItemPrivate::start(lastPosTime);
1131 void QDeclarative1PathView::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
1133 Q_D(QDeclarative1PathView);
1134 if (d->interactive) {
1135 d->handleMouseMoveEvent(event);
1137 setKeepMouseGrab(true);
1140 QDeclarativeItem::mouseMoveEvent(event);
1144 void QDeclarative1PathViewPrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent *event)
1146 Q_Q(QDeclarative1PathView);
1147 if (!interactive || !lastPosTime.isValid())
1151 QPointF pathPoint = pointNear(event->pos(), &newPc);
1153 QPointF delta = pathPoint - startPoint;
1154 if (qAbs(delta.x()) > QApplication::startDragDistance() || qAbs(delta.y()) > QApplication::startDragDistance()) {
1161 moveReason = QDeclarative1PathViewPrivate::Mouse;
1162 qreal diff = (newPc - startPc)*modelCount*mappedRange;
1164 q->setOffset(offset + diff);
1166 if (diff > modelCount/2)
1168 else if (diff < -modelCount/2)
1171 lastElapsed = QDeclarativeItemPrivate::restart(lastPosTime);
1177 emit q->movingChanged();
1178 emit q->movementStarted();
1183 void QDeclarative1PathView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
1185 Q_D(QDeclarative1PathView);
1186 if (d->interactive) {
1187 d->handleMouseReleaseEvent(event);
1191 QDeclarativeItem::mouseReleaseEvent(event);
1195 void QDeclarative1PathViewPrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEvent *)
1197 Q_Q(QDeclarative1PathView);
1199 q->setKeepMouseGrab(false);
1200 if (!interactive || !lastPosTime.isValid())
1203 qreal elapsed = qreal(lastElapsed + QDeclarativeItemPrivate::elapsed(lastPosTime)) / 1000.;
1204 qreal velocity = elapsed > 0. ? lastDist / elapsed : 0;
1205 if (model && modelCount && qAbs(velocity) > 1.) {
1206 qreal count = pathItems == -1 ? modelCount : pathItems;
1207 if (qAbs(velocity) > count * 2) // limit velocity
1208 velocity = (velocity > 0 ? count : -count) * 2;
1209 // Calculate the distance to be travelled
1210 qreal v2 = velocity*velocity;
1211 qreal accel = deceleration/10;
1212 // + 0.25 to encourage moving at least one item in the flick direction
1213 qreal dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2.0) + 0.25));
1214 if (haveHighlightRange && highlightRangeMode == QDeclarative1PathView::StrictlyEnforceRange) {
1215 // round to nearest item.
1217 dist = qRound(dist + offset) - offset;
1219 dist = qRound(dist - offset) + offset;
1220 // Calculate accel required to stop on item boundary
1225 accel = v2 / (2.0f * qAbs(dist));
1229 moveOffset.setValue(offset);
1230 tl.accel(moveOffset, velocity, accel, dist);
1231 tl.callback(QDeclarative1TimeLineCallback(&moveOffset, fixOffsetCallback, this));
1234 emit q->flickingChanged();
1235 emit q->flickStarted();
1241 lastPosTime.invalidate();
1243 q->movementEnding();
1246 bool QDeclarative1PathView::sendMouseEvent(QGraphicsSceneMouseEvent *event)
1248 Q_D(QDeclarative1PathView);
1249 QGraphicsSceneMouseEvent mouseEvent(event->type());
1250 QRectF myRect = mapToScene(QRectF(0, 0, width(), height())).boundingRect();
1251 QGraphicsScene *s = scene();
1252 QDeclarativeItem *grabber = s ? qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem()) : 0;
1253 bool stealThisEvent = d->stealMouse;
1254 if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) {
1255 mouseEvent.setAccepted(false);
1256 for (int i = 0x1; i <= 0x10; i <<= 1) {
1257 if (event->buttons() & i) {
1258 Qt::MouseButton button = Qt::MouseButton(i);
1259 mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button)));
1262 mouseEvent.setScenePos(event->scenePos());
1263 mouseEvent.setLastScenePos(event->lastScenePos());
1264 mouseEvent.setPos(mapFromScene(event->scenePos()));
1265 mouseEvent.setLastPos(mapFromScene(event->lastScenePos()));
1267 switch(mouseEvent.type()) {
1268 case QEvent::GraphicsSceneMouseMove:
1269 d->handleMouseMoveEvent(&mouseEvent);
1271 case QEvent::GraphicsSceneMousePress:
1272 d->handleMousePressEvent(&mouseEvent);
1273 stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
1275 case QEvent::GraphicsSceneMouseRelease:
1276 d->handleMouseReleaseEvent(&mouseEvent);
1281 grabber = qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem());
1282 if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this)
1285 return d->stealMouse;
1286 } else if (d->lastPosTime.isValid()) {
1287 d->lastPosTime.invalidate();
1290 if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease)
1291 d->stealMouse = false;
1295 bool QDeclarative1PathView::sceneEventFilter(QGraphicsItem *i, QEvent *e)
1297 Q_D(QDeclarative1PathView);
1298 if (!isVisible() || !d->interactive)
1299 return QDeclarativeItem::sceneEventFilter(i, e);
1301 switch (e->type()) {
1302 case QEvent::GraphicsSceneMousePress:
1303 case QEvent::GraphicsSceneMouseMove:
1304 case QEvent::GraphicsSceneMouseRelease:
1305 return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e));
1310 return QDeclarativeItem::sceneEventFilter(i, e);
1313 bool QDeclarative1PathView::sceneEvent(QEvent *event)
1315 bool rv = QDeclarativeItem::sceneEvent(event);
1316 if (event->type() == QEvent::UngrabMouse) {
1317 Q_D(QDeclarative1PathView);
1318 if (d->stealMouse) {
1319 // if our mouse grab has been removed (probably by another Flickable),
1321 d->stealMouse = false;
1322 setKeepMouseGrab(false);
1323 d->lastPosTime.invalidate();
1329 bool QDeclarative1PathView::event(QEvent *event)
1331 if (event->type() == QEvent::User) {
1336 return QDeclarativeItem::event(event);
1339 void QDeclarative1PathView::componentComplete()
1341 Q_D(QDeclarative1PathView);
1342 QDeclarativeItem::componentComplete();
1343 d->createHighlight();
1344 // It is possible that a refill has already happended to to Path
1345 // bindings being handled in the componentComplete(). If so
1346 // don't do it again.
1347 if (d->items.count() == 0 && d->model) {
1348 d->modelCount = d->model->count();
1351 d->updateHighlight();
1354 void QDeclarative1PathView::refill()
1356 Q_D(QDeclarative1PathView);
1357 if (!d->isValid() || !isComponentComplete())
1360 d->layoutScheduled = false;
1361 bool currentVisible = false;
1363 // first move existing items and remove items off path
1364 int idx = d->firstIndex;
1365 QList<QDeclarativeItem*>::iterator it = d->items.begin();
1366 while (it != d->items.end()) {
1367 qreal pos = d->positionOfIndex(idx);
1368 QDeclarativeItem *item = *it;
1370 d->updateItem(item, pos);
1371 if (idx == d->currentIndex) {
1372 currentVisible = true;
1373 d->currentItemOffset = pos;
1377 // qDebug() << "release";
1378 d->updateItem(item, 1.0);
1379 d->releaseItem(item);
1380 if (it == d->items.begin()) {
1381 if (++d->firstIndex >= d->modelCount)
1384 it = d->items.erase(it);
1387 if (idx >= d->modelCount)
1390 if (!d->items.count())
1393 if (d->modelCount) {
1394 // add items to beginning and end
1395 int count = d->pathItems == -1 ? d->modelCount : qMin(d->pathItems, d->modelCount);
1396 if (d->items.count() < count) {
1397 int idx = qRound(d->modelCount - d->offset) % d->modelCount;
1398 qreal startPos = 0.0;
1399 if (d->haveHighlightRange && d->highlightRangeMode != QDeclarative1PathView::NoHighlightRange)
1400 startPos = d->highlightRangeStart;
1401 if (d->firstIndex >= 0) {
1402 startPos = d->positionOfIndex(d->firstIndex);
1403 idx = (d->firstIndex + d->items.count()) % d->modelCount;
1405 qreal pos = d->positionOfIndex(idx);
1406 while ((pos > startPos || !d->items.count()) && d->items.count() < count) {
1407 // qDebug() << "append" << idx;
1408 QDeclarativeItem *item = d->getItem(idx);
1409 if (d->model->completePending())
1410 item->setZValue(idx+1);
1411 if (d->currentIndex == idx) {
1412 item->setFocus(true);
1413 if (QDeclarative1PathViewAttached *att = d->attached(item))
1414 att->setIsCurrentItem(true);
1415 currentVisible = true;
1416 d->currentItemOffset = pos;
1417 d->currentItem = item;
1419 if (d->items.count() == 0)
1420 d->firstIndex = idx;
1421 d->items.append(item);
1422 d->updateItem(item, pos);
1423 if (d->model->completePending())
1424 d->model->completeItem();
1426 if (idx >= d->modelCount)
1428 pos = d->positionOfIndex(idx);
1431 idx = d->firstIndex - 1;
1433 idx = d->modelCount - 1;
1434 pos = d->positionOfIndex(idx);
1435 while (pos >= 0.0 && pos < startPos) {
1436 // qDebug() << "prepend" << idx;
1437 QDeclarativeItem *item = d->getItem(idx);
1438 if (d->model->completePending())
1439 item->setZValue(idx+1);
1440 if (d->currentIndex == idx) {
1441 item->setFocus(true);
1442 if (QDeclarative1PathViewAttached *att = d->attached(item))
1443 att->setIsCurrentItem(true);
1444 currentVisible = true;
1445 d->currentItemOffset = pos;
1446 d->currentItem = item;
1448 d->items.prepend(item);
1449 d->updateItem(item, pos);
1450 if (d->model->completePending())
1451 d->model->completeItem();
1452 d->firstIndex = idx;
1453 idx = d->firstIndex - 1;
1455 idx = d->modelCount - 1;
1456 pos = d->positionOfIndex(idx);
1461 if (!currentVisible)
1462 d->currentItemOffset = 1.0;
1464 if (d->highlightItem && d->haveHighlightRange && d->highlightRangeMode == QDeclarative1PathView::StrictlyEnforceRange) {
1465 d->updateItem(d->highlightItem, d->highlightRangeStart);
1466 if (QDeclarative1PathViewAttached *att = d->attached(d->highlightItem))
1467 att->setOnPath(true);
1468 } else if (d->highlightItem && d->moveReason != QDeclarative1PathViewPrivate::SetIndex) {
1469 d->updateItem(d->highlightItem, d->currentItemOffset);
1470 if (QDeclarative1PathViewAttached *att = d->attached(d->highlightItem))
1471 att->setOnPath(currentVisible);
1473 while (d->itemCache.count())
1474 d->releaseItem(d->itemCache.takeLast());
1477 void QDeclarative1PathView::itemsInserted(int modelIndex, int count)
1479 //XXX support animated insertion
1480 Q_D(QDeclarative1PathView);
1481 if (!d->isValid() || !isComponentComplete())
1484 if (d->modelCount) {
1485 d->itemCache += d->items;
1487 if (modelIndex <= d->currentIndex) {
1488 d->currentIndex += count;
1489 emit currentIndexChanged();
1490 } else if (d->offset != 0) {
1492 d->offsetAdj += count;
1495 d->modelCount += count;
1496 if (d->flicking || d->moving) {
1501 d->updateMappedRange();
1502 d->scheduleLayout();
1504 emit countChanged();
1507 void QDeclarative1PathView::itemsRemoved(int modelIndex, int count)
1509 //XXX support animated removal
1510 Q_D(QDeclarative1PathView);
1511 if (!d->model || !d->modelCount || !d->model->isValid() || !d->path || !isComponentComplete())
1515 bool currentChanged = false;
1516 if (d->currentIndex >= modelIndex + count) {
1517 d->currentIndex -= count;
1518 currentChanged = true;
1519 } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) {
1520 // current item has been removed.
1521 d->currentIndex = qMin(modelIndex, d->modelCount-count-1);
1522 if (d->currentItem) {
1523 if (QDeclarative1PathViewAttached *att = d->attached(d->currentItem))
1524 att->setIsCurrentItem(true);
1526 currentChanged = true;
1529 d->itemCache += d->items;
1532 bool changedOffset = false;
1533 if (modelIndex > d->currentIndex) {
1534 if (d->offset >= count) {
1535 changedOffset = true;
1537 d->offsetAdj -= count;
1541 d->modelCount -= count;
1542 if (!d->modelCount) {
1543 while (d->itemCache.count())
1544 d->releaseItem(d->itemCache.takeLast());
1546 changedOffset = true;
1547 d->tl.reset(d->moveOffset);
1552 if (!d->flicking && !d->moving && d->haveHighlightRange && d->highlightRangeMode == QDeclarative1PathView::StrictlyEnforceRange)
1556 emit offsetChanged();
1558 emit currentIndexChanged();
1559 emit countChanged();
1562 void QDeclarative1PathView::itemsMoved(int /*from*/, int /*to*/, int /*count*/)
1564 Q_D(QDeclarative1PathView);
1565 if (!d->isValid() || !isComponentComplete())
1568 QList<QDeclarativeItem *> removedItems = d->items;
1571 while (removedItems.count())
1572 d->releaseItem(removedItems.takeLast());
1574 // Fix current index
1575 if (d->currentIndex >= 0 && d->currentItem) {
1576 int oldCurrent = d->currentIndex;
1577 d->currentIndex = d->model->indexOf(d->currentItem, this);
1578 if (oldCurrent != d->currentIndex)
1579 emit currentIndexChanged();
1584 void QDeclarative1PathView::modelReset()
1586 Q_D(QDeclarative1PathView);
1587 d->modelCount = d->model->count();
1589 emit countChanged();
1592 void QDeclarative1PathView::createdItem(int index, QDeclarativeItem *item)
1594 Q_D(QDeclarative1PathView);
1595 if (d->requestedIndex != index) {
1597 // pre-create one metatype to share with all attached objects
1598 d->attType = new QDeclarative1OpenMetaObjectType(&QDeclarative1PathViewAttached::staticMetaObject, qmlEngine(this));
1599 foreach(const QString &attr, d->path->attributes())
1600 d->attType->createProperty(attr.toUtf8());
1602 qPathViewAttachedType = d->attType;
1603 QDeclarative1PathViewAttached *att = static_cast<QDeclarative1PathViewAttached *>(qmlAttachedPropertiesObject<QDeclarative1PathView>(item));
1604 qPathViewAttachedType = 0;
1607 att->setOnPath(false);
1609 item->setParentItem(this);
1610 d->updateItem(item, index < d->firstIndex ? 0.0 : 1.0);
1614 void QDeclarative1PathView::destroyingItem(QDeclarativeItem *item)
1619 void QDeclarative1PathView::ticked()
1621 Q_D(QDeclarative1PathView);
1625 void QDeclarative1PathView::movementEnding()
1627 Q_D(QDeclarative1PathView);
1629 d->flicking = false;
1630 emit flickingChanged();
1633 if (d->moving && !d->stealMouse) {
1635 emit movingChanged();
1636 emit movementEnded();
1640 // find the item closest to the snap position
1641 int QDeclarative1PathViewPrivate::calcCurrentIndex()
1644 if (modelCount && model && items.count()) {
1645 offset = qmlMod(offset, modelCount);
1647 offset += modelCount;
1648 current = qRound(qAbs(qmlMod(modelCount - offset, modelCount)));
1649 current = current % modelCount;
1655 void QDeclarative1PathViewPrivate::updateCurrent()
1657 Q_Q(QDeclarative1PathView);
1658 if (moveReason != Mouse)
1660 if (!modelCount || !haveHighlightRange || highlightRangeMode != QDeclarative1PathView::StrictlyEnforceRange)
1663 int idx = calcCurrentIndex();
1664 if (model && idx != currentIndex) {
1665 int itemIndex = (currentIndex - firstIndex + modelCount) % modelCount;
1666 if (itemIndex < items.count()) {
1667 if (QDeclarativeItem *item = items.at(itemIndex)) {
1668 if (QDeclarative1PathViewAttached *att = attached(item))
1669 att->setIsCurrentItem(false);
1674 itemIndex = (idx - firstIndex + modelCount) % modelCount;
1675 if (itemIndex < items.count()) {
1676 currentItem = items.at(itemIndex);
1677 currentItem->setFocus(true);
1678 if (QDeclarative1PathViewAttached *att = attached(currentItem))
1679 att->setIsCurrentItem(true);
1681 emit q->currentIndexChanged();
1685 void QDeclarative1PathViewPrivate::fixOffsetCallback(void *d)
1687 ((QDeclarative1PathViewPrivate *)d)->fixOffset();
1690 void QDeclarative1PathViewPrivate::fixOffset()
1692 Q_Q(QDeclarative1PathView);
1693 if (model && items.count()) {
1694 if (haveHighlightRange && highlightRangeMode == QDeclarative1PathView::StrictlyEnforceRange) {
1695 int curr = calcCurrentIndex();
1696 if (curr != currentIndex)
1697 q->setCurrentIndex(curr);
1704 void QDeclarative1PathViewPrivate::snapToCurrent()
1706 if (!model || modelCount <= 0)
1709 qreal targetOffset = qmlMod(modelCount - currentIndex, modelCount);
1713 tl.reset(moveOffset);
1714 moveOffset.setValue(offset);
1716 const int duration = highlightMoveDuration;
1718 if (moveDirection == Positive || (moveDirection == Shortest && targetOffset - offset > modelCount/2)) {
1719 qreal distance = modelCount - targetOffset + offset;
1720 if (targetOffset > moveOffset) {
1721 tl.move(moveOffset, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * offset / distance));
1722 tl.set(moveOffset, modelCount);
1723 tl.move(moveOffset, targetOffset, QEasingCurve(offset == 0.0 ? QEasingCurve::InOutQuad : QEasingCurve::OutQuad), int(duration * (modelCount-targetOffset) / distance));
1725 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
1727 } else if (moveDirection == Negative || targetOffset - offset <= -modelCount/2) {
1728 qreal distance = modelCount - offset + targetOffset;
1729 if (targetOffset < moveOffset) {
1730 tl.move(moveOffset, modelCount, QEasingCurve(targetOffset == 0 ? QEasingCurve::InOutQuad : QEasingCurve::InQuad), int(duration * (modelCount-offset) / distance));
1731 tl.set(moveOffset, 0.0);
1732 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::OutQuad), int(duration * targetOffset / distance));
1734 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
1737 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
1739 moveDirection = Shortest;
1742 QDeclarative1PathViewAttached *QDeclarative1PathView::qmlAttachedProperties(QObject *obj)
1744 return new QDeclarative1PathViewAttached(obj);