Sfoglia il codice sorgente

Use SvgRenderer for Unified Search input icons. Refactor IconUtils. Extend unit tests.

Signed-off-by: alex-z <blackslayer4@gmail.com>
alex-z 4 anni fa
parent
commit
0b8ab5c079

+ 1 - 0
src/gui/CMakeLists.txt

@@ -121,6 +121,7 @@ set(client_SRCS
     userstatusselectormodel.cpp
     emojimodel.cpp
     fileactivitylistmodel.cpp
+    tray/svgimageprovider.cpp
     tray/syncstatussummary.cpp
     tray/activitydata.cpp
     tray/activitylistmodel.cpp

+ 104 - 47
src/gui/iconutils.cpp

@@ -1,44 +1,132 @@
+/*
+ * Copyright (C) by Oleksandr Zolotov <alex@nextcloud.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
 #include "iconutils.h"
 
 #include <theme.h>
 
 #include <QFile>
+#include <QLoggingCategory>
 #include <QPainter>
 #include <QPixmapCache>
 #include <QSvgRenderer>
 
+namespace {
+QString findSvgFilePath(const QString &fileName, const QStringList &possibleColors)
+{
+    const QString baseSvgNoColor{QString{OCC::Theme::themePrefix} + fileName};
+    if (QFile::exists(baseSvgNoColor)) {
+        return baseSvgNoColor;
+    }
+
+    for (const auto &color : possibleColors) {
+        const QString baseSVG{QString{OCC::Theme::themePrefix} + color + QLatin1Char('/') + fileName};
+
+        if (QFile::exists(baseSVG)) {
+            return baseSVG;
+        }
+    }
+
+    return {};
+}
+}
+
 namespace OCC {
 namespace Ui {
-namespace IconUtils {
-QPixmap pixmapForBackground(const QString &fileName, const QColor &backgroundColor)
-{
-    Q_ASSERT(!fileName.isEmpty());
+    Q_LOGGING_CATEGORY(lcIconUtils, "nextcloud.gui.iconutils", QtInfoMsg)
+    namespace IconUtils {
+        QPixmap pixmapForBackground(const QString &fileName, const QColor &backgroundColor)
+        {
+            Q_ASSERT(!fileName.isEmpty());
+
+            const auto pixmapColor = backgroundColor.isValid()
+                && !Theme::isDarkColor(backgroundColor)
+                ? QColorConstants::Svg::black
+                : QColorConstants::Svg::white;
+
+            return createSvgPixmapWithCustomColor(fileName, pixmapColor);
+        }
+
+        QPixmap createSvgPixmapWithCustomColor(const QString &fileName, const QColor &customColor, const QSize &size)
+        {
+            Q_ASSERT(!fileName.isEmpty());
+            Q_ASSERT(customColor.isValid());
+
+            if (fileName.isEmpty()) {
+                qWarning(lcIconUtils) << "fileName is empty";
+            }
+
+            if (!customColor.isValid()) {
+                qWarning(lcIconUtils) << "customColor is invalid";
+            }
+
+            const auto customColorName = customColor.name();
+
+            const QString cacheKey = fileName + QLatin1Char(',') + customColorName;
 
-    // some icons are present in white or black only, so, we need to check both when needed
-    const auto iconBaseColors = QStringList({ QStringLiteral("black"), QStringLiteral("white") });
+            QPixmap cachedPixmap;
 
-    const QString pixmapColor = backgroundColor.isValid() && !Theme::isDarkColor(backgroundColor) ? "black" : "white";
+            // check for existing QPixmap in cache
+            if (QPixmapCache::find(cacheKey, &cachedPixmap)) {
+                return cachedPixmap;
+            }
+
+            // some icons are present in white or black only, so, we need to check both when needed
+            const auto iconBaseColors = QStringList{QStringLiteral("black"), QStringLiteral("white")};
+
+            // check if there is an existing pixmap matching the custom color
+            if (iconBaseColors.contains(customColorName)) {
+                cachedPixmap = QPixmap::fromImage(QImage{QString{OCC::Theme::themePrefix} + customColorName + QLatin1Char('/') + fileName});
+                QPixmapCache::insert(cacheKey, cachedPixmap);
+                return cachedPixmap;
+            }
+
+            // find the first matching svg file
+            const auto sourceSvg = findSvgFilePath(fileName, iconBaseColors);
 
-    const QString cacheKey = fileName + QLatin1Char(',') + pixmapColor;
+            Q_ASSERT(!sourceSvg.isEmpty());
+            if (sourceSvg.isEmpty()) {
+                qWarning(lcIconUtils) << "Failed to find base SVG file for" << cacheKey;
+                return {};
+            }
+
+            cachedPixmap = drawSvgWithCustomFillColor(sourceSvg, customColor, size);
 
-    QPixmap cachedPixmap;
+            Q_ASSERT(!cachedPixmap.isNull());
+            if (cachedPixmap.isNull()) {
+                qWarning(lcIconUtils) << "Failed to load pixmap for" << cacheKey;
+                return {};
+            }
 
-    if (!QPixmapCache::find(cacheKey, &cachedPixmap)) {
-        if (iconBaseColors.contains(pixmapColor)) {
-            cachedPixmap = QPixmap::fromImage(QImage(QString(Theme::themePrefix) + pixmapColor + QLatin1Char('/') + fileName));
             QPixmapCache::insert(cacheKey, cachedPixmap);
+
             return cachedPixmap;
         }
 
-        const auto drawSvgWithCustomFillColor = [](const QString &sourceSvgPath, const QString &fillColor) {
+        QPixmap drawSvgWithCustomFillColor(const QString &sourceSvgPath, const QColor &fillColor, const QSize &size)
+        {
             QSvgRenderer svgRenderer;
 
             if (!svgRenderer.load(sourceSvgPath)) {
-                return QPixmap();
+                qCWarning(lcIconUtils) << "Could no load initial SVG image";
+                return {};
             }
 
+            const auto requestedSize = size.isValid() ? size : svgRenderer.defaultSize();
+
             // render source image
-            QImage svgImage(svgRenderer.defaultSize(), QImage::Format_ARGB32);
+            QImage svgImage(requestedSize, QImage::Format_ARGB32);
             {
                 QPainter svgImagePainter(&svgImage);
                 svgImage.fill(Qt::GlobalColor::transparent);
@@ -46,7 +134,7 @@ QPixmap pixmapForBackground(const QString &fileName, const QColor &backgroundCol
             }
 
             // draw target image with custom fillColor
-            QImage image(svgRenderer.defaultSize(), QImage::Format_ARGB32);
+            QImage image(requestedSize, QImage::Format_ARGB32);
             image.fill(QColor(fillColor));
             {
                 QPainter imagePainter(&image);
@@ -55,38 +143,7 @@ QPixmap pixmapForBackground(const QString &fileName, const QColor &backgroundCol
             }
 
             return QPixmap::fromImage(image);
-        };
-
-        // find the first matching svg among base colors, if any
-        const QString sourceSvg = [&]() {
-            for (const auto &color : iconBaseColors) {
-                const QString baseSVG(QString(Theme::themePrefix) + color + QLatin1Char('/') + fileName);
-
-                if (QFile(baseSVG).exists()) {
-                    return baseSVG;
-                }
-            }
-            return QString();
-        }();
-
-        Q_ASSERT(!sourceSvg.isEmpty());
-        if (sourceSvg.isEmpty()) {
-            qWarning("Failed to find base svg for %s", qPrintable(cacheKey));
-            return {};
-        }
-
-        cachedPixmap = drawSvgWithCustomFillColor(sourceSvg, pixmapColor);
-        QPixmapCache::insert(cacheKey, cachedPixmap);
-
-        Q_ASSERT(!cachedPixmap.isNull());
-        if (cachedPixmap.isNull()) {
-            qWarning("Failed to load pixmap for %s", qPrintable(cacheKey));
-            return {};
         }
     }
-
-    return cachedPixmap;
-}
-}
 }
 }

+ 2 - 0
src/gui/iconutils.h

@@ -22,6 +22,8 @@ namespace OCC {
 namespace Ui {
 namespace IconUtils {
 QPixmap pixmapForBackground(const QString &fileName, const QColor &backgroundColor);
+QPixmap createSvgPixmapWithCustomColor(const QString &fileName, const QColor &customColor, const QSize &size = {});
+QPixmap drawSvgWithCustomFillColor(const QString &sourceSvgPath, const QColor &fillColor, const QSize &size = {});
 }
 }
 }

+ 2 - 0
src/gui/systray.cpp

@@ -17,6 +17,7 @@
 #include "theme.h"
 #include "config.h"
 #include "common/utility.h"
+#include "tray/svgimageprovider.h"
 #include "tray/usermodel.h"
 #include "tray/unifiedsearchresultimageprovider.h"
 #include "configfile.h"
@@ -59,6 +60,7 @@ void Systray::setTrayEngine(QQmlApplicationEngine *trayEngine)
 
     _trayEngine->addImportPath("qrc:/qml/theme");
     _trayEngine->addImageProvider("avatars", new ImageProvider);
+    _trayEngine->addImageProvider(QLatin1String("svgimage-custom-color"), new OCC::Ui::SvgImageProvider);
     _trayEngine->addImageProvider(QLatin1String("unified-search-result-icon"), new UnifiedSearchResultImageProvider);
 }
 

+ 2 - 18
src/gui/tray/UnifiedSearchInputContainer.qml

@@ -46,16 +46,8 @@ TextField {
         smooth: true;
         antialiasing: true
         mipmap: true
-
-        source: "qrc:///client/theme/black/search.svg"
+        source: "image://svgimage-custom-color/search.svg" + "/" + trayWindowUnifiedSearchTextField.textFieldIconsColor
         sourceSize: Qt.size(parent.height * parent.textFieldIconsScaleFactor, parent.height * parent.textFieldIconsScaleFactor)
-
-        ColorOverlay {
-            anchors.fill: parent
-            source: parent
-            cached: true
-            color: parent.parent.textFieldIconsColor
-        }
     }
 
     BusyIndicator {
@@ -87,17 +79,9 @@ TextField {
         mipmap: true
 
         visible: parent.text
-
-        source: "qrc:///client/theme/black/clear.svg"
+        source: "image://svgimage-custom-color/clear.svg" + "/" + trayWindowUnifiedSearchTextField.textFieldIconsColor
         sourceSize: Qt.size(parent.height * parent.textFieldIconsScaleFactor, parent.height * parent.textFieldIconsScaleFactor)
 
-        ColorOverlay {
-            anchors.fill: parent
-            cached: true
-            source: parent
-            color: parent.parent.textFieldIconsColor
-        }
-
         MouseArea {
             id: trayWindowUnifiedSearchTextFieldClearTextButtonMouseArea
 

+ 53 - 0
src/gui/tray/svgimageprovider.cpp

@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) by Oleksandr Zolotov <alex@nextcloud.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "svgimageprovider.h"
+#include "iconutils.h"
+
+#include <QLoggingCategory>
+
+namespace OCC {
+namespace Ui {
+    Q_LOGGING_CATEGORY(lcSvgImageProvider, "nextcloud.gui.svgimageprovider", QtInfoMsg)
+
+    SvgImageProvider::SvgImageProvider()
+        : QQuickImageProvider(QQuickImageProvider::Image)
+    {
+    }
+
+    QImage SvgImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
+    {
+        Q_UNUSED(size)
+
+        Q_ASSERT(!id.isEmpty());
+
+        const auto idSplit = id.split(QLatin1Char('/'), Qt::SkipEmptyParts);
+
+        if (idSplit.isEmpty()) {
+            qCWarning(lcSvgImageProvider) << "Image id is incorrect!";
+            return {};
+        }
+
+        const auto pixmapName = idSplit.at(0);
+        const auto pixmapColor = idSplit.size() > 1 ? QColor(idSplit.at(1)) : QColorConstants::Svg::black;
+
+        if (pixmapName.isEmpty() || !pixmapColor.isValid()) {
+            qCWarning(lcSvgImageProvider) << "Image id is incorrect!";
+            return {};
+        }
+
+        return IconUtils::createSvgPixmapWithCustomColor(pixmapName, pixmapColor, requestedSize).toImage();
+    }
+}
+}

+ 28 - 0
src/gui/tray/svgimageprovider.h

@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) by Oleksandr Zolotov <alex@nextcloud.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#pragma once
+
+#include <QQuickImageProvider>
+
+namespace OCC {
+namespace Ui {
+    class SvgImageProvider : public QQuickImageProvider
+    {
+    public:
+        SvgImageProvider();
+        QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override;
+    };
+}
+}

+ 46 - 13
test/testiconutils.cpp

@@ -29,12 +29,56 @@ public:
     }
 
 private slots:
+    void testDrawSvgWithCustomFillColor()
+    {
+        const QString blackSvgDirPath{QString{OCC::Theme::themePrefix} + QStringLiteral("black")};
+        const QDir blackSvgDir(blackSvgDirPath);
+        const QStringList blackImages = blackSvgDir.entryList(QStringList("*.svg"));
+
+        if (!blackImages.isEmpty()) {
+            QVERIFY(!OCC::Ui::IconUtils::drawSvgWithCustomFillColor(blackSvgDirPath + QLatin1Char('/') + blackImages.at(0), QColorConstants::Svg::red).isNull());
+        }
+
+        if (!blackImages.isEmpty()) {
+            QVERIFY(!OCC::Ui::IconUtils::drawSvgWithCustomFillColor(blackSvgDirPath + QLatin1Char('/') + blackImages.at(0), QColorConstants::Svg::green).isNull());
+        }
+
+        const QString whiteSvgDirPath{QString{OCC::Theme::themePrefix} + QStringLiteral("white")};
+        const QDir whiteSvgDir(whiteSvgDirPath);
+        const QStringList whiteImages = whiteSvgDir.entryList(QStringList("*.svg"));
+
+        if (!whiteImages.isEmpty()) {
+            QVERIFY(!OCC::Ui::IconUtils::drawSvgWithCustomFillColor(whiteSvgDirPath + QLatin1Char('/') + whiteImages.at(0), QColorConstants::Svg::blue).isNull());
+        }
+    }
+
+    void testCreateSvgPixmapWithCustomColor()
+    {
+        const QDir blackSvgDir(QString(QString{OCC::Theme::themePrefix}) + QStringLiteral("black"));
+        const QStringList blackImages = blackSvgDir.entryList(QStringList("*.svg"));
+
+        if (!blackImages.isEmpty()) {
+            QVERIFY(!OCC::Ui::IconUtils::createSvgPixmapWithCustomColor(blackImages.at(0), QColorConstants::Svg::red).isNull());
+        }
+
+        if (!blackImages.isEmpty()) {
+            QVERIFY(!OCC::Ui::IconUtils::createSvgPixmapWithCustomColor(blackImages.at(0), QColorConstants::Svg::green).isNull());
+        }
+
+        const QDir whiteSvgDir(QString(QString{OCC::Theme::themePrefix}) + QStringLiteral("white"));
+        const QStringList whiteImages = whiteSvgDir.entryList(QStringList("*.svg"));
+        
+        if (!whiteImages.isEmpty()) {
+            QVERIFY(!OCC::Ui::IconUtils::createSvgPixmapWithCustomColor(whiteImages.at(0), QColorConstants::Svg::blue).isNull());
+        }
+    }
+
     void testPixmapForBackground()
     {
-        const QDir blackSvgDir(QString(OCC::Theme::themePrefix) + QStringLiteral("black"));
+        const QDir blackSvgDir(QString(QString{OCC::Theme::themePrefix}) + QStringLiteral("black"));
         const QStringList blackImages = blackSvgDir.entryList(QStringList("*.svg"));
 
-        const QDir whiteSvgDir(QString(OCC::Theme::themePrefix) + QStringLiteral("white"));
+        const QDir whiteSvgDir(QString(QString{OCC::Theme::themePrefix}) + QStringLiteral("white"));
         const QStringList whiteImages = whiteSvgDir.entryList(QStringList("*.svg"));
 
         if (blackImages.size() > 0) {
@@ -46,17 +90,6 @@ private slots:
             // black pixmap for bright background - should not fail
             QVERIFY(!OCC::Ui::IconUtils::pixmapForBackground(blackImages.at(0), QColor("yellow")).isNull());
         }
-
-        const auto blackImagesExclusive = QSet<QString>(blackImages.begin(), blackImages.end()).subtract(QSet<QString>(whiteImages.begin(), whiteImages.end()));
-        const auto whiteImagesExclusive = QSet<QString>(whiteImages.begin(), whiteImages.end()).subtract(QSet<QString>(blackImages.begin(), blackImages.end()));
-
-        if (blackImagesExclusive != whiteImagesExclusive) {
-            // black pixmap for dark background - should fail as we don't have this image in black
-            QVERIFY(OCC::Ui::IconUtils::pixmapForBackground(blackImagesExclusive.values().at(0), QColor("blue")).isNull());
-
-            // white pixmap for bright background - should fail as we don't have this image in white
-            QVERIFY(OCC::Ui::IconUtils::pixmapForBackground(whiteImagesExclusive.values().at(0), QColor("yellow")).isNull());
-        }
     }
 };