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 |
|
---|
57 | QT_BEGIN_NAMESPACE
|
---|
58 |
|
---|
59 | void QDeclarativeViewSection::setProperty(const QString &property)
|
---|
60 | {
|
---|
61 | if (property != m_property) {
|
---|
62 | m_property = property;
|
---|
63 | emit propertyChanged();
|
---|
64 | }
|
---|
65 | }
|
---|
66 |
|
---|
67 | void QDeclarativeViewSection::setCriteria(QDeclarativeViewSection::SectionCriteria criteria)
|
---|
68 | {
|
---|
69 | if (criteria != m_criteria) {
|
---|
70 | m_criteria = criteria;
|
---|
71 | emit criteriaChanged();
|
---|
72 | }
|
---|
73 | }
|
---|
74 |
|
---|
75 | void QDeclarativeViewSection::setDelegate(QDeclarativeComponent *delegate)
|
---|
76 | {
|
---|
77 | if (delegate != m_delegate) {
|
---|
78 | m_delegate = delegate;
|
---|
79 | emit delegateChanged();
|
---|
80 | }
|
---|
81 | }
|
---|
82 |
|
---|
83 | QString 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 |
|
---|
93 | class FxListItem
|
---|
94 | {
|
---|
95 | public:
|
---|
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 |
|
---|
165 | class QDeclarativeListViewPrivate : public QDeclarativeFlickablePrivate
|
---|
166 | {
|
---|
167 | Q_DECLARE_PUBLIC(QDeclarativeListView)
|
---|
168 |
|
---|
169 | public:
|
---|
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 |
|
---|
516 | void 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 |
|
---|
526 | void 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 |
|
---|
547 | FxListItem *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 |
|
---|
594 | void 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 |
|
---|
623 | void 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 |
|
---|
726 | void 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 |
|
---|
735 | void 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 |
|
---|
775 | void 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 |
|
---|
783 | void 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 |
|
---|
802 | void 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 |
|
---|
813 | void 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 |
|
---|
881 | void 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 |
|
---|
902 | void 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 |
|
---|
957 | void 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 |
|
---|
988 | void 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 |
|
---|
1013 | void 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 |
|
---|
1065 | void 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 |
|
---|
1075 | void 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 |
|
---|
1115 | void 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 |
|
---|
1156 | void 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 |
|
---|
1167 | void 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 |
|
---|
1226 | void 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 |
|
---|
1434 | QDeclarativeListView::QDeclarativeListView(QDeclarativeItem *parent)
|
---|
1435 | : QDeclarativeFlickable(*(new QDeclarativeListViewPrivate), parent)
|
---|
1436 | {
|
---|
1437 | Q_D(QDeclarativeListView);
|
---|
1438 | d->init();
|
---|
1439 | }
|
---|
1440 |
|
---|
1441 | QDeclarativeListView::~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 | */
|
---|
1532 | QVariant QDeclarativeListView::model() const
|
---|
1533 | {
|
---|
1534 | Q_D(const QDeclarativeListView);
|
---|
1535 | return d->modelVariant;
|
---|
1536 | }
|
---|
1537 |
|
---|
1538 | void 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 | */
|
---|
1626 | QDeclarativeComponent *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 |
|
---|
1637 | void 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 | */
|
---|
1684 | int QDeclarativeListView::currentIndex() const
|
---|
1685 | {
|
---|
1686 | Q_D(const QDeclarativeListView);
|
---|
1687 | return d->currentIndex;
|
---|
1688 | }
|
---|
1689 |
|
---|
1690 | void 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 |
|
---|
1707 | QDeclarativeItem *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 | */
|
---|
1725 | QDeclarativeItem *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 | */
|
---|
1737 | int 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 | */
|
---|
1756 | QDeclarativeComponent *QDeclarativeListView::highlight() const
|
---|
1757 | {
|
---|
1758 | Q_D(const QDeclarativeListView);
|
---|
1759 | return d->highlightComponent;
|
---|
1760 | }
|
---|
1761 |
|
---|
1762 | void 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 | */
|
---|
1793 | bool QDeclarativeListView::highlightFollowsCurrentItem() const
|
---|
1794 | {
|
---|
1795 | Q_D(const QDeclarativeListView);
|
---|
1796 | return d->autoHighlight;
|
---|
1797 | }
|
---|
1798 |
|
---|
1799 | void 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 | */
|
---|
1847 | qreal QDeclarativeListView::preferredHighlightBegin() const
|
---|
1848 | {
|
---|
1849 | Q_D(const QDeclarativeListView);
|
---|
1850 | return d->highlightRangeStart;
|
---|
1851 | }
|
---|
1852 |
|
---|
1853 | void 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 |
|
---|
1863 | qreal QDeclarativeListView::preferredHighlightEnd() const
|
---|
1864 | {
|
---|
1865 | Q_D(const QDeclarativeListView);
|
---|
1866 | return d->highlightRangeEnd;
|
---|
1867 | }
|
---|
1868 |
|
---|
1869 | void 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 |
|
---|
1879 | QDeclarativeListView::HighlightRangeMode QDeclarativeListView::highlightRangeMode() const
|
---|
1880 | {
|
---|
1881 | Q_D(const QDeclarativeListView);
|
---|
1882 | return d->highlightRange;
|
---|
1883 | }
|
---|
1884 |
|
---|
1885 | void 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 | */
|
---|
1902 | qreal QDeclarativeListView::spacing() const
|
---|
1903 | {
|
---|
1904 | Q_D(const QDeclarativeListView);
|
---|
1905 | return d->spacing;
|
---|
1906 | }
|
---|
1907 |
|
---|
1908 | void 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 | */
|
---|
1939 | QDeclarativeListView::Orientation QDeclarativeListView::orientation() const
|
---|
1940 | {
|
---|
1941 | Q_D(const QDeclarativeListView);
|
---|
1942 | return d->orient;
|
---|
1943 | }
|
---|
1944 |
|
---|
1945 | void 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 | */
|
---|
1975 | bool QDeclarativeListView::isWrapEnabled() const
|
---|
1976 | {
|
---|
1977 | Q_D(const QDeclarativeListView);
|
---|
1978 | return d->wrap;
|
---|
1979 | }
|
---|
1980 |
|
---|
1981 | void 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 | */
|
---|
2009 | int QDeclarativeListView::cacheBuffer() const
|
---|
2010 | {
|
---|
2011 | Q_D(const QDeclarativeListView);
|
---|
2012 | return d->buffer;
|
---|
2013 | }
|
---|
2014 |
|
---|
2015 | void 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 | */
|
---|
2071 | QDeclarativeViewSection *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 | */
|
---|
2085 | QString 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 | */
|
---|
2110 | qreal QDeclarativeListView::highlightMoveSpeed() const
|
---|
2111 | {
|
---|
2112 | Q_D(const QDeclarativeListView);\
|
---|
2113 | return d->highlightMoveSpeed;
|
---|
2114 | }
|
---|
2115 |
|
---|
2116 | void 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 |
|
---|
2127 | int QDeclarativeListView::highlightMoveDuration() const
|
---|
2128 | {
|
---|
2129 | Q_D(const QDeclarativeListView);
|
---|
2130 | return d->highlightMoveDuration;
|
---|
2131 | }
|
---|
2132 |
|
---|
2133 | void 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 |
|
---|
2144 | qreal QDeclarativeListView::highlightResizeSpeed() const
|
---|
2145 | {
|
---|
2146 | Q_D(const QDeclarativeListView);\
|
---|
2147 | return d->highlightResizeSpeed;
|
---|
2148 | }
|
---|
2149 |
|
---|
2150 | void 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 |
|
---|
2161 | int QDeclarativeListView::highlightResizeDuration() const
|
---|
2162 | {
|
---|
2163 | Q_D(const QDeclarativeListView);
|
---|
2164 | return d->highlightResizeDuration;
|
---|
2165 | }
|
---|
2166 |
|
---|
2167 | void 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 | */
|
---|
2199 | QDeclarativeListView::SnapMode QDeclarativeListView::snapMode() const
|
---|
2200 | {
|
---|
2201 | Q_D(const QDeclarativeListView);
|
---|
2202 | return d->snapMode;
|
---|
2203 | }
|
---|
2204 |
|
---|
2205 | void 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 | */
|
---|
2223 | QDeclarativeComponent *QDeclarativeListView::footer() const
|
---|
2224 | {
|
---|
2225 | Q_D(const QDeclarativeListView);
|
---|
2226 | return d->footerComponent;
|
---|
2227 | }
|
---|
2228 |
|
---|
2229 | void 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 | */
|
---|
2257 | QDeclarativeComponent *QDeclarativeListView::header() const
|
---|
2258 | {
|
---|
2259 | Q_D(const QDeclarativeListView);
|
---|
2260 | return d->headerComponent;
|
---|
2261 | }
|
---|
2262 |
|
---|
2263 | void 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 |
|
---|
2283 | void 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 |
|
---|
2291 | void 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 |
|
---|
2299 | bool 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 |
|
---|
2310 | void 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 |
|
---|
2384 | qreal 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 |
|
---|
2407 | qreal 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 |
|
---|
2432 | qreal 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 |
|
---|
2451 | qreal 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 |
|
---|
2477 | void 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 |
|
---|
2511 | void 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 | */
|
---|
2530 | void 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 | */
|
---|
2550 | void 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 | */
|
---|
2594 | void 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 | */
|
---|
2673 | int 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 |
|
---|
2685 | void 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 |
|
---|
2709 | void 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 |
|
---|
2721 | void QDeclarativeListView::refill()
|
---|
2722 | {
|
---|
2723 | Q_D(QDeclarativeListView);
|
---|
2724 | d->refill(d->position(), d->position()+d->size()-1);
|
---|
2725 | }
|
---|
2726 |
|
---|
2727 | void 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 |
|
---|
2787 | void 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 |
|
---|
2937 | void 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 |
|
---|
3031 | void 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 |
|
---|
3049 | void 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 |
|
---|
3153 | void QDeclarativeListView::itemsChanged(int, int)
|
---|
3154 | {
|
---|
3155 | Q_D(QDeclarativeListView);
|
---|
3156 | d->updateSections();
|
---|
3157 | d->layout();
|
---|
3158 | }
|
---|
3159 |
|
---|
3160 | void 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 |
|
---|
3177 | void 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 |
|
---|
3190 | void QDeclarativeListView::destroyingItem(QDeclarativeItem *item)
|
---|
3191 | {
|
---|
3192 | Q_D(QDeclarativeListView);
|
---|
3193 | d->unrequestedItems.remove(item);
|
---|
3194 | }
|
---|
3195 |
|
---|
3196 | void 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 |
|
---|
3204 | QDeclarativeListViewAttached *QDeclarativeListView::qmlAttachedProperties(QObject *obj)
|
---|
3205 | {
|
---|
3206 | return new QDeclarativeListViewAttached(obj);
|
---|
3207 | }
|
---|
3208 |
|
---|
3209 | QT_END_NAMESPACE
|
---|