~saviq/ubuntu/saucy/qtdeclarative-opensource-src/add-qtquick-delegate-range

« back to all changes in this revision

Viewing changes to src/quick/items/qquicklistview.cpp

  • Committer: Package Import Robot
  • Author(s): Timo Jyrinki
  • Date: 2013-02-05 14:17:19 UTC
  • Revision ID: package-import@ubuntu.com-20130205141719-qqeyml8wslpyez52
Tags: upstream-5.0.1
ImportĀ upstreamĀ versionĀ 5.0.1

Show diffs side-by-side

added added

removed removed

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