m_columnWidth(0),
m_xPosInc(0),
m_columnCount(0),
+ m_rowOffsets(),
+ m_columnOffsets(),
m_groupItemIndexes(),
m_groupHeaderHeight(0),
m_groupHeaderMargin(0),
return QRectF();
}
+ QSizeF sizeHint;
+ if (m_sizeHintResolver) {
+ sizeHint = m_sizeHintResolver->sizeHint(index);
+ } else {
+ sizeHint = m_itemSize;
+ }
+
+ 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);
+ }
+
+ 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
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();
+ qreal itemWidth;
+ if (m_sizeHintResolver) {
+ itemWidth = m_sizeHintResolver->sizeHint(index).width();
+ } else {
+ itemWidth = m_itemSize.width();
+ }
+
+ if (itemWidth > headerWidth) {
+ headerWidth = itemWidth;
}
++index;
}
- size = QSizeF(width, m_size.height());
+ size = QSizeF(headerWidth, m_size.height());
}
return QRectF(pos, size);
}
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
}
}
- 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.reserve(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();
}
}
- 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
}
maxItemHeight = qMax(maxItemHeight, requiredItemHeight);
- x += m_columnWidth;
++index;
++column;
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;
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;