diff options
Diffstat (limited to 'src/kitemviews/private')
| -rw-r--r-- | src/kitemviews/private/kbaloorolesprovider.cpp | 14 | ||||
| -rw-r--r-- | src/kitemviews/private/kitemlistheaderwidget.cpp | 134 | ||||
| -rw-r--r-- | src/kitemviews/private/kitemlistheaderwidget.h | 15 | ||||
| -rw-r--r-- | src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp | 2 | ||||
| -rw-r--r-- | src/kitemviews/private/kitemlistsizehintresolver.cpp | 25 | ||||
| -rw-r--r-- | src/kitemviews/private/kitemlistsizehintresolver.h | 3 | ||||
| -rw-r--r-- | src/kitemviews/private/kitemlistviewlayouter.cpp | 270 |
7 files changed, 286 insertions, 177 deletions
diff --git a/src/kitemviews/private/kbaloorolesprovider.cpp b/src/kitemviews/private/kbaloorolesprovider.cpp index 5c87de712..4c231e2ff 100644 --- a/src/kitemviews/private/kbaloorolesprovider.cpp +++ b/src/kitemviews/private/kbaloorolesprovider.cpp @@ -13,6 +13,7 @@ #include <QCollator> #include <QDebug> +#include <QSize> #include <QTime> namespace { @@ -117,6 +118,18 @@ QHash<QByteArray, QVariant> KBalooRolesProvider::roleValues(const Baloo::File& f rangeBegin = rangeEnd; } + if (roles.contains("dimensions")) { + bool widthOk = false; + bool heightOk = false; + + const int width = propMap.value(KFileMetaData::Property::Width).toInt(&widthOk); + const int height = propMap.value(KFileMetaData::Property::Height).toInt(&heightOk); + + if (widthOk && heightOk && width >= 0 && height >= 0) { + values.insert("dimensions", QSize(width, height)); + } + } + KFileMetaData::UserMetaData::Attributes attributes; if (roles.contains("tags")) { attributes |= KFileMetaData::UserMetaData::Tags; @@ -160,6 +173,7 @@ KBalooRolesProvider::KBalooRolesProvider() for (const auto& role : propertyRoleMap()) { m_roles.insert(role); } + m_roles.insert("dimensions"); // Display roles provided by UserMetaData m_roles.insert(QByteArrayLiteral("tags")); diff --git a/src/kitemviews/private/kitemlistheaderwidget.cpp b/src/kitemviews/private/kitemlistheaderwidget.cpp index e5cbc602f..5fb929e52 100644 --- a/src/kitemviews/private/kitemlistheaderwidget.cpp +++ b/src/kitemviews/private/kitemlistheaderwidget.cpp @@ -18,6 +18,7 @@ KItemListHeaderWidget::KItemListHeaderWidget(QGraphicsWidget* parent) : m_automaticColumnResizing(true), m_model(nullptr), m_offset(0), + m_leadingPadding(0), m_columns(), m_columnWidths(), m_preferredColumnWidths(), @@ -134,6 +135,20 @@ qreal KItemListHeaderWidget::offset() const return m_offset; } +void KItemListHeaderWidget::setLeadingPadding(qreal width) +{ + if (m_leadingPadding != width) { + m_leadingPadding = width; + leadingPaddingChanged(width); + update(); + } +} + +qreal KItemListHeaderWidget::leadingPadding() const +{ + return m_leadingPadding; +} + qreal KItemListHeaderWidget::minimumColumnWidth() const { QFontMetricsF fontMetrics(font()); @@ -153,7 +168,7 @@ void KItemListHeaderWidget::paint(QPainter* painter, const QStyleOptionGraphicsI painter->setFont(font()); painter->setPen(palette().text().color()); - qreal x = -m_offset; + qreal x = -m_offset + m_leadingPadding; int orderIndex = 0; for (const QByteArray& role : qAsConst(m_columns)) { const qreal roleWidth = m_columnWidths.value(role); @@ -172,10 +187,14 @@ void KItemListHeaderWidget::paint(QPainter* painter, const QStyleOptionGraphicsI void KItemListHeaderWidget::mousePressEvent(QGraphicsSceneMouseEvent* event) { if (event->button() & Qt::LeftButton) { - updatePressedRoleIndex(event->pos()); m_pressedMousePos = event->pos(); - m_roleOperation = isAboveRoleGrip(m_pressedMousePos, m_pressedRoleIndex) ? - ResizeRoleOperation : NoRoleOperation; + if (isAbovePaddingGrip(m_pressedMousePos, PaddingGrip::Leading)) { + m_roleOperation = ResizeLeadingColumnOperation; + } else { + updatePressedRoleIndex(event->pos()); + m_roleOperation = isAboveRoleGrip(m_pressedMousePos, m_pressedRoleIndex) ? + ResizeRoleOperation : NoRoleOperation; + } event->accept(); } else { event->ignore(); @@ -251,7 +270,7 @@ void KItemListHeaderWidget::mouseMoveEvent(QGraphicsSceneMouseEvent* event) case NoRoleOperation: if ((event->pos() - m_pressedMousePos).manhattanLength() >= QApplication::startDragDistance()) { // A role gets dragged by the user. Create a pixmap of the role that will get - // synchronized on each furter mouse-move-event with the mouse-position. + // synchronized on each further mouse-move-event with the mouse-position. m_roleOperation = MoveRoleOperation; const int roleIndex = roleIndexAt(m_pressedMousePos); m_movingRole.index = roleIndex; @@ -263,7 +282,7 @@ void KItemListHeaderWidget::mouseMoveEvent(QGraphicsSceneMouseEvent* event) } else { m_movingRole.pixmap = createRolePixmap(roleIndex); - qreal roleX = -m_offset; + qreal roleX = -m_offset + m_leadingPadding; for (int i = 0; i < roleIndex; ++i) { const QByteArray role = m_columns[i]; roleX += m_columnWidths.value(role); @@ -291,6 +310,20 @@ void KItemListHeaderWidget::mouseMoveEvent(QGraphicsSceneMouseEvent* event) break; } + case ResizeLeadingColumnOperation: { + qreal currentWidth = m_leadingPadding; + currentWidth += event->pos().x() - event->lastPos().x(); + currentWidth = qMax(0.0, currentWidth); + + m_leadingPadding = currentWidth; + + update(); + + Q_EMIT leadingPaddingChanged(currentWidth); + + break; + } + case MoveRoleOperation: { // TODO: It should be configurable whether moving the first role is allowed. // In the context of Dolphin this is not required, however this should be @@ -355,7 +388,9 @@ void KItemListHeaderWidget::hoverMoveEvent(QGraphicsSceneHoverEvent* event) const QPointF& pos = event->pos(); updateHoveredRoleIndex(pos); - if (m_hoveredRoleIndex >= 0 && isAboveRoleGrip(pos, m_hoveredRoleIndex)) { + if ((m_hoveredRoleIndex >= 0 && isAboveRoleGrip(pos, m_hoveredRoleIndex)) || + isAbovePaddingGrip(pos, PaddingGrip::Leading) || + isAbovePaddingGrip(pos, PaddingGrip::Trailing)) { setCursor(Qt::SplitHCursor); } else { unsetCursor(); @@ -385,6 +420,12 @@ void KItemListHeaderWidget::paintRole(QPainter* painter, // The following code is based on the code from QHeaderView::paintSection(). // SPDX-FileCopyrightText: 2011 Nokia Corporation and/or its subsidiary(-ies). QStyleOptionHeader option; + option.direction = widget->layoutDirection(); + option.textAlignment = + widget->layoutDirection() == Qt::LeftToRight + ? Qt::AlignLeft + : Qt::AlignRight; + option.section = orderIndex; option.state = QStyle::State_None | QStyle::State_Raised | QStyle::State_Horizontal; if (isEnabled()) { @@ -404,19 +445,39 @@ void KItemListHeaderWidget::paintRole(QPainter* painter, QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp; } option.rect = rect.toRect(); + option.orientation = Qt::Horizontal; + option.selectedPosition = QStyleOptionHeader::NotAdjacent; + option.text = m_model->roleDescription(role); - bool paintBackgroundForEmptyArea = false; + // First we paint any potential empty (padding) space on left and/or right of this role's column. + const auto paintPadding = [&](int section, const QRectF &rect, const QStyleOptionHeader::SectionPosition &pos){ + QStyleOptionHeader padding; + padding.state = QStyle::State_None | QStyle::State_Raised | QStyle::State_Horizontal; + padding.section = section; + padding.sortIndicator = QStyleOptionHeader::None; + padding.rect = rect.toRect(); + padding.position = pos; + padding.text = QString(); + style()->drawControl(QStyle::CE_Header, &padding, painter, widget); + }; if (m_columns.count() == 1) { - option.position = QStyleOptionHeader::OnlyOneSection; + option.position = QStyleOptionHeader::Middle; + paintPadding(0, QRectF(0.0, 0.0, rect.left(), rect.height()), QStyleOptionHeader::Beginning); + paintPadding(1, QRectF(rect.left(), 0.0, size().width() - rect.left(), rect.height()), QStyleOptionHeader::End); } else if (orderIndex == 0) { - option.position = QStyleOptionHeader::Beginning; + // Paint the header for the first column; check if there is some empty space to the left which needs to be filled. + if (rect.left() > 0) { + option.position = QStyleOptionHeader::Middle; + paintPadding(0,QRectF(0.0, 0.0, rect.left(), rect.height()), QStyleOptionHeader::Beginning); + } else { + option.position = QStyleOptionHeader::Beginning; + } } else if (orderIndex == m_columns.count() - 1) { - // We are just painting the header for the last column. Check if there - // is some empty space to the right which needs to be filled. + // Paint the header for the last column; check if there is some empty space to the right which needs to be filled. if (rect.right() < size().width()) { option.position = QStyleOptionHeader::Middle; - paintBackgroundForEmptyArea = true; + paintPadding(m_columns.count(), QRectF(rect.left(), 0.0, size().width() - rect.left(), rect.height()), QStyleOptionHeader::End); } else { option.position = QStyleOptionHeader::End; } @@ -424,25 +485,7 @@ void KItemListHeaderWidget::paintRole(QPainter* painter, option.position = QStyleOptionHeader::Middle; } - option.orientation = Qt::Horizontal; - option.selectedPosition = QStyleOptionHeader::NotAdjacent; - option.text = m_model->roleDescription(role); - style()->drawControl(QStyle::CE_Header, &option, painter, widget); - - if (paintBackgroundForEmptyArea) { - option.state = QStyle::State_None | QStyle::State_Raised | QStyle::State_Horizontal; - option.section = m_columns.count(); - option.sortIndicator = QStyleOptionHeader::None; - - qreal backgroundRectX = rect.x() + rect.width(); - QRectF backgroundRect(backgroundRectX, 0.0, size().width() - backgroundRectX, rect.height()); - option.rect = backgroundRect.toRect(); - option.position = QStyleOptionHeader::End; - option.text = QString(); - - style()->drawControl(QStyle::CE_Header, &option, painter, widget); - } } void KItemListHeaderWidget::updatePressedRoleIndex(const QPointF& pos) @@ -467,7 +510,7 @@ int KItemListHeaderWidget::roleIndexAt(const QPointF& pos) const { int index = -1; - qreal x = -m_offset; + qreal x = -m_offset + m_leadingPadding; for (const QByteArray& role : qAsConst(m_columns)) { ++index; x += m_columnWidths.value(role); @@ -481,7 +524,7 @@ int KItemListHeaderWidget::roleIndexAt(const QPointF& pos) const bool KItemListHeaderWidget::isAboveRoleGrip(const QPointF& pos, int roleIndex) const { - qreal x = -m_offset; + qreal x = -m_offset + m_leadingPadding; for (int i = 0; i <= roleIndex; ++i) { const QByteArray role = m_columns[i]; x += m_columnWidths.value(role); @@ -491,6 +534,27 @@ bool KItemListHeaderWidget::isAboveRoleGrip(const QPointF& pos, int roleIndex) c return pos.x() >= (x - grip) && pos.x() <= x; } +bool KItemListHeaderWidget::isAbovePaddingGrip(const QPointF& pos, PaddingGrip paddingGrip) const +{ + const qreal lx = -m_offset + m_leadingPadding; + const int grip = style()->pixelMetric(QStyle::PM_HeaderGripMargin); + + switch (paddingGrip) { + case Leading: + return pos.x() >= (lx - grip) && pos.x() <= lx; + case Trailing: + { + qreal rx = lx; + for (const QByteArray& role : qAsConst(m_columns)) { + rx += m_columnWidths.value(role); + } + return pos.x() >= (rx - grip) && pos.x() <= rx; + } + default: + return false; + } +} + QPixmap KItemListHeaderWidget::createRolePixmap(int roleIndex) const { const QByteArray role = m_columns[roleIndex]; @@ -522,7 +586,7 @@ int KItemListHeaderWidget::targetOfMovingRole() const const int movingRight = movingLeft + movingWidth - 1; int targetIndex = 0; - qreal targetLeft = -m_offset; + qreal targetLeft = -m_offset + m_leadingPadding; while (targetIndex < m_columns.count()) { const QByteArray role = m_columns[targetIndex]; const qreal targetWidth = m_columnWidths.value(role); @@ -548,7 +612,7 @@ int KItemListHeaderWidget::targetOfMovingRole() const qreal KItemListHeaderWidget::roleXPosition(const QByteArray& role) const { - qreal x = -m_offset; + qreal x = -m_offset + m_leadingPadding; for (const QByteArray& visibleRole : qAsConst(m_columns)) { if (visibleRole == role) { return x; diff --git a/src/kitemviews/private/kitemlistheaderwidget.h b/src/kitemviews/private/kitemlistheaderwidget.h index 44adc23c5..58f0dc98e 100644 --- a/src/kitemviews/private/kitemlistheaderwidget.h +++ b/src/kitemviews/private/kitemlistheaderwidget.h @@ -50,6 +50,9 @@ public: void setOffset(qreal offset); qreal offset() const; + void setLeadingPadding(qreal width); + qreal leadingPadding() const; + qreal minimumColumnWidth() const; void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override; @@ -63,6 +66,8 @@ Q_SIGNALS: qreal currentWidth, qreal previousWidth); + void leadingPaddingChanged(qreal width); + /** * Is emitted if the user has released the mouse button after adjusting the * width of a visible role. @@ -105,6 +110,13 @@ private Q_SLOTS: void slotSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous); private: + + enum PaddingGrip + { + Leading, + Trailing, + }; + void paintRole(QPainter* painter, const QByteArray& role, const QRectF& rect, @@ -115,6 +127,7 @@ private: void updateHoveredRoleIndex(const QPointF& pos); int roleIndexAt(const QPointF& pos) const; bool isAboveRoleGrip(const QPointF& pos, int roleIndex) const; + bool isAbovePaddingGrip(const QPointF& pos, PaddingGrip paddingGrip) const; /** * Creates a pixmap of the role with the index \a roleIndex that is shown @@ -138,12 +151,14 @@ private: { NoRoleOperation, ResizeRoleOperation, + ResizeLeadingColumnOperation, MoveRoleOperation }; bool m_automaticColumnResizing; KItemModelBase* m_model; qreal m_offset; + qreal m_leadingPadding; QList<QByteArray> m_columns; QHash<QByteArray, qreal> m_columnWidths; QHash<QByteArray, qreal> m_preferredColumnWidths; diff --git a/src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp b/src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp index 57a954adf..0e6280ede 100644 --- a/src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp +++ b/src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp @@ -55,7 +55,7 @@ void KItemListKeyboardSearchManager::addKeys(const QString& keys) const bool searchFromNextItem = (!m_isSearchRestarted && newSearch) || sameKey; // to remember not to searchFromNextItem if selection was deselected - // loosing keyboard search context basically + // losing keyboard search context basically m_isSearchRestarted = false; Q_EMIT changeCurrentItem(sameKey ? firstKey : m_searchedString, searchFromNextItem); diff --git a/src/kitemviews/private/kitemlistsizehintresolver.cpp b/src/kitemviews/private/kitemlistsizehintresolver.cpp index 7deb5c621..0c2dd0b80 100644 --- a/src/kitemviews/private/kitemlistsizehintresolver.cpp +++ b/src/kitemviews/private/kitemlistsizehintresolver.cpp @@ -29,7 +29,12 @@ QSizeF KItemListSizeHintResolver::minSizeHint() QSizeF KItemListSizeHintResolver::sizeHint(int index) { updateCache(); - return QSizeF(m_logicalWidthHint, m_logicalHeightHintCache.at(index)); + return QSizeF(m_logicalWidthHint, m_logicalHeightHintCache.at(index).first); +} + +bool KItemListSizeHintResolver::isElided(int index) +{ + return m_logicalHeightHintCache.at(index).second; } void KItemListSizeHintResolver::itemsInserted(const KItemRangeList& itemRanges) @@ -44,7 +49,7 @@ void KItemListSizeHintResolver::itemsInserted(const KItemRangeList& itemRanges) // We build the new list from the end to the beginning to mimize the // number of moves. - m_logicalHeightHintCache.insert(m_logicalHeightHintCache.end(), insertedCount, 0.0); + m_logicalHeightHintCache.insert(m_logicalHeightHintCache.end(), insertedCount, std::make_pair(0.0, false)); int sourceIndex = currentCount - 1; int targetIndex = m_logicalHeightHintCache.count() - 1; @@ -63,7 +68,7 @@ void KItemListSizeHintResolver::itemsInserted(const KItemRangeList& itemRanges) // Then: insert QSizeF() for the items which are inserted into 'range'. while (targetIndex >= itemsToInsertBeforeCurrentRange + range.index) { - m_logicalHeightHintCache[targetIndex] = 0.0; + m_logicalHeightHintCache[targetIndex] = std::make_pair(0.0, false); --targetIndex; } } @@ -75,14 +80,14 @@ void KItemListSizeHintResolver::itemsInserted(const KItemRangeList& itemRanges) void KItemListSizeHintResolver::itemsRemoved(const KItemRangeList& itemRanges) { - const QVector<qreal>::iterator begin = m_logicalHeightHintCache.begin(); - const QVector<qreal>::iterator end = m_logicalHeightHintCache.end(); + const QVector<std::pair<qreal, bool>>::iterator begin = m_logicalHeightHintCache.begin(); + const QVector<std::pair<qreal, bool>>::iterator end = m_logicalHeightHintCache.end(); KItemRangeList::const_iterator rangeIt = itemRanges.constBegin(); const KItemRangeList::const_iterator rangeEnd = itemRanges.constEnd(); - QVector<qreal>::iterator destIt = begin + rangeIt->index; - QVector<qreal>::iterator srcIt = destIt + rangeIt->count; + QVector<std::pair<qreal, bool>>::iterator destIt = begin + rangeIt->index; + QVector<std::pair<qreal, bool>>::iterator srcIt = destIt + rangeIt->count; ++rangeIt; @@ -109,7 +114,7 @@ void KItemListSizeHintResolver::itemsRemoved(const KItemRangeList& itemRanges) void KItemListSizeHintResolver::itemsMoved(const KItemRange& range, const QList<int>& movedToIndexes) { - QVector<qreal> newLogicalHeightHintCache(m_logicalHeightHintCache); + QVector<std::pair<qreal, bool>> newLogicalHeightHintCache(m_logicalHeightHintCache); const int movedRangeEnd = range.index + range.count; for (int i = range.index; i < movedRangeEnd; ++i) { @@ -124,7 +129,7 @@ void KItemListSizeHintResolver::itemsChanged(int index, int count, const QSet<QB { Q_UNUSED(roles) while (count) { - m_logicalHeightHintCache[index] = 0.0; + m_logicalHeightHintCache[index] = std::make_pair(0.0, false); ++index; --count; } @@ -134,7 +139,7 @@ void KItemListSizeHintResolver::itemsChanged(int index, int count, const QSet<QB void KItemListSizeHintResolver::clearCache() { - m_logicalHeightHintCache.fill(0.0); + m_logicalHeightHintCache.fill(std::make_pair(0.0, false)); m_needsResolving = true; } diff --git a/src/kitemviews/private/kitemlistsizehintresolver.h b/src/kitemviews/private/kitemlistsizehintresolver.h index 9a0ab1f5b..a6cc56614 100644 --- a/src/kitemviews/private/kitemlistsizehintresolver.h +++ b/src/kitemviews/private/kitemlistsizehintresolver.h @@ -25,6 +25,7 @@ public: virtual ~KItemListSizeHintResolver(); QSizeF minSizeHint(); QSizeF sizeHint(int index); + bool isElided(int index); void itemsInserted(const KItemRangeList& itemRanges); void itemsRemoved(const KItemRangeList& itemRanges); @@ -36,7 +37,7 @@ public: private: const KItemListView* m_itemListView; - mutable QVector<qreal> m_logicalHeightHintCache; + mutable QVector<std::pair<qreal /* height */, bool /* isElided */>> m_logicalHeightHintCache; mutable qreal m_logicalWidthHint; mutable qreal m_minHeightHint; bool m_needsResolving; diff --git a/src/kitemviews/private/kitemlistviewlayouter.cpp b/src/kitemviews/private/kitemlistviewlayouter.cpp index 6de83ca87..4c22b4dbc 100644 --- a/src/kitemviews/private/kitemlistviewlayouter.cpp +++ b/src/kitemviews/private/kitemlistviewlayouter.cpp @@ -9,6 +9,9 @@ #include "kitemlistsizehintresolver.h" #include "kitemviews/kitemmodelbase.h" +#include <QGuiApplication> +#include <QScopeGuard> + // #define KITEMLISTVIEWLAYOUTER_DEBUG KItemListViewLayouter::KItemListViewLayouter(KItemListSizeHintResolver* sizeHintResolver, QObject* parent) : @@ -343,170 +346,177 @@ void KItemListViewLayouter::markAsDirty() void KItemListViewLayouter::doLayout() { - if (m_dirty) { + // we always want to update visible indexes after performing a layout + auto qsg = qScopeGuard([this] { updateVisibleIndexes(); }); + + if (!m_dirty) { + return; + } + #ifdef KITEMLISTVIEWLAYOUTER_DEBUG - QElapsedTimer timer; - timer.start(); + QElapsedTimer timer; + timer.start(); #endif - m_visibleIndexesDirty = true; + m_visibleIndexesDirty = true; - QSizeF itemSize = m_itemSize; - QSizeF itemMargin = m_itemMargin; - QSizeF size = m_size; + QSizeF itemSize = m_itemSize; + QSizeF itemMargin = m_itemMargin; + QSizeF size = m_size; - const bool grouped = createGroupHeaders(); + 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(); + 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(); - 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 (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() + itemMargin.width(); - const qreal widthForColumns = size.width() - itemMargin.width(); - m_columnCount = qMax(1, int(widthForColumns / m_columnWidth)); - m_xPosInc = itemMargin.width(); + 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; - } + 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.resize(itemCount); + 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; + // Calculate the offset of each column, i.e., the x-coordinate where the column starts. + m_columnOffsets.resize(m_columnCount); + qreal currentOffset = QGuiApplication::isRightToLeft() ? widthForColumns : 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; - } + if (grouped && horizontalScrolling) { + // All group headers will always be aligned on the top and not + // flipped like the other properties. + currentOffset += m_groupHeaderHeight; + } - for (int column = 0; column < m_columnCount; ++column) { - m_columnOffsets[column] = currentOffset; - currentOffset += m_columnWidth; - } + if (QGuiApplication::isLeftToRight()) for (int column = 0; column < m_columnCount; ++column) { + m_columnOffsets[column] = currentOffset; + currentOffset += m_columnWidth; + } + else for (int column = 0; column < m_columnCount; ++column) { + m_columnOffsets[column] = currentOffset - m_columnWidth; + 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); + // 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; + qreal y = m_headerHeight + itemMargin.height(); + int row = 0; - int index = 0; - while (index < itemCount) { - qreal maxItemHeight = itemSize.height(); + 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(); - } + 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(); + } - if (!horizontalScrolling) { - y += m_groupHeaderHeight; - } + if (!horizontalScrolling) { + y += m_groupHeaderHeight; } } + } - m_rowOffsets[row] = y; + m_rowOffsets[row] = y; - 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; - } + 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; + } - ItemInfo& itemInfo = m_itemInfos[index]; - itemInfo.column = column; - itemInfo.row = row; + 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; - } + 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); - ++index; - ++column; + maxItemHeight = qMax(maxItemHeight, requiredItemHeight); + ++index; + ++column; - if (grouped && m_groupItemIndexes.contains(index)) { - // The item represents the first index of a group - // and must aligned in the first column - break; - } + if (grouped && m_groupItemIndexes.contains(index)) { + // The item represents the first index of a group + // and must aligned in the first column + break; } - - y += maxItemHeight + itemMargin.height(); - ++row; } - if (itemCount > 0) { - m_maximumScrollOffset = y; - m_maximumItemOffset = m_columnCount * m_columnWidth; - } else { - m_maximumScrollOffset = 0; - m_maximumItemOffset = 0; - } + y += maxItemHeight + itemMargin.height(); + ++row; + } -#ifdef KITEMLISTVIEWLAYOUTER_DEBUG - qCDebug(DolphinDebug) << "[TIME] doLayout() for " << m_model->count() << "items:" << timer.elapsed(); -#endif - m_dirty = false; + if (itemCount > 0) { + m_maximumScrollOffset = y; + m_maximumItemOffset = m_columnCount * m_columnWidth; + } else { + m_maximumScrollOffset = 0; + m_maximumItemOffset = 0; } - updateVisibleIndexes(); +#ifdef KITEMLISTVIEWLAYOUTER_DEBUG + qCDebug(DolphinDebug) << "[TIME] doLayout() for " << m_model->count() << "items:" << timer.elapsed(); +#endif + m_dirty = false; } void KItemListViewLayouter::updateVisibleIndexes() |
