X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/863c1be871d7dbd16800cf7400284783d52352d5..0464ea82a6850f58805bc4d6fc1df5369d83c3df:/src/kitemviews/kstandarditemlistwidget.cpp diff --git a/src/kitemviews/kstandarditemlistwidget.cpp b/src/kitemviews/kstandarditemlistwidget.cpp index 340343158..566c4dec2 100644 --- a/src/kitemviews/kstandarditemlistwidget.cpp +++ b/src/kitemviews/kstandarditemlistwidget.cpp @@ -7,7 +7,6 @@ #include "kstandarditemlistwidget.h" #include "kfileitemlistview.h" -#include "kfileitemmodel.h" #include "private/kfileitemclipboard.h" #include "private/kitemlistroleeditor.h" #include "private/kitemviewsutils.h" @@ -18,6 +17,7 @@ #include #include #include +#include #include #include @@ -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) @@ -345,24 +345,39 @@ void KStandardItemListWidget::paint(QPainter *painter, const QStyleOptionGraphic drawSiblingsInformation(painter); } + auto pixmap = isHovered() ? m_hoverPixmap : m_pixmap; + if (!m_overlays.isEmpty()) { + const qreal dpr = KItemViewsUtils::devicePixelRatio(this); + + const bool iconOnTop = (m_layout == IconsLayout); + const KItemListStyleOption &option = styleOption(); + const qreal padding = option.padding; + + const int widgetIconSize = iconSize(); + const int maxIconWidth = iconOnTop ? size().width() - 2 * padding : widgetIconSize; + const int maxIconHeight = widgetIconSize; + + pixmap = addOverlays(pixmap, m_overlays, QSize(maxIconWidth, maxIconHeight), dpr); + } + const KItemListStyleOption &itemListStyleOption = styleOption(); - if (isHovered() && !m_pixmap.isNull()) { + if (isHovered() && !pixmap.isNull()) { if (hoverOpacity() < 1.0) { /* - * Linear interpolation between m_pixmap and m_hoverPixmap. + * Linear interpolation between pixmap and m_hoverPixmap. * * Note that this cannot be achieved by painting m_hoverPixmap over - * m_pixmap, even if the opacities are adjusted. For details see + * pixmap, even if the opacities are adjusted. For details see * https://git.reviewboard.kde.org/r/109614/ */ - // Paint pixmap1 so that pixmap1 = m_pixmap * (1.0 - hoverOpacity()) - QPixmap pixmap1(m_pixmap.size()); - pixmap1.setDevicePixelRatio(m_pixmap.devicePixelRatio()); + // Paint pixmap1 so that pixmap1 = pixmap * (1.0 - hoverOpacity()) + QPixmap pixmap1(pixmap.size()); + pixmap1.setDevicePixelRatio(pixmap.devicePixelRatio()); pixmap1.fill(Qt::transparent); { QPainter p(&pixmap1); p.setOpacity(1.0 - hoverOpacity()); - p.drawPixmap(0, 0, m_pixmap); + p.drawPixmap(0, 0, pixmap); } // Paint pixmap2 so that pixmap2 = m_hoverPixmap * hoverOpacity() @@ -376,8 +391,8 @@ void KStandardItemListWidget::paint(QPainter *painter, const QStyleOptionGraphic } // Paint pixmap2 on pixmap1 using CompositionMode_Plus - // Now pixmap1 = pixmap2 + m_pixmap * (1.0 - hoverOpacity()) - // = m_hoverPixmap * hoverOpacity() + m_pixmap * (1.0 - hoverOpacity()) + // Now pixmap1 = pixmap2 + pixmap * (1.0 - hoverOpacity()) + // = m_hoverPixmap * hoverOpacity() + pixmap * (1.0 - hoverOpacity()) { QPainter p(&pixmap1); p.setCompositionMode(QPainter::CompositionMode_Plus); @@ -387,10 +402,10 @@ void KStandardItemListWidget::paint(QPainter *painter, const QStyleOptionGraphic // Finally paint pixmap1 on the widget drawPixmap(painter, pixmap1); } else { - drawPixmap(painter, m_hoverPixmap); + drawPixmap(painter, pixmap); } - } else if (!m_pixmap.isNull()) { - drawPixmap(painter, m_pixmap); + } else if (!pixmap.isNull()) { + drawPixmap(painter, pixmap); } painter->setFont(m_customizedFont); @@ -632,7 +647,7 @@ void KStandardItemListWidget::startActivateSoonAnimation(int timeUntilActivation m_activateSoonAnimation->setEndValue(1.0); m_activateSoonAnimation->setDuration(timeUntilActivation); - const QVariant originalIconName{data()["iconName"]}; + const QVariant originalIconName{value("iconName")}; connect(m_activateSoonAnimation, &QVariantAnimation::valueChanged, this, [originalIconName, this](const QVariant &value) { auto progress = value.toFloat(); @@ -667,7 +682,7 @@ void KStandardItemListWidget::startActivateSoonAnimation(int timeUntilActivation bool KStandardItemListWidget::isIconControlledByActivateSoonAnimation() const { - return m_activateSoonAnimation && data()["iconName"] == "folder-open"; + return m_activateSoonAnimation && value("iconName") == "folder-open"; } KItemListWidgetInformant *KStandardItemListWidget::createInformant() @@ -685,7 +700,6 @@ void KStandardItemListWidget::invalidateIconCache() { m_dirtyContent = true; m_dirtyContentRoles.insert("iconPixmap"); - m_dirtyContentRoles.insert("iconOverlays"); } void KStandardItemListWidget::refreshCache() @@ -737,16 +751,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 @@ -1102,17 +1121,17 @@ void KStandardItemListWidget::updatePixmapCache() // use a generic icon as fallback iconName = QStringLiteral("unknown"); } - const QStringList overlays = values["iconOverlays"].toStringList(); const bool hasFocus = scene()->views()[0]->parentWidget()->hasFocus(); m_pixmap = pixmapForIcon(iconName, - overlays, - maxIconHeight, + 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_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()) { @@ -1121,14 +1140,7 @@ void KStandardItemListWidget::updatePixmapCache() } if (m_isCut) { -#if KICONTHEMES_VERSION >= QT_VERSION_CHECK(6, 5, 0) KIconEffect::toDisabled(m_pixmap); -#else - QImage img = m_pixmap.toImage(); - KIconEffect::toGray(img, 1); - KIconEffect::semiTransparent(img); - m_pixmap = QPixmap::fromImage(img); -#endif } if (m_isHidden) { @@ -1147,11 +1159,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"); @@ -1201,13 +1208,7 @@ void KStandardItemListWidget::updatePixmapCache() // Prepare the pixmap that is used when the item gets hovered if (isHovered()) { m_hoverPixmap = m_pixmap; -#if KICONTHEMES_VERSION >= QT_VERSION_CHECK(6, 5, 0) KIconEffect::toActive(m_hoverPixmap); -#else - QImage img = m_pixmap.toImage(); - KIconEffect::toGamma(img, 0.7); - m_hoverPixmap = QPixmap::fromImage(img); -#endif } else if (hoverOpacity() <= 0.0) { // No hover animation is ongoing. Clear m_hoverPixmap to save memory. m_hoverPixmap = QPixmap(); @@ -1551,6 +1552,76 @@ 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 dpr, QIcon::Mode mode) const +{ + // similar to KIconUtils::addOverlays, keep in sync preferrably + if (overlays.isEmpty()) { + return pixmap; + } + + int width = size.width(); + int height = size.height(); + const int iconSize = qMin(width, height); + + // Determine the overlay icon + int overlaySize; + if (iconSize < 32) { + overlaySize = 8; + } else if (iconSize <= 48) { + overlaySize = 16; + } else if (iconSize <= 96) { + overlaySize = 22; + } else if (iconSize < 256) { + overlaySize = 32; + } else { + overlaySize = 64; + } + + auto phyiscalSize = QSize(std::clamp(pixmap.width(), qFloor(2 * overlaySize * dpr), qFloor(size.width() * dpr)), + std::clamp(pixmap.height(), qFloor(2 * overlaySize * dpr), qFloor(size.height() * dpr))); + + QPixmap output(phyiscalSize); + output.setDevicePixelRatio(dpr); + output.fill(Qt::transparent); + + QPainter painter(&output); + painter.drawPixmap(qFloor(phyiscalSize.width() / dpr / 2) - qFloor(pixmap.width() / pixmap.devicePixelRatio() / 2), + // align the icon to the bottom to match the behavior elsewhere + qFloor(phyiscalSize.height() / dpr) - qFloor(pixmap.height() / pixmap.devicePixelRatio()), + pixmap); + + width = qCeil(phyiscalSize.width() / dpr); + height = qCeil(phyiscalSize.height() / dpr); + + // Iterate over stored overlays + for (const auto &[corner, overlay] : overlays.asKeyValueRange()) { + const QPixmap overlayPixmap = QIcon::fromTheme(overlay).pixmap(QSize{overlaySize, overlaySize}, dpr, mode); + if (overlayPixmap.isNull()) { + continue; + } + + QPoint startPoint; + switch (corner) { + case Qt::BottomLeftCorner: + startPoint = QPoint{0, height - overlaySize}; + break; + case Qt::BottomRightCorner: + startPoint = QPoint{width - overlaySize, height - overlaySize}; + break; + case Qt::TopRightCorner: + startPoint = QPoint{width - overlaySize, 0}; + break; + case Qt::TopLeftCorner: + startPoint = QPoint{}; + break; + } + painter.drawPixmap(startPoint, overlayPixmap); + } + + return output; +} + void KStandardItemListWidget::drawPixmap(QPainter *painter, const QPixmap &pixmap) { if (m_scaledPixmapSize != pixmap.size() / pixmap.devicePixelRatio()) { @@ -1638,15 +1709,15 @@ 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 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 % ":" % QString::number(iconHeight) % "@" % QString::number(dpr) % ":" % QString::number(mode); QPixmap pixmap; if (!QPixmapCache::find(key, &pixmap)) { @@ -1654,25 +1725,15 @@ 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()) { - icon = fallbackIcon; + if (!icon.isNull()) { + 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.isNull()) { + icon = fallbackIcon; + pixmap = icon.pixmap(iconSize, dpr, mode); } - - // 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; - } + if (pixmap.width() != iconHeight * dpr || pixmap.height() != iconHeight * dpr) { + KPixmapModifier::scale(pixmap, iconSize * dpr); } QPixmapCache::insert(key, pixmap);