X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/83c5692f5e33c2bb239e1122811ce64ed1f144a3..dbc5fd7a491f95ca4084a113d0f902ea975478fd:/src/kitemviews/kitemlistviewlayouter.cpp diff --git a/src/kitemviews/kitemlistviewlayouter.cpp b/src/kitemviews/kitemlistviewlayouter.cpp index b486a390b..d8b5796ec 100644 --- a/src/kitemviews/kitemlistviewlayouter.cpp +++ b/src/kitemviews/kitemlistviewlayouter.cpp @@ -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), @@ -47,7 +48,8 @@ KItemListViewLayouter::KItemListViewLayouter(QObject* parent) : m_columnCount(0), m_groupItemIndexes(), m_groupHeaderHeight(0), - m_itemRects() + m_groupHeaderMargin(0), + m_itemInfos() { } @@ -94,6 +96,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) { @@ -120,6 +135,19 @@ qreal KItemListViewLayouter::groupHeaderHeight() const return m_groupHeaderHeight; } +void KItemListViewLayouter::setGroupHeaderMargin(qreal margin) +{ + if (m_groupHeaderMargin != margin) { + m_groupHeaderMargin = margin; + m_dirty = true; + } +} + +qreal KItemListViewLayouter::groupHeaderMargin() const +{ + return m_groupHeaderMargin; +} + void KItemListViewLayouter::setScrollOffset(qreal offset) { if (m_scrollOffset != offset) { @@ -199,14 +227,14 @@ int KItemListViewLayouter::lastVisibleIndex() const QRectF KItemListViewLayouter::itemRect(int index) const { const_cast(this)->doLayout(); - if (index < 0 || index >= m_itemRects.count()) { + if (index < 0 || index >= m_itemInfos.count()) { return QRectF(); } 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_itemRects[index]; + 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; @@ -214,31 +242,79 @@ QRectF KItemListViewLayouter::itemRect(int index) const return bounds; } - QRectF bounds = m_itemRects[index]; + QRectF bounds = m_itemInfos[index].rect; bounds.moveTo(bounds.topLeft() - QPointF(m_itemOffset, m_scrollOffset)); return bounds; } QRectF KItemListViewLayouter::groupHeaderRect(int index) const { + const_cast(this)->doLayout(); + const QRectF firstItemRect = itemRect(index); QPointF pos = firstItemRect.topLeft(); if (pos.isNull()) { return QRectF(); } - pos.ry() -= m_groupHeaderHeight; - QSizeF size; if (m_scrollOrientation == Qt::Vertical) { pos.rx() = 0; + pos.ry() -= m_groupHeaderHeight; size = QSizeF(m_size.width(), m_groupHeaderHeight); } else { - size = QSizeF(firstItemRect.width(), m_groupHeaderHeight); + 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(); + const int maxIndex = m_itemInfos.count() - 1; + while (index <= maxIndex) { + QRectF bounds = m_itemInfos[index].rect; + if (bounds.y() != y) { + break; + } + + if (bounds.height() > width) { + width = bounds.height(); + } + + ++index; + } + + size = QSizeF(width, m_size.height()); } return QRectF(pos, size); } +int KItemListViewLayouter::itemColumn(int index) const +{ + const_cast(this)->doLayout(); + if (index < 0 || index >= m_itemInfos.count()) { + return -1; + } + + return (m_scrollOrientation == Qt::Vertical) + ? m_itemInfos[index].column + : m_itemInfos[index].row; +} + +int KItemListViewLayouter::itemRow(int index) const +{ + const_cast(this)->doLayout(); + if (index < 0 || index >= m_itemInfos.count()) { + return -1; + } + + return (m_scrollOrientation == Qt::Vertical) + ? m_itemInfos[index].row + : m_itemInfos[index].column; +} + int KItemListViewLayouter::maximumVisibleItems() const { const_cast(this)->doLayout(); @@ -264,6 +340,14 @@ void KItemListViewLayouter::markAsDirty() m_dirty = true; } + +#ifndef QT_NO_DEBUG + bool KItemListViewLayouter::isDirty() + { + return m_dirty; + } +#endif + void KItemListViewLayouter::doLayout() { if (m_dirty) { @@ -274,29 +358,41 @@ 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; + const qreal unusedWidth = widthForColumns - m_columnCount * m_columnWidth; if (unusedWidth > 0) { - // [Comment #1] A cast to int is done on purpose to prevent rounding issues when - // drawing pixmaps and drawing text or other graphic primitives: Qt uses a different - // rastering algorithm for the upper/left of pixmaps - const qreal columnInc = int(unusedWidth / (m_columnCount + 1)); + const qreal columnInc = unusedWidth / (m_columnCount + 1); m_columnWidth += columnInc; m_xPosInc += columnInc; } @@ -307,12 +403,10 @@ void KItemListViewLayouter::doLayout() ++rowCount; } - m_itemRects.reserve(itemCount); + m_itemInfos.reserve(itemCount); - qreal y = m_headerHeight; - int rowIndex = 0; - - const bool grouped = createGroupHeaders(); + qreal y = m_headerHeight + itemMargin.height(); + int row = 0; int index = 0; while (index < itemCount) { @@ -327,10 +421,19 @@ void KItemListViewLayouter::doLayout() } 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) { - // The item is the first item of a group. - // Increase the y-position to provide space - // for the group header. y += m_groupHeaderHeight; } } @@ -348,10 +451,16 @@ void KItemListViewLayouter::doLayout() } const QRectF bounds(x, y, itemSize.width(), requiredItemHeight); - if (index < m_itemRects.count()) { - m_itemRects[index] = bounds; + if (index < m_itemInfos.count()) { + m_itemInfos[index].rect = bounds; + m_itemInfos[index].column = column; + m_itemInfos[index].row = row; } else { - m_itemRects.append(bounds); + ItemInfo itemInfo; + itemInfo.rect = bounds; + itemInfo.column = column; + itemInfo.row = row; + m_itemInfos.append(itemInfo); } if (grouped && horizontalScrolling) { @@ -367,9 +476,9 @@ void KItemListViewLayouter::doLayout() // (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 minimumGroupHeaderWidth = int(m_groupHeaderHeight * 15 / 2); // See [Comment #1] - if (requiredItemHeight < minimumGroupHeaderWidth) { - requiredItemHeight = minimumGroupHeaderWidth; + const qreal headerWidth = minimumGroupHeaderWidth(); + if (requiredItemHeight < headerWidth) { + requiredItemHeight = headerWidth; } } @@ -385,25 +494,27 @@ void KItemListViewLayouter::doLayout() } } - y += maxItemHeight; - ++rowIndex; + y += maxItemHeight + itemMargin.height(); + ++row; } - if (m_itemRects.count() > itemCount) { - m_itemRects.erase(m_itemRects.begin() + itemCount, - m_itemRects.end()); + 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_itemRects.last().bottom(); - const qreal rowY = m_itemRects.last().y(); + m_maximumScrollOffset = m_itemInfos.last().rect.bottom(); + const qreal rowY = m_itemInfos.last().rect.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()); + 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_maximumItemOffset = m_columnCount * m_columnWidth; } else { m_maximumScrollOffset = 0; @@ -442,7 +553,7 @@ void KItemListViewLayouter::updateVisibleIndexes() int mid = 0; do { mid = (min + max) / 2; - if (m_itemRects[mid].top() < m_scrollOffset) { + if (m_itemInfos[mid].rect.top() < m_scrollOffset) { min = mid + 1; } else { max = mid - 1; @@ -452,13 +563,13 @@ void KItemListViewLayouter::updateVisibleIndexes() 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) { + if (m_itemInfos[mid].rect.top() >= m_scrollOffset) { --mid; - Q_ASSERT(m_itemRects[mid].top() < m_scrollOffset); + Q_ASSERT(m_itemInfos[mid].rect.top() < m_scrollOffset); } - const qreal rowTop = m_itemRects[mid].top(); - while (mid > 0 && m_itemRects[mid - 1].top() == rowTop) { + const qreal rowTop = m_itemInfos[mid].rect.top(); + while (mid > 0 && m_itemInfos[mid - 1].rect.top() == rowTop) { --mid; } } @@ -475,14 +586,14 @@ void KItemListViewLayouter::updateVisibleIndexes() max = maxIndex; do { mid = (min + max) / 2; - if (m_itemRects[mid].y() <= bottom) { + if (m_itemInfos[mid].rect.y() <= bottom) { min = mid + 1; } else { max = mid - 1; } } while (min <= max); - while (mid > 0 && m_itemRects[mid].y() > bottom) { + while (mid > 0 && m_itemInfos[mid].rect.y() > bottom) { --mid; } m_lastVisibleIndex = mid; @@ -511,4 +622,9 @@ bool KItemListViewLayouter::createGroupHeaders() return true; } +qreal KItemListViewLayouter::minimumGroupHeaderWidth() const +{ + return 100; +} + #include "kitemlistviewlayouter_p.moc"