From a4c2e95ce16b3c4f9e0c9c983fb1ce9e70b5ce5a Mon Sep 17 00:00:00 2001
From: Gabriel de Dietrich <gabriel.dedietrich@theqtcompany.com>
Date: Mon, 9 Mar 2015 21:44:37 +0100
Subject: [PATCH] Use own QIconEngine in QFileIconProvider
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This allows us to lazily load icons from the platform theme
by reimplementing the pixmap(). Otherwise, we would instantiate
pixmaps in several sizes even though we would not need them
right away. Since, at least on OS X, icon sizes can go up to
128x128 pixels, we can end up saving an order of magnitude of
memory on icon pixmaps alone if we only use the smallest sizes
in our application.

Two side modifications are included. The first allows sub-
classing QPixmapIconEngine by exporting this class. The second
fixes the q_ptr in QFileIconProviderPrivate which was never set.

Change-Id: I91af322ded2823e475703871e607773177ae25d3
Task-number: QTBUG-41796
Reviewed-by: Morten Johan Sørvig <morten.sorvig@theqtcompany.com>
---
 src/gui/image/qicon_p.h                     |   2 +-
 src/widgets/itemviews/qfileiconprovider.cpp | 136 +++++++++++++-------
 src/widgets/itemviews/qfileiconprovider_p.h |   2 +-
 3 files changed, 90 insertions(+), 50 deletions(-)

diff --git a/src/gui/image/qicon_p.h b/src/gui/image/qicon_p.h
index e0fec112b5d..8b42e770fac 100644
--- a/src/gui/image/qicon_p.h
+++ b/src/gui/image/qicon_p.h
@@ -99,7 +99,7 @@ inline QPixmapIconEngineEntry::QPixmapIconEngineEntry(const QString &file, const
     pixmap.setDevicePixelRatio(1.0);
 }
 
-class QPixmapIconEngine : public QIconEngine {
+class Q_GUI_EXPORT QPixmapIconEngine : public QIconEngine {
 public:
     QPixmapIconEngine();
     QPixmapIconEngine(const QPixmapIconEngine &);
diff --git a/src/widgets/itemviews/qfileiconprovider.cpp b/src/widgets/itemviews/qfileiconprovider.cpp
index d1bd0e657e9..740bd853b7a 100644
--- a/src/widgets/itemviews/qfileiconprovider.cpp
+++ b/src/widgets/itemviews/qfileiconprovider.cpp
@@ -39,6 +39,7 @@
 #include <qpixmapcache.h>
 #include <private/qfunctions_p.h>
 #include <private/qguiapplication_p.h>
+#include <private/qicon_p.h>
 #include <qpa/qplatformintegration.h>
 #include <qpa/qplatformservices.h>
 #include <qpa/qplatformtheme.h>
@@ -57,6 +58,88 @@
 
 QT_BEGIN_NAMESPACE
 
+static bool isCacheable(const QFileInfo &fi);
+
+class QFileIconEngine : public QPixmapIconEngine
+{
+public:
+    QFileIconEngine(const QFileIconProvider *fip, const QFileInfo &info)
+        : QPixmapIconEngine(), m_fileIconProvider(fip), m_fileInfo(info)
+    { }
+
+    QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) Q_DECL_OVERRIDE
+    {
+        Q_UNUSED(mode);
+        Q_UNUSED(state);
+        QPixmap pixmap;
+
+        if (!size.isValid())
+            return pixmap;
+
+        const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme();
+        if (!theme)
+            return pixmap;
+
+        const QString &keyBase = QLatin1String("qt_.") + m_fileInfo.suffix().toUpper();
+
+        bool cacheable = isCacheable(m_fileInfo);
+        if (cacheable) {
+            QPixmapCache::find(keyBase + QString::number(size.width()), pixmap);
+            if (!pixmap.isNull())
+                return pixmap;
+        }
+
+        QPlatformTheme::IconOptions iconOptions;
+        if (m_fileIconProvider->options() & QFileIconProvider::DontUseCustomDirectoryIcons)
+            iconOptions |= QPlatformTheme::DontUseCustomDirectoryIcons;
+
+        pixmap = theme->fileIconPixmap(m_fileInfo, size, iconOptions);
+        if (!pixmap.isNull()) {
+            if (cacheable)
+                QPixmapCache::insert(keyBase + QString::number(size.width()), pixmap);
+        }
+
+        return pixmap;
+    }
+
+    QList<QSize> availableSizes(QIcon::Mode mode = QIcon::Normal, QIcon::State state = QIcon::Off) const Q_DECL_OVERRIDE
+    {
+        Q_UNUSED(mode);
+        Q_UNUSED(state);
+        static QList<QSize> sizes;
+        static QPlatformTheme *theme = 0;
+        if (!theme) {
+            theme = QGuiApplicationPrivate::platformTheme();
+            if (!theme)
+                return sizes;
+
+            QList<int> themeSizes = theme->themeHint(QPlatformTheme::IconPixmapSizes).value<QList<int> >();
+            if (themeSizes.isEmpty())
+                return sizes;
+
+            foreach (int size, themeSizes)
+                sizes << QSize(size, size);
+        }
+        return sizes;
+    }
+
+    QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) Q_DECL_OVERRIDE
+    {
+        const QList<QSize> &sizes = availableSizes(mode, state);
+        foreach (const QSize &availableSize, sizes) {
+            if (availableSize.width() >= size.width())
+                return availableSize;
+        }
+
+        return sizes.last();
+    }
+
+private:
+    const QFileIconProvider *m_fileIconProvider;
+    QFileInfo m_fileInfo;
+};
+
+
 /*!
   \class QFileIconProvider
 
@@ -86,8 +169,8 @@ QT_BEGIN_NAMESPACE
     cause a big performance impact over network or removable drives.
 */
 
-QFileIconProviderPrivate::QFileIconProviderPrivate() :
-    homePath(QDir::home().absolutePath())
+QFileIconProviderPrivate::QFileIconProviderPrivate(QFileIconProvider *q) :
+    q_ptr(q), homePath(QDir::home().absolutePath())
 {
 }
 
@@ -153,7 +236,7 @@ QIcon QFileIconProviderPrivate::getIcon(QStyle::StandardPixmap name) const
 */
 
 QFileIconProvider::QFileIconProvider()
-    : d_ptr(new QFileIconProviderPrivate)
+    : d_ptr(new QFileIconProviderPrivate(this))
 {
 }
 
@@ -238,53 +321,10 @@ static bool isCacheable(const QFileInfo &fi)
 
 QIcon QFileIconProviderPrivate::getIcon(const QFileInfo &fi) const
 {
-    QIcon retIcon;
-    const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme();
-    if (!theme)
-        return retIcon;
-
-    QList<int> sizes = theme->themeHint(QPlatformTheme::IconPixmapSizes).value<QList<int> >();
-    if (sizes.isEmpty())
-        return retIcon;
-
-    const QString keyBase = QLatin1String("qt_.") + fi.suffix().toUpper();
-
-    bool cacheable = isCacheable(fi);
-    if (cacheable) {
-        QPixmap pixmap;
-        QPixmapCache::find(keyBase + QString::number(sizes.at(0)), pixmap);
-        if (!pixmap.isNull()) {
-            bool iconIsComplete = true;
-            retIcon.addPixmap(pixmap);
-            for (int i = 1; i < sizes.count(); i++)
-                if (QPixmapCache::find(keyBase + QString::number(sizes.at(i)), pixmap)) {
-                    retIcon.addPixmap(pixmap);
-                } else {
-                    iconIsComplete = false;
-                    break;
-                }
-            if (iconIsComplete)
-                return retIcon;
-        }
-    }
-
-    QPlatformTheme::IconOptions iconOptions;
-    if (options & QFileIconProvider::DontUseCustomDirectoryIcons)
-        iconOptions |= QPlatformTheme::DontUseCustomDirectoryIcons;
-
-    Q_FOREACH (int size, sizes) {
-        QPixmap pixmap = theme->fileIconPixmap(fi, QSizeF(size, size), iconOptions);
-        if (!pixmap.isNull()) {
-            retIcon.addPixmap(pixmap);
-            if (cacheable)
-                QPixmapCache::insert(keyBase + QString::number(size), pixmap);
-        }
-    }
-
-    return retIcon;
+    Q_Q(const QFileIconProvider);
+    return QIcon(new QFileIconEngine(q, fi));
 }
 
-
 /*!
   Returns an icon for the file described by \a info.
 */
diff --git a/src/widgets/itemviews/qfileiconprovider_p.h b/src/widgets/itemviews/qfileiconprovider_p.h
index 213535616ce..a1fb4acbea7 100644
--- a/src/widgets/itemviews/qfileiconprovider_p.h
+++ b/src/widgets/itemviews/qfileiconprovider_p.h
@@ -60,7 +60,7 @@ class QFileIconProviderPrivate
     Q_DECLARE_PUBLIC(QFileIconProvider)
 
 public:
-    QFileIconProviderPrivate();
+    QFileIconProviderPrivate(QFileIconProvider *q);
     QIcon getIcon(QStyle::StandardPixmap name) const;
     QIcon getIcon(const QFileInfo &fi) const;
 
-- 
GitLab