From 961e6270e4401a6c7512df3a2e3efd09a25df962 Mon Sep 17 00:00:00 2001 From: =?utf8?q?M=C3=A9ven=20Car?= Date: Tue, 14 Jan 2025 18:52:36 +0100 Subject: [PATCH] Refactor Overlay Handling Now all overlays icons in kitemviews are added in KStandardItemListWidget::updatePixmapCache. data[iconOverlays] now contains icon names. DolphinFileItemListWidget::refreshCache is the sole responsible of setting the overlays either coming from KFileItemModelRolesUpdater or KVersionControlPlugin. This garantees consistency in rendering. BUG: 497372 --- src/kitemviews/kfileitemmodelrolesupdater.cpp | 10 +-- src/kitemviews/kfileitemmodelrolesupdater.h | 2 +- src/kitemviews/kstandarditemlistwidget.cpp | 88 +++++++++++-------- src/kitemviews/kstandarditemlistwidget.h | 14 ++- .../information/informationpanelcontent.cpp | 19 +++- src/views/dolphinfileitemlistwidget.cpp | 35 ++++---- src/views/dolphinfileitemlistwidget.h | 2 +- 7 files changed, 102 insertions(+), 68 deletions(-) diff --git a/src/kitemviews/kfileitemmodelrolesupdater.cpp b/src/kitemviews/kfileitemmodelrolesupdater.cpp index 92bf2bf8d..97b51ae84 100644 --- a/src/kitemviews/kfileitemmodelrolesupdater.cpp +++ b/src/kitemviews/kfileitemmodelrolesupdater.cpp @@ -552,7 +552,7 @@ void KFileItemModelRolesUpdater::slotGotPreview(const KFileItem &item, const QPi } QHash data = rolesData(item, index); - data.insert("iconPixmap", transformPreviewPixmap(pixmap, data["iconOverlays"].toStringList())); + data.insert("iconPixmap", transformPreviewPixmap(pixmap)); disconnect(m_model, &KFileItemModel::itemsChanged, this, &KFileItemModelRolesUpdater::slotItemsChanged); m_model->setData(index, data); @@ -630,7 +630,7 @@ void KFileItemModelRolesUpdater::slotHoverSequenceGotPreview(const KFileItem &it if (wap < 0.0f || loadedIndex < static_cast(wap)) { // Add the preview to the model data - const QPixmap scaledPixmap = transformPreviewPixmap(pixmap, data["iconOverlays"].toStringList()); + const QPixmap scaledPixmap = transformPreviewPixmap(pixmap); pixmaps.append(scaledPixmap); data["hoverSequencePixmaps"] = QVariant::fromValue(pixmaps); @@ -1001,7 +1001,7 @@ void KFileItemModelRolesUpdater::startPreviewJob() m_previewJob = job; } -QPixmap KFileItemModelRolesUpdater::transformPreviewPixmap(const QPixmap &pixmap, const QStringList &overlays) +QPixmap KFileItemModelRolesUpdater::transformPreviewPixmap(const QPixmap &pixmap) { QPixmap scaledPixmap = pixmap; @@ -1042,10 +1042,6 @@ QPixmap KFileItemModelRolesUpdater::transformPreviewPixmap(const QPixmap &pixmap scaledPixmap.setDevicePixelRatio(m_devicePixelRatio); } - if (!overlays.isEmpty()) { - scaledPixmap = KIconUtils::addOverlays(scaledPixmap, overlays).pixmap(cacheSize(), m_devicePixelRatio); - } - return scaledPixmap; } diff --git a/src/kitemviews/kfileitemmodelrolesupdater.h b/src/kitemviews/kfileitemmodelrolesupdater.h index 28a70cda3..3480713ee 100644 --- a/src/kitemviews/kfileitemmodelrolesupdater.h +++ b/src/kitemviews/kfileitemmodelrolesupdater.h @@ -299,7 +299,7 @@ private: * @param overlays the overlays to add to the pixmap * @return The scaled and decorated preview image. */ - QPixmap transformPreviewPixmap(const QPixmap &pixmap, const QStringList &overlays); + QPixmap transformPreviewPixmap(const QPixmap &pixmap); /** * Starts a PreviewJob for loading the next hover sequence image. diff --git a/src/kitemviews/kstandarditemlistwidget.cpp b/src/kitemviews/kstandarditemlistwidget.cpp index aebcfa7d4..dd0ad8dc4 100644 --- a/src/kitemviews/kstandarditemlistwidget.cpp +++ b/src/kitemviews/kstandarditemlistwidget.cpp @@ -276,7 +276,7 @@ KStandardItemListWidget::KStandardItemListWidget(KItemListWidgetInformant *infor , m_expansionArea() , m_customTextColor() , m_additionalInfoTextColor() - , m_overlay() + , m_overlays() , m_rating() , m_roleEditor(nullptr) , m_oldRoleEditor(nullptr) @@ -737,16 +737,21 @@ QColor KStandardItemListWidget::textColor(const QWidget &widget) const return styleOption().palette.color(group, role); } -void KStandardItemListWidget::setOverlay(const QPixmap &overlay) +void KStandardItemListWidget::setOverlays(QHash &overlays) { - m_overlay = overlay; + if (overlays == m_overlays) { + return; + } + + m_overlays = overlays; m_dirtyContent = true; + m_dirtyContentRoles.insert("iconOverlays"); update(); } -QPixmap KStandardItemListWidget::overlay() const +QHash KStandardItemListWidget::overlays() const { - return m_overlay; + return m_overlays; } QString KStandardItemListWidget::roleText(const QByteArray &role, const QHash &values) const @@ -1105,14 +1110,18 @@ void KStandardItemListWidget::updatePixmapCache() const QStringList overlays = values["iconOverlays"].toStringList(); const bool hasFocus = scene()->views()[0]->parentWidget()->hasFocus(); m_pixmap = pixmapForIcon(iconName, - overlays, - maxIconHeight, + m_overlays, + QSize(maxIconWidth, maxIconHeight), m_layout != IconsLayout && isActiveWindow() && isSelected() && hasFocus ? QIcon::Selected : QIcon::Normal); - } else if (m_pixmap.width() / m_pixmap.devicePixelRatio() != maxIconWidth || m_pixmap.height() / m_pixmap.devicePixelRatio() != maxIconHeight) { - // A custom pixmap has been applied. Assure that the pixmap - // is scaled to the maximum available size. - KPixmapModifier::scale(m_pixmap, QSize(maxIconWidth, maxIconHeight) * dpr); + } else { + if (!m_overlays.isEmpty()) { + m_pixmap = addOverlays(m_pixmap, m_overlays, QSize(maxIconWidth, maxIconHeight), dpr); + } else if (m_pixmap.width() / m_pixmap.devicePixelRatio() != maxIconWidth || m_pixmap.height() / m_pixmap.devicePixelRatio() != maxIconHeight) { + // A custom pixmap has been applied. Assure that the pixmap + // is scaled to the maximum available size. + KPixmapModifier::scale(m_pixmap, QSize(maxIconWidth, maxIconHeight) * dpr); + } } if (m_pixmap.isNull()) { @@ -1140,11 +1149,6 @@ void KStandardItemListWidget::updatePixmapCache() } } - if (!m_overlay.isNull()) { - QPainter painter(&m_pixmap); - painter.drawPixmap(0, (m_pixmap.height() - m_overlay.height()) / m_pixmap.devicePixelRatio(), m_overlay); - } - int scaledIconSize = 0; if (iconOnTop) { const TextInfo *textInfo = m_textInfo.value("text"); @@ -1538,6 +1542,25 @@ void KStandardItemListWidget::updateAdditionalInfoTextColor() QColor((c1.red() * p1 + c2.red() * p2) / 100, (c1.green() * p1 + c2.green() * p2) / 100, (c1.blue() * p1 + c2.blue() * p2) / 100); } +QPixmap KStandardItemListWidget::addOverlays(const QPixmap &pixmap, + const QHash &overlays, + const QSize &size, + qreal devicePixelRatioF, + QIcon::Mode mode) const +{ + if (overlays.isEmpty()) { + return pixmap; + } + + QHash overlayIcons; + + for (const auto &[corner, overlay] : overlays.asKeyValueRange()) { + overlayIcons.insert(corner, QIcon::fromTheme(overlay)); + } + + return KIconUtils::addOverlays(pixmap, overlayIcons).pixmap(size, devicePixelRatioF, mode); +} + void KStandardItemListWidget::drawPixmap(QPainter *painter, const QPixmap &pixmap) { if (m_scaledPixmapSize != pixmap.size() / pixmap.devicePixelRatio()) { @@ -1625,15 +1648,16 @@ void KStandardItemListWidget::closeRoleEditor() m_roleEditor = nullptr; } -QPixmap KStandardItemListWidget::pixmapForIcon(const QString &name, const QStringList &overlays, int size, QIcon::Mode mode) const +QPixmap KStandardItemListWidget::pixmapForIcon(const QString &name, const QHash &overlays, const QSize &size, QIcon::Mode mode) const { static const QIcon fallbackIcon = QIcon::fromTheme(QStringLiteral("unknown")); const qreal dpr = KItemViewsUtils::devicePixelRatio(this); - size *= dpr; + int iconHeight = size.height(); + QSize iconSize = QSize(iconHeight, iconHeight); - const QString key = "KStandardItemListWidget:" % name % ":" % overlays.join(QLatin1Char(':')) % ":" % QString::number(size) % "@" % QString::number(dpr) - % ":" % QString::number(mode); + const QString key = "KStandardItemListWidget:" % name % ":" % overlays.values().join(QLatin1Char(':')) % ":" % QString::number(iconHeight) % "@" + % QString::number(dpr) % ":" % QString::number(mode); QPixmap pixmap; if (!QPixmapCache::find(key, &pixmap)) { @@ -1641,26 +1665,18 @@ QPixmap KStandardItemListWidget::pixmapForIcon(const QString &name, const QStrin if (icon.isNull()) { icon = QIcon(name); } - if (icon.isNull() || icon.pixmap(size / dpr, size / dpr, mode).isNull()) { + if (!icon.isNull()) { + pixmap = icon.pixmap(iconSize, dpr, mode); + } + if (pixmap.isNull()) { icon = fallbackIcon; + pixmap = icon.pixmap(iconSize, dpr, mode); } - - pixmap = icon.pixmap(QSize(size / dpr, size / dpr), dpr, mode); - if (pixmap.width() != size || pixmap.height() != size) { - KPixmapModifier::scale(pixmap, QSize(size, size)); + if (pixmap.width() != iconHeight * dpr || pixmap.height() != iconHeight * dpr) { + KPixmapModifier::scale(pixmap, iconSize * dpr); } - // Strangely KFileItem::overlays() returns empty string-values, so - // we need to check first whether an overlay must be drawn at all. - for (const QString &overlay : overlays) { - if (!overlay.isEmpty()) { - // There is at least one overlay, draw all overlays above m_pixmap - // and cancel the check - const QSize size = pixmap.size(); - pixmap = KIconUtils::addOverlays(pixmap, overlays).pixmap(size, dpr, mode); - break; - } - } + pixmap = addOverlays(pixmap, overlays, size, dpr, mode); QPixmapCache::insert(key, pixmap); } diff --git a/src/kitemviews/kstandarditemlistwidget.h b/src/kitemviews/kstandarditemlistwidget.h index d4a4f1231..3e542b4c5 100644 --- a/src/kitemviews/kstandarditemlistwidget.h +++ b/src/kitemviews/kstandarditemlistwidget.h @@ -158,8 +158,8 @@ protected: void setTextColor(const QColor &color); QColor textColor(const QWidget &widget) const; - void setOverlay(const QPixmap &overlay); - QPixmap overlay() const; + void setOverlays(QHash &overlay); + QHash overlays() const; /** * @see KStandardItemListWidgetInformant::roleText(). @@ -212,6 +212,12 @@ private: void updateCompactLayoutTextCache(); void updateDetailsLayoutTextCache(); + QPixmap addOverlays(const QPixmap &pixmap, + const QHash &overlays, + const QSize &size, + qreal devicePixelRatioF, + QIcon::Mode mode = QIcon::Normal) const; + void drawPixmap(QPainter *painter, const QPixmap &pixmap); /** Draw the lines and arrows that visualize the expanded state and level of this row. */ void drawSiblingsInformation(QPainter *painter); @@ -231,7 +237,7 @@ private: */ void closeRoleEditor(); - QPixmap pixmapForIcon(const QString &name, const QStringList &overlays, int size, QIcon::Mode mode) const; + QPixmap pixmapForIcon(const QString &name, const QHash &overlays, const QSize &size, QIcon::Mode mode) const; /** * @return Preferred size of the rating-image based on the given @@ -283,7 +289,7 @@ private: QColor m_customTextColor; QColor m_additionalInfoTextColor; - QPixmap m_overlay; + QHash m_overlays; QPixmap m_rating; KItemListRoleEditor *m_roleEditor; diff --git a/src/panels/information/informationpanelcontent.cpp b/src/panels/information/informationpanelcontent.cpp index b112b2caf..14a470b11 100644 --- a/src/panels/information/informationpanelcontent.cpp +++ b/src/panels/information/informationpanelcontent.cpp @@ -362,8 +362,23 @@ void InformationPanelContent::showPreview(const KFileItem &item, const QPixmap & { m_outdatedPreviewTimer->stop(); - QPixmap p = KIconUtils::addOverlays(pixmap, item.overlays()).pixmap(m_preview->size(), devicePixelRatioF()); - p.setDevicePixelRatio(devicePixelRatioF()); + QPixmap p = pixmap; + if (!item.overlays().isEmpty()) { + // Avoid scaling the images that are smaller than the preview size, to be consistent when there is no overlays + if (pixmap.height() < m_preview->height() && pixmap.width() < m_preview->width()) { + p = QPixmap(m_preview->size() * devicePixelRatioF()); + p.fill(Qt::transparent); + p.setDevicePixelRatio(devicePixelRatioF()); + + QPainter painter(&p); + painter.drawPixmap(QPointF{m_preview->width() / 2.0 - pixmap.width() / pixmap.devicePixelRatioF() / 2, + m_preview->height() / 2.0 - pixmap.height() / pixmap.devicePixelRatioF() / 2} + .toPoint(), + pixmap); + } + p = KIconUtils::addOverlays(p, item.overlays()).pixmap(m_preview->size(), devicePixelRatioF()); + p.setDevicePixelRatio(devicePixelRatioF()); + } if (m_isVideo) { // adds a play arrow overlay diff --git a/src/views/dolphinfileitemlistwidget.cpp b/src/views/dolphinfileitemlistwidget.cpp index 11c88d402..7ad242f3f 100644 --- a/src/views/dolphinfileitemlistwidget.cpp +++ b/src/views/dolphinfileitemlistwidget.cpp @@ -24,6 +24,7 @@ void DolphinFileItemListWidget::refreshCache() { QColor color; const QHash values = data(); + QHash overlays; if (values.contains("version")) { // The item is under version control. Apply the text color corresponding // to its version state. @@ -70,27 +71,28 @@ void DolphinFileItemListWidget::refreshCache() (tintColor.blue() + textColor.blue()) / 2, (tintColor.alpha() + textColor.alpha()) / 2); - setOverlay(overlayForState(version, styleOption().iconSize)); - } else if (!overlay().isNull()) { - setOverlay(QPixmap()); + overlays.insert(Qt::Corner::BottomLeftCorner, overlayForState(version)); } + if (values.contains("iconOverlays")) { + const auto corners = {Qt::Corner::BottomRightCorner, Qt::Corner::TopLeftCorner, Qt::Corner::TopRightCorner}; + const auto iconOverlays = values.value("iconOverlays").toStringList(); + auto overlaysIt = iconOverlays.constBegin(); + for (const auto &corner : corners) { + if (overlaysIt == iconOverlays.constEnd()) { + break; + } + overlays.insert(corner, *overlaysIt); + overlaysIt = ++overlaysIt; + } + } + + setOverlays(overlays); setTextColor(color); } -QPixmap DolphinFileItemListWidget::overlayForState(KVersionControlPlugin::ItemVersion version, int size) const +QString DolphinFileItemListWidget::overlayForState(KVersionControlPlugin::ItemVersion version) const { - int overlayHeight = KIconLoader::SizeSmall; - if (size >= KIconLoader::SizeEnormous) { - overlayHeight = KIconLoader::SizeMedium; - } else if (size >= KIconLoader::SizeLarge) { - overlayHeight = KIconLoader::SizeSmallMedium; - } else if (size >= KIconLoader::SizeMedium) { - overlayHeight = KIconLoader::SizeSmall; - } else { - overlayHeight = KIconLoader::SizeSmall / 2; - } - QString iconName; switch (version) { case KVersionControlPlugin::NormalVersion: @@ -123,8 +125,7 @@ QPixmap DolphinFileItemListWidget::overlayForState(KVersionControlPlugin::ItemVe break; } - const qreal dpr = KItemViewsUtils::devicePixelRatio(this); - return QIcon::fromTheme(iconName).pixmap(QSize(overlayHeight, overlayHeight), dpr); + return iconName; } #include "moc_dolphinfileitemlistwidget.cpp" diff --git a/src/views/dolphinfileitemlistwidget.h b/src/views/dolphinfileitemlistwidget.h index 92fc66fcc..af15074d5 100644 --- a/src/views/dolphinfileitemlistwidget.h +++ b/src/views/dolphinfileitemlistwidget.h @@ -29,7 +29,7 @@ protected: void refreshCache() override; private: - QPixmap overlayForState(KVersionControlPlugin::ItemVersion version, int size) const; + QString overlayForState(KVersionControlPlugin::ItemVersion version) const; }; #endif -- 2.47.3