Implement support for Scale directory key according to Icon Theme spec

Qt already supports high DPI icons using the “@nx” approach, where the
device pixel ratio that the image was designed for is in the file
name. However, our implementation of the freedekstop.org Icon Theme
specification did not support the Scale directory key:

https://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html#directory_layout

This meant that users creating icons via QIcon::fromTheme() did not
get high DPI support. This patch fixes that.

[ChangeLog][QtGui][QIcon] Implemented support for Scale directory key
according to Icon Theme Spec. Icons created via QIcon::fromTheme()
now have high DPI support by specifying the Scale in the appropriate
entry of the relevant index.theme file.

Task-number: QTBUG-49820
Change-Id: If442fbc551034166d88defe607109de1c6ca1d28
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Reviewed-by: Topi Reiniö <topi.reinio@qt.io>
Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
bb10
Mitch Curtis 2017-01-04 16:27:52 +01:00
parent f6c17c37ba
commit f299b565b5
16 changed files with 443 additions and 27 deletions

View File

@ -44,7 +44,8 @@ depends += \
headerdirs += ..
sourcedirs += .. \
../../../examples/gui/doc/src
../../../examples/gui/doc/src \
src/includes
exampledirs += ../../../examples/gui \
snippets

View File

@ -49,4 +49,14 @@
/*!
\externalpage http://www.opengl.org/wiki/Tessellation_Shader
\title OpenGL Tessellation Shaders
*/
*/
/*!
\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
*/

View File

@ -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.

View File

@ -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;
}
/*!

View File

@ -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

View File

@ -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);

View File

@ -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);
}

View File

@ -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;

View File

@ -10,6 +10,7 @@ SUBDIRS=\
qmovie \
qpicture \
qicon \
qiconhighdpi
!qtHaveModule(network): SUBDIRS -= \
qimagereader

Binary file not shown.

After

Width:  |  Height:  |  Size: 897 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,21 @@
[Icon Theme]
Name=Test
Comment=Test Theme
Directories=16x16/actions,22x22/actions,22x22@2/actions
[16x16/actions]
Size=16
Context=Actions
Type=Fixed
[22x22/actions]
Size=22
Context=Actions
Type=Fixed
[22x22@2/actions]
Size=22
Context=Actions
Scale=2
Type=Fixed

View File

@ -0,0 +1,8 @@
CONFIG += testcase
TARGET = tst_qicon
QT += testlib
SOURCES += tst_qiconhighdpi.cpp
RESOURCES = tst_qiconhighdpi.qrc
TESTDATA += icons/*

View File

@ -0,0 +1,195 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include <qicon.h>
class tst_QIconHighDpi : public QObject
{
Q_OBJECT
public:
tst_QIconHighDpi();
private slots:
void initTestCase();
void fromTheme_data();
void fromTheme();
};
tst_QIconHighDpi::tst_QIconHighDpi()
{
}
void tst_QIconHighDpi::initTestCase()
{
}
void tst_QIconHighDpi::fromTheme_data()
{
QTest::addColumn<int>("requestedSize");
QTest::addColumn<int>("expectedSize");
QTest::addColumn<qreal>("expectedDpr");
// The pixmaps that we have available can be found in tst_qiconhighdpi.qrc.
// Currently we only have @1 and @2 icons available.
const int dpr = qCeil(qApp->devicePixelRatio());
// We have an @2 high DPI version of the 22x22 size of this icon.
switch (dpr) {
case 1: QTest::newRow("22x22,dpr=1") << 22 << 22 << 1.0; break;
case 2: QTest::newRow("22x22,dpr=2") << 22 << 44 << 2.0; break;
case 3: QTest::newRow("22x22,dpr=3") << 22 << 44 << 2.0; break;
}
// We don't have a high DPI version of the 16x16 size of this icon,
// so directoryMatchesSize() will return false for all directories.
// directorySizeDistance() is then called to find the best match.
// The table below illustrates the results for our available images at various DPRs:
// Available size | Available scale | Requested size | Requested scale | Distance
// 22 * 2 - 16 * 1 = 28
// 22 * 1 - 16 * 1 = 6
// 16 * 1 - 16 * 1 = 0 < (16x16)
// Available size | Available scale | Requested size | Requested scale | Distance
// 22 * 2 - 16 * 2 = 12
// 22 * 1 - 16 * 2 = 10 < (22x22)
// 16 * 1 - 16 * 2 = 16
// Available size | Available scale | Requested size | Requested scale | Distance
// 22 * 2 - 16 * 3 = 4 < (22x22@2)
// 22 * 1 - 16 * 3 = 26
// 16 * 1 - 16 * 3 = 32
// Both of these functions are implementations of the freedesktop icon theme spec,
// which dictates that if there is no matching scale, directorySizeDistance() determines
// the winner, regardless of whether or not the scale is too low for the requested scale.
switch (dpr) {
case 1: QTest::newRow("16x16,dpr=1") << 16 << 16 << 1.0; break;
// PixmapEntry::pixmap() will only downscale the pixmap if actualSize.width() > size.width().
// In this case, 22 > 32 is false, so a 22x22 pixmap is returned.
case 2: QTest::newRow("16x16,dpr=2") << 16 << 22 << 1.375; break;
case 3: QTest::newRow("16x16,dpr=3") << 16 << 44 << 2.75; break;
}
// We don't have an 8x8 size of this icon, so:
// Available size | Available scale | Requested size | Requested scale | Distance
// 22 * 2 - 8 * 1 = 36
// 22 * 1 - 8 * 1 = 14
// 16 * 1 - 8 * 1 = 8 < (16x16)
// Available size | Available scale | Requested size | Requested scale | Distance
// 22 * 2 - 8 * 2 = 28
// 22 * 1 - 8 * 2 = 6
// 16 * 1 - 8 * 2 = 0 < (16x16)
// Available size | Available scale | Requested size | Requested scale | Distance
// 22 * 2 - 8 * 3 = 20
// 22 * 1 - 8 * 3 = 2 < (22x22)
// 16 * 1 - 8 * 3 = 8
switch (dpr) {
case 1: QTest::newRow("8x8,dpr=1") << 8 << 8 << 1.0; break;
case 2: QTest::newRow("8x8,dpr=2") << 8 << 16 << 2.0; break;
case 3: QTest::newRow("8x8,dpr=3") << 8 << 22 << 2.75; break;
}
// We don't have a 44x44 size of this icon, so:
// Available size | Available scale | Requested size | Requested scale | Distance
// 22 * 2 - 44 * 1 = 0 < (22x22@2)
// 22 * 1 - 44 * 1 = 22
// 16 * 1 - 44 * 1 = 28
// Available size | Available scale | Requested size | Requested scale | Distance
// 22 * 2 - 44 * 2 = 44 < (22x22@2)
// 22 * 1 - 44 * 2 = 66
// 16 * 1 - 44 * 2 = 72
// Available size | Available scale | Requested size | Requested scale | Distance
// 22 * 2 - 44 * 3 = 88 < (22x22@2)
// 22 * 1 - 44 * 3 = 110
// 16 * 1 - 44 * 3 = 116
switch (dpr) {
case 1: QTest::newRow("44x44,dpr=1") << 44 << 44 << 1.0; break;
case 2: QTest::newRow("44x44,dpr=2") << 44 << 44 << 1.0; break;
case 3: QTest::newRow("44x44,dpr=3") << 44 << 44 << 1.0; break;
}
// We don't have a 20x20 size of this icon, so:
// Available size | Available scale | Requested size | Requested scale | Distance
// 22 * 2 - 20 * 1 = 24
// 22 * 1 - 20 * 1 = 2 < (22x22)
// 16 * 1 - 20 * 1 = 4
// Available size | Available scale | Requested size | Requested scale | Distance
// 22 * 2 - 20 * 2 = 4 < (22x22@2)
// 22 * 1 - 20 * 2 = 18
// 16 * 1 - 20 * 2 = 24
// Available size | Available scale | Requested size | Requested scale | Distance
// 22 * 2 - 20 * 3 = 16 < (22x22@2)
// 22 * 1 - 20 * 3 = 38
// 16 * 1 - 20 * 3 = 44
switch (dpr) {
case 1: QTest::newRow("20x20,dpr=1") << 20 << 20 << 1.0; break;
// PixmapEntry::pixmap() will only downscale the pixmap if actualSize.width() > size.width().
// In this case, 44 > 40 is true, so the 44x44 pixmap is downscaled to 40x40.
case 2: QTest::newRow("20x20,dpr=2") << 20 << 40 << 2.0; break;
case 3: QTest::newRow("20x20,dpr=3") << 20 << 44 << 2.2; break;
}
}
void tst_QIconHighDpi::fromTheme()
{
QFETCH(int, requestedSize);
QFETCH(int, expectedSize);
QFETCH(qreal, expectedDpr);
QString searchPath = QLatin1String(":/icons");
QIcon::setThemeSearchPaths(QStringList() << searchPath);
QCOMPARE(QIcon::themeSearchPaths().size(), 1);
QCOMPARE(searchPath, QIcon::themeSearchPaths()[0]);
QString themeName("testtheme");
QIcon::setThemeName(themeName);
QCOMPARE(QIcon::themeName(), themeName);
QIcon appointmentIcon = QIcon::fromTheme("appointment-new");
QVERIFY(!appointmentIcon.isNull());
QVERIFY(!appointmentIcon.availableSizes(QIcon::Normal, QIcon::Off).isEmpty());
QVERIFY(appointmentIcon.availableSizes().contains(QSize(16, 16)));
QVERIFY(appointmentIcon.availableSizes().contains(QSize(22, 22)));
const QPixmap pixmap = appointmentIcon.pixmap(requestedSize);
QCOMPARE(pixmap.size(), QSize(expectedSize, expectedSize));
// We should get the high DPI version of an image if it exists in the correct directory.
// Note that we didn't pass the QWindow to QIcon::pixmap(),
// because QGuiApplication::devicePixelRatio() will be used if no window was given.
QCOMPARE(pixmap.devicePixelRatio(), expectedDpr);
}
int main(int argc, char *argv[])
{
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QGuiApplication app(argc, argv);
Q_UNUSED(app);
tst_QIconHighDpi test;
QTEST_SET_MAIN_SOURCE_PATH
return QTest::qExec(&test, argc, argv);
}
#include "tst_qiconhighdpi.moc"

View File

@ -0,0 +1,8 @@
<RCC>
<qresource prefix="/">
<file>icons/testtheme/16x16/actions/appointment-new.png</file>
<file>icons/testtheme/22x22/actions/appointment-new.png</file>
<file>icons/testtheme/index.theme</file>
<file>icons/testtheme/22x22@2/actions/appointment-new.png</file>
</qresource>
</RCC>