2
* Copyright (C) 2013-2015 Canonical, Ltd.
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; version 3.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18
* Some documentation on how this thing works:
20
* A flickable has two very important concepts that define the top and
21
* height of the flickable area.
22
* The top is returned in minYExtent()
23
* The height is set using setContentHeight()
24
* By changing those two values we can make the list grow up or down
25
* as needed. e.g. if we are in the middle of the list
26
* and something that is above the viewport grows, since we do not
27
* want to change the viewport because of that we just adjust the
28
* minYExtent so that the list grows up.
30
* The implementation on the list relies on the delegateModel doing
31
* most of the instantiation work. You call createItem() when you
32
* need to create an item asking for it async or not. If returns null
33
* it means the item will be created async and the model will call the
34
* itemCreated slot with the item.
36
* updatePolish is the central point of dispatch for the work of the
37
* class. It is called by the scene graph just before drawing the class.
39
* * Make sure all items are positioned correctly
40
* * Add/Remove items if needed
41
* * Update the content height if it was dirty
43
* m_visibleItems contains all the items we have created at the moment.
44
* Actually not all of them are visible since it includes the ones
45
* in the cache area we create asynchronously to help performance.
46
* The first item in m_visibleItems has the m_firstVisibleIndex in
47
* the model. If you actually want to know what is the first
48
* item in the viewport you have to find the first non culled element
51
* The first item of m_visibleItems is the one that defines the
52
* positions of all the rest of items (see updatePolish()) and
53
* this is why sometimes we move it even if it's not the item
54
* that has triggered the function (i.e. in itemGeometryChanged())
56
* m_visibleItems is a list of ListItem. Each ListItem
57
* will contain a item and potentially a sectionItem. The sectionItem
58
* is only there when the list is using sectionDelegate+sectionProperty
59
* and this is the first item of the section. Each ListItem is vertically
60
* layouted with the sectionItem first and then the item.
62
* Note that minYExtent and height are not always totally accurate, since
63
* we don't have the items created we can't guess their heights
64
* so we can only guarantee the values are correct when the first/last
65
* items of the list are visible, otherwise we just live with good enough
66
* values that make the list scrollable
68
* There are a few things that are not really implemented or tested properly
69
* which we don't use at the moment like changing the model, etc.
70
* The known missing features are marked with TODOs along the code.
73
#include "verticalview.h"
75
#include <QCoreApplication>
78
#include <qqmlengine.h>
79
#pragma GCC diagnostic push
80
#pragma GCC diagnostic ignored "-pedantic"
81
#include <private/qqmldelegatemodel_p.h>
82
#include <private/qqmlglobal_p.h>
83
#include <private/qquickitem_p.h>
84
#include <private/qquickanimation_p.h>
85
#pragma GCC diagnostic pop
87
qreal VerticalView::ListItem::height() const
89
return m_item->height();
92
qreal VerticalView::ListItem::y() const
97
void VerticalView::ListItem::setY(qreal newY)
102
bool VerticalView::ListItem::culled() const
104
return QQuickItemPrivate::get(m_item)->culled;
107
void VerticalView::ListItem::setCulled(bool culled)
109
QQuickItemPrivate::get(m_item)->setCulled(culled);
112
VerticalView::VerticalView()
113
: m_delegateModel(nullptr)
114
, m_asyncRequestedIndex(-1)
115
, m_delegateValidated(false)
116
, m_firstVisibleIndex(-1)
117
, m_currentPageIndex(-1)
119
, m_contentHeightDirty(false)
120
, m_previousContentY(0)
125
connect(this, SIGNAL(heightChanged()), this, SLOT(_q_heightChanged()));
126
connect(this, SIGNAL(contentYChanged()), this, SLOT(_q_updateCurrentPageIndex()));
128
setFlickableDirection(QQuickFlickable::HorizontalAndVerticalFlick);
131
VerticalView::~VerticalView()
135
QAbstractItemModel *VerticalView::model() const
137
return m_delegateModel ? m_delegateModel->model().value<QAbstractItemModel *>() : nullptr;
140
void VerticalView::setModel(QAbstractItemModel *model)
142
if (model != this->model()) {
143
if (!m_delegateModel) {
144
createDelegateModel();
146
disconnect(m_delegateModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), this, SLOT(_q_modelUpdated(QQmlChangeSet,bool)));
148
m_delegateModel->setModel(QVariant::fromValue<QAbstractItemModel *>(model));
149
connect(m_delegateModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), this, SLOT(_q_modelUpdated(QQmlChangeSet,bool)));
150
Q_EMIT modelChanged();
153
// Q_EMIT contentHeightChanged();
154
// Q_EMIT contentYChanged();
158
QQmlComponent *VerticalView::delegate() const
160
return m_delegateModel ? m_delegateModel->delegate() : nullptr;
163
void VerticalView::setDelegate(QQmlComponent *delegate)
165
if (delegate != this->delegate()) {
166
if (!m_delegateModel) {
167
createDelegateModel();
170
// Cleanup the existing items
171
Q_FOREACH(ListItem *item, m_visibleItems)
173
m_visibleItems.clear();
174
m_firstVisibleIndex = -1;
178
m_delegateModel->setDelegate(delegate);
180
Q_EMIT delegateChanged();
181
m_delegateValidated = false;
182
m_contentHeightDirty = true;
187
int VerticalView::cacheBuffer() const
189
return m_cacheBuffer;
192
void VerticalView::setCacheBuffer(int cacheBuffer)
194
if (cacheBuffer < 0) {
195
qmlInfo(this) << "Cannot set a negative cache buffer";
199
if (cacheBuffer != m_cacheBuffer) {
200
m_cacheBuffer = cacheBuffer;
201
Q_EMIT cacheBufferChanged();
206
qreal VerticalView::spacing() const
211
void VerticalView::setSpacing(qreal spacing)
214
qmlInfo(this) << "Cannot set a negative spacing";
218
if (spacing != m_spacing) {
220
Q_EMIT spacingChanged();
225
int VerticalView::count() const
228
return m_delegateModel->count();
233
int VerticalView::currentPageIndex() const
235
return m_currentPageIndex;
238
QQuickItem *VerticalView::currentPageItem() const
240
return itemAt(m_currentPageIndex);
243
void VerticalView::_q_updateCurrentPageIndex()
245
if (!m_visibleItems.isEmpty()) {
246
qreal pos = this->contentY() + (this->height() * 0.5);
248
int oldCurrentPageIndex = m_currentPageIndex;
251
Q_FOREACH(ListItem * item, m_visibleItems) {
252
if (item->y() < pos && item->y() + item->height() > pos)
258
// If spacing is set, there may be no page on posY position,
259
// and the Q_FOREACH loop keep on running until the end.
260
if (i != m_visibleItems.length())
261
m_currentPageIndex = m_firstVisibleIndex + i;
263
if (m_currentPageIndex != oldCurrentPageIndex) {
264
Q_EMIT currentPageIndexChanged();
265
Q_EMIT currentPageItemChanged();
271
void VerticalView::positionAtBeginning()
273
if (m_delegateModel->count() <= 0)
276
if (m_firstVisibleIndex != 0) {
277
// TODO This could be optimized by trying to reuse the interesection
278
// of items that may end up intersecting between the existing
279
// m_visibleItems and the items we are creating in the next loop
280
Q_FOREACH(ListItem *item, m_visibleItems)
282
m_visibleItems.clear();
283
m_firstVisibleIndex = -1;
285
// Create the item 0, it will be already correctly positioned at createItem()
286
ListItem *item = createItem(0, false);
287
// Create the subsequent items
289
qreal pos = item->y() + item->height();
290
const qreal bufferTo = height() + m_cacheBuffer;
291
while (modelIndex < m_delegateModel->count() && pos <= bufferTo) {
292
if (!(item = createItem(modelIndex, false)))
294
pos += item->height();
298
m_previousContentY = m_visibleItems.first()->y();
300
setContentY(m_visibleItems.first()->y());
303
void VerticalView::positionAtIndex(int index)
305
if (m_delegateModel->count() <= 0)
308
if (index < m_firstVisibleIndex || index > m_firstVisibleIndex + m_visibleItems.length()) {
309
// TODO This could be optimized by trying to reuse the interesection
310
// of items that may end up intersecting between the existing
311
// m_visibleItems and the items we are creating in the next loop
312
Q_FOREACH(ListItem *item, m_visibleItems)
314
m_visibleItems.clear();
315
m_firstVisibleIndex = -1;
317
// Create the item with the given index, it will be already correctly positioned at createItem()
318
// Other items are created when createdItem() signal is emitted.
319
createItem(index, false);
321
m_previousContentY = m_visibleItems.first()->y();
324
setContentY(itemAt(index)->y());
327
void VerticalView::positionAtEnd()
329
if (m_delegateModel->count() <= 0)
332
if (m_firstVisibleIndex != m_delegateModel->count() - 1) {
333
// TODO This could be optimized by trying to reuse the interesection
334
// of items that may end up intersecting between the existing
335
// m_visibleItems and the items we are creating in the next loop
336
Q_FOREACH(ListItem *item, m_visibleItems)
338
m_visibleItems.clear();
339
m_firstVisibleIndex = -1;
341
// Create the item 0, it will be already correctly positioned at createItem()
342
ListItem *item = createItem(m_delegateModel->count() - 1, false);
343
// Create the prior items
344
int modelIndex = m_delegateModel->count() - 2;
345
qreal pos = item->y() + item->height();
346
const qreal bufferFrom = contentY() - m_cacheBuffer;
347
while (modelIndex > -1 && pos >= bufferFrom) {
348
if (!(item = createItem(modelIndex, false)))
350
pos += item->height();
354
m_previousContentY = m_visibleItems.first()->y();
356
setContentY(m_visibleItems.first()->y());
359
static inline bool uFuzzyCompare(qreal r1, qreal r2)
361
return qFuzzyCompare(r1, r2) || (qFuzzyIsNull(r1) && qFuzzyIsNull(r2));
364
QQuickItem *VerticalView::itemAt(int modelIndex) const
366
ListItem *item = itemAtIndex(modelIndex);
373
qreal VerticalView::minYExtent() const
378
void VerticalView::componentComplete()
381
m_delegateModel->componentComplete();
383
QQuickFlickable::componentComplete();
388
void VerticalView::viewportMoved(Qt::Orientations orient)
390
// Check we are not being taken down and don't paint anything
391
// TODO Check if we still need this in 5.2
392
// For reproduction just inifnite loop testDash or testDashContent
393
if (!QQmlEngine::contextForObject(this)->parentContext())
396
QQuickFlickable::viewportMoved(orient);
397
m_previousContentY = contentY();
402
void VerticalView::createDelegateModel()
404
m_delegateModel = new QQmlDelegateModel(qmlContext(this), this);
405
connect(m_delegateModel, SIGNAL(createdItem(int,QObject*)), this, SLOT(_q_itemCreated(int,QObject*)));
406
connect(m_delegateModel, SIGNAL(countChanged()), this, SIGNAL(countChanged()));
407
if (isComponentComplete())
408
m_delegateModel->componentComplete();
409
updateWatchedRoles();
412
void VerticalView::refill()
417
if (!isComponentComplete()) {
421
const qreal from = contentY();
422
const qreal to = from + height();
423
const qreal bufferFrom = from - m_cacheBuffer;
424
const qreal bufferTo = to + m_cacheBuffer;
426
bool added = addVisibleItems(from, to, false);
427
bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
428
added |= addVisibleItems(bufferFrom, bufferTo, true);
430
if (added || removed) {
431
m_contentHeightDirty = true;
435
bool VerticalView::addVisibleItems(qreal fillFrom, qreal fillTo, bool asynchronous)
440
if (m_delegateModel->count() == 0)
446
if (!m_visibleItems.isEmpty()) {
447
modelIndex = m_firstVisibleIndex + m_visibleItems.count();
448
item = m_visibleItems.last();
449
pos = item->y() + item->height() + m_spacing;
451
bool changed = false;
453
while (modelIndex < m_delegateModel->count() && pos <= fillTo) {
454
if (!(item = createItem(modelIndex, asynchronous)))
456
pos += item->height() + m_spacing;
463
if (!m_visibleItems.isEmpty()) {
464
modelIndex = m_firstVisibleIndex - 1;
465
item = m_visibleItems.first();
468
while (modelIndex >= 0 && pos > fillFrom) {
469
if (!(item = createItem(modelIndex, asynchronous)))
471
pos -= item->height() + m_spacing;
479
void VerticalView::reallyReleaseItem(ListItem *listItem)
481
QQuickItem *item = listItem->m_item;
482
QQmlDelegateModel::ReleaseFlags flags = m_delegateModel->release(item);
483
if (flags & QQmlDelegateModel::Destroyed) {
484
item->setParentItem(nullptr);
489
void VerticalView::releaseItem(ListItem *listItem)
491
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(listItem->m_item);
492
itemPrivate->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
493
m_itemsToRelease << listItem;
496
void VerticalView::updateWatchedRoles()
498
if (m_delegateModel) {
499
QList<QByteArray> roles;
500
m_delegateModel->setWatchedRoles(roles);
504
bool VerticalView::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
506
// Do not remove items if we are overshooting up or down, since we'll come back
507
// to the "stable" position and delete/create items without any reason
508
if (contentY() < -m_minYExtent) {
510
} else if (contentY() + height() > contentHeight()) {
513
bool changed = false;
515
bool foundVisible = false;
517
int removedItems = 0;
518
const auto oldFirstVisibleIndex = m_firstVisibleIndex;
519
while (i < m_visibleItems.count()) {
520
ListItem *item = m_visibleItems[i];
521
const qreal pos = item->y() + m_spacing;
522
if (pos + item->height() < bufferFrom || pos > bufferTo) {
524
m_visibleItems.removeAt(i);
530
const int itemIndex = m_firstVisibleIndex + removedItems + i;
531
m_firstVisibleIndex = itemIndex;
537
m_firstVisibleIndex = -1;
539
if (m_firstVisibleIndex != oldFirstVisibleIndex) {
546
VerticalView::ListItem *VerticalView::createItem(int modelIndex, bool asynchronous)
548
if (asynchronous && m_asyncRequestedIndex != -1)
551
m_asyncRequestedIndex = -1;
552
QObject* object = m_delegateModel->object(modelIndex, asynchronous);
553
QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
556
m_delegateModel->release(object);
557
if (!m_delegateValidated) {
558
m_delegateValidated = true;
559
QObject* delegateObj = delegate();
560
qmlInfo(delegateObj ? delegateObj : this) << "Delegate must be of Item type";
563
m_asyncRequestedIndex = modelIndex;
567
ListItem *listItem = new ListItem;
568
listItem->m_item = item;
569
QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::Geometry);
570
ListItem *prevItem = itemAtIndex(modelIndex - 1);
571
bool lostItem = false; // Is an item that we requested async but because of model changes
572
// it is no longer attached to any of the existing items (has no prev nor next item)
573
// nor is the first item
575
listItem->setY(prevItem->y() + prevItem->height() + m_spacing);
577
ListItem *currItem = itemAtIndex(modelIndex);
579
// There's something already in m_visibleItems at out index, meaning this is an insert, so attach to its top
580
listItem->setY(currItem->y() - listItem->height() - m_spacing);
582
ListItem *nextItem = itemAtIndex(modelIndex + 1);
584
listItem->setY(nextItem->y() - listItem->height() - m_spacing);
585
} else if (modelIndex == 0) {
587
} else if (!m_visibleItems.isEmpty()) {
593
listItem->setCulled(true);
594
releaseItem(listItem);
597
listItem->setCulled(listItem->y() + listItem->height() + m_spacing <= contentY() || listItem->y() >= contentY() + height());
598
if (m_visibleItems.isEmpty()) {
599
m_visibleItems << listItem;
601
m_visibleItems.insert(modelIndex - m_firstVisibleIndex, listItem);
603
if (m_firstVisibleIndex < 0 || modelIndex < m_firstVisibleIndex) {
604
m_firstVisibleIndex = modelIndex;
608
m_contentHeightDirty = true;
614
void VerticalView::_q_itemCreated(int modelIndex, QObject *object)
616
QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
618
qWarning() << "VerticalView::itemCreated got a non item for index" << modelIndex;
622
// Check we are not being taken down and don't paint anything
623
// TODO Check if we still need this in 5.2
624
// For reproduction just inifnite loop testDash or testDashContent
625
if (!QQmlEngine::contextForObject(this)->parentContext())
628
item->setParentItem(contentItem());
629
QQmlContext *context = QQmlEngine::contextForObject(item)->parentContext();
630
context->setContextProperty(QLatin1String("VerticalView"), this);
631
context->setContextProperty(QLatin1String("heightToClip"), QVariant::fromValue<int>(0));
632
if (modelIndex == m_asyncRequestedIndex) {
633
createItem(modelIndex, false);
638
void VerticalView::_q_heightChanged()
643
void VerticalView::_q_modelUpdated(const QQmlChangeSet &changeSet, bool /*reset*/)
645
// TODO Do something with reset
646
const auto oldFirstVisibleIndex = m_firstVisibleIndex;
648
Q_FOREACH(const QQmlChangeSet::Change &remove, changeSet.removes()) {
649
if (remove.index + remove.count > m_firstVisibleIndex && remove.index < m_firstVisibleIndex + m_visibleItems.count()) {
650
const qreal oldFirstValidIndexPos = m_visibleItems.first()->y();
651
// If all the items we are removing are either not created or culled
652
// we have to grow down to avoid viewport changing
653
bool growDown = true;
654
for (int i = 0; growDown && i < remove.count; ++i) {
655
const int modelIndex = remove.index + i;
656
ListItem *item = itemAtIndex(modelIndex);
657
if (item && !item->culled()) {
661
for (int i = remove.count - 1; i >= 0; --i) {
662
const int visibleIndex = remove.index + i - m_firstVisibleIndex;
663
if (visibleIndex >= 0 && visibleIndex < m_visibleItems.count()) {
664
ListItem *item = m_visibleItems[visibleIndex];
666
m_visibleItems.removeAt(visibleIndex);
671
} else if (remove.index <= m_firstVisibleIndex) {
672
if (!m_visibleItems.isEmpty()) {
673
// We removed the first item that is the one that positions the rest
674
// position the new first item correctly
675
m_visibleItems.first()->setY(oldFirstValidIndexPos);
677
m_firstVisibleIndex = -1;
680
} else if (remove.index + remove.count <= m_firstVisibleIndex) {
681
m_firstVisibleIndex -= remove.count;
683
for (int i = remove.count - 1; i >= 0; --i) {
684
const int modelIndex = remove.index + i;
685
if (modelIndex == m_asyncRequestedIndex) {
686
m_asyncRequestedIndex = -1;
687
} else if (modelIndex < m_asyncRequestedIndex) {
688
m_asyncRequestedIndex--;
693
Q_FOREACH(const QQmlChangeSet::Change &insert, changeSet.inserts()) {
694
const bool insertingInValidIndexes = insert.index > m_firstVisibleIndex && insert.index < m_firstVisibleIndex + m_visibleItems.count();
695
const bool firstItemWithViewOnTop = insert.index == 0 && m_firstVisibleIndex == 0 && m_visibleItems.first()->y() > contentY();
696
if (insertingInValidIndexes || firstItemWithViewOnTop)
698
// If the items we are adding won't be really visible
699
// we grow up instead of down to not change the viewport
701
if (!firstItemWithViewOnTop) {
702
for (int i = 0; i < m_visibleItems.count(); ++i) {
703
if (!m_visibleItems[i]->culled()) {
704
if (insert.index <= m_firstVisibleIndex + i) {
712
const qreal oldFirstValidIndexPos = m_visibleItems.first()->y();
713
for (int i = insert.count - 1; i >= 0; --i) {
714
const int modelIndex = insert.index + i;
715
ListItem *item = createItem(modelIndex, false);
717
ListItem *firstItem = m_visibleItems.first();
718
firstItem->setY(firstItem->y() - item->height());
721
if (firstItemWithViewOnTop) {
722
ListItem *firstItem = m_visibleItems.first();
723
firstItem->setY(oldFirstValidIndexPos);
726
} else if (insert.index <= m_firstVisibleIndex) {
727
m_firstVisibleIndex += insert.count;
730
for (int i = insert.count - 1; i >= 0; --i) {
731
const int modelIndex = insert.index + i;
732
if (modelIndex <= m_asyncRequestedIndex) {
733
m_asyncRequestedIndex++;
738
if (m_firstVisibleIndex != oldFirstVisibleIndex) {
744
m_contentHeightDirty = true;
747
void VerticalView::itemGeometryChanged(QQuickItem * /*item*/, const QRectF &newGeometry, const QRectF &oldGeometry)
749
const qreal heightDiff = newGeometry.height() - oldGeometry.height();
750
if (heightDiff != 0) {
751
if (oldGeometry.y() + oldGeometry.height() <= contentY() && !m_visibleItems.isEmpty()) {
752
ListItem *firstItem = m_visibleItems.first();
753
firstItem->setY(firstItem->y() - heightDiff);
760
m_contentHeightDirty = true;
764
void VerticalView::adjustMinYExtent()
766
if (m_visibleItems.isEmpty()) {
769
qreal nonCreatedHeight = 0;
770
if (m_firstVisibleIndex != 0) {
771
// Calculate the average height of items to estimate the position of the list start
772
const int visibleItems = m_visibleItems.count();
773
qreal visibleItemsHeight = 0;
774
Q_FOREACH(ListItem *item, m_visibleItems) {
775
visibleItemsHeight += item->height() + m_spacing;
777
nonCreatedHeight = m_firstVisibleIndex * visibleItemsHeight / visibleItems;
779
m_minYExtent = nonCreatedHeight - m_visibleItems.first()->y();
780
if (m_minYExtent != 0 && qFuzzyIsNull(m_minYExtent)) {
782
m_visibleItems.first()->setY(nonCreatedHeight);
787
VerticalView::ListItem *VerticalView::itemAtIndex(int modelIndex) const
789
const int visibleIndexedModelIndex = modelIndex - m_firstVisibleIndex;
790
if (visibleIndexedModelIndex >= 0 && visibleIndexedModelIndex < m_visibleItems.count())
791
return m_visibleItems[visibleIndexedModelIndex];
796
void VerticalView::layout()
802
if (!m_visibleItems.isEmpty()) {
803
const qreal visibleFrom = contentY();
804
const qreal visibleTo = contentY() + height();
806
qreal pos = m_visibleItems.first()->y();
808
// qDebug() << "VerticalView::layout Updating positions and heights. contentY" << contentY() << "minYExtent" << minYExtent();
809
int firstReallyVisibleItem = -1;
810
int modelIndex = m_firstVisibleIndex;
811
Q_FOREACH(ListItem *item, m_visibleItems) {
812
const bool cull = pos + item->height() + m_spacing <= visibleFrom || pos >= visibleTo;
813
item->setCulled(cull);
815
if (!cull && firstReallyVisibleItem == -1) {
816
firstReallyVisibleItem = modelIndex;
818
QQmlContext *context = QQmlEngine::contextForObject(item->m_item)->parentContext();
819
const qreal clipFrom = visibleFrom;
820
if (!cull && pos < clipFrom) {
821
context->setContextProperty(QLatin1String("heightToClip"), clipFrom - pos);
823
context->setContextProperty(QLatin1String("heightToClip"), QVariant::fromValue<int>(0));
825
// qDebug() << "VerticalView::layout" << item->m_item;
826
pos += item->height() + m_spacing;
833
void VerticalView::updatePolish()
835
// Check we are not being taken down and don't paint anything
836
// TODO Check if we still need this in 5.2
837
// For reproduction just inifnite loop testDash or testDashContent
838
if (!QQmlEngine::contextForObject(this)->parentContext())
841
Q_FOREACH(ListItem *item, m_itemsToRelease)
842
reallyReleaseItem(item);
843
m_itemsToRelease.clear();
852
if (m_contentHeightDirty) {
854
if (m_visibleItems.isEmpty()) {
857
const int modelCount = model()->rowCount();
858
const int visibleItems = m_visibleItems.count();
859
const int lastValidIndex = m_firstVisibleIndex + visibleItems - 1;
860
qreal nonCreatedHeight = 0;
861
if (lastValidIndex != modelCount - 1) {
862
const int visibleItems = m_visibleItems.count();
863
qreal visibleItemsHeight = 0;
864
Q_FOREACH(ListItem *item, m_visibleItems) {
865
visibleItemsHeight += item->height() + m_spacing;
867
const int unknownSizes = modelCount - (m_firstVisibleIndex + visibleItems);
868
nonCreatedHeight = unknownSizes * visibleItemsHeight / visibleItems;
870
ListItem *item = m_visibleItems.last();
871
contentHeight = nonCreatedHeight + item->y() + item->height();
872
if (m_firstVisibleIndex != 0) {
873
// Make sure that if we are shrinking we tell the view we still fit
874
m_minYExtent = qMax(m_minYExtent, -(contentHeight - height()));
878
m_contentHeightDirty = false;
880
setContentHeight(contentHeight);
884
#include "moc_verticalview.cpp"