]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/private/kitemlistviewlayouter.cpp
Add smaller statusbar and set it as default
[dolphin.git] / src / kitemviews / private / kitemlistviewlayouter.cpp
index c15b44e13b1593b77cbd9360272ce1812751e679..3ed2343a83c2ace6ae93af4b6acbf727a67fa677 100644 (file)
@@ -1,56 +1,48 @@
-/***************************************************************************
- *   Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com>             *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program is distributed in the hope that it will be useful,       *
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
- *   GNU General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA            *
- ***************************************************************************/
+/*
+ * SPDX-FileCopyrightText: 2011 Peter Penz <peter.penz19@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
 
 #include "kitemlistviewlayouter.h"
-
-#include <kitemviews/kitemmodelbase.h>
+#include "dolphindebug.h"
 #include "kitemlistsizehintresolver.h"
+#include "kitemviews/kitemmodelbase.h"
 
-#include <KDebug>
+#include <QGuiApplication>
+#include <QScopeGuard>
 
 // #define KITEMLISTVIEWLAYOUTER_DEBUG
 
-KItemListViewLayouter::KItemListViewLayouter(QObject* parent) :
-    QObject(parent),
-    m_dirty(true),
-    m_visibleIndexesDirty(true),
-    m_scrollOrientation(Qt::Vertical),
-    m_size(),
-    m_itemSize(128, 128),
-    m_itemMargin(),
-    m_headerHeight(0),
-    m_model(0),
-    m_sizeHintResolver(0),
-    m_scrollOffset(0),
-    m_maximumScrollOffset(0),
-    m_itemOffset(0),
-    m_maximumItemOffset(0),
-    m_firstVisibleIndex(-1),
-    m_lastVisibleIndex(-1),
-    m_columnWidth(0),
-    m_xPosInc(0),
-    m_columnCount(0),
-    m_groupItemIndexes(),
-    m_groupHeaderHeight(0),
-    m_groupHeaderMargin(0),
-    m_itemInfos()
-{
+KItemListViewLayouter::KItemListViewLayouter(KItemListSizeHintResolver *sizeHintResolver, QObject *parent)
+    : QObject(parent)
+    , m_dirty(true)
+    , m_visibleIndexesDirty(true)
+    , m_scrollOrientation(Qt::Vertical)
+    , m_size()
+    , m_itemSize(128, 128)
+    , m_itemMargin()
+    , m_headerHeight(0)
+    , m_model(nullptr)
+    , m_sizeHintResolver(sizeHintResolver)
+    , m_scrollOffset(0)
+    , m_maximumScrollOffset(0)
+    , m_itemOffset(0)
+    , m_maximumItemOffset(0)
+    , m_firstVisibleIndex(-1)
+    , m_lastVisibleIndex(-1)
+    , m_columnWidth(0)
+    , m_xPosInc(0)
+    , m_columnCount(0)
+    , m_rowOffsets()
+    , m_columnOffsets()
+    , m_groupItemIndexes()
+    , m_groupHeaderHeight(0)
+    , m_groupHeaderMargin(0)
+    , m_itemInfos()
+    , m_statusBarOffset(0)
+{
+    Q_ASSERT(m_sizeHintResolver);
 }
 
 KItemListViewLayouter::~KItemListViewLayouter()
@@ -70,11 +62,19 @@ Qt::Orientation KItemListViewLayouter::scrollOrientation() const
     return m_scrollOrientation;
 }
 
-void KItemListViewLayouter::setSize(const QSizeFsize)
+void KItemListViewLayouter::setSize(const QSizeF &size)
 {
     if (m_size != size) {
+        if (m_scrollOrientation == Qt::Vertical) {
+            if (m_size.width() != size.width()) {
+                m_dirty = true;
+            }
+        } else if (m_size.height() != size.height()) {
+            m_dirty = true;
+        }
+
         m_size = size;
-        m_dirty = true;
+        m_visibleIndexesDirty = true;
     }
 }
 
@@ -83,7 +83,7 @@ QSizeF KItemListViewLayouter::size() const
     return m_size;
 }
 
-void KItemListViewLayouter::setItemSize(const QSizeFsize)
+void KItemListViewLayouter::setItemSize(const QSizeF &size)
 {
     if (m_itemSize != size) {
         m_itemSize = size;
@@ -96,7 +96,7 @@ QSizeF KItemListViewLayouter::itemSize() const
     return m_itemSize;
 }
 
-void KItemListViewLayouter::setItemMargin(const QSizeFmargin)
+void KItemListViewLayouter::setItemMargin(const QSizeF &margin)
 {
     if (m_itemMargin != margin) {
         m_itemMargin = margin;
@@ -163,7 +163,7 @@ qreal KItemListViewLayouter::scrollOffset() const
 
 qreal KItemListViewLayouter::maximumScrollOffset() const
 {
-    const_cast<KItemListViewLayouter*>(this)->doLayout();
+    const_cast<KItemListViewLayouter *>(this)->doLayout();
     return m_maximumScrollOffset;
 }
 
@@ -182,11 +182,11 @@ qreal KItemListViewLayouter::itemOffset() const
 
 qreal KItemListViewLayouter::maximumItemOffset() const
 {
-    const_cast<KItemListViewLayouter*>(this)->doLayout();
+    const_cast<KItemListViewLayouter *>(this)->doLayout();
     return m_maximumItemOffset;
 }
 
-void KItemListViewLayouter::setModel(const KItemModelBasemodel)
+void KItemListViewLayouter::setModel(const KItemModelBase *model)
 {
     if (m_model != model) {
         m_model = model;
@@ -194,62 +194,60 @@ void KItemListViewLayouter::setModel(const KItemModelBase* model)
     }
 }
 
-const KItemModelBaseKItemListViewLayouter::model() const
+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();
+    const_cast<KItemListViewLayouter *>(this)->doLayout();
     return m_firstVisibleIndex;
 }
 
 int KItemListViewLayouter::lastVisibleIndex() const
 {
-    const_cast<KItemListViewLayouter*>(this)->doLayout();
+    const_cast<KItemListViewLayouter *>(this)->doLayout();
     return m_lastVisibleIndex;
 }
 
 QRectF KItemListViewLayouter::itemRect(int index) const
 {
-    const_cast<KItemListViewLayouter*>(this)->doLayout();
+    const_cast<KItemListViewLayouter *>(this)->doLayout();
     if (index < 0 || index >= m_itemInfos.count()) {
         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();
-        pos.rx() -= m_scrollOffset;
-        bounds.moveTo(pos);
-        return bounds;
+        QPointF pos(y, x);
+        sizeHint.transpose();
+        if (QGuiApplication::isRightToLeft()) {
+            pos.rx() = m_size.width() - 1 + m_scrollOffset - pos.x() - sizeHint.width();
+        } else {
+            pos.rx() -= m_scrollOffset;
+        }
+        return QRectF(pos, sizeHint);
+    }
+
+    if (sizeHint.width() <= 0) {
+        // In Details View, a size hint with negative width is used internally.
+        sizeHint.rwidth() = m_itemSize.width();
     }
 
-    QRectF bounds = m_itemInfos[index].rect;
-    bounds.moveTo(bounds.topLeft() - QPointF(m_itemOffset, m_scrollOffset));
-    return bounds;
+    const QPointF pos(x - m_itemOffset, y - m_scrollOffset);
+    return QRectF(pos, sizeHint);
 }
 
 QRectF KItemListViewLayouter::groupHeaderRect(int index) const
 {
-    const_cast<KItemListViewLayouter*>(this)->doLayout();
+    const_cast<KItemListViewLayouter *>(this)->doLayout();
 
     const QRectF firstItemRect = itemRect(index);
     QPointF pos = firstItemRect.topLeft();
@@ -263,61 +261,65 @@ QRectF KItemListViewLayouter::groupHeaderRect(int index) const
         pos.ry() -= m_groupHeaderHeight;
         size = QSizeF(m_size.width(), m_groupHeaderHeight);
     } else {
-        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_scrollOrientation == Qt::Vertical) ? m_sizeHintResolver->sizeHint(index).width() : m_sizeHintResolver->sizeHint(index).height();
+
+            if (itemWidth > headerWidth) {
+                headerWidth = itemWidth;
             }
 
             ++index;
         }
 
-        size = QSizeF(width, m_size.height());
+        size = QSizeF(headerWidth, m_size.height());
+
+        if (QGuiApplication::isRightToLeft()) {
+            pos.setX(firstItemRect.right() + m_itemMargin.width() - size.width());
+        } else {
+            pos.rx() -= m_itemMargin.width();
+        }
     }
+
     return QRectF(pos, size);
 }
 
 int KItemListViewLayouter::itemColumn(int index) const
 {
-    const_cast<KItemListViewLayouter*>(this)->doLayout();
+    const_cast<KItemListViewLayouter *>(this)->doLayout();
     if (index < 0 || index >= m_itemInfos.count()) {
         return -1;
     }
 
-    return (m_scrollOrientation == Qt::Vertical)
-            ? m_itemInfos[index].column
-            : m_itemInfos[index].row;
+    return (m_scrollOrientation == Qt::Vertical) ? m_itemInfos[index].column : m_itemInfos[index].row;
 }
 
 int KItemListViewLayouter::itemRow(int index) const
 {
-    const_cast<KItemListViewLayouter*>(this)->doLayout();
+    const_cast<KItemListViewLayouter *>(this)->doLayout();
     if (index < 0 || index >= m_itemInfos.count()) {
         return -1;
     }
 
-    return (m_scrollOrientation == Qt::Vertical)
-            ? m_itemInfos[index].row
-            : m_itemInfos[index].column;
+    return (m_scrollOrientation == Qt::Vertical) ? m_itemInfos[index].row : m_itemInfos[index].column;
 }
 
 int KItemListViewLayouter::maximumVisibleItems() const
 {
-    const_cast<KItemListViewLayouter*>(this)->doLayout();
+    const_cast<KItemListViewLayouter *>(this)->doLayout();
 
     const int height = static_cast<int>(m_size.height());
     const int rowHeight = static_cast<int>(m_itemSize.height());
@@ -331,7 +333,7 @@ int KItemListViewLayouter::maximumVisibleItems() const
 
 bool KItemListViewLayouter::isFirstGroupItem(int itemIndex) const
 {
-    const_cast<KItemListViewLayouter*>(this)->doLayout();
+    const_cast<KItemListViewLayouter *>(this)->doLayout();
     return m_groupItemIndexes.contains(itemIndex);
 }
 
@@ -340,194 +342,204 @@ void KItemListViewLayouter::markAsDirty()
     m_dirty = true;
 }
 
+void KItemListViewLayouter::setStatusBarOffset(int offset)
+{
+    if (m_statusBarOffset != offset) {
+        m_statusBarOffset = offset;
+    }
+}
 
 #ifndef QT_NO_DEBUG
-    bool KItemListViewLayouter::isDirty()
-    {
-        return m_dirty;
-    }
+bool KItemListViewLayouter::isDirty()
+{
+    return m_dirty;
+}
 #endif
 
 void KItemListViewLayouter::doLayout()
 {
-    if (m_dirty) {
-#ifdef KITEMLISTVIEWLAYOUTER_DEBUG
-        QElapsedTimer timer;
-        timer.start();
-#endif
-        m_visibleIndexesDirty = true;
+    // we always want to update visible indexes after performing a layout
+    auto qsg = qScopeGuard([this] {
+        updateVisibleIndexes();
+    });
 
-        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;
-            }
-        }
+    if (!m_dirty) {
+        return;
+    }
 
-        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 && m_columnWidth >= 32) {
-            // Apply the unused width equally to each column
-            const qreal unusedWidth = widthForColumns - m_columnCount * m_columnWidth;
-            if (unusedWidth > 0) {
-                const qreal columnInc = unusedWidth / (m_columnCount + 1);
-                m_columnWidth += columnInc;
-                m_xPosInc += columnInc;
-            }
+#ifdef KITEMLISTVIEWLAYOUTER_DEBUG
+    QElapsedTimer timer;
+    timer.start();
+#endif
+    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.transpose();
+        itemMargin.transpose();
+        size.transpose();
+        size.rwidth() -= m_statusBarOffset;
+
+        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;
         }
+    }
 
-        int rowCount = itemCount / m_columnCount;
-        if (itemCount % m_columnCount != 0) {
-            ++rowCount;
+    const bool isRightToLeft = QGuiApplication::isRightToLeft();
+    m_columnWidth = itemSize.width() + itemMargin.width();
+    const qreal widthForColumns = std::max(size.width() - itemMargin.width(), m_columnWidth);
+    m_columnCount = qMax(1, int(widthForColumns / m_columnWidth));
+    m_xPosInc = itemMargin.width();
+
+    const int itemCount = m_model->count();
+    if (itemCount > m_columnCount && m_columnWidth >= 32) {
+        // Apply the unused width equally to each column
+        const qreal unusedWidth = widthForColumns - m_columnCount * m_columnWidth;
+        if (unusedWidth > 0) {
+            const qreal columnInc = unusedWidth / (m_columnCount + 1);
+            m_columnWidth += columnInc;
+            m_xPosInc += columnInc;
         }
+    }
 
-        m_itemInfos.reserve(itemCount);
-
-        qreal y = m_headerHeight + itemMargin.height();
-        int row = 0;
+    m_itemInfos.resize(itemCount);
 
-        int index = 0;
-        while (index < itemCount) {
-            qreal x = m_xPosInc;
-            qreal maxItemHeight = itemSize.height();
+    // Calculate the offset of each column, i.e., the x-coordinate where the column starts.
+    m_columnOffsets.resize(m_columnCount);
+    qreal currentOffset = isRightToLeft && !horizontalScrolling ? widthForColumns : m_xPosInc;
 
-            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 (grouped && horizontalScrolling) {
+        // All group headers will always be aligned on the top and not
+        // flipped like the other properties.
+        currentOffset += m_groupHeaderHeight;
+    }
 
-                if (m_groupItemIndexes.contains(index)) {
-                    // The item is the first item of a group.
-                    // Increase the y-position to provide space
-                    // for the group header.
-                    if (index > 0) {
-                        // Only add a margin if there has been added another
-                        // group already before
-                        y += m_groupHeaderMargin;
-                    } else if (!horizontalScrolling) {
-                        // The first group header should be aligned on top
-                        y -= itemMargin.height();
-                    }
-
-                    if (!horizontalScrolling) {
-                        y += m_groupHeaderHeight;
-                    }
-                }
+    if (isRightToLeft) {
+        for (int column = 0; column < m_columnCount; ++column) {
+            if (horizontalScrolling) {
+                m_columnOffsets[column] = currentOffset + column * m_columnWidth;
+            } else {
+                currentOffset -= m_columnWidth;
+                m_columnOffsets[column] = currentOffset;
             }
+        }
+    } else {
+        for (int column = 0; column < m_columnCount; ++column) {
+            m_columnOffsets[column] = currentOffset;
+            currentOffset += m_columnWidth;
+        }
+    }
 
-            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;
-                    }
+    // 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 maxItemHeight = itemSize.height();
+
+        if (grouped) {
+            if (m_groupItemIndexes.contains(index)) {
+                // The item is the first item of a group.
+                // Increase the y-position to provide space
+                // for the group header.
+                if (index > 0) {
+                    // Only add a margin if there has been added another
+                    // group already before
+                    y += m_groupHeaderMargin;
+                } else if (!horizontalScrolling) {
+                    // The first group header should be aligned on top
+                    y -= itemMargin.height();
                 }
 
-                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);
+                if (!horizontalScrolling) {
+                    y += m_groupHeaderHeight;
                 }
+            }
+        }
 
-                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;
-                    }
-                }
+        m_rowOffsets[row] = y;
 
-                maxItemHeight = qMax(maxItemHeight, requiredItemHeight);
-                x += m_columnWidth;
-                ++index;
-                ++column;
+        int column = 0;
+        while (index < itemCount && column < m_columnCount) {
+            qreal requiredItemHeight = itemSize.height();
+            const QSizeF sizeHint = m_sizeHintResolver->sizeHint(index);
+            const qreal sizeHintHeight = sizeHint.height();
+            if (sizeHintHeight > requiredItemHeight) {
+                requiredItemHeight = sizeHintHeight;
+            }
 
-                if (grouped && m_groupItemIndexes.contains(index)) {
-                    // The item represents the first index of a group
-                    // and must aligned in the first column
-                    break;
+            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
+                // 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;
                 }
             }
 
-            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();
+            maxItemHeight = qMax(maxItemHeight, requiredItemHeight);
+            ++index;
+            ++column;
 
-            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;
+            if (grouped && m_groupItemIndexes.contains(index)) {
+                // The item represents the first index of a group
+                // and must aligned in the first column
+                break;
             }
+        }
 
-            m_maximumScrollOffset += itemMargin.height();
+        y += maxItemHeight + itemMargin.height();
+        ++row;
+    }
 
-            m_maximumItemOffset = m_columnCount * m_columnWidth;
-        } else {
-            m_maximumScrollOffset = 0;
-            m_maximumItemOffset = 0;
-        }
+    if (itemCount > 0) {
+        m_maximumScrollOffset = y;
+        m_maximumItemOffset = m_columnCount * m_columnWidth;
+    } else {
+        m_maximumScrollOffset = 0;
+        m_maximumItemOffset = 0;
+    }
 
 #ifdef KITEMLISTVIEWLAYOUTER_DEBUG
-        kDebug() << "[TIME] doLayout() for " << m_model->count() << "items:" << timer.elapsed();
+    qCDebug(DolphinDebug) << "[TIME] doLayout() for " << m_model->count() << "items:" << timer.elapsed();
 #endif
-        m_dirty = false;
-    }
-
-    updateVisibleIndexes();
+    m_dirty = false;
 }
 
 void KItemListViewLayouter::updateVisibleIndexes()
@@ -553,7 +565,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;
@@ -563,13 +575,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;
         }
     }
@@ -586,14 +598,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;
@@ -609,7 +621,7 @@ bool KItemListViewLayouter::createGroupHeaders()
 
     m_groupItemIndexes.clear();
 
-    const QList<QPair<int, QVariant> > groups = m_model->groups();
+    const QList<QPair<int, QVariant>> groups = m_model->groups();
     if (groups.isEmpty()) {
         return false;
     }
@@ -627,4 +639,4 @@ qreal KItemListViewLayouter::minimumGroupHeaderWidth() const
     return 100;
 }
 
-#include "kitemlistviewlayouter.moc"
+#include "moc_kitemlistviewlayouter.cpp"