diff options
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 Binary files differdeleted file mode 100644 index 13dfca95bc..0000000000 --- a/src/plugins/platforms/cocoa/images/copyarrowcursor.png +++ /dev/null diff --git a/src/plugins/platforms/cocoa/images/forbiddencursor.png b/src/plugins/platforms/cocoa/images/forbiddencursor.png Binary files differdeleted file mode 100644 index a9f21b4a5e..0000000000 --- a/src/plugins/platforms/cocoa/images/forbiddencursor.png +++ /dev/null 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 ®ion, 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 Binary files differnew file mode 100644 index 0000000000..18b7c6781e --- /dev/null +++ b/tests/auto/gui/image/qiconhighdpi/icons/testtheme/16x16/actions/appointment-new.png 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 Binary files differnew file mode 100644 index 0000000000..d676ffd463 --- /dev/null +++ b/tests/auto/gui/image/qiconhighdpi/icons/testtheme/22x22/actions/appointment-new.png 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 Binary files differnew file mode 100644 index 0000000000..6d094d7b54 --- /dev/null +++ b/tests/auto/gui/image/qiconhighdpi/icons/testtheme/22x22@2/actions/appointment-new.png 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> |
