]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/kstandarditemlistwidget.cpp
Merge remote-tracking branch 'fork/work/zakharafoniam/useful-groups'
[dolphin.git] / src / kitemviews / kstandarditemlistwidget.cpp
index 7677299e81fbff6fceb9891ce20f2084b57668dd..2538cddcf6973fac086b3fed50fea7ef79b19f54 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "kstandarditemlistwidget.h"
 
+#include "dolphin_contentdisplaysettings.h"
 #include "kfileitemlistview.h"
 #include "private/kfileitemclipboard.h"
 #include "private/kitemlistroleeditor.h"
@@ -270,7 +271,6 @@ KStandardItemListWidget::KStandardItemListWidget(KItemListWidgetInformant *infor
     , m_scaledPixmapSize()
     , m_columnWidthSum()
     , m_iconRect()
-    , m_hoverPixmap()
     , m_textRect()
     , m_sortedVisibleRoles()
     , m_expansionArea()
@@ -345,24 +345,39 @@ void KStandardItemListWidget::paint(QPainter *painter, const QStyleOptionGraphic
         drawSiblingsInformation(painter);
     }
 
+    auto pixmap = 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()
@@ -372,12 +387,12 @@ void KStandardItemListWidget::paint(QPainter *painter, const QStyleOptionGraphic
             {
                 QPainter p(&pixmap2);
                 p.setOpacity(hoverOpacity());
-                p.drawPixmap(0, 0, m_hoverPixmap);
+                p.drawPixmap(0, 0, m_pixmap);
             }
 
             // 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);
@@ -468,12 +483,6 @@ void KStandardItemListWidget::paint(QPainter *painter, const QStyleOptionGraphic
 #endif
 }
 
-QRectF KStandardItemListWidget::iconRect() const
-{
-    const_cast<KStandardItemListWidget *>(this)->triggerCacheRefreshing();
-    return m_iconRect;
-}
-
 QRectF KStandardItemListWidget::textRect() const
 {
     const_cast<KStandardItemListWidget *>(this)->triggerCacheRefreshing();
@@ -521,35 +530,36 @@ QRectF KStandardItemListWidget::textFocusRect() const
     return m_textRect;
 }
 
-QRectF KStandardItemListWidget::selectionRect() const
+QRectF KStandardItemListWidget::selectionRectFull() const
 {
     const_cast<KStandardItemListWidget *>(this)->triggerCacheRefreshing();
-
-    switch (m_layout) {
-    case IconsLayout:
-        return m_textRect;
-
-    case CompactLayout:
-    case DetailsLayout: {
-        const int padding = styleOption().padding;
-        QRectF adjustedIconRect = iconRect().adjusted(-padding, -padding, padding, padding);
-        QRectF result = adjustedIconRect | m_textRect;
+    const int padding = styleOption().padding;
+    if (m_layout == DetailsLayout) {
+        auto rect = m_iconRect | m_textRect;
         if (m_highlightEntireRow) {
             if (layoutDirection() == Qt::LeftToRight) {
-                result.setRight(leftPadding() + m_columnWidthSum);
+                rect.setRight(leftPadding() + m_columnWidthSum);
             } else {
-                result.setLeft(size().width() - m_columnWidthSum - rightPadding());
+                rect.setLeft(size().width() - m_columnWidthSum - rightPadding());
             }
         }
-        return result;
+        return rect.adjusted(-padding, 0, padding, 0);
+    } else {
+        if (m_layout == CompactLayout) {
+            return rect().adjusted(-padding, 0, padding, 0);
+        }
+        return rect();
     }
+}
 
-    default:
-        Q_ASSERT(false);
-        break;
+QRectF KStandardItemListWidget::selectionRectCore() const
+{
+    // Allow dragging from selection area in details view.
+    if (m_layout == DetailsLayout && highlightEntireRow() && !isSelected()) {
+        QRectF result = m_iconRect | m_textRect;
+        return result;
     }
-
-    return m_textRect;
+    return selectionRectFull();
 }
 
 QRectF KStandardItemListWidget::expansionToggleRect() const
@@ -562,7 +572,6 @@ QRectF KStandardItemListWidget::selectionToggleRect() const
 {
     const_cast<KStandardItemListWidget *>(this)->triggerCacheRefreshing();
 
-    const QRectF widgetIconRect = iconRect();
     const int widgetIconSize = iconSize();
     int toggleSize = KIconLoader::SizeSmall;
     if (widgetIconSize >= KIconLoader::SizeEnormous) {
@@ -571,29 +580,11 @@ QRectF KStandardItemListWidget::selectionToggleRect() const
         toggleSize = KIconLoader::SizeSmallMedium;
     }
 
-    QPointF pos = widgetIconRect.topLeft();
-
-    // If the selection toggle has a very small distance to the
-    // widget borders, the size of the selection toggle will get
-    // increased to prevent an accidental clicking of the item
-    // when trying to hit the toggle.
-    const int widgetHeight = size().height();
-    const int widgetWidth = size().width();
-    const int minMargin = 2;
-
-    if (toggleSize + minMargin * 2 >= widgetHeight) {
-        pos.rx() -= (widgetHeight - toggleSize) / 2;
-        toggleSize = widgetHeight;
-        pos.setY(0);
-    }
-    if (toggleSize + minMargin * 2 >= widgetWidth) {
-        pos.ry() -= (widgetWidth - toggleSize) / 2;
-        toggleSize = widgetWidth;
-        pos.setX(0);
-    }
-
+    const int padding = styleOption().padding;
+    const QRectF selectionRectMinusPadding = selectionRectFull().adjusted(padding, padding, -padding, -padding);
+    QPointF pos = selectionRectMinusPadding.topLeft();
     if (QApplication::isRightToLeft()) {
-        pos.setX(widgetIconRect.right() - (pos.x() + toggleSize - widgetIconRect.left()));
+        pos.setX(selectionRectMinusPadding.right() - (pos.x() + toggleSize - selectionRectMinusPadding.left()));
     }
 
     return QRectF(pos, QSizeF(toggleSize, toggleSize));
@@ -632,7 +623,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 +658,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()
@@ -709,7 +700,11 @@ QFont KStandardItemListWidget::customizedFont(const QFont &baseFont) const
 
 QPalette::ColorRole KStandardItemListWidget::normalTextColorRole() const
 {
-    return QPalette::Text;
+    if (isPressed()) {
+        return QPalette::HighlightedText;
+    } else {
+        return QPalette::Text;
+    }
 }
 
 void KStandardItemListWidget::setTextColor(const QColor &color)
@@ -732,7 +727,7 @@ QColor KStandardItemListWidget::textColor(const QWidget &widget) const
     }
 
     const QPalette::ColorGroup group = isActiveWindow() && widget.hasFocus() ? QPalette::Active : QPalette::Inactive;
-    const QPalette::ColorRole role = isSelected() ? QPalette::HighlightedText : normalTextColorRole();
+    const QPalette::ColorRole role = normalTextColorRole();
     return styleOption().palette.color(group, role);
 }
 
@@ -1106,17 +1101,13 @@ 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,
-                                     m_overlays,
                                      QSize(maxIconWidth, maxIconHeight),
                                      m_layout != IconsLayout && isActiveWindow() && isSelected() && hasFocus ? QIcon::Selected : QIcon::Normal);
 
         } 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) {
+            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);
@@ -1124,7 +1115,6 @@ void KStandardItemListWidget::updatePixmapCache()
         }
 
         if (m_pixmap.isNull()) {
-            m_hoverPixmap = QPixmap();
             return;
         }
 
@@ -1135,17 +1125,6 @@ void KStandardItemListWidget::updatePixmapCache()
         if (m_isHidden) {
             KIconEffect::semiTransparent(m_pixmap);
         }
-
-        if (m_layout == IconsLayout && isSelected()) {
-            const QColor color = palette().brush(QPalette::Normal, QPalette::Highlight).color();
-            QImage image = m_pixmap.toImage();
-            if (image.isNull()) {
-                m_hoverPixmap = QPixmap();
-                return;
-            }
-            KIconEffect::colorize(image, color, 0.8f);
-            m_pixmap = QPixmap::fromImage(image);
-        }
     }
 
     int scaledIconSize = 0;
@@ -1193,15 +1172,6 @@ void KStandardItemListWidget::updatePixmapCache()
         const QSizeF squareIconSize(widgetIconSize, widgetIconSize);
         m_iconRect = QRectF(squareIconPos, squareIconSize);
     }
-
-    // Prepare the pixmap that is used when the item gets hovered
-    if (isHovered()) {
-        m_hoverPixmap = m_pixmap;
-        KIconEffect::toActive(m_hoverPixmap);
-    } else if (hoverOpacity() <= 0.0) {
-        // No hover animation is ongoing. Clear m_hoverPixmap to save memory.
-        m_hoverPixmap = QPixmap();
-    }
 }
 
 void KStandardItemListWidget::updateTextsCache()
@@ -1275,6 +1245,31 @@ void KStandardItemListWidget::updateTextsCache()
     }
 }
 
+QString KStandardItemListWidget::elideText(QString text, qreal maxWidth) const
+{
+    if (ContentDisplaySettings::elidingMode() == ContentDisplaySettings::ElidingMode::Middle) {
+        return m_customizedFontMetrics.elidedText(text, Qt::ElideMiddle, maxWidth);
+    }
+
+    if (ContentDisplaySettings::elidingMode() == ContentDisplaySettings::ElidingMode::Right) {
+        qsizetype lastDotPosition = text.lastIndexOf(".");
+        QString extension = text.mid(lastDotPosition);
+
+        if (m_customizedFontMetrics.horizontalAdvance(QStringLiteral("…") + extension) > maxWidth) {
+            extension = "";
+            lastDotPosition = text.size();
+        }
+
+        maxWidth -= m_customizedFontMetrics.horizontalAdvance(extension);
+        QString leftPart = m_customizedFontMetrics.elidedText(text.left(lastDotPosition), Qt::ElideRight, maxWidth);
+
+        return leftPart + extension;
+    }
+
+    Q_UNREACHABLE();
+    return text;
+}
+
 QString KStandardItemListWidget::escapeString(const QString &text) const
 {
     QString escaped(text);
@@ -1335,11 +1330,11 @@ void KStandardItemListWidget::updateIconsLayoutTextCache()
                 qreal lastLineWidth;
                 do {
                     QString lastTextLine = nameText.mid(line.textStart());
-                    lastTextLine = m_customizedFontMetrics.elidedText(lastTextLine, Qt::ElideMiddle, elidingWidth);
+                    lastTextLine = elideText(lastTextLine, elidingWidth);
                     const QString elidedText = nameText.left(line.textStart()) + lastTextLine;
                     nameTextInfo->staticText.setText(elidedText);
 
-                    lastLineWidth = m_customizedFontMetrics.horizontalAdvance(lastTextLine);
+                    lastLineWidth = m_customizedFontMetrics.boundingRect(lastTextLine).width();
 
                     // We do the text eliding in a loop with decreasing width (1 px / iteration)
                     // to avoid problems related to different width calculation code paths
@@ -1383,7 +1378,7 @@ void KStandardItemListWidget::updateIconsLayoutTextCache()
             textLine.setLineWidth(maxWidth);
             requiredWidth = textLine.naturalTextWidth();
             if (requiredWidth > maxWidth) {
-                const QString elidedText = m_customizedFontMetrics.elidedText(text, Qt::ElideMiddle, maxWidth);
+                const QString elidedText = m_customizedFontMetrics.elidedText(text, Qt::ElideRight, maxWidth);
                 textInfo->staticText.setText(elidedText);
                 requiredWidth = m_customizedFontMetrics.horizontalAdvance(elidedText);
             } else if (role == "rating") {
@@ -1435,8 +1430,13 @@ void KStandardItemListWidget::updateCompactLayoutTextCache()
         qreal requiredWidth = m_customizedFontMetrics.horizontalAdvance(text);
         if (requiredWidth > maxWidth) {
             requiredWidth = maxWidth;
-            const QString elidedText = m_customizedFontMetrics.elidedText(text, Qt::ElideMiddle, maxWidth);
-            textInfo->staticText.setText(elidedText);
+            if (role == "text") {
+                const QString elidedText = elideText(text, maxWidth);
+                textInfo->staticText.setText(elidedText);
+            } else {
+                const QString elidedText = m_customizedFontMetrics.elidedText(text, Qt::ElideRight, maxWidth);
+                textInfo->staticText.setText(elidedText);
+            }
         }
 
         textInfo->pos = QPointF(x, y);
@@ -1495,7 +1495,11 @@ void KStandardItemListWidget::updateDetailsLayoutTextCache()
         }
 
         if (requiredWidth > availableTextWidth) {
-            text = m_customizedFontMetrics.elidedText(text, Qt::ElideMiddle, availableTextWidth);
+            if (isTextRole) {
+                text = elideText(text, availableTextWidth);
+            } else {
+                text = m_customizedFontMetrics.elidedText(text, Qt::ElideRight, availableTextWidth);
+            }
             requiredWidth = m_customizedFontMetrics.horizontalAdvance(text);
         }
 
@@ -1525,7 +1529,7 @@ void KStandardItemListWidget::updateAdditionalInfoTextColor()
     } else if (isSelected() && hasFocus && (m_layout != DetailsLayout || m_highlightEntireRow)) {
         // The detail text color needs to match the main text (HighlightedText) for the same level
         // of readability. We short circuit early here to avoid interpolating with another color.
-        m_additionalInfoTextColor = styleOption().palette.color(QPalette::HighlightedText);
+        m_additionalInfoTextColor = styleOption().palette.color(normalTextColorRole());
         return;
     } else {
         c1 = styleOption().palette.text().color();
@@ -1698,7 +1702,7 @@ void KStandardItemListWidget::closeRoleEditor()
     m_roleEditor = nullptr;
 }
 
-QPixmap KStandardItemListWidget::pixmapForIcon(const QString &name, const QHash<Qt::Corner, QString> &overlays, const QSize &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);
@@ -1706,8 +1710,7 @@ QPixmap KStandardItemListWidget::pixmapForIcon(const QString &name, const QHash<
     int iconHeight = size.height();
     QSize iconSize = QSize(iconHeight, iconHeight);
 
-    const QString key = "KStandardItemListWidget:" % name % ":" % overlays.values().join(QLatin1Char(':')) % ":" % QString::number(iconHeight) % "@"
-        % 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)) {
@@ -1726,8 +1729,6 @@ QPixmap KStandardItemListWidget::pixmapForIcon(const QString &name, const QHash<
             KPixmapModifier::scale(pixmap, iconSize * dpr);
         }
 
-        pixmap = addOverlays(pixmap, overlays, size, dpr, mode);
-
         QPixmapCache::insert(key, pixmap);
     }
     pixmap.setDevicePixelRatio(dpr);