86 return m_metaobject->value(name); |
86 return m_metaobject->value(name); |
87 } |
87 } |
88 void QDeclarativePathViewAttached::setValue(const QByteArray &name, const QVariant &val) |
88 void QDeclarativePathViewAttached::setValue(const QByteArray &name, const QVariant &val) |
89 { |
89 { |
90 m_metaobject->setValue(name, val); |
90 m_metaobject->setValue(name, val); |
|
91 } |
|
92 |
|
93 |
|
94 void QDeclarativePathViewPrivate::init() |
|
95 { |
|
96 Q_Q(QDeclarativePathView); |
|
97 offset = 0; |
|
98 q->setAcceptedMouseButtons(Qt::LeftButton); |
|
99 q->setFlag(QGraphicsItem::ItemIsFocusScope); |
|
100 q->setFiltersChildEvents(true); |
|
101 q->connect(&tl, SIGNAL(updated()), q, SLOT(ticked())); |
|
102 lastPosTime.invalidate(); |
|
103 static int timelineCompletedIdx = -1; |
|
104 static int movementEndingIdx = -1; |
|
105 if (timelineCompletedIdx == -1) { |
|
106 timelineCompletedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("completed()"); |
|
107 movementEndingIdx = QDeclarativePathView::staticMetaObject.indexOfSlot("movementEnding()"); |
|
108 } |
|
109 QMetaObject::connect(&tl, timelineCompletedIdx, |
|
110 q, movementEndingIdx, Qt::DirectConnection); |
91 } |
111 } |
92 |
112 |
93 QDeclarativeItem *QDeclarativePathViewPrivate::getItem(int modelIndex) |
113 QDeclarativeItem *QDeclarativePathViewPrivate::getItem(int modelIndex) |
94 { |
114 { |
95 Q_Q(QDeclarativePathView); |
115 Q_Q(QDeclarativePathView); |
142 items.clear(); |
162 items.clear(); |
143 } |
163 } |
144 |
164 |
145 void QDeclarativePathViewPrivate::updateMappedRange() |
165 void QDeclarativePathViewPrivate::updateMappedRange() |
146 { |
166 { |
147 if (model && pathItems != -1 && pathItems < model->count()) |
167 if (model && pathItems != -1 && pathItems < modelCount) |
148 mappedRange = qreal(pathItems)/model->count(); |
168 mappedRange = qreal(pathItems)/modelCount; |
149 else |
169 else |
150 mappedRange = 1.0; |
170 mappedRange = 1.0; |
151 } |
171 } |
152 |
172 |
153 qreal QDeclarativePathViewPrivate::positionOfIndex(qreal index) const |
173 qreal QDeclarativePathViewPrivate::positionOfIndex(qreal index) const |
154 { |
174 { |
155 qreal pos = -1.0; |
175 qreal pos = -1.0; |
156 |
176 |
157 if (model && index >= 0 && index < model->count()) { |
177 if (model && index >= 0 && index < modelCount) { |
158 qreal start = 0.0; |
178 qreal start = 0.0; |
159 if (haveHighlightRange && highlightRangeMode != QDeclarativePathView::NoHighlightRange) |
179 if (haveHighlightRange && highlightRangeMode != QDeclarativePathView::NoHighlightRange) |
160 start = highlightRangeStart; |
180 start = highlightRangeStart; |
161 qreal globalPos = index + offset; |
181 qreal globalPos = index + offset; |
162 globalPos = qmlMod(globalPos, qreal(model->count())) / model->count(); |
182 globalPos = qmlMod(globalPos, qreal(modelCount)) / modelCount; |
163 if (pathItems != -1 && pathItems < model->count()) { |
183 if (pathItems != -1 && pathItems < modelCount) { |
164 globalPos += start * mappedRange; |
184 globalPos += start * mappedRange; |
165 globalPos = qmlMod(globalPos, 1.0); |
185 globalPos = qmlMod(globalPos, 1.0); |
166 if (globalPos < mappedRange) |
186 if (globalPos < mappedRange) |
167 pos = globalPos / mappedRange; |
187 pos = globalPos / mappedRange; |
168 } else { |
188 } else { |
220 if (haveHighlightRange && highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) { |
240 if (haveHighlightRange && highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) { |
221 updateItem(highlightItem, highlightRangeStart); |
241 updateItem(highlightItem, highlightRangeStart); |
222 } else { |
242 } else { |
223 qreal target = currentIndex; |
243 qreal target = currentIndex; |
224 |
244 |
|
245 offsetAdj = 0.0; |
225 tl.reset(moveHighlight); |
246 tl.reset(moveHighlight); |
226 moveHighlight.setValue(highlightPosition); |
247 moveHighlight.setValue(highlightPosition); |
227 |
248 |
228 const int duration = highlightMoveDuration; |
249 const int duration = highlightMoveDuration; |
229 |
250 |
230 if (target - highlightPosition > model->count()/2) { |
251 if (target - highlightPosition > modelCount/2) { |
231 highlightUp = false; |
252 highlightUp = false; |
232 qreal distance = model->count() - target + highlightPosition; |
253 qreal distance = modelCount - target + highlightPosition; |
233 tl.move(moveHighlight, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * highlightPosition / distance)); |
254 tl.move(moveHighlight, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * highlightPosition / distance)); |
234 tl.set(moveHighlight, model->count()-0.01); |
255 tl.set(moveHighlight, modelCount-0.01); |
235 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * (model->count()-target) / distance)); |
256 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * (modelCount-target) / distance)); |
236 } else if (target - highlightPosition <= -model->count()/2) { |
257 } else if (target - highlightPosition <= -modelCount/2) { |
237 highlightUp = true; |
258 highlightUp = true; |
238 qreal distance = model->count() - highlightPosition + target; |
259 qreal distance = modelCount - highlightPosition + target; |
239 tl.move(moveHighlight, model->count()-0.01, QEasingCurve(QEasingCurve::InQuad), int(duration * (model->count()-highlightPosition) / distance)); |
260 tl.move(moveHighlight, modelCount-0.01, QEasingCurve(QEasingCurve::InQuad), int(duration * (modelCount-highlightPosition) / distance)); |
240 tl.set(moveHighlight, 0.0); |
261 tl.set(moveHighlight, 0.0); |
241 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * target / distance)); |
262 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * target / distance)); |
242 } else { |
263 } else { |
243 highlightUp = highlightPosition - target < 0; |
264 highlightUp = highlightPosition - target < 0; |
244 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::InOutQuad), duration); |
265 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::InOutQuad), duration); |
304 q->refill(); |
328 q->refill(); |
305 } |
329 } |
306 |
330 |
307 /*! |
331 /*! |
308 \qmlclass PathView QDeclarativePathView |
332 \qmlclass PathView QDeclarativePathView |
|
333 \ingroup qml-view-elements |
309 \since 4.7 |
334 \since 4.7 |
310 \brief The PathView element lays out model-provided items on a path. |
335 \brief The PathView element lays out model-provided items on a path. |
311 \inherits Item |
336 \inherits Item |
312 |
337 |
313 A PathView displays data from models created from built-in QML elements like ListModel |
338 A PathView displays data from models created from built-in QML elements like ListModel |
314 and XmlListModel, or custom model classes defined in C++ that inherit from |
339 and XmlListModel, or custom model classes defined in C++ that inherit from |
315 QAbstractListModel. |
340 QAbstractListModel. |
316 |
341 |
317 A ListView has a \l model, which defines the data to be displayed, and |
342 The view has a \l model, which defines the data to be displayed, and |
318 a \l delegate, which defines how the data should be displayed. |
343 a \l delegate, which defines how the data should be displayed. |
319 The \l delegate is instantiated for each item on the \l path. |
344 The \l delegate is instantiated for each item on the \l path. |
320 The items may be flicked to move them along the path. |
345 The items may be flicked to move them along the path. |
321 |
346 |
322 For example, if there is a simple list model defined in a file \c ContactModel.qml like this: |
347 For example, if there is a simple list model defined in a file \c ContactModel.qml like this: |
330 \image pathview.gif |
355 \image pathview.gif |
331 |
356 |
332 (Note the above example uses PathAttribute to scale and modify the |
357 (Note the above example uses PathAttribute to scale and modify the |
333 opacity of the items as they rotate. This additional code can be seen in the |
358 opacity of the items as they rotate. This additional code can be seen in the |
334 PathAttribute documentation.) |
359 PathAttribute documentation.) |
|
360 |
|
361 The \c focus can be set to \c true to enable keyboard navigation. |
|
362 The path view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details). |
335 |
363 |
336 Delegates are instantiated as needed and may be destroyed at any time. |
364 Delegates are instantiated as needed and may be destroyed at any time. |
337 State should \e never be stored in a delegate. |
365 State should \e never be stored in a delegate. |
338 |
366 |
339 \bold Note that views do not enable \e clip automatically. If the view |
367 \bold Note that views do not enable \e clip automatically. If the view |
340 is not clipped by another item or the screen, it will be necessary |
368 is not clipped by another item or the screen, it will be necessary |
341 to set \e {clip: true} in order to have the out of view items clipped |
369 to set \e {clip: true} in order to have the out of view items clipped |
342 nicely. |
370 nicely. |
343 |
371 |
344 \sa Path |
372 \sa Path, {declarative/modelviews/pathview}{PathView example} |
345 */ |
373 */ |
346 |
374 |
347 QDeclarativePathView::QDeclarativePathView(QDeclarativeItem *parent) |
375 QDeclarativePathView::QDeclarativePathView(QDeclarativeItem *parent) |
348 : QDeclarativeItem(*(new QDeclarativePathViewPrivate), parent) |
376 : QDeclarativeItem(*(new QDeclarativePathViewPrivate), parent) |
349 { |
377 { |
447 d->ownModel = true; |
475 d->ownModel = true; |
448 } |
476 } |
449 if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model)) |
477 if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model)) |
450 dataModel->setModel(model); |
478 dataModel->setModel(model); |
451 } |
479 } |
|
480 d->modelCount = 0; |
452 if (d->model) { |
481 if (d->model) { |
453 connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); |
482 connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int))); |
454 connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int))); |
483 connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int))); |
455 connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int))); |
484 connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int))); |
456 connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset())); |
485 connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset())); |
457 connect(d->model, SIGNAL(createdItem(int, QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); |
486 connect(d->model, SIGNAL(createdItem(int, QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); |
458 } |
487 d->modelCount = d->model->count(); |
459 d->offset = qmlMod(d->offset, qreal(d->model->count())); |
488 if (d->model->count()) |
460 if (d->offset < 0) |
489 d->offset = qmlMod(d->offset, qreal(d->model->count())); |
461 d->offset = d->model->count() + d->offset; |
490 if (d->offset < 0) |
|
491 d->offset = d->model->count() + d->offset; |
|
492 } |
462 d->regenerate(); |
493 d->regenerate(); |
463 d->fixOffset(); |
494 d->fixOffset(); |
464 emit countChanged(); |
495 emit countChanged(); |
465 emit modelChanged(); |
496 emit modelChanged(); |
466 } |
497 } |
518 } |
549 } |
519 |
550 |
520 void QDeclarativePathView::setCurrentIndex(int idx) |
551 void QDeclarativePathView::setCurrentIndex(int idx) |
521 { |
552 { |
522 Q_D(QDeclarativePathView); |
553 Q_D(QDeclarativePathView); |
523 if (d->model && d->model->count()) |
554 if (d->model && d->modelCount) |
524 idx = qAbs(idx % d->model->count()); |
555 idx = qAbs(idx % d->modelCount); |
525 if (d->model && idx != d->currentIndex) { |
556 if (d->model && idx != d->currentIndex) { |
526 if (d->model->count()) { |
557 if (d->modelCount) { |
527 int itemIndex = (d->currentIndex - d->firstIndex + d->model->count()) % d->model->count(); |
558 int itemIndex = (d->currentIndex - d->firstIndex + d->modelCount) % d->modelCount; |
528 if (itemIndex < d->items.count()) { |
559 if (itemIndex < d->items.count()) { |
529 if (QDeclarativeItem *item = d->items.at(itemIndex)) { |
560 if (QDeclarativeItem *item = d->items.at(itemIndex)) { |
530 if (QDeclarativePathViewAttached *att = d->attached(item)) |
561 if (QDeclarativePathViewAttached *att = d->attached(item)) |
531 att->setIsCurrentItem(false); |
562 att->setIsCurrentItem(false); |
532 } |
563 } |
533 } |
564 } |
534 } |
565 } |
535 d->currentItem = 0; |
566 d->currentItem = 0; |
536 d->moveReason = QDeclarativePathViewPrivate::SetIndex; |
567 d->moveReason = QDeclarativePathViewPrivate::SetIndex; |
537 d->currentIndex = idx; |
568 d->currentIndex = idx; |
538 if (d->model->count()) { |
569 if (d->modelCount) { |
539 if (d->haveHighlightRange && d->highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) |
570 if (d->haveHighlightRange && d->highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) |
540 d->snapToCurrent(); |
571 d->snapToCurrent(); |
541 int itemIndex = (idx - d->firstIndex + d->model->count()) % d->model->count(); |
572 int itemIndex = (idx - d->firstIndex + d->modelCount) % d->modelCount; |
542 if (itemIndex < d->items.count()) { |
573 if (itemIndex < d->items.count()) { |
543 d->currentItem = d->items.at(itemIndex); |
574 d->currentItem = d->items.at(itemIndex); |
544 d->currentItem->setFocus(true); |
575 d->currentItem->setFocus(true); |
545 if (QDeclarativePathViewAttached *att = d->attached(d->currentItem)) |
576 if (QDeclarativePathViewAttached *att = d->attached(d->currentItem)) |
546 att->setIsCurrentItem(true); |
577 att->setIsCurrentItem(true); |
605 void QDeclarativePathViewPrivate::setOffset(qreal o) |
636 void QDeclarativePathViewPrivate::setOffset(qreal o) |
606 { |
637 { |
607 Q_Q(QDeclarativePathView); |
638 Q_Q(QDeclarativePathView); |
608 if (offset != o) { |
639 if (offset != o) { |
609 if (isValid() && q->isComponentComplete()) { |
640 if (isValid() && q->isComponentComplete()) { |
610 offset = qmlMod(o, qreal(model->count())); |
641 offset = qmlMod(o, qreal(modelCount)); |
611 if (offset < 0) |
642 if (offset < 0) |
612 offset += qreal(model->count()); |
643 offset += qreal(modelCount); |
613 q->refill(); |
644 q->refill(); |
614 } else { |
645 } else { |
615 offset = o; |
646 offset = o; |
616 } |
647 } |
617 emit q->offsetChanged(); |
648 emit q->offsetChanged(); |
618 } |
649 } |
619 } |
650 } |
620 |
651 |
|
652 void QDeclarativePathViewPrivate::setAdjustedOffset(qreal o) |
|
653 { |
|
654 setOffset(o+offsetAdj); |
|
655 } |
|
656 |
621 /*! |
657 /*! |
622 \qmlproperty Component PathView::highlight |
658 \qmlproperty Component PathView::highlight |
623 This property holds the component to use as the highlight. |
659 This property holds the component to use as the highlight. |
624 |
660 |
625 An instance of the highlight component will be created for each view. |
661 An instance of the highlight component will be created for each view. |
626 The geometry of the resultant component instance will be managed by the view |
662 The geometry of the resultant component instance will be managed by the view |
627 so as to stay with the current item. |
663 so as to stay with the current item. |
628 |
664 |
629 The below example demonstrates how to make a simple highlight. Note the use |
665 The below example demonstrates how to make a simple highlight. Note the use |
630 of the PathView.onPath property to ensure that the highlight is hidden |
666 of the \l{PathView::onPath}{PathView.onPath} attached property to ensure that |
631 when flicked off of the path. |
667 the highlight is hidden when flicked away from the path. |
632 |
668 |
633 \code |
669 \code |
634 Component { |
670 Component { |
635 Rectangle { |
671 Rectangle { |
636 visible: PathView.onPath |
672 visible: PathView.onPath |
844 if (!interactive) |
882 if (!interactive) |
845 d->tl.clear(); |
883 d->tl.clear(); |
846 emit interactiveChanged(); |
884 emit interactiveChanged(); |
847 } |
885 } |
848 } |
886 } |
|
887 |
|
888 /*! |
|
889 \qmlproperty bool PathView::moving |
|
890 |
|
891 This property holds whether the view is currently moving |
|
892 due to the user either dragging or flicking the view. |
|
893 */ |
|
894 bool QDeclarativePathView::isMoving() const |
|
895 { |
|
896 Q_D(const QDeclarativePathView); |
|
897 return d->moving; |
|
898 } |
|
899 |
|
900 /*! |
|
901 \qmlproperty bool PathView::flicking |
|
902 |
|
903 This property holds whether the view is currently moving |
|
904 due to the user flicking the view. |
|
905 */ |
|
906 bool QDeclarativePathView::isFlicking() const |
|
907 { |
|
908 Q_D(const QDeclarativePathView); |
|
909 return d->flicking; |
|
910 } |
|
911 |
|
912 /*! |
|
913 \qmlsignal PathView::onMovementStarted() |
|
914 |
|
915 This handler is called when the view begins moving due to user |
|
916 interaction. |
|
917 */ |
|
918 |
|
919 /*! |
|
920 \qmlsignal PathView::onMovementEnded() |
|
921 |
|
922 This handler is called when the view stops moving due to user |
|
923 interaction. If a flick was generated, this handler will |
|
924 be triggered once the flick stops. If a flick was not |
|
925 generated, the handler will be triggered when the |
|
926 user stops dragging - i.e. a mouse or touch release. |
|
927 */ |
|
928 |
|
929 /*! |
|
930 \qmlsignal PathView::onFlickStarted() |
|
931 |
|
932 This handler is called when the view is flicked. A flick |
|
933 starts from the point that the mouse or touch is released, |
|
934 while still in motion. |
|
935 */ |
|
936 |
|
937 /*! |
|
938 \qmlsignal PathView::onFlickEnded() |
|
939 |
|
940 This handler is called when the view stops moving due to a flick. |
|
941 */ |
849 |
942 |
850 /*! |
943 /*! |
851 \qmlproperty Component PathView::delegate |
944 \qmlproperty Component PathView::delegate |
852 |
945 |
853 The delegate provides a template defining each item instantiated by the view. |
946 The delegate provides a template defining each item instantiated by the view. |
977 if (!d->interactive || !d->lastPosTime.isValid()) |
1074 if (!d->interactive || !d->lastPosTime.isValid()) |
978 return; |
1075 return; |
979 |
1076 |
980 if (!d->stealMouse) { |
1077 if (!d->stealMouse) { |
981 QPointF delta = event->pos() - d->startPoint; |
1078 QPointF delta = event->pos() - d->startPoint; |
982 if (qAbs(delta.x()) > QApplication::startDragDistance() && qAbs(delta.y()) > QApplication::startDragDistance()) |
1079 if (qAbs(delta.x()) > QApplication::startDragDistance() || qAbs(delta.y()) > QApplication::startDragDistance()) |
983 d->stealMouse = true; |
1080 d->stealMouse = true; |
984 } |
1081 } |
985 |
1082 |
986 if (d->stealMouse) { |
1083 if (d->stealMouse) { |
987 d->moveReason = QDeclarativePathViewPrivate::Mouse; |
1084 d->moveReason = QDeclarativePathViewPrivate::Mouse; |
988 qreal newPc; |
1085 qreal newPc; |
989 d->pointNear(event->pos(), &newPc); |
1086 d->pointNear(event->pos(), &newPc); |
990 qreal diff = (newPc - d->startPc)*d->model->count()*d->mappedRange; |
1087 qreal diff = (newPc - d->startPc)*d->modelCount*d->mappedRange; |
991 if (diff) { |
1088 if (diff) { |
992 setOffset(d->offset + diff); |
1089 setOffset(d->offset + diff); |
993 |
1090 |
994 if (diff > d->model->count()/2) |
1091 if (diff > d->modelCount/2) |
995 diff -= d->model->count(); |
1092 diff -= d->modelCount; |
996 else if (diff < -d->model->count()/2) |
1093 else if (diff < -d->modelCount/2) |
997 diff += d->model->count(); |
1094 diff += d->modelCount; |
998 |
1095 |
999 d->lastElapsed = QDeclarativeItemPrivate::restart(d->lastPosTime); |
1096 d->lastElapsed = QDeclarativeItemPrivate::restart(d->lastPosTime); |
1000 d->lastDist = diff; |
1097 d->lastDist = diff; |
1001 d->startPc = newPc; |
1098 d->startPc = newPc; |
1002 } |
1099 } |
|
1100 if (!d->moving) { |
|
1101 d->moving = true; |
|
1102 emit movingChanged(); |
|
1103 emit movementStarted(); |
|
1104 } |
1003 } |
1105 } |
1004 } |
1106 } |
1005 |
1107 |
1006 void QDeclarativePathView::mouseReleaseEvent(QGraphicsSceneMouseEvent *) |
1108 void QDeclarativePathView::mouseReleaseEvent(QGraphicsSceneMouseEvent *) |
1007 { |
1109 { |
1011 if (!d->interactive || !d->lastPosTime.isValid()) |
1113 if (!d->interactive || !d->lastPosTime.isValid()) |
1012 return; |
1114 return; |
1013 |
1115 |
1014 qreal elapsed = qreal(d->lastElapsed + QDeclarativeItemPrivate::elapsed(d->lastPosTime)) / 1000.; |
1116 qreal elapsed = qreal(d->lastElapsed + QDeclarativeItemPrivate::elapsed(d->lastPosTime)) / 1000.; |
1015 qreal velocity = elapsed > 0. ? d->lastDist / elapsed : 0; |
1117 qreal velocity = elapsed > 0. ? d->lastDist / elapsed : 0; |
1016 if (d->model && d->model->count() && qAbs(velocity) > 1.) { |
1118 if (d->model && d->modelCount && qAbs(velocity) > 1.) { |
1017 qreal count = d->pathItems == -1 ? d->model->count() : d->pathItems; |
1119 qreal count = d->pathItems == -1 ? d->modelCount : d->pathItems; |
1018 if (qAbs(velocity) > count * 2) // limit velocity |
1120 if (qAbs(velocity) > count * 2) // limit velocity |
1019 velocity = (velocity > 0 ? count : -count) * 2; |
1121 velocity = (velocity > 0 ? count : -count) * 2; |
1020 // Calculate the distance to be travelled |
1122 // Calculate the distance to be travelled |
1021 qreal v2 = velocity*velocity; |
1123 qreal v2 = velocity*velocity; |
1022 qreal accel = d->deceleration/10; |
1124 qreal accel = d->deceleration/10; |
1023 // + 0.25 to encourage moving at least one item in the flick direction |
1125 // + 0.25 to encourage moving at least one item in the flick direction |
1024 qreal dist = qMin(qreal(d->model->count()-1), qreal(v2 / (accel * 2.0) + 0.25)); |
1126 qreal dist = qMin(qreal(d->modelCount-1), qreal(v2 / (accel * 2.0) + 0.25)); |
1025 if (d->haveHighlightRange && d->highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) { |
1127 if (d->haveHighlightRange && d->highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) { |
1026 // round to nearest item. |
1128 // round to nearest item. |
1027 if (velocity > 0.) |
1129 if (velocity > 0.) |
1028 dist = qRound(dist + d->offset) - d->offset; |
1130 dist = qRound(dist + d->offset) - d->offset; |
1029 else |
1131 else |
1162 } else { |
1272 } else { |
1163 // qDebug() << "release"; |
1273 // qDebug() << "release"; |
1164 d->updateItem(item, 1.0); |
1274 d->updateItem(item, 1.0); |
1165 d->releaseItem(item); |
1275 d->releaseItem(item); |
1166 if (it == d->items.begin()) { |
1276 if (it == d->items.begin()) { |
1167 if (++d->firstIndex >= d->model->count()) |
1277 if (++d->firstIndex >= d->modelCount) |
1168 d->firstIndex = 0; |
1278 d->firstIndex = 0; |
1169 } |
1279 } |
1170 it = d->items.erase(it); |
1280 it = d->items.erase(it); |
1171 } |
1281 } |
1172 ++idx; |
1282 ++idx; |
1173 if (idx >= d->model->count()) |
1283 if (idx >= d->modelCount) |
1174 idx = 0; |
1284 idx = 0; |
1175 } |
1285 } |
1176 |
1286 |
1177 // add items to beginning and end |
1287 if (d->modelCount) { |
1178 int count = d->pathItems == -1 ? d->model->count() : qMin(d->pathItems, d->model->count()); |
1288 // add items to beginning and end |
1179 if (d->items.count() < count) { |
1289 int count = d->pathItems == -1 ? d->modelCount : qMin(d->pathItems, d->modelCount); |
1180 int idx = qRound(d->model->count() - d->offset) % d->model->count(); |
1290 if (d->items.count() < count) { |
1181 qreal startPos = 0.0; |
1291 int idx = qRound(d->modelCount - d->offset) % d->modelCount; |
1182 if (d->haveHighlightRange && d->highlightRangeMode != QDeclarativePathView::NoHighlightRange) |
1292 qreal startPos = 0.0; |
1183 startPos = d->highlightRangeStart; |
1293 if (d->haveHighlightRange && d->highlightRangeMode != QDeclarativePathView::NoHighlightRange) |
1184 if (d->firstIndex >= 0) { |
1294 startPos = d->highlightRangeStart; |
1185 startPos = d->positionOfIndex(d->firstIndex); |
1295 if (d->firstIndex >= 0) { |
1186 idx = (d->firstIndex + d->items.count()) % d->model->count(); |
1296 startPos = d->positionOfIndex(d->firstIndex); |
1187 } |
1297 idx = (d->firstIndex + d->items.count()) % d->modelCount; |
1188 qreal pos = d->positionOfIndex(idx); |
|
1189 while ((pos > startPos || !d->items.count()) && d->items.count() < count) { |
|
1190 // qDebug() << "append" << idx; |
|
1191 QDeclarativeItem *item = d->getItem(idx); |
|
1192 if (d->model->completePending()) |
|
1193 item->setZValue(idx+1); |
|
1194 if (d->currentIndex == idx) { |
|
1195 item->setFocus(true); |
|
1196 if (QDeclarativePathViewAttached *att = d->attached(item)) |
|
1197 att->setIsCurrentItem(true); |
|
1198 currentVisible = true; |
|
1199 d->currentItemOffset = pos; |
|
1200 d->currentItem = item; |
|
1201 } |
1298 } |
1202 if (d->items.count() == 0) |
1299 qreal pos = d->positionOfIndex(idx); |
1203 d->firstIndex = idx; |
1300 while ((pos > startPos || !d->items.count()) && d->items.count() < count) { |
1204 d->items.append(item); |
1301 // qDebug() << "append" << idx; |
1205 d->updateItem(item, pos); |
1302 QDeclarativeItem *item = d->getItem(idx); |
1206 if (d->model->completePending()) |
1303 if (d->model->completePending()) |
1207 d->model->completeItem(); |
1304 item->setZValue(idx+1); |
1208 ++idx; |
1305 if (d->currentIndex == idx) { |
1209 if (idx >= d->model->count()) |
1306 item->setFocus(true); |
1210 idx = 0; |
1307 if (QDeclarativePathViewAttached *att = d->attached(item)) |
1211 pos = d->positionOfIndex(idx); |
1308 att->setIsCurrentItem(true); |
1212 } |
1309 currentVisible = true; |
1213 |
1310 d->currentItemOffset = pos; |
1214 idx = d->firstIndex - 1; |
1311 d->currentItem = item; |
1215 if (idx < 0) |
1312 } |
1216 idx = d->model->count() - 1; |
1313 if (d->items.count() == 0) |
1217 pos = d->positionOfIndex(idx); |
1314 d->firstIndex = idx; |
1218 while (pos >= 0.0 && pos < startPos) { |
1315 d->items.append(item); |
1219 // qDebug() << "prepend" << idx; |
1316 d->updateItem(item, pos); |
1220 QDeclarativeItem *item = d->getItem(idx); |
1317 if (d->model->completePending()) |
1221 if (d->model->completePending()) |
1318 d->model->completeItem(); |
1222 item->setZValue(idx+1); |
1319 ++idx; |
1223 if (d->currentIndex == idx) { |
1320 if (idx >= d->modelCount) |
1224 item->setFocus(true); |
1321 idx = 0; |
1225 if (QDeclarativePathViewAttached *att = d->attached(item)) |
1322 pos = d->positionOfIndex(idx); |
1226 att->setIsCurrentItem(true); |
|
1227 currentVisible = true; |
|
1228 d->currentItemOffset = pos; |
|
1229 d->currentItem = item; |
|
1230 } |
1323 } |
1231 d->items.prepend(item); |
1324 |
1232 d->updateItem(item, pos); |
|
1233 if (d->model->completePending()) |
|
1234 d->model->completeItem(); |
|
1235 d->firstIndex = idx; |
|
1236 idx = d->firstIndex - 1; |
1325 idx = d->firstIndex - 1; |
1237 if (idx < 0) |
1326 if (idx < 0) |
1238 idx = d->model->count() - 1; |
1327 idx = d->modelCount - 1; |
1239 pos = d->positionOfIndex(idx); |
1328 pos = d->positionOfIndex(idx); |
|
1329 while (pos >= 0.0 && pos < startPos) { |
|
1330 // qDebug() << "prepend" << idx; |
|
1331 QDeclarativeItem *item = d->getItem(idx); |
|
1332 if (d->model->completePending()) |
|
1333 item->setZValue(idx+1); |
|
1334 if (d->currentIndex == idx) { |
|
1335 item->setFocus(true); |
|
1336 if (QDeclarativePathViewAttached *att = d->attached(item)) |
|
1337 att->setIsCurrentItem(true); |
|
1338 currentVisible = true; |
|
1339 d->currentItemOffset = pos; |
|
1340 d->currentItem = item; |
|
1341 } |
|
1342 d->items.prepend(item); |
|
1343 d->updateItem(item, pos); |
|
1344 if (d->model->completePending()) |
|
1345 d->model->completeItem(); |
|
1346 d->firstIndex = idx; |
|
1347 idx = d->firstIndex - 1; |
|
1348 if (idx < 0) |
|
1349 idx = d->modelCount - 1; |
|
1350 pos = d->positionOfIndex(idx); |
|
1351 } |
1240 } |
1352 } |
1241 } |
1353 } |
1242 |
1354 |
1243 if (!currentVisible) |
1355 if (!currentVisible) |
1244 d->currentItemOffset = 1.0; |
1356 d->currentItemOffset = 1.0; |
1250 } else if (d->highlightItem && d->moveReason != QDeclarativePathViewPrivate::SetIndex) { |
1362 } else if (d->highlightItem && d->moveReason != QDeclarativePathViewPrivate::SetIndex) { |
1251 d->updateItem(d->highlightItem, d->currentItemOffset); |
1363 d->updateItem(d->highlightItem, d->currentItemOffset); |
1252 if (QDeclarativePathViewAttached *att = d->attached(d->highlightItem)) |
1364 if (QDeclarativePathViewAttached *att = d->attached(d->highlightItem)) |
1253 att->setOnPath(currentVisible); |
1365 att->setOnPath(currentVisible); |
1254 } |
1366 } |
|
1367 while (d->itemCache.count()) |
|
1368 d->releaseItem(d->itemCache.takeLast()); |
1255 } |
1369 } |
1256 |
1370 |
1257 void QDeclarativePathView::itemsInserted(int modelIndex, int count) |
1371 void QDeclarativePathView::itemsInserted(int modelIndex, int count) |
1258 { |
1372 { |
1259 //XXX support animated insertion |
1373 //XXX support animated insertion |
1260 Q_D(QDeclarativePathView); |
1374 Q_D(QDeclarativePathView); |
1261 if (!d->isValid() || !isComponentComplete()) |
1375 if (!d->isValid() || !isComponentComplete()) |
1262 return; |
1376 return; |
1263 |
1377 |
1264 QList<QDeclarativeItem *> removedItems = d->items; |
1378 d->itemCache += d->items; |
1265 d->items.clear(); |
1379 d->items.clear(); |
1266 if (modelIndex <= d->currentIndex) { |
1380 if (modelIndex <= d->currentIndex) { |
1267 d->currentIndex += count; |
1381 d->currentIndex += count; |
1268 emit currentIndexChanged(); |
1382 emit currentIndexChanged(); |
1269 } |
1383 } else if (d->offset != 0) { |
1270 d->regenerate(); |
1384 d->offset += count; |
1271 while (removedItems.count()) |
1385 d->offsetAdj += count; |
1272 d->releaseItem(removedItems.takeLast()); |
1386 } |
1273 d->updateCurrent(); |
1387 |
|
1388 d->modelCount = d->model->count(); |
|
1389 if (d->flicking || d->moving) { |
|
1390 d->regenerate(); |
|
1391 d->updateCurrent(); |
|
1392 } else { |
|
1393 d->firstIndex = -1; |
|
1394 d->updateMappedRange(); |
|
1395 d->scheduleLayout(); |
|
1396 } |
1274 emit countChanged(); |
1397 emit countChanged(); |
1275 } |
1398 } |
1276 |
1399 |
1277 void QDeclarativePathView::itemsRemoved(int modelIndex, int count) |
1400 void QDeclarativePathView::itemsRemoved(int modelIndex, int count) |
1278 { |
1401 { |
1279 //XXX support animated removal |
1402 //XXX support animated removal |
1280 Q_D(QDeclarativePathView); |
1403 Q_D(QDeclarativePathView); |
1281 if (!d->isValid() || !isComponentComplete()) |
1404 if (!d->model || !d->modelCount || !d->model->isValid() || !d->path || !isComponentComplete()) |
1282 return; |
1405 return; |
1283 |
1406 |
1284 // fix current |
1407 // fix current |
1285 bool currentChanged = false; |
1408 bool currentChanged = false; |
1286 if (d->currentIndex >= modelIndex + count) { |
1409 if (d->currentIndex >= modelIndex + count) { |
1287 d->currentIndex -= count; |
1410 d->currentIndex -= count; |
1288 currentChanged = true; |
1411 currentChanged = true; |
1289 } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) { |
1412 } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) { |
1290 // current item has been removed. |
1413 // current item has been removed. |
1291 d->currentIndex = qMin(modelIndex, d->model->count()-1); |
1414 d->currentIndex = qMin(modelIndex, d->modelCount-1); |
1292 if (d->currentItem) { |
1415 if (d->currentItem) { |
1293 if (QDeclarativePathViewAttached *att = d->attached(d->currentItem)) |
1416 if (QDeclarativePathViewAttached *att = d->attached(d->currentItem)) |
1294 att->setIsCurrentItem(true); |
1417 att->setIsCurrentItem(true); |
1295 } |
1418 } |
1296 currentChanged = true; |
1419 currentChanged = true; |
1297 } |
1420 } |
1298 |
1421 |
1299 QList<QDeclarativeItem *> removedItems = d->items; |
1422 d->itemCache += d->items; |
1300 d->items.clear(); |
1423 d->items.clear(); |
1301 if (d->offset >= d->model->count()) |
1424 |
1302 d->offset = d->model->count() - 1; |
1425 if (modelIndex > d->currentIndex) { |
1303 |
1426 if (d->offset >= count) { |
|
1427 d->offset -= count; |
|
1428 d->offsetAdj -= count; |
|
1429 } |
|
1430 } |
|
1431 |
|
1432 d->modelCount = d->model->count(); |
1304 d->regenerate(); |
1433 d->regenerate(); |
1305 while (removedItems.count()) |
|
1306 d->releaseItem(removedItems.takeLast()); |
|
1307 d->updateCurrent(); |
1434 d->updateCurrent(); |
|
1435 if (!d->modelCount) |
|
1436 update(); |
1308 if (currentChanged) |
1437 if (currentChanged) |
1309 emit currentIndexChanged(); |
1438 emit currentIndexChanged(); |
1310 emit countChanged(); |
1439 emit countChanged(); |
1311 } |
1440 } |
1312 |
1441 |
1395 if (!haveHighlightRange || highlightRangeMode != QDeclarativePathView::StrictlyEnforceRange) |
1540 if (!haveHighlightRange || highlightRangeMode != QDeclarativePathView::StrictlyEnforceRange) |
1396 return; |
1541 return; |
1397 |
1542 |
1398 int idx = calcCurrentIndex(); |
1543 int idx = calcCurrentIndex(); |
1399 if (model && idx != currentIndex) { |
1544 if (model && idx != currentIndex) { |
1400 int itemIndex = (currentIndex - firstIndex + model->count()) % model->count(); |
1545 int itemIndex = (currentIndex - firstIndex + modelCount) % modelCount; |
1401 if (itemIndex < items.count()) { |
1546 if (itemIndex < items.count()) { |
1402 if (QDeclarativeItem *item = items.at(itemIndex)) { |
1547 if (QDeclarativeItem *item = items.at(itemIndex)) { |
1403 if (QDeclarativePathViewAttached *att = attached(item)) |
1548 if (QDeclarativePathViewAttached *att = attached(item)) |
1404 att->setIsCurrentItem(false); |
1549 att->setIsCurrentItem(false); |
1405 } |
1550 } |
1406 } |
1551 } |
1407 currentIndex = idx; |
1552 currentIndex = idx; |
1408 currentItem = 0; |
1553 currentItem = 0; |
1409 itemIndex = (idx - firstIndex + model->count()) % model->count(); |
1554 itemIndex = (idx - firstIndex + modelCount) % modelCount; |
1410 if (itemIndex < items.count()) { |
1555 if (itemIndex < items.count()) { |
1411 currentItem = items.at(itemIndex); |
1556 currentItem = items.at(itemIndex); |
1412 currentItem->setFocus(true); |
1557 currentItem->setFocus(true); |
1413 if (QDeclarativePathViewAttached *att = attached(currentItem)) |
1558 if (QDeclarativePathViewAttached *att = attached(currentItem)) |
1414 att->setIsCurrentItem(true); |
1559 att->setIsCurrentItem(true); |
1436 } |
1581 } |
1437 } |
1582 } |
1438 |
1583 |
1439 void QDeclarativePathViewPrivate::snapToCurrent() |
1584 void QDeclarativePathViewPrivate::snapToCurrent() |
1440 { |
1585 { |
1441 if (!model || model->count() <= 0) |
1586 if (!model || modelCount <= 0) |
1442 return; |
1587 return; |
1443 |
1588 |
1444 qreal targetOffset = model->count() - currentIndex; |
1589 qreal targetOffset = modelCount - currentIndex; |
1445 |
1590 |
1446 moveReason = Other; |
1591 moveReason = Other; |
|
1592 offsetAdj = 0.0; |
1447 tl.reset(moveOffset); |
1593 tl.reset(moveOffset); |
1448 moveOffset.setValue(offset); |
1594 moveOffset.setValue(offset); |
1449 |
1595 |
1450 const int duration = highlightMoveDuration; |
1596 const int duration = highlightMoveDuration; |
1451 |
1597 |
1452 if (targetOffset - offset > model->count()/2) { |
1598 if (targetOffset - offset > modelCount/2) { |
1453 qreal distance = model->count() - targetOffset + offset; |
1599 qreal distance = modelCount - targetOffset + offset; |
1454 tl.move(moveOffset, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * offset / distance)); |
1600 tl.move(moveOffset, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * offset / distance)); |
1455 tl.set(moveOffset, model->count()); |
1601 tl.set(moveOffset, modelCount); |
1456 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::OutQuad), int(duration * (model->count()-targetOffset) / distance)); |
1602 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::OutQuad), int(duration * (modelCount-targetOffset) / distance)); |
1457 } else if (targetOffset - offset <= -model->count()/2) { |
1603 } else if (targetOffset - offset <= -modelCount/2) { |
1458 qreal distance = model->count() - offset + targetOffset; |
1604 qreal distance = modelCount - offset + targetOffset; |
1459 tl.move(moveOffset, model->count(), QEasingCurve(QEasingCurve::InQuad), int(duration * (model->count()-offset) / distance)); |
1605 tl.move(moveOffset, modelCount, QEasingCurve(QEasingCurve::InQuad), int(duration * (modelCount-offset) / distance)); |
1460 tl.set(moveOffset, 0.0); |
1606 tl.set(moveOffset, 0.0); |
1461 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::OutQuad), int(duration * targetOffset / distance)); |
1607 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::OutQuad), int(duration * targetOffset / distance)); |
1462 } else { |
1608 } else { |
1463 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration); |
1609 tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration); |
1464 } |
1610 } |