82 virtual void updateScrollMetrics(); |
85 virtual void updateScrollMetrics(); |
83 virtual void scrollTo(const QModelIndex &index, HbAbstractItemView::ScrollHint hint); |
86 virtual void scrollTo(const QModelIndex &index, HbAbstractItemView::ScrollHint hint); |
84 |
87 |
85 void init(QAbstractItemModel *model); |
88 void init(QAbstractItemModel *model); |
86 void calculateItemHeight(); |
89 void calculateItemHeight(); |
|
90 void selectCurrentIndex(const QModelIndex& index); |
87 |
91 |
88 void selectMiddleItem(); |
92 void selectMiddleItem(); |
|
93 HbAbstractViewItem* getCenterItem(); |
89 |
94 |
90 void createPrimitives(); |
95 void createPrimitives(); |
91 |
|
92 void delayedSelectCurrent(const QModelIndex& index); |
|
93 |
|
94 void _q_scrollingStarted();//private slot |
96 void _q_scrollingStarted();//private slot |
95 void _q_scrollingEnded();//private slot |
97 void _q_scrollingEnded();//private slot |
96 void _q_delayedSelectCurrent();//private slot |
|
97 |
98 |
98 void setPressedState(HbAbstractViewItem *item); |
99 void setPressedState(HbAbstractViewItem *item); |
99 |
100 |
100 private: |
101 private: |
101 qreal mHeight; |
102 qreal mHeight; |
186 return newDelta; |
187 return newDelta; |
187 } |
188 } |
188 void HbTumbleViewItemContainer::setLoopingEnabled(bool looped) { |
189 void HbTumbleViewItemContainer::setLoopingEnabled(bool looped) { |
189 Q_D(HbTumbleViewItemContainer); |
190 Q_D(HbTumbleViewItemContainer); |
190 d->mIsLooped = looped; |
191 d->mIsLooped = looped; |
|
192 if(looped){ |
|
193 recycleItems(QPointF()); |
|
194 } |
|
195 else{ |
|
196 setModelIndexes(d->mItemView->currentIndex()); |
|
197 } |
191 } |
198 } |
192 bool HbTumbleViewItemContainer::isLoopingEnabled() const { |
199 bool HbTumbleViewItemContainer::isLoopingEnabled() const { |
193 Q_D(const HbTumbleViewItemContainer); |
200 Q_D(const HbTumbleViewItemContainer); |
194 return d->mIsLooped; |
201 return d->mIsLooped; |
|
202 } |
|
203 |
|
204 void HbTumbleViewItemContainer::removeItem(const QModelIndex &index, bool animate ) |
|
205 { |
|
206 Q_D(HbTumbleViewItemContainer); |
|
207 HbListItemContainer::removeItem(index,animate); |
|
208 if (d->mItems.count() < maxItemCount()) { |
|
209 d->updateItemBuffer(); |
|
210 } |
|
211 } |
|
212 |
|
213 void HbTumbleViewItemContainer::setModelIndexes(const QModelIndex &startIndex) |
|
214 { |
|
215 Q_D(HbAbstractItemContainer); |
|
216 |
|
217 if (!d->mItemView || !d->mItemView->model()) { |
|
218 return; |
|
219 } |
|
220 |
|
221 QModelIndex index = startIndex; |
|
222 if (!index.isValid()) { |
|
223 if (!d->mItems.isEmpty()) { |
|
224 index = d->mItems.first()->modelIndex(); |
|
225 } |
|
226 |
|
227 if (!index.isValid()) { |
|
228 index = d->mItemView->modelIterator()->nextIndex(index); |
|
229 } |
|
230 } |
|
231 |
|
232 QModelIndexList indexList; |
|
233 |
|
234 int itemCount(d->mItems.count()); |
|
235 |
|
236 if (itemCount != 0 && index.isValid()) { |
|
237 indexList.append(index); |
|
238 } |
|
239 |
|
240 for (int i = indexList.count(); i < itemCount; ++i) { |
|
241 index = d->mItemView->modelIterator()->nextIndex(indexList.last()); |
|
242 |
|
243 if (index.isValid()) { |
|
244 indexList.append(index); |
|
245 } else { |
|
246 break; |
|
247 } |
|
248 } |
|
249 if(isLoopingEnabled() && indexList.count()<itemCount){ |
|
250 QModelIndex firstModelIndex = d->mItemView->modelIterator()->index(0,QModelIndex()); |
|
251 indexList.append(firstModelIndex); |
|
252 for (int i = indexList.count(); i < itemCount; ++i) { |
|
253 index = d->mItemView->modelIterator()->nextIndex(indexList.last()); |
|
254 |
|
255 if (index.isValid()) { |
|
256 indexList.append(index); |
|
257 } else { |
|
258 break; |
|
259 } |
|
260 } |
|
261 } |
|
262 |
|
263 for (int i = indexList.count(); i < itemCount; ++i) { |
|
264 index = d->mItemView->modelIterator()->previousIndex(indexList.first()); |
|
265 |
|
266 if (index.isValid()) { |
|
267 indexList.prepend(index); |
|
268 } else { |
|
269 break; |
|
270 } |
|
271 } |
|
272 |
|
273 // if items have been added/removed in the middle of the buffer, there might be items |
|
274 // that can be reused at the end of the buffer. The following block will scan for |
|
275 // those items and move them in correct position |
|
276 int lastUsedItem = -1; |
|
277 for (int indexCounter = 0; indexCounter < indexList.count(); ++indexCounter) { |
|
278 HbAbstractViewItem *item = d->mItems.at(indexCounter); |
|
279 if (item && item->modelIndex() == indexList.at(indexCounter)) { |
|
280 lastUsedItem = indexCounter; |
|
281 } else { |
|
282 for (int itemCounter = lastUsedItem + 1; itemCounter < d->mItems.count(); itemCounter++) { |
|
283 HbAbstractViewItem *item2 = d->mItems.at(itemCounter); |
|
284 qDebug()<<"containeritemsat("<<itemCounter<<")="<<item2->modelIndex()<<"--indexList.at(" |
|
285 <<indexCounter<<")="<<indexList.at(indexCounter); |
|
286 |
|
287 if (item2->modelIndex() == indexList.at(indexCounter)) { |
|
288 d->mItems.swap(indexCounter, itemCounter); |
|
289 itemRemoved(d->mItems.at(indexCounter), false); |
|
290 itemRemoved(d->mItems.at(itemCounter), false); |
|
291 itemAdded(qMin(indexCounter, itemCounter), d->mItems.at(qMin(indexCounter, itemCounter)), false); |
|
292 itemAdded(qMax(indexCounter, itemCounter), d->mItems.at(qMax(indexCounter, itemCounter)), false); |
|
293 lastUsedItem = itemCounter; |
|
294 break; |
|
295 } |
|
296 } |
|
297 |
|
298 } |
|
299 qDebug()<<"last used item -"<<lastUsedItem; |
|
300 qDebug()<<"-------------------------------------------------------"; |
|
301 } |
|
302 |
|
303 int indexCount(indexList.count()); |
|
304 for (int i = 0; i < itemCount; ++i) { |
|
305 HbAbstractViewItem *item = d->mItems.at(i); |
|
306 HbAbstractViewItem *prototype = 0; |
|
307 // setItemModelIndex() is considered recycling. It is called only, if recycling is permitted |
|
308 if (i < indexCount) { |
|
309 prototype = d->itemPrototype(indexList.at(i)); |
|
310 } |
|
311 if (prototype) { |
|
312 if ( prototype == item->prototype() |
|
313 && d->mItemRecycling) { |
|
314 setItemModelIndex(item, indexList.at(i)); |
|
315 } else if ( d->mItemRecycling |
|
316 || ( !d->mItemRecycling |
|
317 && item->modelIndex() != indexList.at(i))) { |
|
318 d->deleteItem(item); |
|
319 insertItem(i, indexList.at(i)); |
|
320 } // else: !d->mItemRecycling && item->modelIndex().isValid() == indexList.at(i)) |
|
321 } else { |
|
322 d->deleteItem(item); |
|
323 itemCount--; |
|
324 i--; |
|
325 } |
|
326 } |
195 } |
327 } |
196 |
328 |
197 HbTumbleViewItemContainerPrivate::HbTumbleViewItemContainerPrivate() |
329 HbTumbleViewItemContainerPrivate::HbTumbleViewItemContainerPrivate() |
198 : mIsLooped(false) |
330 : mIsLooped(false) |
199 { |
331 { |
317 mDelayedSelectTimer.setSingleShot(true); |
449 mDelayedSelectTimer.setSingleShot(true); |
318 bool b = q->connect(q,SIGNAL(scrollingStarted()),q,SLOT(_q_scrollingStarted())); |
450 bool b = q->connect(q,SIGNAL(scrollingStarted()),q,SLOT(_q_scrollingStarted())); |
319 Q_ASSERT(b); |
451 Q_ASSERT(b); |
320 b = q->connect(q,SIGNAL(scrollingEnded()),q,SLOT(_q_scrollingEnded())); |
452 b = q->connect(q,SIGNAL(scrollingEnded()),q,SLOT(_q_scrollingEnded())); |
321 Q_ASSERT(b); |
453 Q_ASSERT(b); |
322 b = q->connect(&mDelayedSelectTimer,SIGNAL(timeout()),q,SLOT(_q_delayedSelectCurrent())); |
|
323 Q_UNUSED(b); |
|
324 createPrimitives(); |
454 createPrimitives(); |
325 } |
455 } |
326 |
456 |
327 void HbTumbleViewPrivate::selectMiddleItem() |
457 void HbTumbleViewPrivate::selectMiddleItem() |
328 { |
458 { |
329 Q_Q(HbTumbleView); |
459 Q_Q(HbTumbleView); |
330 //scroll little amount so item settle's in |
460 //scroll little amount so item settle's in |
331 if(!q->scene()) { |
461 if(!q->scene()) { |
332 return; |
462 return; |
333 } |
463 } |
334 QPointF centerPt = q->mapToScene(q->boundingRect().center()); |
464 HbAbstractViewItem *item = getCenterItem(); |
335 HbAbstractViewItem *item = itemAt(centerPt); |
|
336 |
465 |
337 if(item) { |
466 if(item) { |
338 #ifdef HBTUMBLE_DEBUG |
467 #ifdef HBTUMBLE_DEBUG |
339 qDebug() << "HbTumbleViewPrivate::selectMiddleItem - " << item->modelIndex().row() ; |
468 qDebug() << "HbTumbleViewPrivate::selectMiddleItem - " << item->modelIndex().row() ; |
340 #endif |
469 #endif |
341 delayedSelectCurrent(item->modelIndex()); |
470 selectCurrentIndex(item->modelIndex()); |
342 mSelected = item->modelIndex().row(); |
471 mSelected = item->modelIndex().row(); |
343 } |
472 } |
344 } |
473 } |
|
474 |
|
475 HbAbstractViewItem* HbTumbleViewPrivate::getCenterItem() |
|
476 { |
|
477 Q_Q(HbTumbleView); |
|
478 |
|
479 if(!q->scene()) { |
|
480 return 0; |
|
481 } |
|
482 QPointF centerPt = q->mapToScene(q->boundingRect().center()); |
|
483 return itemAt(centerPt); |
|
484 } |
345 |
485 |
346 void HbTumbleViewPrivate::scrollTo(const QModelIndex &index, HbAbstractItemView::ScrollHint hint) |
486 void HbTumbleViewPrivate::scrollTo(const QModelIndex &index, HbAbstractItemView::ScrollHint hint) |
347 { |
487 { |
348 Q_Q(HbTumbleView); |
488 Q_Q(HbTumbleView); |
349 #ifdef HBTUMBLE_DEBUG |
489 #ifdef HBTUMBLE_DEBUG |
364 qDebug() << "HbTumbleViewPrivate::scrollTo(" << index.row() << ",failed to get itembyindex"; |
504 qDebug() << "HbTumbleViewPrivate::scrollTo(" << index.row() << ",failed to get itembyindex"; |
365 } |
505 } |
366 #endif |
506 #endif |
367 } |
507 } |
368 |
508 |
|
509 void HbTumbleViewPrivate::selectCurrentIndex(const QModelIndex& index) |
|
510 { |
|
511 Q_Q(HbTumbleView); |
|
512 if(!mIsAnimating && !mIsScrolling) { |
|
513 if(index == q->currentIndex()){ |
|
514 HbAbstractViewItem *item =q->itemByIndex(index); |
|
515 QPointF delta = pixelsToScroll(item,HbAbstractItemView::PositionAtCenter ); |
|
516 QPointF newPos = -mContainer->pos() + delta; |
|
517 checkBoundaries(newPos); |
|
518 scrollByAmount(newPos - (-mContents->pos())); |
|
519 mIsScrolling = false; |
|
520 } |
|
521 else{ |
|
522 q->setCurrentIndex(index,QItemSelectionModel::SelectCurrent); |
|
523 } |
|
524 } |
|
525 } |
|
526 |
369 void HbTumbleView::scrollTo(const QModelIndex &index, ScrollHint) |
527 void HbTumbleView::scrollTo(const QModelIndex &index, ScrollHint) |
370 { |
528 { |
371 #ifdef HBTUMBLE_DEBUG |
529 #ifdef HBTUMBLE_DEBUG |
372 qDebug() << "HbTumbleView::scrollTo(" << index.row() << ", )"; |
530 qDebug() << "HbTumbleView::scrollTo(" << index.row() << ", )"; |
373 #endif |
531 #endif |
417 } |
575 } |
418 |
576 |
419 /*! |
577 /*! |
420 @proto |
578 @proto |
421 \class HbTumbleView |
579 \class HbTumbleView |
422 \this is a tumbler widget which lets the user select alphanumeric values from a predefined list of |
580 \brief HbTumbleView is a tumbler widget which lets the user select alphanumeric values from a predefined list of values via vertical flicking and dragging.<br> |
423 values via vertical flicking and dragging. Typically widgets such as date picker and time picker use the |
581 |
|
582 Typically widgets such as date picker and time picker use the |
424 Tumbler. The Tumbler could also be used to change other values such as number code sequence, |
583 Tumbler. The Tumbler could also be used to change other values such as number code sequence, |
425 landmark coordinates, country selection, and currency. |
584 landmark coordinates, country selection, and currency.<br> |
426 |
585 |
427 Only strings can be accepted as HbTumbleView's items. |
586 Only strings can be accepted as HbTumbleView's items. |
428 |
587 |
429 \this can be used like this: |
588 HbTumbleView can be used, as shown in the below code snippet: |
430 \snippet{ultimatecodesnippet/ultimatecodesnippet.cpp,52} |
589 \snippet{ultimatecodesnippet/ultimatecodesnippet.cpp,52} |
|
590 |
|
591 \image html hbdatetimepicker_date.png "Two TumbleViews(tumblers) in a datetime picker widget in d/MMMM format. One tumbler for day and the other for month." |
|
592 <b>Note:</b>Graphics in the above image varies depending on theme. |
431 */ |
593 */ |
432 |
594 |
433 /*! |
595 /*! |
434 \fn void itemSelected(int index) |
596 \fn void itemSelected(int index) |
435 |
597 |
436 This signal is emitted when an item is selected in date time picker. |
598 This signal is emitted when an item is selected in the tumbler. |
437 \param index selected item. |
599 \param index selected item. |
438 |
600 |
439 */ |
601 */ |
440 |
602 |
441 /*! |
603 /*! |
539 Q_D(HbTumbleView); |
701 Q_D(HbTumbleView); |
540 d->mSelected = index; |
702 d->mSelected = index; |
541 |
703 |
542 QModelIndex modelIndex = d->mModelIterator->index(index, rootIndex()); |
704 QModelIndex modelIndex = d->mModelIterator->index(index, rootIndex()); |
543 if(modelIndex.isValid()) { |
705 if(modelIndex.isValid()) { |
544 d->delayedSelectCurrent(modelIndex); |
706 d->selectCurrentIndex(modelIndex); |
545 emitActivated(modelIndex); |
707 emitActivated(modelIndex); |
546 } |
708 } |
547 } |
709 } |
548 |
710 |
549 /*! |
711 /*! |
550 Returns the index of the current selected item in integer format. |
712 Returns the index of the current selected item in integer format. |
551 |
713 |
552 \return current index selected in tumble view. |
714 \return current index of the item selected in the tumbler. |
553 */ |
715 */ |
554 int HbTumbleView::selected() const |
716 int HbTumbleView::selected() const |
555 { |
717 { |
556 return currentIndex().row(); |
718 return currentIndex().row(); |
557 } |
719 } |
558 |
720 |
559 /*! |
721 /*! |
560 |
|
561 \deprecated HbTumbleView::primitive(HbStyle::Primitive) |
722 \deprecated HbTumbleView::primitive(HbStyle::Primitive) |
562 is deprecated. |
723 is deprecated. |
563 |
724 |
564 \reimp |
725 \reimp |
565 */ |
726 */ |
651 /*! |
812 /*! |
652 \reimp |
813 \reimp |
653 */ |
814 */ |
654 void HbTumbleView::rowsInserted(const QModelIndex &parent,int start,int end) |
815 void HbTumbleView::rowsInserted(const QModelIndex &parent,int start,int end) |
655 { |
816 { |
|
817 Q_D(HbTumbleView); |
656 HbListView::rowsInserted(parent,start,end); |
818 HbListView::rowsInserted(parent,start,end); |
|
819 |
|
820 //Trigger recycle, in the scenario where item's are deleted and reinserted again. |
|
821 QPointF alignedPosition = d->mContents->pos(); |
|
822 Q_UNUSED(alignedPosition); |
|
823 if(alignedPosition.y() > 0.0){ |
|
824 alignedPosition.setY(0.0); |
|
825 d->HbScrollAreaPrivate::setContentPosition(alignedPosition); |
|
826 } |
657 scrollTo(currentIndex(),PositionAtCenter); |
827 scrollTo(currentIndex(),PositionAtCenter); |
658 } |
|
659 |
|
660 void HbTumbleViewPrivate::_q_delayedSelectCurrent() |
|
661 { |
|
662 Q_Q(HbTumbleView); |
|
663 if(!mIsAnimating && !mIsScrolling) { |
|
664 if(mDelayedSelectIndex == q->currentIndex()){ |
|
665 HbAbstractViewItem *item =q->itemByIndex(mDelayedSelectIndex); |
|
666 QPointF delta = pixelsToScroll(item,HbAbstractItemView::PositionAtCenter ); |
|
667 QPointF newPos = -mContainer->pos() + delta; |
|
668 checkBoundaries(newPos); |
|
669 scrollByAmount(newPos - (-mContents->pos())); |
|
670 } |
|
671 else{ |
|
672 q->setCurrentIndex(mDelayedSelectIndex); |
|
673 } |
|
674 } |
|
675 } |
828 } |
676 |
829 |
677 /*! |
830 /*! |
678 \reimp |
831 \reimp |
679 */ |
832 */ |
790 Q_UNUSED(constraint); |
943 Q_UNUSED(constraint); |
791 Q_D(const HbTumbleView); |
944 Q_D(const HbTumbleView); |
792 QSizeF sh=HbListView::sizeHint(which,constraint); |
945 QSizeF sh=HbListView::sizeHint(which,constraint); |
793 switch(which) { |
946 switch(which) { |
794 case Qt::MinimumSize: |
947 case Qt::MinimumSize: |
795 sh = QSizeF(sh.width(),HB_TUMBLE_PREFERRED_ITEMS*d->mHeight); |
948 sh = QSizeF(sh.width(),HB_TUMBLE_PREFERRED_ITEMS*d->mHeight); |
796 break; |
949 break; |
797 case Qt::PreferredSize: |
950 case Qt::PreferredSize: |
798 sh = QSizeF(sh.width(),HB_TUMBLE_PREFERRED_ITEMS*d->mHeight); |
951 sh = QSizeF(sh.width(),HB_TUMBLE_PREFERRED_ITEMS*d->mHeight); |
799 sh.setWidth(HbWidget::sizeHint(which, constraint).width()); |
952 sh.setWidth(HbWidget::sizeHint(which, constraint).width()); |
800 break; |
953 break; |
876 Q_D(HbTumbleView); |
1028 Q_D(HbTumbleView); |
877 d->mNeedScrolling = true; |
1029 d->mNeedScrolling = true; |
878 HbListView::rowsRemoved(parent,start,end); |
1030 HbListView::rowsRemoved(parent,start,end); |
879 scrollTo(currentIndex(),PositionAtCenter); |
1031 scrollTo(currentIndex(),PositionAtCenter); |
880 } |
1032 } |
881 void HbTumbleViewPrivate::delayedSelectCurrent(const QModelIndex& index) |
1033 |
882 { |
1034 /*! |
883 mDelayedSelectIndex = index; |
1035 \reimp |
884 mDelayedSelectTimer.start(DELAYED_SELECT_INTERVAL); |
1036 */ |
|
1037 void HbTumbleView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) |
|
1038 { |
|
1039 Q_D(const HbListView); |
|
1040 |
|
1041 QList<HbAbstractViewItem *> items = d->mContainer->items(); |
|
1042 bool empty = items.isEmpty(); |
|
1043 QModelIndex rootIndex = d->mModelIterator->rootIndex(); |
|
1044 QModelIndex firstIndex = items.first()->modelIndex(); |
|
1045 QModelIndex lastIndex = items.last()->modelIndex(); |
|
1046 |
|
1047 if (!empty && |
|
1048 topLeft.parent() == rootIndex |
|
1049 /*&& firstIndex.row() <= bottomRight.row() |
|
1050 && topLeft.row() <= lastIndex.row()*/) { |
|
1051 HbAbstractItemView::dataChanged(topLeft, bottomRight); |
|
1052 } |
|
1053 } |
|
1054 |
|
1055 void HbTumbleView::gestureEvent(QGestureEvent *event) |
|
1056 { |
|
1057 Q_D(HbTumbleView); |
|
1058 if(QTapGesture *gesture = static_cast<QTapGesture*>(event->gesture(Qt::TapGesture))){ |
|
1059 switch(gesture->state()){ |
|
1060 case Qt::GestureStarted: |
|
1061 if(d->mIsAnimating || d->mIsScrolling){ |
|
1062 d->mInternalScroll = true; |
|
1063 HbAbstractViewItem* centerItem = d->getCenterItem(); |
|
1064 if(centerItem){ |
|
1065 d->mDelayedSelectIndex = centerItem->modelIndex(); |
|
1066 } |
|
1067 } |
|
1068 else{ |
|
1069 d->mDelayedSelectIndex = QModelIndex(); |
|
1070 } |
|
1071 break; |
|
1072 case Qt::GestureCanceled: |
|
1073 d->mInternalScroll = false; |
|
1074 break; |
|
1075 case Qt::GestureFinished: |
|
1076 d->mInternalScroll = false; |
|
1077 if(d->mDelayedSelectIndex.isValid()){ |
|
1078 d->selectCurrentIndex(d->mDelayedSelectIndex); |
|
1079 } |
|
1080 break; |
|
1081 default: |
|
1082 break; |
|
1083 |
|
1084 } |
|
1085 } |
|
1086 HbListView::gestureEvent(event); |
885 } |
1087 } |
886 |
1088 |
887 #include "moc_hbtumbleview.cpp" |
1089 #include "moc_hbtumbleview.cpp" |