]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/private/kitemlistviewlayouter.cpp
Ensure that KItemListViewLayouter always has a size hint resolver
[dolphin.git] / src / kitemviews / private / kitemlistviewlayouter.cpp
index da569b3dc6cf0ee97b8b119efd9aaba31ea4eb03..9da5384d4f6e849d145db850ef94ba46c53d98ab 100644 (file)
@@ -26,7 +26,7 @@
 
 // #define KITEMLISTVIEWLAYOUTER_DEBUG
 
-KItemListViewLayouter::KItemListViewLayouter(QObject* parent) :
+KItemListViewLayouter::KItemListViewLayouter(KItemListSizeHintResolver* sizeHintResolver, QObject* parent) :
     QObject(parent),
     m_dirty(true),
     m_visibleIndexesDirty(true),
@@ -36,7 +36,7 @@ KItemListViewLayouter::KItemListViewLayouter(QObject* parent) :
     m_itemMargin(),
     m_headerHeight(0),
     m_model(0),
-    m_sizeHintResolver(0),
+    m_sizeHintResolver(sizeHintResolver),
     m_scrollOffset(0),
     m_maximumScrollOffset(0),
     m_itemOffset(0),
@@ -46,11 +46,14 @@ KItemListViewLayouter::KItemListViewLayouter(QObject* parent) :
     m_columnWidth(0),
     m_xPosInc(0),
     m_columnCount(0),
+    m_rowOffsets(),
+    m_columnOffsets(),
     m_groupItemIndexes(),
     m_groupHeaderHeight(0),
     m_groupHeaderMargin(0),
     m_itemInfos()
 {
+    Q_ASSERT(m_sizeHintResolver);
 }
 
 KItemListViewLayouter::~KItemListViewLayouter()
@@ -207,19 +210,6 @@ const KItemModelBase* KItemListViewLayouter::model() const
     return m_model;
 }
 
-void KItemListViewLayouter::setSizeHintResolver(const KItemListSizeHintResolver* sizeHintResolver)
-{
-    if (m_sizeHintResolver != sizeHintResolver) {
-        m_sizeHintResolver = sizeHintResolver;
-        m_dirty = true;
-    }
-}
-
-const KItemListSizeHintResolver* KItemListViewLayouter::sizeHintResolver() const
-{
-    return m_sizeHintResolver;
-}
-
 int KItemListViewLayouter::firstVisibleIndex() const
 {
     const_cast<KItemListViewLayouter*>(this)->doLayout();
@@ -239,20 +229,26 @@ QRectF KItemListViewLayouter::itemRect(int index) const
         return QRectF();
     }
 
+    QSizeF sizeHint = m_sizeHintResolver->sizeHint(index);
+
+    const qreal x = m_columnOffsets.at(m_itemInfos.at(index).column);
+    const qreal y = m_rowOffsets.at(m_itemInfos.at(index).row);
+
     if (m_scrollOrientation == Qt::Horizontal) {
         // Rotate the logical direction which is always vertical by 90°
         // to get the physical horizontal direction
-        const QRectF& b = m_itemInfos[index].rect;
-        QRectF bounds(b.y(), b.x(), b.height(), b.width());
-        QPointF pos = bounds.topLeft();
+        QPointF pos(y, x);
         pos.rx() -= m_scrollOffset;
-        bounds.moveTo(pos);
-        return bounds;
+        return QRectF(pos, sizeHint);
     }
 
-    QRectF bounds = m_itemInfos[index].rect;
-    bounds.moveTo(bounds.topLeft() - QPointF(m_itemOffset, m_scrollOffset));
-    return bounds;
+    if (sizeHint.width() <= 0) {
+        // In Details View, a size hint with negative width is used internally.
+        sizeHint.rwidth() = m_itemSize.width();
+    }
+
+    const QPointF pos(x - m_itemOffset, y - m_scrollOffset);
+    return QRectF(pos, sizeHint);
 }
 
 QRectF KItemListViewLayouter::groupHeaderRect(int index) const
@@ -274,27 +270,28 @@ QRectF KItemListViewLayouter::groupHeaderRect(int index) const
         pos.rx() -= m_itemMargin.width();
         pos.ry() = 0;
 
-        // Determine the maximum width used in the
-        // current column. As the scroll-direction is
-        // Qt::Horizontal and m_itemRects is accessed directly,
-        // the logical height represents the visual width.
-        qreal width = minimumGroupHeaderWidth();
-        const qreal y = m_itemInfos[index].rect.y();
+        // Determine the maximum width used in the current column. As the
+        // scroll-direction is Qt::Horizontal and m_itemRects is accessed
+        // directly, the logical height represents the visual width, and
+        // the logical row represents the column.
+        qreal headerWidth = minimumGroupHeaderWidth();
+        const int row = m_itemInfos[index].row;
         const int maxIndex = m_itemInfos.count() - 1;
         while (index <= maxIndex) {
-            QRectF bounds = m_itemInfos[index].rect;
-            if (bounds.y() != y) {
+            if (m_itemInfos[index].row != row) {
                 break;
             }
 
-            if (bounds.height() > width) {
-                width = bounds.height();
+            const qreal itemWidth = m_sizeHintResolver->sizeHint(index).width();
+
+            if (itemWidth > headerWidth) {
+                headerWidth = itemWidth;
             }
 
             ++index;
         }
 
-        size = QSizeF(width, m_size.height());
+        size = QSizeF(headerWidth, m_size.height());
     }
     return QRectF(pos, size);
 }
@@ -375,12 +372,9 @@ void KItemListViewLayouter::doLayout()
         if (horizontalScrolling) {
             // Flip everything so that the layout logically can work like having
             // a vertical scrolling
-            itemSize.setWidth(m_itemSize.height());
-            itemSize.setHeight(m_itemSize.width());
-            itemMargin.setWidth(m_itemMargin.height());
-            itemMargin.setHeight(m_itemMargin.width());
-            size.setWidth(m_size.height());
-            size.setHeight(m_size.width());
+            itemSize.transpose();
+            itemMargin.transpose();
+            size.transpose();
 
             if (grouped) {
                 // In the horizontal scrolling case all groups are aligned
@@ -406,28 +400,42 @@ void KItemListViewLayouter::doLayout()
             }
         }
 
-        int rowCount = itemCount / m_columnCount;
-        if (itemCount % m_columnCount != 0) {
-            ++rowCount;
+        m_itemInfos.resize(itemCount);
+
+        // Calculate the offset of each column, i.e., the x-coordinate where the column starts.
+        m_columnOffsets.resize(m_columnCount);
+        qreal currentOffset = m_xPosInc;
+
+        if (grouped && horizontalScrolling) {
+            // All group headers will always be aligned on the top and not
+            // flipped like the other properties.
+            currentOffset += m_groupHeaderHeight;
+        }
+
+        for (int column = 0; column < m_columnCount; ++column) {
+            m_columnOffsets[column] = currentOffset;
+            currentOffset += m_columnWidth;
         }
 
-        m_itemInfos.reserve(itemCount);
+        // Prepare the QVector which stores the y-coordinate for each new row.
+        int numberOfRows = (itemCount + m_columnCount - 1) / m_columnCount;
+        if (grouped && m_columnCount > 1) {
+            // In the worst case, a new row will be started for every group.
+            // We could calculate the exact number of rows now to prevent that we reserve
+            // too much memory, but the code required to do that might need much more
+            // memory than it would save in the average case.
+            numberOfRows += m_groupItemIndexes.count();
+        }
+        m_rowOffsets.resize(numberOfRows);
 
         qreal y = m_headerHeight + itemMargin.height();
         int row = 0;
 
         int index = 0;
         while (index < itemCount) {
-            qreal x = m_xPosInc;
             qreal maxItemHeight = itemSize.height();
 
             if (grouped) {
-                if (horizontalScrolling) {
-                    // All group headers will always be aligned on the top and not
-                    // flipped like the other properties
-                    x += m_groupHeaderHeight;
-                }
-
                 if (m_groupItemIndexes.contains(index)) {
                     // The item is the first item of a group.
                     // Increase the y-position to provide space
@@ -447,29 +455,20 @@ void KItemListViewLayouter::doLayout()
                 }
             }
 
+            m_rowOffsets[row] = y;
+
             int column = 0;
             while (index < itemCount && column < m_columnCount) {
                 qreal requiredItemHeight = itemSize.height();
-                if (m_sizeHintResolver) {
-                    const QSizeF sizeHint = m_sizeHintResolver->sizeHint(index);
-                    const qreal sizeHintHeight = horizontalScrolling ? sizeHint.width() : sizeHint.height();
-                    if (sizeHintHeight > requiredItemHeight) {
-                        requiredItemHeight = sizeHintHeight;
-                    }
+                const QSizeF sizeHint = m_sizeHintResolver->sizeHint(index);
+                const qreal sizeHintHeight = horizontalScrolling ? sizeHint.width() : sizeHint.height();
+                if (sizeHintHeight > requiredItemHeight) {
+                    requiredItemHeight = sizeHintHeight;
                 }
 
-                const QRectF bounds(x, y, itemSize.width(), requiredItemHeight);
-                if (index < m_itemInfos.count()) {
-                    m_itemInfos[index].rect = bounds;
-                    m_itemInfos[index].column = column;
-                    m_itemInfos[index].row = row;
-                } else {
-                    ItemInfo itemInfo;
-                    itemInfo.rect = bounds;
-                    itemInfo.column = column;
-                    itemInfo.row = row;
-                    m_itemInfos.append(itemInfo);
-                }
+                ItemInfo& itemInfo = m_itemInfos[index];
+                itemInfo.column = column;
+                itemInfo.row = row;
 
                 if (grouped && horizontalScrolling) {
                     // When grouping is enabled in the horizontal mode, the header alignment
@@ -491,7 +490,6 @@ void KItemListViewLayouter::doLayout()
                 }
 
                 maxItemHeight = qMax(maxItemHeight, requiredItemHeight);
-                x += m_columnWidth;
                 ++index;
                 ++column;
 
@@ -505,24 +503,9 @@ void KItemListViewLayouter::doLayout()
             y += maxItemHeight + itemMargin.height();
             ++row;
         }
-        if (m_itemInfos.count() > itemCount) {
-            m_itemInfos.erase(m_itemInfos.begin() + itemCount,
-                              m_itemInfos.end());
-        }
 
         if (itemCount > 0) {
-            // Calculate the maximum y-range of the last row for m_maximumScrollOffset
-            m_maximumScrollOffset = m_itemInfos.last().rect.bottom();
-            const qreal rowY = m_itemInfos.last().rect.y();
-
-            int index = m_itemInfos.count() - 2;
-            while (index >= 0 && m_itemInfos[index].rect.bottom() >= rowY) {
-                m_maximumScrollOffset = qMax(m_maximumScrollOffset, m_itemInfos[index].rect.bottom());
-                --index;
-            }
-
-            m_maximumScrollOffset += itemMargin.height();
-
+            m_maximumScrollOffset = y;
             m_maximumItemOffset = m_columnCount * m_columnWidth;
         } else {
             m_maximumScrollOffset = 0;
@@ -561,7 +544,7 @@ void KItemListViewLayouter::updateVisibleIndexes()
     int mid = 0;
     do {
         mid = (min + max) / 2;
-        if (m_itemInfos[mid].rect.top() < m_scrollOffset) {
+        if (m_rowOffsets.at(m_itemInfos[mid].row) < m_scrollOffset) {
             min = mid + 1;
         } else {
             max = mid - 1;
@@ -571,13 +554,13 @@ void KItemListViewLayouter::updateVisibleIndexes()
     if (mid > 0) {
         // Include the row before the first fully visible index, as it might
         // be partly visible
-        if (m_itemInfos[mid].rect.top() >= m_scrollOffset) {
+        if (m_rowOffsets.at(m_itemInfos[mid].row) >= m_scrollOffset) {
             --mid;
-            Q_ASSERT(m_itemInfos[mid].rect.top() < m_scrollOffset);
+            Q_ASSERT(m_rowOffsets.at(m_itemInfos[mid].row) < m_scrollOffset);
         }
 
-        const qreal rowTop = m_itemInfos[mid].rect.top();
-        while (mid > 0 && m_itemInfos[mid - 1].rect.top() == rowTop) {
+        const int firstVisibleRow = m_itemInfos[mid].row;
+        while (mid > 0 && m_itemInfos[mid - 1].row == firstVisibleRow) {
             --mid;
         }
     }
@@ -594,14 +577,14 @@ void KItemListViewLayouter::updateVisibleIndexes()
     max = maxIndex;
     do {
         mid = (min + max) / 2;
-        if (m_itemInfos[mid].rect.y() <= bottom) {
+        if (m_rowOffsets.at(m_itemInfos[mid].row) <= bottom) {
             min = mid + 1;
         } else {
             max = mid - 1;
         }
     } while (min <= max);
 
-    while (mid > 0 && m_itemInfos[mid].rect.y() > bottom) {
+    while (mid > 0 && m_rowOffsets.at(m_itemInfos[mid].row) > bottom) {
         --mid;
     }
     m_lastVisibleIndex = mid;