summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gui/doc/qtgui.qdocconf3
-rw-r--r--src/gui/doc/src/external-resources.qdoc12
-rw-r--r--src/gui/doc/src/includes/qiconengine-virtualhookhelper.qdocinc3
-rw-r--r--src/gui/image/qicon.cpp52
-rw-r--r--src/gui/image/qiconengine.cpp102
-rw-r--r--src/gui/image/qiconengine.h12
-rw-r--r--src/gui/image/qiconloader.cpp48
-rw-r--r--src/gui/image/qiconloader_p.h5
8 files changed, 210 insertions, 27 deletions
diff --git a/src/gui/doc/qtgui.qdocconf b/src/gui/doc/qtgui.qdocconf
index b07d39fa37..94574a314c 100644
--- a/src/gui/doc/qtgui.qdocconf
+++ b/src/gui/doc/qtgui.qdocconf
@@ -44,7 +44,8 @@ depends += \
headerdirs += ..
sourcedirs += .. \
- ../../../examples/gui/doc/src
+ ../../../examples/gui/doc/src \
+ src/includes
exampledirs += ../../../examples/gui \
snippets
diff --git a/src/gui/doc/src/external-resources.qdoc b/src/gui/doc/src/external-resources.qdoc
index 6a423323ea..480a4057be 100644
--- a/src/gui/doc/src/external-resources.qdoc
+++ b/src/gui/doc/src/external-resources.qdoc
@@ -49,4 +49,14 @@
/*!
\externalpage http://www.opengl.org/wiki/Tessellation_Shader
\title OpenGL Tessellation Shaders
-*/ \ No newline at end of file
+*/
+
+/*!
+ \externalpage https://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
+ \title Icon Theme Specification
+*/
+
+/*!
+ \externalpage https://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html#directory_layout
+ \title Icon Theme Specification - Directory Layout
+*/
diff --git a/src/gui/doc/src/includes/qiconengine-virtualhookhelper.qdocinc b/src/gui/doc/src/includes/qiconengine-virtualhookhelper.qdocinc
new file mode 100644
index 0000000000..b17d2bd66f
--- /dev/null
+++ b/src/gui/doc/src/includes/qiconengine-virtualhookhelper.qdocinc
@@ -0,0 +1,3 @@
+\note This is a helper method and the actual work is done by the
+virtual_hook() method, hence this method depends on icon engine support
+and may not work with all icon engines.
diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp
index e8f2c878c8..17734f05f3 100644
--- a/src/gui/image/qicon.cpp
+++ b/src/gui/image/qicon.cpp
@@ -605,6 +605,51 @@ QFactoryLoader *qt_iconEngineFactoryLoader()
\note QIcon needs a QGuiApplication instance before the icon is created.
+ \section1 High DPI Icons
+
+ There are two ways that QIcon supports \l {High DPI Displays}{high DPI}
+ icons: via \l addFile() and \l fromTheme().
+
+ \l addFile() is useful if you have your own custom directory structure and do
+ not need to use the \l {Icon Theme Specification}{freedesktop.org Icon Theme
+ Specification}. Icons created via this approach use Qt's \l {High Resolution
+ Versions of Images}{"@nx" high DPI syntax}.
+
+ Using \l fromTheme() is necessary if you plan on following the Icon Theme
+ Specification. To make QIcon use the high DPI version of an image, add an
+ additional entry to the appropriate \c index.theme file:
+
+ \badcode
+ [Icon Theme]
+ Name=Test
+ Comment=Test Theme
+
+ Directories=32x32/actions,32x32@2/actions
+
+ [32x32/actions]
+ Size=32
+ Context=Actions
+ Type=Fixed
+
+ # High DPI version of the entry above.
+ [32x32@2/actions]
+ Size=32
+ Scale=2
+ Type=Fixed
+ \endcode
+
+ Your icon theme directory would then look something like this:
+
+ \badcode
+ ├── 32x32
+ │ └── actions
+ │ └── appointment-new.png
+ ├── 32x32@2
+ │ └── actions
+ │ └── appointment-new.png
+ └── index.theme
+ \endcode
+
\sa {fowler}{GUI Design Handbook: Iconic Label}, {Icons Example}
*/
@@ -847,9 +892,10 @@ QPixmap QIcon::pixmap(QWindow *window, const QSize &size, Mode mode, State state
}
// Try get a pixmap that is big enough to be displayed at device pixel resolution.
- QPixmap pixmap = d->engine->pixmap(size * devicePixelRatio, mode, state);
- pixmap.setDevicePixelRatio(d->pixmapDevicePixelRatio(devicePixelRatio, size, pixmap.size()));
- return pixmap;
+ QIconEngine::ScaledPixmapArgument scalePixmapArg = { size * devicePixelRatio, mode, state, devicePixelRatio, QPixmap() };
+ d->engine->virtual_hook(QIconEngine::ScaledPixmapHook, reinterpret_cast<void*>(&scalePixmapArg));
+ scalePixmapArg.pixmap.setDevicePixelRatio(d->pixmapDevicePixelRatio(devicePixelRatio, size, scalePixmapArg.pixmap.size()));
+ return scalePixmapArg.pixmap;
}
/*!
diff --git a/src/gui/image/qiconengine.cpp b/src/gui/image/qiconengine.cpp
index 0ba9844f7a..1f8e5f321a 100644
--- a/src/gui/image/qiconengine.cpp
+++ b/src/gui/image/qiconengine.cpp
@@ -169,6 +169,12 @@ void QIconEngine::addFile(const QString &/*fileName*/, const QSize &/*size*/, QI
bool that can be set to true if the icon is null. This enum value
was added in Qt 5.7.
+ \value ScaledPixmapHook Provides a way to get a pixmap that is scaled
+ according to the given scale (typically equal to the \l {Glossary Of High
+ DPI Terms}{device pixel ratio}). The \a data argument of the virtual_hook()
+ function is a \l ScaledPixmapArgument pointer that contains both the input and
+ output arguments. This enum value was added in Qt 5.9.
+
\sa virtual_hook()
*/
@@ -207,6 +213,60 @@ void QIconEngine::addFile(const QString &/*fileName*/, const QSize &/*size*/, QI
vectorial format normally return an empty list.
*/
+/*!
+ \class QIconEngine::ScaledPixmapArgument
+ \since 5.9
+
+ \inmodule QtGui
+
+ This struct represents arguments to the virtual_hook() function when
+ the \a id parameter is QIconEngine::ScaledPixmapHook.
+
+ The struct provides a way for icons created via \l QIcon::fromTheme()
+ to return pixmaps that are designed for the current \l {Glossary Of High
+ DPI Terms}{device pixel ratio}. The scale for such an icon is specified
+ using the \l {Icon Theme Specification - Directory Layout}{Scale directory key}
+ in the appropriate \c index.theme file.
+
+ Icons created via other approaches will return the same result as a call to
+ \l pixmap() would, and continue to benefit from Qt's \l {High Resolution
+ Versions of Images}{"@nx" high DPI syntax}.
+
+ \sa virtual_hook(), QIconEngine::IconEngineHook, {High DPI Icons}
+ */
+
+/*!
+ \variable QIconEngine::ScaledPixmapArgument::size
+ \brief The requested size of the pixmap.
+*/
+
+/*!
+ \variable QIconEngine::ScaledPixmapArgument::mode
+ \brief The requested mode of the pixmap.
+
+ \sa QIcon::Mode
+*/
+
+/*!
+ \variable QIconEngine::ScaledPixmapArgument::state
+ \brief The requested state of the pixmap.
+
+ \sa QIcon::State
+*/
+
+/*!
+ \variable QIconEngine::ScaledPixmapArgument::scale
+ \brief The requested scale of the pixmap.
+*/
+
+/*!
+ \variable QIconEngine::ScaledPixmapArgument::pixmap
+
+ \brief The pixmap that is the best match for the given \l size, \l mode, \l
+ \state, and \l scale. This is an output parameter that is set after calling
+ \l virtual_hook().
+*/
+
/*!
Returns a key that identifies this icon engine.
@@ -262,6 +322,13 @@ void QIconEngine::virtual_hook(int id, void *data)
arg.sizes.clear();
break;
}
+ case QIconEngine::ScaledPixmapHook: {
+ // We don't have any notion of scale besides "@nx", so just call pixmap() here.
+ QIconEngine::ScaledPixmapArgument &arg =
+ *reinterpret_cast<QIconEngine::ScaledPixmapArgument*>(data);
+ arg.pixmap = pixmap(arg.size, arg.mode, arg.state);
+ break;
+ }
default:
break;
}
@@ -273,9 +340,7 @@ void QIconEngine::virtual_hook(int id, void *data)
Returns sizes of all images that are contained in the engine for the
specific \a mode and \a state.
- \note This is a helper method and the actual work is done by
- virtual_hook() method, hence this method depends on icon engine support
- and may not work with all icon engines.
+ \include qiconengine-virtualhookhelper.qdocinc
*/
QList<QSize> QIconEngine::availableSizes(QIcon::Mode mode, QIcon::State state) const
{
@@ -291,9 +356,7 @@ QList<QSize> QIconEngine::availableSizes(QIcon::Mode mode, QIcon::State state) c
Returns the name used to create the engine, if available.
- \note This is a helper method and the actual work is done by
- virtual_hook() method, hence this method depends on icon engine support
- and may not work with all icon engines.
+ \include qiconengine-virtualhookhelper.qdocinc
*/
QString QIconEngine::iconName() const
{
@@ -306,6 +369,8 @@ QString QIconEngine::iconName() const
\since 5.7
Returns true if this icon engine represent a null QIcon.
+
+ \include qiconengine-virtualhookhelper.qdocinc
*/
bool QIconEngine::isNull() const
{
@@ -314,4 +379,29 @@ bool QIconEngine::isNull() const
return isNull;
}
+/*!
+ \since 5.9
+
+ Returns a pixmap for the given \a size, \a mode, \a state and \a scale.
+
+ The \a scale argument is typically equal to the \l {Glossary Of High DPI
+ Terms}{device pixel ratio} of the display.
+
+ \include qiconengine-virtualhookhelper.qdocinc
+
+ \note Some engines may cast \a scale to an integer.
+
+ \sa ScaledPixmapArgument
+*/
+QPixmap QIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale)
+{
+ ScaledPixmapArgument arg;
+ arg.size = size;
+ arg.mode = mode;
+ arg.state = state;
+ arg.scale = scale;
+ const_cast<QIconEngine *>(this)->virtual_hook(QIconEngine::ScaledPixmapHook, reinterpret_cast<void*>(&arg));
+ return arg.pixmap;
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/image/qiconengine.h b/src/gui/image/qiconengine.h
index 783770cd30..0c67ef2686 100644
--- a/src/gui/image/qiconengine.h
+++ b/src/gui/image/qiconengine.h
@@ -65,7 +65,7 @@ public:
virtual bool read(QDataStream &in);
virtual bool write(QDataStream &out) const;
- enum IconEngineHook { AvailableSizesHook = 1, IconNameHook, IsNullHook };
+ enum IconEngineHook { AvailableSizesHook = 1, IconNameHook, IsNullHook, ScaledPixmapHook };
struct AvailableSizesArgument
{
@@ -79,6 +79,16 @@ public:
virtual QString iconName() const;
bool isNull() const; // ### Qt6 make virtual
+ QPixmap scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale); // ### Qt6 make virtual
+
+ struct ScaledPixmapArgument
+ {
+ QSize size;
+ QIcon::Mode mode;
+ QIcon::State state;
+ qreal scale;
+ QPixmap pixmap;
+ };
virtual void virtual_hook(int id, void *data);
diff --git a/src/gui/image/qiconloader.cpp b/src/gui/image/qiconloader.cpp
index 324f13a17b..d72c05a3c5 100644
--- a/src/gui/image/qiconloader.cpp
+++ b/src/gui/image/qiconloader.cpp
@@ -47,6 +47,7 @@
#include <qpa/qplatformtheme.h>
#include <QtGui/QIconEngine>
#include <QtGui/QPalette>
+#include <QtCore/qmath.h>
#include <QtCore/QList>
#include <QtCore/QDir>
#include <QtCore/QSettings>
@@ -347,6 +348,10 @@ QIconTheme::QIconTheme(const QString &themeName)
dirInfo.maxSize = indexReader.value(directoryKey +
QLatin1String("/MaxSize"),
size).toInt();
+
+ dirInfo.scale = indexReader.value(directoryKey +
+ QLatin1String("/Scale"),
+ 1).toInt();
m_keyList.append(dirInfo);
}
}
@@ -553,8 +558,11 @@ void QIconLoaderEngine::paint(QPainter *painter, const QRect &rect,
* This algorithm is defined by the freedesktop spec:
* http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
*/
-static bool directoryMatchesSize(const QIconDirInfo &dir, int iconsize)
+static bool directoryMatchesSize(const QIconDirInfo &dir, int iconsize, int iconscale)
{
+ if (dir.scale != iconscale)
+ return false;
+
if (dir.type == QIconDirInfo::Fixed) {
return dir.size == iconsize;
@@ -575,24 +583,25 @@ static bool directoryMatchesSize(const QIconDirInfo &dir, int iconsize)
* This algorithm is defined by the freedesktop spec:
* http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
*/
-static int directorySizeDistance(const QIconDirInfo &dir, int iconsize)
+static int directorySizeDistance(const QIconDirInfo &dir, int iconsize, int iconscale)
{
+ const int scaledIconSize = iconsize * iconscale;
if (dir.type == QIconDirInfo::Fixed) {
- return qAbs(dir.size - iconsize);
+ return qAbs(dir.size * dir.scale - scaledIconSize);
} else if (dir.type == QIconDirInfo::Scalable) {
- if (iconsize < dir.minSize)
- return dir.minSize - iconsize;
- else if (iconsize > dir.maxSize)
- return iconsize - dir.maxSize;
+ if (scaledIconSize < dir.minSize * dir.scale)
+ return dir.minSize * dir.scale - scaledIconSize;
+ else if (scaledIconSize > dir.maxSize * dir.scale)
+ return scaledIconSize - dir.maxSize * dir.scale;
else
return 0;
} else if (dir.type == QIconDirInfo::Threshold) {
- if (iconsize < dir.size - dir.threshold)
- return dir.minSize - iconsize;
- else if (iconsize > dir.size + dir.threshold)
- return iconsize - dir.maxSize;
+ if (scaledIconSize < (dir.size - dir.threshold) * dir.scale)
+ return dir.minSize * dir.scale - scaledIconSize;
+ else if (scaledIconSize > (dir.size + dir.threshold) * dir.scale)
+ return scaledIconSize - dir.maxSize * dir.scale;
else return 0;
}
@@ -600,7 +609,7 @@ static int directorySizeDistance(const QIconDirInfo &dir, int iconsize)
return INT_MAX;
}
-QIconLoaderEngineEntry *QIconLoaderEngine::entryForSize(const QSize &size)
+QIconLoaderEngineEntry *QIconLoaderEngine::entryForSize(const QSize &size, int scale)
{
int iconsize = qMin(size.width(), size.height());
@@ -612,7 +621,7 @@ QIconLoaderEngineEntry *QIconLoaderEngine::entryForSize(const QSize &size)
// Search for exact matches first
for (int i = 0; i < numEntries; ++i) {
QIconLoaderEngineEntry *entry = m_info.entries.at(i);
- if (directoryMatchesSize(entry->dir, iconsize)) {
+ if (directoryMatchesSize(entry->dir, iconsize, scale)) {
return entry;
}
}
@@ -622,7 +631,7 @@ QIconLoaderEngineEntry *QIconLoaderEngine::entryForSize(const QSize &size)
QIconLoaderEngineEntry *closestMatch = 0;
for (int i = 0; i < numEntries; ++i) {
QIconLoaderEngineEntry *entry = m_info.entries.at(i);
- int distance = directorySizeDistance(entry->dir, iconsize);
+ int distance = directorySizeDistance(entry->dir, iconsize, scale);
if (distance < minimalSize) {
minimalSize = distance;
closestMatch = entry;
@@ -665,6 +674,8 @@ QPixmap PixmapEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State st
basePixmap.load(filename);
QSize actualSize = basePixmap.size();
+ // If the size of the best match we have (basePixmap) is larger than the
+ // requested size, we downscale it to match.
if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height()))
actualSize.scale(size, Qt::KeepAspectRatio);
@@ -748,6 +759,15 @@ void QIconLoaderEngine::virtual_hook(int id, void *data)
*reinterpret_cast<bool*>(data) = m_info.entries.isEmpty();
}
break;
+ case QIconEngine::ScaledPixmapHook:
+ {
+ QIconEngine::ScaledPixmapArgument &arg = *reinterpret_cast<QIconEngine::ScaledPixmapArgument*>(data);
+ // QIcon::pixmap() multiplies size by the device pixel ratio.
+ const int integerScale = qCeil(arg.scale);
+ QIconLoaderEngineEntry *entry = entryForSize(arg.size / integerScale, integerScale);
+ arg.pixmap = entry ? entry->pixmap(arg.size, arg.mode, arg.state) : QPixmap();
+ }
+ break;
default:
QIconEngine::virtual_hook(id, data);
}
diff --git a/src/gui/image/qiconloader_p.h b/src/gui/image/qiconloader_p.h
index ed7b7ff7ae..5f3a3ef948 100644
--- a/src/gui/image/qiconloader_p.h
+++ b/src/gui/image/qiconloader_p.h
@@ -76,12 +76,14 @@ struct QIconDirInfo
maxSize(0),
minSize(0),
threshold(0),
+ scale(1),
type(Threshold) {}
QString path;
short size;
short maxSize;
short minSize;
short threshold;
+ short scale;
Type type;
};
Q_DECLARE_TYPEINFO(QIconDirInfo, Q_MOVABLE_TYPE);
@@ -135,7 +137,8 @@ private:
bool hasIcon() const;
void ensureLoaded();
void virtual_hook(int id, void *data) Q_DECL_OVERRIDE;
- QIconLoaderEngineEntry *entryForSize(const QSize &size);
+ QIconLoaderEngineEntry *entryForSize(const QSize &size, int scale = 1);
+
QIconLoaderEngine(const QIconLoaderEngine &other);
QThemeIconInfo m_info;
QString m_iconName;