]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/kitemlistviewlayouter.cpp
Icon-rectangle and selection-toggle optimizations
[dolphin.git] / src / kitemviews / kitemlistviewlayouter.cpp
index 6bd9a6e27a1686331ee945d6b884c30d3bf1d13e..2d02b6725102d04b5dc2ddf5dc9c58480eadf0a3 100644 (file)
@@ -24,7 +24,7 @@
 
 #include <KDebug>
 
-#define KITEMLISTVIEWLAYOUTER_DEBUG
+// #define KITEMLISTVIEWLAYOUTER_DEBUG
 
 KItemListViewLayouter::KItemListViewLayouter(QObject* parent) :
     QObject(parent),
@@ -33,6 +33,7 @@ KItemListViewLayouter::KItemListViewLayouter(QObject* parent) :
     m_scrollOrientation(Qt::Vertical),
     m_size(),
     m_itemSize(128, 128),
+    m_itemMargin(),
     m_headerHeight(0),
     m_model(0),
     m_sizeHintResolver(0),
@@ -94,6 +95,19 @@ QSizeF KItemListViewLayouter::itemSize() const
     return m_itemSize;
 }
 
+void KItemListViewLayouter::setItemMargin(const QSizeF& margin)
+{
+    if (m_itemMargin != margin) {
+        m_itemMargin = margin;
+        m_dirty = true;
+    }
+}
+
+QSizeF KItemListViewLayouter::itemMargin() const
+{
+    return m_itemMargin;
+}
+
 void KItemListViewLayouter::setHeaderHeight(qreal height)
 {
     if (m_headerHeight != height) {
@@ -221,6 +235,8 @@ QRectF KItemListViewLayouter::itemRect(int index) const
 
 QRectF KItemListViewLayouter::groupHeaderRect(int index) const
 {
+    const_cast<KItemListViewLayouter*>(this)->doLayout();
+
     const QRectF firstItemRect = itemRect(index);
     QPointF pos = firstItemRect.topLeft();
     if (pos.isNull()) {
@@ -234,7 +250,7 @@ QRectF KItemListViewLayouter::groupHeaderRect(int index) const
         pos.rx() = 0;
         size = QSizeF(m_size.width(), m_groupHeaderHeight);
     } else {
-        size = QSizeF(firstItemRect.width(), m_groupHeaderHeight);
+        size = QSizeF(minimumGroupHeaderWidth(), m_groupHeaderHeight);
     }
     return QRectF(pos, size);
 }
@@ -253,13 +269,9 @@ int KItemListViewLayouter::maximumVisibleItems() const
     return rows * m_columnCount;
 }
 
-int KItemListViewLayouter::itemsPerOffset() const
-{
-    return m_columnCount;
-}
-
 bool KItemListViewLayouter::isFirstGroupItem(int itemIndex) const
 {
+    const_cast<KItemListViewLayouter*>(this)->doLayout();
     return m_groupItemIndexes.contains(itemIndex);
 }
 
@@ -278,22 +290,37 @@ void KItemListViewLayouter::doLayout()
         m_visibleIndexesDirty = true;
 
         QSizeF itemSize = m_itemSize;
+        QSizeF itemMargin = m_itemMargin;
         QSizeF size = m_size;
+        
+        const bool grouped = createGroupHeaders();
 
         const bool horizontalScrolling = (m_scrollOrientation == Qt::Horizontal);
         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());
+            
+            if (grouped) {
+                // In the horizontal scrolling case all groups are aligned
+                // at the top, which decreases the available height. For the
+                // flipped data this means that the width must be decreased.
+                size.rwidth() -= m_groupHeaderHeight;
+            }
         }
 
-        m_columnWidth = itemSize.width();
-        m_columnCount = qMax(1, int(size.width() / m_columnWidth));
-        m_xPosInc = 0;
+        m_columnWidth = itemSize.width() + itemMargin.width();
+        const qreal widthForColumns = size.width() - itemMargin.width();
+        m_columnCount = qMax(1, int(widthForColumns / m_columnWidth));
+        m_xPosInc = itemMargin.width();
 
         const int itemCount = m_model->count();
-        if (itemCount > m_columnCount) {
+        if (itemCount > m_columnCount && m_columnWidth >= 32) {
             // Apply the unused width equally to each column
             const qreal unusedWidth = size.width() - m_columnCount * m_columnWidth;
             if (unusedWidth > 0) {
@@ -310,11 +337,9 @@ void KItemListViewLayouter::doLayout()
 
         m_itemRects.reserve(itemCount);
 
-        qreal y = m_headerHeight;
+        qreal y = m_headerHeight + itemMargin.height();
         int rowIndex = 0;
 
-        const bool grouped = createGroupHeaders();
-
         int index = 0;
         while (index < itemCount) {
             qreal x = m_xPosInc;
@@ -355,6 +380,25 @@ void KItemListViewLayouter::doLayout()
                     m_itemRects.append(bounds);
                 }
 
+                if (grouped && horizontalScrolling) {
+                    // When grouping is enabled in the horizontal mode, the header alignment
+                    // looks like this:
+                    //   Header-1 Header-2 Header-3
+                    //   Item 1   Item 4   Item 7
+                    //   Item 2   Item 5   Item 8
+                    //   Item 3   Item 6   Item 9
+                    // In this case 'requiredItemHeight' represents the column-width. We don't
+                    // check the content of the header in the layouter to determine the required
+                    // width, hence assure that at least a minimal width of 15 characters is given
+                    // (in average a character requires the halve width of the font height).
+                    //
+                    // TODO: Let the group headers provide a minimum width and respect this width here
+                    const qreal headerWidth = minimumGroupHeaderWidth();
+                    if (requiredItemHeight < headerWidth) {
+                        requiredItemHeight = headerWidth;
+                    }
+                }
+
                 maxItemHeight = qMax(maxItemHeight, requiredItemHeight);
                 x += m_columnWidth;
                 ++index;
@@ -367,16 +411,25 @@ void KItemListViewLayouter::doLayout()
                 }
             }
 
-            y += maxItemHeight;
+            y += maxItemHeight + itemMargin.height();
             ++rowIndex;
         }
         if (m_itemRects.count() > itemCount) {
             m_itemRects.erase(m_itemRects.begin() + itemCount,
-                                      m_itemRects.end());
+                              m_itemRects.end());
         }
 
         if (itemCount > 0) {
+            // Calculate the maximum y-range of the last row for m_maximumScrollOffset
             m_maximumScrollOffset = m_itemRects.last().bottom();
+            const qreal rowY = m_itemRects.last().y();
+
+            int index = m_itemRects.count() - 2;
+            while (index >= 0 && m_itemRects.at(index).bottom() >= rowY) {
+                m_maximumScrollOffset = qMax(m_maximumScrollOffset, m_itemRects.at(index).bottom());
+                --index;
+            }
+
             m_maximumItemOffset = m_columnCount * m_columnWidth;
         } else {
             m_maximumScrollOffset = 0;
@@ -409,21 +462,31 @@ void KItemListViewLayouter::updateVisibleIndexes()
 
     const int maxIndex = m_model->count() - 1;
 
-    // Calculate the first visible index that is (at least partly) visible
+    // Calculate the first visible index that is fully visible
     int min = 0;
     int max = maxIndex;
     int mid = 0;
     do {
         mid = (min + max) / 2;
-        if (m_itemRects[mid].bottom() < m_scrollOffset) {
+        if (m_itemRects[mid].top() < m_scrollOffset) {
             min = mid + 1;
         } else {
             max = mid - 1;
         }
     } while (min <= max);
 
-    while (mid < maxIndex && m_itemRects[mid].bottom() < m_scrollOffset) {
-        ++mid;
+    if (mid > 0) {
+        // Include the row before the first fully visible index, as it might
+        // be partly visible
+        if (m_itemRects[mid].top() >= m_scrollOffset) {
+            --mid;
+            Q_ASSERT(m_itemRects[mid].top() < m_scrollOffset);
+        }
+
+        const qreal rowTop = m_itemRects[mid].top();
+        while (mid > 0 && m_itemRects[mid - 1].top() == rowTop) {
+            --mid;
+        }
     }
     m_firstVisibleIndex = mid;
 
@@ -474,4 +537,9 @@ bool KItemListViewLayouter::createGroupHeaders()
     return true;
 }
 
+qreal KItemListViewLayouter::minimumGroupHeaderWidth() const
+{
+    return m_groupHeaderHeight * 15 / 2;
+}
+
 #include "kitemlistviewlayouter_p.moc"