-/***************************************************************************
- * 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 <KDebug>
+#include "kitemviews/kitemmodelbase.h"
// #define KITEMLISTVIEWLAYOUTER_DEBUG
-KItemListViewLayouter::KItemListViewLayouter(QObject* parent) :
+KItemListViewLayouter::KItemListViewLayouter(KItemListSizeHintResolver* sizeHintResolver, QObject* parent) :
QObject(parent),
m_dirty(true),
m_visibleIndexesDirty(true),
m_itemSize(128, 128),
m_itemMargin(),
m_headerHeight(0),
- m_model(0),
- m_sizeHintResolver(0),
+ m_model(nullptr),
+ m_sizeHintResolver(sizeHintResolver),
m_scrollOffset(0),
m_maximumScrollOffset(0),
m_itemOffset(0),
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()
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();
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;
+ sizeHint.transpose();
+ 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
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());
}
return QRectF(pos, size);
}
}
}
- 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;
}
- m_itemInfos.resize(itemCount);
+ for (int column = 0; column < m_columnCount; ++column) {
+ m_columnOffsets[column] = currentOffset;
+ currentOffset += m_columnWidth;
+ }
+
+ // 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
}
}
+ 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 = sizeHint.height();
+ if (sizeHintHeight > requiredItemHeight) {
+ requiredItemHeight = sizeHintHeight;
}
ItemInfo& itemInfo = m_itemInfos[index];
- itemInfo.rect = QRectF(x, y, itemSize.width(), requiredItemHeight);
itemInfo.column = column;
itemInfo.row = row;
}
maxItemHeight = qMax(maxItemHeight, requiredItemHeight);
- x += m_columnWidth;
++index;
++column;
}
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;
}
#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;
}
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;
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;
}
}
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;
return 100;
}
-#include "kitemlistviewlayouter.moc"