summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/dbus/dbus.pro2
-rw-r--r--mkspecs/features/mac/default_post.prf7
-rw-r--r--mkspecs/features/qml_plugin.prf3
-rw-r--r--mkspecs/features/qt_module.prf3
-rw-r--r--mkspecs/features/qt_plugin.prf3
-rw-r--r--qmake/generators/mac/pbuilder_pbx.cpp1
-rw-r--r--src/corelib/kernel/qeventdispatcher_cf.mm3
-rw-r--r--src/gui/accessible/qaccessiblecache.cpp14
-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
-rw-r--r--src/plugins/platforms/cocoa/cocoa.pro2
-rw-r--r--src/plugins/platforms/cocoa/images/copyarrowcursor.pngbin1976 -> 0 bytes
-rw-r--r--src/plugins/platforms/cocoa/images/forbiddencursor.pngbin1745 -> 0 bytes
-rw-r--r--src/plugins/platforms/cocoa/qcocoacursor.mm9
-rw-r--r--src/plugins/platforms/cocoa/qcocoaresources.qrc14
-rw-r--r--src/plugins/platforms/ios/kernel.pro2
-rw-r--r--src/plugins/platforms/windows/qwindowskeymapper.cpp3
-rw-r--r--src/widgets/kernel/qwidgetbackingstore.cpp22
-rw-r--r--tests/auto/corelib/io/io.pro6
-rw-r--r--tests/auto/gui/image/image.pro1
-rw-r--r--tests/auto/gui/image/qiconhighdpi/icons/testtheme/16x16/actions/appointment-new.pngbin0 -> 897 bytes
-rw-r--r--tests/auto/gui/image/qiconhighdpi/icons/testtheme/22x22/actions/appointment-new.pngbin0 -> 1411 bytes
-rw-r--r--tests/auto/gui/image/qiconhighdpi/icons/testtheme/22x22@2/actions/appointment-new.pngbin0 -> 1740 bytes
-rw-r--r--tests/auto/gui/image/qiconhighdpi/icons/testtheme/index.theme21
-rw-r--r--tests/auto/gui/image/qiconhighdpi/qiconhighdpi.pro8
-rw-r--r--tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.cpp195
-rw-r--r--tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.qrc8
33 files changed, 500 insertions, 64 deletions
diff --git a/examples/dbus/dbus.pro b/examples/dbus/dbus.pro
index afeb1de0d5..1b2164c4ae 100644
--- a/examples/dbus/dbus.pro
+++ b/examples/dbus/dbus.pro
@@ -4,7 +4,7 @@ TEMPLATE = subdirs
SUBDIRS = listnames \
pingpong
-!uikit: SUBDIRS += complexpingpong
+qtConfig(process): SUBDIRS += complexpingpong
qtHaveModule(widgets) {
SUBDIRS += chat \
diff --git a/mkspecs/features/mac/default_post.prf b/mkspecs/features/mac/default_post.prf
index 1df6c4f8b2..3a1f7584b7 100644
--- a/mkspecs/features/mac/default_post.prf
+++ b/mkspecs/features/mac/default_post.prf
@@ -34,6 +34,13 @@ qt {
!bitcode: QMAKE_LFLAGS += $$QMAKE_LFLAGS_HEADERPAD
+app_extension_api_only {
+ QMAKE_CFLAGS += -fapplication-extension
+ QMAKE_CXXFLAGS += -fapplication-extension
+ QMAKE_CXXFLAGS_PRECOMPILE += -fapplication-extension
+ QMAKE_LFLAGS += -fapplication-extension
+}
+
macx-xcode {
!isEmpty(QMAKE_XCODE_DEBUG_INFORMATION_FORMAT) {
debug_information_format.name = DEBUG_INFORMATION_FORMAT
diff --git a/mkspecs/features/qml_plugin.prf b/mkspecs/features/qml_plugin.prf
index 7e12626db3..de036c129e 100644
--- a/mkspecs/features/qml_plugin.prf
+++ b/mkspecs/features/qml_plugin.prf
@@ -21,6 +21,9 @@ if(win32|mac):!macx-xcode {
CONFIG += relative_qt_rpath # Qt's QML plugins should be relocatable
+# Qt libraries should only use Application Extension safe APIs
+darwin:!no_app_extension_api_only: CONFIG += app_extension_api_only
+
!no_cxx_module:isEmpty(CXX_MODULE) {
CXX_MODULE = $$TARGET
TARGET = declarative_$${TARGET}
diff --git a/mkspecs/features/qt_module.prf b/mkspecs/features/qt_module.prf
index 1f14dec0d4..87cc18e8d5 100644
--- a/mkspecs/features/qt_module.prf
+++ b/mkspecs/features/qt_module.prf
@@ -46,6 +46,9 @@ CONFIG(shared, static|shared):qtConfig(framework): \
CONFIG += relative_qt_rpath # Qt libraries should be relocatable
+# Qt libraries should only use Application Extension safe APIs
+darwin:!no_app_extension_api_only: CONFIG += app_extension_api_only
+
ucmodule = $$upper($$MODULE)
isEmpty(MODULE_INCNAME): MODULE_INCNAME = $$TARGET
diff --git a/mkspecs/features/qt_plugin.prf b/mkspecs/features/qt_plugin.prf
index 80d9c87e05..62e1b69fde 100644
--- a/mkspecs/features/qt_plugin.prf
+++ b/mkspecs/features/qt_plugin.prf
@@ -32,6 +32,9 @@ tool_plugin {
CONFIG += relative_qt_rpath # Qt's plugins should be relocatable
+# Qt libraries should only use Application Extension safe APIs
+darwin:!no_app_extension_api_only: CONFIG += app_extension_api_only
+
CONFIG(static, static|shared)|prefix_build {
isEmpty(MODULE): MODULE = $$basename(TARGET)
diff --git a/qmake/generators/mac/pbuilder_pbx.cpp b/qmake/generators/mac/pbuilder_pbx.cpp
index f42b3021f1..728e654be8 100644
--- a/qmake/generators/mac/pbuilder_pbx.cpp
+++ b/qmake/generators/mac/pbuilder_pbx.cpp
@@ -1485,6 +1485,7 @@ ProjectBuilderMakefileGenerator::writeMakeParts(QTextStream &t)
}
}
settings.insert("COPY_PHASE_STRIP", (as_release ? "YES" : "NO"));
+ settings.insert("APPLICATION_EXTENSION_API_ONLY", project->isActiveConfig("app_extension_api_only") ? "YES" : "NO");
// required for tvOS (and watchos), optional on iOS (deployment target >= iOS 6.0)
settings.insert("ENABLE_BITCODE", project->isActiveConfig("bitcode") ? "YES" : "NO");
settings.insert("GCC_GENERATE_DEBUGGING_SYMBOLS", as_release ? "NO" : "YES");
diff --git a/src/corelib/kernel/qeventdispatcher_cf.mm b/src/corelib/kernel/qeventdispatcher_cf.mm
index 9ac48f144d..27eb3d0337 100644
--- a/src/corelib/kernel/qeventdispatcher_cf.mm
+++ b/src/corelib/kernel/qeventdispatcher_cf.mm
@@ -80,7 +80,8 @@ QT_USE_NAMESPACE
#elif defined(Q_OS_WATCHOS)
object:[WKExtension sharedExtension]];
#else
- object:[UIApplication sharedApplication]];
+ // Use performSelector so this can work in an App Extension
+ object:[[UIApplication class] performSelector:@selector(sharedApplication)]];
#endif
}
diff --git a/src/gui/accessible/qaccessiblecache.cpp b/src/gui/accessible/qaccessiblecache.cpp
index 43c4b15cb0..097634c0a3 100644
--- a/src/gui/accessible/qaccessiblecache.cpp
+++ b/src/gui/accessible/qaccessiblecache.cpp
@@ -49,11 +49,21 @@ QT_BEGIN_NAMESPACE
\brief Maintains a cache of accessible interfaces.
*/
-Q_GLOBAL_STATIC(QAccessibleCache, qAccessibleCache)
+static QAccessibleCache *accessibleCache = nullptr;
+
+static void cleanupAccessibleCache()
+{
+ delete accessibleCache;
+ accessibleCache = Q_NULLPTR;
+}
QAccessibleCache *QAccessibleCache::instance()
{
- return qAccessibleCache;
+ if (!accessibleCache) {
+ accessibleCache = new QAccessibleCache;
+ qAddPostRoutine(cleanupAccessibleCache);
+ }
+ return accessibleCache;
}
/*
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;
diff --git a/src/plugins/platforms/cocoa/cocoa.pro b/src/plugins/platforms/cocoa/cocoa.pro
index 0664841c2d..62935210be 100644
--- a/src/plugins/platforms/cocoa/cocoa.pro
+++ b/src/plugins/platforms/cocoa/cocoa.pro
@@ -86,6 +86,8 @@ QT += \
accessibility_support-private clipboard_support-private theme_support-private \
fontdatabase_support-private graphics_support-private cgl_support-private
+CONFIG += no_app_extension_api_only
+
qtHaveModule(widgets) {
OBJECTIVE_SOURCES += \
qpaintengine_mac.mm \
diff --git a/src/plugins/platforms/cocoa/images/copyarrowcursor.png b/src/plugins/platforms/cocoa/images/copyarrowcursor.png
deleted file mode 100644
index 13dfca95bc..0000000000
--- a/src/plugins/platforms/cocoa/images/copyarrowcursor.png
+++ /dev/null
Binary files differ
diff --git a/src/plugins/platforms/cocoa/images/forbiddencursor.png b/src/plugins/platforms/cocoa/images/forbiddencursor.png
deleted file mode 100644
index a9f21b4a5e..0000000000
--- a/src/plugins/platforms/cocoa/images/forbiddencursor.png
+++ /dev/null
Binary files differ
diff --git a/src/plugins/platforms/cocoa/qcocoacursor.mm b/src/plugins/platforms/cocoa/qcocoacursor.mm
index 3df2a7c962..99a136d384 100644
--- a/src/plugins/platforms/cocoa/qcocoacursor.mm
+++ b/src/plugins/platforms/cocoa/qcocoacursor.mm
@@ -97,6 +97,9 @@ NSCursor *QCocoaCursor::convertCursor(QCursor *cursor)
case Qt::ArrowCursor:
cocoaCursor= [NSCursor arrowCursor];
break;
+ case Qt::ForbiddenCursor:
+ cocoaCursor = [NSCursor operationNotAllowedCursor];
+ break;
case Qt::CrossCursor:
cocoaCursor = [NSCursor crosshairCursor];
break;
@@ -123,7 +126,7 @@ NSCursor *QCocoaCursor::convertCursor(QCursor *cursor)
cocoaCursor = [NSCursor crosshairCursor];
break;
case Qt::DragCopyCursor:
- cocoaCursor = [NSCursor crosshairCursor];
+ cocoaCursor = [NSCursor dragCopyCursor];
break;
case Qt::DragLinkCursor:
cocoaCursor = [NSCursor dragLinkCursor];
@@ -235,10 +238,6 @@ NSCursor *QCocoaCursor::createCursorData(QCursor *cursor)
QPixmap pixmap = QPixmap(QLatin1String(":/qt-project.org/mac/cursors/images/waitcursor.png"));
return createCursorFromPixmap(pixmap, hotspot);
break; }
- case Qt::ForbiddenCursor: {
- QPixmap pixmap = QPixmap(QLatin1String(":/qt-project.org/mac/cursors/images/forbiddencursor.png"));
- return createCursorFromPixmap(pixmap, hotspot);
- break; }
#define QT_USE_APPROXIMATE_CURSORS
#ifdef QT_USE_APPROXIMATE_CURSORS
case Qt::SizeVerCursor:
diff --git a/src/plugins/platforms/cocoa/qcocoaresources.qrc b/src/plugins/platforms/cocoa/qcocoaresources.qrc
index b06ab65ea8..1c4b941b9b 100644
--- a/src/plugins/platforms/cocoa/qcocoaresources.qrc
+++ b/src/plugins/platforms/cocoa/qcocoaresources.qrc
@@ -1,9 +1,7 @@
-<!DOCTYPE RCC><RCC version="1.0">
-<qresource prefix="/qt-project.org/mac/cursors">
-<file>images/copyarrowcursor.png</file>
-<file>images/forbiddencursor.png</file>
-<file>images/spincursor.png</file>
-<file>images/waitcursor.png</file>
-<file>images/sizeallcursor.png</file>
-</qresource>
+<RCC>
+ <qresource prefix="/qt-project.org/mac/cursors">
+ <file>images/spincursor.png</file>
+ <file>images/waitcursor.png</file>
+ <file>images/sizeallcursor.png</file>
+ </qresource>
</RCC>
diff --git a/src/plugins/platforms/ios/kernel.pro b/src/plugins/platforms/ios/kernel.pro
index 71257d09f7..6eb9f2c534 100644
--- a/src/plugins/platforms/ios/kernel.pro
+++ b/src/plugins/platforms/ios/kernel.pro
@@ -5,6 +5,8 @@ TARGET = qios
# application's main() when the plugin is a shared library.
qtConfig(shared): CONFIG += static
+CONFIG += no_app_extension_api_only
+
QT += \
core-private gui-private \
clipboard_support-private fontdatabase_support-private graphics_support-private
diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp
index ab806fd3ea..24c2df86d4 100644
--- a/src/plugins/platforms/windows/qwindowskeymapper.cpp
+++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp
@@ -971,7 +971,8 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, const MSG &ms
state = state ^ Qt::ShiftModifier;
else if (code == Qt::Key_Alt)
state = state ^ Qt::AltModifier;
-
+ else if (code == 0 && modifiersIndex != 0)
+ code = keyLayout[vk_key].qtKey[0];
// If the bit 24 of lParm is set you received a enter,
// otherwise a Return. (This is the extended key bit)
if ((code == Qt::Key_Return) && (msg.lParam & 0x1000000))
diff --git a/src/widgets/kernel/qwidgetbackingstore.cpp b/src/widgets/kernel/qwidgetbackingstore.cpp
index 910704498c..781ad9600d 100644
--- a/src/widgets/kernel/qwidgetbackingstore.cpp
+++ b/src/widgets/kernel/qwidgetbackingstore.cpp
@@ -164,23 +164,11 @@ static void showYellowThing_win(QWidget *widget, const QRegion &region, int msec
return;
const HDC hdc = reinterpret_cast<HDC>(hdcV);
- HBRUSH brush;
- static int i = 0;
- switch (i) {
- case 0:
- brush = CreateSolidBrush(RGB(255, 255, 0));
- break;
- case 1:
- brush = CreateSolidBrush(RGB(255, 200, 55));
- break;
- case 2:
- brush = CreateSolidBrush(RGB(200, 255, 55));
- break;
- case 3:
- brush = CreateSolidBrush(RGB(200, 200, 0));
- break;
- }
- i = (i + 1) & 3;
+ static const COLORREF colors[] = {RGB(255, 255, 0), RGB(255, 200, 55), RGB(200, 255, 55), RGB(200, 200, 0)};
+
+ static size_t i = 0;
+ const HBRUSH brush = CreateSolidBrush(colors[i]);
+ i = (i + 1) % (sizeof(colors) / sizeof(colors[0]));
for (const QRect &rect : region) {
RECT winRect;
diff --git a/tests/auto/corelib/io/io.pro b/tests/auto/corelib/io/io.pro
index c37c060acd..470a1dde26 100644
--- a/tests/auto/corelib/io/io.pro
+++ b/tests/auto/corelib/io/io.pro
@@ -63,8 +63,10 @@ win32:!qtConfig(private_tests): SUBDIRS -= \
!qtConfig(processenvironment): SUBDIRS -= \
qprocessenvironment
-winrt: SUBDIRS -= \
+!qtConfig(process): SUBDIRS -= \
qprocess \
- qprocess-noapplication \
+ qprocess-noapplication
+
+winrt: SUBDIRS -= \
qstorageinfo \
qwinoverlappedionotifier
diff --git a/tests/auto/gui/image/image.pro b/tests/auto/gui/image/image.pro
index 5a74df4c78..f4e2ab20ba 100644
--- a/tests/auto/gui/image/image.pro
+++ b/tests/auto/gui/image/image.pro
@@ -10,6 +10,7 @@ SUBDIRS=\
qmovie \
qpicture \
qicon \
+ qiconhighdpi
!qtHaveModule(network): SUBDIRS -= \
qimagereader
diff --git a/tests/auto/gui/image/qiconhighdpi/icons/testtheme/16x16/actions/appointment-new.png b/tests/auto/gui/image/qiconhighdpi/icons/testtheme/16x16/actions/appointment-new.png
new file mode 100644
index 0000000000..18b7c6781e
--- /dev/null
+++ b/tests/auto/gui/image/qiconhighdpi/icons/testtheme/16x16/actions/appointment-new.png
Binary files differ
diff --git a/tests/auto/gui/image/qiconhighdpi/icons/testtheme/22x22/actions/appointment-new.png b/tests/auto/gui/image/qiconhighdpi/icons/testtheme/22x22/actions/appointment-new.png
new file mode 100644
index 0000000000..d676ffd463
--- /dev/null
+++ b/tests/auto/gui/image/qiconhighdpi/icons/testtheme/22x22/actions/appointment-new.png
Binary files differ
diff --git a/tests/auto/gui/image/qiconhighdpi/icons/testtheme/22x22@2/actions/appointment-new.png b/tests/auto/gui/image/qiconhighdpi/icons/testtheme/22x22@2/actions/appointment-new.png
new file mode 100644
index 0000000000..6d094d7b54
--- /dev/null
+++ b/tests/auto/gui/image/qiconhighdpi/icons/testtheme/22x22@2/actions/appointment-new.png
Binary files differ
diff --git a/tests/auto/gui/image/qiconhighdpi/icons/testtheme/index.theme b/tests/auto/gui/image/qiconhighdpi/icons/testtheme/index.theme
new file mode 100644
index 0000000000..6ab6c15c42
--- /dev/null
+++ b/tests/auto/gui/image/qiconhighdpi/icons/testtheme/index.theme
@@ -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
diff --git a/tests/auto/gui/image/qiconhighdpi/qiconhighdpi.pro b/tests/auto/gui/image/qiconhighdpi/qiconhighdpi.pro
new file mode 100644
index 0000000000..17553158bc
--- /dev/null
+++ b/tests/auto/gui/image/qiconhighdpi/qiconhighdpi.pro
@@ -0,0 +1,8 @@
+CONFIG += testcase
+TARGET = tst_qicon
+
+QT += testlib
+SOURCES += tst_qiconhighdpi.cpp
+RESOURCES = tst_qiconhighdpi.qrc
+
+TESTDATA += icons/*
diff --git a/tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.cpp b/tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.cpp
new file mode 100644
index 0000000000..ce7f68a0a6
--- /dev/null
+++ b/tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.cpp
@@ -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"
diff --git a/tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.qrc b/tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.qrc
new file mode 100644
index 0000000000..80b5e38ee6
--- /dev/null
+++ b/tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.qrc
@@ -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>