source: trunk/src/declarative/graphicsitems/qdeclarativelistview.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 115.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtDeclarative module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "private/qdeclarativelistview_p.h"
43
44#include "private/qdeclarativeflickable_p_p.h"
45#include "private/qdeclarativevisualitemmodel_p.h"
46
47#include "private/qdeclarativesmoothedanimation_p_p.h"
48#include <qdeclarativeexpression.h>
49#include <qdeclarativeengine.h>
50#include <qdeclarativeguard_p.h>
51#include <qdeclarativeinfo.h>
52
53#include <qlistmodelinterface_p.h>
54#include <qmath.h>
55#include <QKeyEvent>
56
57QT_BEGIN_NAMESPACE
58
59void QDeclarativeViewSection::setProperty(const QString &property)
60{
61 if (property != m_property) {
62 m_property = property;
63 emit propertyChanged();
64 }
65}
66
67void QDeclarativeViewSection::setCriteria(QDeclarativeViewSection::SectionCriteria criteria)
68{
69 if (criteria != m_criteria) {
70 m_criteria = criteria;
71 emit criteriaChanged();
72 }
73}
74
75void QDeclarativeViewSection::setDelegate(QDeclarativeComponent *delegate)
76{
77 if (delegate != m_delegate) {
78 m_delegate = delegate;
79 emit delegateChanged();
80 }
81}
82
83QString QDeclarativeViewSection::sectionString(const QString &value)
84{
85 if (m_criteria == FirstCharacter)
86 return value.isEmpty() ? QString() : value.at(0);
87 else
88 return value;
89}
90
91//----------------------------------------------------------------------------
92
93class FxListItem
94{
95public:
96 FxListItem(QDeclarativeItem *i, QDeclarativeListView *v) : item(i), section(0), view(v) {
97 attached = static_cast<QDeclarativeListViewAttached*>(qmlAttachedPropertiesObject<QDeclarativeListView>(item));
98 if (attached)
99 attached->setView(view);
100 }
101 ~FxListItem() {}
102 qreal position() const {
103 if (section)
104 return (view->orientation() == QDeclarativeListView::Vertical ? section->y() : section->x());
105 else
106 return (view->orientation() == QDeclarativeListView::Vertical ? item->y() : item->x());
107 }
108 qreal itemPosition() const {
109 return (view->orientation() == QDeclarativeListView::Vertical ? item->y() : item->x());
110 }
111 qreal size() const {
112 if (section)
113 return (view->orientation() == QDeclarativeListView::Vertical ? item->height()+section->height() : item->width()+section->width());
114 else
115 return (view->orientation() == QDeclarativeListView::Vertical ? item->height() : item->width());
116 }
117 qreal itemSize() const {
118 return (view->orientation() == QDeclarativeListView::Vertical ? item->height() : item->width());
119 }
120 qreal sectionSize() const {
121 if (section)
122 return (view->orientation() == QDeclarativeListView::Vertical ? section->height() : section->width());
123 return 0.0;
124 }
125 qreal endPosition() const {
126 return (view->orientation() == QDeclarativeListView::Vertical
127 ? item->y() + (item->height() >= 1.0 ? item->height() : 1)
128 : item->x() + (item->width() >= 1.0 ? item->width() : 1)) - 1;
129 }
130 void setPosition(qreal pos) {
131 if (view->orientation() == QDeclarativeListView::Vertical) {
132 if (section) {
133 section->setY(pos);
134 pos += section->height();
135 }
136 item->setY(pos);
137 } else {
138 if (section) {
139 section->setX(pos);
140 pos += section->width();
141 }
142 item->setX(pos);
143 }
144 }
145 void setSize(qreal size) {
146 if (view->orientation() == QDeclarativeListView::Vertical)
147 item->setHeight(size);
148 else
149 item->setWidth(size);
150 }
151 bool contains(int x, int y) const {
152 return (x >= item->x() && x < item->x() + item->width() &&
153 y >= item->y() && y < item->y() + item->height());
154 }
155
156 QDeclarativeItem *item;
157 QDeclarativeItem *section;
158 QDeclarativeListView *view;
159 QDeclarativeListViewAttached *attached;
160 int index;
161};
162
163//----------------------------------------------------------------------------
164
165class QDeclarativeListViewPrivate : public QDeclarativeFlickablePrivate
166{
167 Q_DECLARE_PUBLIC(QDeclarativeListView)
168
169public:
170 QDeclarativeListViewPrivate()
171 : currentItem(0), orient(QDeclarativeListView::Vertical)
172 , visiblePos(0), visibleIndex(0)
173 , averageSize(100.0), currentIndex(-1), requestedIndex(-1)
174 , itemCount(0), highlightRangeStart(0), highlightRangeEnd(0)
175 , highlightComponent(0), highlight(0), trackedItem(0)
176 , moveReason(Other), buffer(0), highlightPosAnimator(0), highlightSizeAnimator(0)
177 , sectionCriteria(0), spacing(0.0)
178 , highlightMoveSpeed(400), highlightMoveDuration(-1)
179 , highlightResizeSpeed(400), highlightResizeDuration(-1), highlightRange(QDeclarativeListView::NoHighlightRange)
180 , snapMode(QDeclarativeListView::NoSnap), overshootDist(0.0)
181 , footerComponent(0), footer(0), headerComponent(0), header(0)
182 , bufferMode(BufferBefore | BufferAfter)
183 , ownModel(false), wrap(false), autoHighlight(true), haveHighlightRange(false)
184 , correctFlick(false), inFlickCorrection(false), lazyRelease(false)
185 , deferredRelease(false), layoutScheduled(false), currentIndexCleared(false)
186 , inViewportMoved(false)
187 , minExtentDirty(true), maxExtentDirty(true)
188 {}
189
190 void init();
191 void clear();
192 FxListItem *createItem(int modelIndex);
193 void releaseItem(FxListItem *item);
194
195 FxListItem *visibleItem(int modelIndex) const {
196 if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) {
197 for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) {
198 FxListItem *item = visibleItems.at(i);
199 if (item->index == modelIndex)
200 return item;
201 }
202 }
203 return 0;
204 }
205
206 FxListItem *firstVisibleItem() const {
207 const qreal pos = position();
208 for (int i = 0; i < visibleItems.count(); ++i) {
209 FxListItem *item = visibleItems.at(i);
210 if (item->index != -1 && item->endPosition() > pos)
211 return item;
212 }
213 return visibleItems.count() ? visibleItems.first() : 0;
214 }
215
216 FxListItem *nextVisibleItem() const {
217 const qreal pos = position();
218 bool foundFirst = false;
219 for (int i = 0; i < visibleItems.count(); ++i) {
220 FxListItem *item = visibleItems.at(i);
221 if (item->index != -1) {
222 if (foundFirst)
223 return item;
224 else if (item->position() < pos && item->endPosition() > pos)
225 foundFirst = true;
226 }
227 }
228 return 0;
229 }
230
231 qreal position() const {
232 Q_Q(const QDeclarativeListView);
233 return orient == QDeclarativeListView::Vertical ? q->contentY() : q->contentX();
234 }
235 void setPosition(qreal pos) {
236 Q_Q(QDeclarativeListView);
237 if (orient == QDeclarativeListView::Vertical)
238 q->QDeclarativeFlickable::setContentY(pos);
239 else
240 q->QDeclarativeFlickable::setContentX(pos);
241 }
242 qreal size() const {
243 Q_Q(const QDeclarativeListView);
244 return orient == QDeclarativeListView::Vertical ? q->height() : q->width();
245 }
246
247 qreal startPosition() const {
248 qreal pos = 0;
249 if (!visibleItems.isEmpty()) {
250 pos = (*visibleItems.constBegin())->position();
251 if (visibleIndex > 0)
252 pos -= visibleIndex * (averageSize + spacing);
253 }
254 return pos;
255 }
256
257 qreal endPosition() const {
258 qreal pos = 0;
259 if (!visibleItems.isEmpty()) {
260 int invisibleCount = visibleItems.count() - visibleIndex;
261 for (int i = visibleItems.count()-1; i >= 0; --i) {
262 if (visibleItems.at(i)->index != -1) {
263 invisibleCount = model->count() - visibleItems.at(i)->index - 1;
264 break;
265 }
266 }
267 pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
268 } else if (model && model->count()) {
269 pos = model->count() * averageSize + (model->count()-1) * spacing;
270 }
271 return pos;
272 }
273
274 qreal positionAt(int modelIndex) const {
275 if (FxListItem *item = visibleItem(modelIndex))
276 return item->position();
277 if (!visibleItems.isEmpty()) {
278 if (modelIndex < visibleIndex) {
279 int count = visibleIndex - modelIndex;
280 qreal cs = 0;
281 if (modelIndex == currentIndex && currentItem) {
282 cs = currentItem->size() + spacing;
283 --count;
284 }
285 return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
286 } else {
287 int idx = visibleItems.count() - 1;
288 while (idx >= 0 && visibleItems.at(idx)->index == -1)
289 --idx;
290 if (idx < 0)
291 idx = visibleIndex;
292 else
293 idx = visibleItems.at(idx)->index;
294 int count = modelIndex - idx - 1;
295 return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing) + 1;
296 }
297 }
298 return 0;
299 }
300
301 qreal endPositionAt(int modelIndex) const {
302 if (FxListItem *item = visibleItem(modelIndex))
303 return item->endPosition();
304 if (!visibleItems.isEmpty()) {
305 if (modelIndex < visibleIndex) {
306 int count = visibleIndex - modelIndex;
307 return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing - 1;
308 } else {
309 int idx = visibleItems.count() - 1;
310 while (idx >= 0 && visibleItems.at(idx)->index == -1)
311 --idx;
312 if (idx < 0)
313 idx = visibleIndex;
314 else
315 idx = visibleItems.at(idx)->index;
316 int count = modelIndex - idx - 1;
317 return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing);
318 }
319 }
320 return 0;
321 }
322
323 QString sectionAt(int modelIndex) {
324 if (FxListItem *item = visibleItem(modelIndex))
325 return item->attached->section();
326
327 QString section;
328 if (sectionCriteria) {
329 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
330 section = sectionCriteria->sectionString(propValue);
331 }
332
333 return section;
334 }
335
336 bool isValid() const {
337 return model && model->count() && model->isValid();
338 }
339
340 qreal snapPosAt(qreal pos) {
341 if (FxListItem *snapItem = snapItemAt(pos))
342 return snapItem->position();
343 if (visibleItems.count()) {
344 qreal firstPos = visibleItems.first()->position();
345 qreal endPos = visibleItems.last()->position();
346 if (pos < firstPos) {
347 return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
348 } else if (pos > endPos)
349 return endPos + qRound((pos - endPos) / averageSize) * averageSize;
350 }
351 return qRound((pos - startPosition()) / averageSize) * averageSize + startPosition();
352 }
353
354 FxListItem *snapItemAt(qreal pos) {
355 FxListItem *snapItem = 0;
356 for (int i = 0; i < visibleItems.count(); ++i) {
357 FxListItem *item = visibleItems[i];
358 if (item->index == -1)
359 continue;
360 qreal itemTop = item->position();
361 if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size() - 1)
362 return item;
363 if (itemTop+item->size()/2 >= pos && itemTop-item->size()/2 < pos)
364 snapItem = item;
365 }
366 return snapItem;
367 }
368
369 int lastVisibleIndex() const {
370 int lastIndex = -1;
371 for (int i = visibleItems.count()-1; i >= 0; --i) {
372 FxListItem *listItem = visibleItems.at(i);
373 if (listItem->index != -1) {
374 lastIndex = listItem->index;
375 break;
376 }
377 }
378 return lastIndex;
379 }
380
381 // map a model index to visibleItems index.
382 int mapFromModel(int modelIndex) const {
383 if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count())
384 return -1;
385 for (int i = 0; i < visibleItems.count(); ++i) {
386 FxListItem *listItem = visibleItems.at(i);
387 if (listItem->index == modelIndex)
388 return i;
389 if (listItem->index > modelIndex)
390 return -1;
391 }
392 return -1; // Not in visibleList
393 }
394
395 void updateViewport() {
396 Q_Q(QDeclarativeListView);
397 if (orient == QDeclarativeListView::Vertical) {
398 q->setContentHeight(endPosition() - startPosition() + 1);
399 } else {
400 q->setContentWidth(endPosition() - startPosition() + 1);
401 }
402 }
403
404 void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) {
405 Q_Q(QDeclarativeListView);
406 QDeclarativeFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
407 if (!q->isComponentComplete())
408 return;
409 if (item != contentItem && (!highlight || item != highlight->item)) {
410 if ((orient == QDeclarativeListView::Vertical && newGeometry.height() != oldGeometry.height())
411 || (orient == QDeclarativeListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
412 scheduleLayout();
413 }
414 }
415 if ((header && header->item == item) || (footer && footer->item == item)) {
416 updateHeader();
417 updateFooter();
418 }
419 if (currentItem && currentItem->item == item)
420 updateHighlight();
421 if (trackedItem && trackedItem->item == item)
422 q->trackedPositionChanged();
423 }
424
425 // for debugging only
426 void checkVisible() const {
427 int skip = 0;
428 for (int i = 0; i < visibleItems.count(); ++i) {
429 FxListItem *listItem = visibleItems.at(i);
430 if (listItem->index == -1) {
431 ++skip;
432 } else if (listItem->index != visibleIndex + i - skip) {
433 qFatal("index %d %d %d", visibleIndex, i, listItem->index);
434 }
435 }
436 }
437
438 void refill(qreal from, qreal to, bool doBuffer = false);
439 void scheduleLayout();
440 void layout();
441 void updateUnrequestedIndexes();
442 void updateUnrequestedPositions();
443 void updateTrackedItem();
444 void createHighlight();
445 void updateHighlight();
446 void createSection(FxListItem *);
447 void updateSections();
448 void updateCurrentSection();
449 void updateCurrent(int);
450 void updateAverage();
451 void updateHeader();
452 void updateFooter();
453 void fixupPosition();
454 virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
455 virtual void flick(QDeclarativeFlickablePrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
456 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity);
457
458 QDeclarativeGuard<QDeclarativeVisualModel> model;
459 QVariant modelVariant;
460 QList<FxListItem*> visibleItems;
461 QHash<QDeclarativeItem*,int> unrequestedItems;
462 FxListItem *currentItem;
463 QDeclarativeListView::Orientation orient;
464 qreal visiblePos;
465 int visibleIndex;
466 qreal averageSize;
467 int currentIndex;
468 int requestedIndex;
469 int itemCount;
470 qreal highlightRangeStart;
471 qreal highlightRangeEnd;
472 QDeclarativeComponent *highlightComponent;
473 FxListItem *highlight;
474 FxListItem *trackedItem;
475 enum MovementReason { Other, SetIndex, Mouse };
476 MovementReason moveReason;
477 int buffer;
478 QSmoothedAnimation *highlightPosAnimator;
479 QSmoothedAnimation *highlightSizeAnimator;
480 QDeclarativeViewSection *sectionCriteria;
481 QString currentSection;
482 static const int sectionCacheSize = 4;
483 QDeclarativeItem *sectionCache[sectionCacheSize];
484 qreal spacing;
485 qreal highlightMoveSpeed;
486 int highlightMoveDuration;
487 qreal highlightResizeSpeed;
488 int highlightResizeDuration;
489 QDeclarativeListView::HighlightRangeMode highlightRange;
490 QDeclarativeListView::SnapMode snapMode;
491 qreal overshootDist;
492 QDeclarativeComponent *footerComponent;
493 FxListItem *footer;
494 QDeclarativeComponent *headerComponent;
495 FxListItem *header;
496 enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 };
497 int bufferMode;
498 mutable qreal minExtent;
499 mutable qreal maxExtent;
500
501 bool ownModel : 1;
502 bool wrap : 1;
503 bool autoHighlight : 1;
504 bool haveHighlightRange : 1;
505 bool correctFlick : 1;
506 bool inFlickCorrection : 1;
507 bool lazyRelease : 1;
508 bool deferredRelease : 1;
509 bool layoutScheduled : 1;
510 bool currentIndexCleared : 1;
511 bool inViewportMoved : 1;
512 mutable bool minExtentDirty : 1;
513 mutable bool maxExtentDirty : 1;
514};
515
516void QDeclarativeListViewPrivate::init()
517{
518 Q_Q(QDeclarativeListView);
519 q->setFlag(QGraphicsItem::ItemIsFocusScope);
520 addItemChangeListener(this, Geometry);
521 QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
522 q->setFlickableDirection(QDeclarativeFlickable::VerticalFlick);
523 ::memset(sectionCache, 0, sizeof(QDeclarativeItem*) * sectionCacheSize);
524}
525
526void QDeclarativeListViewPrivate::clear()
527{
528 timeline.clear();
529 for (int i = 0; i < visibleItems.count(); ++i)
530 releaseItem(visibleItems.at(i));
531 visibleItems.clear();
532 for (int i = 0; i < sectionCacheSize; ++i) {
533 delete sectionCache[i];
534 sectionCache[i] = 0;
535 }
536 visiblePos = header ? header->size() : 0;
537 visibleIndex = 0;
538 releaseItem(currentItem);
539 currentItem = 0;
540 createHighlight();
541 trackedItem = 0;
542 minExtentDirty = true;
543 maxExtentDirty = true;
544 itemCount = 0;
545}
546
547FxListItem *QDeclarativeListViewPrivate::createItem(int modelIndex)
548{
549 Q_Q(QDeclarativeListView);
550 // create object
551 requestedIndex = modelIndex;
552 FxListItem *listItem = 0;
553 if (QDeclarativeItem *item = model->item(modelIndex, false)) {
554 listItem = new FxListItem(item, q);
555 listItem->index = modelIndex;
556 // initialise attached properties
557 if (sectionCriteria) {
558 QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
559 listItem->attached->m_section = sectionCriteria->sectionString(propValue);
560 if (modelIndex > 0) {
561 if (FxListItem *item = visibleItem(modelIndex-1))
562 listItem->attached->m_prevSection = item->attached->section();
563 else
564 listItem->attached->m_prevSection = sectionAt(modelIndex-1);
565 }
566 if (modelIndex < model->count()-1) {
567 if (FxListItem *item = visibleItem(modelIndex+1))
568 listItem->attached->m_nextSection = item->attached->section();
569 else
570 listItem->attached->m_nextSection = sectionAt(modelIndex+1);
571 }
572 }
573 if (model->completePending()) {
574 // complete
575 listItem->item->setZValue(1);
576 listItem->item->setParentItem(q->contentItem());
577 model->completeItem();
578 } else {
579 listItem->item->setParentItem(q->contentItem());
580 }
581 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
582 itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
583 if (sectionCriteria && sectionCriteria->delegate()) {
584 if (listItem->attached->m_prevSection != listItem->attached->m_section)
585 createSection(listItem);
586 }
587 unrequestedItems.remove(listItem->item);
588 }
589 requestedIndex = -1;
590
591 return listItem;
592}
593
594void QDeclarativeListViewPrivate::releaseItem(FxListItem *item)
595{
596 Q_Q(QDeclarativeListView);
597 if (!item || !model)
598 return;
599 if (trackedItem == item)
600 trackedItem = 0;
601 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item->item));
602 itemPrivate->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
603 if (model->release(item->item) == 0) {
604 // item was not destroyed, and we no longer reference it.
605 unrequestedItems.insert(item->item, model->indexOf(item->item, q));
606 }
607 if (item->section) {
608 int i = 0;
609 do {
610 if (!sectionCache[i]) {
611 sectionCache[i] = item->section;
612 sectionCache[i]->setVisible(false);
613 item->section = 0;
614 break;
615 }
616 ++i;
617 } while (i < sectionCacheSize);
618 delete item->section;
619 }
620 delete item;
621}
622
623void QDeclarativeListViewPrivate::refill(qreal from, qreal to, bool doBuffer)
624{
625 Q_Q(QDeclarativeListView);
626 if (!isValid() || !q->isComponentComplete())
627 return;
628 itemCount = model->count();
629 qreal bufferFrom = from - buffer;
630 qreal bufferTo = to + buffer;
631 qreal fillFrom = from;
632 qreal fillTo = to;
633 if (doBuffer && (bufferMode & BufferAfter))
634 fillTo = bufferTo;
635 if (doBuffer && (bufferMode & BufferBefore))
636 fillFrom = bufferFrom;
637
638 int modelIndex = visibleIndex;
639 qreal itemEnd = visiblePos-1;
640 if (!visibleItems.isEmpty()) {
641 visiblePos = (*visibleItems.constBegin())->position();
642 itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing;
643 int i = visibleItems.count() - 1;
644 while (i > 0 && visibleItems.at(i)->index == -1)
645 --i;
646 if (visibleItems.at(i)->index != -1)
647 modelIndex = visibleItems.at(i)->index + 1;
648 }
649
650 bool changed = false;
651 FxListItem *item = 0;
652 qreal pos = itemEnd + 1;
653 while (modelIndex < model->count() && pos <= fillTo) {
654// qDebug() << "refill: append item" << modelIndex << "pos" << pos;
655 if (!(item = createItem(modelIndex)))
656 break;
657 item->setPosition(pos);
658 pos += item->size() + spacing;
659 visibleItems.append(item);
660 ++modelIndex;
661 changed = true;
662 if (doBuffer) // never buffer more than one item per frame
663 break;
664 }
665 while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos-1 >= fillFrom) {
666// qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos;
667 if (!(item = createItem(visibleIndex-1)))
668 break;
669 --visibleIndex;
670 visiblePos -= item->size() + spacing;
671 item->setPosition(visiblePos);
672 visibleItems.prepend(item);
673 changed = true;
674 if (doBuffer) // never buffer more than one item per frame
675 break;
676 }
677
678 if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create
679 while (visibleItems.count() > 1 && (item = visibleItems.first()) && item->endPosition() < bufferFrom) {
680 if (item->attached->delayRemove())
681 break;
682// qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
683 if (item->index != -1)
684 visibleIndex++;
685 visibleItems.removeFirst();
686 releaseItem(item);
687 changed = true;
688 }
689 while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
690 if (item->attached->delayRemove())
691 break;
692// qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position();
693 visibleItems.removeLast();
694 releaseItem(item);
695 changed = true;
696 }
697 deferredRelease = false;
698 } else {
699 deferredRelease = true;
700 }
701 if (changed) {
702 minExtentDirty = true;
703 maxExtentDirty = true;
704 if (visibleItems.count())
705 visiblePos = (*visibleItems.constBegin())->position();
706 updateAverage();
707 if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
708 currentItem->setPosition(positionAt(currentIndex));
709 updateHighlight();
710 }
711
712 if (sectionCriteria)
713 updateCurrentSection();
714 if (header)
715 updateHeader();
716 if (footer)
717 updateFooter();
718 updateViewport();
719 updateUnrequestedPositions();
720 } else if (!doBuffer && buffer && bufferMode != NoBuffer) {
721 refill(from, to, true);
722 }
723 lazyRelease = false;
724}
725
726void QDeclarativeListViewPrivate::scheduleLayout()
727{
728 Q_Q(QDeclarativeListView);
729 if (!layoutScheduled) {
730 layoutScheduled = true;
731 QCoreApplication::postEvent(q, new QEvent(QEvent::User), Qt::HighEventPriority);
732 }
733}
734
735void QDeclarativeListViewPrivate::layout()
736{
737 Q_Q(QDeclarativeListView);
738 layoutScheduled = false;
739 if (!isValid() && !visibleItems.count()) {
740 clear();
741 setPosition(0);
742 return;
743 }
744 if (!visibleItems.isEmpty()) {
745 bool fixedCurrent = currentItem && visibleItems.first()->item == currentItem->item;
746 qreal sum = visibleItems.first()->size();
747 qreal pos = visibleItems.first()->position() + visibleItems.first()->size() + spacing;
748 for (int i=1; i < visibleItems.count(); ++i) {
749 FxListItem *item = visibleItems.at(i);
750 item->setPosition(pos);
751 pos += item->size() + spacing;
752 sum += item->size();
753 fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
754 }
755 averageSize = qRound(sum / visibleItems.count());
756 // move current item if it is not a visible item.
757 if (currentIndex >= 0 && currentItem && !fixedCurrent)
758 currentItem->setPosition(positionAt(currentIndex));
759 }
760 q->refill();
761 minExtentDirty = true;
762 maxExtentDirty = true;
763 updateHighlight();
764 if (!q->isMoving() && !q->isFlicking()) {
765 fixupPosition();
766 q->refill();
767 }
768 if (header)
769 updateHeader();
770 if (footer)
771 updateFooter();
772 updateViewport();
773}
774
775void QDeclarativeListViewPrivate::updateUnrequestedIndexes()
776{
777 Q_Q(QDeclarativeListView);
778 QHash<QDeclarativeItem*,int>::iterator it;
779 for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
780 *it = model->indexOf(it.key(), q);
781}
782
783void QDeclarativeListViewPrivate::updateUnrequestedPositions()
784{
785 Q_Q(QDeclarativeListView);
786 if (unrequestedItems.count()) {
787 qreal pos = position();
788 QHash<QDeclarativeItem*,int>::const_iterator it;
789 for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) {
790 QDeclarativeItem *item = it.key();
791 if (orient == QDeclarativeListView::Vertical) {
792 if (item->y() + item->height() > pos && item->y() < pos + q->height())
793 item->setY(positionAt(*it));
794 } else {
795 if (item->x() + item->width() > pos && item->x() < pos + q->width())
796 item->setX(positionAt(*it));
797 }
798 }
799 }
800}
801
802void QDeclarativeListViewPrivate::updateTrackedItem()
803{
804 Q_Q(QDeclarativeListView);
805 FxListItem *item = currentItem;
806 if (highlight)
807 item = highlight;
808 trackedItem = item;
809 if (trackedItem)
810 q->trackedPositionChanged();
811}
812
813void QDeclarativeListViewPrivate::createHighlight()
814{
815 Q_Q(QDeclarativeListView);
816 bool changed = false;
817 if (highlight) {
818 if (trackedItem == highlight)
819 trackedItem = 0;
820 delete highlight->item;
821 delete highlight;
822 highlight = 0;
823 delete highlightPosAnimator;
824 delete highlightSizeAnimator;
825 highlightPosAnimator = 0;
826 highlightSizeAnimator = 0;
827 changed = true;
828 }
829
830 if (currentItem) {
831 QDeclarativeItem *item = 0;
832 if (highlightComponent) {
833 QDeclarativeContext *highlightContext = new QDeclarativeContext(qmlContext(q));
834 QObject *nobj = highlightComponent->create(highlightContext);
835 if (nobj) {
836 QDeclarative_setParent_noEvent(highlightContext, nobj);
837 item = qobject_cast<QDeclarativeItem *>(nobj);
838 if (!item)
839 delete nobj;
840 } else {
841 delete highlightContext;
842 }
843 } else {
844 item = new QDeclarativeItem;
845 }
846 if (item) {
847 QDeclarative_setParent_noEvent(item, q->contentItem());
848 item->setParentItem(q->contentItem());
849 highlight = new FxListItem(item, q);
850 if (currentItem && autoHighlight) {
851 if (orient == QDeclarativeListView::Vertical) {
852 highlight->item->setHeight(currentItem->item->height());
853 } else {
854 highlight->item->setWidth(currentItem->item->width());
855 }
856 highlight->setPosition(currentItem->itemPosition());
857 }
858 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
859 itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
860 const QLatin1String posProp(orient == QDeclarativeListView::Vertical ? "y" : "x");
861 highlightPosAnimator = new QSmoothedAnimation(q);
862 highlightPosAnimator->target = QDeclarativeProperty(highlight->item, posProp);
863 highlightPosAnimator->velocity = highlightMoveSpeed;
864 highlightPosAnimator->userDuration = highlightMoveDuration;
865 const QLatin1String sizeProp(orient == QDeclarativeListView::Vertical ? "height" : "width");
866 highlightSizeAnimator = new QSmoothedAnimation(q);
867 highlightSizeAnimator->velocity = highlightResizeSpeed;
868 highlightSizeAnimator->userDuration = highlightResizeDuration;
869 highlightSizeAnimator->target = QDeclarativeProperty(highlight->item, sizeProp);
870 if (autoHighlight) {
871 highlightPosAnimator->restart();
872 highlightSizeAnimator->restart();
873 }
874 changed = true;
875 }
876 }
877 if (changed)
878 emit q->highlightItemChanged();
879}
880
881void QDeclarativeListViewPrivate::updateHighlight()
882{
883 if ((!currentItem && highlight) || (currentItem && !highlight))
884 createHighlight();
885 if (currentItem && autoHighlight && highlight && !movingHorizontally && !movingVertically) {
886 // auto-update highlight
887 highlightPosAnimator->to = currentItem->itemPosition();
888 highlightSizeAnimator->to = currentItem->itemSize();
889 if (orient == QDeclarativeListView::Vertical) {
890 if (highlight->item->width() == 0)
891 highlight->item->setWidth(currentItem->item->width());
892 } else {
893 if (highlight->item->height() == 0)
894 highlight->item->setHeight(currentItem->item->height());
895 }
896 highlightPosAnimator->restart();
897 highlightSizeAnimator->restart();
898 }
899 updateTrackedItem();
900}
901
902void QDeclarativeListViewPrivate::createSection(FxListItem *listItem)
903{
904 Q_Q(QDeclarativeListView);
905 if (!sectionCriteria || !sectionCriteria->delegate())
906 return;
907 if (listItem->attached->m_prevSection != listItem->attached->m_section) {
908 if (!listItem->section) {
909 qreal pos = listItem->position();
910 int i = sectionCacheSize-1;
911 while (i >= 0 && !sectionCache[i])
912 --i;
913 if (i >= 0) {
914 listItem->section = sectionCache[i];
915 sectionCache[i] = 0;
916 listItem->section->setVisible(true);
917 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
918 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
919 } else {
920 QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
921 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
922 QObject *nobj = sectionCriteria->delegate()->create(context);
923 if (nobj) {
924 QDeclarative_setParent_noEvent(context, nobj);
925 listItem->section = qobject_cast<QDeclarativeItem *>(nobj);
926 if (!listItem->section) {
927 delete nobj;
928 } else {
929 listItem->section->setZValue(1);
930 QDeclarative_setParent_noEvent(listItem->section, q->contentItem());
931 listItem->section->setParentItem(q->contentItem());
932 }
933 } else {
934 delete context;
935 }
936 }
937 listItem->setPosition(pos);
938 }
939 } else if (listItem->section) {
940 qreal pos = listItem->position();
941 int i = 0;
942 do {
943 if (!sectionCache[i]) {
944 sectionCache[i] = listItem->section;
945 sectionCache[i]->setVisible(false);
946 listItem->section = 0;
947 return;
948 }
949 ++i;
950 } while (i < sectionCacheSize);
951 delete listItem->section;
952 listItem->section = 0;
953 listItem->setPosition(pos);
954 }
955}
956
957void QDeclarativeListViewPrivate::updateSections()
958{
959 if (sectionCriteria && !visibleItems.isEmpty()) {
960 QString prevSection;
961 if (visibleIndex > 0)
962 prevSection = sectionAt(visibleIndex-1);
963 QDeclarativeListViewAttached *prevAtt = 0;
964 int idx = -1;
965 for (int i = 0; i < visibleItems.count(); ++i) {
966 if (visibleItems.at(i)->index != -1) {
967 QDeclarativeListViewAttached *attached = visibleItems.at(i)->attached;
968 attached->setPrevSection(prevSection);
969 QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property());
970 attached->setSection(sectionCriteria->sectionString(propValue));
971 if (prevAtt)
972 prevAtt->setNextSection(attached->section());
973 createSection(visibleItems.at(i));
974 prevSection = attached->section();
975 prevAtt = attached;
976 idx = visibleItems.at(i)->index;
977 }
978 }
979 if (prevAtt) {
980 if (idx > 0 && idx < model->count()-1)
981 prevAtt->setNextSection(sectionAt(idx+1));
982 else
983 prevAtt->setNextSection(QString());
984 }
985 }
986}
987
988void QDeclarativeListViewPrivate::updateCurrentSection()
989{
990 Q_Q(QDeclarativeListView);
991 if (!sectionCriteria || visibleItems.isEmpty()) {
992 if (!currentSection.isEmpty()) {
993 currentSection.clear();
994 emit q->currentSectionChanged();
995 }
996 return;
997 }
998 int index = 0;
999 while (index < visibleItems.count() && visibleItems.at(index)->endPosition() < position())
1000 ++index;
1001
1002 QString newSection = currentSection;
1003 if (index < visibleItems.count())
1004 newSection = visibleItems.at(index)->attached->section();
1005 else
1006 newSection = visibleItems.first()->attached->section();
1007 if (newSection != currentSection) {
1008 currentSection = newSection;
1009 emit q->currentSectionChanged();
1010 }
1011}
1012
1013void QDeclarativeListViewPrivate::updateCurrent(int modelIndex)
1014{
1015 Q_Q(QDeclarativeListView);
1016 if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
1017 if (currentItem) {
1018 currentItem->attached->setIsCurrentItem(false);
1019 releaseItem(currentItem);
1020 currentItem = 0;
1021 currentIndex = modelIndex;
1022 emit q->currentIndexChanged();
1023 updateHighlight();
1024 } else if (currentIndex != modelIndex) {
1025 currentIndex = modelIndex;
1026 emit q->currentIndexChanged();
1027 }
1028 return;
1029 }
1030
1031 if (currentItem && currentIndex == modelIndex) {
1032 updateHighlight();
1033 return;
1034 }
1035 FxListItem *oldCurrentItem = currentItem;
1036 currentIndex = modelIndex;
1037 currentItem = createItem(modelIndex);
1038 if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item))
1039 oldCurrentItem->attached->setIsCurrentItem(false);
1040 if (currentItem) {
1041 if (modelIndex == visibleIndex - 1 && visibleItems.count()) {
1042 // We can calculate exact postion in this case
1043 currentItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
1044 } else {
1045 // Create current item now and position as best we can.
1046 // Its position will be corrected when it becomes visible.
1047 currentItem->setPosition(positionAt(modelIndex));
1048 }
1049 currentItem->item->setFocus(true);
1050 currentItem->attached->setIsCurrentItem(true);
1051 // Avoid showing section delegate twice. We still need the section heading so that
1052 // currentItem positioning works correctly.
1053 // This is slightly sub-optimal, but section heading caching minimizes the impact.
1054 if (currentItem->section)
1055 currentItem->section->setVisible(false);
1056 if (visibleItems.isEmpty())
1057 averageSize = currentItem->size();
1058 }
1059 updateHighlight();
1060 emit q->currentIndexChanged();
1061 // Release the old current item
1062 releaseItem(oldCurrentItem);
1063}
1064
1065void QDeclarativeListViewPrivate::updateAverage()
1066{
1067 if (!visibleItems.count())
1068 return;
1069 qreal sum = 0.0;
1070 for (int i = 0; i < visibleItems.count(); ++i)
1071 sum += visibleItems.at(i)->size();
1072 averageSize = qRound(sum / visibleItems.count());
1073}
1074
1075void QDeclarativeListViewPrivate::updateFooter()
1076{
1077 Q_Q(QDeclarativeListView);
1078 if (!footer && footerComponent) {
1079 QDeclarativeItem *item = 0;
1080 QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
1081 QObject *nobj = footerComponent->create(context);
1082 if (nobj) {
1083 QDeclarative_setParent_noEvent(context, nobj);
1084 item = qobject_cast<QDeclarativeItem *>(nobj);
1085 if (!item)
1086 delete nobj;
1087 } else {
1088 delete context;
1089 }
1090 if (item) {
1091 QDeclarative_setParent_noEvent(item, q->contentItem());
1092 item->setParentItem(q->contentItem());
1093 item->setZValue(1);
1094 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
1095 itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
1096 footer = new FxListItem(item, q);
1097 }
1098 }
1099 if (footer) {
1100 if (visibleItems.count()) {
1101 qreal endPos = endPosition() + 1;
1102 if (lastVisibleIndex() == model->count()-1) {
1103 footer->setPosition(endPos);
1104 } else {
1105 qreal visiblePos = position() + q->height();
1106 if (endPos <= visiblePos || footer->position() < endPos)
1107 footer->setPosition(endPos);
1108 }
1109 } else {
1110 footer->setPosition(visiblePos);
1111 }
1112 }
1113}
1114
1115void QDeclarativeListViewPrivate::updateHeader()
1116{
1117 Q_Q(QDeclarativeListView);
1118 if (!header && headerComponent) {
1119 QDeclarativeItem *item = 0;
1120 QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
1121 QObject *nobj = headerComponent->create(context);
1122 if (nobj) {
1123 QDeclarative_setParent_noEvent(context, nobj);
1124 item = qobject_cast<QDeclarativeItem *>(nobj);
1125 if (!item)
1126 delete nobj;
1127 } else {
1128 delete context;
1129 }
1130 if (item) {
1131 QDeclarative_setParent_noEvent(item, q->contentItem());
1132 item->setParentItem(q->contentItem());
1133 item->setZValue(1);
1134 QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
1135 itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
1136 header = new FxListItem(item, q);
1137 }
1138 }
1139 if (header) {
1140 if (visibleItems.count()) {
1141 qreal startPos = startPosition();
1142 if (visibleIndex == 0) {
1143 header->setPosition(startPos - header->size());
1144 } else {
1145 if (position() <= startPos || header->position() > startPos - header->size())
1146 header->setPosition(startPos - header->size());
1147 }
1148 } else {
1149 if (itemCount == 0)
1150 visiblePos = header->size();
1151 header->setPosition(0);
1152 }
1153 }
1154}
1155
1156void QDeclarativeListViewPrivate::fixupPosition()
1157{
1158 if ((haveHighlightRange && highlightRange == QDeclarativeListView::StrictlyEnforceRange)
1159 || snapMode != QDeclarativeListView::NoSnap)
1160 moveReason = Other;
1161 if (orient == QDeclarativeListView::Vertical)
1162 fixupY();
1163 else
1164 fixupX();
1165}
1166
1167void QDeclarativeListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
1168{
1169 if ((orient == QDeclarativeListView::Horizontal && &data == &vData)
1170 || (orient == QDeclarativeListView::Vertical && &data == &hData))
1171 return;
1172
1173 correctFlick = false;
1174 int oldDuration = fixupDuration;
1175 fixupDuration = moveReason == Mouse ? fixupDuration : 0;
1176
1177 if (currentItem && haveHighlightRange && highlightRange == QDeclarativeListView::StrictlyEnforceRange) {
1178 updateHighlight();
1179 qreal pos = currentItem->itemPosition();
1180 qreal viewPos = position();
1181 if (viewPos < pos + currentItem->itemSize() - highlightRangeEnd)
1182 viewPos = pos + currentItem->itemSize() - highlightRangeEnd;
1183 if (viewPos > pos - highlightRangeStart)
1184 viewPos = pos - highlightRangeStart;
1185
1186 timeline.reset(data.move);
1187 if (viewPos != position()) {
1188 if (fixupDuration)
1189 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1190 else
1191 timeline.set(data.move, -viewPos);
1192 }
1193 vTime = timeline.time();
1194 } else if (snapMode != QDeclarativeListView::NoSnap) {
1195 FxListItem *topItem = snapItemAt(position()+highlightRangeStart);
1196 FxListItem *bottomItem = snapItemAt(position()+highlightRangeEnd);
1197 qreal pos;
1198 if (topItem) {
1199 if (topItem->index == 0 && header && position()+highlightRangeStart < header->position()+header->size()/2)
1200 pos = header->position() - highlightRangeStart;
1201 else
1202 pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent);
1203 } else if (bottomItem) {
1204 pos = qMax(qMin(bottomItem->position() - highlightRangeStart, -maxExtent), -minExtent);
1205 } else {
1206 QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent);
1207 fixupDuration = oldDuration;
1208 return;
1209 }
1210
1211 qreal dist = qAbs(data.move + pos);
1212 if (dist > 0) {
1213 timeline.reset(data.move);
1214 if (fixupDuration)
1215 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1216 else
1217 timeline.set(data.move, -pos);
1218 vTime = timeline.time();
1219 }
1220 } else {
1221 QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent);
1222 }
1223 fixupDuration = oldDuration;
1224}
1225
1226void QDeclarativeListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1227 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
1228{
1229 Q_Q(QDeclarativeListView);
1230
1231 moveReason = Mouse;
1232 if ((!haveHighlightRange || highlightRange != QDeclarativeListView::StrictlyEnforceRange) && snapMode == QDeclarativeListView::NoSnap) {
1233 correctFlick = true;
1234 QDeclarativeFlickablePrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1235 return;
1236 }
1237 qreal maxDistance = 0;
1238 // -ve velocity means list is moving up/left
1239 if (velocity > 0) {
1240 if (data.move.value() < minExtent) {
1241 if (snapMode == QDeclarativeListView::SnapOneItem) {
1242 if (FxListItem *item = firstVisibleItem())
1243 maxDistance = qAbs(item->position() + data.move.value());
1244 } else {
1245 maxDistance = qAbs(minExtent - data.move.value());
1246 }
1247 }
1248 if (snapMode == QDeclarativeListView::NoSnap && highlightRange != QDeclarativeListView::StrictlyEnforceRange)
1249 data.flickTarget = minExtent;
1250 } else {
1251 if (data.move.value() > maxExtent) {
1252 if (snapMode == QDeclarativeListView::SnapOneItem) {
1253 if (FxListItem *item = nextVisibleItem())
1254 maxDistance = qAbs(item->position() + data.move.value());
1255 } else {
1256 maxDistance = qAbs(maxExtent - data.move.value());
1257 }
1258 }
1259 if (snapMode == QDeclarativeListView::NoSnap && highlightRange != QDeclarativeListView::StrictlyEnforceRange)
1260 data.flickTarget = maxExtent;
1261 }
1262 bool overShoot = boundsBehavior == QDeclarativeFlickable::DragAndOvershootBounds;
1263 if (maxDistance > 0 || overShoot) {
1264 // These modes require the list to stop exactly on an item boundary.
1265 // The initial flick will estimate the boundary to stop on.
1266 // Since list items can have variable sizes, the boundary will be
1267 // reevaluated and adjusted as we approach the boundary.
1268 qreal v = velocity;
1269 if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1270 if (v < 0)
1271 v = -maxVelocity;
1272 else
1273 v = maxVelocity;
1274 }
1275 if (!flickingHorizontally && !flickingVertically) {
1276 // the initial flick - estimate boundary
1277 qreal accel = deceleration;
1278 qreal v2 = v * v;
1279 overshootDist = 0.0;
1280 // + averageSize/4 to encourage moving at least one item in the flick direction
1281 qreal dist = v2 / (accel * 2.0) + averageSize/4;
1282 if (maxDistance > 0)
1283 dist = qMin(dist, maxDistance);
1284 if (v > 0)
1285 dist = -dist;
1286 if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QDeclarativeListView::SnapOneItem) {
1287 data.flickTarget = -snapPosAt(-(data.move.value() - highlightRangeStart) + dist) + highlightRangeStart;
1288 if (overShoot) {
1289 if (data.flickTarget >= minExtent) {
1290 overshootDist = overShootDistance(v, vSize);
1291 data.flickTarget += overshootDist;
1292 } else if (data.flickTarget <= maxExtent) {
1293 overshootDist = overShootDistance(v, vSize);
1294 data.flickTarget -= overshootDist;
1295 }
1296 }
1297 qreal adjDist = -data.flickTarget + data.move.value();
1298 if (qAbs(adjDist) > qAbs(dist)) {
1299 // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1300 qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1301 if (adjv2 > v2) {
1302 v2 = adjv2;
1303 v = qSqrt(v2);
1304 if (dist > 0)
1305 v = -v;
1306 }
1307 }
1308 dist = adjDist;
1309 accel = v2 / (2.0f * qAbs(dist));
1310 } else if (overShoot) {
1311 data.flickTarget = data.move.value() - dist;
1312 if (data.flickTarget >= minExtent) {
1313 overshootDist = overShootDistance(v, vSize);
1314 data.flickTarget += overshootDist;
1315 } else if (data.flickTarget <= maxExtent) {
1316 overshootDist = overShootDistance(v, vSize);
1317 data.flickTarget -= overshootDist;
1318 }
1319 }
1320 timeline.reset(data.move);
1321 timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1322 timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1323 if (!flickingHorizontally && q->xflick()) {
1324 flickingHorizontally = true;
1325 emit q->flickingChanged();
1326 emit q->flickingHorizontallyChanged();
1327 emit q->flickStarted();
1328 }
1329 if (!flickingVertically && q->yflick()) {
1330 flickingVertically = true;
1331 emit q->flickingChanged();
1332 emit q->flickingVerticallyChanged();
1333 emit q->flickStarted();
1334 }
1335 correctFlick = true;
1336 } else {
1337 // reevaluate the target boundary.
1338 qreal newtarget = data.flickTarget;
1339 if (snapMode != QDeclarativeListView::NoSnap || highlightRange == QDeclarativeListView::StrictlyEnforceRange)
1340 newtarget = -snapPosAt(-(data.flickTarget - highlightRangeStart)) + highlightRangeStart;
1341 if (velocity < 0 && newtarget <= maxExtent)
1342 newtarget = maxExtent - overshootDist;
1343 else if (velocity > 0 && newtarget >= minExtent)
1344 newtarget = minExtent + overshootDist;
1345 if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
1346 if (qAbs(velocity) < MinimumFlickVelocity)
1347 correctFlick = false;
1348 return;
1349 }
1350 data.flickTarget = newtarget;
1351 qreal dist = -newtarget + data.move.value();
1352 if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
1353 correctFlick = false;
1354 timeline.reset(data.move);
1355 fixup(data, minExtent, maxExtent);
1356 return;
1357 }
1358 timeline.reset(data.move);
1359 timeline.accelDistance(data.move, v, -dist);
1360 timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1361 }
1362 } else {
1363 correctFlick = false;
1364 timeline.reset(data.move);
1365 fixup(data, minExtent, maxExtent);
1366 }
1367}
1368
1369//----------------------------------------------------------------------------
1370
1371/*!
1372 \qmlclass ListView QDeclarativeListView
1373 \ingroup qml-view-elements
1374 \since 4.7
1375 \inherits Flickable
1376 \brief The ListView item provides a list view of items provided by a model.
1377
1378 A ListView displays data from models created from built-in QML elements like ListModel
1379 and XmlListModel, or custom model classes defined in C++ that inherit from
1380 QAbstractListModel.
1381
1382 A ListView has a \l model, which defines the data to be displayed, and
1383 a \l delegate, which defines how the data should be displayed. Items in a
1384 ListView are laid out horizontally or vertically. List views are inherently
1385 flickable because ListView inherits from \l Flickable.
1386
1387 \section1 Example Usage
1388
1389 The following example shows the definition of a simple list model defined
1390 in a file called \c ContactModel.qml:
1391
1392 \snippet doc/src/snippets/declarative/listview/ContactModel.qml 0
1393
1394 Another component can display this model data in a ListView, like this:
1395
1396 \snippet doc/src/snippets/declarative/listview/listview.qml import
1397 \codeline
1398 \snippet doc/src/snippets/declarative/listview/listview.qml classdocs simple
1399
1400 \image listview-simple.png
1401
1402 Here, the ListView creates a \c ContactModel component for its model, and a \l Text element
1403 for its delegate. The view will create a new \l Text component for each item in the model. Notice
1404 the delegate is able to access the model's \c name and \c number data directly.
1405
1406 An improved list view is shown below. The delegate is visually improved and is moved
1407 into a separate \c contactDelegate component.
1408
1409 \snippet doc/src/snippets/declarative/listview/listview.qml classdocs advanced
1410 \image listview-highlight.png
1411
1412 The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
1413 and \c focus is set to \c true to enable keyboard navigation for the list view.
1414 The list view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
1415
1416 Delegates are instantiated as needed and may be destroyed at any time.
1417 State should \e never be stored in a delegate.
1418
1419 ListView attaches a number of properties to the root item of the delegate, for example
1420 \c {ListView.isCurrentItem}. In the following example, the root delegate item can access
1421 this attached property directly as \c ListView.isCurrentItem, while the child
1422 \c contactInfo object must refer to this property as \c wrapper.ListView.isCurrentItem.
1423
1424 \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem
1425
1426 \note Views do not enable \e clip automatically. If the view
1427 is not clipped by another item or the screen, it will be necessary
1428 to set \e {clip: true} in order to have the out of view items clipped
1429 nicely.
1430
1431 \sa {QML Data Models}, GridView, {declarative/modelviews/listview}{ListView examples}
1432*/
1433
1434QDeclarativeListView::QDeclarativeListView(QDeclarativeItem *parent)
1435 : QDeclarativeFlickable(*(new QDeclarativeListViewPrivate), parent)
1436{
1437 Q_D(QDeclarativeListView);
1438 d->init();
1439}
1440
1441QDeclarativeListView::~QDeclarativeListView()
1442{
1443 Q_D(QDeclarativeListView);
1444 d->clear();
1445 if (d->ownModel)
1446 delete d->model;
1447 delete d->header;
1448 delete d->footer;
1449}
1450
1451/*!
1452 \qmlattachedproperty bool ListView::isCurrentItem
1453 This attached property is true if this delegate is the current item; otherwise false.
1454
1455 It is attached to each instance of the delegate.
1456
1457 This property may be used to adjust the appearance of the current item, for example:
1458
1459 \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem
1460*/
1461
1462/*!
1463 \qmlattachedproperty ListView ListView::view
1464 This attached property holds the view that manages this delegate instance.
1465
1466 It is attached to each instance of the delegate.
1467*/
1468
1469/*!
1470 \qmlattachedproperty string ListView::previousSection
1471 This attached property holds the section of the previous element.
1472
1473 It is attached to each instance of the delegate.
1474
1475 The section is evaluated using the \l {ListView::section.property}{section} properties.
1476*/
1477
1478/*!
1479 \qmlattachedproperty string ListView::nextSection
1480 This attached property holds the section of the next element.
1481
1482 It is attached to each instance of the delegate.
1483
1484 The section is evaluated using the \l {ListView::section.property}{section} properties.
1485*/
1486
1487/*!
1488 \qmlattachedproperty string ListView::section
1489 This attached property holds the section of this element.
1490
1491 It is attached to each instance of the delegate.
1492
1493 The section is evaluated using the \l {ListView::section.property}{section} properties.
1494*/
1495
1496/*!
1497 \qmlattachedproperty bool ListView::delayRemove
1498 This attached property holds whether the delegate may be destroyed.
1499
1500 It is attached to each instance of the delegate.
1501
1502 It is sometimes necessary to delay the destruction of an item
1503 until an animation completes.
1504
1505 The example delegate below ensures that the animation completes before
1506 the item is removed from the list.
1507
1508 \snippet doc/src/snippets/declarative/listview/listview.qml delayRemove
1509*/
1510
1511/*!
1512 \qmlattachedsignal ListView::onAdd()
1513 This attached handler is called immediately after an item is added to the view.
1514*/
1515
1516/*!
1517 \qmlattachedsignal ListView::onRemove()
1518 This attached handler is called immediately before an item is removed from the view.
1519*/
1520
1521/*!
1522 \qmlproperty model ListView::model
1523 This property holds the model providing data for the list.
1524
1525 The model provides the set of data that is used to create the items
1526 in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
1527 or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
1528 used, it must be a subclass of \l QAbstractItemModel or a simple list.
1529
1530 \sa {qmlmodels}{Data Models}
1531*/
1532QVariant QDeclarativeListView::model() const
1533{
1534 Q_D(const QDeclarativeListView);
1535 return d->modelVariant;
1536}
1537
1538void QDeclarativeListView::setModel(const QVariant &model)
1539{
1540 Q_D(QDeclarativeListView);
1541 if (d->modelVariant == model)
1542 return;
1543 if (d->model) {
1544 disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
1545 disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
1546 disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
1547 disconnect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int)));
1548 disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
1549 disconnect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*)));
1550 disconnect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*)));
1551 }
1552 d->clear();
1553 QDeclarativeVisualModel *oldModel = d->model;
1554 d->model = 0;
1555 d->setPosition(0);
1556 d->modelVariant = model;
1557 QObject *object = qvariant_cast<QObject*>(model);
1558 QDeclarativeVisualModel *vim = 0;
1559 if (object && (vim = qobject_cast<QDeclarativeVisualModel *>(object))) {
1560 if (d->ownModel) {
1561 delete oldModel;
1562 d->ownModel = false;
1563 }
1564 d->model = vim;
1565 } else {
1566 if (!d->ownModel) {
1567 d->model = new QDeclarativeVisualDataModel(qmlContext(this), this);
1568 d->ownModel = true;
1569 } else {
1570 d->model = oldModel;
1571 }
1572 if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model))
1573 dataModel->setModel(model);
1574 }
1575 if (d->model) {
1576 d->bufferMode = QDeclarativeListViewPrivate::BufferBefore | QDeclarativeListViewPrivate::BufferAfter;
1577 if (isComponentComplete()) {
1578 updateSections();
1579 refill();
1580 if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) {
1581 setCurrentIndex(0);
1582 } else {
1583 d->moveReason = QDeclarativeListViewPrivate::SetIndex;
1584 d->updateCurrent(d->currentIndex);
1585 if (d->highlight && d->currentItem) {
1586 if (d->autoHighlight)
1587 d->highlight->setPosition(d->currentItem->position());
1588 d->updateTrackedItem();
1589 }
1590 }
1591 d->updateViewport();
1592 }
1593 connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
1594 connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
1595 connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
1596 connect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int)));
1597 connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
1598 connect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*)));
1599 connect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*)));
1600 emit countChanged();
1601 }
1602 emit modelChanged();
1603}
1604
1605/*!
1606 \qmlproperty Component ListView::delegate
1607
1608 The delegate provides a template defining each item instantiated by the view.
1609 The index is exposed as an accessible \c index property. Properties of the
1610 model are also available depending upon the type of \l {qmlmodels}{Data Model}.
1611
1612 The number of elements in the delegate has a direct effect on the
1613 flicking performance of the view. If at all possible, place functionality
1614 that is not needed for the normal display of the delegate in a \l Loader which
1615 can load additional elements when needed.
1616
1617 The ListView will lay out the items based on the size of the root item
1618 in the delegate.
1619
1620 It is recommended that the delagate's size be a whole number to avoid sub-pixel
1621 alignment of items.
1622
1623 \note Delegates are instantiated as needed and may be destroyed at any time.
1624 State should \e never be stored in a delegate.
1625*/
1626QDeclarativeComponent *QDeclarativeListView::delegate() const
1627{
1628 Q_D(const QDeclarativeListView);
1629 if (d->model) {
1630 if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model))
1631 return dataModel->delegate();
1632 }
1633
1634 return 0;
1635}
1636
1637void QDeclarativeListView::setDelegate(QDeclarativeComponent *delegate)
1638{
1639 Q_D(QDeclarativeListView);
1640 if (delegate == this->delegate())
1641 return;
1642 if (!d->ownModel) {
1643 d->model = new QDeclarativeVisualDataModel(qmlContext(this));
1644 d->ownModel = true;
1645 }
1646 if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model)) {
1647 dataModel->setDelegate(delegate);
1648 if (isComponentComplete()) {
1649 for (int i = 0; i < d->visibleItems.count(); ++i)
1650 d->releaseItem(d->visibleItems.at(i));
1651 d->visibleItems.clear();
1652 d->releaseItem(d->currentItem);
1653 d->currentItem = 0;
1654 updateSections();
1655 refill();
1656 d->moveReason = QDeclarativeListViewPrivate::SetIndex;
1657 d->updateCurrent(d->currentIndex);
1658 if (d->highlight && d->currentItem) {
1659 if (d->autoHighlight)
1660 d->highlight->setPosition(d->currentItem->position());
1661 d->updateTrackedItem();
1662 }
1663 d->updateViewport();
1664 }
1665 }
1666 emit delegateChanged();
1667}
1668
1669/*!
1670 \qmlproperty int ListView::currentIndex
1671 \qmlproperty Item ListView::currentItem
1672
1673 The \c currentIndex property holds the index of the current item, and
1674 \c currentItem holds the current item. Setting the currentIndex to -1
1675 will clear the highlight and set currentItem to null.
1676
1677 If highlightFollowsCurrentItem is \c true, setting either of these
1678 properties will smoothly scroll the ListView so that the current
1679 item becomes visible.
1680
1681 Note that the position of the current item
1682 may only be approximate until it becomes visible in the view.
1683*/
1684int QDeclarativeListView::currentIndex() const
1685{
1686 Q_D(const QDeclarativeListView);
1687 return d->currentIndex;
1688}
1689
1690void QDeclarativeListView::setCurrentIndex(int index)
1691{
1692 Q_D(QDeclarativeListView);
1693 if (d->requestedIndex >= 0) // currently creating item
1694 return;
1695 d->currentIndexCleared = (index == -1);
1696 if (index == d->currentIndex)
1697 return;
1698 if (isComponentComplete() && d->isValid()) {
1699 d->moveReason = QDeclarativeListViewPrivate::SetIndex;
1700 d->updateCurrent(index);
1701 } else if (d->currentIndex != index) {
1702 d->currentIndex = index;
1703 emit currentIndexChanged();
1704 }
1705}
1706
1707QDeclarativeItem *QDeclarativeListView::currentItem()
1708{
1709 Q_D(QDeclarativeListView);
1710 if (!d->currentItem)
1711 return 0;
1712 return d->currentItem->item;
1713}
1714
1715/*!
1716 \qmlproperty Item ListView::highlightItem
1717
1718 This holds the highlight item created from the \l highlight component.
1719
1720 The \c highlightItem is managed by the view unless
1721 \l highlightFollowsCurrentItem is set to false.
1722
1723 \sa highlight, highlightFollowsCurrentItem
1724*/
1725QDeclarativeItem *QDeclarativeListView::highlightItem()
1726{
1727 Q_D(QDeclarativeListView);
1728 if (!d->highlight)
1729 return 0;
1730 return d->highlight->item;
1731}
1732
1733/*!
1734 \qmlproperty int ListView::count
1735 This property holds the number of items in the view.
1736*/
1737int QDeclarativeListView::count() const
1738{
1739 Q_D(const QDeclarativeListView);
1740 if (d->model)
1741 return d->model->count();
1742 return 0;
1743}
1744
1745/*!
1746 \qmlproperty Component ListView::highlight
1747 This property holds the component to use as the highlight.
1748
1749 An instance of the highlight component is created for each list.
1750 The geometry of the resulting component instance is managed by the list
1751 so as to stay with the current item, unless the highlightFollowsCurrentItem
1752 property is false.
1753
1754 \sa highlightItem, highlightFollowsCurrentItem, {declarative/modelviews/listview}{ListView examples}
1755*/
1756QDeclarativeComponent *QDeclarativeListView::highlight() const
1757{
1758 Q_D(const QDeclarativeListView);
1759 return d->highlightComponent;
1760}
1761
1762void QDeclarativeListView::setHighlight(QDeclarativeComponent *highlight)
1763{
1764 Q_D(QDeclarativeListView);
1765 if (highlight != d->highlightComponent) {
1766 d->highlightComponent = highlight;
1767 d->createHighlight();
1768 if (d->currentItem)
1769 d->updateHighlight();
1770 emit highlightChanged();
1771 }
1772}
1773
1774/*!
1775 \qmlproperty bool ListView::highlightFollowsCurrentItem
1776 This property holds whether the highlight is managed by the view.
1777
1778 If this property is true (the default value), the highlight is moved smoothly
1779 to follow the current item. Otherwise, the
1780 highlight is not moved by the view, and any movement must be implemented
1781 by the highlight.
1782
1783 Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1784
1785 \snippet doc/src/snippets/declarative/listview/listview.qml highlightFollowsCurrentItem
1786
1787 Note that the highlight animation also affects the way that the view
1788 is scrolled. This is because the view moves to maintain the
1789 highlight within the preferred highlight range (or visible viewport).
1790
1791 \sa highlight, highlightMoveSpeed
1792*/
1793bool QDeclarativeListView::highlightFollowsCurrentItem() const
1794{
1795 Q_D(const QDeclarativeListView);
1796 return d->autoHighlight;
1797}
1798
1799void QDeclarativeListView::setHighlightFollowsCurrentItem(bool autoHighlight)
1800{
1801 Q_D(QDeclarativeListView);
1802 if (d->autoHighlight != autoHighlight) {
1803 d->autoHighlight = autoHighlight;
1804 if (autoHighlight) {
1805 d->updateHighlight();
1806 } else {
1807 if (d->highlightPosAnimator)
1808 d->highlightPosAnimator->stop();
1809 if (d->highlightSizeAnimator)
1810 d->highlightSizeAnimator->stop();
1811 }
1812 emit highlightFollowsCurrentItemChanged();
1813 }
1814}
1815
1816//###Possibly rename these properties, since they are very useful even without a highlight?
1817/*!
1818 \qmlproperty real ListView::preferredHighlightBegin
1819 \qmlproperty real ListView::preferredHighlightEnd
1820 \qmlproperty enumeration ListView::highlightRangeMode
1821
1822 These properties define the preferred range of the highlight (for the current item)
1823 within the view. The \c preferredHighlightBegin value must be less than the
1824 \c preferredHighlightEnd value.
1825
1826 These properties affect the position of the current item when the list is scrolled.
1827 For example, if the currently selected item should stay in the middle of the
1828 list when the view is scrolled, set the \c preferredHighlightBegin and
1829 \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
1830 item would be. If the \c currentItem is changed programmatically, the list will
1831 automatically scroll so that the current item is in the middle of the view.
1832 Furthermore, the behavior of the current item index will occur whether or not a
1833 highlight exists.
1834
1835 Valid values for \c highlightRangeMode are:
1836
1837 \list
1838 \o ListView.ApplyRange - the view attempts to maintain the highlight within the range.
1839 However, the highlight can move outside of the range at the ends of the list or due
1840 to mouse interaction.
1841 \o ListView.StrictlyEnforceRange - the highlight never moves outside of the range.
1842 The current item changes if a keyboard or mouse action would cause the highlight to move
1843 outside of the range.
1844 \o ListView.NoHighlightRange - this is the default value.
1845 \endlist
1846*/
1847qreal QDeclarativeListView::preferredHighlightBegin() const
1848{
1849 Q_D(const QDeclarativeListView);
1850 return d->highlightRangeStart;
1851}
1852
1853void QDeclarativeListView::setPreferredHighlightBegin(qreal start)
1854{
1855 Q_D(QDeclarativeListView);
1856 if (d->highlightRangeStart == start)
1857 return;
1858 d->highlightRangeStart = start;
1859 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
1860 emit preferredHighlightBeginChanged();
1861}
1862
1863qreal QDeclarativeListView::preferredHighlightEnd() const
1864{
1865 Q_D(const QDeclarativeListView);
1866 return d->highlightRangeEnd;
1867}
1868
1869void QDeclarativeListView::setPreferredHighlightEnd(qreal end)
1870{
1871 Q_D(QDeclarativeListView);
1872 if (d->highlightRangeEnd == end)
1873 return;
1874 d->highlightRangeEnd = end;
1875 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
1876 emit preferredHighlightEndChanged();
1877}
1878
1879QDeclarativeListView::HighlightRangeMode QDeclarativeListView::highlightRangeMode() const
1880{
1881 Q_D(const QDeclarativeListView);
1882 return d->highlightRange;
1883}
1884
1885void QDeclarativeListView::setHighlightRangeMode(HighlightRangeMode mode)
1886{
1887 Q_D(QDeclarativeListView);
1888 if (d->highlightRange == mode)
1889 return;
1890 d->highlightRange = mode;
1891 d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
1892 emit highlightRangeModeChanged();
1893}
1894
1895/*!
1896 \qmlproperty real ListView::spacing
1897
1898 This property holds the spacing between items.
1899
1900 The default value is 0.
1901*/
1902qreal QDeclarativeListView::spacing() const
1903{
1904 Q_D(const QDeclarativeListView);
1905 return d->spacing;
1906}
1907
1908void QDeclarativeListView::setSpacing(qreal spacing)
1909{
1910 Q_D(QDeclarativeListView);
1911 if (spacing != d->spacing) {
1912 d->spacing = spacing;
1913 d->layout();
1914 emit spacingChanged();
1915 }
1916}
1917
1918/*!
1919 \qmlproperty enumeration ListView::orientation
1920 This property holds the orientation of the list.
1921
1922 Possible values:
1923
1924 \list
1925 \o ListView.Horizontal - Items are laid out horizontally
1926 \o ListView.Vertical (default) - Items are laid out vertically
1927 \endlist
1928
1929 \table
1930 \row
1931 \o Horizontal orientation:
1932 \image ListViewHorizontal.png
1933
1934 \row
1935 \o Vertical orientation:
1936 \image listview-highlight.png
1937 \endtable
1938*/
1939QDeclarativeListView::Orientation QDeclarativeListView::orientation() const
1940{
1941 Q_D(const QDeclarativeListView);
1942 return d->orient;
1943}
1944
1945void QDeclarativeListView::setOrientation(QDeclarativeListView::Orientation orientation)
1946{
1947 Q_D(QDeclarativeListView);
1948 if (d->orient != orientation) {
1949 d->orient = orientation;
1950 if (d->orient == QDeclarativeListView::Vertical) {
1951 setContentWidth(-1);
1952 setFlickableDirection(VerticalFlick);
1953 } else {
1954 setContentHeight(-1);
1955 setFlickableDirection(HorizontalFlick);
1956 }
1957 d->clear();
1958 d->setPosition(0);
1959 refill();
1960 emit orientationChanged();
1961 d->updateCurrent(d->currentIndex);
1962 }
1963}
1964
1965/*!
1966 \qmlproperty bool ListView::keyNavigationWraps
1967 This property holds whether the list wraps key navigation.
1968
1969 If this is true, key navigation that would move the current item selection
1970 past the end of the list instead wraps around and moves the selection to
1971 the start of the list, and vice-versa.
1972
1973 By default, key navigation is not wrapped.
1974*/
1975bool QDeclarativeListView::isWrapEnabled() const
1976{
1977 Q_D(const QDeclarativeListView);
1978 return d->wrap;
1979}
1980
1981void QDeclarativeListView::setWrapEnabled(bool wrap)
1982{
1983 Q_D(QDeclarativeListView);
1984 if (d->wrap == wrap)
1985 return;
1986 d->wrap = wrap;
1987 emit keyNavigationWrapsChanged();
1988}
1989
1990/*!
1991 \qmlproperty int ListView::cacheBuffer
1992 This property determines whether delegates are retained outside the
1993 visible area of the view.
1994
1995 If this value is non-zero, the view keeps as many delegates
1996 instantiated as it can fit within the buffer specified. For example,
1997 if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
1998 set to 40, then up to 2 delegates above and 2 delegates below the visible
1999 area may be retained.
2000
2001 Note that cacheBuffer is not a pixel buffer - it only maintains additional
2002 instantiated delegates.
2003
2004 Setting this value can improve the smoothness of scrolling behavior at the expense
2005 of additional memory usage. It is not a substitute for creating efficient
2006 delegates; the fewer elements in a delegate, the faster a view can be
2007 scrolled.
2008*/
2009int QDeclarativeListView::cacheBuffer() const
2010{
2011 Q_D(const QDeclarativeListView);
2012 return d->buffer;
2013}
2014
2015void QDeclarativeListView::setCacheBuffer(int b)
2016{
2017 Q_D(QDeclarativeListView);
2018 if (d->buffer != b) {
2019 d->buffer = b;
2020 if (isComponentComplete()) {
2021 d->bufferMode = QDeclarativeListViewPrivate::BufferBefore | QDeclarativeListViewPrivate::BufferAfter;
2022 refill();
2023 }
2024 emit cacheBufferChanged();
2025 }
2026}
2027
2028/*!
2029 \qmlproperty string ListView::section.property
2030 \qmlproperty enumeration ListView::section.criteria
2031 \qmlproperty Component ListView::section.delegate
2032
2033 These properties hold the expression to be evaluated for the \l section attached property.
2034
2035 The \l section attached property enables a ListView to be visually
2036 separated into different parts. These properties determine how sections
2037 are created.
2038
2039 \c section.property holds the name of the property that is the basis
2040 of each section.
2041
2042 \c section.criteria holds the criteria for forming each section based on
2043 \c section.property. This value can be one of:
2044
2045 \list
2046 \o ViewSection.FullString (default) - sections are created based on the
2047 \c section.property value.
2048 \o ViewSection.FirstCharacter - sections are created based on the first
2049 character of the \c section.property value (for example, 'A', 'B', 'C'
2050 sections, etc. for an address book)
2051 \endlist
2052
2053 \c section.delegate holds the delegate component for each section.
2054
2055 Each item in the list has attached properties named \c ListView.section,
2056 \c ListView.previousSection and \c ListView.nextSection. These may be
2057 used to place a section header for related items.
2058
2059 For example, here is a ListView that displays a list of animals, separated
2060 into sections. Each item in the ListView is placed in a different section
2061 depending on the "size" property of the model item. The \c sectionHeading
2062 delegate component provides the light blue bar that marks the beginning of
2063 each section.
2064
2065 \snippet examples/declarative/modelviews/listview/sections.qml 0
2066
2067 \image qml-listview-sections-example.png
2068
2069 \sa {declarative/modelviews/listview}{ListView examples}
2070*/
2071QDeclarativeViewSection *QDeclarativeListView::sectionCriteria()
2072{
2073 Q_D(QDeclarativeListView);
2074 if (!d->sectionCriteria) {
2075 d->sectionCriteria = new QDeclarativeViewSection(this);
2076 connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections()));
2077 }
2078 return d->sectionCriteria;
2079}
2080
2081/*!
2082 \qmlproperty string ListView::currentSection
2083 This property holds the section that is currently at the beginning of the view.
2084*/
2085QString QDeclarativeListView::currentSection() const
2086{
2087 Q_D(const QDeclarativeListView);
2088 return d->currentSection;
2089}
2090
2091/*!
2092 \qmlproperty real ListView::highlightMoveSpeed
2093 \qmlproperty int ListView::highlightMoveDuration
2094 \qmlproperty real ListView::highlightResizeSpeed
2095 \qmlproperty int ListView::highlightResizeDuration
2096
2097 These properties hold the move and resize animation speed of the highlight delegate.
2098
2099 \l highlightFollowsCurrentItem must be true for these properties
2100 to have effect.
2101
2102 The default value for the speed properties is 400 pixels/second.
2103 The default value for the duration properties is -1, i.e. the
2104 highlight will take as much time as necessary to move at the set speed.
2105
2106 These properties have the same characteristics as a SmoothedAnimation.
2107
2108 \sa highlightFollowsCurrentItem
2109*/
2110qreal QDeclarativeListView::highlightMoveSpeed() const
2111{
2112 Q_D(const QDeclarativeListView);\
2113 return d->highlightMoveSpeed;
2114}
2115
2116void QDeclarativeListView::setHighlightMoveSpeed(qreal speed)
2117{
2118 Q_D(QDeclarativeListView);\
2119 if (d->highlightMoveSpeed != speed) {
2120 d->highlightMoveSpeed = speed;
2121 if (d->highlightPosAnimator)
2122 d->highlightPosAnimator->velocity = d->highlightMoveSpeed;
2123 emit highlightMoveSpeedChanged();
2124 }
2125}
2126
2127int QDeclarativeListView::highlightMoveDuration() const
2128{
2129 Q_D(const QDeclarativeListView);
2130 return d->highlightMoveDuration;
2131}
2132
2133void QDeclarativeListView::setHighlightMoveDuration(int duration)
2134{
2135 Q_D(QDeclarativeListView);\
2136 if (d->highlightMoveDuration != duration) {
2137 d->highlightMoveDuration = duration;
2138 if (d->highlightPosAnimator)
2139 d->highlightPosAnimator->userDuration = d->highlightMoveDuration;
2140 emit highlightMoveDurationChanged();
2141 }
2142}
2143
2144qreal QDeclarativeListView::highlightResizeSpeed() const
2145{
2146 Q_D(const QDeclarativeListView);\
2147 return d->highlightResizeSpeed;
2148}
2149
2150void QDeclarativeListView::setHighlightResizeSpeed(qreal speed)
2151{
2152 Q_D(QDeclarativeListView);\
2153 if (d->highlightResizeSpeed != speed) {
2154 d->highlightResizeSpeed = speed;
2155 if (d->highlightSizeAnimator)
2156 d->highlightSizeAnimator->velocity = d->highlightResizeSpeed;
2157 emit highlightResizeSpeedChanged();
2158 }
2159}
2160
2161int QDeclarativeListView::highlightResizeDuration() const
2162{
2163 Q_D(const QDeclarativeListView);
2164 return d->highlightResizeDuration;
2165}
2166
2167void QDeclarativeListView::setHighlightResizeDuration(int duration)
2168{
2169 Q_D(QDeclarativeListView);\
2170 if (d->highlightResizeDuration != duration) {
2171 d->highlightResizeDuration = duration;
2172 if (d->highlightSizeAnimator)
2173 d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
2174 emit highlightResizeDurationChanged();
2175 }
2176}
2177
2178/*!
2179 \qmlproperty enumeration ListView::snapMode
2180
2181 This property determines how the view scrolling will settle following a drag or flick.
2182 The possible values are:
2183
2184 \list
2185 \o ListView.NoSnap (default) - the view stops anywhere within the visible area.
2186 \o ListView.SnapToItem - the view settles with an item aligned with the start of
2187 the view.
2188 \o ListView.SnapOneItem - the view settles no more than one item away from the first
2189 visible item at the time the mouse button is released. This mode is particularly
2190 useful for moving one page at a time.
2191 \endlist
2192
2193 \c snapMode does not affect the \l currentIndex. To update the
2194 \l currentIndex as the list is moved, set \l highlightRangeMode
2195 to \c ListView.StrictlyEnforceRange.
2196
2197 \sa highlightRangeMode
2198*/
2199QDeclarativeListView::SnapMode QDeclarativeListView::snapMode() const
2200{
2201 Q_D(const QDeclarativeListView);
2202 return d->snapMode;
2203}
2204
2205void QDeclarativeListView::setSnapMode(SnapMode mode)
2206{
2207 Q_D(QDeclarativeListView);
2208 if (d->snapMode != mode) {
2209 d->snapMode = mode;
2210 emit snapModeChanged();
2211 }
2212}
2213
2214/*!
2215 \qmlproperty Component ListView::footer
2216 This property holds the component to use as the footer.
2217
2218 An instance of the footer component is created for each view. The
2219 footer is positioned at the end of the view, after any items.
2220
2221 \sa header
2222*/
2223QDeclarativeComponent *QDeclarativeListView::footer() const
2224{
2225 Q_D(const QDeclarativeListView);
2226 return d->footerComponent;
2227}
2228
2229void QDeclarativeListView::setFooter(QDeclarativeComponent *footer)
2230{
2231 Q_D(QDeclarativeListView);
2232 if (d->footerComponent != footer) {
2233 if (d->footer) {
2234 delete d->footer;
2235 d->footer = 0;
2236 }
2237 d->footerComponent = footer;
2238 d->minExtentDirty = true;
2239 d->maxExtentDirty = true;
2240 if (isComponentComplete()) {
2241 d->updateFooter();
2242 d->updateViewport();
2243 }
2244 emit footerChanged();
2245 }
2246}
2247
2248/*!
2249 \qmlproperty Component ListView::header
2250 This property holds the component to use as the header.
2251
2252 An instance of the header component is created for each view. The
2253 header is positioned at the beginning of the view, before any items.
2254
2255 \sa footer
2256*/
2257QDeclarativeComponent *QDeclarativeListView::header() const
2258{
2259 Q_D(const QDeclarativeListView);
2260 return d->headerComponent;
2261}
2262
2263void QDeclarativeListView::setHeader(QDeclarativeComponent *header)
2264{
2265 Q_D(QDeclarativeListView);
2266 if (d->headerComponent != header) {
2267 if (d->header) {
2268 delete d->header;
2269 d->header = 0;
2270 }
2271 d->headerComponent = header;
2272 d->minExtentDirty = true;
2273 d->maxExtentDirty = true;
2274 if (isComponentComplete()) {
2275 d->updateHeader();
2276 d->updateFooter();
2277 d->updateViewport();
2278 }
2279 emit headerChanged();
2280 }
2281}
2282
2283void QDeclarativeListView::setContentX(qreal pos)
2284{
2285 Q_D(QDeclarativeListView);
2286 // Positioning the view manually should override any current movement state
2287 d->moveReason = QDeclarativeListViewPrivate::Other;
2288 QDeclarativeFlickable::setContentX(pos);
2289}
2290
2291void QDeclarativeListView::setContentY(qreal pos)
2292{
2293 Q_D(QDeclarativeListView);
2294 // Positioning the view manually should override any current movement state
2295 d->moveReason = QDeclarativeListViewPrivate::Other;
2296 QDeclarativeFlickable::setContentY(pos);
2297}
2298
2299bool QDeclarativeListView::event(QEvent *event)
2300{
2301 Q_D(QDeclarativeListView);
2302 if (event->type() == QEvent::User) {
2303 d->layout();
2304 return true;
2305 }
2306
2307 return QDeclarativeFlickable::event(event);
2308}
2309
2310void QDeclarativeListView::viewportMoved()
2311{
2312 Q_D(QDeclarativeListView);
2313 QDeclarativeFlickable::viewportMoved();
2314 if (!d->itemCount)
2315 return;
2316 // Recursion can occur due to refill changing the content size.
2317 if (d->inViewportMoved)
2318 return;
2319 d->inViewportMoved = true;
2320 d->lazyRelease = true;
2321 refill();
2322 if (d->flickingHorizontally || d->flickingVertically || d->movingHorizontally || d->movingVertically)
2323 d->moveReason = QDeclarativeListViewPrivate::Mouse;
2324 if (d->moveReason != QDeclarativeListViewPrivate::SetIndex) {
2325 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2326 // reposition highlight
2327 qreal pos = d->highlight->position();
2328 qreal viewPos = d->position();
2329 if (pos > viewPos + d->highlightRangeEnd - d->highlight->size())
2330 pos = viewPos + d->highlightRangeEnd - d->highlight->size();
2331 if (pos < viewPos + d->highlightRangeStart)
2332 pos = viewPos + d->highlightRangeStart;
2333 d->highlightPosAnimator->stop();
2334 d->highlight->setPosition(qRound(pos));
2335
2336 // update current index
2337 if (FxListItem *snapItem = d->snapItemAt(d->highlight->position())) {
2338 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
2339 d->updateCurrent(snapItem->index);
2340 }
2341 }
2342 }
2343
2344 if ((d->flickingHorizontally || d->flickingVertically) && d->correctFlick && !d->inFlickCorrection) {
2345 d->inFlickCorrection = true;
2346 // Near an end and it seems that the extent has changed?
2347 // Recalculate the flick so that we don't end up in an odd position.
2348 if (yflick()) {
2349 if (d->vData.velocity > 0) {
2350 const qreal minY = minYExtent();
2351 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
2352 && minY != d->vData.flickTarget)
2353 d->flickY(-d->vData.smoothVelocity.value());
2354 d->bufferMode = QDeclarativeListViewPrivate::BufferBefore;
2355 } else if (d->vData.velocity < 0) {
2356 const qreal maxY = maxYExtent();
2357 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
2358 && maxY != d->vData.flickTarget)
2359 d->flickY(-d->vData.smoothVelocity.value());
2360 d->bufferMode = QDeclarativeListViewPrivate::BufferAfter;
2361 }
2362 }
2363
2364 if (xflick()) {
2365 if (d->hData.velocity > 0) {
2366 const qreal minX = minXExtent();
2367 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
2368 && minX != d->hData.flickTarget)
2369 d->flickX(-d->hData.smoothVelocity.value());
2370 d->bufferMode = QDeclarativeListViewPrivate::BufferBefore;
2371 } else if (d->hData.velocity < 0) {
2372 const qreal maxX = maxXExtent();
2373 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
2374 && maxX != d->hData.flickTarget)
2375 d->flickX(-d->hData.smoothVelocity.value());
2376 d->bufferMode = QDeclarativeListViewPrivate::BufferAfter;
2377 }
2378 }
2379 d->inFlickCorrection = false;
2380 }
2381 d->inViewportMoved = false;
2382}
2383
2384qreal QDeclarativeListView::minYExtent() const
2385{
2386 Q_D(const QDeclarativeListView);
2387 if (d->orient == QDeclarativeListView::Horizontal)
2388 return QDeclarativeFlickable::minYExtent();
2389 if (d->minExtentDirty) {
2390 d->minExtent = -d->startPosition();
2391 if (d->header && d->visibleItems.count())
2392 d->minExtent += d->header->size();
2393 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2394 d->minExtent += d->highlightRangeStart;
2395 if (d->sectionCriteria) {
2396 if (d->visibleItem(0))
2397 d->minExtent -= d->visibleItem(0)->sectionSize();
2398 }
2399 d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd + 1));
2400 }
2401 d->minExtentDirty = false;
2402 }
2403
2404 return d->minExtent;
2405}
2406
2407qreal QDeclarativeListView::maxYExtent() const
2408{
2409 Q_D(const QDeclarativeListView);
2410 if (d->orient == QDeclarativeListView::Horizontal)
2411 return height();
2412 if (d->maxExtentDirty) {
2413 if (!d->model || !d->model->count()) {
2414 d->maxExtent = 0;
2415 } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2416 d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart);
2417 if (d->highlightRangeEnd != d->highlightRangeStart)
2418 d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd + 1));
2419 } else {
2420 d->maxExtent = -(d->endPosition() - height() + 1);
2421 }
2422 if (d->footer)
2423 d->maxExtent -= d->footer->size();
2424 qreal minY = minYExtent();
2425 if (d->maxExtent > minY)
2426 d->maxExtent = minY;
2427 d->maxExtentDirty = false;
2428 }
2429 return d->maxExtent;
2430}
2431
2432qreal QDeclarativeListView::minXExtent() const
2433{
2434 Q_D(const QDeclarativeListView);
2435 if (d->orient == QDeclarativeListView::Vertical)
2436 return QDeclarativeFlickable::minXExtent();
2437 if (d->minExtentDirty) {
2438 d->minExtent = -d->startPosition();
2439 if (d->header)
2440 d->minExtent += d->header->size();
2441 if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2442 d->minExtent += d->highlightRangeStart;
2443 d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd + 1));
2444 }
2445 d->minExtentDirty = false;
2446 }
2447
2448 return d->minExtent;
2449}
2450
2451qreal QDeclarativeListView::maxXExtent() const
2452{
2453 Q_D(const QDeclarativeListView);
2454 if (d->orient == QDeclarativeListView::Vertical)
2455 return width();
2456 if (d->maxExtentDirty) {
2457 if (!d->model || !d->model->count()) {
2458 d->maxExtent = 0;
2459 } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2460 d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart);
2461 if (d->highlightRangeEnd != d->highlightRangeStart)
2462 d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd + 1));
2463 } else {
2464 d->maxExtent = -(d->endPosition() - width() + 1);
2465 }
2466 if (d->footer)
2467 d->maxExtent -= d->footer->size();
2468 qreal minX = minXExtent();
2469 if (d->maxExtent > minX)
2470 d->maxExtent = minX;
2471 d->maxExtentDirty = false;
2472 }
2473
2474 return d->maxExtent;
2475}
2476
2477void QDeclarativeListView::keyPressEvent(QKeyEvent *event)
2478{
2479 Q_D(QDeclarativeListView);
2480 keyPressPreHandler(event);
2481 if (event->isAccepted())
2482 return;
2483
2484 if (d->model && d->model->count() && d->interactive) {
2485 if ((d->orient == QDeclarativeListView::Horizontal && event->key() == Qt::Key_Left)
2486 || (d->orient == QDeclarativeListView::Vertical && event->key() == Qt::Key_Up)) {
2487 if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
2488 decrementCurrentIndex();
2489 event->accept();
2490 return;
2491 } else if (d->wrap) {
2492 event->accept();
2493 return;
2494 }
2495 } else if ((d->orient == QDeclarativeListView::Horizontal && event->key() == Qt::Key_Right)
2496 || (d->orient == QDeclarativeListView::Vertical && event->key() == Qt::Key_Down)) {
2497 if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
2498 incrementCurrentIndex();
2499 event->accept();
2500 return;
2501 } else if (d->wrap) {
2502 event->accept();
2503 return;
2504 }
2505 }
2506 }
2507 event->ignore();
2508 QDeclarativeFlickable::keyPressEvent(event);
2509}
2510
2511void QDeclarativeListView::geometryChanged(const QRectF &newGeometry,
2512 const QRectF &oldGeometry)
2513{
2514 Q_D(QDeclarativeListView);
2515 d->maxExtentDirty = true;
2516 d->minExtentDirty = true;
2517 QDeclarativeFlickable::geometryChanged(newGeometry, oldGeometry);
2518}
2519
2520
2521/*!
2522 \qmlmethod ListView::incrementCurrentIndex()
2523
2524 Increments the current index. The current index will wrap
2525 if keyNavigationWraps is true and it is currently at the end.
2526 This method has no effect if the \l count is zero.
2527
2528 \bold Note: methods should only be called after the Component has completed.
2529*/
2530void QDeclarativeListView::incrementCurrentIndex()
2531{
2532 Q_D(QDeclarativeListView);
2533 int count = d->model ? d->model->count() : 0;
2534 if (count && (currentIndex() < count - 1 || d->wrap)) {
2535 d->moveReason = QDeclarativeListViewPrivate::SetIndex;
2536 int index = currentIndex()+1;
2537 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2538 }
2539}
2540
2541/*!
2542 \qmlmethod ListView::decrementCurrentIndex()
2543
2544 Decrements the current index. The current index will wrap
2545 if keyNavigationWraps is true and it is currently at the beginning.
2546 This method has no effect if the \l count is zero.
2547
2548 \bold Note: methods should only be called after the Component has completed.
2549*/
2550void QDeclarativeListView::decrementCurrentIndex()
2551{
2552 Q_D(QDeclarativeListView);
2553 int count = d->model ? d->model->count() : 0;
2554 if (count && (currentIndex() > 0 || d->wrap)) {
2555 d->moveReason = QDeclarativeListViewPrivate::SetIndex;
2556 int index = currentIndex()-1;
2557 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2558 }
2559}
2560
2561/*!
2562 \qmlmethod ListView::positionViewAtIndex(int index, PositionMode mode)
2563
2564 Positions the view such that the \a index is at the position specified by
2565 \a mode:
2566
2567 \list
2568 \o ListView.Beginning - position item at the top (or left for horizontal orientation) of the view.
2569 \o ListView.Center - position item in the center of the view.
2570 \o ListView.End - position item at bottom (or right for horizontal orientation) of the view.
2571 \o ListView.Visible - if any part of the item is visible then take no action, otherwise
2572 bring the item into view.
2573 \o ListView.Contain - ensure the entire item is visible. If the item is larger than
2574 the view the item is positioned at the top (or left for horizontal orientation) of the view.
2575 \endlist
2576
2577 If positioning the view at \a index would cause empty space to be displayed at
2578 the beginning or end of the view, the view will be positioned at the boundary.
2579
2580 It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2581 at a particular index. This is unreliable since removing items from the start
2582 of the list does not cause all other items to be repositioned, and because
2583 the actual start of the view can vary based on the size of the delegates.
2584 The correct way to bring an item into view is with \c positionViewAtIndex.
2585
2586 \bold Note: methods should only be called after the Component has completed. To position
2587 the view at startup, this method should be called by Component.onCompleted. For
2588 example, to position the view at the end:
2589
2590 \code
2591 Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning)
2592 \endcode
2593*/
2594void QDeclarativeListView::positionViewAtIndex(int index, int mode)
2595{
2596 Q_D(QDeclarativeListView);
2597 if (!d->isValid() || index < 0 || index >= d->model->count())
2598 return;
2599 if (mode < Beginning || mode > Contain)
2600 return;
2601
2602 if (d->layoutScheduled)
2603 d->layout();
2604 qreal pos = d->position();
2605 FxListItem *item = d->visibleItem(index);
2606 qreal maxExtent = d->orient == QDeclarativeListView::Vertical ? -maxYExtent() : -maxXExtent();
2607 if (!item) {
2608 int itemPos = d->positionAt(index);
2609 // save the currently visible items in case any of them end up visible again
2610 QList<FxListItem*> oldVisible = d->visibleItems;
2611 d->visibleItems.clear();
2612 d->visiblePos = itemPos;
2613 d->visibleIndex = index;
2614 d->setPosition(qMin(qreal(itemPos), maxExtent));
2615 // now release the reference to all the old visible items.
2616 for (int i = 0; i < oldVisible.count(); ++i)
2617 d->releaseItem(oldVisible.at(i));
2618 item = d->visibleItem(index);
2619 }
2620 if (item) {
2621 const qreal itemPos = item->position();
2622 switch (mode) {
2623 case Beginning:
2624 pos = itemPos;
2625 break;
2626 case Center:
2627 pos = itemPos - (d->size() - item->size())/2;
2628 break;
2629 case End:
2630 pos = itemPos - d->size() + item->size();
2631 break;
2632 case Visible:
2633 if (itemPos > pos + d->size())
2634 pos = itemPos - d->size() + item->size();
2635 else if (item->endPosition() < pos)
2636 pos = itemPos;
2637 break;
2638 case Contain:
2639 if (item->endPosition() > pos + d->size())
2640 pos = itemPos - d->size() + item->size();
2641 if (itemPos < pos)
2642 pos = itemPos;
2643 }
2644 pos = qMin(pos, maxExtent);
2645 qreal minExtent = d->orient == QDeclarativeListView::Vertical ? -minYExtent() : -minXExtent();
2646 pos = qMax(pos, minExtent);
2647 d->moveReason = QDeclarativeListViewPrivate::Other;
2648 cancelFlick();
2649 d->setPosition(pos);
2650 if (d->highlight) {
2651 if (d->autoHighlight) {
2652 d->highlight->setPosition(d->currentItem->itemPosition());
2653 d->highlight->setSize(d->currentItem->itemSize());
2654 }
2655 d->updateHighlight();
2656 }
2657 }
2658 d->fixupPosition();
2659}
2660
2661/*!
2662 \qmlmethod int ListView::indexAt(int x, int y)
2663
2664 Returns the index of the visible item containing the point \a x, \a y in content
2665 coordinates. If there is no item at the point specified, or the item is
2666 not visible -1 is returned.
2667
2668 If the item is outside the visible area, -1 is returned, regardless of
2669 whether an item will exist at that point when scrolled into view.
2670
2671 \bold Note: methods should only be called after the Component has completed.
2672*/
2673int QDeclarativeListView::indexAt(int x, int y) const
2674{
2675 Q_D(const QDeclarativeListView);
2676 for (int i = 0; i < d->visibleItems.count(); ++i) {
2677 const FxListItem *listItem = d->visibleItems.at(i);
2678 if(listItem->contains(x, y))
2679 return listItem->index;
2680 }
2681
2682 return -1;
2683}
2684
2685void QDeclarativeListView::componentComplete()
2686{
2687 Q_D(QDeclarativeListView);
2688 QDeclarativeFlickable::componentComplete();
2689 updateSections();
2690 d->updateHeader();
2691 d->updateFooter();
2692 if (d->isValid()) {
2693 refill();
2694 d->moveReason = QDeclarativeListViewPrivate::SetIndex;
2695 if (d->currentIndex < 0 && !d->currentIndexCleared)
2696 d->updateCurrent(0);
2697 else
2698 d->updateCurrent(d->currentIndex);
2699 if (d->highlight && d->currentItem) {
2700 if (d->autoHighlight)
2701 d->highlight->setPosition(d->currentItem->position());
2702 d->updateTrackedItem();
2703 }
2704 d->moveReason = QDeclarativeListViewPrivate::Other;
2705 d->fixupPosition();
2706 }
2707}
2708
2709void QDeclarativeListView::updateSections()
2710{
2711 Q_D(QDeclarativeListView);
2712 if (isComponentComplete() && d->model) {
2713 QList<QByteArray> roles;
2714 if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty())
2715 roles << d->sectionCriteria->property().toUtf8();
2716 d->model->setWatchedRoles(roles);
2717 d->updateSections();
2718 }
2719}
2720
2721void QDeclarativeListView::refill()
2722{
2723 Q_D(QDeclarativeListView);
2724 d->refill(d->position(), d->position()+d->size()-1);
2725}
2726
2727void QDeclarativeListView::trackedPositionChanged()
2728{
2729 Q_D(QDeclarativeListView);
2730 if (!d->trackedItem || !d->currentItem)
2731 return;
2732 if (d->moveReason == QDeclarativeListViewPrivate::SetIndex) {
2733 qreal trackedPos = qCeil(d->trackedItem->position());
2734 qreal trackedSize = d->trackedItem->size();
2735 if (d->trackedItem != d->currentItem) {
2736 trackedPos -= d->currentItem->sectionSize();
2737 trackedSize += d->currentItem->sectionSize();
2738 }
2739 const qreal viewPos = d->position();
2740 qreal pos = viewPos;
2741 if (d->haveHighlightRange) {
2742 if (d->highlightRange == StrictlyEnforceRange) {
2743 if (trackedPos > pos + d->highlightRangeEnd - d->trackedItem->size())
2744 pos = trackedPos - d->highlightRangeEnd + d->trackedItem->size();
2745 if (trackedPos < pos + d->highlightRangeStart)
2746 pos = trackedPos - d->highlightRangeStart;
2747 } else {
2748 if (trackedPos < d->startPosition() + d->highlightRangeStart) {
2749 pos = d->startPosition();
2750 } else if (d->trackedItem->endPosition() > d->endPosition() - d->size() + d->highlightRangeEnd) {
2751 pos = d->endPosition() - d->size() + 1;
2752 if (pos < d->startPosition())
2753 pos = d->startPosition();
2754 } else {
2755 if (trackedPos < viewPos + d->highlightRangeStart) {
2756 pos = trackedPos - d->highlightRangeStart;
2757 } else if (trackedPos > viewPos + d->highlightRangeEnd - trackedSize) {
2758 pos = trackedPos - d->highlightRangeEnd + trackedSize;
2759 }
2760 }
2761 }
2762 } else {
2763 if (trackedPos < viewPos && d->currentItem->position() < viewPos) {
2764 pos = d->currentItem->position() < trackedPos ? trackedPos : d->currentItem->position();
2765 } else if (d->trackedItem->endPosition() >= viewPos + d->size()
2766 && d->currentItem->endPosition() >= viewPos + d->size()) {
2767 if (d->trackedItem->endPosition() <= d->currentItem->endPosition()) {
2768 pos = d->trackedItem->endPosition() - d->size() + 1;
2769 if (trackedSize > d->size())
2770 pos = trackedPos;
2771 } else {
2772 pos = d->currentItem->endPosition() - d->size() + 1;
2773 if (d->currentItem->size() > d->size())
2774 pos = d->currentItem->position();
2775 }
2776 }
2777 }
2778 if (viewPos != pos) {
2779 cancelFlick();
2780 d->calcVelocity = true;
2781 d->setPosition(pos);
2782 d->calcVelocity = false;
2783 }
2784 }
2785}
2786
2787void QDeclarativeListView::itemsInserted(int modelIndex, int count)
2788{
2789 Q_D(QDeclarativeListView);
2790 if (!isComponentComplete())
2791 return;
2792 d->updateUnrequestedIndexes();
2793 d->moveReason = QDeclarativeListViewPrivate::Other;
2794 if (!d->visibleItems.count() || d->model->count() <= 1) {
2795 d->scheduleLayout();
2796 if (d->itemCount && d->currentIndex >= modelIndex) {
2797 // adjust current item index
2798 d->currentIndex += count;
2799 if (d->currentItem)
2800 d->currentItem->index = d->currentIndex;
2801 emit currentIndexChanged();
2802 } else if (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared)) {
2803 d->updateCurrent(0);
2804 }
2805 d->itemCount += count;
2806 emit countChanged();
2807 return;
2808 }
2809
2810 int index = d->mapFromModel(modelIndex);
2811 if (index < 0) {
2812 int i = d->visibleItems.count() - 1;
2813 while (i > 0 && d->visibleItems.at(i)->index == -1)
2814 --i;
2815 if (i == 0 && d->visibleItems.first()->index == -1) {
2816 // there are no visible items except items marked for removal
2817 index = d->visibleItems.count();
2818 } else if (d->visibleItems.at(i)->index + 1 == modelIndex
2819 && d->visibleItems.at(i)->endPosition() < d->buffer+d->position()+d->size()-1) {
2820 // Special case of appending an item to the model.
2821 index = d->visibleItems.count();
2822 } else {
2823 if (modelIndex < d->visibleIndex) {
2824 // Insert before visible items
2825 d->visibleIndex += count;
2826 for (int i = 0; i < d->visibleItems.count(); ++i) {
2827 FxListItem *listItem = d->visibleItems.at(i);
2828 if (listItem->index != -1 && listItem->index >= modelIndex)
2829 listItem->index += count;
2830 }
2831 }
2832 if (d->currentIndex >= modelIndex) {
2833 // adjust current item index
2834 d->currentIndex += count;
2835 if (d->currentItem)
2836 d->currentItem->index = d->currentIndex;
2837 emit currentIndexChanged();
2838 }
2839 d->scheduleLayout();
2840 d->itemCount += count;
2841 emit countChanged();
2842 return;
2843 }
2844 }
2845
2846 // At least some of the added items will be visible
2847
2848 // index can be the next item past the end of the visible items list (i.e. appended)
2849 int pos = index < d->visibleItems.count() ? d->visibleItems.at(index)->position()
2850 : d->visibleItems.last()->endPosition()+d->spacing+1;
2851 int initialPos = pos;
2852 int diff = 0;
2853 QList<FxListItem*> added;
2854 bool addedVisible = false;
2855 FxListItem *firstVisible = d->firstVisibleItem();
2856 if (firstVisible && pos < firstVisible->position()) {
2857 // Insert items before the visible item.
2858 int insertionIdx = index;
2859 int i = 0;
2860 int from = d->position() - d->buffer;
2861 for (i = count-1; i >= 0 && pos > from; --i) {
2862 if (!addedVisible) {
2863 d->scheduleLayout();
2864 addedVisible = true;
2865 }
2866 FxListItem *item = d->createItem(modelIndex + i);
2867 d->visibleItems.insert(insertionIdx, item);
2868 pos -= item->size() + d->spacing;
2869 item->setPosition(pos);
2870 index++;
2871 }
2872 if (i >= 0) {
2873 // If we didn't insert all our new items - anything
2874 // before the current index is not visible - remove it.
2875 while (insertionIdx--) {
2876 FxListItem *item = d->visibleItems.takeFirst();
2877 if (item->index != -1)
2878 d->visibleIndex++;
2879 d->releaseItem(item);
2880 }
2881 } else {
2882 // adjust pos of items before inserted items.
2883 for (int i = insertionIdx-1; i >= 0; i--) {
2884 FxListItem *listItem = d->visibleItems.at(i);
2885 listItem->setPosition(listItem->position() - (initialPos - pos));
2886 }
2887 }
2888 } else {
2889 int i = 0;
2890 int to = d->buffer+d->position()+d->size()-1;
2891 for (i = 0; i < count && pos <= to; ++i) {
2892 if (!addedVisible) {
2893 d->scheduleLayout();
2894 addedVisible = true;
2895 }
2896 FxListItem *item = d->createItem(modelIndex + i);
2897 d->visibleItems.insert(index, item);
2898 item->setPosition(pos);
2899 added.append(item);
2900 pos += item->size() + d->spacing;
2901 ++index;
2902 }
2903 if (i != count) {
2904 // We didn't insert all our new items, which means anything
2905 // beyond the current index is not visible - remove it.
2906 while (d->visibleItems.count() > index)
2907 d->releaseItem(d->visibleItems.takeLast());
2908 }
2909 diff = pos - initialPos;
2910 }
2911 if (d->itemCount && d->currentIndex >= modelIndex) {
2912 // adjust current item index
2913 d->currentIndex += count;
2914 if (d->currentItem) {
2915 d->currentItem->index = d->currentIndex;
2916 d->currentItem->setPosition(d->currentItem->position() + diff);
2917 }
2918 emit currentIndexChanged();
2919 }
2920 // Update the indexes of the following visible items.
2921 for (; index < d->visibleItems.count(); ++index) {
2922 FxListItem *listItem = d->visibleItems.at(index);
2923 if (d->currentItem && listItem->item != d->currentItem->item)
2924 listItem->setPosition(listItem->position() + diff);
2925 if (listItem->index != -1)
2926 listItem->index += count;
2927 }
2928 // everything is in order now - emit add() signal
2929 for (int j = 0; j < added.count(); ++j)
2930 added.at(j)->attached->emitAdd();
2931
2932 d->updateSections();
2933 d->itemCount += count;
2934 emit countChanged();
2935}
2936
2937void QDeclarativeListView::itemsRemoved(int modelIndex, int count)
2938{
2939 Q_D(QDeclarativeListView);
2940 if (!isComponentComplete())
2941 return;
2942 d->moveReason = QDeclarativeListViewPrivate::Other;
2943 d->updateUnrequestedIndexes();
2944 d->itemCount -= count;
2945
2946 FxListItem *firstVisible = d->firstVisibleItem();
2947 int preRemovedSize = 0;
2948 bool removedVisible = false;
2949 // Remove the items from the visible list, skipping anything already marked for removal
2950 QList<FxListItem*>::Iterator it = d->visibleItems.begin();
2951 while (it != d->visibleItems.end()) {
2952 FxListItem *item = *it;
2953 if (item->index == -1 || item->index < modelIndex) {
2954 // already removed, or before removed items
2955 ++it;
2956 } else if (item->index >= modelIndex + count) {
2957 // after removed items
2958 item->index -= count;
2959 ++it;
2960 } else {
2961 // removed item
2962 if (!removedVisible) {
2963 d->scheduleLayout();
2964 removedVisible = true;
2965 }
2966 item->attached->emitRemove();
2967 if (item->attached->delayRemove()) {
2968 item->index = -1;
2969 connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection);
2970 ++it;
2971 } else {
2972 if (item == firstVisible)
2973 firstVisible = 0;
2974 if (firstVisible && item->position() < firstVisible->position())
2975 preRemovedSize += item->size();
2976 it = d->visibleItems.erase(it);
2977 d->releaseItem(item);
2978 }
2979 }
2980 }
2981
2982 if (firstVisible && d->visibleItems.first() != firstVisible)
2983 d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + preRemovedSize);
2984
2985 // fix current
2986 if (d->currentIndex >= modelIndex + count) {
2987 d->currentIndex -= count;
2988 if (d->currentItem)
2989 d->currentItem->index -= count;
2990 emit currentIndexChanged();
2991 } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) {
2992 // current item has been removed.
2993 d->currentItem->attached->setIsCurrentItem(false);
2994 d->releaseItem(d->currentItem);
2995 d->currentItem = 0;
2996 d->currentIndex = -1;
2997 if (d->itemCount)
2998 d->updateCurrent(qMin(modelIndex, d->itemCount-1));
2999 }
3000
3001 // update visibleIndex
3002 bool haveVisibleIndex = false;
3003 for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
3004 if ((*it)->index != -1) {
3005 d->visibleIndex = (*it)->index;
3006 haveVisibleIndex = true;
3007 break;
3008 }
3009 }
3010
3011 if (removedVisible && !haveVisibleIndex) {
3012 d->timeline.clear();
3013 if (d->itemCount == 0) {
3014 d->visibleIndex = 0;
3015 d->visiblePos = d->header ? d->header->size() : 0;
3016 d->setPosition(0);
3017 d->updateHeader();
3018 d->updateFooter();
3019 update();
3020 } else {
3021 if (modelIndex < d->visibleIndex)
3022 d->visibleIndex = modelIndex+1;
3023 d->visibleIndex = qMax(qMin(d->visibleIndex, d->itemCount-1), 0);
3024 }
3025 }
3026
3027 d->updateSections();
3028 emit countChanged();
3029}
3030
3031void QDeclarativeListView::destroyRemoved()
3032{
3033 Q_D(QDeclarativeListView);
3034 for (QList<FxListItem*>::Iterator it = d->visibleItems.begin();
3035 it != d->visibleItems.end();) {
3036 FxListItem *listItem = *it;
3037 if (listItem->index == -1 && listItem->attached->delayRemove() == false) {
3038 d->releaseItem(listItem);
3039 it = d->visibleItems.erase(it);
3040 } else {
3041 ++it;
3042 }
3043 }
3044
3045 // Correct the positioning of the items
3046 d->layout();
3047}
3048
3049void QDeclarativeListView::itemsMoved(int from, int to, int count)
3050{
3051 Q_D(QDeclarativeListView);
3052 if (!isComponentComplete())
3053 return;
3054 d->updateUnrequestedIndexes();
3055
3056 if (d->visibleItems.isEmpty()) {
3057 refill();
3058 return;
3059 }
3060
3061 d->moveReason = QDeclarativeListViewPrivate::Other;
3062 FxListItem *firstVisible = d->firstVisibleItem();
3063 qreal firstItemPos = firstVisible->position();
3064 QHash<int,FxListItem*> moved;
3065 int moveBy = 0;
3066
3067 QList<FxListItem*>::Iterator it = d->visibleItems.begin();
3068 while (it != d->visibleItems.end()) {
3069 FxListItem *item = *it;
3070 if (item->index >= from && item->index < from + count) {
3071 // take the items that are moving
3072 item->index += (to-from);
3073 moved.insert(item->index, item);
3074 if (item->position() < firstItemPos)
3075 moveBy += item->size();
3076 it = d->visibleItems.erase(it);
3077 } else {
3078 // move everything after the moved items.
3079 if (item->index > from && item->index != -1)
3080 item->index -= count;
3081 ++it;
3082 }
3083 }
3084
3085 int remaining = count;
3086 int endIndex = d->visibleIndex;
3087 it = d->visibleItems.begin();
3088 while (it != d->visibleItems.end()) {
3089 FxListItem *item = *it;
3090 if (remaining && item->index >= to && item->index < to + count) {
3091 // place items in the target position, reusing any existing items
3092 FxListItem *movedItem = moved.take(item->index);
3093 if (!movedItem)
3094 movedItem = d->createItem(item->index);
3095 if (item->index <= firstVisible->index)
3096 moveBy -= movedItem->size();
3097 it = d->visibleItems.insert(it, movedItem);
3098 ++it;
3099 --remaining;
3100 } else {
3101 if (item->index != -1) {
3102 if (item->index >= to) {
3103 // update everything after the moved items.
3104 item->index += count;
3105 }
3106 endIndex = item->index;
3107 }
3108 ++it;
3109 }
3110 }
3111
3112 // If we have moved items to the end of the visible items
3113 // then add any existing moved items that we have
3114 while (FxListItem *item = moved.take(endIndex+1)) {
3115 d->visibleItems.append(item);
3116 ++endIndex;
3117 }
3118
3119 // update visibleIndex
3120 for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
3121 if ((*it)->index != -1) {
3122 d->visibleIndex = (*it)->index;
3123 break;
3124 }
3125 }
3126
3127 // Fix current index
3128 if (d->currentIndex >= 0 && d->currentItem) {
3129 int oldCurrent = d->currentIndex;
3130 d->currentIndex = d->model->indexOf(d->currentItem->item, this);
3131 if (oldCurrent != d->currentIndex) {
3132 d->currentItem->index = d->currentIndex;
3133 emit currentIndexChanged();
3134 }
3135 }
3136
3137 // Whatever moved items remain are no longer visible items.
3138 while (moved.count()) {
3139 int idx = moved.begin().key();
3140 FxListItem *item = moved.take(idx);
3141 if (d->currentItem && item->item == d->currentItem->item)
3142 item->setPosition(d->positionAt(idx));
3143 d->releaseItem(item);
3144 }
3145
3146 // Ensure we don't cause an ugly list scroll.
3147 d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + moveBy);
3148
3149 d->updateSections();
3150 d->layout();
3151}
3152
3153void QDeclarativeListView::itemsChanged(int, int)
3154{
3155 Q_D(QDeclarativeListView);
3156 d->updateSections();
3157 d->layout();
3158}
3159
3160void QDeclarativeListView::modelReset()
3161{
3162 Q_D(QDeclarativeListView);
3163 d->clear();
3164 d->setPosition(0);
3165 refill();
3166 d->moveReason = QDeclarativeListViewPrivate::SetIndex;
3167 d->updateCurrent(d->currentIndex);
3168 if (d->highlight && d->currentItem) {
3169 if (d->autoHighlight)
3170 d->highlight->setPosition(d->currentItem->position());
3171 d->updateTrackedItem();
3172 }
3173 d->moveReason = QDeclarativeListViewPrivate::Other;
3174 emit countChanged();
3175}
3176
3177void QDeclarativeListView::createdItem(int index, QDeclarativeItem *item)
3178{
3179 Q_D(QDeclarativeListView);
3180 if (d->requestedIndex != index) {
3181 item->setParentItem(contentItem());
3182 d->unrequestedItems.insert(item, index);
3183 if (d->orient == QDeclarativeListView::Vertical)
3184 item->setY(d->positionAt(index));
3185 else
3186 item->setX(d->positionAt(index));
3187 }
3188}
3189
3190void QDeclarativeListView::destroyingItem(QDeclarativeItem *item)
3191{
3192 Q_D(QDeclarativeListView);
3193 d->unrequestedItems.remove(item);
3194}
3195
3196void QDeclarativeListView::animStopped()
3197{
3198 Q_D(QDeclarativeListView);
3199 d->bufferMode = QDeclarativeListViewPrivate::NoBuffer;
3200 if (d->haveHighlightRange && d->highlightRange == QDeclarativeListView::StrictlyEnforceRange)
3201 d->updateHighlight();
3202}
3203
3204QDeclarativeListViewAttached *QDeclarativeListView::qmlAttachedProperties(QObject *obj)
3205{
3206 return new QDeclarativeListViewAttached(obj);
3207}
3208
3209QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.