diff --git a/src/kitemviews/kitemlistview.cpp b/src/kitemviews/kitemlistview.cpp index b5e1058433..2ea6657a5a 100644 --- a/src/kitemviews/kitemlistview.cpp +++ b/src/kitemviews/kitemlistview.cpp @@ -958,6 +958,8 @@ void KItemListView::slotItemsInserted(const KItemRangeList& itemRanges) m_layouter->markAsDirty(); + m_sizeHintResolver->itemsInserted(itemRanges); + int previouslyInsertedCount = 0; foreach (const KItemRange& range, itemRanges) { // range.index is related to the model before anything has been inserted. @@ -971,8 +973,6 @@ void KItemListView::slotItemsInserted(const KItemRangeList& itemRanges) } previouslyInsertedCount += count; - m_sizeHintResolver->itemsInserted(index, count); - // Determine which visible items must be moved QList itemsToMove; QHashIterator it(m_visibleItems); @@ -1030,12 +1030,6 @@ void KItemListView::slotItemsInserted(const KItemRangeList& itemRanges) } if (hasMultipleRanges) { -#ifndef QT_NO_DEBUG - // Important: Don't read any m_layouter-property inside the for-loop in case if - // multiple ranges are given! m_layouter accesses m_sizeHintResolver which is - // updated in each loop-cycle and has only a consistent state after the loop. - Q_ASSERT(m_layouter->isDirty()); -#endif m_endTransactionAnimationHint = NoAnimation; endTransaction(); @@ -1074,6 +1068,8 @@ void KItemListView::slotItemsRemoved(const KItemRangeList& itemRanges) removedItemsCount += itemRanges[i].count; } + m_sizeHintResolver->itemsRemoved(itemRanges); + for (int i = itemRanges.count() - 1; i >= 0; --i) { const KItemRange& range = itemRanges[i]; const int index = range.index; @@ -1083,8 +1079,6 @@ void KItemListView::slotItemsRemoved(const KItemRangeList& itemRanges) continue; } - m_sizeHintResolver->itemsRemoved(index, count); - const int firstRemovedIndex = index; const int lastRemovedIndex = index + count - 1; const int lastIndex = m_model->count() - 1 + removedItemsCount; @@ -1153,15 +1147,6 @@ void KItemListView::slotItemsRemoved(const KItemRangeList& itemRanges) } if (hasMultipleRanges) { -#ifndef QT_NO_DEBUG - // Important: Don't read any m_layouter-property inside the for-loop in case if - // multiple ranges are given! m_layouter accesses m_sizeHintResolver which is - // updated in each loop-cycle and has only a consistent state after the loop. - // TODO: This assert can be hit when filtering in Icons and Compact view, - // see https://bugs.kde.org/show_bug.cgi?id=317827 comments 2 and 3. - // We should try to figure out if the assert is wrong or if there is a bug in the code. - //Q_ASSERT(m_layouter->isDirty()); -#endif m_endTransactionAnimationHint = NoAnimation; endTransaction(); updateSiblingsInformation(); @@ -1540,9 +1525,9 @@ void KItemListView::setModel(KItemModelBase* model) this, SLOT(slotSortOrderChanged(Qt::SortOrder,Qt::SortOrder))); disconnect(m_model, SIGNAL(sortRoleChanged(QByteArray,QByteArray)), this, SLOT(slotSortRoleChanged(QByteArray,QByteArray))); - } - m_sizeHintResolver->clearCache(); + m_sizeHintResolver->itemsRemoved(KItemRangeList() << KItemRange(0, m_model->count())); + } m_model = model; m_layouter->setModel(model); @@ -1566,7 +1551,6 @@ void KItemListView::setModel(KItemModelBase* model) const int itemCount = m_model->count(); if (itemCount > 0) { - m_sizeHintResolver->itemsInserted(0, itemCount); slotItemsInserted(KItemRangeList() << KItemRange(0, itemCount)); } } diff --git a/src/kitemviews/private/kitemlistsizehintresolver.cpp b/src/kitemviews/private/kitemlistsizehintresolver.cpp index e075b46d09..5db87f34db 100644 --- a/src/kitemviews/private/kitemlistsizehintresolver.cpp +++ b/src/kitemviews/private/kitemlistsizehintresolver.cpp @@ -20,7 +20,6 @@ #include "kitemlistsizehintresolver.h" #include -#include KItemListSizeHintResolver::KItemListSizeHintResolver(const KItemListView* itemListView) : m_itemListView(itemListView), @@ -42,22 +41,77 @@ QSizeF KItemListSizeHintResolver::sizeHint(int index) const return size; } -void KItemListSizeHintResolver::itemsInserted(int index, int count) +void KItemListSizeHintResolver::itemsInserted(const KItemRangeList& itemRanges) { - const int currentCount = m_sizeHintCache.count(); - m_sizeHintCache.reserve(currentCount + count); - while (count > 0) { - m_sizeHintCache.insert(index, QSizeF()); - ++index; - --count; + int insertedCount = 0; + foreach (const KItemRange& range, itemRanges) { + insertedCount += range.count; } + + const int currentCount = m_sizeHintCache.count(); + m_sizeHintCache.reserve(currentCount + insertedCount); + + // We build the new list from the end to the beginning to mimize the + // number of moves. + m_sizeHintCache.insert(m_sizeHintCache.end(), insertedCount, QSizeF()); + + int sourceIndex = currentCount - 1; + int targetIndex = m_sizeHintCache.count() - 1; + int itemsToInsertBeforeCurrentRange = insertedCount; + + for (int rangeIndex = itemRanges.count() - 1; rangeIndex >= 0; --rangeIndex) { + const KItemRange& range = itemRanges.at(rangeIndex); + itemsToInsertBeforeCurrentRange -= range.count; + + // First: move all existing items that must be put behind 'range'. + while (targetIndex >= itemsToInsertBeforeCurrentRange + range.index + range.count) { + m_sizeHintCache[targetIndex] = m_sizeHintCache[sourceIndex]; + --sourceIndex; + --targetIndex; + } + + // Then: insert QSizeF() for the items which are inserted into 'range'. + while (targetIndex >= itemsToInsertBeforeCurrentRange + range.index) { + m_sizeHintCache[targetIndex] = QSizeF(); + --targetIndex; + } + } + + Q_ASSERT(m_sizeHintCache.count() == m_itemListView->model()->count()); } -void KItemListSizeHintResolver::itemsRemoved(int index, int count) +void KItemListSizeHintResolver::itemsRemoved(const KItemRangeList& itemRanges) { - const QVector::iterator begin = m_sizeHintCache.begin() + index; - const QVector::iterator end = begin + count; - m_sizeHintCache.erase(begin, end); + const QVector::iterator begin = m_sizeHintCache.begin(); + const QVector::iterator end = m_sizeHintCache.end(); + + KItemRangeList::const_iterator rangeIt = itemRanges.constBegin(); + const KItemRangeList::const_iterator rangeEnd = itemRanges.constEnd(); + + QVector::iterator destIt = begin + rangeIt->index; + QVector::iterator srcIt = destIt + rangeIt->count; + + ++rangeIt; + + while (srcIt != end) { + *destIt = *srcIt; + ++destIt; + ++srcIt; + + if (rangeIt != rangeEnd && srcIt == begin + rangeIt->index) { + // Skip the items in the next removed range. + srcIt += rangeIt->count; + ++rangeIt; + } + } + + m_sizeHintCache.erase(destIt, end); + + // Note that the cache size might temporarily not match the model size if + // this function is called from KItemListView::setModel() to empty the cache. + if (!m_sizeHintCache.isEmpty() && m_itemListView->model()) { + Q_ASSERT(m_sizeHintCache.count() == m_itemListView->model()->count()); + } } void KItemListSizeHintResolver::itemsMoved(int index, int count) diff --git a/src/kitemviews/private/kitemlistsizehintresolver.h b/src/kitemviews/private/kitemlistsizehintresolver.h index 6f50d673ca..5ec5f4a215 100644 --- a/src/kitemviews/private/kitemlistsizehintresolver.h +++ b/src/kitemviews/private/kitemlistsizehintresolver.h @@ -22,7 +22,7 @@ #include -#include +#include #include #include @@ -38,8 +38,8 @@ public: virtual ~KItemListSizeHintResolver(); QSizeF sizeHint(int index) const; - void itemsInserted(int index, int count); - void itemsRemoved(int index, int count); + void itemsInserted(const KItemRangeList& itemRanges); + void itemsRemoved(const KItemRangeList& itemRanges); void itemsMoved(int index, int count); void itemsChanged(int index, int count, const QSet& roles);