X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/4d9ea4261a1f24e299595b897ea790eab1748fe9..fc965a725cd0cfdca520b1a08e1f144f06aab7d2:/src/kitemviews/kstandarditemlistwidget.cpp diff --git a/src/kitemviews/kstandarditemlistwidget.cpp b/src/kitemviews/kstandarditemlistwidget.cpp index e58340fb8..247581a4e 100644 --- a/src/kitemviews/kstandarditemlistwidget.cpp +++ b/src/kitemviews/kstandarditemlistwidget.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -35,7 +36,7 @@ KStandardItemListWidgetInformant::~KStandardItemListWidgetInformant() { } -void KStandardItemListWidgetInformant::calculateItemSizeHints(QVector& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const +void KStandardItemListWidgetInformant::calculateItemSizeHints(QVector>& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const { switch (static_cast(view)->itemLayout()) { case KStandardItemListView::IconsLayout: @@ -120,7 +121,7 @@ QFont KStandardItemListWidgetInformant::customizedFontForLinks(const QFont& base return baseFont; } -void KStandardItemListWidgetInformant::calculateIconsLayoutItemSizeHints(QVector& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const +void KStandardItemListWidgetInformant::calculateIconsLayoutItemSizeHints(QVector>& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const { const KItemListStyleOption& option = view->styleOption(); const QFont& normalFont = option.font; @@ -137,7 +138,7 @@ void KStandardItemListWidgetInformant::calculateIconsLayoutItemSizeHints(QVector textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); for (int index = 0; index < logicalHeightHints.count(); ++index) { - if (logicalHeightHints.at(index) > 0.0) { + if (logicalHeightHints.at(index).first > 0.0) { continue; } @@ -145,7 +146,7 @@ void KStandardItemListWidgetInformant::calculateIconsLayoutItemSizeHints(QVector const QFont& font = itemIsLink(index, view) ? linkFont : normalFont; const QString& text = KStringHandler::preProcessWrap(itemText(index, view)); - + // Calculate the number of lines required for wrapping the name qreal textHeight = 0; QTextLayout layout(text, font); @@ -153,6 +154,7 @@ void KStandardItemListWidgetInformant::calculateIconsLayoutItemSizeHints(QVector layout.beginLayout(); QTextLine line; int lineCount = 0; + bool isElided = false; while ((line = layout.createLine()).isValid()) { line.setLineWidth(maxWidth); line.naturalTextWidth(); @@ -160,6 +162,7 @@ void KStandardItemListWidgetInformant::calculateIconsLayoutItemSizeHints(QVector ++lineCount; if (lineCount == option.maxTextLines) { + isElided = layout.createLine().isValid(); break; } } @@ -168,13 +171,14 @@ void KStandardItemListWidgetInformant::calculateIconsLayoutItemSizeHints(QVector // Add one line for each additional information textHeight += additionalRolesSpacing; - logicalHeightHints[index] = textHeight + spacingAndIconHeight; + logicalHeightHints[index].first = textHeight + spacingAndIconHeight; + logicalHeightHints[index].second = isElided; } logicalWidthHint = itemWidth; } -void KStandardItemListWidgetInformant::calculateCompactLayoutItemSizeHints(QVector& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const +void KStandardItemListWidgetInformant::calculateCompactLayoutItemSizeHints(QVector>& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const { const KItemListStyleOption& option = view->styleOption(); const QFontMetrics& normalFontMetrics = option.fontMetrics; @@ -189,7 +193,7 @@ void KStandardItemListWidgetInformant::calculateCompactLayoutItemSizeHints(QVect const QFontMetrics linkFontMetrics(customizedFontForLinks(option.font)); for (int index = 0; index < logicalHeightHints.count(); ++index) { - if (logicalHeightHints.at(index) > 0.0) { + if (logicalHeightHints.at(index).first > 0.0) { continue; } @@ -216,17 +220,17 @@ void KStandardItemListWidgetInformant::calculateCompactLayoutItemSizeHints(QVect width = maxWidth; } - logicalHeightHints[index] = width; + logicalHeightHints[index].first = width; } logicalWidthHint = height; } -void KStandardItemListWidgetInformant::calculateDetailsLayoutItemSizeHints(QVector& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const +void KStandardItemListWidgetInformant::calculateDetailsLayoutItemSizeHints(QVector>& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const { const KItemListStyleOption& option = view->styleOption(); const qreal height = option.padding * 2 + qMax(option.iconSize, option.fontMetrics.height()); - logicalHeightHints.fill(height); + logicalHeightHints.fill(std::make_pair(height, false)); logicalWidthHint = -1.0; } @@ -246,6 +250,7 @@ KStandardItemListWidget::KStandardItemListWidget(KItemListWidgetInformant* infor m_pixmapPos(), m_pixmap(), m_scaledPixmapSize(), + m_columnWidthSum(), m_iconRect(), m_hoverPixmap(), m_textRect(), @@ -289,6 +294,18 @@ KStandardItemListWidget::Layout KStandardItemListWidget::layout() const return m_layout; } +void KStandardItemListWidget::setHighlightEntireRow(bool highlightEntireRow) { + if (m_highlightEntireRow != highlightEntireRow) { + m_highlightEntireRow = highlightEntireRow; + m_dirtyLayout = true; + update(); + } +} + +bool KStandardItemListWidget::highlightEntireRow() const { + return m_highlightEntireRow; +} + void KStandardItemListWidget::setSupportsItemExpanding(bool supportsItemExpanding) { if (m_supportsItemExpanding != supportsItemExpanding) { @@ -402,7 +419,7 @@ void KStandardItemListWidget::paint(QPainter* painter, const QStyleOptionGraphic QPointF pos = ratingTextInfo->pos; const Qt::Alignment align = ratingTextInfo->staticText.textOption().alignment(); if (align & Qt::AlignHCenter) { - pos.rx() += (size().width() - m_rating.width()) / 2 - 2; + pos.rx() += (size().width() - m_rating.width() / m_rating.devicePixelRatioF()) / 2 - 2; } painter->drawPixmap(pos, m_rating); } @@ -490,7 +507,11 @@ QRectF KStandardItemListWidget::selectionRect() const case DetailsLayout: { const int padding = styleOption().padding; QRectF adjustedIconRect = iconRect().adjusted(-padding, -padding, padding, padding); - return adjustedIconRect | m_textRect; + QRectF result = adjustedIconRect | m_textRect; + if (m_highlightEntireRow) { + result.setRight(m_columnWidthSum + leadingPadding()); + } + return result; } default: @@ -511,12 +532,11 @@ QRectF KStandardItemListWidget::selectionToggleRect() const { const_cast(this)->triggerCacheRefreshing(); - const int iconHeight = styleOption().iconSize; - + const int widgetIconSize = iconSize(); int toggleSize = KIconLoader::SizeSmall; - if (iconHeight >= KIconLoader::SizeEnormous) { + if (widgetIconSize >= KIconLoader::SizeEnormous) { toggleSize = KIconLoader::SizeMedium; - } else if (iconHeight >= KIconLoader::SizeLarge) { + } else if (widgetIconSize >= KIconLoader::SizeLarge) { toggleSize = KIconLoader::SizeSmallMedium; } @@ -581,6 +601,13 @@ void KStandardItemListWidget::invalidateCache() m_dirtyContent = true; } +void KStandardItemListWidget::invalidateIconCache() +{ + m_dirtyContent = true; + m_dirtyContentRoles.insert("iconPixmap"); + m_dirtyContentRoles.insert("iconOverlays"); +} + void KStandardItemListWidget::refreshCache() { } @@ -700,11 +727,16 @@ void KStandardItemListWidget::columnWidthChanged(const QByteArray& role, m_dirtyLayout = true; } +void KStandardItemListWidget::leadingPaddingChanged(qreal padding) { + Q_UNUSED(padding) + m_dirtyLayout = true; +} + void KStandardItemListWidget::styleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous) { - Q_UNUSED(current) - Q_UNUSED(previous) + KItemListWidget::styleOptionChanged(current, previous); + updateAdditionalInfoTextColor(); m_dirtyLayout = true; } @@ -800,6 +832,15 @@ void KStandardItemListWidget::editedRoleChanged(const QByteArray& current, const m_roleEditor->setFocus(); } +void KStandardItemListWidget::iconSizeChanged(int current, int previous) +{ + KItemListWidget::iconSizeChanged(current, previous); + + invalidateIconCache(); + triggerCacheRefreshing(); + update(); +} + void KStandardItemListWidget::resizeEvent(QGraphicsSceneResizeEvent* event) { if (m_roleEditor) { @@ -892,10 +933,13 @@ void KStandardItemListWidget::triggerCacheRefreshing() m_isHidden = isHidden(); m_customizedFont = customizedFont(styleOption().font); m_customizedFontMetrics = QFontMetrics(m_customizedFont); + m_columnWidthSum = std::accumulate(m_sortedVisibleRoles.begin(), m_sortedVisibleRoles.end(), + qreal(), [this](qreal sum, const auto &role){ return sum + columnWidth(role); }); updateExpansionArea(); updateTextsCache(); updatePixmapCache(); + clearHoverCache(); m_dirtyLayout = false; m_dirtyContent = false; @@ -908,12 +952,13 @@ void KStandardItemListWidget::updateExpansionArea() const QHash values = data(); const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt(); if (expandedParentsCount >= 0) { - const KItemListStyleOption& option = styleOption(); + const int widgetIconSize = iconSize(); const qreal widgetHeight = size().height(); - const qreal inc = (widgetHeight - option.iconSize) / 2; + const qreal inc = (widgetHeight - widgetIconSize) / 2; const qreal x = expandedParentsCount * widgetHeight + inc; const qreal y = inc; - m_expansionArea = QRectF(x, y, option.iconSize, option.iconSize); + const qreal xPadding = m_highlightEntireRow ? leadingPadding() : 0; + m_expansionArea = QRectF(xPadding + x, y, widgetIconSize, widgetIconSize); return; } } @@ -931,8 +976,9 @@ void KStandardItemListWidget::updatePixmapCache() const KItemListStyleOption& option = styleOption(); const qreal padding = option.padding; - const int maxIconWidth = iconOnTop ? widgetSize.width() - 2 * padding : option.iconSize; - const int maxIconHeight = option.iconSize; + const int widgetIconSize = iconSize(); + const int maxIconWidth = iconOnTop ? widgetSize.width() - 2 * padding : widgetIconSize; + const int maxIconHeight = widgetIconSize; const QHash values = data(); @@ -945,7 +991,34 @@ void KStandardItemListWidget::updatePixmapCache() } if (updatePixmap) { - m_pixmap = values["iconPixmap"].value(); + m_pixmap = QPixmap(); + + int sequenceIndex = hoverSequenceIndex(); + + if (values.contains("hoverSequencePixmaps")) { + // Use one of the hover sequence pixmaps instead of the default + // icon pixmap. + + const QVector pixmaps = values["hoverSequencePixmaps"].value>(); + + if (values.contains("hoverSequenceWraparoundPoint")) { + const float wap = values["hoverSequenceWraparoundPoint"].toFloat(); + if (wap >= 1.0f) { + sequenceIndex %= static_cast(wap); + } + } + + const int loadedIndex = qMax(qMin(sequenceIndex, pixmaps.size()-1), 0); + + if (loadedIndex != 0) { + m_pixmap = pixmaps[loadedIndex]; + } + } + + if (m_pixmap.isNull()) { + m_pixmap = values["iconPixmap"].value(); + } + if (m_pixmap.isNull()) { // Use the icon that fits to the MIME-type QString iconName = values["iconName"].toString(); @@ -1123,7 +1196,11 @@ QString KStandardItemListWidget::elideRightKeepExtension(const QString &text, in QString ret = m_customizedFontMetrics.elidedText(text.chopped(extensionLength), Qt::ElideRight, elidingWidth - extensionWidth); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) ret.append(text.rightRef(extensionLength)); +#else + ret.append(QStringView(text).right(extensionLength)); +#endif return ret; } } @@ -1148,7 +1225,6 @@ void KStandardItemListWidget::updateIconsLayoutTextCache() const KItemListStyleOption& option = styleOption(); const qreal padding = option.padding; const qreal maxWidth = size().width() - 2 * padding; - const qreal widgetHeight = size().height(); const qreal lineSpacing = m_customizedFontMetrics.lineSpacing(); // Initialize properties for the "text" role. It will be used as anchor @@ -1202,12 +1278,8 @@ void KStandardItemListWidget::updateIconsLayoutTextCache() layout.endLayout(); // Use one line for each additional information - const int additionalRolesCount = qMax(visibleRoles().count() - 1, 0); nameTextInfo->staticText.setTextWidth(maxWidth); - nameTextInfo->pos = QPointF(padding, widgetHeight - - nameHeight - - additionalRolesCount * lineSpacing - - padding); + nameTextInfo->pos = QPointF(padding, iconSize() + 2 * padding); m_textRect = QRectF(padding + (maxWidth - nameWidth) / 2, nameTextInfo->pos.y(), nameWidth, @@ -1242,7 +1314,7 @@ void KStandardItemListWidget::updateIconsLayoutTextCache() requiredWidth = m_customizedFontMetrics.horizontalAdvance(elidedText); } else if (role == "rating") { // Use the width of the rating pixmap, because the rating text is empty. - requiredWidth = m_rating.width(); + requiredWidth = m_rating.width() / m_rating.devicePixelRatioF(); } } layout.endLayout(); @@ -1251,7 +1323,11 @@ void KStandardItemListWidget::updateIconsLayoutTextCache() textInfo->staticText.setTextWidth(maxWidth); const QRectF textRect(padding + (maxWidth - requiredWidth) / 2, y, requiredWidth, lineSpacing); - m_textRect |= textRect; + + // Ignore empty roles. Avoids a text rect taller than the area that actually contains text. + if (!textRect.isEmpty()) { + m_textRect |= textRect; + } y += lineSpacing; } @@ -1272,10 +1348,9 @@ void KStandardItemListWidget::updateCompactLayoutTextCache() const qreal widgetHeight = size().height(); const qreal lineSpacing = m_customizedFontMetrics.lineSpacing(); const qreal textLinesHeight = qMax(visibleRoles().count(), 1) * lineSpacing; - const int scaledIconSize = (textLinesHeight < option.iconSize) ? widgetHeight - 2 * option.padding : option.iconSize; qreal maximumRequiredTextWidth = 0; - const qreal x = option.padding * 3 + scaledIconSize; + const qreal x = option.padding * 3 + iconSize(); qreal y = qRound((widgetHeight - textLinesHeight) / 2); const qreal maxWidth = size().width() - x - option.padding; for (const QByteArray& role : qAsConst(m_sortedVisibleRoles)) { @@ -1315,15 +1390,14 @@ void KStandardItemListWidget::updateDetailsLayoutTextCache() const QHash values = data(); const qreal widgetHeight = size().height(); - const int scaledIconSize = widgetHeight - 2 * option.padding; const int fontHeight = m_customizedFontMetrics.height(); const qreal columnWidthInc = columnPadding(option); - qreal firstColumnInc = scaledIconSize; + qreal firstColumnInc = iconSize(); if (m_supportsItemExpanding) { firstColumnInc += (m_expansionArea.left() + m_expansionArea.right() + widgetHeight) / 2; } else { - firstColumnInc += option.padding; + firstColumnInc += option.padding + leadingPadding(); } qreal x = firstColumnInc; @@ -1339,7 +1413,7 @@ void KStandardItemListWidget::updateDetailsLayoutTextCache() const bool isTextRole = (role == "text"); if (isTextRole) { - availableTextWidth -= firstColumnInc; + availableTextWidth -= firstColumnInc - leadingPadding(); } if (requiredWidth > availableTextWidth) { @@ -1361,7 +1435,7 @@ void KStandardItemListWidget::updateDetailsLayoutTextCache() // The column after the name should always be aligned on the same x-position independent // from the expansion-level shown in the name column - x -= firstColumnInc; + x -= firstColumnInc - leadingPadding(); } else if (isRoleRightAligned(role)) { textInfo->pos.rx() += roleWidth - requiredWidth - columnWidthInc; } @@ -1373,8 +1447,11 @@ void KStandardItemListWidget::updateAdditionalInfoTextColor() QColor c1; if (m_customTextColor.isValid()) { c1 = m_customTextColor; - } else if (isSelected() && m_layout != DetailsLayout) { - c1 = styleOption().palette.highlightedText().color(); + } else if (isSelected()) { + // The detail text colour needs to match the main text (HighlightedText) for the same level + // of readability. We short circuit early here to avoid interpolating with another colour. + m_additionalInfoTextColor = styleOption().palette.color(QPalette::HighlightedText); + return; } else { c1 = styleOption().palette.text().color(); } @@ -1413,15 +1490,15 @@ void KStandardItemListWidget::drawSiblingsInformation(QPainter* painter) const int x = (m_expansionArea.left() + m_expansionArea.right() - siblingSize) / 2; QRect siblingRect(x, 0, siblingSize, siblingSize); - QStyleOption option; - option.palette.setColor(QPalette::Text, option.palette.color(normalTextColorRole())); bool isItemSibling = true; const QBitArray siblings = siblingsInformation(); + QStyleOption option; + const auto normalColor = option.palette.color(normalTextColorRole()); + const auto highlightColor = option.palette.color(expansionAreaHovered() ? QPalette::Highlight : normalTextColorRole()); for (int i = siblings.count() - 1; i >= 0; --i) { option.rect = siblingRect; option.state = siblings.at(i) ? QStyle::State_Sibling : QStyle::State_None; - if (isItemSibling) { option.state |= QStyle::State_Item; if (m_isExpandable) { @@ -1430,7 +1507,10 @@ void KStandardItemListWidget::drawSiblingsInformation(QPainter* painter) if (data().value("isExpanded").toBool()) { option.state |= QStyle::State_Open; } + option.palette.setColor(QPalette::Text, highlightColor); isItemSibling = false; + } else { + option.palette.setColor(QPalette::Text, normalColor); } style()->drawPrimitive(QStyle::PE_IndicatorBranch, &option, painter);