1 // Commit: ac704e9f682378a5ec56e3f5c195dcf2f2dfa1ac
2 /****************************************************************************
4 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
5 ** All rights reserved.
6 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 ** This file is part of the QtDeclarative module of the Qt Toolkit.
10 ** $QT_BEGIN_LICENSE:LGPL$
11 ** No Commercial Usage
12 ** This file contains pre-release code and may not be distributed.
13 ** You may use this file in accordance with the terms and conditions
14 ** contained in the Technology Preview License Agreement accompanying
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
41 ****************************************************************************/
43 #include "qsgpathview_p.h"
44 #include "qsgpathview_p_p.h"
45 #include "qsgcanvas.h"
47 #include <private/qdeclarativestate_p.h>
48 #include <private/qdeclarativeopenmetaobject_p.h>
49 #include <private/qlistmodelinterface_p.h>
51 #include <QtGui/qevent.h>
52 #include <QtGui/qgraphicssceneevent.h>
53 #include <QtGui/qapplication.h>
54 #include <QtCore/qmath.h>
59 inline qreal qmlMod(qreal x, qreal y)
61 #ifdef QT_USE_MATH_H_FLOATS
62 if(sizeof(qreal) == sizeof(float))
63 return fmodf(float(x), float(y));
69 static QDeclarativeOpenMetaObjectType *qPathViewAttachedType = 0;
71 QSGPathViewAttached::QSGPathViewAttached(QObject *parent)
72 : QObject(parent), m_percent(-1), m_view(0), m_onPath(false), m_isCurrent(false)
74 if (qPathViewAttachedType) {
75 m_metaobject = new QDeclarativeOpenMetaObject(this, qPathViewAttachedType);
76 m_metaobject->setCached(true);
78 m_metaobject = new QDeclarativeOpenMetaObject(this);
82 QSGPathViewAttached::~QSGPathViewAttached()
86 QVariant QSGPathViewAttached::value(const QByteArray &name) const
88 return m_metaobject->value(name);
90 void QSGPathViewAttached::setValue(const QByteArray &name, const QVariant &val)
92 m_metaobject->setValue(name, val);
96 void QSGPathViewPrivate::init()
100 q->setAcceptedMouseButtons(Qt::LeftButton);
101 q->setFlag(QSGItem::ItemIsFocusScope);
102 q->setFiltersChildMouseEvents(true);
103 q->connect(&tl, SIGNAL(updated()), q, SLOT(ticked()));
104 lastPosTime.invalidate();
105 static int timelineCompletedIdx = -1;
106 static int movementEndingIdx = -1;
107 if (timelineCompletedIdx == -1) {
108 timelineCompletedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("completed()");
109 movementEndingIdx = QSGPathView::staticMetaObject.indexOfSlot("movementEnding()");
111 QMetaObject::connect(&tl, timelineCompletedIdx,
112 q, movementEndingIdx, Qt::DirectConnection);
115 QSGItem *QSGPathViewPrivate::getItem(int modelIndex)
118 requestedIndex = modelIndex;
119 QSGItem *item = model->item(modelIndex, false);
122 // pre-create one metatype to share with all attached objects
123 attType = new QDeclarativeOpenMetaObjectType(&QSGPathViewAttached::staticMetaObject, qmlEngine(q));
124 foreach(const QString &attr, path->attributes())
125 attType->createProperty(attr.toUtf8());
127 qPathViewAttachedType = attType;
128 QSGPathViewAttached *att = static_cast<QSGPathViewAttached *>(qmlAttachedPropertiesObject<QSGPathView>(item));
129 qPathViewAttachedType = 0;
132 att->setOnPath(true);
134 item->setParentItem(q);
135 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
136 itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
142 void QSGPathViewPrivate::releaseItem(QSGItem *item)
146 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
147 itemPrivate->removeItemChangeListener(this, QSGItemPrivate::Geometry);
148 if (model->release(item) == 0) {
149 // item was not destroyed, and we no longer reference it.
150 if (QSGPathViewAttached *att = attached(item))
151 att->setOnPath(false);
155 QSGPathViewAttached *QSGPathViewPrivate::attached(QSGItem *item)
157 return static_cast<QSGPathViewAttached *>(qmlAttachedPropertiesObject<QSGPathView>(item, false));
160 void QSGPathViewPrivate::clear()
162 for (int i=0; i<items.count(); i++){
163 QSGItem *p = items[i];
169 void QSGPathViewPrivate::updateMappedRange()
171 if (model && pathItems != -1 && pathItems < modelCount)
172 mappedRange = qreal(pathItems)/modelCount;
177 qreal QSGPathViewPrivate::positionOfIndex(qreal index) const
181 if (model && index >= 0 && index < modelCount) {
183 if (haveHighlightRange && highlightRangeMode != QSGPathView::NoHighlightRange)
184 start = highlightRangeStart;
185 qreal globalPos = index + offset;
186 globalPos = qmlMod(globalPos, qreal(modelCount)) / modelCount;
187 if (pathItems != -1 && pathItems < modelCount) {
188 globalPos += start * mappedRange;
189 globalPos = qmlMod(globalPos, 1.0);
190 if (globalPos < mappedRange)
191 pos = globalPos / mappedRange;
193 pos = qmlMod(globalPos + start, 1.0);
200 void QSGPathViewPrivate::createHighlight()
203 if (!q->isComponentComplete())
206 bool changed = false;
208 delete highlightItem;
214 if (highlightComponent) {
215 QDeclarativeContext *highlightContext = new QDeclarativeContext(qmlContext(q));
216 QObject *nobj = highlightComponent->create(highlightContext);
218 QDeclarative_setParent_noEvent(highlightContext, nobj);
219 item = qobject_cast<QSGItem *>(nobj);
223 delete highlightContext;
229 QDeclarative_setParent_noEvent(item, q);
230 item->setParentItem(q);
231 highlightItem = item;
235 emit q->highlightItemChanged();
238 void QSGPathViewPrivate::updateHighlight()
241 if (!q->isComponentComplete() || !isValid())
244 if (haveHighlightRange && highlightRangeMode == QSGPathView::StrictlyEnforceRange) {
245 updateItem(highlightItem, highlightRangeStart);
247 qreal target = currentIndex;
250 tl.reset(moveHighlight);
251 moveHighlight.setValue(highlightPosition);
253 const int duration = highlightMoveDuration;
255 if (target - highlightPosition > modelCount/2) {
257 qreal distance = modelCount - target + highlightPosition;
258 tl.move(moveHighlight, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * highlightPosition / distance));
259 tl.set(moveHighlight, modelCount-0.01);
260 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * (modelCount-target) / distance));
261 } else if (target - highlightPosition <= -modelCount/2) {
263 qreal distance = modelCount - highlightPosition + target;
264 tl.move(moveHighlight, modelCount-0.01, QEasingCurve(QEasingCurve::InQuad), int(duration * (modelCount-highlightPosition) / distance));
265 tl.set(moveHighlight, 0.0);
266 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * target / distance));
268 highlightUp = highlightPosition - target < 0;
269 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::InOutQuad), duration);
275 void QSGPathViewPrivate::setHighlightPosition(qreal pos)
277 if (pos != highlightPosition) {
280 if (haveHighlightRange && highlightRangeMode != QSGPathView::NoHighlightRange) {
281 start = highlightRangeStart;
282 end = highlightRangeEnd;
285 qreal range = qreal(modelCount);
286 // calc normalized position of highlight relative to offset
287 qreal relativeHighlight = qmlMod(pos + offset, range) / range;
289 if (!highlightUp && relativeHighlight > end * mappedRange) {
290 qreal diff = 1.0 - relativeHighlight;
291 setOffset(offset + diff * range);
292 } else if (highlightUp && relativeHighlight >= (end - start) * mappedRange) {
293 qreal diff = relativeHighlight - (end - start) * mappedRange;
294 setOffset(offset - diff * range - 0.00001);
297 highlightPosition = pos;
298 qreal pathPos = positionOfIndex(pos);
299 updateItem(highlightItem, pathPos);
300 if (QSGPathViewAttached *att = attached(highlightItem))
301 att->setOnPath(pathPos != -1.0);
305 void QSGPathView::pathUpdated()
308 QList<QSGItem*>::iterator it = d->items.begin();
309 while (it != d->items.end()) {
311 if (QSGPathViewAttached *att = d->attached(item))
318 void QSGPathViewPrivate::updateItem(QSGItem *item, qreal percent)
320 if (QSGPathViewAttached *att = attached(item)) {
321 if (qFuzzyCompare(att->m_percent, percent))
323 att->m_percent = percent;
324 foreach(const QString &attr, path->attributes())
325 att->setValue(attr.toUtf8(), path->attributeAt(attr, percent));
327 QPointF pf = path->pointAt(percent);
328 item->setX(qRound(pf.x() - item->width()/2));
329 item->setY(qRound(pf.y() - item->height()/2));
332 void QSGPathViewPrivate::regenerate()
335 if (!q->isComponentComplete())
348 QSGPathView::QSGPathView(QSGItem *parent)
349 : QSGItem(*(new QSGPathViewPrivate), parent)
355 QSGPathView::~QSGPathView()
360 d->attType->release();
365 QVariant QSGPathView::model() const
367 Q_D(const QSGPathView);
368 return d->modelVariant;
371 void QSGPathView::setModel(const QVariant &model)
374 if (d->modelVariant == model)
378 disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
379 disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
380 disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
381 disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
382 disconnect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*)));
383 for (int i=0; i<d->items.count(); i++){
384 QSGItem *p = d->items[i];
385 d->model->release(p);
390 d->modelVariant = model;
391 QObject *object = qvariant_cast<QObject*>(model);
392 QSGVisualModel *vim = 0;
393 if (object && (vim = qobject_cast<QSGVisualModel *>(object))) {
401 d->model = new QSGVisualDataModel(qmlContext(this), this);
404 if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model))
405 dataModel->setModel(model);
409 connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
410 connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
411 connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
412 connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
413 connect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*)));
414 d->modelCount = d->model->count();
415 if (d->model->count())
416 d->offset = qmlMod(d->offset, qreal(d->model->count()));
418 d->offset = d->model->count() + d->offset;
426 int QSGPathView::count() const
428 Q_D(const QSGPathView);
429 return d->model ? d->modelCount : 0;
432 QDeclarativePath *QSGPathView::path() const
434 Q_D(const QSGPathView);
438 void QSGPathView::setPath(QDeclarativePath *path)
444 disconnect(d->path, SIGNAL(changed()), this, SLOT(pathUpdated()));
446 connect(d->path, SIGNAL(changed()), this, SLOT(pathUpdated()));
447 if (d->isValid() && isComponentComplete()) {
450 d->attType->release();
458 int QSGPathView::currentIndex() const
460 Q_D(const QSGPathView);
461 return d->currentIndex;
464 void QSGPathView::setCurrentIndex(int idx)
467 if (d->model && d->modelCount)
468 idx = qAbs(idx % d->modelCount);
469 if (d->model && idx != d->currentIndex) {
471 int itemIndex = (d->currentIndex - d->firstIndex + d->modelCount) % d->modelCount;
472 if (itemIndex < d->items.count()) {
473 if (QSGItem *item = d->items.at(itemIndex)) {
474 if (QSGPathViewAttached *att = d->attached(item))
475 att->setIsCurrentItem(false);
480 d->moveReason = QSGPathViewPrivate::SetIndex;
481 d->currentIndex = idx;
483 if (d->haveHighlightRange && d->highlightRangeMode == QSGPathView::StrictlyEnforceRange)
485 int itemIndex = (idx - d->firstIndex + d->modelCount) % d->modelCount;
486 if (itemIndex < d->items.count()) {
487 d->currentItem = d->items.at(itemIndex);
488 d->currentItem->setFocus(true);
489 if (QSGPathViewAttached *att = d->attached(d->currentItem))
490 att->setIsCurrentItem(true);
492 d->currentItemOffset = d->positionOfIndex(d->currentIndex);
493 d->updateHighlight();
495 emit currentIndexChanged();
499 void QSGPathView::incrementCurrentIndex()
502 d->moveDirection = QSGPathViewPrivate::Positive;
503 setCurrentIndex(currentIndex()+1);
506 void QSGPathView::decrementCurrentIndex()
509 if (d->model && d->modelCount) {
510 int idx = currentIndex()-1;
512 idx = d->modelCount - 1;
513 d->moveDirection = QSGPathViewPrivate::Negative;
514 setCurrentIndex(idx);
518 qreal QSGPathView::offset() const
520 Q_D(const QSGPathView);
524 void QSGPathView::setOffset(qreal offset)
527 d->setOffset(offset);
531 void QSGPathViewPrivate::setOffset(qreal o)
535 if (isValid() && q->isComponentComplete()) {
536 offset = qmlMod(o, qreal(modelCount));
538 offset += qreal(modelCount);
543 emit q->offsetChanged();
547 void QSGPathViewPrivate::setAdjustedOffset(qreal o)
549 setOffset(o+offsetAdj);
552 QDeclarativeComponent *QSGPathView::highlight() const
554 Q_D(const QSGPathView);
555 return d->highlightComponent;
558 void QSGPathView::setHighlight(QDeclarativeComponent *highlight)
561 if (highlight != d->highlightComponent) {
562 d->highlightComponent = highlight;
563 d->createHighlight();
564 d->updateHighlight();
565 emit highlightChanged();
569 QSGItem *QSGPathView::highlightItem()
571 Q_D(const QSGPathView);
572 return d->highlightItem;
575 qreal QSGPathView::preferredHighlightBegin() const
577 Q_D(const QSGPathView);
578 return d->highlightRangeStart;
581 void QSGPathView::setPreferredHighlightBegin(qreal start)
584 if (d->highlightRangeStart == start || start < 0 || start > 1.0)
586 d->highlightRangeStart = start;
587 d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
589 emit preferredHighlightBeginChanged();
592 qreal QSGPathView::preferredHighlightEnd() const
594 Q_D(const QSGPathView);
595 return d->highlightRangeEnd;
598 void QSGPathView::setPreferredHighlightEnd(qreal end)
601 if (d->highlightRangeEnd == end || end < 0 || end > 1.0)
603 d->highlightRangeEnd = end;
604 d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
606 emit preferredHighlightEndChanged();
609 QSGPathView::HighlightRangeMode QSGPathView::highlightRangeMode() const
611 Q_D(const QSGPathView);
612 return d->highlightRangeMode;
615 void QSGPathView::setHighlightRangeMode(HighlightRangeMode mode)
618 if (d->highlightRangeMode == mode)
620 d->highlightRangeMode = mode;
621 d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
622 emit highlightRangeModeChanged();
625 int QSGPathView::highlightMoveDuration() const
627 Q_D(const QSGPathView);
628 return d->highlightMoveDuration;
631 void QSGPathView::setHighlightMoveDuration(int duration)
634 if (d->highlightMoveDuration == duration)
636 d->highlightMoveDuration = duration;
637 emit highlightMoveDurationChanged();
640 qreal QSGPathView::dragMargin() const
642 Q_D(const QSGPathView);
643 return d->dragMargin;
646 void QSGPathView::setDragMargin(qreal dragMargin)
649 if (d->dragMargin == dragMargin)
651 d->dragMargin = dragMargin;
652 emit dragMarginChanged();
655 qreal QSGPathView::flickDeceleration() const
657 Q_D(const QSGPathView);
658 return d->deceleration;
661 void QSGPathView::setFlickDeceleration(qreal dec)
664 if (d->deceleration == dec)
666 d->deceleration = dec;
667 emit flickDecelerationChanged();
670 bool QSGPathView::isInteractive() const
672 Q_D(const QSGPathView);
673 return d->interactive;
676 void QSGPathView::setInteractive(bool interactive)
679 if (interactive != d->interactive) {
680 d->interactive = interactive;
683 emit interactiveChanged();
687 bool QSGPathView::isMoving() const
689 Q_D(const QSGPathView);
693 bool QSGPathView::isFlicking() const
695 Q_D(const QSGPathView);
699 QDeclarativeComponent *QSGPathView::delegate() const
701 Q_D(const QSGPathView);
703 if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model))
704 return dataModel->delegate();
710 void QSGPathView::setDelegate(QDeclarativeComponent *delegate)
713 if (delegate == this->delegate())
716 d->model = new QSGVisualDataModel(qmlContext(this));
719 if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model)) {
720 dataModel->setDelegate(delegate);
721 d->modelCount = dataModel->count();
723 emit delegateChanged();
727 int QSGPathView::pathItemCount() const
729 Q_D(const QSGPathView);
733 void QSGPathView::setPathItemCount(int i)
736 if (i == d->pathItems)
741 d->updateMappedRange();
742 if (d->isValid() && isComponentComplete()) {
745 emit pathItemCountChanged();
748 QPointF QSGPathViewPrivate::pointNear(const QPointF &point, qreal *nearPercent) const
750 //XXX maybe do recursively at increasing resolution.
751 qreal mindist = 1e10; // big number
752 QPointF nearPoint = path->pointAt(0);
754 for (qreal i=1; i < 1000; i++) {
755 QPointF pt = path->pointAt(i/1000.0);
756 QPointF diff = pt - point;
757 qreal dist = diff.x()*diff.x() + diff.y()*diff.y();
758 if (dist < mindist) {
766 *nearPercent = nearPc / 1000.0;
771 void QSGPathView::mousePressEvent(QGraphicsSceneMouseEvent *event)
774 if (d->interactive) {
775 d->handleMousePressEvent(event);
778 QSGItem::mousePressEvent(event);
782 void QSGPathViewPrivate::handleMousePressEvent(QGraphicsSceneMouseEvent *event)
785 if (!interactive || !items.count())
787 QPointF scenePoint = q->mapToScene(event->pos());
789 for (; idx < items.count(); ++idx) {
790 QRectF rect = items.at(idx)->boundingRect();
791 rect = items.at(idx)->mapRectToScene(rect);
792 if (rect.contains(scenePoint))
795 if (idx == items.count() && dragMargin == 0.) // didn't click on an item
798 startPoint = pointNear(event->pos(), &startPc);
799 if (idx == items.count()) {
800 qreal distance = qAbs(event->pos().x() - startPoint.x()) + qAbs(event->pos().y() - startPoint.y());
801 if (distance > dragMargin)
805 if (tl.isActive() && flicking)
806 stealMouse = true; // If we've been flicked then steal the click.
812 QSGItemPrivate::start(lastPosTime);
816 void QSGPathView::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
819 if (d->interactive) {
820 d->handleMouseMoveEvent(event);
822 setKeepMouseGrab(true);
825 QSGItem::mouseMoveEvent(event);
829 void QSGPathViewPrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent *event)
832 if (!interactive || !lastPosTime.isValid())
836 QPointF pathPoint = pointNear(event->pos(), &newPc);
838 QPointF delta = pathPoint - startPoint;
839 if (qAbs(delta.x()) > QApplication::startDragDistance() || qAbs(delta.y()) > QApplication::startDragDistance()) {
846 moveReason = QSGPathViewPrivate::Mouse;
847 qreal diff = (newPc - startPc)*modelCount*mappedRange;
849 q->setOffset(offset + diff);
851 if (diff > modelCount/2)
853 else if (diff < -modelCount/2)
856 lastElapsed = QSGItemPrivate::restart(lastPosTime);
862 emit q->movingChanged();
863 emit q->movementStarted();
868 void QSGPathView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
871 if (d->interactive) {
872 d->handleMouseReleaseEvent(event);
876 QSGItem::mouseReleaseEvent(event);
880 void QSGPathViewPrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEvent *)
884 q->setKeepMouseGrab(false);
885 if (!interactive || !lastPosTime.isValid())
888 qreal elapsed = qreal(lastElapsed + QSGItemPrivate::elapsed(lastPosTime)) / 1000.;
889 qreal velocity = elapsed > 0. ? lastDist / elapsed : 0;
890 if (model && modelCount && qAbs(velocity) > 1.) {
891 qreal count = pathItems == -1 ? modelCount : pathItems;
892 if (qAbs(velocity) > count * 2) // limit velocity
893 velocity = (velocity > 0 ? count : -count) * 2;
894 // Calculate the distance to be travelled
895 qreal v2 = velocity*velocity;
896 qreal accel = deceleration/10;
897 // + 0.25 to encourage moving at least one item in the flick direction
898 qreal dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2.0) + 0.25));
899 if (haveHighlightRange && highlightRangeMode == QSGPathView::StrictlyEnforceRange) {
900 // round to nearest item.
902 dist = qRound(dist + offset) - offset;
904 dist = qRound(dist - offset) + offset;
905 // Calculate accel required to stop on item boundary
910 accel = v2 / (2.0f * qAbs(dist));
914 moveOffset.setValue(offset);
915 tl.accel(moveOffset, velocity, accel, dist);
916 tl.callback(QDeclarativeTimeLineCallback(&moveOffset, fixOffsetCallback, this));
919 emit q->flickingChanged();
920 emit q->flickStarted();
926 lastPosTime.invalidate();
931 bool QSGPathView::sendMouseEvent(QGraphicsSceneMouseEvent *event)
934 QGraphicsSceneMouseEvent mouseEvent(event->type());
935 QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
936 QSGCanvas *c = canvas();
937 QSGItem *grabber = c ? c->mouseGrabberItem() : 0;
938 bool stealThisEvent = d->stealMouse;
939 if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) {
940 mouseEvent.setAccepted(false);
941 for (int i = 0x1; i <= 0x10; i <<= 1) {
942 if (event->buttons() & i) {
943 Qt::MouseButton button = Qt::MouseButton(i);
944 mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button)));
947 mouseEvent.setScenePos(event->scenePos());
948 mouseEvent.setLastScenePos(event->lastScenePos());
949 mouseEvent.setPos(mapFromScene(event->scenePos()));
950 mouseEvent.setLastPos(mapFromScene(event->lastScenePos()));
952 switch(mouseEvent.type()) {
953 case QEvent::GraphicsSceneMouseMove:
954 d->handleMouseMoveEvent(&mouseEvent);
956 case QEvent::GraphicsSceneMousePress:
957 d->handleMousePressEvent(&mouseEvent);
958 stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
960 case QEvent::GraphicsSceneMouseRelease:
961 d->handleMouseReleaseEvent(&mouseEvent);
966 grabber = c->mouseGrabberItem();
967 if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this)
970 return d->stealMouse;
971 } else if (d->lastPosTime.isValid()) {
972 d->lastPosTime.invalidate();
974 if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease)
975 d->stealMouse = false;
979 bool QSGPathView::childMouseEventFilter(QSGItem *i, QEvent *e)
982 if (!isVisible() || !d->interactive)
983 return QSGItem::childMouseEventFilter(i, e);
986 case QEvent::GraphicsSceneMousePress:
987 case QEvent::GraphicsSceneMouseMove:
988 case QEvent::GraphicsSceneMouseRelease:
989 return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e));
994 return QSGItem::childMouseEventFilter(i, e);
997 void QSGPathView::updatePolish()
999 QSGItem::updatePolish();
1003 void QSGPathView::componentComplete()
1006 QSGItem::componentComplete();
1007 d->createHighlight();
1008 // It is possible that a refill has already happended to to Path
1009 // bindings being handled in the componentComplete(). If so
1010 // don't do it again.
1011 if (d->items.count() == 0 && d->model) {
1012 d->modelCount = d->model->count();
1015 d->updateHighlight();
1018 void QSGPathView::refill()
1021 if (!d->isValid() || !isComponentComplete())
1024 d->layoutScheduled = false;
1025 bool currentVisible = false;
1027 // first move existing items and remove items off path
1028 int idx = d->firstIndex;
1029 QList<QSGItem*>::iterator it = d->items.begin();
1030 while (it != d->items.end()) {
1031 qreal pos = d->positionOfIndex(idx);
1032 QSGItem *item = *it;
1034 d->updateItem(item, pos);
1035 if (idx == d->currentIndex) {
1036 currentVisible = true;
1037 d->currentItemOffset = pos;
1041 // qDebug() << "release";
1042 d->updateItem(item, 1.0);
1043 d->releaseItem(item);
1044 if (it == d->items.begin()) {
1045 if (++d->firstIndex >= d->modelCount)
1048 it = d->items.erase(it);
1051 if (idx >= d->modelCount)
1054 if (!d->items.count())
1057 if (d->modelCount) {
1058 // add items to beginning and end
1059 int count = d->pathItems == -1 ? d->modelCount : qMin(d->pathItems, d->modelCount);
1060 if (d->items.count() < count) {
1061 int idx = qRound(d->modelCount - d->offset) % d->modelCount;
1062 qreal startPos = 0.0;
1063 if (d->haveHighlightRange && d->highlightRangeMode != QSGPathView::NoHighlightRange)
1064 startPos = d->highlightRangeStart;
1065 if (d->firstIndex >= 0) {
1066 startPos = d->positionOfIndex(d->firstIndex);
1067 idx = (d->firstIndex + d->items.count()) % d->modelCount;
1069 qreal pos = d->positionOfIndex(idx);
1070 while ((pos > startPos || !d->items.count()) && d->items.count() < count) {
1071 // qDebug() << "append" << idx;
1072 QSGItem *item = d->getItem(idx);
1073 if (d->model->completePending())
1075 if (d->currentIndex == idx) {
1076 item->setFocus(true);
1077 if (QSGPathViewAttached *att = d->attached(item))
1078 att->setIsCurrentItem(true);
1079 currentVisible = true;
1080 d->currentItemOffset = pos;
1081 d->currentItem = item;
1083 if (d->items.count() == 0)
1084 d->firstIndex = idx;
1085 d->items.append(item);
1086 d->updateItem(item, pos);
1087 if (d->model->completePending())
1088 d->model->completeItem();
1090 if (idx >= d->modelCount)
1092 pos = d->positionOfIndex(idx);
1095 idx = d->firstIndex - 1;
1097 idx = d->modelCount - 1;
1098 pos = d->positionOfIndex(idx);
1099 while (pos >= 0.0 && pos < startPos) {
1100 // qDebug() << "prepend" << idx;
1101 QSGItem *item = d->getItem(idx);
1102 if (d->model->completePending())
1104 if (d->currentIndex == idx) {
1105 item->setFocus(true);
1106 if (QSGPathViewAttached *att = d->attached(item))
1107 att->setIsCurrentItem(true);
1108 currentVisible = true;
1109 d->currentItemOffset = pos;
1110 d->currentItem = item;
1112 d->items.prepend(item);
1113 d->updateItem(item, pos);
1114 if (d->model->completePending())
1115 d->model->completeItem();
1116 d->firstIndex = idx;
1117 idx = d->firstIndex - 1;
1119 idx = d->modelCount - 1;
1120 pos = d->positionOfIndex(idx);
1125 if (!currentVisible)
1126 d->currentItemOffset = 1.0;
1128 if (d->highlightItem && d->haveHighlightRange && d->highlightRangeMode == QSGPathView::StrictlyEnforceRange) {
1129 d->updateItem(d->highlightItem, d->highlightRangeStart);
1130 if (QSGPathViewAttached *att = d->attached(d->highlightItem))
1131 att->setOnPath(true);
1132 } else if (d->highlightItem && d->moveReason != QSGPathViewPrivate::SetIndex) {
1133 d->updateItem(d->highlightItem, d->currentItemOffset);
1134 if (QSGPathViewAttached *att = d->attached(d->highlightItem))
1135 att->setOnPath(currentVisible);
1137 while (d->itemCache.count())
1138 d->releaseItem(d->itemCache.takeLast());
1141 void QSGPathView::itemsInserted(int modelIndex, int count)
1143 //XXX support animated insertion
1145 if (!d->isValid() || !isComponentComplete())
1148 if (d->modelCount) {
1149 d->itemCache += d->items;
1151 if (modelIndex <= d->currentIndex) {
1152 d->currentIndex += count;
1153 emit currentIndexChanged();
1154 } else if (d->offset != 0) {
1156 d->offsetAdj += count;
1160 d->modelCount += count;
1161 if (d->flicking || d->moving) {
1166 d->updateMappedRange();
1167 d->scheduleLayout();
1169 emit countChanged();
1172 void QSGPathView::itemsRemoved(int modelIndex, int count)
1174 //XXX support animated removal
1176 if (!d->model || !d->modelCount || !d->model->isValid() || !d->path || !isComponentComplete())
1180 bool currentChanged = false;
1181 if (d->currentIndex >= modelIndex + count) {
1182 d->currentIndex -= count;
1183 currentChanged = true;
1184 } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) {
1185 // current item has been removed.
1186 d->currentIndex = qMin(modelIndex, d->modelCount-count-1);
1187 if (d->currentItem) {
1188 if (QSGPathViewAttached *att = d->attached(d->currentItem))
1189 att->setIsCurrentItem(true);
1191 currentChanged = true;
1194 d->itemCache += d->items;
1197 bool changedOffset = false;
1198 if (modelIndex > d->currentIndex) {
1199 if (d->offset >= count) {
1200 changedOffset = true;
1202 d->offsetAdj -= count;
1206 d->modelCount -= count;
1207 if (!d->modelCount) {
1208 while (d->itemCache.count())
1209 d->releaseItem(d->itemCache.takeLast());
1211 changedOffset = true;
1212 d->tl.reset(d->moveOffset);
1218 emit offsetChanged();
1220 emit currentIndexChanged();
1221 emit countChanged();
1224 void QSGPathView::itemsMoved(int /*from*/, int /*to*/, int /*count*/)
1227 if (!d->isValid() || !isComponentComplete())
1230 QList<QSGItem *> removedItems = d->items;
1233 while (removedItems.count())
1234 d->releaseItem(removedItems.takeLast());
1236 // Fix current index
1237 if (d->currentIndex >= 0 && d->currentItem) {
1238 int oldCurrent = d->currentIndex;
1239 d->currentIndex = d->model->indexOf(d->currentItem, this);
1240 if (oldCurrent != d->currentIndex)
1241 emit currentIndexChanged();
1246 void QSGPathView::modelReset()
1249 d->modelCount = d->model->count();
1251 emit countChanged();
1254 void QSGPathView::createdItem(int index, QSGItem *item)
1257 if (d->requestedIndex != index) {
1259 // pre-create one metatype to share with all attached objects
1260 d->attType = new QDeclarativeOpenMetaObjectType(&QSGPathViewAttached::staticMetaObject, qmlEngine(this));
1261 foreach(const QString &attr, d->path->attributes())
1262 d->attType->createProperty(attr.toUtf8());
1264 qPathViewAttachedType = d->attType;
1265 QSGPathViewAttached *att = static_cast<QSGPathViewAttached *>(qmlAttachedPropertiesObject<QSGPathView>(item));
1266 qPathViewAttachedType = 0;
1269 att->setOnPath(false);
1271 item->setParentItem(this);
1272 d->updateItem(item, index < d->firstIndex ? 0.0 : 1.0);
1276 void QSGPathView::destroyingItem(QSGItem *item)
1281 void QSGPathView::ticked()
1287 void QSGPathView::movementEnding()
1291 d->flicking = false;
1292 emit flickingChanged();
1295 if (d->moving && !d->stealMouse) {
1297 emit movingChanged();
1298 emit movementEnded();
1302 // find the item closest to the snap position
1303 int QSGPathViewPrivate::calcCurrentIndex()
1306 if (modelCount && model && items.count()) {
1307 offset = qmlMod(offset, modelCount);
1309 offset += modelCount;
1310 current = qRound(qAbs(qmlMod(modelCount - offset, modelCount)));
1311 current = current % modelCount;
1317 void QSGPathViewPrivate::updateCurrent()
1320 if (moveReason != Mouse)
1322 if (!modelCount || !haveHighlightRange || highlightRangeMode != QSGPathView::StrictlyEnforceRange)
1325 int idx = calcCurrentIndex();
1326 if (model && idx != currentIndex) {
1327 int itemIndex = (currentIndex - firstIndex + modelCount) % modelCount;
1328 if (itemIndex < items.count()) {
1329 if (QSGItem *item = items.at(itemIndex)) {
1330 if (QSGPathViewAttached *att = attached(item))
1331 att->setIsCurrentItem(false);
1336 itemIndex = (idx - firstIndex + modelCount) % modelCount;
1337 if (itemIndex < items.count()) {
1338 currentItem = items.at(itemIndex);
1339 currentItem->setFocus(true);
1340 if (QSGPathViewAttached *att = attached(currentItem))
1341 att->setIsCurrentItem(true);
1343 emit q->currentIndexChanged();
1347 void QSGPathViewPrivate::fixOffsetCallback(void *d)
1349 ((QSGPathViewPrivate *)d)->fixOffset();
1352 void QSGPathViewPrivate::fixOffset()
1355 if (model && items.count()) {
1356 if (haveHighlightRange && highlightRangeMode == QSGPathView::StrictlyEnforceRange) {
1357 int curr = calcCurrentIndex();
1358 if (curr != currentIndex)
1359 q->setCurrentIndex(curr);
1366 void QSGPathViewPrivate::snapToCurrent()
1368 if (!model || modelCount <= 0)
1371 qreal targetOffset = qmlMod(modelCount - currentIndex, modelCount);
1375 tl.reset(moveOffset);
1376 moveOffset.setValue(offset);
1378 const int duration = highlightMoveDuration;
1380 if (moveDirection == Positive || (moveDirection == Shortest && targetOffset - offset > modelCount/2)) {
1381 qreal distance = modelCount - targetOffset + offset;
1382 if (targetOffset > moveOffset) {
1383 tl.move(moveOffset, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * offset / distance));
1384 tl.set(moveOffset, modelCount);
1385 tl.move(moveOffset, targetOffset, QEasingCurve(offset == 0.0 ? QEasingCurve::InOutQuad : QEasingCurve::OutQuad), int(duration * (modelCount-targetOffset) / distance));
1387 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
1389 } else if (moveDirection == Negative || targetOffset - offset <= -modelCount/2) {
1390 qreal distance = modelCount - offset + targetOffset;
1391 if (targetOffset < moveOffset) {
1392 tl.move(moveOffset, modelCount, QEasingCurve(targetOffset == 0 ? QEasingCurve::InOutQuad : QEasingCurve::InQuad), int(duration * (modelCount-offset) / distance));
1393 tl.set(moveOffset, 0.0);
1394 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::OutQuad), int(duration * targetOffset / distance));
1396 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
1399 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
1401 moveDirection = Shortest;
1404 QSGPathViewAttached *QSGPathView::qmlAttachedProperties(QObject *obj)
1406 return new QSGPathViewAttached(obj);