summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/windows
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/windows')
-rw-r--r--src/plugins/platforms/windows/CMakeLists.txt39
-rw-r--r--src/plugins/platforms/windows/qtwindowsglobal.h36
-rw-r--r--src/plugins/platforms/windows/qwindowsapplication.cpp31
-rw-r--r--src/plugins/platforms/windows/qwindowsapplication.h9
-rw-r--r--src/plugins/platforms/windows/qwindowsbackingstore.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowsclipboard.cpp4
-rw-r--r--src/plugins/platforms/windows/qwindowscombase.h80
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.cpp342
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.h21
-rw-r--r--src/plugins/platforms/windows/qwindowscursor.cpp27
-rw-r--r--src/plugins/platforms/windows/qwindowsdialoghelpers.cpp84
-rw-r--r--src/plugins/platforms/windows/qwindowsdrag.cpp95
-rw-r--r--src/plugins/platforms/windows/qwindowsdrag.h6
-rw-r--r--src/plugins/platforms/windows/qwindowsdropdataobject.cpp10
-rw-r--r--src/plugins/platforms/windows/qwindowsglcontext.cpp16
-rw-r--r--src/plugins/platforms/windows/qwindowsiconengine.cpp394
-rw-r--r--src/plugins/platforms/windows/qwindowsiconengine.h47
-rw-r--r--src/plugins/platforms/windows/qwindowsinputcontext.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowsintegration.cpp268
-rw-r--r--src/plugins/platforms/windows/qwindowsintegration.h13
-rw-r--r--src/plugins/platforms/windows/qwindowsinternalmimedata.cpp12
-rw-r--r--src/plugins/platforms/windows/qwindowskeymapper.cpp137
-rw-r--r--src/plugins/platforms/windows/qwindowskeymapper.h8
-rw-r--r--src/plugins/platforms/windows/qwindowsmenu.cpp3
-rw-r--r--src/plugins/platforms/windows/qwindowsmenu.h1
-rw-r--r--src/plugins/platforms/windows/qwindowsmimeregistry.cpp (renamed from src/plugins/platforms/windows/qwindowsmime.cpp)280
-rw-r--r--src/plugins/platforms/windows/qwindowsmimeregistry.h (renamed from src/plugins/platforms/windows/qwindowsmime.h)28
-rw-r--r--src/plugins/platforms/windows/qwindowsmousehandler.cpp39
-rw-r--r--src/plugins/platforms/windows/qwindowsole.cpp8
-rw-r--r--src/plugins/platforms/windows/qwindowsole.h6
-rw-r--r--src/plugins/platforms/windows/qwindowsopengltester.cpp1
-rw-r--r--src/plugins/platforms/windows/qwindowspointerhandler.cpp67
-rw-r--r--src/plugins/platforms/windows/qwindowsscreen.cpp240
-rw-r--r--src/plugins/platforms/windows/qwindowsscreen.h15
-rw-r--r--src/plugins/platforms/windows/qwindowsservices.cpp81
-rw-r--r--src/plugins/platforms/windows/qwindowssystemtrayicon.cpp78
-rw-r--r--src/plugins/platforms/windows/qwindowssystemtrayicon.h2
-rw-r--r--src/plugins/platforms/windows/qwindowstabletsupport.cpp3
-rw-r--r--src/plugins/platforms/windows/qwindowstheme.cpp519
-rw-r--r--src/plugins/platforms/windows/qwindowstheme.h8
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.cpp444
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.h10
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp26
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.h1
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiabaseprovider.h4
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiaexpandcollapseprovider.h2
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiagriditemprovider.h2
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiagridprovider.h3
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiainvokeprovider.h3
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp174
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h11
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.cpp2
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiarangevalueprovider.h2
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.cpp38
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.h2
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.cpp168
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.h21
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiatableitemprovider.h2
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiatableprovider.h3
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.cpp21
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.h19
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.cpp53
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.h4
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiatoggleprovider.h3
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp3
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiautils.h2
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.cpp74
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.h70
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiavalueprovider.h3
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.h3
70 files changed, 2764 insertions, 1471 deletions
diff --git a/src/plugins/platforms/windows/CMakeLists.txt b/src/plugins/platforms/windows/CMakeLists.txt
index af9c1fccb4..4b92317978 100644
--- a/src/plugins/platforms/windows/CMakeLists.txt
+++ b/src/plugins/platforms/windows/CMakeLists.txt
@@ -1,7 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-# Generated from windows.pro.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## QWindowsIntegrationPlugin Plugin:
@@ -10,26 +8,26 @@
qt_internal_add_plugin(QWindowsIntegrationPlugin
OUTPUT_NAME qwindows
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES windows # special case
+ DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES windows
SOURCES
main.cpp
qtwindowsglobal.h
qwin10helpers.cpp qwin10helpers.h
qwindowsapplication.cpp qwindowsapplication.h
qwindowsbackingstore.cpp qwindowsbackingstore.h
- qwindowscombase.h
qwindowscontext.cpp qwindowscontext.h
qwindowscursor.cpp qwindowscursor.h
qwindowsdialoghelpers.cpp qwindowsdialoghelpers.h
qwindowsdropdataobject.cpp qwindowsdropdataobject.h
qwindowsgdiintegration.cpp qwindowsgdiintegration.h
qwindowsgdinativeinterface.cpp qwindowsgdinativeinterface.h
+ qwindowsiconengine.cpp qwindowsiconengine.h
qwindowsinputcontext.cpp qwindowsinputcontext.h
qwindowsintegration.cpp qwindowsintegration.h
qwindowsinternalmimedata.cpp qwindowsinternalmimedata.h
qwindowskeymapper.cpp qwindowskeymapper.h
qwindowsmenu.cpp qwindowsmenu.h
- qwindowsmime.cpp qwindowsmime.h
+ qwindowsmimeregistry.cpp qwindowsmimeregistry.h
qwindowsmousehandler.cpp qwindowsmousehandler.h
qwindowsnativeinterface.cpp qwindowsnativeinterface.h
qwindowsole.cpp qwindowsole.h
@@ -40,6 +38,8 @@ qt_internal_add_plugin(QWindowsIntegrationPlugin
qwindowstheme.cpp qwindowstheme.h
qwindowsthreadpoolrunner.h
qwindowswindow.cpp qwindowswindow.h
+ NO_UNITY_BUILD_SOURCES
+ qwindowspointerhandler.cpp
DEFINES
QT_NO_CAST_FROM_ASCII
QT_NO_FOREACH
@@ -56,6 +56,7 @@ qt_internal_add_plugin(QWindowsIntegrationPlugin
imm32
ole32
oleaut32
+ setupapi
shell32
shlwapi
user32
@@ -83,10 +84,6 @@ qt_internal_add_resource(QWindowsIntegrationPlugin "openglblacklists"
${openglblacklists_resource_files}
)
-
-#### Keys ignored in scope 1:.:.:windows.pro:<TRUE>:
-# OTHER_FILES = "windows.json"
-
## Scopes:
#####################################################################
@@ -98,9 +95,6 @@ qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION QT_FEATURE_opengl
Qt::OpenGLPrivate
)
-#### Keys ignored in scope 3:.:.:windows.pro:NOT TARGET___equals____ss_QT_DEFAULT_QPA_PLUGIN:
-# PLUGIN_EXTENDS = "-"
-
qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION QT_FEATURE_opengl AND NOT QT_FEATURE_dynamicgl
LIBRARIES
opengl32
@@ -109,6 +103,8 @@ qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION QT_FEATURE_opengl
qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION MINGW
LIBRARIES
uuid
+ NO_PCH_SOURCES
+ qwindowspointerhandler.cpp
)
qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION QT_FEATURE_systemtrayicon
@@ -179,6 +175,7 @@ endif()
qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION QT_FEATURE_accessibility
SOURCES
+ uiautomation/qwindowsuiautomation.cpp uiautomation/qwindowsuiautomation.h
uiautomation/qwindowsuiaaccessibility.cpp uiautomation/qwindowsuiaaccessibility.h
uiautomation/qwindowsuiabaseprovider.cpp uiautomation/qwindowsuiabaseprovider.h
uiautomation/qwindowsuiaexpandcollapseprovider.cpp uiautomation/qwindowsuiaexpandcollapseprovider.h
@@ -200,13 +197,17 @@ qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION QT_FEATURE_accessi
uiautomation/qwindowsuiawindowprovider.cpp uiautomation/qwindowsuiawindowprovider.h
)
+if(QT_FEATURE_accessibility)
+ find_library(UI_AUTOMATION_LIBRARY uiautomationcore)
+ if(UI_AUTOMATION_LIBRARY)
+ qt_internal_extend_target(QWindowsIntegrationPlugin
+ LIBRARIES
+ ${UI_AUTOMATION_LIBRARY}
+ )
+ endif()
+endif()
+
qt_internal_extend_target(QWindowsIntegrationPlugin CONDITION MINGW AND QT_FEATURE_accessibility
LIBRARIES
uuid
)
-
-# begin special case
-if (MINGW)
- set_source_files_properties(qwindowspointerhandler.cpp PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
-endif()
-# end special case
diff --git a/src/plugins/platforms/windows/qtwindowsglobal.h b/src/plugins/platforms/windows/qtwindowsglobal.h
index 9a65603e24..96a72600eb 100644
--- a/src/plugins/platforms/windows/qtwindowsglobal.h
+++ b/src/plugins/platforms/windows/qtwindowsglobal.h
@@ -65,7 +65,7 @@ QT_BEGIN_NAMESPACE
namespace QtWindows
{
-enum
+enum WindowsEventTypeFlags
{
WindowEventFlag = 0x10000,
MouseEventFlag = 0x20000,
@@ -107,6 +107,7 @@ enum WindowsEventType // Simplify event types
PointerActivateWindowEvent = WindowEventFlag + 24,
DpiScaledSizeEvent = WindowEventFlag + 25,
DpiChangedAfterParentEvent = WindowEventFlag + 27,
+ TaskbarButtonCreated = WindowEventFlag + 28,
MouseEvent = MouseEventFlag + 1,
MouseWheelEvent = MouseEventFlag + 2,
CursorEvent = MouseEventFlag + 3,
@@ -118,7 +119,7 @@ enum WindowsEventType // Simplify event types
NonClientPointerEvent = NonClientEventFlag + PointerEventFlag + 4,
KeyEvent = KeyEventFlag + 1,
KeyDownEvent = KeyEventFlag + KeyDownEventFlag + 1,
- KeyboardLayoutChangeEvent = KeyEventFlag + 2,
+ InputLanguageChangeEvent = KeyEventFlag + 2,
InputMethodKeyEvent = InputMethodEventFlag + KeyEventFlag + 1,
InputMethodKeyDownEvent = InputMethodEventFlag + KeyEventFlag + KeyDownEventFlag + 1,
ClipboardEvent = ClipboardEventFlag + 1,
@@ -146,20 +147,31 @@ enum WindowsEventType // Simplify event types
GestureEvent = 124,
UnknownEvent = 542
};
+Q_DECLARE_MIXED_ENUM_OPERATORS(bool, WindowsEventTypeFlags, WindowsEventType);
+Q_DECLARE_MIXED_ENUM_OPERATORS(bool, WindowsEventType, WindowsEventTypeFlags);
-// Matches Process_DPI_Awareness (Windows 8.1 onwards), used for SetProcessDpiAwareness()
-enum ProcessDpiAwareness
+enum class DpiAwareness
{
- ProcessDpiUnaware,
- ProcessSystemDpiAware,
- ProcessPerMonitorDpiAware,
- ProcessPerMonitorV2DpiAware // Qt extension (not in Process_DPI_Awareness)
+ Invalid = -1,
+ Unaware,
+ System,
+ PerMonitor,
+ PerMonitorVersion2,
+ Unaware_GdiScaled
};
} // namespace QtWindows
inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamIn, LPARAM lParamIn)
{
+ static const UINT WM_TASKBAR_BUTTON_CREATED = []{
+ UINT message = RegisterWindowMessage(L"TaskbarButtonCreated");
+ // In case the application is run elevated, allow the
+ // TaskbarButtonCreated message through.
+ ChangeWindowMessageFilter(message, MSGFLT_ADD);
+ return message;
+ }();
+
switch (message) {
case WM_PAINT:
case WM_ERASEBKGND:
@@ -218,7 +230,7 @@ inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamI
return QtWindows::InputMethodKeyDownEvent;
#ifdef WM_INPUTLANGCHANGE
case WM_INPUTLANGCHANGE:
- return QtWindows::KeyboardLayoutChangeEvent;
+ return QtWindows::InputLanguageChangeEvent;
#endif // WM_INPUTLANGCHANGE
case WM_TOUCH:
return QtWindows::TouchEvent;
@@ -310,9 +322,15 @@ inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamI
return QtWindows::NonClientPointerEvent;
if (message >= WM_POINTERUPDATE && message <= WM_POINTERHWHEEL)
return QtWindows::PointerEvent;
+ if (message == WM_TASKBAR_BUTTON_CREATED)
+ return QtWindows::TaskbarButtonCreated;
return QtWindows::UnknownEvent;
}
+#ifndef QT_NO_DEBUG_STREAM
+extern QDebug operator<<(QDebug, QtWindows::DpiAwareness);
+#endif
+
QT_END_NAMESPACE
#endif // QTWINDOWSGLOBAL_H
diff --git a/src/plugins/platforms/windows/qwindowsapplication.cpp b/src/plugins/platforms/windows/qwindowsapplication.cpp
index ce52cc817e..42e34ac99f 100644
--- a/src/plugins/platforms/windows/qwindowsapplication.cpp
+++ b/src/plugins/platforms/windows/qwindowsapplication.cpp
@@ -4,11 +4,17 @@
#include "qwindowsapplication.h"
#include "qwindowsclipboard.h"
#include "qwindowscontext.h"
-#include "qwindowsmime.h"
+#include "qwindowsmimeregistry.h"
#include "qwin10helpers.h"
#include "qwindowsopengltester.h"
+#include "qwindowswindow.h"
+#include "qwindowsintegration.h"
+#include "qwindowstheme.h"
-#include <QtCore/QVariant>
+#include <QtCore/qvariant.h>
+#include <QtCore/private/qfunctions_win_p.h>
+
+#include <QtGui/qpalette.h>
QT_BEGIN_NAMESPACE
@@ -36,6 +42,11 @@ void QWindowsApplication::setWindowActivationBehavior(WindowActivationBehavior b
m_windowActivationBehavior = behavior;
}
+void QWindowsApplication::setHasBorderInFullScreenDefault(bool border)
+{
+ QWindowsWindow::setHasBorderInFullScreenDefault(border);
+}
+
bool QWindowsApplication::isTabletMode() const
{
#if QT_CONFIG(clipboard)
@@ -61,11 +72,6 @@ bool QWindowsApplication::setWinTabEnabled(bool enabled)
return enabled ? ctx->initTablet() : ctx->disposeTablet();
}
-bool QWindowsApplication::isDarkMode() const
-{
- return QWindowsContext::isDarkMode();
-}
-
QWindowsApplication::DarkModeHandling QWindowsApplication::darkModeHandling() const
{
return m_darkModeHandling;
@@ -76,13 +82,13 @@ void QWindowsApplication::setDarkModeHandling(QWindowsApplication::DarkModeHandl
m_darkModeHandling = handling;
}
-void QWindowsApplication::registerMime(QNativeInterface::Private::QWindowsMime *mime)
+void QWindowsApplication::registerMime(QWindowsMimeConverter *mime)
{
if (auto ctx = QWindowsContext::instance())
ctx->mimeConverter().registerMime(mime);
}
-void QWindowsApplication::unregisterMime(QNativeInterface::Private::QWindowsMime *mime)
+void QWindowsApplication::unregisterMime(QWindowsMimeConverter *mime)
{
if (auto ctx = QWindowsContext::instance())
ctx->mimeConverter().unregisterMime(mime);
@@ -90,7 +96,7 @@ void QWindowsApplication::unregisterMime(QNativeInterface::Private::QWindowsMime
int QWindowsApplication::registerMimeType(const QString &mime)
{
- return QWindowsMimeConverter::registerMimeType(mime);
+ return QWindowsMimeRegistry::registerMimeType(mime);
}
HWND QWindowsApplication::createMessageWindow(const QString &classNameTemplate,
@@ -132,4 +138,9 @@ QVariant QWindowsApplication::gpuList() const
return result;
}
+void QWindowsApplication::populateLightSystemPalette(QPalette &result) const
+{
+ result = QWindowsTheme::systemPalette(Qt::ColorScheme::Light);
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/qwindowsapplication.h b/src/plugins/platforms/windows/qwindowsapplication.h
index fc6d8b5b76..0918df91af 100644
--- a/src/plugins/platforms/windows/qwindowsapplication.h
+++ b/src/plugins/platforms/windows/qwindowsapplication.h
@@ -17,17 +17,18 @@ public:
WindowActivationBehavior windowActivationBehavior() const override;
void setWindowActivationBehavior(WindowActivationBehavior behavior) override;
+ void setHasBorderInFullScreenDefault(bool border) override;
+
bool isTabletMode() const override;
bool isWinTabEnabled() const override;
bool setWinTabEnabled(bool enabled) override;
- bool isDarkMode() const override;
DarkModeHandling darkModeHandling() const override;
void setDarkModeHandling(DarkModeHandling handling) override;
- void registerMime(QNativeInterface::Private::QWindowsMime *mime) override;
- void unregisterMime(QNativeInterface::Private::QWindowsMime *mime) override;
+ void registerMime(QWindowsMimeConverter *mime) override;
+ void unregisterMime(QWindowsMimeConverter *mime) override;
int registerMimeType(const QString &mime) override;
@@ -41,6 +42,8 @@ public:
QVariant gpu() const override;
QVariant gpuList() const override;
+ void populateLightSystemPalette(QPalette &palette) const override;
+
private:
WindowActivationBehavior m_windowActivationBehavior = DefaultActivateWindow;
TouchWindowTouchTypes m_touchWindowTouchTypes = NormalTouch;
diff --git a/src/plugins/platforms/windows/qwindowsbackingstore.cpp b/src/plugins/platforms/windows/qwindowsbackingstore.cpp
index 0f9d0172d9..07918f6094 100644
--- a/src/plugins/platforms/windows/qwindowsbackingstore.cpp
+++ b/src/plugins/platforms/windows/qwindowsbackingstore.cpp
@@ -117,7 +117,7 @@ void QWindowsBackingStore::resize(const QSize &size, const QRegion &region)
if (QImage::toPixelFormat(format).alphaUsage() == QPixelFormat::UsesAlpha)
m_alphaNeedsFill = true;
else // upgrade but here we know app painting does not rely on alpha hence no need to fill
- format = qt_maybeAlphaVersionWithSameDepth(format);
+ format = qt_maybeDataCompatibleAlphaVersion(format);
QWindowsNativeImage *oldwni = m_image.data();
auto *newwni = new QWindowsNativeImage(size.width(), size.height(), format);
diff --git a/src/plugins/platforms/windows/qwindowsclipboard.cpp b/src/plugins/platforms/windows/qwindowsclipboard.cpp
index 9bdd331467..7a6d41e0b3 100644
--- a/src/plugins/platforms/windows/qwindowsclipboard.cpp
+++ b/src/plugins/platforms/windows/qwindowsclipboard.cpp
@@ -4,7 +4,6 @@
#include "qwindowsclipboard.h"
#include "qwindowscontext.h"
#include "qwindowsole.h"
-#include "qwindowsmime.h"
#include <QtGui/qguiapplication.h>
#include <QtGui/qclipboard.h>
@@ -17,6 +16,7 @@
#include <QtCore/qthread.h>
#include <QtCore/qvariant.h>
#include <QtCore/qurl.h>
+#include <QtCore/private/qsystemerror_p.h>
#include <QtGui/private/qwindowsguieventdispatcher_p.h>
@@ -301,7 +301,7 @@ void QWindowsClipboard::setMimeData(QMimeData *mimeData, QClipboard::Mode mode)
mimeData->formats().join(u", ") : QString(QStringLiteral("NULL"));
qErrnoWarning("OleSetClipboard: Failed to set mime data (%s) on clipboard: %s",
qPrintable(mimeDataFormats),
- QWindowsContext::comErrorString(src).constData());
+ qPrintable(QSystemError::windowsComString(src)));
releaseIData();
return;
}
diff --git a/src/plugins/platforms/windows/qwindowscombase.h b/src/plugins/platforms/windows/qwindowscombase.h
deleted file mode 100644
index 04d4dc51cf..0000000000
--- a/src/plugins/platforms/windows/qwindowscombase.h
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef QWINDOWSCOMBASE_H
-#define QWINDOWSCOMBASE_H
-
-#include <QtCore/qglobal.h>
-
-#include <unknwn.h>
-
-QT_BEGIN_NAMESPACE
-
-// The __uuidof operator of MinGW does not work for all interfaces (for example,
-// IAccessible2). Specializations of this function can be provides to work
-// around this.
-template <class DesiredInterface>
-static IID qUuidOf() { return __uuidof(DesiredInterface); }
-
-// Helper for implementing IUnknown::QueryInterface.
-template <class DesiredInterface, class Derived>
-bool qWindowsComQueryInterface(Derived *d, REFIID id, LPVOID *iface)
-{
- if (id == qUuidOf<DesiredInterface>()) {
- *iface = static_cast<DesiredInterface *>(d);
- d->AddRef();
- return true;
- }
- return false;
-}
-
-// Helper for implementing IUnknown::QueryInterface for IUnknown
-// in the case of multiple inheritance via the first inherited class.
-template <class FirstInheritedInterface, class Derived>
-bool qWindowsComQueryUnknownInterfaceMulti(Derived *d, REFIID id, LPVOID *iface)
-{
- if (id == __uuidof(IUnknown)) {
- *iface = static_cast<FirstInheritedInterface *>(d);
- d->AddRef();
- return true;
- }
- return false;
-}
-
-// Helper base class to provide IUnknown methods for COM classes (single inheritance)
-template <class ComInterface> class QWindowsComBase : public ComInterface
-{
- Q_DISABLE_COPY_MOVE(QWindowsComBase)
-public:
- explicit QWindowsComBase(ULONG initialRefCount = 1) : m_ref(initialRefCount) {}
- virtual ~QWindowsComBase() = default;
-
- HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, LPVOID *iface) override
- {
- *iface = nullptr;
- return qWindowsComQueryInterface<IUnknown>(this, id, iface) || qWindowsComQueryInterface<ComInterface>(this, id, iface)
- ? S_OK : E_NOINTERFACE;
- }
-
- ULONG STDMETHODCALLTYPE AddRef() override { return ++m_ref; }
-
- ULONG STDMETHODCALLTYPE Release() override
- {
- if (!--m_ref) {
- delete this;
- return 0;
- }
- return m_ref;
- }
-
-private:
- ULONG m_ref;
-};
-
-// Clang does not consider __declspec(nothrow) as nothrow
-QT_WARNING_DISABLE_CLANG("-Wmicrosoft-exception-spec")
-QT_WARNING_DISABLE_CLANG("-Wmissing-exception-spec")
-
-QT_END_NAMESPACE
-
-#endif // QWINDOWSCOMBASE_H
diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp
index c95f2ef272..de65a2171d 100644
--- a/src/plugins/platforms/windows/qwindowscontext.cpp
+++ b/src/plugins/platforms/windows/qwindowscontext.cpp
@@ -11,7 +11,7 @@
#include "qwindowspointerhandler.h"
#include "qtwindowsglobal.h"
#include "qwindowsmenu.h"
-#include "qwindowsmime.h"
+#include "qwindowsmimeregistry.h"
#include "qwindowsinputcontext.h"
#if QT_CONFIG(tabletevent)
# include "qwindowstabletsupport.h"
@@ -45,13 +45,17 @@
#include <QtCore/qscopedpointer.h>
#include <QtCore/quuid.h>
#include <QtCore/private/qwinregistry_p.h>
+#if QT_CONFIG(cpp_winrt)
+# include <QtCore/private/qfactorycacheregistration_p.h>
+#endif
+#include <QtCore/private/qsystemerror_p.h>
#include <QtGui/private/qwindowsguieventdispatcher_p.h>
+#include <QtGui/private/qwindowsthemecache_p.h>
#include <stdlib.h>
#include <stdio.h>
#include <windowsx.h>
-#include <comdef.h>
#include <dbt.h>
#include <wtsapi32.h>
#include <shellscalingapi.h>
@@ -117,26 +121,6 @@ static inline bool sessionManagerInteractionBlocked()
static inline bool sessionManagerInteractionBlocked() { return false; }
#endif
-static inline int windowDpiAwareness(HWND hwnd)
-{
- return static_cast<int>(GetAwarenessFromDpiAwarenessContext(GetWindowDpiAwarenessContext(hwnd)));
-}
-
-// Note: This only works within WM_NCCREATE
-static bool enableNonClientDpiScaling(HWND hwnd)
-{
- bool result = false;
- if (windowDpiAwareness(hwnd) == 2) {
- result = EnableNonClientDpiScaling(hwnd) != FALSE;
- if (!result) {
- const DWORD errorCode = GetLastError();
- qErrnoWarning(int(errorCode), "EnableNonClientDpiScaling() failed for HWND %p (%lu)",
- hwnd, errorCode);
- }
- }
- return result;
-}
-
QWindowsContext *QWindowsContext::m_instance = nullptr;
/*!
@@ -159,7 +143,7 @@ struct QWindowsContextPrivate {
QWindowsKeyMapper m_keyMapper;
QWindowsMouseHandler m_mouseHandler;
QWindowsPointerHandler m_pointerHandler;
- QWindowsMimeConverter m_mimeConverter;
+ QWindowsMimeRegistry m_mimeConverter;
QWindowsScreenManager m_screenManager;
QSharedPointer<QWindowCreationContext> m_creationContext;
#if QT_CONFIG(tabletevent)
@@ -170,11 +154,9 @@ struct QWindowsContextPrivate {
bool m_asyncExpose = false;
HPOWERNOTIFY m_powerNotification = nullptr;
HWND m_powerDummyWindow = nullptr;
- static bool m_darkMode;
static bool m_v2DpiAware;
};
-bool QWindowsContextPrivate::m_darkMode = false;
bool QWindowsContextPrivate::m_v2DpiAware = false;
QWindowsContextPrivate::QWindowsContextPrivate()
@@ -188,10 +170,9 @@ QWindowsContextPrivate::QWindowsContextPrivate()
m_systemInfo |= QWindowsContext::SI_RTL_Extensions;
m_keyMapper.setUseRTLExtensions(true);
}
- m_darkMode = QWindowsTheme::queryDarkMode();
if (FAILED(m_oleInitializeResult)) {
qWarning() << "QWindowsContext: OleInitialize() failed: "
- << QWindowsContext::comErrorString(m_oleInitializeResult);
+ << QSystemError::windowsComString(m_oleInitializeResult);
}
}
@@ -221,8 +202,12 @@ QWindowsContext::~QWindowsContext()
DestroyWindow(d->m_powerDummyWindow);
unregisterWindowClasses();
- if (d->m_oleInitializeResult == S_OK || d->m_oleInitializeResult == S_FALSE)
+ if (d->m_oleInitializeResult == S_OK || d->m_oleInitializeResult == S_FALSE) {
+#ifdef QT_USE_FACTORY_CACHE_REGISTRATION
+ detail::QWinRTFactoryCacheRegistration::clearAllCaches();
+#endif
OleUninitialize();
+ }
d->m_screenManager.clearScreens(); // Order: Potentially calls back to the windows.
if (d->m_displayContext)
@@ -264,7 +249,7 @@ void QWindowsContext::registerTouchWindows()
{
if (QGuiApplicationPrivate::is_app_running
&& (d->m_systemInfo & QWindowsContext::SI_SupportsTouch) != 0) {
- for (QWindowsWindow *w : qAsConst(d->m_windows))
+ for (QWindowsWindow *w : std::as_const(d->m_windows))
w->registerTouchWindow();
}
}
@@ -361,48 +346,121 @@ void QWindowsContext::setDetectAltGrModifier(bool a)
d->m_keyMapper.setDetectAltGrModifier(a);
}
-int QWindowsContext::processDpiAwareness()
-{
- PROCESS_DPI_AWARENESS result;
- if (SUCCEEDED(GetProcessDpiAwareness(nullptr, &result))) {
- return static_cast<int>(result);
+[[nodiscard]] static inline QtWindows::DpiAwareness
+ dpiAwarenessContextToQtDpiAwareness(DPI_AWARENESS_CONTEXT context)
+{
+ // IsValidDpiAwarenessContext() will handle the NULL pointer case.
+ if (!IsValidDpiAwarenessContext(context))
+ return QtWindows::DpiAwareness::Invalid;
+ if (AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED))
+ return QtWindows::DpiAwareness::Unaware_GdiScaled;
+ if (AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2))
+ return QtWindows::DpiAwareness::PerMonitorVersion2;
+ if (AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE))
+ return QtWindows::DpiAwareness::PerMonitor;
+ if (AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_SYSTEM_AWARE))
+ return QtWindows::DpiAwareness::System;
+ if (AreDpiAwarenessContextsEqual(context, DPI_AWARENESS_CONTEXT_UNAWARE))
+ return QtWindows::DpiAwareness::Unaware;
+ return QtWindows::DpiAwareness::Invalid;
+}
+
+QtWindows::DpiAwareness QWindowsContext::windowDpiAwareness(HWND hwnd)
+{
+ if (!hwnd)
+ return QtWindows::DpiAwareness::Invalid;
+ const auto context = GetWindowDpiAwarenessContext(hwnd);
+ return dpiAwarenessContextToQtDpiAwareness(context);
+}
+
+QtWindows::DpiAwareness QWindowsContext::processDpiAwareness()
+{
+ // Although we have GetDpiAwarenessContextForProcess(), however,
+ // it's only available on Win10 1903+, which is a little higher
+ // than Qt's minimum supported version (1809), so we can't use it.
+ // Luckily, MS docs said GetThreadDpiAwarenessContext() will also
+ // return the default DPI_AWARENESS_CONTEXT for the process if
+ // SetThreadDpiAwarenessContext() was never called. So we can use
+ // it as an equivalent.
+ const auto context = GetThreadDpiAwarenessContext();
+ return dpiAwarenessContextToQtDpiAwareness(context);
+}
+
+[[nodiscard]] static inline DPI_AWARENESS_CONTEXT
+ qtDpiAwarenessToDpiAwarenessContext(QtWindows::DpiAwareness dpiAwareness)
+{
+ switch (dpiAwareness) {
+ case QtWindows::DpiAwareness::Invalid:
+ return nullptr;
+ case QtWindows::DpiAwareness::Unaware:
+ return DPI_AWARENESS_CONTEXT_UNAWARE;
+ case QtWindows::DpiAwareness::System:
+ return DPI_AWARENESS_CONTEXT_SYSTEM_AWARE;
+ case QtWindows::DpiAwareness::PerMonitor:
+ return DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE;
+ case QtWindows::DpiAwareness::PerMonitorVersion2:
+ return DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
+ case QtWindows::DpiAwareness::Unaware_GdiScaled:
+ return DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED;
}
- return -1;
+ return nullptr;
}
-void QWindowsContext::setProcessDpiAwareness(QtWindows::ProcessDpiAwareness dpiAwareness)
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, QtWindows::DpiAwareness dpiAwareness)
{
- qCDebug(lcQpaWindow) << __FUNCTION__ << dpiAwareness;
- const HRESULT hr = SetProcessDpiAwareness(static_cast<PROCESS_DPI_AWARENESS>(dpiAwareness));
- // E_ACCESSDENIED means set externally (MSVC manifest or external app loading Qt plugin).
- // Silence warning in that case unless debug is enabled.
- if (FAILED(hr) && (hr != E_ACCESSDENIED || lcQpaWindow().isDebugEnabled())) {
- qWarning().noquote().nospace() << "SetProcessDpiAwareness("
- << dpiAwareness << ") failed: " << QWindowsContext::comErrorString(hr)
- << ", using " << QWindowsContext::processDpiAwareness();
+ const QDebugStateSaver saver(d);
+ QString message = u"QtWindows::DpiAwareness::"_s;
+ switch (dpiAwareness) {
+ case QtWindows::DpiAwareness::Invalid:
+ message += u"Invalid"_s;
+ break;
+ case QtWindows::DpiAwareness::Unaware:
+ message += u"Unaware"_s;
+ break;
+ case QtWindows::DpiAwareness::System:
+ message += u"System"_s;
+ break;
+ case QtWindows::DpiAwareness::PerMonitor:
+ message += u"PerMonitor"_s;
+ break;
+ case QtWindows::DpiAwareness::PerMonitorVersion2:
+ message += u"PerMonitorVersion2"_s;
+ break;
+ case QtWindows::DpiAwareness::Unaware_GdiScaled:
+ message += u"Unaware_GdiScaled"_s;
+ break;
}
+ d.nospace().noquote() << message;
+ return d;
}
+#endif
-bool QWindowsContext::setProcessDpiV2Awareness()
+bool QWindowsContext::setProcessDpiAwareness(QtWindows::DpiAwareness dpiAwareness)
{
- qCDebug(lcQpaWindow) << __FUNCTION__;
- const BOOL ok = SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
- if (!ok) {
- const HRESULT errorCode = GetLastError();
- qCWarning(lcQpaWindow).noquote().nospace() << "setProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) failed: "
- << QWindowsContext::comErrorString(errorCode);
+ qCDebug(lcQpaWindow) << __FUNCTION__ << dpiAwareness;
+ if (processDpiAwareness() == dpiAwareness)
+ return true;
+ const auto context = qtDpiAwarenessToDpiAwarenessContext(dpiAwareness);
+ if (!IsValidDpiAwarenessContext(context)) {
+ qCWarning(lcQpaWindow) << dpiAwareness << "is not supported by current system.";
return false;
}
-
- QWindowsContextPrivate::m_v2DpiAware = true;
+ if (!SetProcessDpiAwarenessContext(context)) {
+ qCWarning(lcQpaWindow).noquote().nospace()
+ << "SetProcessDpiAwarenessContext() failed: "
+ << QSystemError::windowsString()
+ << "\nQt's default DPI awareness context is "
+ << "DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2. If you know what you "
+ << "are doing, you can overwrite this default using qt.conf "
+ << "(https://doc.qt.io/qt-6/highdpi.html#configuring-windows).";
+ return false;
+ }
+ QWindowsContextPrivate::m_v2DpiAware
+ = processDpiAwareness() == QtWindows::DpiAwareness::PerMonitorVersion2;
return true;
}
-bool QWindowsContext::isDarkMode()
-{
- return QWindowsContextPrivate::m_darkMode;
-}
-
QWindowsContext *QWindowsContext::instance()
{
return m_instance;
@@ -418,9 +476,9 @@ bool QWindowsContext::useRTLExtensions() const
return d->m_keyMapper.useRTLExtensions();
}
-QList<int> QWindowsContext::possibleKeys(const QKeyEvent *e) const
+QPlatformKeyMapper *QWindowsContext::keyMapper() const
{
- return d->m_keyMapper.possibleKeys(e);
+ return &d->m_keyMapper;
}
QWindowsContext::HandleBaseWindowHash &QWindowsContext::windows()
@@ -472,6 +530,8 @@ QString QWindowsContext::classNamePrefix()
# define xstr(s) str(s)
# define str(s) #s
str << xstr(QT_NAMESPACE);
+# undef str
+# undef xstr
#endif
}
return result;
@@ -532,7 +592,7 @@ QString QWindowsContext::registerWindowClass(const QWindow *w)
if (icon)
cname += "Icon"_L1;
- return registerWindowClass(cname, qWindowsWndProc, style, GetSysColorBrush(COLOR_WINDOW), icon);
+ return registerWindowClass(cname, qWindowsWndProc, style, nullptr, icon);
}
QString QWindowsContext::registerWindowClass(QString cname,
@@ -601,7 +661,7 @@ void QWindowsContext::unregisterWindowClasses()
{
const auto appInstance = static_cast<HINSTANCE>(GetModuleHandle(nullptr));
- for (const QString &name : qAsConst(d->m_registeredWindowClassNames)) {
+ for (const QString &name : std::as_const(d->m_registeredWindowClassNames)) {
if (!UnregisterClass(reinterpret_cast<LPCWSTR>(name.utf16()), appInstance) && QWindowsContext::verbose)
qErrnoWarning("UnregisterClass failed for '%s'", qPrintable(name));
}
@@ -613,23 +673,6 @@ int QWindowsContext::screenDepth() const
return GetDeviceCaps(d->m_displayContext, BITSPIXEL);
}
-QString QWindowsContext::windowsErrorMessage(unsigned long errorCode)
-{
- QString rc = QString::fromLatin1("#%1: ").arg(errorCode);
- char16_t *lpMsgBuf;
-
- const DWORD len = FormatMessage(
- FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- nullptr, errorCode, 0, reinterpret_cast<LPTSTR>(&lpMsgBuf), 0, nullptr);
- if (len) {
- rc = QString::fromUtf16(lpMsgBuf, int(len));
- LocalFree(lpMsgBuf);
- } else {
- rc += QString::fromLatin1("<unknown error>");
- }
- return rc;
-}
-
void QWindowsContext::addWindow(HWND hwnd, QWindowsWindow *w)
{
d->m_windows.insert(hwnd, w);
@@ -792,7 +835,7 @@ bool QWindowsContext::isSessionLocked()
return result;
}
-QWindowsMimeConverter &QWindowsContext::mimeConverter() const
+QWindowsMimeRegistry &QWindowsContext::mimeConverter() const
{
return d->m_mimeConverter;
}
@@ -830,79 +873,6 @@ HWND QWindowsContext::createDummyWindow(const QString &classNameIn,
HWND_MESSAGE, nullptr, static_cast<HINSTANCE>(GetModuleHandle(nullptr)), nullptr);
}
-/*!
- \brief Common COM error strings.
-*/
-
-QByteArray QWindowsContext::comErrorString(HRESULT hr)
-{
- QByteArray result = QByteArrayLiteral("COM error 0x")
- + QByteArray::number(quintptr(hr), 16) + ' ';
- switch (hr) {
- case S_OK:
- result += QByteArrayLiteral("S_OK");
- break;
- case S_FALSE:
- result += QByteArrayLiteral("S_FALSE");
- break;
- case E_UNEXPECTED:
- result += QByteArrayLiteral("E_UNEXPECTED");
- break;
- case E_ACCESSDENIED:
- result += QByteArrayLiteral("E_ACCESSDENIED");
- break;
- case CO_E_ALREADYINITIALIZED:
- result += QByteArrayLiteral("CO_E_ALREADYINITIALIZED");
- break;
- case CO_E_NOTINITIALIZED:
- result += QByteArrayLiteral("CO_E_NOTINITIALIZED");
- break;
- case RPC_E_CHANGED_MODE:
- result += QByteArrayLiteral("RPC_E_CHANGED_MODE");
- break;
- case OLE_E_WRONGCOMPOBJ:
- result += QByteArrayLiteral("OLE_E_WRONGCOMPOBJ");
- break;
- case CO_E_NOT_SUPPORTED:
- result += QByteArrayLiteral("CO_E_NOT_SUPPORTED");
- break;
- case E_NOTIMPL:
- result += QByteArrayLiteral("E_NOTIMPL");
- break;
- case E_INVALIDARG:
- result += QByteArrayLiteral("E_INVALIDARG");
- break;
- case E_NOINTERFACE:
- result += QByteArrayLiteral("E_NOINTERFACE");
- break;
- case E_POINTER:
- result += QByteArrayLiteral("E_POINTER");
- break;
- case E_HANDLE:
- result += QByteArrayLiteral("E_HANDLE");
- break;
- case E_ABORT:
- result += QByteArrayLiteral("E_ABORT");
- break;
- case E_FAIL:
- result += QByteArrayLiteral("E_FAIL");
- break;
- case RPC_E_WRONG_THREAD:
- result += QByteArrayLiteral("RPC_E_WRONG_THREAD");
- break;
- case RPC_E_THREAD_NOT_INIT:
- result += QByteArrayLiteral("RPC_E_THREAD_NOT_INIT");
- break;
- default:
- break;
- }
- _com_error error(hr);
- result += QByteArrayLiteral(" (");
- result += QString::fromWCharArray(error.ErrorMessage()).toUtf8();
- result += ')';
- return result;
-}
-
void QWindowsContext::forceNcCalcSize(HWND hwnd)
{
// Force WM_NCCALCSIZE to adjust margin
@@ -997,6 +967,21 @@ static inline bool isInputMessage(UINT m)
|| (m >= WM_KEYFIRST && m <= WM_KEYLAST);
}
+// Note: This only works within WM_NCCREATE
+static bool enableNonClientDpiScaling(HWND hwnd)
+{
+ bool result = false;
+ if (QWindowsContext::windowDpiAwareness(hwnd) == QtWindows::DpiAwareness::PerMonitor) {
+ result = EnableNonClientDpiScaling(hwnd) != FALSE;
+ if (!result) {
+ const DWORD errorCode = GetLastError();
+ qErrnoWarning(int(errorCode), "EnableNonClientDpiScaling() failed for HWND %p (%lu)",
+ hwnd, errorCode);
+ }
+ }
+ return result;
+}
+
/*!
\brief Main windows procedure registered for windows.
@@ -1013,9 +998,10 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
MSG msg;
msg.hwnd = hwnd; // re-create MSG structure
- msg.message = message; // time and pt fields ignored
+ msg.message = message;
msg.wParam = wParam;
msg.lParam = lParam;
+ msg.time = GetMessageTime();
msg.pt.x = msg.pt.y = 0;
if (et != QtWindows::CursorEvent && (et & (QtWindows::MouseEventFlag | QtWindows::NonClientEventFlag))) {
msg.pt.x = GET_X_LPARAM(lParam);
@@ -1099,20 +1085,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
// Only refresh the window theme if the user changes the personalize settings.
if ((wParam == 0) && (lParam != 0) // lParam sometimes may be NULL.
&& (wcscmp(reinterpret_cast<LPCWSTR>(lParam), L"ImmersiveColorSet") == 0)) {
- const bool darkMode = QWindowsTheme::queryDarkMode();
- const bool darkModeChanged = darkMode != QWindowsContextPrivate::m_darkMode;
- QWindowsContextPrivate::m_darkMode = darkMode;
- auto integration = QWindowsIntegration::instance();
- if (integration->darkModeHandling().testFlag(QWindowsApplication::DarkModeStyle)) {
- QWindowsTheme::instance()->refresh();
- QWindowSystemInterface::handleThemeChange();
- }
- if (darkModeChanged) {
- if (integration->darkModeHandling().testFlag(QWindowsApplication::DarkModeWindowFrames)) {
- for (QWindowsWindow *w : d->m_windows)
- w->setDarkBorder(QWindowsContextPrivate::m_darkMode);
- }
- }
+ QWindowsTheme::handleSettingsChanged();
}
return d->m_screenManager.handleScreenChanges();
}
@@ -1129,7 +1102,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
d->m_creationContext->applyToMinMaxInfo(reinterpret_cast<MINMAXINFO *>(lParam));
return true;
case QtWindows::ResizeEvent:
- d->m_creationContext->obtainedSize = QSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+ d->m_creationContext->obtainedSize = QSize(LOWORD(lParam), HIWORD(lParam));
return true;
case QtWindows::MoveEvent:
d->m_creationContext->obtainedPos = QPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
@@ -1172,7 +1145,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
if (wParam == DBT_DEVNODES_CHANGED)
initTouch();
break;
- case QtWindows::KeyboardLayoutChangeEvent:
+ case QtWindows::InputLanguageChangeEvent:
if (QWindowsInputContext *wic = windowsInputContext())
wic->handleInputLanguageChanged(wParam, lParam);
Q_FALLTHROUGH();
@@ -1202,7 +1175,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
platformWindow->handleMoved();
return true;
case QtWindows::ResizeEvent:
- platformWindow->handleResized(static_cast<int>(wParam));
+ platformWindow->handleResized(static_cast<int>(wParam), lParam);
return true;
case QtWindows::QuerySizeHints:
platformWindow->getSizeHints(reinterpret_cast<MINMAXINFO *>(lParam));
@@ -1216,21 +1189,29 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
case QtWindows::ExposeEvent:
return platformWindow->handleWmPaint(hwnd, message, wParam, lParam, result);
case QtWindows::NonClientMouseEvent:
- if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer) && platformWindow->frameStrutEventsEnabled())
+ if (!platformWindow->frameStrutEventsEnabled())
+ break;
+ if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer))
return sessionManagerInteractionBlocked() || d->m_pointerHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result);
else
return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result);
case QtWindows::NonClientPointerEvent:
- if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer) && platformWindow->frameStrutEventsEnabled())
+ if (!platformWindow->frameStrutEventsEnabled())
+ break;
+ if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer))
return sessionManagerInteractionBlocked() || d->m_pointerHandler.translatePointerEvent(platformWindow->window(), hwnd, et, msg, result);
break;
case QtWindows::EnterSizeMoveEvent:
platformWindow->setFlag(QWindowsWindow::ResizeMoveActive);
+ if (!IsZoomed(hwnd))
+ platformWindow->updateRestoreGeometry();
return true;
case QtWindows::ExitSizeMoveEvent:
platformWindow->clearFlag(QWindowsWindow::ResizeMoveActive);
platformWindow->checkForScreenChanged();
handleExitSizeMove(platformWindow->window());
+ if (!IsZoomed(hwnd))
+ platformWindow->updateRestoreGeometry();
return true;
case QtWindows::ScrollEvent:
if (!(d->m_systemInfo & QWindowsContext::SI_SupportsPointer))
@@ -1276,6 +1257,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
QWindowSystemInterface::handleCloseEvent(platformWindow->window());
return true;
case QtWindows::ThemeChanged: {
+ QWindowsThemeCache::clearThemeCache(platformWindow->handle());
// Switch from Aero to Classic changes margins.
if (QWindowsTheme *theme = QWindowsTheme::instance())
theme->windowsThemeChanged(platformWindow->window());
@@ -1367,6 +1349,11 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
return true;
}
#endif // !defined(QT_NO_SESSIONMANAGER)
+ case QtWindows::TaskbarButtonCreated:
+ // Apply application badge if this is the first time we have a taskbar
+ // button, or after Explorer restart.
+ QWindowsIntegration::instance()->updateApplicationBadge();
+ break;
default:
break;
}
@@ -1409,7 +1396,7 @@ void QWindowsContext::handleFocusEvent(QtWindows::WindowsEventType et,
}
if (nextActiveWindow != d->m_lastActiveWindow) {
d->m_lastActiveWindow = nextActiveWindow;
- QWindowSystemInterface::handleWindowActivated(nextActiveWindow, Qt::ActiveWindowFocusReason);
+ QWindowSystemInterface::handleFocusWindowChanged(nextActiveWindow, Qt::ActiveWindowFocusReason);
}
}
@@ -1439,7 +1426,7 @@ bool QWindowsContext::handleContextMenuEvent(QWindow *window, const MSG &msg)
}
QWindowSystemInterface::handleContextMenuEvent(window, mouseTriggered, pos, globalPos,
- QWindowsKeyMapper::queryKeyboardModifiers());
+ keyMapper()->queryKeyboardModifiers());
return true;
}
#endif
@@ -1460,7 +1447,7 @@ void QWindowsContext::handleExitSizeMove(QWindow *window)
const Qt::MouseButtons appButtons = QGuiApplication::mouseButtons();
if (currentButtons == appButtons)
return;
- const Qt::KeyboardModifiers keyboardModifiers = QWindowsKeyMapper::queryKeyboardModifiers();
+ const Qt::KeyboardModifiers keyboardModifiers = keyMapper()->queryKeyboardModifiers();
const QPoint globalPos = QWindowsCursor::mousePosition();
const QPlatformWindow *platWin = window->handle();
const QPoint localPos = platWin->mapFromGlobal(globalPos);
@@ -1469,8 +1456,7 @@ void QWindowsContext::handleExitSizeMove(QWindow *window)
for (Qt::MouseButton button : {Qt::LeftButton, Qt::RightButton, Qt::MiddleButton}) {
if (appButtons.testFlag(button) && !currentButtons.testFlag(button)) {
QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos,
- currentButtons, button, type,
- keyboardModifiers);
+ currentButtons, button, type, keyboardModifiers);
}
}
if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer)
diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h
index 47c5ead772..0539a22afc 100644
--- a/src/plugins/platforms/windows/qwindowscontext.h
+++ b/src/plugins/platforms/windows/qwindowscontext.h
@@ -15,9 +15,6 @@
#include <shlobj.h>
#include <shlwapi.h>
-struct IBindCtx;
-struct _SHSTOCKICONINFO;
-
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcQpaWindow)
@@ -36,17 +33,17 @@ Q_DECLARE_LOGGING_CATEGORY(lcQpaScreen)
class QWindow;
class QPlatformScreen;
class QPlatformWindow;
+class QPlatformKeyMapper;
class QWindowsMenuBar;
class QWindowsScreenManager;
class QWindowsTabletSupport;
class QWindowsWindow;
-class QWindowsMimeConverter;
+class QWindowsMimeRegistry;
struct QWindowCreationContext;
struct QWindowsContextPrivate;
class QPoint;
class QKeyEvent;
class QPointingDevice;
-
class QWindowsContext
{
Q_DISABLE_COPY_MOVE(QWindowsContext)
@@ -91,8 +88,6 @@ public:
static QWindowsContext *instance();
- static QString windowsErrorMessage(unsigned long errorCode);
-
void addWindow(HWND, QWindowsWindow *w);
void removeWindow(HWND);
@@ -120,11 +115,10 @@ public:
QSharedPointer<QWindowCreationContext> windowCreationContext() const;
static void setTabletAbsoluteRange(int a);
- void setProcessDpiAwareness(QtWindows::ProcessDpiAwareness dpiAwareness);
- static int processDpiAwareness();
- bool setProcessDpiV2Awareness();
- static bool isDarkMode();
+ static bool setProcessDpiAwareness(QtWindows::DpiAwareness dpiAwareness);
+ static QtWindows::DpiAwareness processDpiAwareness();
+ static QtWindows::DpiAwareness windowDpiAwareness(HWND hwnd);
void setDetectAltGrModifier(bool a);
@@ -132,17 +126,16 @@ public:
unsigned systemInfo() const;
bool useRTLExtensions() const;
- QList<int> possibleKeys(const QKeyEvent *e) const;
+ QPlatformKeyMapper *keyMapper() const;
HandleBaseWindowHash &windows();
static bool isSessionLocked();
- QWindowsMimeConverter &mimeConverter() const;
+ QWindowsMimeRegistry &mimeConverter() const;
QWindowsScreenManager &screenManager();
QWindowsTabletSupport *tabletSupport() const;
- static QByteArray comErrorString(HRESULT hr);
bool asyncExpose() const;
void setAsyncExpose(bool value);
diff --git a/src/plugins/platforms/windows/qwindowscursor.cpp b/src/plugins/platforms/windows/qwindowscursor.cpp
index 5b904709ef..b416886120 100644
--- a/src/plugins/platforms/windows/qwindowscursor.cpp
+++ b/src/plugins/platforms/windows/qwindowscursor.cpp
@@ -14,6 +14,7 @@
#include <QtGui/qscreen.h>
#include <QtGui/private/qguiapplication_p.h> // getPixmapCursor()
#include <QtGui/private/qhighdpiscaling_p.h>
+#include <QtGui/private/qpixmap_win_p.h>
#include <QtCore/private/qwinregistry_p.h>
#include <QtCore/qdebug.h>
@@ -29,8 +30,7 @@ static bool initResources()
QT_BEGIN_NAMESPACE
-Q_GUI_EXPORT HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &p, int hbitmapFormat = 0);
-Q_GUI_EXPORT HBITMAP qt_createIconMask(const QBitmap &bitmap);
+using namespace Qt::Literals::StringLiterals;
/*!
\class QWindowsCursorCacheKey
@@ -105,14 +105,16 @@ static HCURSOR createBitmapCursor(const QImage &bbits, const QImage &mbits,
hotSpot.setX(width / 2);
if (hotSpot.y() < 0)
hotSpot.setY(height / 2);
- const int n = qMax(1, width / 8);
- QScopedArrayPointer<uchar> xBits(new uchar[height * n]);
- QScopedArrayPointer<uchar> xMask(new uchar[height * n]);
+ // a ddb is word aligned, QImage depends on bow it was created
+ const auto bplDdb = qMax(1, ((width + 15) >> 4) << 1);
+ const auto bplImg = int(bbits.bytesPerLine());
+ QScopedArrayPointer<uchar> xBits(new uchar[height * bplDdb]);
+ QScopedArrayPointer<uchar> xMask(new uchar[height * bplDdb]);
int x = 0;
for (int i = 0; i < height; ++i) {
const uchar *bits = bbits.constScanLine(i);
const uchar *mask = mbits.constScanLine(i);
- for (int j = 0; j < n; ++j) {
+ for (int j = 0; j < bplImg && j < bplDdb; ++j) {
uchar b = bits[j];
uchar m = mask[j];
if (invb)
@@ -123,6 +125,11 @@ static HCURSOR createBitmapCursor(const QImage &bbits, const QImage &mbits,
xMask[x] = b ^ m;
++x;
}
+ for (int i = bplImg; i < bplDdb; ++i) {
+ xBits[x] = 0;
+ xMask[x] = 0;
+ ++x;
+ }
}
return CreateCursor(GetModuleHandle(nullptr), hotSpot.x(), hotSpot.y(), width, height,
xBits.data(), xMask.data());
@@ -140,8 +147,8 @@ static HCURSOR createBitmapCursor(const QCursor &cursor, qreal scaleFactor = 1)
bbits = bbits.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
mbits = mbits.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
- bbits = bbits.convertToFormat(QImage::Format_Mono);
- mbits = mbits.convertToFormat(QImage::Format_Mono);
+ bbits = std::move(bbits).convertToFormat(QImage::Format_Mono);
+ mbits = std::move(mbits).convertToFormat(QImage::Format_Mono);
const bool invb = bbits.colorCount() > 1 && qGray(bbits.color(0)) < qGray(bbits.color(1));
const bool invm = mbits.colorCount() > 1 && qGray(mbits.color(0)) < qGray(mbits.color(1));
return createBitmapCursor(bbits, mbits, cursor.hotSpot(), invb, invm);
@@ -438,8 +445,8 @@ QWindowsCursor::PixmapCursor QWindowsCursor::customCursor(Qt::CursorShape cursor
if (!bestFit)
return PixmapCursor();
- const QPixmap rawImage(QStringLiteral(":/qt-project.org/windows/cursors/images/") +
- QString::fromLatin1(bestFit->fileName));
+ const QPixmap rawImage(":/qt-project.org/windows/cursors/images/"_L1 +
+ QLatin1StringView(bestFit->fileName));
return PixmapCursor(rawImage, QPoint(bestFit->hotSpotX, bestFit->hotSpotY));
}
#endif // !QT_NO_IMAGEFORMAT_PNG
diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
index d851964b9d..0ce0b0e2a7 100644
--- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
+++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
@@ -3,12 +3,7 @@
#define QT_NO_URL_CAST_FROM_STRING 1
-#ifndef _WIN32_WINNT
-#define _WIN32_WINNT 0x0A00
-#endif
-
#include <QtCore/qt_windows.h>
-#include "qwindowscombase.h"
#include "qwindowsdialoghelpers.h"
#include "qwindowscontext.h"
@@ -35,6 +30,9 @@
#include <QtCore/qmutex.h>
#include <QtCore/quuid.h>
#include <QtCore/qtemporaryfile.h>
+#include <QtCore/private/qfunctions_win_p.h>
+#include <QtCore/private/qsystemerror_p.h>
+#include <QtCore/private/qcomobject_p.h>
#include <algorithm>
#include <vector>
@@ -45,34 +43,6 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-#ifndef QT_NO_DEBUG_STREAM
-/* Output UID (IID, CLSID) as C++ constants.
- * The constants are contained in the Windows SDK libs, but not for MinGW. */
-static inline QString guidToString(const GUID &g)
-{
- QString rc;
- QTextStream str(&rc);
- str.setIntegerBase(16);
- str.setNumberFlags(str.numberFlags() | QTextStream::ShowBase);
- str << '{' << g.Data1 << ", " << g.Data2 << ", " << g.Data3;
- str.setFieldWidth(2);
- str.setFieldAlignment(QTextStream::AlignRight);
- str.setPadChar(u'0');
- str << ",{" << g.Data4[0] << ", " << g.Data4[1] << ", " << g.Data4[2] << ", " << g.Data4[3]
- << ", " << g.Data4[4] << ", " << g.Data4[5] << ", " << g.Data4[6] << ", " << g.Data4[7]
- << "}};";
- return rc;
-}
-
-inline QDebug operator<<(QDebug d, const GUID &g)
-{
- QDebugStateSaver saver(d);
- d.nospace();
- d << guidToString(g);
- return d;
-}
-#endif // !QT_NO_DEBUG_STREAM
-
// Return an allocated wchar_t array from a QString, reserve more memory if desired.
static wchar_t *qStringToWCharArray(const QString &s, size_t reserveSize = 0)
{
@@ -239,7 +209,7 @@ QWindowsNativeDialogBase *QWindowsDialogHelperBase<BaseClass>::ensureNativeDialo
// Create dialog and apply common settings. Check "executed" flag as well
// since for example IFileDialog::Show() works only once.
if (m_nativeDialog.isNull() || m_nativeDialog->executed())
- m_nativeDialog = QWindowsNativeDialogBasePtr(createNativeDialog());
+ m_nativeDialog = QWindowsNativeDialogBasePtr(createNativeDialog(), &QObject::deleteLater);
return m_nativeDialog.data();
}
@@ -268,6 +238,7 @@ private:
void QWindowsDialogThread::run()
{
qCDebug(lcQpaDialogs) << '>' << __FUNCTION__;
+ QComHelper comInit(COINIT_APARTMENTTHREADED);
m_dialog->exec(m_owner);
qCDebug(lcQpaDialogs) << '<' << __FUNCTION__;
}
@@ -324,12 +295,13 @@ void QWindowsDialogHelperBase<BaseClass>::stopTimer()
}
}
-
template <class BaseClass>
void QWindowsDialogHelperBase<BaseClass>::hide()
{
- if (m_nativeDialog)
+ if (m_nativeDialog) {
m_nativeDialog->close();
+ m_nativeDialog.clear();
+ }
m_ownerWindow = nullptr;
}
@@ -451,7 +423,7 @@ inline void QWindowsFileDialogSharedData::fromOptions(const QSharedPointer<QFile
class QWindowsNativeFileDialogBase;
-class QWindowsNativeFileDialogEventHandler : public QWindowsComBase<IFileDialogEvents>
+class QWindowsNativeFileDialogEventHandler : public QComObject<IFileDialogEvents>
{
Q_DISABLE_COPY_MOVE(QWindowsNativeFileDialogEventHandler)
public:
@@ -544,8 +516,18 @@ QWindowsShellItem::QWindowsShellItem(IShellItem *item)
: m_item(item)
, m_attributes(0)
{
- if (FAILED(item->GetAttributes(SFGAO_CAPABILITYMASK | SFGAO_DISPLAYATTRMASK | SFGAO_CONTENTSMASK | SFGAO_STORAGECAPMASK, &m_attributes)))
+ SFGAOF mask = (SFGAO_CAPABILITYMASK | SFGAO_CONTENTSMASK | SFGAO_STORAGECAPMASK);
+
+ // Check for attributes which might be expensive to enumerate for subfolders
+ if (FAILED(item->GetAttributes((SFGAO_STREAM | SFGAO_COMPRESSED), &m_attributes))) {
m_attributes = 0;
+ } else {
+ // If the item is compressed or stream, skip expensive subfolder test
+ if (m_attributes & (SFGAO_STREAM | SFGAO_COMPRESSED))
+ mask &= ~SFGAO_HASSUBFOLDER;
+ if (FAILED(item->GetAttributes(mask, &m_attributes)))
+ m_attributes = 0;
+ }
}
QString QWindowsShellItem::path() const
@@ -584,7 +566,7 @@ QUrl QWindowsShellItem::url() const
if (urlV.isValid())
return urlV;
// Last resort: encode the absolute desktop parsing id as data URL
- const QString data = QStringLiteral("data:text/plain;base64,")
+ const QString data = "data:text/plain;base64,"_L1
+ QLatin1StringView(desktopAbsoluteParsing().toLatin1().toBase64());
return QUrl(data);
}
@@ -625,7 +607,7 @@ bool QWindowsShellItem::copyData(QIODevice *out, QString *errorMessage)
HRESULT hr = m_item->BindToHandler(nullptr, BHID_Stream, IID_PPV_ARGS(&istream));
if (FAILED(hr)) {
*errorMessage = "BindToHandler() failed: "_L1
- + QLatin1StringView(QWindowsContext::comErrorString(hr));
+ + QSystemError::windowsComString(hr);
return false;
}
enum : ULONG { bufSize = 102400 };
@@ -642,7 +624,7 @@ bool QWindowsShellItem::copyData(QIODevice *out, QString *errorMessage)
istream->Release();
if (hr != S_OK && hr != S_FALSE) {
*errorMessage = "Read() failed: "_L1
- + QLatin1StringView(QWindowsContext::comErrorString(hr));
+ + QSystemError::windowsComString(hr);
return false;
}
return true;
@@ -1355,7 +1337,7 @@ Q_GLOBAL_STATIC(QStringList, temporaryItemCopies)
static void cleanupTemporaryItemCopies()
{
- for (const QString &file : qAsConst(*temporaryItemCopies()))
+ for (const QString &file : std::as_const(*temporaryItemCopies()))
QFile::remove(file);
}
@@ -1377,7 +1359,7 @@ QString tempFilePattern(QString name)
int lastDot = name.lastIndexOf(u'.');
if (lastDot < 0)
lastDot = name.size();
- name.insert(lastDot, QStringLiteral("_XXXXXX"));
+ name.insert(lastDot, "_XXXXXX"_L1);
for (int i = lastDot - 1; i >= 0; --i) {
if (!validFileNameCharacter(name.at(i)))
@@ -1569,7 +1551,7 @@ QWindowsNativeDialogBase *QWindowsFileDialogHelper::createNativeDialog()
if (!info.isDir())
result->selectFile(info.fileName());
} else {
- result->selectFile(url.path()); // TODO url.fileName() once it exists
+ result->selectFile(url.fileName());
}
}
// No need to select initialNameFilter if mode is Dir
@@ -1603,7 +1585,7 @@ void QWindowsFileDialogHelper::selectFile(const QUrl &fileName)
qCDebug(lcQpaDialogs) << __FUNCTION__ << fileName.toString();
if (hasNativeDialog()) // Might be invoked from the QFileDialog constructor.
- nativeFileDialog()->selectFile(fileName.toLocalFile()); // ## should use QUrl::fileName() once it exists
+ nativeFileDialog()->selectFile(fileName.fileName());
}
QList<QUrl> QWindowsFileDialogHelper::selectedFiles() const
@@ -1711,14 +1693,6 @@ static int QT_WIN_CALLBACK xpFileDialogGetExistingDirCallbackProc(HWND hwnd, UIN
return dialog->existingDirCallback(hwnd, uMsg, lParam);
}
-/* The correct declaration of the SHGetPathFromIDList symbol is
- * being used in mingw-w64 as of r6215, which is a v3 snapshot. */
-#if defined(Q_CC_MINGW) && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 3)
-typedef ITEMIDLIST *qt_LpItemIdList;
-#else
-using qt_LpItemIdList = PIDLIST_ABSOLUTE;
-#endif
-
int QWindowsXpNativeFileDialog::existingDirCallback(HWND hwnd, UINT uMsg, LPARAM lParam)
{
switch (uMsg) {
@@ -1732,7 +1706,7 @@ int QWindowsXpNativeFileDialog::existingDirCallback(HWND hwnd, UINT uMsg, LPARAM
break;
case BFFM_SELCHANGED: {
wchar_t path[MAX_PATH];
- const bool ok = SHGetPathFromIDList(reinterpret_cast<qt_LpItemIdList>(lParam), path)
+ const bool ok = SHGetPathFromIDList(reinterpret_cast<PIDLIST_ABSOLUTE>(lParam), path)
&& path[0];
SendMessage(hwnd, BFFM_ENABLEOK, ok ? 1 : 0, 1);
}
@@ -1754,7 +1728,7 @@ QList<QUrl> QWindowsXpNativeFileDialog::execExistingDir(HWND owner)
bi.lpfn = xpFileDialogGetExistingDirCallbackProc;
bi.lParam = LPARAM(this);
QList<QUrl> selectedFiles;
- if (qt_LpItemIdList pItemIDList = SHBrowseForFolder(&bi)) {
+ if (const auto pItemIDList = SHBrowseForFolder(&bi)) {
wchar_t path[MAX_PATH];
path[0] = 0;
if (SHGetPathFromIDList(pItemIDList, path) && path[0])
diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp
index 33b1cf2abd..c6f55c3509 100644
--- a/src/plugins/platforms/windows/qwindowsdrag.cpp
+++ b/src/plugins/platforms/windows/qwindowsdrag.cpp
@@ -28,6 +28,8 @@
#include <QtCore/qdebug.h>
#include <QtCore/qbuffer.h>
#include <QtCore/qpoint.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/private/qcomobject_p.h>
#include <shlobj.h>
@@ -167,7 +169,7 @@ static Qt::MouseButtons lastButtons = Qt::NoButton;
\internal
*/
-class QWindowsOleDropSource : public QWindowsComBase<IDropSource>
+class QWindowsOleDropSource : public QComObject<IDropSource>
{
public:
enum Mode {
@@ -348,7 +350,7 @@ QWindowsOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
} else {
if (buttons && !m_currentButtons) {
m_currentButtons = buttons;
- } else if (!(m_currentButtons & buttons)) { // Button changed: Complete Drop operation.
+ } else if (m_currentButtons != buttons) { // Button changed: Complete Drop operation.
result = DRAGDROP_S_DROP;
}
}
@@ -526,7 +528,8 @@ QWindowsOleDropTarget::DragLeave()
qCDebug(lcQpaMime) << __FUNCTION__ << ' ' << m_window;
- lastModifiers = QWindowsKeyMapper::queryKeyboardModifiers();
+ const auto *keyMapper = QWindowsContext::instance()->keyMapper();
+ lastModifiers = keyMapper->queryKeyboardModifiers();
lastButtons = QWindowsMouseHandler::queryMouseButtons();
QWindowSystemInterface::handleDrag(m_window, nullptr, QPoint(), Qt::IgnoreAction,
@@ -611,7 +614,6 @@ QWindowsOleDropTarget::Drop(LPDATAOBJECT pDataObj, DWORD grfKeyState,
*/
bool QWindowsDrag::m_canceled = false;
-bool QWindowsDrag::m_dragging = false;
QWindowsDrag::QWindowsDrag() = default;
@@ -644,6 +646,86 @@ IDropTargetHelper* QWindowsDrag::dropHelper() {
return m_cachedDropTargetHelper;
}
+// Workaround for DoDragDrop() not working with touch/pen input, causing DnD to hang until the mouse is moved.
+// We process pointer messages for touch/pen and generate mouse input through SendInput() to trigger DoDragDrop()
+static HRESULT startDoDragDrop(LPDATAOBJECT pDataObj, LPDROPSOURCE pDropSource, DWORD dwOKEffects, LPDWORD pdwEffect)
+{
+ QWindow *underMouse = QWindowsContext::instance()->windowUnderMouse();
+ const bool hasMouseCapture = underMouse && static_cast<QWindowsWindow *>(underMouse->handle())->hasMouseCapture();
+ const HWND hwnd = hasMouseCapture ? reinterpret_cast<HWND>(underMouse->winId()) : ::GetFocus();
+ bool starting = false;
+
+ for (;;) {
+ MSG msg{};
+ if (::GetMessage(&msg, hwnd, 0, 0) > 0) {
+
+ if (msg.message == WM_MOUSEMOVE) {
+
+ // Only consider the first simulated event, or actual mouse messages.
+ if (!starting && (msg.wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON | MK_XBUTTON1 | MK_XBUTTON2)) == 0)
+ return E_FAIL;
+
+ return ::DoDragDrop(pDataObj, pDropSource, dwOKEffects, pdwEffect);
+ }
+
+ if (msg.message == WM_POINTERUPDATE) {
+
+ const quint32 pointerId = GET_POINTERID_WPARAM(msg.wParam);
+
+ POINTER_INFO pointerInfo{};
+ if (!GetPointerInfo(pointerId, &pointerInfo))
+ return E_FAIL;
+
+ if (pointerInfo.pointerFlags & POINTER_FLAG_PRIMARY) {
+
+ DWORD flags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_MOVE;
+ if (IS_POINTER_FIRSTBUTTON_WPARAM(msg.wParam))
+ flags |= MOUSEEVENTF_LEFTDOWN;
+ if (IS_POINTER_SECONDBUTTON_WPARAM(msg.wParam))
+ flags |= MOUSEEVENTF_RIGHTDOWN;
+ if (IS_POINTER_THIRDBUTTON_WPARAM(msg.wParam))
+ flags |= MOUSEEVENTF_MIDDLEDOWN;
+
+ if (!starting) {
+ POINT pt{};
+ if (::GetCursorPos(&pt)) {
+
+ // Send mouse input that can generate a WM_MOUSEMOVE message.
+ if ((flags & MOUSEEVENTF_LEFTDOWN || flags & MOUSEEVENTF_RIGHTDOWN || flags & MOUSEEVENTF_MIDDLEDOWN)
+ && (pt.x != pointerInfo.ptPixelLocation.x || pt.y != pointerInfo.ptPixelLocation.y)) {
+
+ const int origin_x = ::GetSystemMetrics(SM_XVIRTUALSCREEN);
+ const int origin_y = ::GetSystemMetrics(SM_YVIRTUALSCREEN);
+ const int virt_w = ::GetSystemMetrics(SM_CXVIRTUALSCREEN);
+ const int virt_h = ::GetSystemMetrics(SM_CYVIRTUALSCREEN);
+ const int virt_x = pointerInfo.ptPixelLocation.x - origin_x;
+ const int virt_y = pointerInfo.ptPixelLocation.y - origin_y;
+
+ INPUT input{};
+ input.type = INPUT_MOUSE;
+ input.mi.dx = static_cast<DWORD>(virt_x * (65535.0 / virt_w));
+ input.mi.dy = static_cast<DWORD>(virt_y * (65535.0 / virt_h));
+ input.mi.dwFlags = flags;
+
+ ::SendInput(1, &input, sizeof(input));
+ starting = true;
+ }
+ }
+ }
+ }
+ } else {
+ // Handle other messages.
+ qWindowsWndProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
+
+ if (msg.message == WM_POINTERLEAVE)
+ return E_FAIL;
+ }
+ } else {
+ return E_FAIL;
+ }
+ }
+}
+
Qt::DropAction QWindowsDrag::drag(QDrag *drag)
{
// TODO: Accessibility handling?
@@ -659,10 +741,7 @@ Qt::DropAction QWindowsDrag::drag(QDrag *drag)
const DWORD allowedEffects = translateToWinDragEffects(possibleActions);
qCDebug(lcQpaMime) << '>' << __FUNCTION__ << "possible Actions=0x"
<< Qt::hex << int(possibleActions) << "effects=0x" << allowedEffects << Qt::dec;
- // Indicate message handlers we are in DoDragDrop() event loop.
- QWindowsDrag::m_dragging = true;
- const HRESULT r = DoDragDrop(dropDataObject, windowDropSource, allowedEffects, &resultEffect);
- QWindowsDrag::m_dragging = false;
+ const HRESULT r = startDoDragDrop(dropDataObject, windowDropSource, allowedEffects, &resultEffect);
const DWORD reportedPerformedEffect = dropDataObject->reportedPerformedEffect();
if (r == DRAGDROP_S_DROP) {
if (reportedPerformedEffect == DROPEFFECT_MOVE && resultEffect != DROPEFFECT_MOVE) {
diff --git a/src/plugins/platforms/windows/qwindowsdrag.h b/src/plugins/platforms/windows/qwindowsdrag.h
index cf5d43fb84..a2d0e54044 100644
--- a/src/plugins/platforms/windows/qwindowsdrag.h
+++ b/src/plugins/platforms/windows/qwindowsdrag.h
@@ -4,12 +4,12 @@
#ifndef QWINDOWSDRAG_H
#define QWINDOWSDRAG_H
-#include "qwindowscombase.h"
#include "qwindowsinternalmimedata.h"
#include <qpa/qplatformdrag.h>
#include <QtGui/qpixmap.h>
#include <QtGui/qdrag.h>
+#include <QtCore/private/qcomobject_p.h>
struct IDropTargetHelper;
@@ -23,7 +23,7 @@ public:
IDataObject *retrieveDataObject() const override;
};
-class QWindowsOleDropTarget : public QWindowsComBase<IDropTarget>
+class QWindowsOleDropTarget : public QComObject<IDropTarget>
{
public:
explicit QWindowsOleDropTarget(QWindow *w);
@@ -58,7 +58,6 @@ public:
static QWindowsDrag *instance();
void cancelDrag() override { QWindowsDrag::m_canceled = true; }
static bool isCanceled() { return QWindowsDrag::m_canceled; }
- static bool isDragging() { return QWindowsDrag::m_dragging; }
IDataObject *dropDataObject() const { return m_dropDataObject; }
void setDropDataObject(IDataObject *dataObject) { m_dropDataObject = dataObject; }
@@ -69,7 +68,6 @@ public:
private:
static bool m_canceled;
- static bool m_dragging;
QWindowsDropMimeData m_dropData;
IDataObject *m_dropDataObject = nullptr;
diff --git a/src/plugins/platforms/windows/qwindowsdropdataobject.cpp b/src/plugins/platforms/windows/qwindowsdropdataobject.cpp
index 4f5e1f5b85..629291640f 100644
--- a/src/plugins/platforms/windows/qwindowsdropdataobject.cpp
+++ b/src/plugins/platforms/windows/qwindowsdropdataobject.cpp
@@ -5,10 +5,12 @@
#include <QtCore/qurl.h>
#include <QtCore/qmimedata.h>
-#include "qwindowsmime.h"
+#include "qwindowsmimeregistry.h"
QT_BEGIN_NAMESPACE
+using namespace Qt::Literals::StringLiterals;
+
/*!
\class QWindowsDropDataObject
\brief QWindowsOleDataObject subclass specialized for handling Drag&Drop.
@@ -53,11 +55,11 @@ bool QWindowsDropDataObject::shouldIgnore(LPFORMATETC pformatetc) const
QMimeData *dropData = mimeData();
if (dropData && dropData->formats().size() == 1 && dropData->hasUrls()) {
- QString formatName = QWindowsMimeConverter::clipboardFormatName(pformatetc->cfFormat);
+ QString formatName = QWindowsMimeRegistry::clipboardFormatName(pformatetc->cfFormat);
if (pformatetc->cfFormat == CF_UNICODETEXT
|| pformatetc->cfFormat == CF_TEXT
- || formatName == QStringLiteral("UniformResourceLocator")
- || formatName == QStringLiteral("UniformResourceLocatorW")) {
+ || formatName == "UniformResourceLocator"_L1
+ || formatName == "UniformResourceLocatorW"_L1) {
const auto urls = dropData->urls();
return std::all_of(urls.cbegin(), urls.cend(), [] (const QUrl &u) { return u.isLocalFile(); });
}
diff --git a/src/plugins/platforms/windows/qwindowsglcontext.cpp b/src/plugins/platforms/windows/qwindowsglcontext.cpp
index 65740d69da..5ca52c2c19 100644
--- a/src/plugins/platforms/windows/qwindowsglcontext.cpp
+++ b/src/plugins/platforms/windows/qwindowsglcontext.cpp
@@ -544,11 +544,14 @@ static int choosePixelFormat(HDC hdc,
iAttributes[i++] = WGL_NUMBER_OVERLAYS_ARB;
iAttributes[i++] = 1;
}
+ // must be the one before the last one
const int samples = format.samples();
const bool sampleBuffersRequested = samples > 1
&& testFlag(staticContext.extensions, QOpenGLStaticContext::SampleBuffers);
+ int sampleBuffersKeyPosition = 0;
int samplesValuePosition = 0;
if (sampleBuffersRequested) {
+ sampleBuffersKeyPosition = i;
iAttributes[i++] = WGL_SAMPLE_BUFFERS_ARB;
iAttributes[i++] = TRUE;
iAttributes[i++] = WGL_SAMPLES_ARB;
@@ -560,9 +563,9 @@ static int choosePixelFormat(HDC hdc,
}
// must be the last
bool srgbRequested = format.colorSpace() == QColorSpace::SRgb;
- int srgbValuePosition = 0;
+ int srgbCapableKeyPosition = 0;
if (srgbRequested) {
- srgbValuePosition = i;
+ srgbCapableKeyPosition = i;
iAttributes[i++] = WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT;
iAttributes[i++] = TRUE;
}
@@ -576,8 +579,9 @@ static int choosePixelFormat(HDC hdc,
&& numFormats >= 1;
if (valid || (!sampleBuffersRequested && !srgbRequested))
break;
+ // NB reductions must be done in reverse order (disable the last first, then move on to the one before that, etc.)
if (srgbRequested) {
- iAttributes[srgbValuePosition] = 0;
+ iAttributes[srgbCapableKeyPosition] = 0;
srgbRequested = false;
} else if (sampleBuffersRequested) {
if (iAttributes[samplesValuePosition] > 1) {
@@ -585,11 +589,8 @@ static int choosePixelFormat(HDC hdc,
} else if (iAttributes[samplesValuePosition] == 1) {
// Fallback in case it is unable to initialize with any
// samples to avoid falling back to the GDI path
- // NB: The sample attributes needs to be at the end for this
- // to work correctly
- iAttributes[samplesValuePosition - 1] = FALSE;
+ iAttributes[sampleBuffersKeyPosition] = 0;
iAttributes[samplesValuePosition] = 0;
- iAttributes[samplesValuePosition + 1] = 0;
} else {
break;
}
@@ -1261,7 +1262,6 @@ bool QWindowsGLContext::makeCurrent(QPlatformSurface *surface)
// Do we already have a DC entry for that window?
auto *window = static_cast<QWindowsWindow *>(surface);
- window->aboutToMakeCurrent();
const HWND hwnd = window->handle();
if (const QOpenGLContextData *contextData = findByHWND(m_windowContexts, hwnd)) {
// Repeated calls to wglMakeCurrent when vsync is enabled in the driver will
diff --git a/src/plugins/platforms/windows/qwindowsiconengine.cpp b/src/plugins/platforms/windows/qwindowsiconengine.cpp
new file mode 100644
index 0000000000..5e5ca22ec1
--- /dev/null
+++ b/src/plugins/platforms/windows/qwindowsiconengine.cpp
@@ -0,0 +1,394 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qwindowsiconengine.h"
+
+#include <QtCore/qoperatingsystemversion.h>
+#include <QtGui/qguiapplication.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qpalette.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+QString QWindowsIconEngine::glyphs() const
+{
+ if (!QFontInfo(m_iconFont).exactMatch())
+ return {};
+
+ static constexpr std::pair<QLatin1StringView, QStringView> glyphMap[] = {
+ {"address-book-new"_L1, u"\ue780"},
+ {"application-exit"_L1, u"\ue8bb"},
+ {"appointment-new"_L1, u"\ue878"},
+ {"call-start"_L1, u"\uf715"},
+ {"call-stop"_L1, u"\uf405"},
+ {"contact-new"_L1, u"\ue8fa"},
+ {"document-new"_L1, u"\ue8a5"},
+ {"document-open"_L1, u"\ue8e5"},
+ {"document-open-recent"_L1, u"\ue823"},
+ {"document-page-setup"_L1, u"\ue7c3"},
+ {"document-print"_L1, u"\ue749"},
+ {"document-print-preview"_L1, u"\ue956"},
+ {"document-properties"_L1, u"\ue90f"},
+ {"document-revert"_L1, u"\ue7a7"}, // ?
+ {"document-save"_L1, u"\ue74e"}, // or e78c?
+ {"document-save-as"_L1, u"\ue792"},
+ {"document-send"_L1, u"\ue724"},
+ {"edit-clear"_L1, u"\ue894"},
+ {"edit-copy"_L1, u"\ue8c8"},
+ {"edit-cut"_L1, u"\ue8c6"},
+ {"edit-delete"_L1, u"\ue74d"},
+ {"edit-find"_L1, u"\ue721"},
+ //{"edit-find-replace"_L1, u"\u"},
+ {"edit-paste"_L1, u"\ue77f"},
+ {"edit-redo"_L1, u"\ue7a6"},
+ {"edit-select-all"_L1, u"\ue8b3"},
+ {"edit-undo"_L1, u"\ue7a7"},
+ {"folder-new"_L1, u"\ue8f4"},
+ //{"format-indent-less"_L1, u"\u"},
+ //{"format-indent-more"_L1, u"\u"},
+ {"format-justify-center"_L1, u"\ue8e3"},
+ //{"format-justify-fill"_L1, u"\ue235"},
+ {"format-justify-left"_L1, u"\ue8e4"},
+ {"format-justify-right"_L1, u"\ue8e2"},
+ //{"format-text-direction-ltr"_L1, u"\ue247"},
+ //{"format-text-direction-rtl"_L1, u"\ue248"},
+ {"format-text-bold"_L1, u"\ue8dd"},
+ {"format-text-italic"_L1, u"\ue8db"},
+ {"format-text-underline"_L1, u"\ue8dc"},
+ {"format-text-strikethrough"_L1, u"\uede0"},
+ //{"go-bottom"_L1,u"\ue258"},
+ {"go-down"_L1,u"\ue74b"},
+ //{"go-first"_L1, u"\ue5dc"},
+ {"go-home"_L1, u"\ue80f"},
+ // {"go-jump"_L1, u"\uf719"},
+ //{"go-last"_L1, u"\ue5dd"},
+ {"go-next"_L1, u"\ue893"},
+ {"go-previous"_L1, u"\ue892"},
+ //{"go-top"_L1, u"\ue25a"},
+ {"go-up"_L1, u"\ue74a"},
+ {"help-about"_L1, u"\ue946"},
+ //{"help-contents"_L1, u"\ue8de"},
+ {"help-faq"_L1, u"\ue897"},
+ {"insert-image"_L1, u"\ue946"},
+ {"insert-link"_L1, u"\ue71b"},
+ //{"insert-object"_L1, u"\u"},
+ //{"insert-text"_L1, u"\uf827"},
+ {"list-add"_L1, u"\ue710"},
+ {"list-remove"_L1, u"\ue738"},
+ {"mail-forward"_L1, u"\ue89c"},
+ //{"mail-mark-important"_L1, u"\ue937"},
+ //{"mail-mark-junk"_L1, u"\u"},
+ //{"mail-mark-notjunk"_L1, u"\u"},
+ {"mail-mark-read"_L1, u"\ue8c3"},
+ //{"mail-mark-unread"_L1, u"\ue9bc"},
+ {"mail-message-new"_L1, u"\ue70f"},
+ {"mail-reply-all"_L1, u"\ue8c2"},
+ {"mail-reply-sender"_L1, u"\ue8ca"},
+ {"mail-send"_L1, u"\ue724"},
+ //{"mail-send-receive"_L1, u"\u"},
+ {"media-eject"_L1, u"\uf847"},
+ {"media-playback-pause"_L1, u"\ue769"},
+ {"media-playback-start"_L1, u"\ue768"},
+ {"media-playback-stop"_L1, u"\ue71a"},
+ {"media-record"_L1, u"\ue7c8"},
+ {"media-seek-backward"_L1, u"\ueb9e"},
+ {"media-seek-forward"_L1, u"\ueb9d"},
+ {"media-skip-backward"_L1, u"\ue892"},
+ {"media-skip-forward"_L1, u"\ue893"},
+ //{"object-flip-horizontal"_L1, u"\u"},
+ //{"object-flip-vertical"_L1, u"\u"},
+ {"object-rotate-left"_L1, u"\ue80c"},
+ {"object-rotate-right"_L1, u"\ue80d"},
+ //{"process-stop"_L1, u"\ue5c9"},
+ {"system-lock-screen"_L1, u"\uee3f"},
+ {"system-log-out"_L1, u"\uf3b1"},
+ //{"system-run"_L1, u"\u"},
+ {"system-search"_L1, u"\ue721"},
+ {"system-reboot"_L1, u"\ue777"}, // unsure?
+ {"system-shutdown"_L1, u"\ue7e8"},
+ {"tools-check-spelling"_L1, u"\uf87b"},
+ {"view-fullscreen"_L1, u"\ue740"},
+ {"view-refresh"_L1, u"\ue72c"},
+ {"view-restore"_L1, u"\ue777"},
+ //{"view-sort-ascending"_L1, u"\ue25a"},
+ //{"view-sort-descending"_L1, u"\ue258"},
+ {"window-close"_L1, u"\ue8bb"},
+ {"window-new"_L1, u"\ue78b"},
+ {"zoom-fit-best"_L1, u"\ue9a6"},
+ {"zoom-in"_L1, u"\ue8a3"},
+ //{"zoom-original"_L1, u"\u"},
+ {"zoom-out"_L1, u"\ue71f"},
+
+ {"process-working"_L1, u"\ue9f3"},
+
+ {"accessories-calculator"_L1, u"\ue8ef"},
+ {"accessories-character-map"_L1, u"\uf2b7"},
+ {"accessories-dictionary"_L1, u"\ue82d"},
+ //{"accessories-text-editor"_L1, u"\ue262"},
+ {"help-browser"_L1, u"\ue897"},
+ {"multimedia-volume-control"_L1, u"\ue767"},
+ {"preferences-desktop-accessibility"_L1, u"\ue776"},
+ {"preferences-desktop-font"_L1, u"\ue8d2"},
+ {"preferences-desktop-keyboard"_L1, u"\ue765"},
+ {"preferences-desktop-locale"_L1, u"\uf2b7"},
+ //{"preferences-desktop-multimedia"_L1, u"\uea75"},
+ {"preferences-desktop-screensaver"_L1, u"\uf182"},
+ //{"preferences-desktop-theme"_L1, u"\uf560"},
+ //{"preferences-desktop-wallpaper"_L1, u"\ue1bc"},
+ {"system-file-manager"_L1, u"\uec50"},
+ //{"system-software-install"_L1, u"\ueb71"},
+ {"system-software-update"_L1, u"\uecc5"},
+ {"utilities-system-monitor"_L1, u"\ue7f4"},
+ //{"utilities-terminal"_L1, u"\ueb8e"},
+
+ //{"applications-accessories"_L1, u"\u"},
+ {"applications-development"_L1, u"\uec7a"},
+ //{"applications-engineering"_L1, u"\uea3d"},
+ {"applications-games"_L1, u"\ue7fc"},
+ //{"applications-graphics"_L1, u"\u"},
+ {"applications-internet"_L1, u"\ue774"},
+ {"applications-multimedia"_L1, u"\uea69"},
+ //{"applications-office"_L1, u"\u"},
+ //{"applications-other"_L1, u"\u"},
+ //{"applications-science"_L1, u"\uea4b"},
+ {"applications-system"_L1, u"\ue770"},
+ //{"applications-utilities"_L1, u"\u"},
+ //{"preferences-desktop"_L1, u"\ueb97"},
+ //{"preferences-desktop-peripherals"_L1, u"\u"},
+ //{"preferences-desktop-personal"_L1, u"\uf835"},
+ //{"preferences-other"_L1, u"\u"},
+ //{"preferences-system"_L1, u"\ue8b8"},
+ //{"preferences-system-network"_L1, u"\ue894"},
+ {"system-help"_L1, u"\ue946"},
+
+ {"audio-card"_L1, u"\ue8d6"},
+ {"audio-input-microphone"_L1, u"\ue720"},
+ {"battery"_L1, u"\ue83f"},
+ {"camera-photo"_L1, u"\ue722"},
+ {"camera-video"_L1, u"\ue714"},
+ {"camera-web"_L1, u"\ue8b8"},
+ {"computer"_L1, u"\ue7f8"}, // or e7fb?
+ {"drive-harddisk"_L1, u"\ueda2"},
+ {"drive-optical"_L1, u"\ue958"},
+ //{"drive-removable-media"_L1, u"\u"},
+ //{"input-gaming"_L1, u"\u"},
+ {"input-keyboard"_L1, u"\ue92e"},
+ {"input-mouse"_L1, u"\ue962"},
+ {"input-tablet"_L1, u"\ue70a"},
+ {"media-flash"_L1, u"\ue88e"},
+ //{"media-floppy"_L1, u"\u"},
+ {"media-optical"_L1, u"\ue958"},
+ {"media-tape"_L1, u"\ue96a"},
+ //{"modem"_L1, u"\u"},
+ //{"multimedia-player"_L1, u"\u"},
+ {"network-wired"_L1, u"\ue968"},
+ {"network-wireless"_L1, u"\ue701"},
+ //{"pda"_L1, u"\u"},
+ {"phone"_L1, u"\ue717"},
+ {"printer"_L1, u"\ue749"},
+ {"scanner"_L1, u"\ue8fe"},
+ //{"video-display"_L1, u"\uf06a"},
+
+ {"emblem-default"_L1, u"\uf56d"},
+ {"emblem-documents"_L1, u"\ue8a5"},
+ {"emblem-downloads"_L1, u"\ue896"},
+ {"emblem-favorite"_L1, u"\ue734"},
+ {"emblem-important"_L1, u"\ue8c9"},
+ {"emblem-mail"_L1, u"\ue715"},
+ {"emblem-photos"_L1, u"\ue91b"},
+ //{"emblem-readonly"_L1, u"\u"},
+ {"emblem-shared"_L1, u"\ue902"},
+ {"emblem-symbolic-link"_L1, u"\ue71b"},
+ {"emblem-synchronized"_L1, u"\uedab"},
+ {"emblem-system"_L1, u"\ue770"},
+ //{"emblem-unreadable"_L1, u"\u"},
+
+ {"folder"_L1, u"\ue8b7"},
+ //{"folder-remote"_L1, u"\u"},
+ //{"network-server"_L1, u"\ue875"},
+ //{"network-workgroup"_L1, u"\ue1a0"},
+ {"start-here"_L1, u"\ue8fc"}, // unsure
+ {"user-bookmarks"_L1, u"\ue8a4"},
+ //{"user-desktop"_L1, u"\ue30a"},
+ {"user-home"_L1, u"\ue80f"},
+ {"user-trash"_L1, u"\ue74d"},
+
+ //{"appointment-missed"_L1, u"\ue615"},
+ //{"appointment-soon"_L1, u"\uf540"},
+ {"audio-volume-high"_L1, u"\ue995"},
+ {"audio-volume-low"_L1, u"\ue993"},
+ {"audio-volume-medium"_L1, u"\ue994"},
+ {"audio-volume-muted"_L1, u"\ue992"},
+ //{"battery-caution"_L1, u"\ue19c"},
+ {"battery-low"_L1, u"\ue851"}, // ?
+ {"dialog-error"_L1, u"\ue783"},
+ {"dialog-information"_L1, u"\ue946"},
+ //{"dialog-password"_L1, u"\uf042"},
+ {"dialog-question"_L1, u"\uf142"}, // unsure
+ {"dialog-warning"_L1, u"\ue7ba"},
+ //{"folder-drag-accept"_L1, @u"\ue9a3"},
+ {"folder-open"_L1, u"\ue838"},
+ //{"folder-visiting"_L1, u"\ue8a7"},
+ //{"image-loading"_L1, u"\ue41a"},
+ //{"image-missing"_L1, u"\ue3ad"},
+ {"mail-attachment"_L1, u"\ue723"},
+ //{"mail-unread"_L1, u"\uf18a"},
+ //{"mail-read"_L1, u"\uf18c"},
+ {"mail-replied"_L1, u"\ue8ca"},
+ //{"mail-signed"_L1, u"\u"},
+ //{"mail-signed-verified"_L1, u"\u"},
+ {"media-playlist-repeat"_L1, u"\ue8ee"},
+ {"media-playlist-shuffle"_L1, u"\ue8b1"},
+ //{"network-error"_L1, u"\uead9"},
+ //{"network-idle"_L1, u"\ue51f"},
+ {"network-offline"_L1, u"\uf384"},
+ //{"network-receive"_L1, u"\ue2c0"},
+ //{"network-transmit"_L1, u"\ue2c3"},
+ //{"network-transmit-receive"_L1, u"\uca18"},
+ //{"printer-error"_L1, u"\uf7a0"},
+ //{"printer-printing"_L1, u"\uf7a1"},
+ //{"security-high"_L1, u"\ue32a"},
+ //{"security-medium"_L1, u"\ue9e0"},
+ //{"security-low"_L1, u"\uf012"},
+ //{"software-update-available"_L1, u"\ue923"},
+ //{"software-update-urgent"_L1, u"\uf05a"},
+ {"sync-error"_L1, u"\uea6a"},
+ {"sync-synchronizing"_L1, u"\ue895"},
+ //{"task-due"_L1, u"\u"},
+ //{"task-past-due"_L1, u"\u"},
+ {"user-available"_L1, u"\ue8cf"},
+ //{"user-away"_L1, u"\ue510"},
+ //{"user-idle"_L1, u"\u"},
+ //{"user-offline"_L1, u"\uf7b3"},
+ //{"user-trash-full"_L1, u"\ue872"}, //delete
+ //{"user-trash-full"_L1, u"\ue92b"}, //delete_forever
+ {"weather-clear"_L1, u"\ue706"},
+ //{"weather-clear-night"_L1, u"\uf159"},
+ //{"weather-few-clouds"_L1, u"\uf172"},
+ //{"weather-few-clouds-night"_L1, u"\uf174"},
+ //{"weather-fog"_L1, u"\ue818"},
+ {"weather-overcast"_L1, u"\ue753"},
+ //{"weather-severe-alert"_L1, u"\ue002"}, //warning
+ //{"weather-showers"_L1, u"\uf176"},
+ //{"weather-showers-scattered"_L1, u"\u"},
+ //{"weather-snow"_L1, u"\ue80f"}, //snowing
+ //{"weather-storm"_L1, u"\uf070"},
+ };
+
+ const auto it = std::find_if(std::begin(glyphMap), std::end(glyphMap), [this](const auto &c){
+ return c.first == m_iconName;
+ });
+
+ return it != std::end(glyphMap) ? it->second.toString()
+ : (m_iconName.length() == 1 ? m_iconName : QString());
+}
+
+namespace {
+auto iconFontFamily()
+{
+ static const bool isWindows11 = QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11;
+ return isWindows11 ? u"Segoe Fluent Icons"_s
+ : u"Segoe MDL2 Assets"_s;
+}
+}
+
+QWindowsIconEngine::QWindowsIconEngine(const QString &iconName)
+ : m_iconName(iconName), m_iconFont(iconFontFamily())
+ , m_glyphs(glyphs())
+{
+}
+
+QWindowsIconEngine::~QWindowsIconEngine()
+{}
+
+QIconEngine *QWindowsIconEngine::clone() const
+{
+ return new QWindowsIconEngine(m_iconName);
+}
+
+QString QWindowsIconEngine::key() const
+{
+ return u"QWindowsIconEngine"_s;
+}
+
+QString QWindowsIconEngine::iconName()
+{
+ return m_iconName;
+}
+
+bool QWindowsIconEngine::isNull()
+{
+ if (m_glyphs.isEmpty())
+ return true;
+
+ const QChar c0 = m_glyphs.at(0);
+ const QFontMetrics fontMetrics(m_iconFont);
+ if (c0.category() == QChar::Other_Surrogate && m_glyphs.size() > 1)
+ return !fontMetrics.inFontUcs4(QChar::surrogateToUcs4(c0, m_glyphs.at(1)));
+ return !fontMetrics.inFont(c0);
+}
+
+QList<QSize> QWindowsIconEngine::availableSizes(QIcon::Mode, QIcon::State)
+{
+ return {{16, 16}, {24, 24}, {48, 48}, {128, 128}};
+}
+
+QSize QWindowsIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+ return QIconEngine::actualSize(size, mode, state);
+}
+
+QPixmap QWindowsIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+ return scaledPixmap(size, mode, state, 1.0);
+}
+
+QPixmap QWindowsIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale)
+{
+ const quint64 cacheKey = calculateCacheKey(mode, state);
+ if (cacheKey != m_cacheKey || m_pixmap.size() != size || m_pixmap.devicePixelRatio() != scale) {
+ m_pixmap = QPixmap(size * scale);
+ m_pixmap.fill(Qt::transparent);
+ m_pixmap.setDevicePixelRatio(scale);
+
+ QPainter painter(&m_pixmap);
+ paint(&painter, QRect(QPoint(), size), mode, state);
+
+ m_cacheKey = cacheKey;
+ }
+
+ return m_pixmap;
+}
+
+void QWindowsIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state)
+{
+ Q_UNUSED(state);
+
+ painter->save();
+ QFont renderFont(m_iconFont);
+ renderFont.setPixelSize(rect.height());
+ painter->setFont(renderFont);
+
+ QPalette palette;
+ switch (mode) {
+ case QIcon::Active:
+ painter->setPen(palette.color(QPalette::Active, QPalette::Text));
+ break;
+ case QIcon::Normal:
+ painter->setPen(palette.color(QPalette::Active, QPalette::Text));
+ break;
+ case QIcon::Disabled:
+ painter->setPen(palette.color(QPalette::Disabled, QPalette::Text));
+ break;
+ case QIcon::Selected:
+ painter->setPen(palette.color(QPalette::Active, QPalette::HighlightedText));
+ break;
+ }
+
+ painter->drawText(rect, Qt::AlignCenter, m_glyphs);
+ painter->restore();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/qwindowsiconengine.h b/src/plugins/platforms/windows/qwindowsiconengine.h
new file mode 100644
index 0000000000..3c6cbddb8b
--- /dev/null
+++ b/src/plugins/platforms/windows/qwindowsiconengine.h
@@ -0,0 +1,47 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QWINDOWSICONENGINE_H
+#define QWINDOWSICONENGINE_H
+
+#include <QtCore/qt_windows.h>
+
+#include <QtGui/qfont.h>
+#include <QtGui/qiconengine.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsIconEngine : public QIconEngine
+{
+public:
+ QWindowsIconEngine(const QString &iconName);
+ ~QWindowsIconEngine();
+ QIconEngine *clone() const override;
+ QString key() const override;
+ QString iconName() override;
+ bool isNull() override;
+
+ QList<QSize> availableSizes(QIcon::Mode, QIcon::State) override;
+ QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) override;
+ QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override;
+ QPixmap scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale) override;
+ void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override;
+
+private:
+ static constexpr quint64 calculateCacheKey(QIcon::Mode mode, QIcon::State state)
+ {
+ return (quint64(mode) << 32) | state;
+ }
+
+ QString glyphs() const;
+
+ const QString m_iconName;
+ const QFont m_iconFont;
+ const QString m_glyphs;
+ mutable QPixmap m_pixmap;
+ mutable quint64 m_cacheKey = {};
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSICONENGINE_H
diff --git a/src/plugins/platforms/windows/qwindowsinputcontext.cpp b/src/plugins/platforms/windows/qwindowsinputcontext.cpp
index 758490ffb7..0281025b5b 100644
--- a/src/plugins/platforms/windows/qwindowsinputcontext.cpp
+++ b/src/plugins/platforms/windows/qwindowsinputcontext.cpp
@@ -676,7 +676,7 @@ int QWindowsInputContext::reconvertString(RECONVERTSTRING *reconv)
reconv->dwTargetStrOffset = reconv->dwCompStrOffset;
auto *pastReconv = reinterpret_cast<ushort *>(reconv + 1);
std::copy(surroundingText.utf16(), surroundingText.utf16() + surroundingText.size(),
- QT_MAKE_UNCHECKED_ARRAY_ITERATOR(pastReconv));
+ pastReconv);
return memSize;
}
diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp
index 2faeb7456b..aa6be266da 100644
--- a/src/plugins/platforms/windows/qwindowsintegration.cpp
+++ b/src/plugins/platforms/windows/qwindowsintegration.cpp
@@ -48,6 +48,11 @@
#include <QtCore/qdebug.h>
#include <QtCore/qvariant.h>
+#include <QtCore/qoperatingsystemversion.h>
+#include <QtCore/private/qfunctions_win_p.h>
+
+#include <wrl.h>
+
#include <limits.h>
#if !defined(QT_NO_OPENGL)
@@ -56,6 +61,14 @@
#include "qwindowsopengltester.h"
+#if QT_CONFIG(cpp_winrt)
+# include <QtCore/private/qt_winrtbase_p.h>
+# include <winrt/Windows.UI.Notifications.h>
+# include <winrt/Windows.Data.Xml.Dom.h>
+# include <winrt/Windows.Foundation.h>
+# include <winrt/Windows.UI.ViewManagement.h>
+#endif
+
#include <memory>
static inline void initOpenGlBlacklistResources()
@@ -67,31 +80,6 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-/*!
- \class QWindowsIntegration
- \brief QPlatformIntegration implementation for Windows.
- \internal
-
- \section1 Programming Considerations
-
- The platform plugin should run on Desktop Windows from Windows XP onwards
- and Windows Embedded.
-
- It should compile with:
- \list
- \li Microsoft Visual Studio 2013 or later (using the Microsoft Windows SDK,
- (\c Q_CC_MSVC).
- \li Stock \l{http://mingw.org/}{MinGW} (\c Q_CC_MINGW).
- This version ships with headers that are missing a lot of WinAPI.
- \li MinGW distributions using GCC 4.7 or higher and a recent MinGW-w64 runtime API,
- such as \l{http://tdm-gcc.tdragon.net/}{TDM-GCC}, or
- \l{http://mingwbuilds.sourceforge.net/}{MinGW-builds}
- (\c Q_CC_MINGW and \c __MINGW64_VERSION_MAJOR indicating the version).
- MinGW-w64 provides more complete headers (compared to stock MinGW from mingw.org),
- including a considerable part of the Windows SDK.
- \endlist
-*/
-
struct QWindowsIntegrationPrivate
{
Q_DISABLE_COPY_MOVE(QWindowsIntegrationPrivate)
@@ -131,7 +119,7 @@ bool parseIntOption(const QString &parameter,const QLatin1StringView &option,
const auto valueRef = QStringView{parameter}.right(valueLength);
const int value = valueRef.toInt(&ok);
if (ok) {
- if (value >= minimumValue && value <= maximumValue)
+ if (value >= int(minimumValue) && value <= int(maximumValue))
*target = static_cast<IntType>(value);
else {
qWarning() << "Value" << value << "for option" << option << "out of range"
@@ -148,14 +136,14 @@ using DarkModeHandling = QNativeInterface::Private::QWindowsApplication::DarkMod
static inline unsigned parseOptions(const QStringList &paramList,
int *tabletAbsoluteRange,
- QtWindows::ProcessDpiAwareness *dpiAwareness,
+ QtWindows::DpiAwareness *dpiAwareness,
DarkModeHandling *darkModeHandling)
{
unsigned options = 0;
for (const QString &param : paramList) {
if (param.startsWith(u"fontengine=")) {
- if (param.endsWith(u"directwrite")) {
- options |= QWindowsIntegration::FontDatabaseDirectWrite;
+ if (param.endsWith(u"gdi")) {
+ options |= QWindowsIntegration::FontDatabaseGDI;
} else if (param.endsWith(u"freetype")) {
options |= QWindowsIntegration::FontDatabaseFreeType;
} else if (param.endsWith(u"native")) {
@@ -179,7 +167,8 @@ static inline unsigned parseOptions(const QStringList &paramList,
options |= QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch;
} else if (parseIntOption(param, "verbose"_L1, 0, INT_MAX, &QWindowsContext::verbose)
|| parseIntOption(param, "tabletabsoluterange"_L1, 0, INT_MAX, tabletAbsoluteRange)
- || parseIntOption(param, "dpiawareness"_L1, QtWindows::ProcessDpiUnaware, QtWindows::ProcessPerMonitorV2DpiAware, dpiAwareness)) {
+ || parseIntOption(param, "dpiawareness"_L1, QtWindows::DpiAwareness::Invalid,
+ QtWindows::DpiAwareness::PerMonitorVersion2, dpiAwareness)) {
} else if (param == u"menus=native") {
options |= QWindowsIntegration::AlwaysUseNativeMenus;
} else if (param == u"menus=none") {
@@ -209,10 +198,11 @@ void QWindowsIntegrationPrivate::parseOptions(QWindowsIntegration *q, const QStr
static bool dpiAwarenessSet = false;
// Default to per-monitor-v2 awareness (if available)
- QtWindows::ProcessDpiAwareness dpiAwareness = QtWindows::ProcessPerMonitorV2DpiAware;
+ QtWindows::DpiAwareness dpiAwareness = QtWindows::DpiAwareness::PerMonitorVersion2;
int tabletAbsoluteRange = -1;
- DarkModeHandling darkModeHandling = DarkModeHandlingFlag::DarkModeWindowFrames;
+ DarkModeHandling darkModeHandling = DarkModeHandlingFlag::DarkModeWindowFrames
+ | DarkModeHandlingFlag::DarkModeStyle;
m_options = ::parseOptions(paramList, &tabletAbsoluteRange, &dpiAwareness, &darkModeHandling);
q->setDarkModeHandling(darkModeHandling);
QWindowsFontDatabase::setFontOptions(m_options);
@@ -223,25 +213,13 @@ void QWindowsIntegrationPrivate::parseOptions(QWindowsIntegration *q, const QStr
QCoreApplication::setAttribute(Qt::AA_CompressHighFrequencyEvents);
else
m_context.initTablet();
+ QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
if (!dpiAwarenessSet) { // Set only once in case of repeated instantiations of QGuiApplication.
if (!QCoreApplication::testAttribute(Qt::AA_PluginApplication)) {
- if (dpiAwareness == QtWindows::ProcessPerMonitorV2DpiAware) {
- // DpiAwareV2 requires using new API
- if (m_context.setProcessDpiV2Awareness()) {
- qCDebug(lcQpaWindow, "DpiAwareness: DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2");
- dpiAwarenessSet = true;
- } else {
- // fallback to old API
- dpiAwareness = QtWindows::ProcessPerMonitorDpiAware;
- }
- }
-
- if (!dpiAwarenessSet) {
- m_context.setProcessDpiAwareness(dpiAwareness);
- qCDebug(lcQpaWindow) << "DpiAwareness=" << dpiAwareness
- << "effective process DPI awareness=" << QWindowsContext::processDpiAwareness();
- }
+ m_context.setProcessDpiAwareness(dpiAwareness);
+ qCDebug(lcQpaWindow) << "DpiAwareness=" << dpiAwareness
+ << "effective process DPI awareness=" << QWindowsContext::processDpiAwareness();
}
dpiAwarenessSet = true;
}
@@ -278,9 +256,9 @@ QWindowsIntegration::~QWindowsIntegration()
void QWindowsIntegration::initialize()
{
- QString icStr = QPlatformInputContextFactory::requested();
- icStr.isNull() ? d->m_inputContext.reset(new QWindowsInputContext)
- : d->m_inputContext.reset(QPlatformInputContextFactory::create(icStr));
+ auto icStrs = QPlatformInputContextFactory::requested();
+ icStrs.isEmpty() ? d->m_inputContext.reset(new QWindowsInputContext)
+ : d->m_inputContext.reset(QPlatformInputContextFactory::create(icStrs));
}
bool QWindowsIntegration::hasCapability(QPlatformIntegration::Capability cap) const
@@ -308,6 +286,8 @@ bool QWindowsIntegration::hasCapability(QPlatformIntegration::Capability cap) co
return true;
case SwitchableWidgetComposition:
return false; // QTBUG-68329 QTBUG-53515 QTBUG-54734
+ case BackingStoreStaticContents:
+ return true;
default:
return QPlatformIntegration::hasCapability(cap);
}
@@ -328,10 +308,12 @@ QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) cons
requested.geometry = window->isTopLevel()
? QHighDpi::toNativePixels(window->geometry(), window)
: QHighDpi::toNativeLocalPosition(window->geometry(), window);
- // Apply custom margins (see QWindowsWindow::setCustomMargins())).
- const QVariant customMarginsV = window->property("_q_windowsCustomMargins");
- if (customMarginsV.isValid())
- requested.customMargins = qvariant_cast<QMargins>(customMarginsV);
+ if (!(requested.flags & Qt::FramelessWindowHint)) {
+ // Apply custom margins (see QWindowsWindow::setCustomMargins())).
+ const QVariant customMarginsV = window->property("_q_windowsCustomMargins");
+ if (customMarginsV.isValid())
+ requested.customMargins = qvariant_cast<QMargins>(customMarginsV);
+ }
QWindowsWindowData obtained =
QWindowsWindowData::create(window, requested,
@@ -502,17 +484,17 @@ QWindowsStaticOpenGLContext *QWindowsIntegration::staticOpenGLContext()
QPlatformFontDatabase *QWindowsIntegration::fontDatabase() const
{
if (!d->m_fontDatabase) {
-#if QT_CONFIG(directwrite3)
- if (d->m_options & QWindowsIntegration::FontDatabaseDirectWrite)
- d->m_fontDatabase = new QWindowsDirectWriteFontDatabase;
- else
-#endif
#ifndef QT_NO_FREETYPE
if (d->m_options & QWindowsIntegration::FontDatabaseFreeType)
d->m_fontDatabase = new QWindowsFontDatabaseFT;
else
#endif // QT_NO_FREETYPE
- d->m_fontDatabase = new QWindowsFontDatabase();
+#if QT_CONFIG(directwrite3)
+ if (!(d->m_options & (QWindowsIntegration::FontDatabaseGDI | QWindowsIntegration::DontUseDirectWriteFonts)))
+ d->m_fontDatabase = new QWindowsDirectWriteFontDatabase;
+ else
+#endif
+ d->m_fontDatabase = new QWindowsFontDatabase;
}
return d->m_fontDatabase;
}
@@ -560,14 +542,9 @@ QVariant QWindowsIntegration::styleHint(QPlatformIntegration::StyleHint hint) co
return QPlatformIntegration::styleHint(hint);
}
-Qt::KeyboardModifiers QWindowsIntegration::queryKeyboardModifiers() const
-{
- return QWindowsKeyMapper::queryKeyboardModifiers();
-}
-
-QList<int> QWindowsIntegration::possibleKeys(const QKeyEvent *e) const
+QPlatformKeyMapper *QWindowsIntegration::keyMapper() const
{
- return d->m_context.possibleKeys(e);
+ return d->m_context.keyMapper();
}
#if QT_CONFIG(clipboard)
@@ -634,6 +611,161 @@ void QWindowsIntegration::beep() const
MessageBeep(MB_OK); // For QApplication
}
+void QWindowsIntegration::setApplicationBadge(qint64 number)
+{
+ // Clamp to positive numbers, as the Windows API doesn't support negative numbers
+ number = qMax(0, number);
+
+ // Persist, so we can re-apply it on setting changes and Explorer restart
+ m_applicationBadgeNumber = number;
+
+ static const bool isWindows11 = QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11;
+
+#if QT_CONFIG(cpp_winrt)
+ // We prefer the native BadgeUpdater API, that allows us to set a number directly,
+ // but it requires that the application has a package identity, and also doesn't
+ // seem to work in all cases on < Windows 11.
+ QT_TRY {
+ if (isWindows11 && qt_win_hasPackageIdentity()) {
+ using namespace winrt::Windows::UI::Notifications;
+ auto badgeXml = BadgeUpdateManager::GetTemplateContent(BadgeTemplateType::BadgeNumber);
+ badgeXml.SelectSingleNode(L"//badge/@value").NodeValue(winrt::box_value(winrt::to_hstring(number)));
+ BadgeUpdateManager::CreateBadgeUpdaterForApplication().Update(BadgeNotification(badgeXml));
+ return;
+ }
+ } QT_CATCH(...) {
+ // fall back to win32 implementation
+ }
+#endif
+
+ // Fallback for non-packaged apps, Windows 10, or Qt builds without WinRT/C++ support
+
+ if (!number) {
+ // Clear badge
+ setApplicationBadge(QImage());
+ return;
+ }
+
+ const bool isDarkMode = QWindowsTheme::instance()->colorScheme()
+ == Qt::ColorScheme::Dark;
+
+ QColor badgeColor;
+ QColor textColor;
+
+#if QT_CONFIG(cpp_winrt)
+ if (isWindows11) {
+ // Match colors used by BadgeUpdater
+ static const auto fromUIColor = [](winrt::Windows::UI::Color &&color) {
+ return QColor(color.R, color.G, color.B, color.A);
+ };
+ using namespace winrt::Windows::UI::ViewManagement;
+ const auto settings = UISettings();
+ badgeColor = fromUIColor(settings.GetColorValue(isDarkMode ?
+ UIColorType::AccentLight2 : UIColorType::Accent));
+ textColor = fromUIColor(settings.GetColorValue(UIColorType::Background));
+ }
+#endif
+
+ if (!badgeColor.isValid()) {
+ // Fall back to basic badge colors, based on Windows 10 look
+ badgeColor = isDarkMode ? Qt::black : QColor(220, 220, 220);
+ badgeColor.setAlphaF(0.5f);
+ textColor = isDarkMode ? Qt::white : Qt::black;
+ }
+
+ const auto devicePixelRatio = qApp->devicePixelRatio();
+
+ static const QSize iconBaseSize(16, 16);
+ QImage image(iconBaseSize * devicePixelRatio,
+ QImage::Format_ARGB32_Premultiplied);
+ image.fill(Qt::transparent);
+
+ QPainter painter(&image);
+
+ QRect badgeRect = image.rect();
+ QPen badgeBorderPen = Qt::NoPen;
+ if (!isWindows11) {
+ QColor badgeBorderColor = textColor;
+ badgeBorderColor.setAlphaF(0.5f);
+ badgeBorderPen = badgeBorderColor;
+ badgeRect.adjust(1, 1, -1, -1);
+ }
+ painter.setBrush(badgeColor);
+ painter.setPen(badgeBorderPen);
+ painter.setRenderHint(QPainter::Antialiasing);
+ painter.drawEllipse(badgeRect);
+
+ auto pixelSize = qCeil(10.5 * devicePixelRatio);
+ // Unlike the BadgeUpdater API we're limited by a square
+ // badge, so adjust the font size when above two digits.
+ const bool textOverflow = number > 99;
+ if (textOverflow)
+ pixelSize *= 0.8;
+
+ QFont font = painter.font();
+ font.setPixelSize(pixelSize);
+ font.setWeight(isWindows11 ? QFont::Medium : QFont::DemiBold);
+ painter.setFont(font);
+
+ painter.setRenderHint(QPainter::TextAntialiasing, devicePixelRatio > 1);
+ painter.setPen(textColor);
+
+ auto text = textOverflow ? u"99+"_s : QString::number(number);
+ painter.translate(textOverflow ? 1 : 0, textOverflow ? 0 : -1);
+ painter.drawText(image.rect(), Qt::AlignCenter, text);
+
+ painter.end();
+
+ setApplicationBadge(image);
+}
+
+void QWindowsIntegration::setApplicationBadge(const QImage &image)
+{
+ QComHelper comHelper;
+
+ using Microsoft::WRL::ComPtr;
+
+ ComPtr<ITaskbarList3> taskbarList;
+ CoCreateInstance(CLSID_TaskbarList, nullptr,
+ CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&taskbarList));
+ if (!taskbarList) {
+ // There may not be any windows with a task bar button yet,
+ // in which case we'll apply the badge once a window with
+ // a button has been created.
+ return;
+ }
+
+ const auto hIcon = image.toHICON();
+
+ // Apply the icon to all top level windows, since the badge is
+ // set on an application level. If one of the windows go away
+ // the other windows will take over in showing the badge.
+ const auto topLevelWindows = QGuiApplication::topLevelWindows();
+ for (auto *topLevelWindow : topLevelWindows) {
+ if (!topLevelWindow->handle())
+ continue;
+ auto hwnd = reinterpret_cast<HWND>(topLevelWindow->winId());
+ taskbarList->SetOverlayIcon(hwnd, hIcon, L"");
+ }
+
+ DestroyIcon(hIcon);
+
+ // FIXME: Update icon when the application scale factor changes.
+ // Doing so in response to screen DPI changes is too soon, as the
+ // task bar is not yet ready for an updated icon, and will just
+ // result in a blurred icon even if our icon is high-DPI.
+}
+
+void QWindowsIntegration::updateApplicationBadge()
+{
+ // The system color settings have changed, or we are reacting
+ // to a task bar button being created for the fist time or after
+ // Explorer had crashed and re-started. In any case, re-apply the
+ // badge so that everything is up to date.
+ if (m_applicationBadgeNumber)
+ setApplicationBadge(m_applicationBadgeNumber);
+}
+
#if QT_CONFIG(vulkan)
QPlatformVulkanInstance *QWindowsIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const
{
diff --git a/src/plugins/platforms/windows/qwindowsintegration.h b/src/plugins/platforms/windows/qwindowsintegration.h
index 265ef719a6..c271207741 100644
--- a/src/plugins/platforms/windows/qwindowsintegration.h
+++ b/src/plugins/platforms/windows/qwindowsintegration.h
@@ -10,7 +10,9 @@
#include <qpa/qplatformintegration.h>
#include <QtCore/qscopedpointer.h>
#include <QtGui/private/qwindowsfontdatabase_p.h>
+#ifndef QT_NO_OPENGL
#include <QtGui/private/qopenglcontext_p.h>
+#endif
#include <qpa/qplatformopenglcontext.h>
QT_BEGIN_NAMESPACE
@@ -43,7 +45,7 @@ public:
DontUseWMPointer = 0x400,
DetectAltGrModifier = 0x800,
RtlEnabled = 0x1000,
- FontDatabaseDirectWrite = 0x2000
+ FontDatabaseGDI = 0x2000
};
explicit QWindowsIntegration(const QStringList &paramList);
@@ -80,8 +82,7 @@ public:
QPlatformServices *services() const override;
QVariant styleHint(StyleHint hint) const override;
- Qt::KeyboardModifiers queryKeyboardModifiers() const override;
- QList<int> possibleKeys(const QKeyEvent *e) const override;
+ QPlatformKeyMapper *keyMapper() const override;
static QWindowsIntegration *instance() { return m_instance; }
@@ -89,6 +90,10 @@ public:
void beep() const override;
+ void setApplicationBadge(qint64 number) override;
+ void setApplicationBadge(const QImage &image);
+ void updateApplicationBadge();
+
#if QT_CONFIG(sessionmanager)
QPlatformSessionManager *createPlatformSessionManager(const QString &id, const QString &key) const override;
#endif
@@ -104,6 +109,8 @@ private:
QScopedPointer<QWindowsIntegrationPrivate> d;
static QWindowsIntegration *m_instance;
+
+ qint64 m_applicationBadgeNumber = 0;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/qwindowsinternalmimedata.cpp b/src/plugins/platforms/windows/qwindowsinternalmimedata.cpp
index 11d5bc1c36..0542473a4b 100644
--- a/src/plugins/platforms/windows/qwindowsinternalmimedata.cpp
+++ b/src/plugins/platforms/windows/qwindowsinternalmimedata.cpp
@@ -3,7 +3,7 @@
#include "qwindowsinternalmimedata.h"
#include "qwindowscontext.h"
-#include "qwindowsmime.h"
+#include "qwindowsmimeregistry.h"
#include <QtCore/qdebug.h>
#include <QtCore/qvariant.h>
@@ -22,9 +22,9 @@
The base class introduces new virtuals to obtain and release
the instances IDataObject from the clipboard or Drag and Drop and
- does conversion using QWindowsMime classes.
+ does conversion using QWindowsMimeConverter classes.
- \sa QInternalMimeData, QWindowsMime, QWindowsMimeConverter
+ \sa QInternalMimeData, QWindowsMimeConverter, QWindowsMimeRegistry
\internal
*/
@@ -34,7 +34,7 @@ bool QWindowsInternalMimeData::hasFormat_sys(const QString &mime) const
if (!pDataObj)
return false;
- const QWindowsMimeConverter &mc = QWindowsContext::instance()->mimeConverter();
+ const QWindowsMimeRegistry &mc = QWindowsContext::instance()->mimeConverter();
const bool has = mc.converterToMime(mime, pDataObj) != nullptr;
releaseDataObject(pDataObj);
qCDebug(lcQpaMime) << __FUNCTION__ << mime << has;
@@ -47,7 +47,7 @@ QStringList QWindowsInternalMimeData::formats_sys() const
if (!pDataObj)
return QStringList();
- const QWindowsMimeConverter &mc = QWindowsContext::instance()->mimeConverter();
+ const QWindowsMimeRegistry &mc = QWindowsContext::instance()->mimeConverter();
const QStringList fmts = mc.allMimesForFormats(pDataObj);
releaseDataObject(pDataObj);
qCDebug(lcQpaMime) << __FUNCTION__ << fmts;
@@ -61,7 +61,7 @@ QVariant QWindowsInternalMimeData::retrieveData_sys(const QString &mimeType, QMe
return QVariant();
QVariant result;
- const QWindowsMimeConverter &mc = QWindowsContext::instance()->mimeConverter();
+ const QWindowsMimeRegistry &mc = QWindowsContext::instance()->mimeConverter();
if (auto converter = mc.converterToMime(mimeType, pDataObj))
result = converter->convertToMime(mimeType, pDataObj, type);
releaseDataObject(pDataObj);
diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp
index 006396d92a..ba76cda40b 100644
--- a/src/plugins/platforms/windows/qwindowskeymapper.cpp
+++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp
@@ -15,6 +15,7 @@
#include <QtGui/qevent.h>
#include <QtGui/private/qwindowsguieventdispatcher_p.h>
#include <QtCore/private/qdebug_p.h>
+#include <QtCore/private/qtools_p.h>
#if defined(WM_APPCOMMAND)
# ifndef FAPPCOMMAND_MOUSE
@@ -87,9 +88,17 @@ QWindowsKeyMapper::~QWindowsKeyMapper()= default;
#define VK_OEM_3 0xC0
#endif
-// We not only need the scancode itself but also the extended bit of key messages. Thus we need
-// the additional bit when masking the scancode.
-enum { scancodeBitmask = 0x1ff };
+// Get scancode from the given message
+static constexpr quint32 getScancode(const MSG &msg)
+{
+ const auto keyFlags = HIWORD(msg.lParam);
+ quint32 scancode = LOBYTE(keyFlags);
+ // if extended-key flag is on, the scan code consists of a sequence of two bytes,
+ // where the first byte has a value of 0xe0.
+ if ((keyFlags & KF_EXTENDED) != 0)
+ scancode |= 0xE000;
+ return scancode;
+}
// Key recorder ------------------------------------------------------------------------[ start ] --
struct KeyRecord {
@@ -531,33 +540,6 @@ QDebug operator<<(QDebug d, const KeyboardLayoutItem &k)
d << ')';
return d;
}
-
-// Helpers to format a list of int as Qt key sequence
-class formatKeys
-{
-public:
- explicit formatKeys(const QList<int> &keys) : m_keys(keys) {}
-
-private:
- friend QDebug operator<<(QDebug d, const formatKeys &keys);
- const QList<int> &m_keys;
-};
-
-QDebug operator<<(QDebug d, const formatKeys &k)
-{
- QDebugStateSaver saver(d);
- d.nospace();
- d << '(';
- for (int i =0, size = k.m_keys.size(); i < size; ++i) {
- if (i)
- d << ", ";
- d << QKeySequence(k.m_keys.at(i));
- }
- d << ')';
- return d;
-}
-#else // !QT_NO_DEBUG_STREAM
-static int formatKeys(const QList<int> &) { return 0; }
#endif // QT_NO_DEBUG_STREAM
/**
@@ -599,8 +581,7 @@ static inline quint32 toKeyOrUnicode(quint32 vk, quint32 scancode, unsigned char
static inline int asciiToKeycode(char a, int state)
{
- if (a >= 'a' && a <= 'z')
- a = toupper(a);
+ a = QtMiscUtils::toAsciiUpper(a);
if ((state & Qt::ControlModifier) != 0) {
if (a >= 0 && a <= 31) // Ctrl+@..Ctrl+A..CTRL+Z..Ctrl+_
a += '@'; // to @..A..Z.._
@@ -656,7 +637,7 @@ void QWindowsKeyMapper::updateKeyMap(const MSG &msg)
{
unsigned char kbdBuffer[256]; // Will hold the complete keyboard state
GetKeyboardState(kbdBuffer);
- const quint32 scancode = (msg.lParam >> 16) & scancodeBitmask;
+ const quint32 scancode = getScancode(msg);
updatePossibleKeyCodes(kbdBuffer, scancode, quint32(msg.wParam));
}
@@ -778,50 +759,49 @@ static void showSystemMenu(QWindow* w)
if (!menu)
return; // no menu for this window
-#define enabled (MF_BYCOMMAND | MF_ENABLED)
-#define disabled (MF_BYCOMMAND | MF_GRAYED)
+#define enabled (MF_BYCOMMAND | MFS_ENABLED)
+#define disabled (MF_BYCOMMAND | MFS_GRAYED)
- EnableMenuItem(menu, SC_MINIMIZE, (topLevel->flags() & Qt::WindowMinimizeButtonHint)?enabled:disabled);
- bool maximized = IsZoomed(topLevelHwnd);
+ EnableMenuItem(menu, SC_MINIMIZE, (topLevel->flags() & Qt::WindowMinimizeButtonHint) ? enabled : disabled);
+ const bool maximized = IsZoomed(topLevelHwnd);
- EnableMenuItem(menu, SC_MAXIMIZE, ! (topLevel->flags() & Qt::WindowMaximizeButtonHint) || maximized?disabled:enabled);
+ EnableMenuItem(menu, SC_MAXIMIZE, !(topLevel->flags() & Qt::WindowMaximizeButtonHint) || maximized ? disabled : enabled);
// We should _not_ check with the setFixedSize(x,y) case here, since Windows is not able to check
// this and our menu here would be out-of-sync with the menu produced by mouse-click on the
// System Menu, or right-click on the title bar.
- EnableMenuItem(menu, SC_SIZE, (topLevel->flags() & Qt::MSWindowsFixedSizeDialogHint) || maximized?disabled:enabled);
- EnableMenuItem(menu, SC_MOVE, maximized?disabled:enabled);
+ EnableMenuItem(menu, SC_SIZE, (topLevel->flags() & Qt::MSWindowsFixedSizeDialogHint) || maximized ? disabled : enabled);
+ EnableMenuItem(menu, SC_MOVE, maximized ? disabled : enabled);
EnableMenuItem(menu, SC_CLOSE, enabled);
+ EnableMenuItem(menu, SC_RESTORE, maximized ? enabled : disabled);
// Highlight the first entry in the menu, this is what native Win32 applications usually do.
- MENUITEMINFOW restoreItem;
- SecureZeroMemory(&restoreItem, sizeof(restoreItem));
- restoreItem.cbSize = sizeof(restoreItem);
- restoreItem.fMask = MIIM_STATE;
- restoreItem.fState = MFS_HILITE | (maximized ? MFS_ENABLED : MFS_GRAYED);
- SetMenuItemInfoW(menu, SC_RESTORE, FALSE, &restoreItem);
+ HiliteMenuItem(topLevelHwnd, menu, SC_RESTORE, MF_BYCOMMAND | MFS_HILITE);
// Set bold on close menu item
- MENUITEMINFO closeItem;
- closeItem.cbSize = sizeof(MENUITEMINFO);
- closeItem.fMask = MIIM_STATE;
- closeItem.fState = MFS_DEFAULT;
- SetMenuItemInfo(menu, SC_CLOSE, FALSE, &closeItem);
+ SetMenuDefaultItem(menu, SC_CLOSE, FALSE);
#undef enabled
#undef disabled
+
const QPoint pos = QHighDpi::toNativePixels(topLevel->geometry().topLeft(), topLevel);
const int titleBarOffset = isSystemMenuOffsetNeeded(topLevel->flags()) ? getTitleBarHeight(topLevelHwnd) : 0;
const int ret = TrackPopupMenuEx(menu,
- TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | TPM_RETURNCMD,
+ TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | TPM_RETURNCMD,
pos.x(), pos.y() + titleBarOffset,
topLevelHwnd,
nullptr);
+
+ // Remove the highlight of the restore menu item, otherwise when the user right-clicks
+ // on the title bar, the popuped system menu will also highlight the restore item, which
+ // is not appropriate, it should only be highlighted if the menu is brought up by keyboard.
+ HiliteMenuItem(topLevelHwnd, menu, SC_RESTORE, MF_BYCOMMAND | MFS_UNHILITE);
+
if (ret)
qWindowsWndProc(topLevelHwnd, WM_SYSCOMMAND, WPARAM(ret), 0);
}
-static inline void sendExtendedPressRelease(QWindow *w, int k,
+static inline void sendExtendedPressRelease(QWindow *w, unsigned long timestamp, int k,
Qt::KeyboardModifiers mods,
quint32 nativeScanCode,
quint32 nativeVirtualKey,
@@ -830,8 +810,8 @@ static inline void sendExtendedPressRelease(QWindow *w, int k,
bool autorep = false,
ushort count = 1)
{
- QWindowSystemInterface::handleExtendedKeyEvent(w, QEvent::KeyPress, k, mods, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count);
- QWindowSystemInterface::handleExtendedKeyEvent(w, QEvent::KeyRelease, k, mods, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count);
+ QWindowSystemInterface::handleExtendedKeyEvent(w, timestamp, QEvent::KeyPress, k, mods, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count);
+ QWindowSystemInterface::handleExtendedKeyEvent(w, timestamp, QEvent::KeyRelease, k, mods, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count);
}
/*!
@@ -898,7 +878,7 @@ bool QWindowsKeyMapper::translateMultimediaKeyEventInternal(QWindow *window, con
const int qtKey = int(CmdTbl[cmd]);
if (!skipPressRelease)
- sendExtendedPressRelease(receiver, qtKey, Qt::KeyboardModifier(state), 0, 0, 0);
+ sendExtendedPressRelease(receiver, msg.time, qtKey, Qt::KeyboardModifier(state), 0, 0, 0);
// QTBUG-43343: Make sure to return false if Qt does not handle the key, otherwise,
// the keys are not passed to the active media player.
# if QT_CONFIG(shortcut)
@@ -943,7 +923,7 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg,
m_seenAltGr = true;
const UINT msgType = msg.message;
- const quint32 scancode = (msg.lParam >> 16) & scancodeBitmask;
+ const quint32 scancode = getScancode(msg);
auto vk_key = quint32(msg.wParam);
quint32 nModifiers = 0;
@@ -978,7 +958,7 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg,
// A multi-character key or a Input method character
// not found by our look-ahead
if (msgType == WM_CHAR || msgType == WM_IME_CHAR) {
- sendExtendedPressRelease(receiver, 0, Qt::KeyboardModifier(state), scancode, vk_key, nModifiers, messageKeyText(msg), false);
+ sendExtendedPressRelease(receiver, msg.time, 0, Qt::KeyboardModifier(state), scancode, 0, nModifiers, messageKeyText(msg), false);
return true;
}
@@ -1013,14 +993,14 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg,
if (dirStatus == VK_LSHIFT
&& ((msg.wParam == VK_SHIFT && GetKeyState(VK_LCONTROL))
|| (msg.wParam == VK_CONTROL && GetKeyState(VK_LSHIFT)))) {
- sendExtendedPressRelease(receiver, Qt::Key_Direction_L, {},
+ sendExtendedPressRelease(receiver, msg.time, Qt::Key_Direction_L, {},
scancode, vk_key, nModifiers, QString(), false);
result = true;
dirStatus = 0;
} else if (dirStatus == VK_RSHIFT
&& ( (msg.wParam == VK_SHIFT && GetKeyState(VK_RCONTROL))
|| (msg.wParam == VK_CONTROL && GetKeyState(VK_RSHIFT)))) {
- sendExtendedPressRelease(receiver, Qt::Key_Direction_R, {},
+ sendExtendedPressRelease(receiver, msg.time, Qt::Key_Direction_R, {},
scancode, vk_key, nModifiers, QString(), false);
result = true;
dirStatus = 0;
@@ -1233,9 +1213,9 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg,
// so, we have an auto-repeating key
if (rec) {
if (code < Qt::Key_Shift || code > Qt::Key_ScrollLock) {
- QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyRelease, code,
+ QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyRelease, code,
Qt::KeyboardModifier(state), scancode, quint32(msg.wParam), nModifiers, rec->text, true);
- QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyPress, code,
+ QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyPress, code,
Qt::KeyboardModifier(state), scancode, quint32(msg.wParam), nModifiers, rec->text, true);
result = true;
}
@@ -1261,7 +1241,7 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg,
if (msg.wParam == VK_PACKET)
code = asciiToKeycode(char(uch.cell()), state);
- QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyPress, code,
+ QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyPress, code,
modifiers, scancode, quint32(msg.wParam), nModifiers, text, false);
result =true;
bool store = true;
@@ -1303,10 +1283,10 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg,
if ((msg.lParam & 0x40000000) == 0 &&
Qt::KeyboardModifier(state) == Qt::NoModifier &&
((code == Qt::Key_F18) || (code == Qt::Key_F19) || (code == Qt::Key_F20))) {
- QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyPress, code,
+ QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyPress, code,
Qt::MetaModifier, scancode,
quint32(msg.wParam), MetaLeft);
- QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyRelease, code,
+ QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyRelease, code,
Qt::NoModifier, scancode,
quint32(msg.wParam), 0);
result = true;
@@ -1318,7 +1298,7 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg,
// Map SHIFT + Tab to SHIFT + BackTab, QShortcutMap knows about this translation
if (code == Qt::Key_Tab && (state & Qt::ShiftModifier) == Qt::ShiftModifier)
code = Qt::Key_Backtab;
- QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyRelease, code,
+ QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyRelease, code,
Qt::KeyboardModifier(state), scancode, quint32(msg.wParam),
nModifiers,
(rec ? rec->text : QString()), false);
@@ -1340,7 +1320,7 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg,
return result;
}
-Qt::KeyboardModifiers QWindowsKeyMapper::queryKeyboardModifiers()
+Qt::KeyboardModifiers QWindowsKeyMapper::queryKeyboardModifiers() const
{
Qt::KeyboardModifiers modifiers = Qt::NoModifier;
if (GetKeyState(VK_SHIFT) < 0)
@@ -1354,9 +1334,9 @@ Qt::KeyboardModifiers QWindowsKeyMapper::queryKeyboardModifiers()
return modifiers;
}
-QList<int> QWindowsKeyMapper::possibleKeys(const QKeyEvent *e) const
+QList<QKeyCombination> QWindowsKeyMapper::possibleKeyCombinations(const QKeyEvent *e) const
{
- QList<int> result;
+ QList<QKeyCombination> result;
const quint32 nativeVirtualKey = e->nativeVirtualKey();
@@ -1370,31 +1350,34 @@ QList<int> QWindowsKeyMapper::possibleKeys(const QKeyEvent *e) const
quint32 baseKey = kbItem.qtKey[0];
Qt::KeyboardModifiers keyMods = e->modifiers();
if (baseKey == Qt::Key_Return && (e->nativeModifiers() & ExtendedKey)) {
- result << (Qt::Key_Enter | keyMods).toCombined();
+ result << (Qt::Key_Enter | keyMods);
return result;
}
- result << int(baseKey) + int(keyMods); // The base key is _always_ valid, of course
+
+ // The base key is _always_ valid, of course
+ result << QKeyCombination::fromCombined(int(baseKey) + int(keyMods));
for (size_t i = 1; i < NumMods; ++i) {
Qt::KeyboardModifiers neededMods = ModsTbl[i];
quint32 key = kbItem.qtKey[i];
if (key && key != baseKey && ((keyMods & neededMods) == neededMods)) {
const Qt::KeyboardModifiers missingMods = keyMods & ~neededMods;
- const int matchedKey = int(key) + int(missingMods);
- const auto it =
- std::find_if(result.begin(), result.end(),
- [key] (int k) { return (k & ~Qt::KeyboardModifierMask) == key; });
+ const auto matchedKey = QKeyCombination::fromCombined(int(key) + int(missingMods));
+ const auto it = std::find_if(result.begin(), result.end(),
+ [key](auto keyCombination) {
+ return keyCombination.key() == key;
+ });
// QTBUG-67200: Use the match with the least modifiers (prefer
// Shift+9 over Alt + Shift + 9) resulting in more missing modifiers.
if (it == result.end())
result << matchedKey;
- else if (missingMods > (*it & Qt::KeyboardModifierMask))
+ else if (missingMods > it->keyboardModifiers())
*it = matchedKey;
}
}
qCDebug(lcQpaEvents) << __FUNCTION__ << e << "nativeVirtualKey="
<< Qt::showbase << Qt::hex << e->nativeVirtualKey() << Qt::dec << Qt::noshowbase
- << e->modifiers() << kbItem << "\n returns" << formatKeys(result);
+ << e->modifiers() << kbItem << "\n returns" << result;
return result;
}
diff --git a/src/plugins/platforms/windows/qwindowskeymapper.h b/src/plugins/platforms/windows/qwindowskeymapper.h
index 6e13c47323..72b2536ad7 100644
--- a/src/plugins/platforms/windows/qwindowskeymapper.h
+++ b/src/plugins/platforms/windows/qwindowskeymapper.h
@@ -8,6 +8,8 @@
#include <QtCore/qlocale.h>
+#include <qpa/qplatformkeymapper.h>
+
QT_BEGIN_NAMESPACE
class QKeyEvent;
@@ -33,7 +35,7 @@ struct KeyboardLayoutItem {
quint32 qtKey[NumQtKeys]; // Can by any Qt::Key_<foo>, or unicode character
};
-class QWindowsKeyMapper
+class QWindowsKeyMapper : public QPlatformKeyMapper
{
Q_DISABLE_COPY_MOVE(QWindowsKeyMapper)
public:
@@ -53,8 +55,8 @@ public:
QWindow *keyGrabber() const { return m_keyGrabber; }
void setKeyGrabber(QWindow *w) { m_keyGrabber = w; }
- static Qt::KeyboardModifiers queryKeyboardModifiers();
- QList<int> possibleKeys(const QKeyEvent *e) const;
+ Qt::KeyboardModifiers queryKeyboardModifiers() const override;
+ QList<QKeyCombination> possibleKeyCombinations(const QKeyEvent *e) const override;
private:
bool translateKeyEventInternal(QWindow *receiver, MSG msg, bool grab, LRESULT *lResult);
diff --git a/src/plugins/platforms/windows/qwindowsmenu.cpp b/src/plugins/platforms/windows/qwindowsmenu.cpp
index 403ce52cb0..79deeeaea2 100644
--- a/src/plugins/platforms/windows/qwindowsmenu.cpp
+++ b/src/plugins/platforms/windows/qwindowsmenu.cpp
@@ -6,6 +6,7 @@
#include "qwindowswindow.h"
#include <QtGui/qwindow.h>
+#include <QtGui/private/qpixmap_win_p.h>
#include <QtCore/qdebug.h>
#include <QtCore/qvariant.h>
#include <QtCore/qmetaobject.h>
@@ -229,8 +230,6 @@ void QWindowsMenuItem::setIcon(const QIcon &icon)
updateBitmap();
}
-Q_GUI_EXPORT HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &p, int hbitmapFormat = 0);
-
void QWindowsMenuItem::updateBitmap()
{
freeBitmap();
diff --git a/src/plugins/platforms/windows/qwindowsmenu.h b/src/plugins/platforms/windows/qwindowsmenu.h
index 646ab64894..6f66180d82 100644
--- a/src/plugins/platforms/windows/qwindowsmenu.h
+++ b/src/plugins/platforms/windows/qwindowsmenu.h
@@ -9,7 +9,6 @@
#include <qpa/qplatformmenu.h>
#include <QtCore/qlist.h>
-#include <QtCore/qpair.h>
QT_BEGIN_NAMESPACE
diff --git a/src/plugins/platforms/windows/qwindowsmime.cpp b/src/plugins/platforms/windows/qwindowsmimeregistry.cpp
index 8233e82d8e..8d147e8fa0 100644
--- a/src/plugins/platforms/windows/qwindowsmime.cpp
+++ b/src/plugins/platforms/windows/qwindowsmimeregistry.cpp
@@ -1,7 +1,7 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include "qwindowsmime.h"
+#include "qwindowsmimeregistry.h"
#include "qwindowscontext.h"
#include <QtGui/private/qinternalmimedata_p.h>
@@ -154,7 +154,7 @@ static bool qt_write_dibv5(QDataStream &s, QImage image)
return false;
if (image.format() != QImage::Format_ARGB32)
- image = image.convertToFormat(QImage::Format_ARGB32);
+ image = std::move(image).convertToFormat(QImage::Format_ARGB32);
auto *buf = new uchar[bpl_bmp];
@@ -300,7 +300,7 @@ QDebug operator<<(QDebug d, const FORMATETC &tc)
d << "CF_ENHMETAFILE";
break;
default:
- d << QWindowsMimeConverter::clipboardFormatName(tc.cfFormat);
+ d << QWindowsMimeRegistry::clipboardFormatName(tc.cfFormat);
break;
}
d << ", dwAspect=" << tc.dwAspect << ", lindex=" << tc.lindex
@@ -333,106 +333,7 @@ QDebug operator<<(QDebug d, IDataObject *dataObj)
}
#endif // !QT_NO_DEBUG_STREAM
-/*!
- \class QWindowsMime
- \brief The QWindowsMime class maps open-standard MIME to Window Clipboard formats.
- \internal
-
- Qt's drag-and-drop and clipboard facilities use the MIME standard.
- On X11, this maps trivially to the Xdnd protocol, but on Windows
- although some applications use MIME types to describe clipboard
- formats, others use arbitrary non-standardized naming conventions,
- or unnamed built-in formats of Windows.
-
- By instantiating subclasses of QWindowsMime that provide conversions
- between Windows Clipboard and MIME formats, you can convert
- proprietary clipboard formats to MIME formats.
-
- Qt has predefined support for the following Windows Clipboard formats:
-
- \table
- \header \li Windows Format \li Equivalent MIME type
- \row \li \c CF_UNICODETEXT \li \c text/plain
- \row \li \c CF_TEXT \li \c text/plain
- \row \li \c CF_DIB \li \c{image/xyz}, where \c xyz is
- a \l{QImageWriter::supportedImageFormats()}{Qt image format}
- \row \li \c CF_HDROP \li \c text/uri-list
- \row \li \c CF_INETURL \li \c text/uri-list
- \row \li \c CF_HTML \li \c text/html
- \endtable
-
- An example use of this class would be to map the Windows Metafile
- clipboard format (\c CF_METAFILEPICT) to and from the MIME type
- \c{image/x-wmf}. This conversion might simply be adding or removing
- a header, or even just passing on the data. See \l{Drag and Drop}
- for more information on choosing and definition MIME types.
-
- You can check if a MIME type is convertible using canConvertFromMime() and
- can perform conversions with convertToMime() and convertFromMime().
-
- \sa QWindowsMimeConverter
-*/
-
-
-/*!
-\fn bool QWindowsMime::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
-
- Returns \c true if the converter can convert from the \a mimeData to
- the format specified in \a formatetc.
-
- All subclasses must reimplement this pure virtual function.
-*/
-
-/*!
- \fn bool QWindowsMime::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
-
- Returns \c true if the converter can convert to the \a mimeType from
- the available formats in \a pDataObj.
-
- All subclasses must reimplement this pure virtual function.
-*/
-
-/*!
-\fn QString QWindowsMime::mimeForFormat(const FORMATETC &formatetc) const
-
- Returns the mime type that will be created form the format specified
- in \a formatetc, or an empty string if this converter does not support
- \a formatetc.
-
- All subclasses must reimplement this pure virtual function.
-*/
-
-/*!
-\fn QList<FORMATETC> QWindowsMime::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
-
- Returns a QList of FORMATETC structures representing the different windows clipboard
- formats that can be provided for the \a mimeType from the \a mimeData.
-
- All subclasses must reimplement this pure virtual function.
-*/
-
-/*!
- \fn QVariant QWindowsMime::convertToMime(const QString &mimeType, IDataObject *pDataObj,
- QMetaType preferredType) const
-
- Returns a QVariant containing the converted data for \a mimeType from \a pDataObj.
- If possible the QVariant should be of the \a preferredType to avoid needless conversions.
-
- All subclasses must reimplement this pure virtual function.
-*/
-
-/*!
-\fn bool QWindowsMime::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const
-
- Convert the \a mimeData to the format specified in \a formatetc.
- The converted data should then be placed in \a pmedium structure.
-
- Return true if the conversion was successful.
-
- All subclasses must reimplement this pure virtual function.
-*/
-
-class QWindowsMimeText : public QNativeInterface::Private::QWindowsMime
+class QWindowsMimeText : public QWindowsMimeConverter
{
public:
bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const override;
@@ -446,7 +347,7 @@ public:
bool QWindowsMimeText::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
{
int cf = getCf(formatetc);
- return (cf == CF_UNICODETEXT || cf == CF_TEXT) && mimeData->hasText();
+ return (cf == CF_UNICODETEXT || (cf == CF_TEXT && GetACP() != CP_UTF8)) && mimeData->hasText();
}
/*
@@ -537,7 +438,7 @@ QString QWindowsMimeText::mimeForFormat(const FORMATETC &formatetc) const
{
int cf = getCf(formatetc);
if (cf == CF_UNICODETEXT || cf == CF_TEXT)
- return QStringLiteral("text/plain");
+ return u"text/plain"_s;
return QString();
}
@@ -547,7 +448,8 @@ QList<FORMATETC> QWindowsMimeText::formatsForMime(const QString &mimeType, const
QList<FORMATETC> formatics;
if (mimeType.startsWith(u"text/plain") && mimeData->hasText()) {
formatics += setCf(CF_UNICODETEXT);
- formatics += setCf(CF_TEXT);
+ if (GetACP() != CP_UTF8)
+ formatics += setCf(CF_TEXT);
}
return formatics;
}
@@ -588,7 +490,7 @@ QVariant QWindowsMimeText::convertToMime(const QString &mime, LPDATAOBJECT pData
return ret;
}
-class QWindowsMimeURI : public QNativeInterface::Private::QWindowsMime
+class QWindowsMimeURI : public QWindowsMimeConverter
{
public:
QWindowsMimeURI();
@@ -605,8 +507,8 @@ private:
QWindowsMimeURI::QWindowsMimeURI()
{
- CF_INETURL_W = QWindowsMimeConverter::registerMimeType(QStringLiteral("UniformResourceLocatorW"));
- CF_INETURL = QWindowsMimeConverter::registerMimeType(QStringLiteral("UniformResourceLocator"));
+ CF_INETURL_W = registerMimeType(u"UniformResourceLocatorW"_s);
+ CF_INETURL = registerMimeType(u"UniformResourceLocator"_s);
}
bool QWindowsMimeURI::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
@@ -689,7 +591,7 @@ QString QWindowsMimeURI::mimeForFormat(const FORMATETC &formatetc) const
{
QString format;
if (getCf(formatetc) == CF_HDROP || getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL)
- format = QStringLiteral("text/uri-list");
+ format = u"text/uri-list"_s;
return format;
}
@@ -754,7 +656,7 @@ QVariant QWindowsMimeURI::convertToMime(const QString &mimeType, LPDATAOBJECT pD
return QVariant();
}
-class QWindowsMimeHtml : public QNativeInterface::Private::QWindowsMime
+class QWindowsMimeHtml : public QWindowsMimeConverter
{
public:
QWindowsMimeHtml();
@@ -775,7 +677,7 @@ private:
QWindowsMimeHtml::QWindowsMimeHtml()
{
- CF_HTML = QWindowsMimeConverter::registerMimeType(QStringLiteral("HTML Format"));
+ CF_HTML = registerMimeType(u"HTML Format"_s);
}
QList<FORMATETC> QWindowsMimeHtml::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
@@ -789,7 +691,7 @@ QList<FORMATETC> QWindowsMimeHtml::formatsForMime(const QString &mimeType, const
QString QWindowsMimeHtml::mimeForFormat(const FORMATETC &formatetc) const
{
if (getCf(formatetc) == CF_HTML)
- return QStringLiteral("text/html");
+ return u"text/html"_s;
return QString();
}
@@ -892,7 +794,7 @@ bool QWindowsMimeHtml::convertFromMime(const FORMATETC &formatetc, const QMimeDa
#ifndef QT_NO_IMAGEFORMAT_BMP
-class QWindowsMimeImage : public QNativeInterface::Private::QWindowsMime
+class QWindowsMimeImage : public QWindowsMimeConverter
{
public:
QWindowsMimeImage();
@@ -906,6 +808,7 @@ public:
QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QMetaType preferredType) const override;
QString mimeForFormat(const FORMATETC &formatetc) const override;
private:
+ bool hasOriginalDIBV5(IDataObject *pDataObj) const;
UINT CF_PNG;
};
@@ -933,7 +836,7 @@ QString QWindowsMimeImage::mimeForFormat(const FORMATETC &formatetc) const
{
int cf = getCf(formatetc);
if (cf == CF_DIB || cf == CF_DIBV5 || cf == int(CF_PNG))
- return QStringLiteral("application/x-qt-image");
+ return u"application/x-qt-image"_s;
return QString();
}
@@ -967,7 +870,7 @@ bool QWindowsMimeImage::convertFromMime(const FORMATETC &formatetc, const QMimeD
QByteArray ba;
if (cf == CF_DIB) {
if (img.format() > QImage::Format_ARGB32)
- img = img.convertToFormat(QImage::Format_RGB32);
+ img = std::move(img).convertToFormat(QImage::Format_RGB32);
const QByteArray ba = writeDib(img);
if (!ba.isEmpty())
return setData(ba, pmedium);
@@ -980,22 +883,48 @@ bool QWindowsMimeImage::convertFromMime(const FORMATETC &formatetc, const QMimeD
} else {
QDataStream s(&ba, QIODevice::WriteOnly);
s.setByteOrder(QDataStream::LittleEndian);// Intel byte order ####
- if (qt_write_dibv5(s, img))
+ if (qt_write_dibv5(s, std::move(img)))
return setData(ba, pmedium);
}
}
return false;
}
+bool QWindowsMimeImage::hasOriginalDIBV5(IDataObject *pDataObj) const
+{
+ bool isSynthesized = true;
+ IEnumFORMATETC *pEnum = nullptr;
+ HRESULT res = pDataObj->EnumFormatEtc(1, &pEnum);
+ if (res == S_OK && pEnum) {
+ FORMATETC fc;
+ while ((res = pEnum->Next(1, &fc, nullptr)) == S_OK) {
+ if (fc.ptd)
+ CoTaskMemFree(fc.ptd);
+ if (fc.cfFormat == CF_DIB)
+ break;
+ if (fc.cfFormat == CF_DIBV5) {
+ isSynthesized = false;
+ break;
+ }
+ }
+ pEnum->Release();
+ }
+ return !isSynthesized;
+}
+
QVariant QWindowsMimeImage::convertToMime(const QString &mimeType, IDataObject *pDataObj, QMetaType preferredType) const
{
Q_UNUSED(preferredType);
QVariant result;
if (mimeType != u"application/x-qt-image")
return result;
- //Try to convert from DIBV5 as it is the most
- //widespread format that support transparency
- if (canGetData(CF_DIBV5, pDataObj)) {
+ // Try to convert from DIBV5 as it is the most widespread format that supports transparency,
+ // but avoid synthesizing it, as that typically loses transparency, e.g. from Office
+ const bool canGetDibV5 = canGetData(CF_DIBV5, pDataObj);
+ const bool hasOrigDibV5 = canGetDibV5 ? hasOriginalDIBV5(pDataObj) : false;
+ qCDebug(lcQpaMime) << "canGetDibV5:" << canGetDibV5 << "hasOrigDibV5:" << hasOrigDibV5;
+ if (hasOrigDibV5) {
+ qCDebug(lcQpaMime) << "Decoding DIBV5";
QImage img;
QByteArray data = getData(CF_DIBV5, pDataObj);
QBuffer buffer(&data);
@@ -1004,6 +933,7 @@ QVariant QWindowsMimeImage::convertToMime(const QString &mimeType, IDataObject *
}
//PNG, MS Office place this (undocumented)
if (canGetData(CF_PNG, pDataObj)) {
+ qCDebug(lcQpaMime) << "Decoding PNG";
QImage img;
QByteArray data = getData(CF_PNG, pDataObj);
if (img.loadFromData(data, "PNG")) {
@@ -1012,6 +942,7 @@ QVariant QWindowsMimeImage::convertToMime(const QString &mimeType, IDataObject *
}
//Fallback to DIB
if (canGetData(CF_DIB, pDataObj)) {
+ qCDebug(lcQpaMime) << "Decoding DIB";
QImage img;
QByteArray data = getData(CF_DIBV5, pDataObj);
QBuffer buffer(&data);
@@ -1023,7 +954,7 @@ QVariant QWindowsMimeImage::convertToMime(const QString &mimeType, IDataObject *
}
#endif
-class QBuiltInMimes : public QNativeInterface::Private::QWindowsMime
+class QBuiltInMimes : public QWindowsMimeConverter
{
public:
QBuiltInMimes();
@@ -1044,10 +975,10 @@ private:
};
QBuiltInMimes::QBuiltInMimes()
-: QWindowsMime()
+: QWindowsMimeConverter()
{
- outFormats.insert(QWindowsMimeConverter::registerMimeType(QStringLiteral("application/x-color")), QStringLiteral("application/x-color"));
- inFormats.insert(QWindowsMimeConverter::registerMimeType(QStringLiteral("application/x-color")), QStringLiteral("application/x-color"));
+ outFormats.insert(registerMimeType(u"application/x-color"_s), u"application/x-color"_s);
+ inFormats.insert(registerMimeType(u"application/x-color"_s), u"application/x-color"_s);
}
bool QBuiltInMimes::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
@@ -1144,7 +1075,7 @@ QString QBuiltInMimes::mimeForFormat(const FORMATETC &formatetc) const
}
-class QLastResortMimes : public QNativeInterface::Private::QWindowsMime
+class QLastResortMimes : public QWindowsMimeConverter
{
public:
@@ -1172,25 +1103,25 @@ QLastResortMimes::QLastResortMimes()
{
//MIME Media-Types
if (ianaTypes.isEmpty()) {
- ianaTypes.append(QStringLiteral("application/"));
- ianaTypes.append(QStringLiteral("audio/"));
- ianaTypes.append(QStringLiteral("example/"));
- ianaTypes.append(QStringLiteral("image/"));
- ianaTypes.append(QStringLiteral("message/"));
- ianaTypes.append(QStringLiteral("model/"));
- ianaTypes.append(QStringLiteral("multipart/"));
- ianaTypes.append(QStringLiteral("text/"));
- ianaTypes.append(QStringLiteral("video/"));
+ ianaTypes.append(u"application/"_s);
+ ianaTypes.append(u"audio/"_s);
+ ianaTypes.append(u"example/"_s);
+ ianaTypes.append(u"image/"_s);
+ ianaTypes.append(u"message/"_s);
+ ianaTypes.append(u"model/"_s);
+ ianaTypes.append(u"multipart/"_s);
+ ianaTypes.append(u"text/"_s);
+ ianaTypes.append(u"video/"_s);
}
//Types handled by other classes
if (excludeList.isEmpty()) {
- excludeList.append(QStringLiteral("HTML Format"));
- excludeList.append(QStringLiteral("UniformResourceLocator"));
- excludeList.append(QStringLiteral("text/html"));
- excludeList.append(QStringLiteral("text/plain"));
- excludeList.append(QStringLiteral("text/uri-list"));
- excludeList.append(QStringLiteral("application/x-qt-image"));
- excludeList.append(QStringLiteral("application/x-color"));
+ excludeList.append(u"HTML Format"_s);
+ excludeList.append(u"UniformResourceLocator"_s);
+ excludeList.append(u"text/html"_s);
+ excludeList.append(u"text/plain"_s);
+ excludeList.append(u"text/uri-list"_s);
+ excludeList.append(u"application/x-qt-image"_s);
+ excludeList.append(u"application/x-color"_s);
}
}
@@ -1228,7 +1159,7 @@ QList<FORMATETC> QLastResortMimes::formatsForMime(const QString &mimeType, const
auto mit = std::find(formats.begin(), formats.end(), mimeType);
// register any other available formats
if (mit == formats.end() && !excludeList.contains(mimeType, Qt::CaseInsensitive))
- mit = formats.insert(QWindowsMimeConverter::registerMimeType(mimeType), mimeType);
+ mit = formats.insert(registerMimeType(mimeType), mimeType);
if (mit != formats.end())
formatetcs += setCf(mit.key());
@@ -1272,7 +1203,7 @@ bool QLastResortMimes::canConvertToMime(const QString &mimeType, IDataObject *pD
}
// if it is not in there then register it and see if we can get it
const auto mit = std::find(formats.cbegin(), formats.cend(), mimeType);
- const int cf = mit != formats.cend() ? mit.key() : QWindowsMimeConverter::registerMimeType(mimeType);
+ const int cf = mit != formats.cend() ? mit.key() : registerMimeType(mimeType);
return canGetData(cf, pDataObj);
}
@@ -1289,7 +1220,7 @@ QVariant QLastResortMimes::convertToMime(const QString &mimeType, IDataObject *p
data = getData(int(cf), pDataObj, lindex);
} else {
const auto mit = std::find(formats.cbegin(), formats.cend(), mimeType);
- const int cf = mit != formats.cend() ? mit.key() : QWindowsMimeConverter::registerMimeType(mimeType);
+ const int cf = mit != formats.cend() ? mit.key() : registerMimeType(mimeType);
data = getData(cf, pDataObj);
}
if (!data.isEmpty())
@@ -1304,7 +1235,7 @@ QString QLastResortMimes::mimeForFormat(const FORMATETC &formatetc) const
if (!format.isEmpty())
return format;
- const QString clipFormat = QWindowsMimeConverter::clipboardFormatName(getCf(formatetc));
+ const QString clipFormat = QWindowsMimeRegistry::clipboardFormatName(getCf(formatetc));
if (!clipFormat.isEmpty()) {
#if QT_CONFIG(draganddrop)
if (QInternalMimeData::canReadData(clipFormat))
@@ -1334,20 +1265,20 @@ QString QLastResortMimes::mimeForFormat(const FORMATETC &formatetc) const
}
/*!
- \class QWindowsMimeConverter
- \brief Manages the list of QWindowsMime instances.
+ \class QWindowsMimeRegistry
+ \brief Manages the list of QWindowsMimeConverter instances.
\internal
- \sa QWindowsMime
+ \sa QWindowsMimeConverter
*/
-QWindowsMimeConverter::QWindowsMimeConverter() = default;
+QWindowsMimeRegistry::QWindowsMimeRegistry() = default;
-QWindowsMimeConverter::~QWindowsMimeConverter()
+QWindowsMimeRegistry::~QWindowsMimeRegistry()
{
qDeleteAll(m_mimes.begin(), m_mimes.begin() + m_internalMimeCount);
}
-QWindowsMimeConverter::QWindowsMime *QWindowsMimeConverter::converterToMime(const QString &mimeType, IDataObject *pDataObj) const
+QWindowsMimeRegistry::QWindowsMimeConverter *QWindowsMimeRegistry::converterToMime(const QString &mimeType, IDataObject *pDataObj) const
{
ensureInitialized();
for (int i = m_mimes.size()-1; i >= 0; --i) {
@@ -1357,9 +1288,9 @@ QWindowsMimeConverter::QWindowsMime *QWindowsMimeConverter::converterToMime(cons
return nullptr;
}
-QStringList QWindowsMimeConverter::allMimesForFormats(IDataObject *pDataObj) const
+QStringList QWindowsMimeRegistry::allMimesForFormats(IDataObject *pDataObj) const
{
- qCDebug(lcQpaMime) << "QWindowsMime::allMimesForFormats()";
+ qCDebug(lcQpaMime) << "QWindowsMimeConverter::allMimesForFormats()";
ensureInitialized();
QStringList formats;
LPENUMFORMATETC FAR fmtenum;
@@ -1386,7 +1317,7 @@ QStringList QWindowsMimeConverter::allMimesForFormats(IDataObject *pDataObj) con
return formats;
}
-QWindowsMimeConverter::QWindowsMime *QWindowsMimeConverter::converterFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
+QWindowsMimeRegistry::QWindowsMimeConverter *QWindowsMimeRegistry::converterFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const
{
ensureInitialized();
qCDebug(lcQpaMime) << __FUNCTION__ << formatetc;
@@ -1397,7 +1328,7 @@ QWindowsMimeConverter::QWindowsMime *QWindowsMimeConverter::converterFromMime(co
return nullptr;
}
-QList<FORMATETC> QWindowsMimeConverter::allFormatsForMime(const QMimeData *mimeData) const
+QList<FORMATETC> QWindowsMimeRegistry::allFormatsForMime(const QMimeData *mimeData) const
{
ensureInitialized();
QList<FORMATETC> formatics;
@@ -1414,34 +1345,37 @@ QList<FORMATETC> QWindowsMimeConverter::allFormatsForMime(const QMimeData *mimeD
return formatics;
}
-void QWindowsMimeConverter::ensureInitialized() const
+void QWindowsMimeRegistry::ensureInitialized() const
{
- if (m_mimes.isEmpty()) {
- m_mimes
+ if (m_internalMimeCount == 0) {
+ m_internalMimeCount = -1; // prevent reentrancy when types register themselves
#ifndef QT_NO_IMAGEFORMAT_BMP
- << new QWindowsMimeImage
+ (void)new QWindowsMimeImage;
#endif //QT_NO_IMAGEFORMAT_BMP
- << new QLastResortMimes
- << new QWindowsMimeText << new QWindowsMimeURI
- << new QWindowsMimeHtml << new QBuiltInMimes;
+ (void)new QLastResortMimes;
+ (void)new QWindowsMimeText;
+ (void)new QWindowsMimeURI;
+ (void)new QWindowsMimeHtml;
+ (void)new QBuiltInMimes;
m_internalMimeCount = m_mimes.size();
+ Q_ASSERT(m_internalMimeCount > 0);
}
}
-QString QWindowsMimeConverter::clipboardFormatName(int cf)
+QString QWindowsMimeRegistry::clipboardFormatName(int cf)
{
wchar_t buf[256] = {0};
return GetClipboardFormatName(UINT(cf), buf, 255)
? QString::fromWCharArray(buf) : QString();
}
-QVariant QWindowsMimeConverter::convertToMime(const QStringList &mimeTypes,
+QVariant QWindowsMimeRegistry::convertToMime(const QStringList &mimeTypes,
IDataObject *pDataObj,
QMetaType preferredType,
QString *formatIn /* = 0 */) const
{
for (const QString &format : mimeTypes) {
- if (const QWindowsMime *converter = converterToMime(format, pDataObj)) {
+ if (const QWindowsMimeConverter *converter = converterToMime(format, pDataObj)) {
if (converter->canConvertToMime(format, pDataObj)) {
const QVariant dataV = converter->convertToMime(format, pDataObj, preferredType);
if (dataV.isValid()) {
@@ -1458,7 +1392,7 @@ QVariant QWindowsMimeConverter::convertToMime(const QStringList &mimeTypes,
return QVariant();
}
-void QWindowsMimeConverter::registerMime(QWindowsMime *mime)
+void QWindowsMimeRegistry::registerMime(QWindowsMimeConverter *mime)
{
ensureInitialized();
m_mimes.append(mime);
@@ -1467,12 +1401,18 @@ void QWindowsMimeConverter::registerMime(QWindowsMime *mime)
/*!
Registers the MIME type \a mime, and returns an ID number
identifying the format on Windows.
+
+ A mime type \c {application/x-qt-windows-mime;value="WindowsType"} will be
+ registered as the clipboard format for \c WindowsType.
*/
-int QWindowsMimeConverter::registerMimeType(const QString &mime)
+int QWindowsMimeRegistry::registerMimeType(const QString &mime)
{
- const UINT f = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (mime.utf16()));
- if (!f)
- qErrnoWarning("QWindowsApplication::registerMimeType: Failed to register clipboard format");
+ const QString mimeType = isCustomMimeType(mime) ? customMimeType(mime) : mime;
+ const UINT f = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (mimeType.utf16()));
+ if (!f) {
+ qErrnoWarning("QWindowsMimeRegistry::registerMimeType: Failed to register clipboard format "
+ "for %s", qPrintable(mime));
+ }
return int(f);
}
diff --git a/src/plugins/platforms/windows/qwindowsmime.h b/src/plugins/platforms/windows/qwindowsmimeregistry.h
index 1f30e991d9..a0f4b4c60a 100644
--- a/src/plugins/platforms/windows/qwindowsmime.h
+++ b/src/plugins/platforms/windows/qwindowsmimeregistry.h
@@ -1,13 +1,13 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#ifndef QWINDOWSMIME_H
-#define QWINDOWSMIME_H
+#ifndef QWINDOWSMIMEREGISTRY_H
+#define QWINDOWSMIMEREGISTRY_H
-#include <QtGui/private/qwindowsmime_p.h>
#include <QtCore/qt_windows.h>
+#include <QtGui/qwindowsmimeconverter.h>
#include <QtCore/qlist.h>
#include <QtCore/qvariant.h>
@@ -16,26 +16,26 @@ QT_BEGIN_NAMESPACE
class QDebug;
class QMimeData;
-class QWindowsMimeConverter
+class QWindowsMimeRegistry
{
- Q_DISABLE_COPY_MOVE(QWindowsMimeConverter)
+ Q_DISABLE_COPY_MOVE(QWindowsMimeRegistry)
public:
- using QWindowsMime = QNativeInterface::Private::QWindowsMime;
+ using QWindowsMimeConverter = QWindowsMimeConverter;
- QWindowsMimeConverter();
- ~QWindowsMimeConverter();
+ QWindowsMimeRegistry();
+ ~QWindowsMimeRegistry();
- QWindowsMime *converterToMime(const QString &mimeType, IDataObject *pDataObj) const;
+ QWindowsMimeConverter *converterToMime(const QString &mimeType, IDataObject *pDataObj) const;
QStringList allMimesForFormats(IDataObject *pDataObj) const;
- QWindowsMime *converterFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const;
+ QWindowsMimeConverter *converterFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const;
QList<FORMATETC> allFormatsForMime(const QMimeData *mimeData) const;
// Convenience.
QVariant convertToMime(const QStringList &mimeTypes, IDataObject *pDataObj, QMetaType preferredType,
QString *format = nullptr) const;
- void registerMime(QWindowsMime *mime);
- void unregisterMime(QWindowsMime *mime) { m_mimes.removeOne(mime); }
+ void registerMime(QWindowsMimeConverter *mime);
+ void unregisterMime(QWindowsMimeConverter *mime) { m_mimes.removeOne(mime); }
static int registerMimeType(const QString &mime);
@@ -44,7 +44,7 @@ public:
private:
void ensureInitialized() const;
- mutable QList<QWindowsMime *> m_mimes;
+ mutable QList<QWindowsMimeConverter *> m_mimes;
mutable int m_internalMimeCount = 0;
};
@@ -55,4 +55,4 @@ QDebug operator<<(QDebug d, IDataObject *);
QT_END_NAMESPACE
-#endif // QWINDOWSMIME_H
+#endif // QWINDOWSMIMEREGISTRY_H
diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp
index 6918f523ce..9af9fba408 100644
--- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp
+++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp
@@ -16,7 +16,8 @@
#include <QtGui/qcursor.h>
#include <QtCore/qdebug.h>
-#include <QtCore/qscopedpointer.h>
+
+#include <memory>
#include <windowsx.h>
@@ -123,7 +124,7 @@ Qt::MouseButtons QWindowsMouseHandler::queryMouseButtons()
return result;
}
-static QPoint lastMouseMovePos;
+Q_CONSTINIT static QPoint lastMouseMovePos;
namespace {
struct MouseEvent {
@@ -267,7 +268,8 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
}
}
- const Qt::KeyboardModifiers keyModifiers = QWindowsKeyMapper::queryKeyboardModifiers();
+ const auto *keyMapper = QWindowsContext::instance()->keyMapper();
+ const Qt::KeyboardModifiers keyModifiers = keyMapper->queryKeyboardModifiers();
const MouseEvent mouseEvent = eventFromMsg(msg);
Qt::MouseButtons buttons;
@@ -285,19 +287,16 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
if (m_lastEventType == QEvent::NonClientAreaMouseButtonPress
&& (mouseEvent.type == QEvent::NonClientAreaMouseMove || mouseEvent.type == QEvent::MouseMove)
&& (m_lastEventButton & buttons) == 0) {
- if (mouseEvent.type == QEvent::NonClientAreaMouseMove) {
- QWindowSystemInterface::handleFrameStrutMouseEvent(window, device, clientPosition, globalPosition, buttons, m_lastEventButton,
- QEvent::NonClientAreaMouseButtonRelease, keyModifiers, source);
- } else {
- QWindowSystemInterface::handleMouseEvent(window, device, clientPosition, globalPosition, buttons, m_lastEventButton,
- QEvent::MouseButtonRelease, keyModifiers, source);
- }
+ auto releaseType = mouseEvent.type == QEvent::NonClientAreaMouseMove ?
+ QEvent::NonClientAreaMouseButtonRelease : QEvent::MouseButtonRelease;
+ QWindowSystemInterface::handleMouseEvent(window, msg.time, device, clientPosition, globalPosition, buttons, m_lastEventButton,
+ releaseType, keyModifiers, source);
}
m_lastEventType = mouseEvent.type;
m_lastEventButton = mouseEvent.button;
if (mouseEvent.type >= QEvent::NonClientAreaMouseMove && mouseEvent.type <= QEvent::NonClientAreaMouseButtonDblClick) {
- QWindowSystemInterface::handleFrameStrutMouseEvent(window, device, clientPosition,
+ QWindowSystemInterface::handleMouseEvent(window, msg.time, device, clientPosition,
globalPosition, buttons,
mouseEvent.button, mouseEvent.type,
keyModifiers, source);
@@ -447,7 +446,7 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
}
if (!discardEvent && mouseEvent.type != QEvent::None) {
- QWindowSystemInterface::handleMouseEvent(window, device, clientPosition, globalPosition, buttons,
+ QWindowSystemInterface::handleMouseEvent(window, msg.time, device, clientPosition, globalPosition, buttons,
mouseEvent.button, mouseEvent.type,
keyModifiers, source);
}
@@ -471,7 +470,7 @@ static bool isValidWheelReceiver(QWindow *candidate)
return false;
}
-static void redirectWheelEvent(QWindow *window, const QPoint &globalPos, int delta,
+static void redirectWheelEvent(QWindow *window, unsigned long timestamp, const QPoint &globalPos, int delta,
Qt::Orientation orientation, Qt::KeyboardModifiers mods)
{
// Redirect wheel event to one of the following, in order of preference:
@@ -492,6 +491,7 @@ static void redirectWheelEvent(QWindow *window, const QPoint &globalPos, int del
if (handleEvent) {
const QPoint point = (orientation == Qt::Vertical) ? QPoint(0, delta) : QPoint(delta, 0);
QWindowSystemInterface::handleWheelEvent(receiver,
+ timestamp,
QWindowsGeometryHint::mapFromGlobal(receiver, globalPos),
globalPos, QPoint(), point, mods);
}
@@ -520,7 +520,7 @@ bool QWindowsMouseHandler::translateMouseWheelEvent(QWindow *window, HWND,
delta = -delta;
const QPoint globalPos(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam));
- redirectWheelEvent(window, globalPos, delta, orientation, mods);
+ redirectWheelEvent(window, msg.time, globalPos, delta, orientation, mods);
return true;
}
@@ -551,7 +551,7 @@ bool QWindowsMouseHandler::translateScrollEvent(QWindow *window, HWND,
return false;
}
- redirectWheelEvent(window, QCursor::pos(), delta, Qt::Horizontal, Qt::NoModifier);
+ redirectWheelEvent(window, msg.time, QCursor::pos(), delta, Qt::Horizontal, Qt::NoModifier);
return true;
}
@@ -577,15 +577,14 @@ bool QWindowsMouseHandler::translateTouchEvent(QWindow *window, HWND,
const QRect screenGeometry = screen->geometry();
const int winTouchPointCount = int(msg.wParam);
- QScopedArrayPointer<TOUCHINPUT> winTouchInputs(new TOUCHINPUT[winTouchPointCount]);
- memset(winTouchInputs.data(), 0, sizeof(TOUCHINPUT) * size_t(winTouchPointCount));
+ const auto winTouchInputs = std::make_unique<TOUCHINPUT[]>(winTouchPointCount);
QTouchPointList touchPoints;
touchPoints.reserve(winTouchPointCount);
QEventPoint::States allStates;
GetTouchInputInfo(reinterpret_cast<HTOUCHINPUT>(msg.lParam),
- UINT(msg.wParam), winTouchInputs.data(), sizeof(TOUCHINPUT));
+ UINT(msg.wParam), winTouchInputs.get(), sizeof(TOUCHINPUT));
for (int i = 0; i < winTouchPointCount; ++i) {
const TOUCHINPUT &winTouchInput = winTouchInputs[i];
int id = m_touchInputIDToTouchPointID.value(winTouchInput.dwID, -1);
@@ -632,10 +631,12 @@ bool QWindowsMouseHandler::translateTouchEvent(QWindow *window, HWND,
if (allStates == QEventPoint::State::Released)
m_touchInputIDToTouchPointID.clear();
+ const auto *keyMapper = QWindowsContext::instance()->keyMapper();
QWindowSystemInterface::handleTouchEvent(window,
+ msg.time,
m_touchDevice.data(),
touchPoints,
- QWindowsKeyMapper::queryKeyboardModifiers());
+ keyMapper->queryKeyboardModifiers());
return true;
}
diff --git a/src/plugins/platforms/windows/qwindowsole.cpp b/src/plugins/platforms/windows/qwindowsole.cpp
index 31b851ce17..c01f716054 100644
--- a/src/plugins/platforms/windows/qwindowsole.cpp
+++ b/src/plugins/platforms/windows/qwindowsole.cpp
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwindowsole.h"
-#include "qwindowsmime.h"
+#include "qwindowsmimeregistry.h"
#include "qwindowscontext.h"
\
#include <QtGui/qevent.h>
@@ -66,7 +66,7 @@ QWindowsOleDataObject::GetData(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium)
HRESULT hr = ResultFromScode(DATA_E_FORMATETC);
if (data) {
- const QWindowsMimeConverter &mc = QWindowsContext::instance()->mimeConverter();
+ const QWindowsMimeRegistry &mc = QWindowsContext::instance()->mimeConverter();
if (auto converter = mc.converterFromMime(*pformatetc, data))
if (converter->convertFromMime(*pformatetc, data, pmedium))
hr = ResultFromScode(S_OK);
@@ -93,7 +93,7 @@ QWindowsOleDataObject::QueryGetData(LPFORMATETC pformatetc)
qCDebug(lcQpaMime) << __FUNCTION__;
if (data) {
- const QWindowsMimeConverter &mc = QWindowsContext::instance()->mimeConverter();
+ const QWindowsMimeRegistry &mc = QWindowsContext::instance()->mimeConverter();
hr = mc.converterFromMime(*pformatetc, data) ?
ResultFromScode(S_OK) : ResultFromScode(S_FALSE);
}
@@ -144,7 +144,7 @@ QWindowsOleDataObject::EnumFormatEtc(DWORD dwDirection, LPENUMFORMATETC FAR* ppe
QList<FORMATETC> fmtetcs;
if (dwDirection == DATADIR_GET) {
- QWindowsMimeConverter &mc = QWindowsContext::instance()->mimeConverter();
+ QWindowsMimeRegistry &mc = QWindowsContext::instance()->mimeConverter();
fmtetcs = mc.allFormatsForMime(data);
} else {
FORMATETC formatetc;
diff --git a/src/plugins/platforms/windows/qwindowsole.h b/src/plugins/platforms/windows/qwindowsole.h
index b687384684..016f9dd04c 100644
--- a/src/plugins/platforms/windows/qwindowsole.h
+++ b/src/plugins/platforms/windows/qwindowsole.h
@@ -4,12 +4,12 @@
#ifndef QWINDOWSOLE_H
#define QWINDOWSOLE_H
-#include "qwindowscombase.h"
#include <QtCore/qt_windows.h>
#include <QtCore/qlist.h>
#include <QtCore/qmap.h>
#include <QtCore/qpointer.h>
+#include <QtCore/private/qcomobject_p.h>
#include <objidl.h>
@@ -18,7 +18,7 @@ QT_BEGIN_NAMESPACE
class QMimeData;
class QWindow;
-class QWindowsOleDataObject : public QWindowsComBase<IDataObject>
+class QWindowsOleDataObject : public QComObject<IDataObject>
{
public:
explicit QWindowsOleDataObject(QMimeData *mimeData);
@@ -47,7 +47,7 @@ private:
DWORD performedEffect = DROPEFFECT_NONE;
};
-class QWindowsOleEnumFmtEtc : public QWindowsComBase<IEnumFORMATETC>
+class QWindowsOleEnumFmtEtc : public QComObject<IEnumFORMATETC>
{
public:
explicit QWindowsOleEnumFmtEtc(const QList<FORMATETC> &fmtetcs);
diff --git a/src/plugins/platforms/windows/qwindowsopengltester.cpp b/src/plugins/platforms/windows/qwindowsopengltester.cpp
index 19b60a4542..6a790bcc1b 100644
--- a/src/plugins/platforms/windows/qwindowsopengltester.cpp
+++ b/src/plugins/platforms/windows/qwindowsopengltester.cpp
@@ -15,6 +15,7 @@
#include <QtCore/qlibraryinfo.h>
#include <QtCore/qhash.h>
#include <private/qsystemlibrary_p.h>
+#include <QtGui/qtgui-config.h>
#ifndef QT_NO_OPENGL
#include <private/qopengl_p.h>
diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.cpp b/src/plugins/platforms/windows/qwindowspointerhandler.cpp
index 943eaa2b0a..71c7217671 100644
--- a/src/plugins/platforms/windows/qwindowspointerhandler.cpp
+++ b/src/plugins/platforms/windows/qwindowspointerhandler.cpp
@@ -1,9 +1,7 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#ifndef WINVER
-# define WINVER 0x0A00 // Enable pointer functions for MinGW
-#endif
+#include <QtCore/qt_windows.h>
#include "qwindowspointerhandler.h"
#include "qwindowsmousehandler.h"
@@ -430,8 +428,9 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
return false;
if (msg.message == WM_POINTERCAPTURECHANGED) {
- QWindowSystemInterface::handleTouchCancelEvent(window, m_touchDevice.data(),
- QWindowsKeyMapper::queryKeyboardModifiers());
+ const auto *keyMapper = QWindowsContext::instance()->keyMapper();
+ QWindowSystemInterface::handleTouchCancelEvent(window, msg.time, m_touchDevice.data(),
+ keyMapper->queryKeyboardModifiers());
m_lastTouchPoints.clear();
return true;
}
@@ -443,6 +442,8 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
if (id != -1)
m_lastTouchPoints.remove(id);
}
+ // Send LeaveEvent to reset hover when the last finger leaves the touch screen (QTBUG-62912)
+ QWindowSystemInterface::handleEnterLeaveEvent(nullptr, window);
}
// Only handle down/up/update, ignore others like WM_POINTERENTER, WM_POINTERLEAVE, etc.
@@ -524,7 +525,7 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
// Some devices send touches for each finger in a different message/frame, instead of consolidating
// them in the same frame as we were expecting. We account for missing unreleased touches here.
- for (auto tp : qAsConst(m_lastTouchPoints)) {
+ for (auto tp : std::as_const(m_lastTouchPoints)) {
if (!inputIds.contains(tp.id)) {
tp.state = QEventPoint::State::Stationary;
allStates |= tp.state;
@@ -539,8 +540,9 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
if (allStates == QEventPoint::State::Released)
m_touchInputIDToTouchPointID.clear();
- QWindowSystemInterface::handleTouchEvent(window, m_touchDevice.data(), touchPoints,
- QWindowsKeyMapper::queryKeyboardModifiers());
+ const auto *keyMapper = QWindowsContext::instance()->keyMapper();
+ QWindowSystemInterface::handleTouchEvent(window, msg.time, m_touchDevice.data(), touchPoints,
+ keyMapper->queryKeyboardModifiers());
return false; // Allow mouse messages to be generated.
}
@@ -637,7 +639,7 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin
switch (msg.message) {
case WM_POINTERENTER: {
- QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(window, device.data(), true);
+ QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(window, msg.time, device.data(), true);
m_windowUnderPointer = window;
// The local coordinates may fall outside the window.
// Wait until the next update to send the enter event.
@@ -650,7 +652,7 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin
m_windowUnderPointer = nullptr;
m_currentWindow = nullptr;
}
- QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(window, device.data(), false);
+ QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(window, msg.time, device.data(), false);
break;
case WM_POINTERDOWN:
case WM_POINTERUP:
@@ -673,9 +675,10 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin
wumPlatformWindow->applyCursor();
}
}
- const Qt::KeyboardModifiers keyModifiers = QWindowsKeyMapper::queryKeyboardModifiers();
+ const auto *keyMapper = QWindowsContext::instance()->keyMapper();
+ const Qt::KeyboardModifiers keyModifiers = keyMapper->queryKeyboardModifiers();
- QWindowSystemInterface::handleTabletEvent(target, device.data(),
+ QWindowSystemInterface::handleTabletEvent(target, msg.time, device.data(),
localPos, hiResGlobalPos, mouseButtons,
pressure, xTilt, yTilt, tangentialPressure,
rotation, z, keyModifiers);
@@ -726,7 +729,7 @@ bool QWindowsPointerHandler::translateMouseWheelEvent(QWindow *window,
QPoint localPos = QWindowsGeometryHint::mapFromGlobal(receiver, globalPos);
- QWindowSystemInterface::handleWheelEvent(receiver, localPos, globalPos, QPoint(), angleDelta, keyModifiers);
+ QWindowSystemInterface::handleWheelEvent(receiver, msg.time, localPos, globalPos, QPoint(), angleDelta, keyModifiers);
return true;
}
@@ -739,20 +742,20 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window,
{
*result = 0;
- QPoint eventPos(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam));
- if ((et & QtWindows::NonClientEventFlag) == 0 && QWindowsBaseWindow::isRtlLayout(hwnd)) {
- RECT clientArea;
- GetClientRect(hwnd, &clientArea);
- eventPos.setX(clientArea.right - eventPos.x());
- }
-
QPoint localPos;
QPoint globalPos;
+ QPoint eventPos(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam));
if ((et == QtWindows::MouseWheelEvent) || (et & QtWindows::NonClientEventFlag)) {
globalPos = eventPos;
localPos = QWindowsGeometryHint::mapFromGlobal(hwnd, eventPos);
} else {
+ if (QWindowsBaseWindow::isRtlLayout(hwnd)) {
+ RECT clientArea;
+ GetClientRect(hwnd, &clientArea);
+ eventPos.setX(clientArea.right - eventPos.x());
+ }
+
globalPos = QWindowsGeometryHint::mapToGlobal(hwnd, eventPos);
auto targetHwnd = hwnd;
if (auto *pw = window->handle())
@@ -762,7 +765,8 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window,
: QWindowsGeometryHint::mapFromGlobal(targetHwnd, globalPos);
}
- const Qt::KeyboardModifiers keyModifiers = QWindowsKeyMapper::queryKeyboardModifiers();
+ const auto *keyMapper = QWindowsContext::instance()->keyMapper();
+ const Qt::KeyboardModifiers keyModifiers = keyMapper->queryKeyboardModifiers();
QWindow *currentWindowUnderPointer = getWindowUnderPointer(window, globalPos);
if (et == QtWindows::MouseWheelEvent)
@@ -774,7 +778,7 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window,
// X11 and macOS.
bool discardEvent = false;
if (msg.message == WM_MOUSEMOVE) {
- static QPoint lastMouseMovePos;
+ Q_CONSTINIT static QPoint lastMouseMovePos;
if (msg.wParam == 0 && (m_windowUnderPointer.isNull() || globalPos == lastMouseMovePos))
discardEvent = true;
lastMouseMovePos = globalPos;
@@ -798,8 +802,8 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window,
break;
case QT_PT_PEN:
#if QT_CONFIG(tabletevent)
- if (!m_activeTabletDevice.isNull())
- device = m_activeTabletDevice.data();
+ qCDebug(lcQpaTablet) << "ignoring synth-mouse event for tablet event from" << device;
+ return false;
#endif
break;
}
@@ -822,19 +826,16 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window,
if (m_lastEventType == QEvent::NonClientAreaMouseButtonPress
&& (mouseEvent.type == QEvent::NonClientAreaMouseMove || mouseEvent.type == QEvent::MouseMove)
&& (m_lastEventButton & mouseButtons) == 0) {
- if (mouseEvent.type == QEvent::NonClientAreaMouseMove) {
- QWindowSystemInterface::handleFrameStrutMouseEvent(window, device, localPos, globalPos, mouseButtons, m_lastEventButton,
- QEvent::NonClientAreaMouseButtonRelease, keyModifiers, source);
- } else {
- QWindowSystemInterface::handleMouseEvent(window, device, localPos, globalPos, mouseButtons, m_lastEventButton,
- QEvent::MouseButtonRelease, keyModifiers, source);
- }
+ auto releaseType = mouseEvent.type == QEvent::NonClientAreaMouseMove ?
+ QEvent::NonClientAreaMouseButtonRelease : QEvent::MouseButtonRelease;
+ QWindowSystemInterface::handleMouseEvent(window, msg.time, device, localPos, globalPos, mouseButtons, m_lastEventButton,
+ releaseType, keyModifiers, source);
}
m_lastEventType = mouseEvent.type;
m_lastEventButton = mouseEvent.button;
if (mouseEvent.type >= QEvent::NonClientAreaMouseMove && mouseEvent.type <= QEvent::NonClientAreaMouseButtonDblClick) {
- QWindowSystemInterface::handleFrameStrutMouseEvent(window, device, localPos, globalPos, mouseButtons,
+ QWindowSystemInterface::handleMouseEvent(window, msg.time, device, localPos, globalPos, mouseButtons,
mouseEvent.button, mouseEvent.type, keyModifiers, source);
return false; // Allow further event processing
}
@@ -854,7 +855,7 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window,
handleEnterLeave(window, currentWindowUnderPointer, globalPos);
if (!discardEvent && mouseEvent.type != QEvent::None) {
- QWindowSystemInterface::handleMouseEvent(window, device, localPos, globalPos, mouseButtons,
+ QWindowSystemInterface::handleMouseEvent(window, msg.time, device, localPos, globalPos, mouseButtons,
mouseEvent.button, mouseEvent.type, keyModifiers, source);
}
diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp
index 6f0b949b67..a50f9fd4b0 100644
--- a/src/plugins/platforms/windows/qwindowsscreen.cpp
+++ b/src/plugins/platforms/windows/qwindowsscreen.cpp
@@ -14,12 +14,22 @@
#include <QtGui/qpixmap.h>
#include <QtGui/qguiapplication.h>
#include <qpa/qwindowsysteminterface.h>
+#include <QtCore/private/qsystemerror_p.h>
+#include <QtGui/private/qedidparser_p.h>
#include <private/qhighdpiscaling_p.h>
#include <private/qwindowsfontdatabasebase_p.h>
+#include <private/qpixmap_win_p.h>
+#include <private/quniquehandle_p.h>
+
#include <QtGui/qscreen.h>
#include <QtCore/qdebug.h>
+#include <memory>
+#include <type_traits>
+
+#include <cfgmgr32.h>
+#include <setupapi.h>
#include <shellscalingapi.h>
QT_BEGIN_NAMESPACE
@@ -40,7 +50,7 @@ static inline QDpi monitorDPI(HMONITOR hMonitor)
return {0, 0};
}
-static bool getPathInfo(const MONITORINFOEX &viewInfo, DISPLAYCONFIG_PATH_INFO *pathInfo)
+static std::vector<DISPLAYCONFIG_PATH_INFO> getPathInfo(const MONITORINFOEX &viewInfo)
{
// We might want to consider storing adapterId/id from DISPLAYCONFIG_PATH_TARGET_INFO.
std::vector<DISPLAYCONFIG_PATH_INFO> pathInfos;
@@ -56,7 +66,7 @@ static bool getPathInfo(const MONITORINFOEX &viewInfo, DISPLAYCONFIG_PATH_INFO *
// look up the needed buffer sizes.
if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &numPathArrayElements,
&numModeInfoArrayElements) != ERROR_SUCCESS) {
- return false;
+ return {};
}
pathInfos.resize(numPathArrayElements);
modeInfos.resize(numModeInfoArrayElements);
@@ -65,24 +75,25 @@ static bool getPathInfo(const MONITORINFOEX &viewInfo, DISPLAYCONFIG_PATH_INFO *
} while (result == ERROR_INSUFFICIENT_BUFFER);
if (result != ERROR_SUCCESS)
- return false;
-
- // Find path matching monitor name
- for (uint32_t p = 0; p < numPathArrayElements; p++) {
- DISPLAYCONFIG_SOURCE_DEVICE_NAME deviceName;
- deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
- deviceName.header.size = sizeof(DISPLAYCONFIG_SOURCE_DEVICE_NAME);
- deviceName.header.adapterId = pathInfos[p].sourceInfo.adapterId;
- deviceName.header.id = pathInfos[p].sourceInfo.id;
- if (DisplayConfigGetDeviceInfo(&deviceName.header) == ERROR_SUCCESS) {
- if (wcscmp(viewInfo.szDevice, deviceName.viewGdiDeviceName) == 0) {
- *pathInfo = pathInfos[p];
+ return {};
+
+ // Find paths matching monitor name
+ auto discardThese =
+ std::remove_if(pathInfos.begin(), pathInfos.end(), [&](const auto &path) -> bool {
+ DISPLAYCONFIG_SOURCE_DEVICE_NAME deviceName;
+ deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
+ deviceName.header.size = sizeof(DISPLAYCONFIG_SOURCE_DEVICE_NAME);
+ deviceName.header.adapterId = path.sourceInfo.adapterId;
+ deviceName.header.id = path.sourceInfo.id;
+ if (DisplayConfigGetDeviceInfo(&deviceName.header) == ERROR_SUCCESS) {
+ return wcscmp(viewInfo.szDevice, deviceName.viewGdiDeviceName) != 0;
+ }
return true;
- }
- }
- }
+ });
+
+ pathInfos.erase(discardThese, pathInfos.end());
- return false;
+ return pathInfos;
}
#if 0
@@ -106,6 +117,149 @@ static float getMonitorSDRWhiteLevel(DISPLAYCONFIG_PATH_TARGET_INFO *targetInfo)
using WindowsScreenDataList = QList<QWindowsScreenData>;
+namespace {
+
+struct DiRegKeyHandleTraits
+{
+ using Type = HKEY;
+ static Type invalidValue()
+ {
+ // The setupapi.h functions return INVALID_HANDLE_VALUE when failing to open a registry key
+ return reinterpret_cast<HKEY>(INVALID_HANDLE_VALUE);
+ }
+ static bool close(Type handle) { return RegCloseKey(handle) == ERROR_SUCCESS; }
+};
+
+using DiRegKeyHandle = QUniqueHandle<DiRegKeyHandleTraits>;
+
+}
+
+static void setMonitorDataFromSetupApi(QWindowsScreenData &data,
+ const std::vector<DISPLAYCONFIG_PATH_INFO> &pathGroup)
+{
+ if (pathGroup.empty()) {
+ return;
+ }
+
+ // The only property shared among monitors in a clone group is deviceName
+ {
+ DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName = {};
+ deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
+ deviceName.header.size = sizeof(DISPLAYCONFIG_TARGET_DEVICE_NAME);
+ // The first element in the clone group is the main monitor.
+ deviceName.header.adapterId = pathGroup[0].targetInfo.adapterId;
+ deviceName.header.id = pathGroup[0].targetInfo.id;
+ if (DisplayConfigGetDeviceInfo(&deviceName.header) == ERROR_SUCCESS) {
+ data.devicePath = QString::fromWCharArray(deviceName.monitorDevicePath);
+ } else {
+ qCWarning(lcQpaScreen)
+ << u"Unable to get device information for %1:"_s.arg(pathGroup[0].targetInfo.id)
+ << QSystemError::windowsString();
+ }
+ }
+
+ // The rest must be concatenated into the resulting property
+ QStringList names;
+ QStringList manufacturers;
+ QStringList models;
+ QStringList serialNumbers;
+
+ for (const auto &path : pathGroup) {
+ DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName = {};
+ deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
+ deviceName.header.size = sizeof(DISPLAYCONFIG_TARGET_DEVICE_NAME);
+ deviceName.header.adapterId = path.targetInfo.adapterId;
+ deviceName.header.id = path.targetInfo.id;
+ if (DisplayConfigGetDeviceInfo(&deviceName.header) != ERROR_SUCCESS) {
+ qCWarning(lcQpaScreen)
+ << u"Unable to get device information for %1:"_s.arg(path.targetInfo.id)
+ << QSystemError::windowsString();
+ continue;
+ }
+
+ // https://learn.microsoft.com/en-us/windows-hardware/drivers/install/guid-devinterface-monitor
+ constexpr GUID GUID_DEVINTERFACE_MONITOR = {
+ 0xe6f07b5f, 0xee97, 0x4a90, { 0xb0, 0x76, 0x33, 0xf5, 0x7b, 0xf4, 0xea, 0xa7 }
+ };
+ const HDEVINFO devInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_MONITOR, nullptr, nullptr,
+ DIGCF_DEVICEINTERFACE);
+
+ SP_DEVICE_INTERFACE_DATA deviceInterfaceData{};
+ deviceInterfaceData.cbSize = sizeof(deviceInterfaceData);
+
+ if (!SetupDiOpenDeviceInterfaceW(devInfo, deviceName.monitorDevicePath, DIODI_NO_ADD,
+ &deviceInterfaceData)) {
+ qCWarning(lcQpaScreen)
+ << u"Unable to open monitor interface to %1:"_s.arg(data.deviceName)
+ << QSystemError::windowsString();
+ continue;
+ }
+
+ DWORD requiredSize{ 0 };
+ if (SetupDiGetDeviceInterfaceDetailW(devInfo, &deviceInterfaceData, nullptr, 0,
+ &requiredSize, nullptr)
+ || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ continue;
+ }
+
+ const std::unique_ptr<std::byte[]> storage(new std::byte[requiredSize]);
+ auto *devicePath = reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA_W *>(storage.get());
+ devicePath->cbSize = sizeof(std::remove_pointer_t<decltype(devicePath)>);
+ SP_DEVINFO_DATA deviceInfoData{};
+ deviceInfoData.cbSize = sizeof(deviceInfoData);
+ if (!SetupDiGetDeviceInterfaceDetailW(devInfo, &deviceInterfaceData, devicePath,
+ requiredSize, nullptr, &deviceInfoData)) {
+ qCDebug(lcQpaScreen) << u"Unable to get monitor metadata for %1:"_s.arg(data.deviceName)
+ << QSystemError::windowsString();
+ continue;
+ }
+
+ const DiRegKeyHandle edidRegistryKey{ SetupDiOpenDevRegKey(
+ devInfo, &deviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ) };
+
+ if (!edidRegistryKey.isValid())
+ continue;
+
+ DWORD edidDataSize{ 0 };
+ if (RegQueryValueExW(edidRegistryKey.get(), L"EDID", nullptr, nullptr, nullptr,
+ &edidDataSize)
+ != ERROR_SUCCESS) {
+ continue;
+ }
+
+ QByteArray edidData;
+ edidData.resize(edidDataSize);
+
+ if (RegQueryValueExW(edidRegistryKey.get(), L"EDID", nullptr, nullptr,
+ reinterpret_cast<unsigned char *>(edidData.data()), &edidDataSize)
+ != ERROR_SUCCESS) {
+ qCDebug(lcQpaScreen) << u"Unable to get EDID from the Registry for %1:"_s.arg(
+ data.deviceName)
+ << QSystemError::windowsString();
+ continue;
+ }
+
+ QEdidParser edid;
+
+ if (!edid.parse(edidData)) {
+ qCDebug(lcQpaScreen) << "Invalid EDID blob for" << data.deviceName;
+ continue;
+ }
+
+ // We skip edid.identifier because it is unreliable, and a better option
+ // is already available through DisplayConfigGetDeviceInfo (see below).
+ names << QString::fromWCharArray(deviceName.monitorFriendlyDeviceName);
+ manufacturers << edid.manufacturer;
+ models << edid.model;
+ serialNumbers << edid.serialNumber;
+ }
+
+ data.name = names.join(u"|"_s);
+ data.manufacturer = manufacturers.join(u"|"_s);
+ data.model = models.join(u"|"_s);
+ data.serialNumber = serialNumbers.join(u"|"_s);
+}
+
static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data)
{
MONITORINFOEX info;
@@ -118,16 +272,9 @@ static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data)
data->geometry = QRect(QPoint(info.rcMonitor.left, info.rcMonitor.top), QPoint(info.rcMonitor.right - 1, info.rcMonitor.bottom - 1));
data->availableGeometry = QRect(QPoint(info.rcWork.left, info.rcWork.top), QPoint(info.rcWork.right - 1, info.rcWork.bottom - 1));
data->deviceName = QString::fromWCharArray(info.szDevice);
- DISPLAYCONFIG_PATH_INFO pathInfo = {};
- const bool hasPathInfo = getPathInfo(info, &pathInfo);
- if (hasPathInfo) {
- DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName = {};
- deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
- deviceName.header.size = sizeof(DISPLAYCONFIG_TARGET_DEVICE_NAME);
- deviceName.header.adapterId = pathInfo.targetInfo.adapterId;
- deviceName.header.id = pathInfo.targetInfo.id;
- if (DisplayConfigGetDeviceInfo(&deviceName.header) == ERROR_SUCCESS)
- data->name = QString::fromWCharArray(deviceName.monitorFriendlyDeviceName);
+ const auto pathGroup = getPathInfo(info);
+ if (!pathGroup.empty()) {
+ setMonitorDataFromSetupApi(*data, pathGroup);
}
if (data->name.isEmpty())
data->name = data->deviceName;
@@ -153,7 +300,9 @@ static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data)
// ### We might want to consider storing adapterId/id from DISPLAYCONFIG_PATH_TARGET_INFO,
// if we are going to use DISPLAYCONFIG lookups more.
- if (hasPathInfo) {
+ if (!pathGroup.empty()) {
+ // The first element in the clone group is the main monitor.
+ const auto &pathInfo = pathGroup[0];
switch (pathInfo.targetInfo.rotation) {
case DISPLAYCONFIG_ROTATION_IDENTITY:
data->orientation = Qt::LandscapeOrientation;
@@ -182,11 +331,8 @@ static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data)
// EnumDisplayMonitors (as opposed to EnumDisplayDevices) enumerates only
// virtual desktop screens.
data->flags |= QWindowsScreenData::VirtualDesktop;
- if (info.dwFlags & MONITORINFOF_PRIMARY) {
+ if (info.dwFlags & MONITORINFOF_PRIMARY)
data->flags |= QWindowsScreenData::PrimaryScreen;
- if ((data->flags & QWindowsScreenData::LockScreen) == 0)
- QWindowsFontDatabase::setDefaultVerticalDPI(data->dpi.second);
- }
return true;
}
@@ -232,15 +378,14 @@ static QDebug operator<<(QDebug dbg, const QWindowsScreenData &d)
QDebugStateSaver saver(dbg);
dbg.nospace();
dbg.noquote();
- dbg << "Screen \"" << d.name << "\" "
- << d.geometry.width() << 'x' << d.geometry.height() << '+' << d.geometry.x() << '+' << d.geometry.y()
- << " avail: "
- << d.availableGeometry.width() << 'x' << d.availableGeometry.height() << '+' << d.availableGeometry.x() << '+' << d.availableGeometry.y()
- << " physical: " << d.physicalSizeMM.width() << 'x' << d.physicalSizeMM.height()
- << " DPI: " << d.dpi.first << 'x' << d.dpi.second << " Depth: " << d.depth
- << " Format: " << d.format
- << " hMonitor: " << d.hMonitor
- << " device name: " << d.deviceName;
+ dbg << "Screen \"" << d.name << "\" " << d.geometry.width() << 'x' << d.geometry.height() << '+'
+ << d.geometry.x() << '+' << d.geometry.y() << " avail: " << d.availableGeometry.width()
+ << 'x' << d.availableGeometry.height() << '+' << d.availableGeometry.x() << '+'
+ << d.availableGeometry.y() << " physical: " << d.physicalSizeMM.width() << 'x'
+ << d.physicalSizeMM.height() << " DPI: " << d.dpi.first << 'x' << d.dpi.second
+ << " Depth: " << d.depth << " Format: " << d.format << " hMonitor: " << d.hMonitor
+ << " device name: " << d.deviceName << " manufacturer: " << d.manufacturer
+ << " model: " << d.model << " serial number: " << d.serialNumber;
if (d.flags & QWindowsScreenData::PrimaryScreen)
dbg << " primary";
if (d.flags & QWindowsScreenData::VirtualDesktop)
@@ -273,8 +418,6 @@ QString QWindowsScreen::name() const
: m_data.name;
}
-Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0);
-
QPixmap QWindowsScreen::grabWindow(WId window, int xIn, int yIn, int width, int height) const
{
QSize windowSize;
@@ -393,10 +536,14 @@ void QWindowsScreen::handleChanges(const QWindowsScreenData &newData)
const bool dpiChanged = !qFuzzyCompare(m_data.dpi.first, newData.dpi.first)
|| !qFuzzyCompare(m_data.dpi.second, newData.dpi.second);
const bool orientationChanged = m_data.orientation != newData.orientation;
+ const bool primaryChanged = (newData.flags & QWindowsScreenData::PrimaryScreen)
+ && !(m_data.flags & QWindowsScreenData::PrimaryScreen);
m_data.dpi = newData.dpi;
m_data.orientation = newData.orientation;
m_data.geometry = newData.geometry;
m_data.availableGeometry = newData.availableGeometry;
+ m_data.flags = (m_data.flags & ~QWindowsScreenData::PrimaryScreen)
+ | (newData.flags & QWindowsScreenData::PrimaryScreen);
if (dpiChanged) {
QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(),
@@ -409,6 +556,8 @@ void QWindowsScreen::handleChanges(const QWindowsScreenData &newData)
QWindowSystemInterface::handleScreenGeometryChange(screen(),
newData.geometry, newData.availableGeometry);
}
+ if (primaryChanged)
+ QWindowSystemInterface::handlePrimaryScreenChanged(this);
}
HMONITOR QWindowsScreen::handle() const
@@ -520,7 +669,8 @@ extern "C" LRESULT QT_WIN_CALLBACK qDisplayChangeObserverWndProc(HWND hwnd, UINT
if (QWindowsTheme *t = QWindowsTheme::instance())
t->displayChanged();
QWindowsWindow::displayChanged();
- QWindowsContext::instance()->screenManager().handleScreenChanges();
+ if (auto *context = QWindowsContext::instance())
+ context->screenManager().handleScreenChanges();
}
return DefWindowProc(hwnd, message, wParam, lParam);
diff --git a/src/plugins/platforms/windows/qwindowsscreen.h b/src/plugins/platforms/windows/qwindowsscreen.h
index b1c94d2204..0467ab2a0c 100644
--- a/src/plugins/platforms/windows/qwindowsscreen.h
+++ b/src/plugins/platforms/windows/qwindowsscreen.h
@@ -7,10 +7,9 @@
#include "qtwindowsglobal.h"
#include <QtCore/qlist.h>
-#include <QtCore/qpair.h>
#include <QtCore/qscopedpointer.h>
#include <qpa/qplatformscreen.h>
-#include <qpa/qplatformscreen_p.h>
+#include <QtGui/qscreen_platform.h>
QT_BEGIN_NAMESPACE
@@ -31,15 +30,18 @@ struct QWindowsScreenData
QImage::Format format = QImage::Format_ARGB32_Premultiplied;
unsigned flags = VirtualDesktop;
QString name;
+ QString manufacturer;
+ QString model;
+ QString serialNumber;
Qt::ScreenOrientation orientation = Qt::LandscapeOrientation;
qreal refreshRateHz = 60;
HMONITOR hMonitor = nullptr;
- QString deviceName = {};
+ QString deviceName;
+ QString devicePath;
std::optional<int> deviceIndex = std::nullopt;
};
-class QWindowsScreen : public QPlatformScreen
- , public QNativeInterface::Private::QWindowsScreen
+class QWindowsScreen : public QPlatformScreen, public QNativeInterface::QWindowsScreen
{
public:
#ifndef QT_NO_CURSOR
@@ -58,6 +60,9 @@ public:
qreal devicePixelRatio() const override { return 1.0; }
qreal refreshRate() const override { return m_data.refreshRateHz; }
QString name() const override;
+ QString manufacturer() const override { return m_data.manufacturer; }
+ QString model() const override { return m_data.model; }
+ QString serialNumber() const override { return m_data.serialNumber; }
Qt::ScreenOrientation orientation() const override { return m_data.orientation; }
QList<QPlatformScreen *> virtualSiblings() const override;
QWindow *topLevelAt(const QPoint &point) const override;
diff --git a/src/plugins/platforms/windows/qwindowsservices.cpp b/src/plugins/platforms/windows/qwindowsservices.cpp
index fbad934a05..89f93fd161 100644
--- a/src/plugins/platforms/windows/qwindowsservices.cpp
+++ b/src/plugins/platforms/windows/qwindowsservices.cpp
@@ -1,7 +1,6 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#define QT_NO_URL_CAST_FROM_STRING
#include "qwindowsservices.h"
#include <QtCore/qt_windows.h>
@@ -12,8 +11,10 @@
#include <QtCore/qthread.h>
#include <QtCore/private/qwinregistry_p.h>
+#include <QtCore/private/qfunctions_win_p.h>
#include <shlobj.h>
+#include <shlwapi.h>
#include <intshcut.h>
QT_BEGIN_NAMESPACE
@@ -25,23 +26,73 @@ enum { debug = 0 };
class QWindowsShellExecuteThread : public QThread
{
public:
- explicit QWindowsShellExecuteThread(const wchar_t *path) : m_path(path) { }
+ explicit QWindowsShellExecuteThread(const wchar_t *operation, const wchar_t *file,
+ const wchar_t *parameters)
+ : m_operation(operation)
+ , m_file(file)
+ , m_parameters(parameters) { }
void run() override
{
- if (SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE))) {
- m_result = ShellExecute(nullptr, nullptr, m_path, nullptr, nullptr, SW_SHOWNORMAL);
- CoUninitialize();
- }
+ QComHelper comHelper;
+ if (comHelper.isValid())
+ m_result = ShellExecute(nullptr, m_operation, m_file, m_parameters, nullptr,
+ SW_SHOWNORMAL);
}
HINSTANCE result() const { return m_result; }
private:
HINSTANCE m_result = nullptr;
- const wchar_t *m_path;
+ const wchar_t *m_operation;
+ const wchar_t *m_file;
+ const wchar_t *m_parameters;
};
+static QString msgShellExecuteFailed(const QUrl &url, quintptr code)
+{
+ QString result;
+ QTextStream(&result) <<"ShellExecute '" << url.toString() << "' failed (error " << code << ").";
+ return result;
+}
+
+// Retrieve the web browser and open the URL. This should be used for URLs with
+// fragments which don't work when using ShellExecute() directly (QTBUG-14460,
+// QTBUG-55300).
+static bool openWebBrowser(const QUrl &url)
+{
+ WCHAR browserExecutable[MAX_PATH] = {};
+ const wchar_t operation[] = L"open";
+ DWORD browserExecutableSize = MAX_PATH;
+ if (FAILED(AssocQueryString(0, ASSOCSTR_EXECUTABLE, L"http", operation,
+ browserExecutable, &browserExecutableSize))) {
+ return false;
+ }
+ QString browser = QString::fromWCharArray(browserExecutable, browserExecutableSize - 1);
+ // Workaround for "old" MS Edge entries. Instead of LaunchWinApp.exe we can just use msedge.exe
+ if (browser.contains("LaunchWinApp.exe"_L1, Qt::CaseInsensitive))
+ browser = "msedge.exe"_L1;
+ const QString urlS = url.toString(QUrl::FullyEncoded);
+
+ // Run ShellExecute() in a thread since it may spin the event loop.
+ // Prevent it from interfering with processing of posted events (QTBUG-85676).
+ QWindowsShellExecuteThread thread(operation,
+ reinterpret_cast<const wchar_t *>(browser.utf16()),
+ reinterpret_cast<const wchar_t *>(urlS.utf16()));
+ thread.start();
+ thread.wait();
+
+ const auto result = reinterpret_cast<quintptr>(thread.result());
+ if (debug)
+ qDebug() << __FUNCTION__ << urlS << QString::fromWCharArray(browserExecutable) << result;
+ // ShellExecute returns a value greater than 32 if successful
+ if (result <= 32) {
+ qWarning("%s", qPrintable(msgShellExecuteFailed(url, result)));
+ return false;
+ }
+ return true;
+}
+
static inline bool shellExecute(const QUrl &url)
{
const QString nativeFilePath = url.isLocalFile() && !url.hasFragment() && !url.hasQuery()
@@ -51,7 +102,9 @@ static inline bool shellExecute(const QUrl &url)
// Run ShellExecute() in a thread since it may spin the event loop.
// Prevent it from interfering with processing of posted events (QTBUG-85676).
- QWindowsShellExecuteThread thread(reinterpret_cast<const wchar_t *>(nativeFilePath.utf16()));
+ QWindowsShellExecuteThread thread(nullptr,
+ reinterpret_cast<const wchar_t *>(nativeFilePath.utf16()),
+ nullptr);
thread.start();
thread.wait();
@@ -59,7 +112,7 @@ static inline bool shellExecute(const QUrl &url)
// ShellExecute returns a value greater than 32 if successful
if (result <= 32) {
- qWarning("ShellExecute '%ls' failed (error %zu).", qUtf16Printable(url.toString()), result);
+ qWarning("%s", qPrintable(msgShellExecuteFailed(url, result)));
return false;
}
return true;
@@ -101,10 +154,15 @@ static inline bool launchMail(const QUrl &url)
qWarning("Cannot launch '%ls': There is no mail program installed.", qUtf16Printable(url.toString()));
return false;
}
+ // Fix mail launch if no param is expected in this command.
+ if (command.indexOf("%1"_L1) < 0) {
+ qWarning() << "The mail command lacks the '%1' parameter.";
+ return false;
+ }
//Make sure the path for the process is in quotes
const QChar doubleQuote = u'"';
if (!command.startsWith(doubleQuote)) {
- const int exeIndex = command.indexOf(QStringLiteral(".exe "), 0, Qt::CaseInsensitive);
+ const int exeIndex = command.indexOf(".exe "_L1, 0, Qt::CaseInsensitive);
if (exeIndex != -1) {
command.insert(exeIndex + 4, doubleQuote);
command.prepend(doubleQuote);
@@ -136,7 +194,8 @@ bool QWindowsServices::openUrl(const QUrl &url)
const QString scheme = url.scheme();
if (scheme == u"mailto" && launchMail(url))
return true;
- return shellExecute(url);
+ return url.isLocalFile() && url.hasFragment()
+ ? openWebBrowser(url) : shellExecute(url);
}
bool QWindowsServices::openDocument(const QUrl &url)
diff --git a/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp b/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp
index cf150b5772..6f0680ac23 100644
--- a/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp
+++ b/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp
@@ -1,13 +1,7 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#ifndef WINVER
-# define WINVER 0x0A00 // required for NOTIFYICONDATA_V2_SIZE, ChangeWindowMessageFilterEx() (MinGW 5.3)
-#endif
-
-#ifndef NTDDI_VERSION
-# define NTDDI_VERSION 0x0A00000B // required for Shell_NotifyIconGetRect (MinGW 5.3)
-#endif
+#include <QtCore/qt_windows.h>
#include "qwindowssystemtrayicon.h"
#include "qwindowscontext.h"
@@ -23,7 +17,6 @@
#include <QtCore/qsettings.h>
#include <qpa/qwindowsysteminterface.h>
-#include <qt_windows.h>
#include <commctrl.h>
#include <shellapi.h>
#include <shlobj.h>
@@ -31,6 +24,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::Literals::StringLiterals;
+
static const UINT q_uNOTIFYICONID = 0;
static uint MYWM_TASKBARCREATED = 0;
@@ -123,7 +118,7 @@ static inline HWND createTrayIconMessageWindow()
return nullptr;
// Register window class in the platform plugin.
const QString className =
- ctx->registerWindowClass(QWindowsContext::classNamePrefix() + QStringLiteral("TrayIconMessageWindowClass"),
+ ctx->registerWindowClass(QWindowsContext::classNamePrefix() + "TrayIconMessageWindowClass"_L1,
qWindowsTrayIconWndProc);
const wchar_t windowName[] = L"QTrayIconMessageWindow";
return CreateWindowEx(0, reinterpret_cast<const wchar_t *>(className.utf16()),
@@ -169,8 +164,7 @@ void QWindowsSystemTrayIcon::cleanup()
void QWindowsSystemTrayIcon::updateIcon(const QIcon &icon)
{
qCDebug(lcQpaTrayIcon) << __FUNCTION__ << '(' << icon << ')' << this;
- if (icon.cacheKey() == m_icon.cacheKey())
- return;
+ m_icon = icon;
const HICON hIconToDestroy = createIcon(icon);
if (ensureInstalled())
sendTrayMessage(NIM_MODIFY);
@@ -190,6 +184,9 @@ void QWindowsSystemTrayIcon::updateToolTip(const QString &tooltip)
QRect QWindowsSystemTrayIcon::geometry() const
{
+ if (!isIconVisible())
+ return QRect();
+
NOTIFYICONIDENTIFIER nid;
memset(&nid, 0, sizeof(nid));
nid.cbSize = sizeof(nid);
@@ -223,25 +220,19 @@ void QWindowsSystemTrayIcon::showMessage(const QString &title, const QString &me
qStringToLimitedWCharArray(title, tnd.szInfoTitle, 64);
tnd.uID = q_uNOTIFYICONID;
- tnd.dwInfoFlags = NIIF_USER;
-
- QSize size(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
- const QSize largeIcon(GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON));
- const QSize more = icon.actualSize(largeIcon);
- if (more.height() > (largeIcon.height() * 3/4) || more.width() > (largeIcon.width() * 3/4)) {
- tnd.dwInfoFlags |= NIIF_LARGE_ICON;
- size = largeIcon;
- }
+
+ const auto size = icon.actualSize(QSize(256, 256));
QPixmap pm = icon.pixmap(size);
+ if (m_hMessageIcon) {
+ DestroyIcon(m_hMessageIcon);
+ m_hMessageIcon = nullptr;
+ }
if (pm.isNull()) {
tnd.dwInfoFlags = NIIF_INFO;
} else {
- if (pm.size() != size) {
- qWarning("QSystemTrayIcon::showMessage: Wrong icon size (%dx%d), please add standard one: %dx%d",
- pm.size().width(), pm.size().height(), size.width(), size.height());
- pm = pm.scaled(size, Qt::IgnoreAspectRatio);
- }
- tnd.hBalloonIcon = qt_pixmapToWinHICON(pm);
+ tnd.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON;
+ m_hMessageIcon = qt_pixmapToWinHICON(pm);
+ tnd.hBalloonIcon = m_hMessageIcon;
}
tnd.hWnd = m_hwnd;
tnd.uTimeout = msecsIn <= 0 ? UINT(10000) : UINT(msecsIn); // 10s default
@@ -299,7 +290,10 @@ void QWindowsSystemTrayIcon::ensureCleanup()
}
if (m_hIcon != nullptr)
DestroyIcon(m_hIcon);
+ if (m_hMessageIcon != nullptr)
+ DestroyIcon(m_hMessageIcon);
m_hIcon = nullptr;
+ m_hMessageIcon = nullptr;
m_menu = nullptr; // externally owned
m_toolTip.clear();
}
@@ -316,6 +310,29 @@ bool QWindowsSystemTrayIcon::setIconVisible(bool visible)
return Shell_NotifyIcon(NIM_MODIFY, &tnd) == TRUE;
}
+bool QWindowsSystemTrayIcon::isIconVisible() const
+{
+ NOTIFYICONIDENTIFIER nid;
+ memset(&nid, 0, sizeof(nid));
+ nid.cbSize = sizeof(nid);
+ nid.hWnd = m_hwnd;
+ nid.uID = q_uNOTIFYICONID;
+ RECT rect;
+ const HRESULT hr = Shell_NotifyIconGetRect(&nid, &rect);
+ // Windows 10 returns S_FALSE if the icon is hidden
+ if (FAILED(hr) || hr == S_FALSE)
+ return false;
+
+ HMONITOR monitor = MonitorFromWindow(m_hwnd, MONITOR_DEFAULTTONEAREST);
+ MONITORINFO info;
+ info.cbSize = sizeof(MONITORINFO);
+ GetMonitorInfo(monitor, &info);
+ // Windows 11 seems to return a geometry outside of the current monitor's geometry in case of
+ // the icon being hidden. As it's impossible to change the alignment of the task bar on Windows
+ // 11 this check should be fine.
+ return rect.bottom <= info.rcMonitor.bottom;
+}
+
bool QWindowsSystemTrayIcon::sendTrayMessage(DWORD msg)
{
NOTIFYICONDATA tnd;
@@ -409,8 +426,15 @@ bool QWindowsSystemTrayIcon::winEvent(const MSG &message, long *result)
QWindowsPopupMenu::notifyTriggered(LOWORD(message.wParam));
break;
default:
- if (message.message == MYWM_TASKBARCREATED) // self-registered message id (tray crashed)
+ if (message.message == MYWM_TASKBARCREATED) {
+ // self-registered message id to handle that
+ // - screen resolution/DPR changed
+ const QIcon oldIcon = m_icon;
+ m_icon = QIcon(); // updateIcon is a no-op if the icon doesn't change
+ updateIcon(oldIcon);
+ // - or tray crashed
sendTrayMessage(NIM_ADD);
+ }
break;
}
return false;
diff --git a/src/plugins/platforms/windows/qwindowssystemtrayicon.h b/src/plugins/platforms/windows/qwindowssystemtrayicon.h
index 2545ae9101..3ad5feb125 100644
--- a/src/plugins/platforms/windows/qwindowssystemtrayicon.h
+++ b/src/plugins/platforms/windows/qwindowssystemtrayicon.h
@@ -49,12 +49,14 @@ private:
void ensureCleanup();
bool sendTrayMessage(DWORD msg);
bool setIconVisible(bool visible);
+ bool isIconVisible() const;
HICON createIcon(const QIcon &icon);
QIcon m_icon;
QString m_toolTip;
HWND m_hwnd = nullptr;
HICON m_hIcon = nullptr;
+ HICON m_hMessageIcon = nullptr;
mutable QPointer<QWindowsPopupMenu> m_menu;
bool m_ignoreNextMouseRelease = false;
bool m_visible = false;
diff --git a/src/plugins/platforms/windows/qwindowstabletsupport.cpp b/src/plugins/platforms/windows/qwindowstabletsupport.cpp
index b7cc2bf77b..ceebb483d2 100644
--- a/src/plugins/platforms/windows/qwindowstabletsupport.cpp
+++ b/src/plugins/platforms/windows/qwindowstabletsupport.cpp
@@ -595,7 +595,8 @@ bool QWindowsTabletSupport::translateTabletPacketEvent()
<< "mode=" << m_mode;
}
- const Qt::KeyboardModifiers keyboardModifiers = QWindowsKeyMapper::queryKeyboardModifiers();
+ const auto *keyMapper = QWindowsContext::instance()->keyMapper();
+ const Qt::KeyboardModifiers keyboardModifiers = keyMapper->queryKeyboardModifiers();
for (int i = 0; i < packetCount ; ++i) {
const PACKET &packet = localPacketBuf[i];
diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp
index adc4befaec..488935e02d 100644
--- a/src/plugins/platforms/windows/qwindowstheme.cpp
+++ b/src/plugins/platforms/windows/qwindowstheme.cpp
@@ -1,37 +1,35 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-// SHSTOCKICONINFO is only available since Vista
-#ifndef _WIN32_WINNT
-# define _WIN32_WINNT 0x0A00
-#endif
+#include <QtCore/qt_windows.h>
#include "qwindowstheme.h"
#include "qwindowsmenu.h"
#include "qwindowsdialoghelpers.h"
#include "qwindowscontext.h"
+#include "qwindowsiconengine.h"
#include "qwindowsintegration.h"
#if QT_CONFIG(systemtrayicon)
# include "qwindowssystemtrayicon.h"
#endif
#include "qwindowsscreen.h"
-#include "qt_windows.h"
+#include "qwindowswindow.h"
#include <commctrl.h>
#include <objbase.h>
-#ifndef Q_CC_MINGW
-# include <commoncontrols.h>
-#endif
+#include <commoncontrols.h>
#include <shellapi.h>
+#include <QtCore/qapplicationstatic.h>
#include <QtCore/qvariant.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qdebug.h>
-#include <QtCore/qtextstream.h>
#include <QtCore/qsysinfo.h>
#include <QtCore/qcache.h>
#include <QtCore/qthread.h>
+#include <QtCore/qqueue.h>
#include <QtCore/qmutex.h>
#include <QtCore/qwaitcondition.h>
+#include <QtCore/qoperatingsystemversion.h>
#include <QtGui/qcolor.h>
#include <QtGui/qpalette.h>
#include <QtGui/qguiapplication.h>
@@ -43,46 +41,20 @@
#include <private/qhighdpiscaling_p.h>
#include <private/qsystemlibrary_p.h>
#include <private/qwinregistry_p.h>
+#include <QtCore/private/qfunctions_win_p.h>
#include <algorithm>
-#if QT_CONFIG(cpp_winrt) && !defined(Q_CC_CLANG)
-# include <winrt/base.h>
-// Workaround for Windows SDK bug.
-// See https://github.com/microsoft/Windows.UI.Composition-Win32-Samples/issues/47
-namespace winrt::impl
-{
- template <typename Async>
- auto wait_for(Async const& async, Windows::Foundation::TimeSpan const& timeout);
-}
-# include <winrt/Windows.UI.ViewManagement.h>
-# define HAS_UISETTINGS 1
-#endif
+#if QT_CONFIG(cpp_winrt)
+# include <QtCore/private/qt_winrtbase_p.h>
-#if defined(__IImageList_INTERFACE_DEFINED__) && defined(__IID_DEFINED__)
-# define USE_IIMAGELIST
-#endif
+# include <winrt/Windows.UI.ViewManagement.h>
+#endif // QT_CONFIG(cpp_winrt)
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-static inline QColor COLORREFToQColor(COLORREF cr)
-{
- return QColor(GetRValue(cr), GetGValue(cr), GetBValue(cr));
-}
-
-static inline QTextStream& operator<<(QTextStream &str, const QColor &c)
-{
- str.setIntegerBase(16);
- str.setFieldWidth(2);
- str.setPadChar(u'0');
- str << " rgb: #" << c.red() << c.green() << c.blue();
- str.setIntegerBase(10);
- str.setFieldWidth(0);
- return str;
-}
-
static inline bool booleanSystemParametersInfo(UINT what, bool defaultValue)
{
BOOL result;
@@ -106,127 +78,159 @@ static inline QColor mixColors(const QColor &c1, const QColor &c2)
(c1.blue() + c2.blue()) / 2};
}
-static inline QColor getSysColor(int index)
-{
- return COLORREFToQColor(GetSysColor(index));
-}
+enum AccentColorLevel {
+ AccentColorDarkest,
+ AccentColorNormal,
+ AccentColorLightest
+};
-#if defined(HAS_UISETTINGS)
+#if QT_CONFIG(cpp_winrt)
static constexpr QColor getSysColor(winrt::Windows::UI::Color &&color)
{
return QColor(color.R, color.G, color.B, color.A);
}
#endif
-// QTBUG-48823/Windows 10: SHGetFileInfo() (as called by item views on file system
-// models has been observed to trigger a WM_PAINT on the mainwindow. Suppress the
-// behavior by running it in a thread.
+[[maybe_unused]] [[nodiscard]] static inline QColor qt_accentColor(AccentColorLevel level)
+{
+#if QT_CONFIG(cpp_winrt)
+ using namespace winrt::Windows::UI::ViewManagement;
+ const auto settings = UISettings();
+ const QColor accent = getSysColor(settings.GetColorValue(UIColorType::Accent));
+ const QColor accentLight = getSysColor(settings.GetColorValue(UIColorType::AccentLight1));
+ const QColor accentDarkest = getSysColor(settings.GetColorValue(UIColorType::AccentDark3));
+#else
+ const QWinRegistryKey registry(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\DWM)");
+ if (!registry.isValid())
+ return {};
+ const QVariant value = registry.value(L"AccentColor");
+ if (!value.isValid())
+ return {};
+ // The retrieved value is in the #AABBGGRR format, we need to
+ // convert it to the #AARRGGBB format which Qt expects.
+ const QColor abgr = QColor::fromRgba(qvariant_cast<DWORD>(value));
+ if (!abgr.isValid())
+ return {};
+ const QColor accent = QColor::fromRgb(abgr.blue(), abgr.green(), abgr.red(), abgr.alpha());
+ const QColor accentLight = accent.lighter(120);
+ const QColor accentDarkest = accent.darker(120 * 120 * 120);
+#endif
+ if (level == AccentColorDarkest)
+ return accentDarkest;
+ else if (level == AccentColorLightest)
+ return accentLight;
+ return accent;
+}
-struct QShGetFileInfoParams
+static inline QColor getSysColor(int index)
{
- QShGetFileInfoParams(const QString &fn, DWORD a, SHFILEINFO *i, UINT f, bool *r)
- : fileName(fn), attributes(a), flags(f), info(i), result(r)
- { }
-
- const QString &fileName;
- const DWORD attributes;
- const UINT flags;
- SHFILEINFO *const info;
- bool *const result;
-};
+ COLORREF cr = GetSysColor(index);
+ return QColor(GetRValue(cr), GetGValue(cr), GetBValue(cr));
+}
+// QTBUG-48823/Windows 10: SHGetFileInfo() (as called by item views on file system
+// models has been observed to trigger a WM_PAINT on the mainwindow. Suppress the
+// behavior by running it in a thread.
class QShGetFileInfoThread : public QThread
{
public:
- explicit QShGetFileInfoThread()
- : QThread(), m_params(nullptr)
+ struct Task
+ {
+ Task(const QString &fn, DWORD a, UINT f)
+ : fileName(fn), attributes(a), flags(f)
+ {}
+ Q_DISABLE_COPY(Task)
+ ~Task()
+ {
+ DestroyIcon(hIcon);
+ hIcon = 0;
+ }
+ // Request
+ const QString fileName;
+ const DWORD attributes;
+ const UINT flags;
+ // Result
+ HICON hIcon = 0;
+ int iIcon = -1;
+ bool finished = false;
+ bool resultValid() const { return hIcon != 0 && iIcon >= 0 && finished; }
+ };
+
+ QShGetFileInfoThread()
+ : QThread()
{
- connect(this, &QThread::finished, this, &QObject::deleteLater);
+ start();
}
- void run() override
+ ~QShGetFileInfoThread()
+ {
+ cancel();
+ wait();
+ }
+
+ QSharedPointer<Task> getNextTask()
{
- m_init = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
+ QMutexLocker l(&m_waitForTaskMutex);
+ while (!isInterruptionRequested()) {
+ if (!m_taskQueue.isEmpty())
+ return m_taskQueue.dequeue();
+ m_waitForTaskCondition.wait(&m_waitForTaskMutex);
+ }
+ return nullptr;
+ }
- QMutexLocker readyLocker(&m_readyMutex);
- while (!m_cancelled.loadRelaxed()) {
- if (!m_params && !m_cancelled.loadRelaxed()
- && !m_readyCondition.wait(&m_readyMutex, QDeadlineTimer(1000ll)))
- continue;
+ void run() override
+ {
+ QComHelper comHelper(COINIT_MULTITHREADED);
- if (m_params) {
- const QString fileName = m_params->fileName;
+ while (!isInterruptionRequested()) {
+ auto task = getNextTask();
+ if (task) {
SHFILEINFO info;
- const bool result = SHGetFileInfo(reinterpret_cast<const wchar_t *>(fileName.utf16()),
- m_params->attributes, &info, sizeof(SHFILEINFO),
- m_params->flags);
- m_doneMutex.lock();
- if (!m_cancelled.loadRelaxed()) {
- *m_params->result = result;
- memcpy(m_params->info, &info, sizeof(SHFILEINFO));
+ const bool result = SHGetFileInfo(reinterpret_cast<const wchar_t *>(task->fileName.utf16()),
+ task->attributes, &info, sizeof(SHFILEINFO),
+ task->flags);
+ if (result) {
+ task->hIcon = info.hIcon;
+ task->iIcon = info.iIcon;
}
- m_params = nullptr;
-
+ task->finished = true;
m_doneCondition.wakeAll();
- m_doneMutex.unlock();
}
}
-
- if (m_init != S_FALSE)
- CoUninitialize();
}
- bool runWithParams(QShGetFileInfoParams *params, qint64 timeOutMSecs)
+ void runWithParams(const QSharedPointer<Task> &task,
+ std::chrono::milliseconds timeout = std::chrono::milliseconds(5000))
{
- QMutexLocker doneLocker(&m_doneMutex);
-
- m_readyMutex.lock();
- m_params = params;
- m_readyCondition.wakeAll();
- m_readyMutex.unlock();
+ {
+ QMutexLocker l(&m_waitForTaskMutex);
+ m_taskQueue.enqueue(task);
+ m_waitForTaskCondition.wakeAll();
+ }
- return m_doneCondition.wait(&m_doneMutex, QDeadlineTimer(timeOutMSecs));
+ QMutexLocker doneLocker(&m_doneMutex);
+ while (!task->finished && !isInterruptionRequested()) {
+ if (!m_doneCondition.wait(&m_doneMutex, QDeadlineTimer(timeout)))
+ return;
+ }
}
void cancel()
{
- QMutexLocker doneLocker(&m_doneMutex);
- m_cancelled.storeRelaxed(1);
- m_readyCondition.wakeAll();
+ requestInterruption();
+ m_doneCondition.wakeAll();
+ m_waitForTaskCondition.wakeAll();
}
private:
- HRESULT m_init;
- QShGetFileInfoParams *m_params;
- QAtomicInt m_cancelled;
- QWaitCondition m_readyCondition;
+ QQueue<QSharedPointer<Task>> m_taskQueue;
QWaitCondition m_doneCondition;
- QMutex m_readyMutex;
+ QWaitCondition m_waitForTaskCondition;
QMutex m_doneMutex;
+ QMutex m_waitForTaskMutex;
};
-
-static bool shGetFileInfoBackground(const QString &fileName, DWORD attributes,
- SHFILEINFO *info, UINT flags,
- qint64 timeOutMSecs = 5000)
-{
- static QShGetFileInfoThread *getFileInfoThread = nullptr;
- if (!getFileInfoThread) {
- getFileInfoThread = new QShGetFileInfoThread;
- getFileInfoThread->start();
- }
-
- bool result = false;
- QShGetFileInfoParams params(fileName, attributes, info, flags, &result);
- if (!getFileInfoThread->runWithParams(&params, timeOutMSecs)) {
- // Cancel and reset getFileInfoThread. It'll
- // be reinitialized the next time we get called.
- getFileInfoThread->cancel();
- getFileInfoThread = nullptr;
- qWarning().noquote() << "SHGetFileInfo() timed out for " << fileName;
- return false;
- }
- return result;
-}
+Q_APPLICATION_STATIC(QShGetFileInfoThread, s_shGetFileInfoThread)
// from QStyle::standardPalette
static inline QPalette standardPalette()
@@ -257,25 +261,17 @@ static QColor placeHolderColor(QColor textColor)
*/
static void populateLightSystemBasePalette(QPalette &result)
{
- QColor background = getSysColor(COLOR_BTNFACE);
- QColor textColor = getSysColor(COLOR_WINDOWTEXT);
- QColor accent = getSysColor(COLOR_HIGHLIGHT);
-
-#if defined(HAS_UISETTINGS)
- if (QWindowsIntegration::instance()->darkModeHandling().testFlag(QWindowsApplication::DarkModeStyle)) {
- using namespace winrt::Windows::UI::ViewManagement;
- const auto settings = UISettings();
-
- background = getSysColor(settings.GetColorValue(UIColorType::Background));
- textColor = getSysColor(settings.GetColorValue(UIColorType::Foreground));
- accent = getSysColor(settings.GetColorValue(UIColorType::Accent));
- }
-#endif
+ const QColor background = getSysColor(COLOR_BTNFACE);
+ const QColor textColor = getSysColor(COLOR_WINDOWTEXT);
+
+ const QColor accent = qt_accentColor(AccentColorNormal);
+ const QColor accentDarkest = qt_accentColor(AccentColorDarkest);
+ const QColor linkColor = accent;
const QColor btnFace = background;
const QColor btnHighlight = getSysColor(COLOR_BTNHIGHLIGHT);
- result.setColor(QPalette::Highlight, accent);
+ result.setColor(QPalette::Highlight, getSysColor(COLOR_HIGHLIGHT));
result.setColor(QPalette::WindowText, getSysColor(COLOR_WINDOWTEXT));
result.setColor(QPalette::Button, btnFace);
result.setColor(QPalette::Light, btnHighlight);
@@ -290,9 +286,10 @@ static void populateLightSystemBasePalette(QPalette &result)
result.setColor(QPalette::Midlight, getSysColor(COLOR_3DLIGHT));
result.setColor(QPalette::Shadow, getSysColor(COLOR_3DDKSHADOW));
result.setColor(QPalette::HighlightedText, getSysColor(COLOR_HIGHLIGHTTEXT));
+ result.setColor(QPalette::Accent, accent);
- result.setColor(QPalette::Link, Qt::blue);
- result.setColor(QPalette::LinkVisited, Qt::magenta);
+ result.setColor(QPalette::Link, linkColor);
+ result.setColor(QPalette::LinkVisited, accentDarkest);
result.setColor(QPalette::Inactive, QPalette::Button, result.button().color());
result.setColor(QPalette::Inactive, QPalette::Window, result.window().color());
result.setColor(QPalette::Inactive, QPalette::Light, result.light().color());
@@ -304,7 +301,7 @@ static void populateLightSystemBasePalette(QPalette &result)
static void populateDarkSystemBasePalette(QPalette &result)
{
-#if defined(HAS_UISETTINGS)
+#if QT_CONFIG(cpp_winrt)
using namespace winrt::Windows::UI::ViewManagement;
const auto settings = UISettings();
@@ -326,19 +323,18 @@ static void populateDarkSystemBasePalette(QPalette &result)
const QColor accentLight = getSysColor(settings.GetColorValue(UIColorType::AccentLight1));
const QColor accentLighter = getSysColor(settings.GetColorValue(UIColorType::AccentLight2));
const QColor accentLightest = getSysColor(settings.GetColorValue(UIColorType::AccentLight3));
- const QColor linkColor = accent;
#else
const QColor foreground = Qt::white;
const QColor background = QColor(0x1E, 0x1E, 0x1E);
- const QColor accent = QColor(0x00, 0x55, 0xff);
+ const QColor accent = qt_accentColor(AccentColorNormal);
const QColor accentDark = accent.darker(120);
const QColor accentDarker = accentDark.darker(120);
const QColor accentDarkest = accentDarker.darker(120);
const QColor accentLight = accent.lighter(120);
const QColor accentLighter = accentLight.lighter(120);
const QColor accentLightest = accentLighter.lighter(120);
- const QColor linkColor = Qt::blue;
#endif
+ const QColor linkColor = accent;
const QColor buttonColor = background.lighter(200);
result.setColor(QPalette::All, QPalette::WindowText, foreground);
@@ -361,49 +357,19 @@ static void populateDarkSystemBasePalette(QPalette &result)
result.setColor(QPalette::All, QPalette::Link, linkColor);
result.setColor(QPalette::All, QPalette::LinkVisited, accentDarkest);
result.setColor(QPalette::All, QPalette::AlternateBase, accentDarkest);
- result.setColor(QPalette::All, QPalette::ToolTipBase, accentDarkest);
- result.setColor(QPalette::All, QPalette::ToolTipText, accentLightest);
+ result.setColor(QPalette::All, QPalette::ToolTipBase, buttonColor);
+ result.setColor(QPalette::All, QPalette::ToolTipText, foreground.darker(120));
result.setColor(QPalette::All, QPalette::PlaceholderText, placeHolderColor(foreground));
-}
-
-static QPalette systemPalette(bool light)
-{
- QPalette result = standardPalette();
- if (light)
- populateLightSystemBasePalette(result);
- else
- populateDarkSystemBasePalette(result);
-
- if (result.window() != result.base()) {
- result.setColor(QPalette::Inactive, QPalette::Highlight,
- result.color(QPalette::Inactive, QPalette::Window));
- result.setColor(QPalette::Inactive, QPalette::HighlightedText,
- result.color(QPalette::Inactive, QPalette::Text));
- }
-
- const QColor disabled = mixColors(result.windowText().color(), result.button().color());
-
- result.setColorGroup(QPalette::Disabled, result.windowText(), result.button(),
- result.light(), result.dark(), result.mid(),
- result.text(), result.brightText(), result.base(),
- result.window());
- result.setColor(QPalette::Disabled, QPalette::WindowText, disabled);
- result.setColor(QPalette::Disabled, QPalette::Text, disabled);
- result.setColor(QPalette::Disabled, QPalette::ButtonText, disabled);
- result.setColor(QPalette::Disabled, QPalette::Highlight, result.color(QPalette::Highlight));
- result.setColor(QPalette::Disabled, QPalette::HighlightedText, result.color(QPalette::HighlightedText));
- result.setColor(QPalette::Disabled, QPalette::Base, result.window().color());
- return result;
+ result.setColor(QPalette::All, QPalette::Accent, accent);
}
static inline QPalette toolTipPalette(const QPalette &systemPalette, bool light)
{
- if (!light)
- return systemPalette;
-
QPalette result(systemPalette);
- const QColor tipBgColor = getSysColor(COLOR_INFOBK);
- const QColor tipTextColor = getSysColor(COLOR_INFOTEXT);
+ const QColor tipBgColor = light ? getSysColor(COLOR_INFOBK)
+ : systemPalette.button().color();
+ const QColor tipTextColor = light ? getSysColor(COLOR_INFOTEXT)
+ : systemPalette.buttonText().color().darker(120);
result.setColor(QPalette::All, QPalette::Button, tipBgColor);
result.setColor(QPalette::All, QPalette::Window, tipBgColor);
@@ -486,6 +452,7 @@ QWindowsTheme *QWindowsTheme::m_instance = nullptr;
QWindowsTheme::QWindowsTheme()
{
m_instance = this;
+ s_darkMode = QWindowsTheme::queryDarkMode();
std::fill(m_fonts, m_fonts + NFonts, nullptr);
std::fill(m_palettes, m_palettes + NPalettes, nullptr);
refresh();
@@ -507,7 +474,10 @@ static inline QStringList iconThemeSearchPaths()
static inline QStringList styleNames()
{
- return { QStringLiteral("WindowsVista"), QStringLiteral("Windows") };
+ QStringList styles = { QStringLiteral("WindowsVista"), QStringLiteral("Windows") };
+ if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11)
+ styles.prepend(QStringLiteral("Windows11"));
+ return styles;
}
static inline int uiEffects()
@@ -562,15 +532,38 @@ QVariant QWindowsTheme::themeHint(ThemeHint hint) const
}
case MouseDoubleClickDistance:
return GetSystemMetrics(SM_CXDOUBLECLK);
+ case MenuBarFocusOnAltPressRelease:
+ return true;
default:
break;
}
return QPlatformTheme::themeHint(hint);
}
-QPlatformTheme::Appearance QWindowsTheme::appearance() const
+Qt::ColorScheme QWindowsTheme::colorScheme() const
+{
+ if (queryHighContrast())
+ return Qt::ColorScheme::Unknown;
+ return s_darkMode ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light;
+}
+
+void QWindowsTheme::handleSettingsChanged()
{
- return QWindowsContext::isDarkMode() ? Appearance::Dark : Appearance::Light;
+ const bool darkMode = QWindowsTheme::queryDarkMode();
+ const bool darkModeChanged = darkMode != QWindowsTheme::s_darkMode;
+ s_darkMode = darkMode;
+ auto integration = QWindowsIntegration::instance();
+ integration->updateApplicationBadge();
+ if (integration->darkModeHandling().testFlag(QWindowsApplication::DarkModeStyle)) {
+ QWindowsTheme::instance()->refresh();
+ QWindowSystemInterface::handleThemeChange();
+ }
+ if (darkModeChanged) {
+ if (integration->darkModeHandling().testFlag(QWindowsApplication::DarkModeWindowFrames)) {
+ for (QWindowsWindow *w : std::as_const(QWindowsContext::instance()->windows()))
+ w->setDarkBorder(s_darkMode);
+ }
+ }
}
void QWindowsTheme::clearPalettes()
@@ -581,41 +574,66 @@ void QWindowsTheme::clearPalettes()
void QWindowsTheme::refreshPalettes()
{
-
if (!QGuiApplication::desktopSettingsAware())
return;
const bool light =
- !QWindowsContext::isDarkMode()
+ !s_darkMode
|| !QWindowsIntegration::instance()->darkModeHandling().testFlag(QWindowsApplication::DarkModeStyle);
clearPalettes();
- m_palettes[SystemPalette] = new QPalette(systemPalette(light));
+ m_palettes[SystemPalette] = new QPalette(QWindowsTheme::systemPalette(light ? Qt::ColorScheme::Light : Qt::ColorScheme::Dark));
m_palettes[ToolTipPalette] = new QPalette(toolTipPalette(*m_palettes[SystemPalette], light));
m_palettes[MenuPalette] = new QPalette(menuPalette(*m_palettes[SystemPalette], light));
m_palettes[MenuBarPalette] = menuBarPalette(*m_palettes[MenuPalette], light);
if (!light) {
-#if defined(HAS_UISETTINGS)
- using namespace winrt::Windows::UI::ViewManagement;
- const auto settings = UISettings();
- const QColor accent = getSysColor(settings.GetColorValue(UIColorType::Accent));
- const QColor accentLight = getSysColor(settings.GetColorValue(UIColorType::AccentLight1));
- const QColor accentDarkest = getSysColor(settings.GetColorValue(UIColorType::AccentDark3));
- m_palettes[CheckBoxPalette] = new QPalette(*m_palettes[SystemPalette]);
- m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Base, accent);
- m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Button, accentLight);
- m_palettes[CheckBoxPalette]->setColor(QPalette::Inactive, QPalette::Base, accentDarkest);
-#else
- m_palettes[ButtonPalette] = new QPalette(*m_palettes[SystemPalette]);
- m_palettes[ButtonPalette]->setColor(QPalette::Button, QColor(0x666666u));
- const QColor checkBoxBlue(0x0078d7u);
m_palettes[CheckBoxPalette] = new QPalette(*m_palettes[SystemPalette]);
- m_palettes[CheckBoxPalette]->setColor(QPalette::Base, checkBoxBlue);
- m_palettes[CheckBoxPalette]->setColor(QPalette::Button, checkBoxBlue);
- m_palettes[CheckBoxPalette]->setColor(QPalette::ButtonText, Qt::white);
-#endif
+ m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Base, qt_accentColor(AccentColorNormal));
+ m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Button, qt_accentColor(AccentColorLightest));
+ m_palettes[CheckBoxPalette]->setColor(QPalette::Inactive, QPalette::Base, qt_accentColor(AccentColorDarkest));
m_palettes[RadioButtonPalette] = new QPalette(*m_palettes[CheckBoxPalette]);
}
}
+QPalette QWindowsTheme::systemPalette(Qt::ColorScheme colorScheme)
+{
+ QPalette result = standardPalette();
+
+ switch (colorScheme) {
+ case Qt::ColorScheme::Light:
+ populateLightSystemBasePalette(result);
+ break;
+ case Qt::ColorScheme::Dark:
+ populateDarkSystemBasePalette(result);
+ break;
+ default:
+ qFatal("Unknown color scheme");
+ break;
+ }
+
+ if (result.window() != result.base()) {
+ result.setColor(QPalette::Inactive, QPalette::Highlight,
+ result.color(QPalette::Inactive, QPalette::Window));
+ result.setColor(QPalette::Inactive, QPalette::HighlightedText,
+ result.color(QPalette::Inactive, QPalette::Text));
+ result.setColor(QPalette::Inactive, QPalette::Accent,
+ result.color(QPalette::Inactive, QPalette::Window));
+ }
+
+ const QColor disabled = mixColors(result.windowText().color(), result.button().color());
+
+ result.setColorGroup(QPalette::Disabled, result.windowText(), result.button(),
+ result.light(), result.dark(), result.mid(),
+ result.text(), result.brightText(), result.base(),
+ result.window());
+ result.setColor(QPalette::Disabled, QPalette::WindowText, disabled);
+ result.setColor(QPalette::Disabled, QPalette::Text, disabled);
+ result.setColor(QPalette::Disabled, QPalette::ButtonText, disabled);
+ result.setColor(QPalette::Disabled, QPalette::Highlight, result.color(QPalette::Highlight));
+ result.setColor(QPalette::Disabled, QPalette::HighlightedText, result.color(QPalette::HighlightedText));
+ result.setColor(QPalette::Disabled, QPalette::Accent, disabled);
+ result.setColor(QPalette::Disabled, QPalette::Base, result.window().color());
+ return result;
+}
+
void QWindowsTheme::clearFonts()
{
qDeleteAll(m_fonts, m_fonts + NFonts);
@@ -655,20 +673,22 @@ void QWindowsTheme::refreshFonts()
clearFonts();
if (!QGuiApplication::desktopSettingsAware())
return;
+
+ const int dpi = 96;
NONCLIENTMETRICS ncm;
- auto &screenManager = QWindowsContext::instance()->screenManager();
- QWindowsContext::nonClientMetricsForScreen(&ncm, screenManager.screens().value(0));
+ QWindowsContext::nonClientMetrics(&ncm, dpi);
qCDebug(lcQpaWindow) << __FUNCTION__ << ncm;
- const QFont menuFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMenuFont);
- const QFont messageBoxFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMessageFont);
- const QFont statusFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfStatusFont);
- const QFont titleFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfCaptionFont);
+
+ const QFont menuFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMenuFont, dpi);
+ const QFont messageBoxFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMessageFont, dpi);
+ const QFont statusFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfStatusFont, dpi);
+ const QFont titleFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfCaptionFont, dpi);
QFont fixedFont(QStringLiteral("Courier New"), messageBoxFont.pointSize());
fixedFont.setStyleHint(QFont::TypeWriter);
LOGFONT lfIconTitleFont;
- SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0);
- const QFont iconTitleFont = QWindowsFontDatabase::LOGFONT_to_QFont(lfIconTitleFont);
+ SystemParametersInfoForDpi(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0, dpi);
+ const QFont iconTitleFont = QWindowsFontDatabase::LOGFONT_to_QFont(lfIconTitleFont, dpi);
m_fonts[SystemFont] = new QFont(QWindowsFontDatabase::systemDefaultFont());
m_fonts[MenuFont] = new QFont(menuFont);
@@ -725,11 +745,7 @@ void QWindowsTheme::refreshIconPixmapSizes()
fileIconSizes[LargeFileIcon] + fileIconSizes[LargeFileIcon] / 2;
fileIconSizes[JumboFileIcon] = 8 * fileIconSizes[LargeFileIcon]; // empirical, has not been observed to work
-#ifdef USE_IIMAGELIST
int *availEnd = fileIconSizes + JumboFileIcon + 1;
-#else
- int *availEnd = fileIconSizes + LargeFileIcon + 1;
-#endif // USE_IIMAGELIST
m_fileIconSizes = QAbstractFileIconEngine::toSizeList(fileIconSizes, availEnd);
qCDebug(lcQpaWindow) << __FUNCTION__ << m_fileIconSizes;
}
@@ -842,15 +858,18 @@ QPixmap QWindowsTheme::standardPixmap(StandardPixmap sp, const QSizeF &pixmapSiz
}
if (stockId != SIID_INVALID) {
- QPixmap pixmap;
SHSTOCKICONINFO iconInfo;
memset(&iconInfo, 0, sizeof(iconInfo));
iconInfo.cbSize = sizeof(iconInfo);
- stockFlags |= (pixmapSize.width() > 16 ? SHGFI_LARGEICON : SHGFI_SMALLICON);
- if (SHGetStockIconInfo(stockId, SHGFI_ICON | stockFlags, &iconInfo) == S_OK) {
- pixmap = qt_pixmapFromWinHICON(iconInfo.hIcon);
- DestroyIcon(iconInfo.hIcon);
- return pixmap;
+ stockFlags |= SHGSI_ICONLOCATION;
+ if (SHGetStockIconInfo(stockId, stockFlags, &iconInfo) == S_OK) {
+ const auto iconSize = pixmapSize.width();
+ HICON icon;
+ if (SHDefExtractIcon(iconInfo.szPath, iconInfo.iIcon, 0, &icon, nullptr, iconSize) == S_OK) {
+ QPixmap pixmap = qt_pixmapFromWinHICON(icon);
+ DestroyIcon(icon);
+ return pixmap;
+ }
}
}
@@ -920,10 +939,9 @@ public:
// Shell image list helper functions.
-static QPixmap pixmapFromShellImageList(int iImageList, const SHFILEINFO &info)
+static QPixmap pixmapFromShellImageList(int iImageList, int iIcon)
{
QPixmap result;
-#ifdef USE_IIMAGELIST
// For MinGW:
static const IID iID_IImageList = {0x46eb5926, 0x582e, 0x4017, {0x9f, 0xdf, 0xe8, 0x99, 0x8d, 0xaa, 0x9, 0x50}};
@@ -932,16 +950,12 @@ static QPixmap pixmapFromShellImageList(int iImageList, const SHFILEINFO &info)
if (hr != S_OK)
return result;
HICON hIcon;
- hr = imageList->GetIcon(info.iIcon, ILD_TRANSPARENT, &hIcon);
+ hr = imageList->GetIcon(iIcon, ILD_TRANSPARENT, &hIcon);
if (hr == S_OK) {
result = qt_pixmapFromWinHICON(hIcon);
DestroyIcon(hIcon);
}
imageList->Release();
-#else
- Q_UNUSED(iImageList);
- Q_UNUSED(info);
-#endif // USE_IIMAGELIST
return result;
}
@@ -981,13 +995,10 @@ QString QWindowsFileIconEngine::cacheKey() const
QPixmap QWindowsFileIconEngine::filePixmap(const QSize &size, QIcon::Mode, QIcon::State)
{
- /* We don't use the variable, but by storing it statically, we
- * ensure CoInitialize is only called once. */
- static HRESULT comInit = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
- Q_UNUSED(comInit);
+ QComHelper comHelper;
static QCache<QString, FakePointer<int> > dirIconEntryCache(1000);
- static QMutex mx;
+ Q_CONSTINIT static QMutex mx;
static int defaultFolderIIcon = -1;
const bool useDefaultFolderIcon = options() & QPlatformTheme::DontUseCustomDirectoryIcons;
@@ -996,13 +1007,9 @@ QPixmap QWindowsFileIconEngine::filePixmap(const QSize &size, QIcon::Mode, QIcon
const int width = int(size.width());
const int iconSize = width > fileIconSizes[SmallFileIcon] ? SHGFI_LARGEICON : SHGFI_SMALLICON;
const int requestedImageListSize =
-#ifdef USE_IIMAGELIST
width > fileIconSizes[ExtraLargeFileIcon]
? sHIL_JUMBO
: (width > fileIconSizes[LargeFileIcon] ? sHIL_EXTRALARGE : 0);
-#else
- 0;
-#endif // !USE_IIMAGELIST
bool cacheableDirIcon = fileInfo().isDir() && !fileInfo().isRoot();
if (cacheableDirIcon) {
QMutexLocker locker(&mx);
@@ -1018,7 +1025,6 @@ QPixmap QWindowsFileIconEngine::filePixmap(const QSize &size, QIcon::Mode, QIcon
}
}
- SHFILEINFO info;
unsigned int flags = SHGFI_ICON | iconSize | SHGFI_SYSICONINDEX | SHGFI_ADDOVERLAYS | SHGFI_OVERLAYINDEX;
DWORD attributes = 0;
QString path = filePath;
@@ -1030,43 +1036,43 @@ QPixmap QWindowsFileIconEngine::filePixmap(const QSize &size, QIcon::Mode, QIcon
flags |= SHGFI_USEFILEATTRIBUTES;
attributes |= FILE_ATTRIBUTE_NORMAL;
}
- const bool val = shGetFileInfoBackground(path, attributes, &info, flags);
-
+ auto task = QSharedPointer<QShGetFileInfoThread::Task>(
+ new QShGetFileInfoThread::Task(path, attributes, flags));
+ s_shGetFileInfoThread()->runWithParams(task);
// Even if GetFileInfo returns a valid result, hIcon can be empty in some cases
- if (val && info.hIcon) {
+ if (task->resultValid()) {
QString key;
if (cacheableDirIcon) {
if (useDefaultFolderIcon && defaultFolderIIcon < 0)
- defaultFolderIIcon = info.iIcon;
+ defaultFolderIIcon = task->iIcon;
//using the unique icon index provided by windows save us from duplicate keys
- key = dirIconPixmapCacheKey(info.iIcon, iconSize, requestedImageListSize);
+ key = dirIconPixmapCacheKey(task->iIcon, iconSize, requestedImageListSize);
QPixmapCache::find(key, &pixmap);
if (!pixmap.isNull()) {
QMutexLocker locker(&mx);
- dirIconEntryCache.insert(filePath, FakePointer<int>::create(info.iIcon));
+ dirIconEntryCache.insert(filePath, FakePointer<int>::create(task->iIcon));
}
}
if (pixmap.isNull()) {
if (requestedImageListSize) {
- pixmap = pixmapFromShellImageList(requestedImageListSize, info);
+ pixmap = pixmapFromShellImageList(requestedImageListSize, task->iIcon);
if (pixmap.isNull() && requestedImageListSize == sHIL_JUMBO)
- pixmap = pixmapFromShellImageList(sHIL_EXTRALARGE, info);
+ pixmap = pixmapFromShellImageList(sHIL_EXTRALARGE, task->iIcon);
}
if (pixmap.isNull())
- pixmap = qt_pixmapFromWinHICON(info.hIcon);
+ pixmap = qt_pixmapFromWinHICON(task->hIcon);
if (!pixmap.isNull()) {
if (cacheableDirIcon) {
QMutexLocker locker(&mx);
QPixmapCache::insert(key, pixmap);
- dirIconEntryCache.insert(filePath, FakePointer<int>::create(info.iIcon));
+ dirIconEntryCache.insert(filePath, FakePointer<int>::create(task->iIcon));
}
} else {
qWarning("QWindowsTheme::fileIconPixmap() no icon found");
}
}
- DestroyIcon(info.hIcon);
}
return pixmap;
@@ -1077,6 +1083,11 @@ QIcon QWindowsTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOpt
return QIcon(new QWindowsFileIconEngine(fileInfo, iconOptions));
}
+QIconEngine *QWindowsTheme::createIconEngine(const QString &iconName) const
+{
+ return new QWindowsIconEngine(iconName);
+}
+
static inline bool doUseNativeMenus()
{
const unsigned options = QWindowsIntegration::instance()->options();
@@ -1113,7 +1124,11 @@ bool QWindowsTheme::queryDarkMode()
bool QWindowsTheme::queryHighContrast()
{
- return booleanSystemParametersInfo(SPI_GETHIGHCONTRAST, false);
+ HIGHCONTRAST hcf = {};
+ hcf.cbSize = static_cast<UINT>(sizeof(HIGHCONTRAST));
+ if (SystemParametersInfo(SPI_GETHIGHCONTRAST, hcf.cbSize, &hcf, FALSE))
+ return hcf.dwFlags & HCF_HIGHCONTRASTON;
+ return false;
}
QPlatformMenuItem *QWindowsTheme::createPlatformMenuItem() const
diff --git a/src/plugins/platforms/windows/qwindowstheme.h b/src/plugins/platforms/windows/qwindowstheme.h
index 85a7607d7f..6f444d8408 100644
--- a/src/plugins/platforms/windows/qwindowstheme.h
+++ b/src/plugins/platforms/windows/qwindowstheme.h
@@ -31,7 +31,9 @@ public:
#endif
QVariant themeHint(ThemeHint) const override;
- Appearance appearance() const override;
+ Qt::ColorScheme colorScheme() const override;
+
+ static void handleSettingsChanged();
const QPalette *palette(Palette type = SystemPalette) const override
{ return m_palettes[type]; }
@@ -41,6 +43,7 @@ public:
QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const override;
QIcon fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions iconOptions = {}) const override;
+ QIconEngine *createIconEngine(const QString &iconName) const override;
void windowsThemeChanged(QWindow *window);
void displayChanged() { refreshIconPixmapSizes(); }
@@ -61,6 +64,8 @@ public:
static const char *name;
+ static QPalette systemPalette(Qt::ColorScheme);
+
private:
void clearPalettes();
void refreshPalettes();
@@ -68,6 +73,7 @@ private:
void refreshIconPixmapSizes();
static QWindowsTheme *m_instance;
+ static inline bool s_darkMode = false;
QPalette *m_palettes[NPalettes];
QFont *m_fonts[NFonts];
QList<QSize> m_fileIconSizes;
diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp
index 468b028b8b..03c5d149a6 100644
--- a/src/plugins/platforms/windows/qwindowswindow.cpp
+++ b/src/plugins/platforms/windows/qwindowswindow.cpp
@@ -1,12 +1,11 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#ifndef WINVER
-# define WINVER 0x0A00 // Enable touch functions for MinGW
-#endif
+#include <QtCore/qt_windows.h>
#include "qwindowswindow.h"
#include "qwindowscontext.h"
+#include "qwindowstheme.h"
#if QT_CONFIG(draganddrop)
# include "qwindowsdrag.h"
#endif
@@ -29,6 +28,7 @@
#include <QtGui/qwindow.h>
#include <QtGui/qregion.h>
#include <QtGui/qopenglcontext.h>
+#include <QtGui/private/qwindowsthemecache_p.h>
#include <private/qwindow_p.h> // QWINDOWSIZE_MAX
#include <private/qguiapplication_p.h>
#include <private/qhighdpiscaling_p.h>
@@ -431,11 +431,7 @@ static inline bool windowIsAccelerated(const QWindow *w)
{
switch (w->surfaceType()) {
case QSurface::OpenGLSurface:
- return true;
- case QSurface::RasterGLSurface:
- return qt_window_private(const_cast<QWindow *>(w))->compositing;
case QSurface::VulkanSurface:
- return true;
case QSurface::Direct3DSurface:
return true;
default:
@@ -470,14 +466,21 @@ static bool shouldShowMaximizeButton(const QWindow *w, Qt::WindowFlags flags)
w->maximumSize() == QSize(QWINDOWSIZE_MAX, QWINDOWSIZE_MAX);
}
+bool QWindowsWindow::hasNoNativeFrame(HWND hwnd, Qt::WindowFlags flags)
+{
+ const LONG_PTR style = GetWindowLongPtr(hwnd, GWL_STYLE);
+ return (style & WS_CHILD) || (flags & Qt::FramelessWindowHint);
+}
+
// Set the WS_EX_LAYERED flag on a HWND if required. This is required for
// translucent backgrounds, not fully opaque windows and for
// Qt::WindowTransparentForInput (in combination with WS_EX_TRANSPARENT).
bool QWindowsWindow::setWindowLayered(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, qreal opacity)
{
const LONG_PTR exStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
+ // Native children are frameless by nature, so check for that as well.
const bool needsLayered = (flags & Qt::WindowTransparentForInput)
- || (hasAlpha && (flags & Qt::FramelessWindowHint)) || opacity < 1.0;
+ || (hasAlpha && hasNoNativeFrame(hwnd, flags)) || opacity < 1.0;
const bool isLayered = (exStyle & WS_EX_LAYERED);
if (needsLayered != isLayered) {
if (needsLayered) {
@@ -493,7 +496,7 @@ static void setWindowOpacity(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, bo
{
if (QWindowsWindow::setWindowLayered(hwnd, flags, hasAlpha, level)) {
const BYTE alpha = BYTE(qRound(255.0 * level));
- if (hasAlpha && !accelerated && (flags & Qt::FramelessWindowHint)) {
+ if (hasAlpha && !accelerated && QWindowsWindow::hasNoNativeFrame(hwnd, flags)) {
// Non-GL windows with alpha: Use blend function to update.
BLENDFUNCTION blend = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA};
UpdateLayeredWindow(hwnd, nullptr, nullptr, nullptr, nullptr, nullptr, 0, &blend, ULW_ALPHA);
@@ -795,15 +798,8 @@ void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flag
style = WS_CHILD;
}
- // if (!testAttribute(Qt::WA_PaintUnclipped))
- // ### Commented out for now as it causes some problems, but
- // this should be correct anyway, so dig some more into this
-#ifdef Q_FLATTEN_EXPOSE
- if (windowIsOpenGL(w)) // a bit incorrect since the is-opengl status may change from false to true at any time later on
- style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN; // see SetPixelFormat
-#else
style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN ;
-#endif
+
if (topLevel) {
if ((type == Qt::Window || dialog || tool)) {
if (!(flags & Qt::FramelessWindowHint)) {
@@ -843,6 +839,10 @@ void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flag
// NOTE: WS_EX_TRANSPARENT flag can make mouse inputs fall through a layered window
if (flagsIn & Qt::WindowTransparentForInput)
exStyle |= WS_EX_LAYERED | WS_EX_TRANSPARENT;
+
+ // Currently only compatible with D3D surfaces, use it with care.
+ if (qEnvironmentVariableIntValue("QT_QPA_DISABLE_REDIRECTION_SURFACE"))
+ exStyle |= WS_EX_NOREDIRECTIONBITMAP;
}
}
@@ -850,13 +850,16 @@ static inline bool shouldApplyDarkFrame(const QWindow *w)
{
if (!w->isTopLevel() || w->flags().testFlag(Qt::FramelessWindowHint))
return false;
- if (QWindowsIntegration::instance()->darkModeHandling().testFlag(QWindowsApplication::DarkModeStyle))
- return true;
+ // the application has explicitly opted out of dark frames
+ if (!QWindowsIntegration::instance()->darkModeHandling().testFlag(QWindowsApplication::DarkModeWindowFrames))
+ return false;
+
// if the application supports a dark border, and the palette is dark (window background color
// is darker than the text), then turn dark-border support on, otherwise use a light border.
- const QPalette defaultPalette;
- return defaultPalette.color(QPalette::WindowText).lightness()
- > defaultPalette.color(QPalette::Window).lightness();
+ auto *dWindow = QWindowPrivate::get(const_cast<QWindow*>(w));
+ const QPalette windowPal = dWindow->windowPalette();
+ return windowPal.color(QPalette::WindowText).lightness()
+ > windowPal.color(QPalette::Window).lightness();
}
QWindowsWindowData
@@ -887,7 +890,8 @@ QWindowsWindowData
style, exStyle));
QWindowsContext::instance()->setWindowCreationContext(context);
- const bool hasFrame = (style & (WS_DLGFRAME | WS_THICKFRAME));
+ const bool hasFrame = (style & (WS_DLGFRAME | WS_THICKFRAME))
+ && !(result.flags & Qt::FramelessWindowHint);
QMargins invMargins = topLevel && hasFrame && QWindowsGeometryHint::positionIncludesFrame(w)
? invisibleMargins(QPoint(context->frameX, context->frameY)) : QMargins();
@@ -926,11 +930,8 @@ QWindowsWindowData
return result;
}
- if (QWindowsContext::isDarkMode()
- && QWindowsIntegration::instance()->darkModeHandling().testFlag(QWindowsApplication::DarkModeWindowFrames)
- && shouldApplyDarkFrame(w)) {
+ if (QWindowsTheme::instance()->colorScheme() == Qt::ColorScheme::Dark && shouldApplyDarkFrame(w))
QWindowsWindow::setDarkBorderToWindow(result.hwnd, true);
- }
if (mirrorParentWidth != 0) {
context->obtainedPos.setX(mirrorParentWidth - context->obtainedSize.width()
@@ -940,6 +941,7 @@ QWindowsWindowData
QRect obtainedGeometry(context->obtainedPos, context->obtainedSize);
result.geometry = obtainedGeometry;
+ result.restoreGeometry = frameGeometry(result.hwnd, topLevel);
result.fullFrameMargins = context->margins;
result.embedded = embedded;
result.hasFrame = hasFrame;
@@ -1014,6 +1016,21 @@ static QSize toNativeSizeConstrained(QSize dip, const QScreen *s)
return dip;
}
+// Helper for checking if frame adjustment needs to be skipped
+// NOTE: Unmaximized frameless windows will skip margins calculation
+static bool shouldOmitFrameAdjustment(const Qt::WindowFlags flags, DWORD style)
+{
+ return flags.testFlag(Qt::FramelessWindowHint) && !(style & WS_MAXIMIZE);
+}
+
+// Helper for checking if frame adjustment needs to be skipped
+// NOTE: Unmaximized frameless windows will skip margins calculation
+static bool shouldOmitFrameAdjustment(const Qt::WindowFlags flags, HWND hwnd)
+{
+ DWORD style = hwnd != nullptr ? DWORD(GetWindowLongPtr(hwnd, GWL_STYLE)) : 0;
+ return flags.testFlag(Qt::FramelessWindowHint) && !(style & WS_MAXIMIZE);
+}
+
/*!
\class QWindowsGeometryHint
\brief Stores geometry constraints and provides utility functions.
@@ -1026,7 +1043,7 @@ static QSize toNativeSizeConstrained(QSize dip, const QScreen *s)
QMargins QWindowsGeometryHint::frameOnPrimaryScreen(const QWindow *w, DWORD style, DWORD exStyle)
{
- if (!w->isTopLevel() || w->flags().testFlag(Qt::FramelessWindowHint))
+ if (!w->isTopLevel() || shouldOmitFrameAdjustment(w->flags(), style))
return {};
RECT rect = {0,0,0,0};
style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs.
@@ -1042,15 +1059,13 @@ QMargins QWindowsGeometryHint::frameOnPrimaryScreen(const QWindow *w, DWORD styl
QMargins QWindowsGeometryHint::frameOnPrimaryScreen(const QWindow *w, HWND hwnd)
{
- if (!w->isTopLevel() || w->flags().testFlag(Qt::FramelessWindowHint))
- return {};
return frameOnPrimaryScreen(w, DWORD(GetWindowLongPtr(hwnd, GWL_STYLE)),
DWORD(GetWindowLongPtr(hwnd, GWL_EXSTYLE)));
}
QMargins QWindowsGeometryHint::frame(const QWindow *w, DWORD style, DWORD exStyle, qreal dpi)
{
- if (!w->isTopLevel() || w->flags().testFlag(Qt::FramelessWindowHint))
+ if (!w->isTopLevel() || shouldOmitFrameAdjustment(w->flags(), style))
return {};
RECT rect = {0,0,0,0};
style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs.
@@ -1068,7 +1083,7 @@ QMargins QWindowsGeometryHint::frame(const QWindow *w, DWORD style, DWORD exStyl
QMargins QWindowsGeometryHint::frame(const QWindow *w, HWND hwnd, DWORD style, DWORD exStyle)
{
- if (!w->isTopLevel() || w->flags().testFlag(Qt::FramelessWindowHint))
+ if (!w->isTopLevel() || shouldOmitFrameAdjustment(w->flags(), style))
return {};
if (QWindowsScreenManager::isSingleScreen())
return frameOnPrimaryScreen(w, style, exStyle);
@@ -1082,8 +1097,6 @@ QMargins QWindowsGeometryHint::frame(const QWindow *w, HWND hwnd, DWORD style, D
QMargins QWindowsGeometryHint::frame(const QWindow *w, HWND hwnd)
{
- if (!w->isTopLevel() || w->flags().testFlag(Qt::FramelessWindowHint))
- return {};
return frame(w, hwnd, DWORD(GetWindowLongPtr(hwnd, GWL_STYLE)),
DWORD(GetWindowLongPtr(hwnd, GWL_EXSTYLE)));
}
@@ -1092,7 +1105,7 @@ QMargins QWindowsGeometryHint::frame(const QWindow *w, HWND hwnd)
QMargins QWindowsGeometryHint::frame(const QWindow *w, const QRect &geometry,
DWORD style, DWORD exStyle)
{
- if (!w->isTopLevel() || w->flags().testFlag(Qt::FramelessWindowHint))
+ if (!w->isTopLevel() || shouldOmitFrameAdjustment(w->flags(), style))
return {};
if (QWindowsScreenManager::isSingleScreen()
|| !QWindowsContext::shouldHaveNonClientDpiScaling(w)) {
@@ -1345,6 +1358,8 @@ QWindowsForeignWindow::QWindowsForeignWindow(QWindow *window, HWND hwnd)
, m_hwnd(hwnd)
, m_topLevelStyle(0)
{
+ if (QPlatformWindow::parent())
+ setParent(QPlatformWindow::parent());
}
void QWindowsForeignWindow::setParent(const QPlatformWindow *newParentWindow)
@@ -1408,13 +1423,16 @@ QWindowCreationContext::QWindowCreationContext(const QWindow *w, const QScreen *
requestedGeometry(geometry),
obtainedPos(geometryIn.topLeft()),
obtainedSize(geometryIn.size()),
- margins(QWindowsGeometryHint::frame(w, geometry, style, exStyle)),
- customMargins(cm)
+ margins(QWindowsGeometryHint::frame(w, geometry, style, exStyle))
{
// Geometry of toplevels does not consider window frames.
// TODO: No concept of WA_wasMoved yet that would indicate a
// CW_USEDEFAULT unless set. For now, assume that 0,0 means 'default'
// for toplevels.
+
+ if (!(w->flags() & Qt::FramelessWindowHint))
+ customMargins = cm;
+
if (geometry.isValid()
|| !qt_window_private(const_cast<QWindow *>(w))->resizeAutomatic) {
frameX = geometry.x();
@@ -1517,6 +1535,7 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const QWindowsWindowData &data)
QWindowsWindow::~QWindowsWindow()
{
setFlag(WithinDestroy);
+ QWindowsThemeCache::clearThemeCache(m_data.hwnd);
if (testFlag(TouchRegistered))
UnregisterTouchWindow(m_data.hwnd);
destroyWindow();
@@ -1958,6 +1977,12 @@ void QWindowsWindow::handleCompositionSettingsChanged()
}
}
+qreal QWindowsWindow::dpiRelativeScale(const UINT dpi) const
+{
+ return QHighDpiScaling::roundScaleFactor(qreal(dpi) / QWindowsScreen::baseDpi) /
+ QHighDpiScaling::roundScaleFactor(qreal(savedDpi()) / QWindowsScreen::baseDpi);
+}
+
void QWindowsWindow::handleDpiScaledSize(WPARAM wParam, LPARAM lParam, LRESULT *result)
{
// We want to keep QWindow's device independent size constant across the
@@ -1965,11 +1990,19 @@ void QWindowsWindow::handleDpiScaledSize(WPARAM wParam, LPARAM lParam, LRESULT *
// by the change of DPI (e.g. 120 -> 144 = 1.2), also taking any scale
// factor rounding into account. The win32 window size includes the margins;
// add the margins for the new DPI to the window size.
- const int dpi = int(wParam);
- const qreal scale = QHighDpiScaling::roundScaleFactor(qreal(dpi) / QWindowsScreen::baseDpi) /
- QHighDpiScaling::roundScaleFactor(qreal(savedDpi()) / QWindowsScreen::baseDpi);
- const QMargins margins = QWindowsGeometryHint::frame(window(), style(), exStyle(), dpi);
- const QSize windowSize = (geometry().size() * scale).grownBy(margins);
+ const UINT dpi = UINT(wParam);
+ const qreal scale = dpiRelativeScale(dpi);
+ const QMargins margins = fullFrameMargins();
+ if (!(m_data.flags & Qt::FramelessWindowHint)) {
+ // We need to update the custom margins to match the current DPI, because
+ // we don't want our users manually hook into this message just to set a
+ // new margin, but here we can't call setCustomMargins() directly, that
+ // function will change the window geometry which conflicts with what we
+ // are currently doing.
+ m_data.customMargins *= scale;
+ }
+
+ const QSize windowSize = (geometry().size() * scale).grownBy((margins * scale) + customMargins());
SIZE *size = reinterpret_cast<SIZE *>(lParam);
size->cx = windowSize.width();
size->cy = windowSize.height();
@@ -1979,11 +2012,17 @@ void QWindowsWindow::handleDpiScaledSize(WPARAM wParam, LPARAM lParam, LRESULT *
void QWindowsWindow::handleDpiChanged(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
const UINT dpi = HIWORD(wParam);
+ const qreal scale = dpiRelativeScale(dpi);
setSavedDpi(dpi);
+ QWindowsThemeCache::clearThemeCache(hwnd);
+
// Send screen change first, so that the new screen is set during any following resize
checkForScreenChanged(QWindowsWindow::FromDpiChange);
+ if (!IsZoomed(hwnd))
+ m_data.restoreGeometry.setSize(m_data.restoreGeometry.size() * scale);
+
// We get WM_DPICHANGED in one of two situations:
//
// 1. The DPI change is a "spontaneous" DPI change as a result of e.g.
@@ -2006,13 +2045,22 @@ void QWindowsWindow::handleDpiChanged(HWND hwnd, WPARAM wParam, LPARAM lParam)
SetWindowPos(hwnd, nullptr, prcNewWindow->left, prcNewWindow->top,
prcNewWindow->right - prcNewWindow->left,
prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE);
+ // If the window does not have a frame, WM_MOVE and WM_SIZE won't be
+ // called which prevents the content from being scaled appropriately
+ // after a DPI change.
+ if (shouldOmitFrameAdjustment(m_data.flags, m_data.hwnd))
+ handleGeometryChange();
}
+
+ // Re-apply mask now that we have a new DPI, which have resulted in
+ // a new scale factor.
+ setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window()));
}
void QWindowsWindow::handleDpiChangedAfterParent(HWND hwnd)
{
const UINT dpi = GetDpiForWindow(hwnd);
- const qreal scale = qreal(dpi) / qreal(savedDpi());
+ const qreal scale = dpiRelativeScale(dpi);
setSavedDpi(dpi);
checkForScreenChanged(QWindowsWindow::FromDpiChange);
@@ -2119,7 +2167,7 @@ void QWindowsWindow::setGeometry(const QRect &rectIn)
if (m_data.geometry != rect && (isVisible() || QLibraryInfo::isDebugBuild())) {
const auto warning =
msgUnableToSetGeometry(this, rectIn, m_data.geometry,
- m_data.fullFrameMargins, m_data.customMargins);
+ fullFrameMargins(), customMargins());
qWarning("%s: %s", __FUNCTION__, qPrintable(warning));
}
} else {
@@ -2134,8 +2182,41 @@ void QWindowsWindow::handleMoved()
handleGeometryChange();
}
-void QWindowsWindow::handleResized(int wParam)
+void QWindowsWindow::handleResized(int wParam, LPARAM lParam)
{
+ /* Prevents borderless windows from covering the taskbar when maximized. */
+ if ((m_data.flags.testFlag(Qt::FramelessWindowHint)
+ || (m_data.flags.testFlag(Qt::CustomizeWindowHint) && !m_data.flags.testFlag(Qt::WindowTitleHint)))
+ && IsZoomed(m_data.hwnd)) {
+ const int resizedWidth = LOWORD(lParam);
+ const int resizedHeight = HIWORD(lParam);
+
+ const HMONITOR monitor = MonitorFromWindow(m_data.hwnd, MONITOR_DEFAULTTOPRIMARY);
+ MONITORINFO monitorInfo = {};
+ monitorInfo.cbSize = sizeof(MONITORINFO);
+ GetMonitorInfoW(monitor, &monitorInfo);
+
+ int correctLeft = monitorInfo.rcMonitor.left;
+ int correctTop = monitorInfo.rcMonitor.top;
+ int correctWidth = monitorInfo.rcWork.right - monitorInfo.rcWork.left;
+ int correctHeight = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top;
+
+ if (!m_data.flags.testFlag(Qt::FramelessWindowHint)) {
+ const int borderWidth = invisibleMargins(m_data.hwnd).left();
+ correctLeft -= borderWidth;
+ correctTop -= borderWidth;
+ correctWidth += borderWidth * 2;
+ correctHeight += borderWidth * 2;
+ }
+
+ if (resizedWidth != correctWidth || resizedHeight != correctHeight) {
+ qCDebug(lcQpaWindow) << __FUNCTION__ << "correcting: " << resizedWidth << "x"
+ << resizedHeight << " -> " << correctWidth << "x" << correctHeight;
+ SetWindowPos(m_data.hwnd, nullptr, correctLeft, correctTop, correctWidth, correctHeight,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+ }
+ }
+
switch (wParam) {
case SIZE_MAXHIDE: // Some other window affected.
case SIZE_MAXSHOW:
@@ -2183,10 +2264,10 @@ void QWindowsWindow::checkForScreenChanged(ScreenChangeMode mode)
return;
// For screens with different DPI: postpone until WM_DPICHANGE
// Check on currentScreen as it can be 0 when resuming a session (QTBUG-80436).
- if (mode == FromGeometryChange && currentScreen != nullptr
- && !equalDpi(currentScreen->logicalDpi(), newScreen->logicalDpi())) {
+ const bool changingDpi = !equalDpi(QDpi(savedDpi(), savedDpi()), newScreen->logicalDpi());
+ if (mode == FromGeometryChange && currentScreen != nullptr && changingDpi)
return;
- }
+
qCDebug(lcQpaWindow).noquote().nospace() << __FUNCTION__
<< ' ' << window() << " \"" << (currentScreen ? currentScreen->name() : QString())
<< "\"->\"" << newScreen->name() << '"';
@@ -2198,6 +2279,7 @@ void QWindowsWindow::handleGeometryChange()
{
const QRect previousGeometry = m_data.geometry;
m_data.geometry = geometry_sys();
+ updateFullFrameMargins();
QWindowSystemInterface::handleGeometryChange(window(), m_data.geometry);
// QTBUG-32121: OpenGL/normal windows (with exception of ANGLE
// which we no longer support in Qt 6) do not receive expose
@@ -2215,6 +2297,9 @@ void QWindowsWindow::handleGeometryChange()
if (testFlag(SynchronousGeometryChangeEvent))
QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
+ if (!testFlag(ResizeMoveActive))
+ updateRestoreGeometry();
+
if (!wasSync)
clearFlag(SynchronousGeometryChangeEvent);
qCDebug(lcQpaEvents) << __FUNCTION__ << this << window() << m_data.geometry;
@@ -2375,7 +2460,8 @@ QWindowsWindowData QWindowsWindow::setWindowFlags_sys(Qt::WindowFlags wt,
QWindowsWindowData result = m_data;
result.flags = creationData.flags;
result.embedded = creationData.embedded;
- result.hasFrame = (creationData.style & (WS_DLGFRAME | WS_THICKFRAME));
+ result.hasFrame = (creationData.style & (WS_DLGFRAME | WS_THICKFRAME))
+ && !(creationData.flags & Qt::FramelessWindowHint);
return result;
}
@@ -2389,6 +2475,14 @@ void QWindowsWindow::handleWindowStateChange(Qt::WindowStates state)
handleHidden();
QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents); // Tell QQuickWindow to stop rendering now.
} else {
+ if (state & Qt::WindowMaximized) {
+ WINDOWPLACEMENT windowPlacement{};
+ windowPlacement.length = sizeof(WINDOWPLACEMENT);
+ GetWindowPlacement(m_data.hwnd, &windowPlacement);
+ const RECT geometry = RECTfromQRect(m_data.restoreGeometry);
+ windowPlacement.rcNormalPosition = geometry;
+ SetWindowPlacement(m_data.hwnd, &windowPlacement);
+ }
// QTBUG-17548: We send expose events when receiving WM_Paint, but for
// layered windows and transient children, we won't receive any WM_Paint.
QWindow *w = window();
@@ -2412,6 +2506,11 @@ void QWindowsWindow::handleWindowStateChange(Qt::WindowStates state)
}
}
+void QWindowsWindow::updateRestoreGeometry()
+{
+ m_data.restoreGeometry = normalFrameGeometry(m_data.hwnd);
+}
+
void QWindowsWindow::setWindowState(Qt::WindowStates state)
{
if (m_data.hwnd) {
@@ -2456,11 +2555,7 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowStates newState)
if (stateChange & Qt::WindowFullScreen) {
if (newState & Qt::WindowFullScreen) {
-#ifndef Q_FLATTEN_EXPOSE
UINT newStyle = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_POPUP;
-#else
- UINT newStyle = WS_POPUP;
-#endif
// Save geometry and style to be restored when fullscreen
// is turned off again, since on Windows, it is not a real
// Window state but emulated by changing geometry and style.
@@ -2483,26 +2578,26 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowStates newState)
if (testFlag(HasBorderInFullScreen))
newStyle |= WS_BORDER;
setStyle(newStyle);
- // Use geometry of QWindow::screen() within creation or the virtual screen the
- // window is in (QTBUG-31166, QTBUG-30724).
- const QScreen *screen = window()->screen();
- if (!screen)
- screen = QGuiApplication::primaryScreen();
- const QRect r = screen ? QHighDpi::toNativePixels(screen->geometry(), window()) : m_savedFrameGeometry;
-
+ const HMONITOR monitor = MonitorFromWindow(m_data.hwnd, MONITOR_DEFAULTTONEAREST);
+ MONITORINFO monitorInfo = {};
+ monitorInfo.cbSize = sizeof(MONITORINFO);
+ GetMonitorInfoW(monitor, &monitorInfo);
+ const QRect screenGeometry(monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top,
+ monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left,
+ monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top);
if (newState & Qt::WindowMinimized) {
- setMinimizedGeometry(m_data.hwnd, r);
+ setMinimizedGeometry(m_data.hwnd, screenGeometry);
if (stateChange & Qt::WindowMaximized)
setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized);
} else {
const UINT swpf = SWP_FRAMECHANGED | SWP_NOACTIVATE;
const bool wasSync = testFlag(SynchronousGeometryChangeEvent);
setFlag(SynchronousGeometryChangeEvent);
- SetWindowPos(m_data.hwnd, HWND_TOP, r.left(), r.top(), r.width(), r.height(), swpf);
+ SetWindowPos(m_data.hwnd, HWND_TOP, screenGeometry.left(), screenGeometry.top(), screenGeometry.width(), screenGeometry.height(), swpf);
if (!wasSync)
clearFlag(SynchronousGeometryChangeEvent);
clearFlag(MaximizeToFullScreen);
- QWindowSystemInterface::handleGeometryChange(window(), r);
+ QWindowSystemInterface::handleGeometryChange(window(), screenGeometry);
QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
}
} else {
@@ -2594,6 +2689,9 @@ void QWindowsWindow::setExStyle(unsigned s) const
bool QWindowsWindow::windowEvent(QEvent *event)
{
switch (event->type()) {
+ case QEvent::ApplicationPaletteChange:
+ setDarkBorder(QWindowsTheme::instance()->colorScheme() == Qt::ColorScheme::Dark);
+ break;
case QEvent::WindowBlocked: // Blocked by another modal window.
setEnabled(false);
setFlag(BlockedByModal);
@@ -2619,10 +2717,17 @@ void QWindowsWindow::propagateSizeHints()
bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow *qWindow, const QMargins &margins)
{
auto *windowPos = reinterpret_cast<WINDOWPOS *>(message->lParam);
+ const QRect suggestedFrameGeometry(windowPos->x, windowPos->y,
+ windowPos->cx, windowPos->cy);
+ const QRect suggestedGeometry = suggestedFrameGeometry - margins;
// Tell Windows to discard the entire contents of the client area, as re-using
// parts of the client area would lead to jitter during resize.
- windowPos->flags |= SWP_NOCOPYBITS;
+ // Check the suggestedGeometry against the current one to only discard during
+ // resize, and not a plain move. We also look for SWP_NOSIZE since that, too,
+ // implies an identical size, and comparing QRects wouldn't work with null cx/cy
+ if (!(windowPos->flags & SWP_NOSIZE) && suggestedGeometry.size() != qWindow->geometry().size())
+ windowPos->flags |= SWP_NOCOPYBITS;
if ((windowPos->flags & SWP_NOZORDER) == 0) {
if (QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(qWindow)) {
@@ -2638,9 +2743,6 @@ bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow *
return false;
if (windowPos->flags & SWP_NOSIZE)
return false;
- const QRect suggestedFrameGeometry(windowPos->x, windowPos->y,
- windowPos->cx, windowPos->cy);
- const QRect suggestedGeometry = suggestedFrameGeometry - margins;
const QRectF correctedGeometryF = QPlatformWindow::closestAcceptableGeometry(qWindow, suggestedGeometry);
if (!correctedGeometryF.isValid())
return false;
@@ -2662,6 +2764,8 @@ bool QWindowsWindow::handleGeometryChanging(MSG *message) const
void QWindowsWindow::setFullFrameMargins(const QMargins &newMargins)
{
+ if (shouldOmitFrameAdjustment(m_data.flags, m_data.hwnd))
+ return;
if (m_data.fullFrameMargins != newMargins) {
qCDebug(lcQpaWindow) << __FUNCTION__ << window() << m_data.fullFrameMargins << "->" << newMargins;
m_data.fullFrameMargins = newMargins;
@@ -2679,12 +2783,46 @@ void QWindowsWindow::updateFullFrameMargins()
void QWindowsWindow::calculateFullFrameMargins()
{
+ if (shouldOmitFrameAdjustment(m_data.flags, m_data.hwnd))
+ return;
+
+ // QTBUG-113736: systemMargins depends on AdjustWindowRectExForDpi. This doesn't take into
+ // account possible external modifications to the titlebar, as with ExtendsContentIntoTitleBar()
+ // from the Windows App SDK. We can fix this by comparing the WindowRect (which includes the
+ // frame) to the ClientRect. If a 'typical' frame is detected, i.e. only the titlebar has been
+ // modified, we can safely adjust the frame by deducting the bottom margin to the total Y
+ // difference between the two rects, to get the actual size of the titlebar and prevent
+ // unwanted client area slicing.
+
+ RECT windowRect{};
+ RECT clientRect{};
+ GetWindowRect(handle(), &windowRect);
+ GetClientRect(handle(), &clientRect);
+
+ // QTBUG-117704 It is also possible that the user has manually removed the frame (for example
+ // by handling WM_NCCALCSIZE). If that is the case, i.e., the client area and the window area
+ // have identical sizes, we don't want to override the user-defined margins.
+
+ if (qrectFromRECT(windowRect).size() == qrectFromRECT(clientRect).size())
+ return;
+
// Normally obtained from WM_NCCALCSIZE. This calculation only works
// when no native menu is present.
const auto systemMargins = testFlag(DisableNonClientScaling)
? QWindowsGeometryHint::frameOnPrimaryScreen(window(), m_data.hwnd)
: frameMargins_sys();
- setFullFrameMargins(systemMargins + m_data.customMargins);
+ const QMargins actualMargins = systemMargins + customMargins();
+
+ const int yDiff = (windowRect.bottom - windowRect.top) - (clientRect.bottom - clientRect.top);
+ const bool typicalFrame = (actualMargins.left() == actualMargins.right())
+ && (actualMargins.right() == actualMargins.bottom());
+
+ const QMargins adjustedMargins = typicalFrame ?
+ QMargins(actualMargins.left(), (yDiff - actualMargins.bottom()),
+ actualMargins.right(), actualMargins.bottom())
+ : actualMargins;
+
+ setFullFrameMargins(adjustedMargins);
}
QMargins QWindowsWindow::frameMargins() const
@@ -2697,6 +2835,8 @@ QMargins QWindowsWindow::frameMargins() const
QMargins QWindowsWindow::fullFrameMargins() const
{
+ if (shouldOmitFrameAdjustment(m_data.flags, m_data.hwnd))
+ return {};
return m_data.fullFrameMargins;
}
@@ -2763,40 +2903,75 @@ void QWindowsWindow::setMask(const QRegion &region)
void QWindowsWindow::requestActivateWindow()
{
qCDebug(lcQpaWindow) << __FUNCTION__ << this << window();
- // 'Active' state handling is based in focus since it needs to work for
- // child windows as well.
- if (m_data.hwnd) {
- const DWORD currentThread = GetCurrentThreadId();
- bool attached = false;
- DWORD foregroundThread = 0;
-
- // QTBUG-14062, QTBUG-37435: Windows normally only flashes the taskbar entry
- // when activating windows of inactive applications. Attach to the input of the
- // currently active window while setting the foreground window to always activate
- // the window when desired.
- const auto activationBehavior = QWindowsIntegration::instance()->windowActivationBehavior();
- if (QGuiApplication::applicationState() != Qt::ApplicationActive
- && activationBehavior == QWindowsApplication::AlwaysActivateWindow) {
- if (const HWND foregroundWindow = GetForegroundWindow()) {
- foregroundThread = GetWindowThreadProcessId(foregroundWindow, nullptr);
- if (foregroundThread && foregroundThread != currentThread)
- attached = AttachThreadInput(foregroundThread, currentThread, TRUE) == TRUE;
- if (attached) {
- if (!window()->flags().testFlag(Qt::WindowStaysOnBottomHint)
- && !window()->flags().testFlag(Qt::WindowStaysOnTopHint)
- && window()->type() != Qt::ToolTip) {
- const UINT swpFlags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER;
- SetWindowPos(m_data.hwnd, HWND_TOPMOST, 0, 0, 0, 0, swpFlags);
- SetWindowPos(m_data.hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, swpFlags);
- }
- }
- }
- }
+
+ if (!m_data.hwnd)
+ return;
+
+ const auto activationBehavior = QWindowsIntegration::instance()->windowActivationBehavior();
+ if (QGuiApplication::applicationState() == Qt::ApplicationActive
+ || activationBehavior != QWindowsApplication::AlwaysActivateWindow) {
SetForegroundWindow(m_data.hwnd);
SetFocus(m_data.hwnd);
- if (attached)
- AttachThreadInput(foregroundThread, currentThread, FALSE);
+ return;
+ }
+
+ // Force activate this window. The following code will bring the window to the
+ // foreground and activate it. If the window is hidden, it will show up. If
+ // the window is minimized, it will restore to the previous position.
+
+ // But first we need some sanity checks.
+ if (m_data.flags & Qt::WindowStaysOnBottomHint) {
+ qCWarning(lcQpaWindow) <<
+ "Windows with Qt::WindowStaysOnBottomHint can't be brought to the foreground.";
+ return;
+ }
+ if (m_data.flags & Qt::WindowStaysOnTopHint) {
+ qCWarning(lcQpaWindow) <<
+ "Windows with Qt::WindowStaysOnTopHint will always be on the foreground.";
+ return;
+ }
+ if (window()->type() == Qt::ToolTip) {
+ qCWarning(lcQpaWindow) << "ToolTip windows should not be activated.";
+ return;
+ }
+
+ // We need to show the window first, otherwise we won't be able to bring it to front.
+ if (!IsWindowVisible(m_data.hwnd))
+ ShowWindow(m_data.hwnd, SW_SHOW);
+
+ if (IsIconic(m_data.hwnd)) {
+ ShowWindow(m_data.hwnd, SW_RESTORE);
+ // When the window is restored, it will always become the foreground window.
+ // So return early here, we don't need the following code to bring it to front.
+ return;
}
+
+ // OK, our window is not minimized, so now we will try to bring it to front manually.
+ const HWND oldForegroundWindow = GetForegroundWindow();
+ if (!oldForegroundWindow) // It may be NULL, according to MS docs.
+ return;
+
+ // First try to send a message to the current foreground window to check whether
+ // it is currently hanging or not.
+ if (SendMessageTimeoutW(oldForegroundWindow, WM_NULL, 0, 0,
+ SMTO_BLOCK | SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG, 1000, nullptr) == 0) {
+ qCWarning(lcQpaWindow) << "The foreground window hangs, can't activate current window.";
+ return;
+ }
+
+ const DWORD windowThreadProcessId = GetWindowThreadProcessId(oldForegroundWindow, nullptr);
+ const DWORD currentThreadId = GetCurrentThreadId();
+
+ AttachThreadInput(windowThreadProcessId, currentThreadId, TRUE);
+ const auto cleanup = qScopeGuard([windowThreadProcessId, currentThreadId](){
+ AttachThreadInput(windowThreadProcessId, currentThreadId, FALSE);
+ });
+
+ BringWindowToTop(m_data.hwnd);
+
+ // Activate the window too. This will force us to the virtual desktop this
+ // window is on, if it's on another virtual desktop.
+ SetActiveWindow(m_data.hwnd);
}
bool QWindowsWindow::setKeyboardGrabEnabled(bool grab)
@@ -2894,36 +3069,6 @@ void QWindowsWindow::setFrameStrutEventsEnabled(bool enabled)
void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const
{
QWindowsGeometryHint::applyToMinMaxInfo(window(), fullFrameMargins(), mmi);
-
- // This block fixes QTBUG-8361, QTBUG-4362: Frameless/title-less windows shouldn't cover the
- // taskbar when maximized
- if (m_data.flags.testFlag(Qt::FramelessWindowHint)
- || (m_data.flags.testFlag(Qt::CustomizeWindowHint) && !m_data.flags.testFlag(Qt::WindowTitleHint))) {
- if (QPlatformScreen *currentScreen = screen()) {
- const QRect geometry = currentScreen->geometry();
- const QRect availableGeometry = currentScreen->availableGeometry();
- mmi->ptMaxSize.y = availableGeometry.height();
-
- // Width, because you can have the taskbar on the sides too.
- mmi->ptMaxSize.x = availableGeometry.width();
-
- // If you have the taskbar on top, or on the left you don't want it at (0,0):
- QPoint availablePositionDiff = availableGeometry.topLeft() - geometry.topLeft();
- mmi->ptMaxPosition.x = availablePositionDiff.x();
- mmi->ptMaxPosition.y = availablePositionDiff.y();
- if (!m_data.flags.testFlag(Qt::FramelessWindowHint)) {
- const int borderWidth = invisibleMargins(m_data.hwnd).left();
- mmi->ptMaxSize.x += borderWidth * 2;
- mmi->ptMaxSize.y += borderWidth * 2;
- mmi->ptMaxTrackSize = mmi->ptMaxSize;
- mmi->ptMaxPosition.x -= borderWidth;
- mmi->ptMaxPosition.y -= borderWidth;
- }
- } else {
- qWarning("screen() returned a null screen");
- }
- }
-
qCDebug(lcQpaWindow) << __FUNCTION__ << window() << *mmi;
}
@@ -3148,7 +3293,7 @@ static bool queryDarkBorder(HWND hwnd)
SUCCEEDED(DwmGetWindowAttribute(hwnd, DwmwaUseImmersiveDarkMode, &result, sizeof(result)))
|| SUCCEEDED(DwmGetWindowAttribute(hwnd, DwmwaUseImmersiveDarkModeBefore20h1, &result, sizeof(result)));
if (!ok)
- qWarning("%s: Unable to retrieve dark window border setting.", __FUNCTION__);
+ qCWarning(lcQpaWindow, "%s: Unable to retrieve dark window border setting.", __FUNCTION__);
return result == TRUE;
}
@@ -3159,14 +3304,18 @@ bool QWindowsWindow::setDarkBorderToWindow(HWND hwnd, bool d)
SUCCEEDED(DwmSetWindowAttribute(hwnd, DwmwaUseImmersiveDarkMode, &darkBorder, sizeof(darkBorder)))
|| SUCCEEDED(DwmSetWindowAttribute(hwnd, DwmwaUseImmersiveDarkModeBefore20h1, &darkBorder, sizeof(darkBorder)));
if (!ok)
- qWarning("%s: Unable to set dark window border.", __FUNCTION__);
+ qCWarning(lcQpaWindow, "%s: Unable to set %s window border.", __FUNCTION__, d ? "dark" : "light");
return ok;
}
void QWindowsWindow::setDarkBorder(bool d)
{
- if (shouldApplyDarkFrame(window()) && queryDarkBorder(m_data.hwnd) != d)
- setDarkBorderToWindow(m_data.hwnd, d);
+ // respect explicit opt-out and incompatible palettes or styles
+ d = d && shouldApplyDarkFrame(window());
+ if (queryDarkBorder(m_data.hwnd) == d)
+ return;
+
+ setDarkBorderToWindow(m_data.hwnd, d);
}
QWindowsMenuBar *QWindowsWindow::menuBar() const
@@ -3179,6 +3328,13 @@ void QWindowsWindow::setMenuBar(QWindowsMenuBar *mb)
m_menuBar = mb;
}
+QMargins QWindowsWindow::customMargins() const
+{
+ if (m_data.flags & Qt::FramelessWindowHint)
+ return {};
+ return m_data.customMargins;
+}
+
/*!
\brief Sets custom margins to be added to the default margins determined by
the windows style in the handling of the WM_NCCALCSIZE message.
@@ -3191,6 +3347,10 @@ void QWindowsWindow::setMenuBar(QWindowsMenuBar *mb)
void QWindowsWindow::setCustomMargins(const QMargins &newCustomMargins)
{
+ if (m_data.flags & Qt::FramelessWindowHint) {
+ qCWarning(lcQpaWindow) << "You should not set custom margins for a frameless window.";
+ return;
+ }
if (newCustomMargins != m_data.customMargins) {
const QMargins oldCustomMargins = m_data.customMargins;
m_data.customMargins = newCustomMargins;
@@ -3282,24 +3442,6 @@ void QWindowsWindow::registerTouchWindow()
qErrnoWarning("RegisterTouchWindow() failed for window '%s'.", qPrintable(window()->objectName()));
}
-void QWindowsWindow::aboutToMakeCurrent()
-{
-#ifndef QT_NO_OPENGL
- // For RasterGLSurface windows, that become OpenGL windows dynamically, it might be
- // time to set up some GL specifics. This is particularly important for layered
- // windows (WS_EX_LAYERED due to alpha > 0).
- const bool isCompositing = qt_window_private(window())->compositing;
- if (isCompositing != testFlag(Compositing)) {
- if (isCompositing)
- setFlag(Compositing);
- else
- clearFlag(Compositing);
-
- updateGLWindowSettings(window(), m_data.hwnd, m_data.flags, m_opacity);
- }
-#endif
-}
-
void QWindowsWindow::setHasBorderInFullScreenStatic(QWindow *window, bool border)
{
if (QPlatformWindow *handle = window->handle())
diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h
index 29dfbdb856..024711e7f3 100644
--- a/src/plugins/platforms/windows/qwindowswindow.h
+++ b/src/plugins/platforms/windows/qwindowswindow.h
@@ -78,6 +78,7 @@ struct QWindowsWindowData
{
Qt::WindowFlags flags;
QRect geometry;
+ QRect restoreGeometry;
QMargins fullFrameMargins; // Do not use directly for windows, see FrameDirty.
QMargins customMargins; // User-defined, additional frame for NCCALCSIZE
HWND hwnd = nullptr;
@@ -218,6 +219,8 @@ public:
void setGeometry(const QRect &rect) override;
QRect geometry() const override { return m_data.geometry; }
QRect normalGeometry() const override;
+ QRect restoreGeometry() const { return m_data.restoreGeometry; }
+ void updateRestoreGeometry();
void setVisible(bool visible) override;
bool isVisible() const;
@@ -272,7 +275,7 @@ public:
QWindowsMenuBar *menuBar() const;
void setMenuBar(QWindowsMenuBar *mb);
- QMargins customMargins() const override { return m_data.customMargins; }
+ QMargins customMargins() const override;
void setCustomMargins(const QMargins &m) override;
void setStyle(unsigned s) const;
@@ -281,7 +284,7 @@ public:
bool handleWmPaint(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result);
void handleMoved();
- void handleResized(int wParam);
+ void handleResized(int wParam, LPARAM lParam);
void handleHidden();
void handleCompositionSettingsChanged();
void handleDpiScaledSize(WPARAM wParam, LPARAM lParam, LRESULT *result);
@@ -296,6 +299,7 @@ public:
static inline void *userDataOf(HWND hwnd);
static inline void setUserDataOf(HWND hwnd, void *ud);
+ static bool hasNoNativeFrame(HWND hwnd, Qt::WindowFlags flags);
static bool setWindowLayered(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, qreal opacity);
bool isLayered() const;
@@ -320,7 +324,6 @@ public:
void *surface(void *nativeConfig, int *err);
void invalidateSurface() override;
- void aboutToMakeCurrent();
void setAlertState(bool enabled) override;
bool isAlertState() const override { return testFlag(AlertState); }
@@ -342,6 +345,7 @@ public:
void setSavedDpi(int dpi) { m_savedDpi = dpi; }
int savedDpi() const { return m_savedDpi; }
+ qreal dpiRelativeScale(const UINT dpi) const;
private:
inline void show_sys() const;
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp
index 001cb8505b..1abb412ccd 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp
@@ -5,6 +5,7 @@
#if QT_CONFIG(accessibility)
#include "qwindowsuiaaccessibility.h"
+#include "qwindowsuiautomation.h"
#include "qwindowsuiamainprovider.h"
#include "qwindowsuiautils.h"
@@ -14,13 +15,13 @@
#include <QtGui/private/qguiapplication_p.h>
#include <QtCore/qt_windows.h>
#include <qpa/qplatformintegration.h>
-#include <QtGui/private/qwindowsuiawrapper_p.h>
#include <QtCore/private/qwinregistry_p.h>
QT_BEGIN_NAMESPACE
using namespace QWindowsUiAutomation;
+using namespace Qt::Literals::StringLiterals;
bool QWindowsUiaAccessibility::m_accessibleActive = false;
@@ -46,7 +47,7 @@ bool QWindowsUiaAccessibility::handleWmGetObject(HWND hwnd, WPARAM wParam, LPARA
if (QWindow *window = QWindowsContext::instance()->findWindow(hwnd)) {
if (QAccessibleInterface *accessible = window->accessibleRoot()) {
QWindowsUiaMainProvider *provider = QWindowsUiaMainProvider::providerForAccessible(accessible);
- *lResult = QWindowsUiaWrapper::instance()->returnRawElementProvider(hwnd, wParam, lParam, provider);
+ *lResult = UiaReturnRawElementProvider(hwnd, wParam, lParam, provider);
return true;
}
}
@@ -78,8 +79,8 @@ static QString alertSound(const QObject *object)
static QString soundFileName(const QString &soundName)
{
- const QString key = QStringLiteral("AppEvents\\Schemes\\Apps\\.Default\\")
- + soundName + QStringLiteral("\\.Current");
+ const QString key = "AppEvents\\Schemes\\Apps\\.Default\\"_L1
+ + soundName + "\\.Current"_L1;
return QWinRegistryKey(HKEY_CURRENT_USER, key).stringValue(L"");
}
@@ -97,11 +98,7 @@ void QWindowsUiaAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event
if (!event)
return;
- // Ignore events sent before the first UI Automation
- // request or while QAccessible is being activated.
- if (!m_accessibleActive)
- return;
-
+ // Always handle system sound events
switch (event->type()) {
case QAccessible::PopupMenuStart:
playSystemSound(QStringLiteral("MenuPopup"));
@@ -116,16 +113,17 @@ void QWindowsUiaAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event
break;
}
- QAccessibleInterface *accessible = event->accessibleInterface();
- if (!isActive() || !accessible || !accessible->isValid())
+ // Ignore events sent before the first UI Automation
+ // request or while QAccessible is being activated.
+ if (!m_accessibleActive)
return;
- // Ensures QWindowsUiaWrapper is properly initialized.
- if (!QWindowsUiaWrapper::instance()->ready())
+ QAccessibleInterface *accessible = event->accessibleInterface();
+ if (!isActive() || !accessible || !accessible->isValid())
return;
// No need to do anything when nobody is listening.
- if (!QWindowsUiaWrapper::instance()->clientsAreListening())
+ if (!UiaClientsAreListening())
return;
switch (event->type()) {
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.h
index 1813bb4d89..2e8ee585da 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.h
@@ -7,6 +7,7 @@
#include <QtGui/qtguiglobal.h>
#if QT_CONFIG(accessibility)
+#include <QtCore/qt_windows.h>
#include "qwindowscontext.h"
#include <qpa/qplatformaccessibility.h>
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiabaseprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiabaseprovider.h
index c899b4096e..032679ab10 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiabaseprovider.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiabaseprovider.h
@@ -10,8 +10,8 @@
#include <QtGui/qaccessible.h>
#include <QtCore/qpointer.h>
-#include <qwindowscombase.h>
-#include <QtGui/private/qwindowsuiawrapper_p.h>
+#include "qwindowsuiautomation.h"
+#include <QtCore/private/qcomobject_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaexpandcollapseprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiaexpandcollapseprovider.h
index 49b37ba295..b384eb521c 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaexpandcollapseprovider.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaexpandcollapseprovider.h
@@ -13,7 +13,7 @@ QT_BEGIN_NAMESPACE
// Implements the Expand/Collapse control pattern provider. Used for menu items with submenus.
class QWindowsUiaExpandCollapseProvider : public QWindowsUiaBaseProvider,
- public QWindowsComBase<IExpandCollapseProvider>
+ public QComObject<IExpandCollapseProvider>
{
Q_DISABLE_COPY_MOVE(QWindowsUiaExpandCollapseProvider)
public:
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiagriditemprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiagriditemprovider.h
index b501616966..289a867869 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiagriditemprovider.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiagriditemprovider.h
@@ -13,7 +13,7 @@ QT_BEGIN_NAMESPACE
// Implements the Grid Item control pattern provider. Used by items within a table/tree.
class QWindowsUiaGridItemProvider : public QWindowsUiaBaseProvider,
- public QWindowsComBase<IGridItemProvider>
+ public QComObject<IGridItemProvider>
{
Q_DISABLE_COPY_MOVE(QWindowsUiaGridItemProvider)
public:
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiagridprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiagridprovider.h
index b905133f6b..d33bbd0429 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiagridprovider.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiagridprovider.h
@@ -12,8 +12,7 @@
QT_BEGIN_NAMESPACE
// Implements the Grid control pattern provider. Used by tables/trees.
-class QWindowsUiaGridProvider : public QWindowsUiaBaseProvider,
- public QWindowsComBase<IGridProvider>
+class QWindowsUiaGridProvider : public QWindowsUiaBaseProvider, public QComObject<IGridProvider>
{
Q_DISABLE_COPY_MOVE(QWindowsUiaGridProvider)
public:
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiainvokeprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiainvokeprovider.h
index eca7e73039..ec006c673e 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiainvokeprovider.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiainvokeprovider.h
@@ -12,8 +12,7 @@
QT_BEGIN_NAMESPACE
// Implements the Invoke control pattern provider.
-class QWindowsUiaInvokeProvider : public QWindowsUiaBaseProvider,
- public QWindowsComBase<IInvokeProvider>
+class QWindowsUiaInvokeProvider : public QWindowsUiaBaseProvider, public QComObject<IInvokeProvider>
{
Q_DISABLE_COPY_MOVE(QWindowsUiaInvokeProvider)
public:
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
index 2d5d91a7ea..95ddbcced6 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
@@ -18,7 +18,6 @@
#include "qwindowsuiagriditemprovider.h"
#include "qwindowsuiawindowprovider.h"
#include "qwindowsuiaexpandcollapseprovider.h"
-#include "qwindowscombase.h"
#include "qwindowscontext.h"
#include "qwindowsuiautils.h"
#include "qwindowsuiaprovidercache.h"
@@ -27,6 +26,7 @@
#include <QtGui/qaccessible.h>
#include <QtGui/qguiapplication.h>
#include <QtGui/qwindow.h>
+#include <qpa/qplatforminputcontextfactory_p.h>
#if !defined(Q_CC_BOR) && !defined (Q_CC_GNU)
#include <comdef.h>
@@ -38,10 +38,13 @@ QT_BEGIN_NAMESPACE
using namespace QWindowsUiAutomation;
+QMutex QWindowsUiaMainProvider::m_mutex;
// Returns a cached instance of the provider for a specific accessible interface.
QWindowsUiaMainProvider *QWindowsUiaMainProvider::providerForAccessible(QAccessibleInterface *accessible)
{
+ QMutexLocker locker(&m_mutex);
+
if (!accessible)
return nullptr;
@@ -58,9 +61,8 @@ QWindowsUiaMainProvider *QWindowsUiaMainProvider::providerForAccessible(QAccessi
return provider;
}
-QWindowsUiaMainProvider::QWindowsUiaMainProvider(QAccessibleInterface *a, int initialRefCount)
- : QWindowsUiaBaseProvider(QAccessible::uniqueId(a)),
- m_ref(initialRefCount)
+QWindowsUiaMainProvider::QWindowsUiaMainProvider(QAccessibleInterface *a)
+ : QWindowsUiaBaseProvider(QAccessible::uniqueId(a))
{
}
@@ -71,12 +73,13 @@ QWindowsUiaMainProvider::~QWindowsUiaMainProvider()
void QWindowsUiaMainProvider::notifyFocusChange(QAccessibleEvent *event)
{
if (QAccessibleInterface *accessible = event->accessibleInterface()) {
- // If this is a table/tree/list, raise event for the focused cell/item instead.
- if (accessible->tableInterface())
+ // If this is a complex element, raise event for the focused child instead.
+ if (accessible->childCount()) {
if (QAccessibleInterface *child = accessible->focusChild())
accessible = child;
+ }
if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible))
- QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_AutomationFocusChangedEventId);
+ UiaRaiseAutomationEvent(provider, UIA_AutomationFocusChangedEventId);
}
}
@@ -93,7 +96,7 @@ void QWindowsUiaMainProvider::notifyStateChange(QAccessibleStateChangeEvent *eve
if (accessible->state().checked)
toggleState = accessible->state().checkStateMixed ? ToggleState_Indeterminate : ToggleState_On;
setVariantI4(toggleState, &newVal);
- QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(provider, UIA_ToggleToggleStatePropertyId, oldVal, newVal);
+ UiaRaiseAutomationPropertyChangedEvent(provider, UIA_ToggleToggleStatePropertyId, oldVal, newVal);
}
}
}
@@ -102,9 +105,13 @@ void QWindowsUiaMainProvider::notifyStateChange(QAccessibleStateChangeEvent *eve
// Notifies window opened/closed.
if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
if (accessible->state().active) {
- QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_Window_WindowOpenedEventId);
+ UiaRaiseAutomationEvent(provider, UIA_Window_WindowOpenedEventId);
+ if (QAccessibleInterface *focused = accessible->focusChild()) {
+ if (QWindowsUiaMainProvider *focusedProvider = providerForAccessible(focused))
+ UiaRaiseAutomationEvent(focusedProvider, UIA_AutomationFocusChangedEventId);
+ }
} else {
- QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_Window_WindowClosedEventId);
+ UiaRaiseAutomationEvent(provider, UIA_Window_WindowClosedEventId);
}
}
}
@@ -133,27 +140,11 @@ void QWindowsUiaMainProvider::notifyValueChange(QAccessibleValueChangeEvent *eve
}
if (event->value().typeId() == QMetaType::QString) {
if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
-
- // Tries to notify the change using UiaRaiseNotificationEvent(), which is only available on
- // Windows 10 version 1709 or newer. Otherwise uses UiaRaiseAutomationPropertyChangedEvent().
-
- BSTR displayString = bStrFromQString(event->value().toString());
- BSTR activityId = bStrFromQString(QString());
-
- HRESULT hr = QWindowsUiaWrapper::instance()->raiseNotificationEvent(provider, NotificationKind_Other,
- NotificationProcessing_ImportantMostRecent,
- displayString, activityId);
-
- ::SysFreeString(displayString);
- ::SysFreeString(activityId);
-
- if (hr == static_cast<HRESULT>(UIA_E_NOTSUPPORTED)) {
- VARIANT oldVal, newVal;
- clearVariant(&oldVal);
- setVariantString(event->value().toString(), &newVal);
- QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(provider, UIA_ValueValuePropertyId, oldVal, newVal);
- ::SysFreeString(newVal.bstrVal);
- }
+ // Notifies changes in string values.
+ VARIANT oldVal, newVal;
+ clearVariant(&oldVal);
+ setVariantString(event->value().toString(), &newVal);
+ UiaRaiseAutomationPropertyChangedEvent(provider, UIA_ValueValuePropertyId, oldVal, newVal);
}
} else if (QAccessibleValueInterface *valueInterface = accessible->valueInterface()) {
if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
@@ -161,7 +152,7 @@ void QWindowsUiaMainProvider::notifyValueChange(QAccessibleValueChangeEvent *eve
VARIANT oldVal, newVal;
clearVariant(&oldVal);
setVariantDouble(valueInterface->currentValue().toDouble(), &newVal);
- QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(provider, UIA_RangeValueValuePropertyId, oldVal, newVal);
+ UiaRaiseAutomationPropertyChangedEvent(provider, UIA_RangeValueValuePropertyId, oldVal, newVal);
}
}
}
@@ -177,7 +168,7 @@ void QWindowsUiaMainProvider::notifyNameChange(QAccessibleEvent *event)
VARIANT oldVal, newVal;
clearVariant(&oldVal);
setVariantString(accessible->text(QAccessible::Name), &newVal);
- QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(provider, UIA_NamePropertyId, oldVal, newVal);
+ UiaRaiseAutomationPropertyChangedEvent(provider, UIA_NamePropertyId, oldVal, newVal);
::SysFreeString(newVal.bstrVal);
}
}
@@ -188,7 +179,7 @@ void QWindowsUiaMainProvider::notifySelectionChange(QAccessibleEvent *event)
{
if (QAccessibleInterface *accessible = event->accessibleInterface()) {
if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
- QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_SelectionItem_ElementSelectedEventId);
+ UiaRaiseAutomationEvent(provider, UIA_SelectionItem_ElementSelectedEventId);
}
}
}
@@ -200,13 +191,13 @@ void QWindowsUiaMainProvider::notifyTextChange(QAccessibleEvent *event)
if (accessible->textInterface()) {
if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
if (event->type() == QAccessible::TextSelectionChanged) {
- QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_Text_TextSelectionChangedEventId);
+ UiaRaiseAutomationEvent(provider, UIA_Text_TextSelectionChangedEventId);
} else if (event->type() == QAccessible::TextCaretMoved) {
if (!accessible->state().readOnly) {
- QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_Text_TextSelectionChangedEventId);
+ UiaRaiseAutomationEvent(provider, UIA_Text_TextSelectionChangedEventId);
}
} else {
- QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_Text_TextChangedEventId);
+ UiaRaiseAutomationEvent(provider, UIA_Text_TextChangedEventId);
}
}
}
@@ -215,31 +206,26 @@ void QWindowsUiaMainProvider::notifyTextChange(QAccessibleEvent *event)
HRESULT STDMETHODCALLTYPE QWindowsUiaMainProvider::QueryInterface(REFIID iid, LPVOID *iface)
{
- if (!iface)
- return E_INVALIDARG;
- *iface = nullptr;
+ HRESULT result = QComObject::QueryInterface(iid, iface);
- QAccessibleInterface *accessible = accessibleInterface();
-
- const bool result = qWindowsComQueryUnknownInterfaceMulti<IRawElementProviderSimple>(this, iid, iface)
- || qWindowsComQueryInterface<IRawElementProviderSimple>(this, iid, iface)
- || qWindowsComQueryInterface<IRawElementProviderFragment>(this, iid, iface)
- || (accessible && hwndForAccessible(accessible) && qWindowsComQueryInterface<IRawElementProviderFragmentRoot>(this, iid, iface));
- return result ? S_OK : E_NOINTERFACE;
-}
+ if (SUCCEEDED(result) && iid == __uuidof(IRawElementProviderFragmentRoot)) {
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (accessible && hwndForAccessible(accessible)) {
+ result = S_OK;
+ } else {
+ result = E_NOINTERFACE;
+ iface = nullptr;
+ }
+ }
-ULONG QWindowsUiaMainProvider::AddRef()
-{
- return ++m_ref;
+ return result;
}
ULONG STDMETHODCALLTYPE QWindowsUiaMainProvider::Release()
{
- if (!--m_ref) {
- delete this;
- return 0;
- }
- return m_ref;
+ QMutexLocker locker(&m_mutex);
+
+ return QComObject::Release();
}
HRESULT QWindowsUiaMainProvider::get_ProviderOptions(ProviderOptions *pRetVal)
@@ -294,15 +280,18 @@ HRESULT QWindowsUiaMainProvider::GetPatternProvider(PATTERNID idPattern, IUnknow
*pRetVal = new QWindowsUiaToggleProvider(id());
break;
case UIA_SelectionPatternId:
- // Lists of items.
- if (accessible->role() == QAccessible::List
+ case UIA_SelectionPattern2Id:
+ // Selections via QAccessibleSelectionInterface or lists of items.
+ if (accessible->selectionInterface()
+ || accessible->role() == QAccessible::List
|| accessible->role() == QAccessible::PageTabList) {
*pRetVal = new QWindowsUiaSelectionProvider(id());
}
break;
case UIA_SelectionItemPatternId:
- // Items within a list and radio buttons.
- if ((accessible->role() == QAccessible::RadioButton)
+ // Parent supports selection interface or items within a list and radio buttons.
+ if ((accessible->parent() && accessible->parent()->selectionInterface())
+ || (accessible->role() == QAccessible::RadioButton)
|| (accessible->role() == QAccessible::ListItem)
|| (accessible->role() == QAccessible::PageTab)) {
*pRetVal = new QWindowsUiaSelectionItemProvider(id());
@@ -359,6 +348,28 @@ HRESULT QWindowsUiaMainProvider::GetPatternProvider(PATTERNID idPattern, IUnknow
return S_OK;
}
+void QWindowsUiaMainProvider::fillVariantArrayForRelation(QAccessibleInterface* accessible,
+ QAccessible::Relation relation, VARIANT *pRetVal)
+{
+ Q_ASSERT(accessible);
+
+ typedef QPair<QAccessibleInterface*, QAccessible::Relation> RelationPair;
+ const QList<RelationPair> relationInterfaces = accessible->relations(relation);
+ if (relationInterfaces.empty())
+ return;
+
+ SAFEARRAY *elements = SafeArrayCreateVector(VT_UNKNOWN, 0, relationInterfaces.size());
+ for (LONG i = 0; i < relationInterfaces.size(); ++i) {
+ if (QWindowsUiaMainProvider *childProvider = QWindowsUiaMainProvider::providerForAccessible(relationInterfaces.at(i).first)) {
+ SafeArrayPutElement(elements, &i, static_cast<IRawElementProviderSimple*>(childProvider));
+ childProvider->Release();
+ }
+ }
+
+ pRetVal->vt = VT_UNKNOWN | VT_ARRAY;
+ pRetVal->parray = elements;
+}
+
HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pRetVal)
{
qCDebug(lcQpaUiAutomation) << __FUNCTION__ << idProp;
@@ -393,6 +404,15 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR
setVariantString(className, pRetVal);
}
break;
+ case UIA_DescribedByPropertyId:
+ fillVariantArrayForRelation(accessible, QAccessible::DescriptionFor, pRetVal);
+ break;
+ case UIA_FlowsFromPropertyId:
+ fillVariantArrayForRelation(accessible, QAccessible::FlowsTo, pRetVal);
+ break;
+ case UIA_FlowsToPropertyId:
+ fillVariantArrayForRelation(accessible, QAccessible::FlowsFrom, pRetVal);
+ break;
case UIA_FrameworkIdPropertyId:
setVariantString(QStringLiteral("Qt"), pRetVal);
break;
@@ -406,7 +426,7 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR
// The native OSK should be disabled if the Qt OSK is in use,
// or if disabled via application attribute.
- static bool imModuleEmpty = qEnvironmentVariableIsEmpty("QT_IM_MODULE");
+ static bool imModuleEmpty = QPlatformInputContextFactory::requested().isEmpty();
bool nativeVKDisabled = QCoreApplication::testAttribute(Qt::AA_DisableNativeVirtualKeyboard);
// If we want to disable the native OSK auto-showing
@@ -509,7 +529,7 @@ HRESULT QWindowsUiaMainProvider::get_HostRawElementProvider(IRawElementProviderS
// Returns a host provider only for controls associated with a native window handle. Others should return NULL.
if (QAccessibleInterface *accessible = accessibleInterface()) {
if (HWND hwnd = hwndForAccessible(accessible)) {
- return QWindowsUiaWrapper::instance()->hostProviderFromHwnd(hwnd, pRetVal);
+ return UiaHostProviderFromHwnd(hwnd, pRetVal);
}
}
return S_OK;
@@ -699,26 +719,18 @@ HRESULT QWindowsUiaMainProvider::ElementProviderFromPoint(double x, double y, IR
QPoint point;
nativeUiaPointToPoint(uiaPoint, window, &point);
- if (auto targetacc = accessible->childAt(point.x(), point.y())) {
- auto acc = accessible->childAt(point.x(), point.y());
- // Reject the cases where childAt() returns a different instance in each call for the same
- // element (e.g., QAccessibleTree), as it causes an endless loop with Youdao Dictionary installed.
- if (targetacc == acc) {
- // Controls can be embedded within grouping elements. By default returns the innermost control.
- while (acc) {
- targetacc = acc;
- // For accessibility tools it may be better to return the text element instead of its subcomponents.
- if (targetacc->textInterface()) break;
- acc = targetacc->childAt(point.x(), point.y());
- if (acc != targetacc->childAt(point.x(), point.y())) {
- qCDebug(lcQpaUiAutomation) << "Non-unique childAt() for" << targetacc;
- break;
- }
- }
- *pRetVal = providerForAccessible(targetacc);
- } else {
- qCDebug(lcQpaUiAutomation) << "Non-unique childAt() for" << accessible;
+ QAccessibleInterface *targetacc = accessible->childAt(point.x(), point.y());
+
+ if (targetacc) {
+ QAccessibleInterface *acc = targetacc;
+ // Controls can be embedded within grouping elements. By default returns the innermost control.
+ while (acc) {
+ targetacc = acc;
+ // For accessibility tools it may be better to return the text element instead of its subcomponents.
+ if (targetacc->textInterface()) break;
+ acc = acc->childAt(point.x(), point.y());
}
+ *pRetVal = providerForAccessible(targetacc);
}
return S_OK;
}
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h
index 5659a28e35..99db0ed318 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h
@@ -11,6 +11,7 @@
#include <QtCore/qpointer.h>
#include <QtCore/qsharedpointer.h>
+#include <QtCore/qmutex.h>
#include <QtCore/qt_windows.h>
#include <QtGui/qaccessible.h>
@@ -19,15 +20,13 @@ QT_BEGIN_NAMESPACE
// The main UI Automation class.
class QWindowsUiaMainProvider :
public QWindowsUiaBaseProvider,
- public IRawElementProviderSimple,
- public IRawElementProviderFragment,
- public IRawElementProviderFragmentRoot
+ public QComObject<IRawElementProviderSimple, IRawElementProviderFragment, IRawElementProviderFragmentRoot>
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(QWindowsUiaMainProvider)
public:
static QWindowsUiaMainProvider *providerForAccessible(QAccessibleInterface *accessible);
- explicit QWindowsUiaMainProvider(QAccessibleInterface *a, int initialRefCount = 1);
+ explicit QWindowsUiaMainProvider(QAccessibleInterface *a);
virtual ~QWindowsUiaMainProvider();
static void notifyFocusChange(QAccessibleEvent *event);
static void notifyStateChange(QAccessibleStateChangeEvent *event);
@@ -38,7 +37,6 @@ public:
// IUnknown
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, LPVOID *iface) override;
- ULONG STDMETHODCALLTYPE AddRef() override;
ULONG STDMETHODCALLTYPE Release() override;
// IRawElementProviderSimple methods
@@ -61,7 +59,8 @@ public:
private:
QString automationIdForAccessible(const QAccessibleInterface *accessible);
- ULONG m_ref;
+ static void fillVariantArrayForRelation(QAccessibleInterface *accessible, QAccessible::Relation relation, VARIANT *pRetVal);
+ static QMutex m_mutex;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.cpp
index c53b130b92..ba2a88bb4e 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.cpp
@@ -39,7 +39,7 @@ void QWindowsUiaProviderCache::insert(QAccessible::Id id, QWindowsUiaBaseProvide
m_providerTable[id] = provider;
m_inverseTable[provider] = id;
// Connects the destroyed signal to our slot, to remove deleted objects from the cache.
- QObject::connect(provider, &QObject::destroyed, this, &QWindowsUiaProviderCache::objectDestroyed);
+ QObject::connect(provider, &QObject::destroyed, this, &QWindowsUiaProviderCache::objectDestroyed, Qt::DirectConnection);
}
}
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiarangevalueprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiarangevalueprovider.h
index 3e06961a82..ffb5ae155b 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiarangevalueprovider.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiarangevalueprovider.h
@@ -13,7 +13,7 @@ QT_BEGIN_NAMESPACE
// Implements the Range Value control pattern provider.
class QWindowsUiaRangeValueProvider : public QWindowsUiaBaseProvider,
- public QWindowsComBase<IRangeValueProvider>
+ public QComObject<IRangeValueProvider>
{
Q_DISABLE_COPY_MOVE(QWindowsUiaRangeValueProvider)
public:
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.cpp
index 7e829b24a7..95bc2f7570 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.cpp
@@ -36,6 +36,14 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::Select()
if (!accessible)
return UIA_E_ELEMENTNOTAVAILABLE;
+ if (QAccessibleInterface *parent = accessible->parent()) {
+ if (QAccessibleSelectionInterface *selectionInterface = parent->selectionInterface()) {
+ selectionInterface->clear();
+ bool ok = selectionInterface->select(accessible);
+ return ok ? S_OK : S_FALSE;
+ }
+ }
+
QAccessibleActionInterface *actionInterface = accessible->actionInterface();
if (!actionInterface)
return UIA_E_ELEMENTNOTAVAILABLE;
@@ -73,6 +81,13 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::AddToSelection()
if (!accessible)
return UIA_E_ELEMENTNOTAVAILABLE;
+ if (QAccessibleInterface *parent = accessible->parent()) {
+ if (QAccessibleSelectionInterface *selectionInterface = parent->selectionInterface()) {
+ bool ok = selectionInterface->select(accessible);
+ return ok ? S_OK : S_FALSE;
+ }
+ }
+
QAccessibleActionInterface *actionInterface = accessible->actionInterface();
if (!actionInterface)
return UIA_E_ELEMENTNOTAVAILABLE;
@@ -98,6 +113,13 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::RemoveFromSelection(
if (!accessible)
return UIA_E_ELEMENTNOTAVAILABLE;
+ if (QAccessibleInterface *parent = accessible->parent()) {
+ if (QAccessibleSelectionInterface *selectionInterface = parent->selectionInterface()) {
+ bool ok = selectionInterface->unselect(accessible);
+ return ok ? S_OK : S_FALSE;
+ }
+ }
+
QAccessibleActionInterface *actionInterface = accessible->actionInterface();
if (!actionInterface)
return UIA_E_ELEMENTNOTAVAILABLE;
@@ -124,6 +146,14 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::get_IsSelected(BOOL
if (!accessible)
return UIA_E_ELEMENTNOTAVAILABLE;
+ if (QAccessibleInterface *parent = accessible->parent()) {
+ if (QAccessibleSelectionInterface *selectionInterface = parent->selectionInterface()) {
+ bool selected = selectionInterface->isSelected(accessible);
+ *pRetVal = selected ? TRUE : FALSE;
+ return S_OK;
+ }
+ }
+
if (accessible->role() == QAccessible::RadioButton)
*pRetVal = accessible->state().checked;
else if (accessible->role() == QAccessible::PageTab)
@@ -146,12 +176,18 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::get_SelectionContain
if (!accessible)
return UIA_E_ELEMENTNOTAVAILABLE;
+ QAccessibleInterface *parent = accessible->parent();
+ if (parent && parent->selectionInterface()) {
+ *pRetVal = QWindowsUiaMainProvider::providerForAccessible(parent);
+ return S_OK;
+ }
+
QAccessibleActionInterface *actionInterface = accessible->actionInterface();
if (!actionInterface)
return UIA_E_ELEMENTNOTAVAILABLE;
// Radio buttons do not require a container.
- if (QAccessibleInterface *parent = accessible->parent()) {
+ if (parent) {
if ((accessible->role() == QAccessible::ListItem && parent->role() == QAccessible::List)
|| (accessible->role() == QAccessible::PageTab && parent->role() == QAccessible::PageTabList)) {
*pRetVal = QWindowsUiaMainProvider::providerForAccessible(parent);
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.h
index b510ae951c..ee34fd9edd 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.h
@@ -13,7 +13,7 @@ QT_BEGIN_NAMESPACE
// Implements the Selection Item control pattern provider. Used for List items and radio buttons.
class QWindowsUiaSelectionItemProvider : public QWindowsUiaBaseProvider,
- public QWindowsComBase<ISelectionItemProvider>
+ public QComObject<ISelectionItemProvider>
{
Q_DISABLE_COPY_MOVE(QWindowsUiaSelectionItemProvider)
public:
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.cpp
index 45f3b20552..37148c655a 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.cpp
@@ -41,17 +41,23 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::GetSelection(SAFEARRAY *
if (!accessible)
return UIA_E_ELEMENTNOTAVAILABLE;
- // First put selected items in a list, then build a safe array with the right size.
+ // First get/create list of selected items, then build a safe array with the right size.
QList<QAccessibleInterface *> selectedList;
- for (int i = 0; i < accessible->childCount(); ++i) {
- if (QAccessibleInterface *child = accessible->child(i)) {
- if (accessible->role() == QAccessible::PageTabList) {
- if (child->role() == QAccessible::PageTab && child->state().focused) {
- selectedList.append(child);
- }
- } else {
- if (child->state().selected) {
- selectedList.append(child);
+ if (QAccessibleSelectionInterface *selectionInterface = accessible->selectionInterface()) {
+ selectedList = selectionInterface->selectedItems();
+ } else {
+ const int childCount = accessible->childCount();
+ selectedList.reserve(childCount);
+ for (int i = 0; i < childCount; ++i) {
+ if (QAccessibleInterface *child = accessible->child(i)) {
+ if (accessible->role() == QAccessible::PageTabList) {
+ if (child->role() == QAccessible::PageTab && child->state().focused) {
+ selectedList.append(child);
+ }
+ } else {
+ if (child->state().selected) {
+ selectedList.append(child);
+ }
}
}
}
@@ -102,11 +108,15 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::get_IsSelectionRequired(
// Initially returns false if none are selected. After the first selection, it may be required.
bool anySelected = false;
- for (int i = 0; i < accessible->childCount(); ++i) {
- if (QAccessibleInterface *child = accessible->child(i)) {
- if (child->state().selected) {
- anySelected = true;
- break;
+ if (QAccessibleSelectionInterface *selectionInterface = accessible->selectionInterface()) {
+ anySelected = selectionInterface->selectedItem(0) != nullptr;
+ } else {
+ for (int i = 0; i < accessible->childCount(); ++i) {
+ if (QAccessibleInterface *child = accessible->child(i)) {
+ if (child->state().selected) {
+ anySelected = true;
+ break;
+ }
}
}
}
@@ -116,6 +126,134 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::get_IsSelectionRequired(
return S_OK;
}
+HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::get_FirstSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleInterface *firstSelectedChild = nullptr;
+ if (QAccessibleSelectionInterface *selectionInterface = accessible->selectionInterface()) {
+ firstSelectedChild = selectionInterface->selectedItem(0);
+ if (!firstSelectedChild)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+ } else {
+ int i = 0;
+ while (!firstSelectedChild && i < accessible->childCount()) {
+ if (QAccessibleInterface *child = accessible->child(i)) {
+ if (accessible->role() == QAccessible::PageTabList) {
+ if (child->role() == QAccessible::PageTab && child->state().focused)
+ firstSelectedChild = child;
+ } else if (child->state().selected) {
+ firstSelectedChild = child;
+ }
+ }
+ i++;
+ }
+ }
+
+ if (!firstSelectedChild)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ if (QWindowsUiaMainProvider *childProvider = QWindowsUiaMainProvider::providerForAccessible(firstSelectedChild))
+ {
+ *pRetVal = static_cast<IRawElementProviderSimple *>(childProvider);
+ return S_OK;
+ }
+
+ return S_FALSE;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::get_LastSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleInterface *lastSelectedChild = nullptr;
+ if (QAccessibleSelectionInterface *selectionInterface = accessible->selectionInterface()) {
+ const int selectedItemCount = selectionInterface->selectedItemCount();
+ if (selectedItemCount <= 0)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+ lastSelectedChild = selectionInterface->selectedItem(selectedItemCount - 1);
+ } else {
+ int i = accessible->childCount() - 1;
+ while (!lastSelectedChild && i >= 0) {
+ if (QAccessibleInterface *child = accessible->child(i)) {
+ if (accessible->role() == QAccessible::PageTabList) {
+ if (child->role() == QAccessible::PageTab && child->state().focused)
+ lastSelectedChild = child;
+ } else if (child->state().selected) {
+ lastSelectedChild = child;
+ }
+ }
+ i--;
+ }
+ }
+
+ if (!lastSelectedChild)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ if (QWindowsUiaMainProvider *childProvider = QWindowsUiaMainProvider::providerForAccessible(lastSelectedChild))
+ {
+ *pRetVal = static_cast<IRawElementProviderSimple *>(childProvider);
+ return S_OK;
+ }
+
+ return S_FALSE;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::get_CurrentSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+ return get_FirstSelectedItem(pRetVal);
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::get_ItemCount(__RPC__out int *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = -1;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+
+ if (QAccessibleSelectionInterface *selectionInterface = accessible->selectionInterface())
+ *pRetVal = selectionInterface->selectedItemCount();
+ else {
+ int selectedCount = 0;
+ for (int i = 0; i < accessible->childCount(); i++) {
+ if (QAccessibleInterface *child = accessible->child(i)) {
+ if (accessible->role() == QAccessible::PageTabList) {
+ if (child->role() == QAccessible::PageTab && child->state().focused)
+ selectedCount++;
+ } else if (child->state().selected) {
+ selectedCount++;
+ }
+ }
+ }
+ *pRetVal = selectedCount;
+ }
+
+ return S_OK;
+}
+
QT_END_NAMESPACE
#endif // QT_CONFIG(accessibility)
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.h
index 9a187c9413..7a899e4261 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.h
@@ -11,9 +11,22 @@
QT_BEGIN_NAMESPACE
+namespace QtPrivate {
+
+template <>
+struct QComObjectTraits<ISelectionProvider2>
+{
+ static constexpr bool isGuidOf(REFIID riid) noexcept
+ {
+ return QComObjectTraits<ISelectionProvider2, ISelectionProvider>::isGuidOf(riid);
+ }
+};
+
+} // namespace QtPrivate
+
// Implements the Selection control pattern provider. Used for Lists.
class QWindowsUiaSelectionProvider : public QWindowsUiaBaseProvider,
- public QWindowsComBase<ISelectionProvider>
+ public QComObject<ISelectionProvider2>
{
Q_DISABLE_COPY_MOVE(QWindowsUiaSelectionProvider)
public:
@@ -24,6 +37,12 @@ public:
HRESULT STDMETHODCALLTYPE GetSelection(SAFEARRAY **pRetVal) override;
HRESULT STDMETHODCALLTYPE get_CanSelectMultiple(BOOL *pRetVal) override;
HRESULT STDMETHODCALLTYPE get_IsSelectionRequired(BOOL *pRetVal) override;
+
+ // ISelectionProvider2
+ HRESULT STDMETHODCALLTYPE get_FirstSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **pRetVal) override;
+ HRESULT STDMETHODCALLTYPE get_LastSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **pRetVal) override;
+ HRESULT STDMETHODCALLTYPE get_CurrentSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **pRetVal) override;
+ HRESULT STDMETHODCALLTYPE get_ItemCount(__RPC__out int *pRetVal) override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatableitemprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiatableitemprovider.h
index 8aed180671..3bb0e1027e 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiatableitemprovider.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatableitemprovider.h
@@ -13,7 +13,7 @@ QT_BEGIN_NAMESPACE
// Implements the Table Item control pattern provider. Used by items within a table/tree.
class QWindowsUiaTableItemProvider : public QWindowsUiaBaseProvider,
- public QWindowsComBase<ITableItemProvider>
+ public QComObject<ITableItemProvider>
{
Q_DISABLE_COPY_MOVE(QWindowsUiaTableItemProvider)
public:
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatableprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiatableprovider.h
index 6454bb9441..8beb11decf 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiatableprovider.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatableprovider.h
@@ -12,8 +12,7 @@
QT_BEGIN_NAMESPACE
// Implements the Table control pattern provider. Used by tables/trees.
-class QWindowsUiaTableProvider : public QWindowsUiaBaseProvider,
- public QWindowsComBase<ITableProvider>
+class QWindowsUiaTableProvider : public QWindowsUiaBaseProvider, public QComObject<ITableProvider>
{
Q_DISABLE_COPY_MOVE(QWindowsUiaTableProvider)
public:
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.cpp
index e3b7c69b60..172836232e 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.cpp
@@ -26,20 +26,6 @@ QWindowsUiaTextProvider::~QWindowsUiaTextProvider()
{
}
-HRESULT STDMETHODCALLTYPE QWindowsUiaTextProvider::QueryInterface(REFIID iid, LPVOID *iface)
-{
- qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
-
- if (!iface)
- return E_INVALIDARG;
- *iface = nullptr;
-
- const bool result = qWindowsComQueryUnknownInterfaceMulti<ITextProvider>(this, iid, iface)
- || qWindowsComQueryInterface<ITextProvider>(this, iid, iface)
- || qWindowsComQueryInterface<ITextProvider2>(this, iid, iface);
- return result ? S_OK : E_NOINTERFACE;
-}
-
// Returns an array of providers for the selected text ranges.
HRESULT STDMETHODCALLTYPE QWindowsUiaTextProvider::GetSelection(SAFEARRAY **pRetVal)
{
@@ -146,9 +132,10 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaTextProvider::RangeFromPoint(UiaPoint point
nativeUiaPointToPoint(point, window, &pt);
int offset = textInterface->offsetAtPoint(pt);
- if ((offset >= 0) && (offset < textInterface->characterCount())) {
- *pRetVal = new QWindowsUiaTextRangeProvider(id(), offset, offset);
- }
+ if (offset < 0 || offset >= textInterface->characterCount())
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ *pRetVal = new QWindowsUiaTextRangeProvider(id(), offset, offset);
return S_OK;
}
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.h
index 5e6d430f1a..8f886510b4 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.h
@@ -12,18 +12,27 @@
QT_BEGIN_NAMESPACE
+namespace QtPrivate {
+
+template <>
+struct QComObjectTraits<ITextProvider2>
+{
+ static constexpr bool isGuidOf(REFIID riid) noexcept
+ {
+ return QComObjectTraits<ITextProvider2, ITextProvider>::isGuidOf(riid);
+ }
+};
+
+} // namespace QtPrivate
+
// Implements the Text control pattern provider. Used for text controls.
-class QWindowsUiaTextProvider : public QWindowsUiaBaseProvider,
- public QWindowsComBase<ITextProvider2>
+class QWindowsUiaTextProvider : public QWindowsUiaBaseProvider, public QComObject<ITextProvider2>
{
Q_DISABLE_COPY_MOVE(QWindowsUiaTextProvider)
public:
explicit QWindowsUiaTextProvider(QAccessible::Id id);
~QWindowsUiaTextProvider();
- // IUnknown overrides
- HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, LPVOID *iface) override;
-
// ITextProvider
HRESULT STDMETHODCALLTYPE GetSelection(SAFEARRAY **pRetVal) override;
HRESULT STDMETHODCALLTYPE GetVisibleRanges(SAFEARRAY **pRetVal) override;
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.cpp
index 4d02036196..a62a33cfe2 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.cpp
@@ -167,6 +167,15 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaTextRangeProvider::GetAttributeValue(TEXTAT
else
setVariantI4(CaretPosition_Unknown, pRetVal);
break;
+ case UIA_StrikethroughStyleAttributeId:
+ {
+ const QString value = valueForIA2Attribute(textInterface, QStringLiteral("text-line-through-type"));
+ if (value.isEmpty())
+ break;
+ const TextDecorationLineStyle uiaLineStyle = uiaLineStyleForIA2LineStyle(value);
+ setVariantI4(uiaLineStyle, pRetVal);
+ break;
+ }
default:
break;
}
@@ -307,14 +316,14 @@ HRESULT QWindowsUiaTextRangeProvider::Move(TextUnit unit, int count, int *pRetVa
int len = textInterface->characterCount();
- if (len < 1)
+ if (len < 1 || count == 0) // MSDN: "Zero has no effect."
return S_OK;
if (unit == TextUnit_Character) {
// Moves the start point, ensuring it lies within the bounds.
- int start = qBound(0, m_startOffset + count, len - 1);
+ int start = qBound(0, m_startOffset + count, len);
// If range was initially empty, leaves it as is; otherwise, normalizes it to one char.
- m_endOffset = (m_endOffset > m_startOffset) ? start + 1 : start;
+ m_endOffset = (m_endOffset > m_startOffset) ? qMin(start + 1, len) : start;
*pRetVal = start - m_startOffset; // Returns the actually moved distance.
m_startOffset = start;
} else {
@@ -385,7 +394,7 @@ HRESULT QWindowsUiaTextRangeProvider::MoveEndpointByUnit(TextPatternRangeEndpoin
if (unit == TextUnit_Character) {
if (endpoint == TextPatternRangeEndpoint_Start) {
- int boundedValue = qBound(0, m_startOffset + count, len - 1);
+ int boundedValue = qBound(0, m_startOffset + count, len);
*pRetVal = boundedValue - m_startOffset;
m_startOffset = boundedValue;
m_endOffset = qBound(m_startOffset, m_endOffset, len);
@@ -517,6 +526,42 @@ HRESULT QWindowsUiaTextRangeProvider::unselect()
return S_OK;
}
+// helper method to retrieve the value of the given IAccessible2 text attribute,
+// or an empty string if not set
+QString QWindowsUiaTextRangeProvider::valueForIA2Attribute(QAccessibleTextInterface *textInterface,
+ const QString &key)
+{
+ Q_ASSERT(textInterface);
+
+ int startOffset;
+ int endOffset;
+ const QString attributes = textInterface->attributes(m_startOffset, &startOffset, &endOffset);
+ // don't report if attributes don't apply for the whole range
+ if (startOffset > m_startOffset || endOffset < m_endOffset)
+ return {};
+
+ for (auto attr : QStringTokenizer{attributes, u';'})
+ {
+ const QList<QStringView> items = attr.split(u':', Qt::SkipEmptyParts, Qt::CaseSensitive);
+ if (items.count() == 2 && items[0] == key)
+ return items[1].toString();
+ }
+
+ return {};
+}
+
+TextDecorationLineStyle QWindowsUiaTextRangeProvider::uiaLineStyleForIA2LineStyle(const QString &ia2LineStyle)
+{
+ if (ia2LineStyle == QStringLiteral("none"))
+ return TextDecorationLineStyle_None;
+ if (ia2LineStyle == QStringLiteral("single"))
+ return TextDecorationLineStyle_Single;
+ if (ia2LineStyle == QStringLiteral("double"))
+ return TextDecorationLineStyle_Double;
+
+ return TextDecorationLineStyle_Other;
+}
+
QT_END_NAMESPACE
#endif // QT_CONFIG(accessibility)
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.h
index f7d28a34d2..a37429a603 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.h
@@ -13,7 +13,7 @@ QT_BEGIN_NAMESPACE
// Implements the Text Range control pattern provider. Used for text controls.
class QWindowsUiaTextRangeProvider : public QWindowsUiaBaseProvider,
- public QWindowsComBase<ITextRangeProvider>
+ public QComObject<ITextRangeProvider>
{
Q_DISABLE_COPY_MOVE(QWindowsUiaTextRangeProvider)
public:
@@ -42,6 +42,8 @@ public:
private:
HRESULT unselect();
+ QString valueForIA2Attribute(QAccessibleTextInterface *textInterface, const QString &key);
+ TextDecorationLineStyle uiaLineStyleForIA2LineStyle(const QString &ia2LineStyle);
int m_startOffset;
int m_endOffset;
};
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatoggleprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiatoggleprovider.h
index 742e26f6d6..17150dfbe0 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiatoggleprovider.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatoggleprovider.h
@@ -12,8 +12,7 @@
QT_BEGIN_NAMESPACE
// Implements the Toggle control pattern provider. Used for checkboxes.
-class QWindowsUiaToggleProvider : public QWindowsUiaBaseProvider,
- public QWindowsComBase<IToggleProvider>
+class QWindowsUiaToggleProvider : public QWindowsUiaBaseProvider, public QComObject<IToggleProvider>
{
Q_DISABLE_COPY_MOVE(QWindowsUiaToggleProvider)
public:
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp
index 9287e89084..78ab3e890e 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp
@@ -176,6 +176,9 @@ long roleToControlTypeId(QAccessible::Role role)
{QAccessible::PageTabList, UIA_TabControlTypeId},
{QAccessible::Clock, UIA_CustomControlTypeId},
{QAccessible::Splitter, UIA_CustomControlTypeId},
+ {QAccessible::Paragraph, UIA_TextControlTypeId},
+ {QAccessible::WebDocument, UIA_DocumentControlTypeId},
+ {QAccessible::Heading, UIA_TextControlTypeId},
};
return mapping.value(role, UIA_CustomControlTypeId);
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.h
index 8fe8b1c6d7..bf90211cec 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.h
@@ -12,7 +12,7 @@
#include <QtGui/qaccessible.h>
#include <QtGui/qwindow.h>
#include <QtCore/qrect.h>
-#include <QtGui/private/qwindowsuiawrapper_p.h>
+#include "qwindowsuiautomation.h"
QT_BEGIN_NAMESPACE
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.cpp
new file mode 100644
index 0000000000..1593a07202
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.cpp
@@ -0,0 +1,74 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QtGui/qtguiglobal.h>
+#if QT_CONFIG(accessibility)
+
+#include "qwindowsuiautomation.h"
+
+#if defined(__MINGW32__) || defined(__MINGW64__)
+
+template<typename T, typename... TArg>
+struct winapi_func
+{
+ using func_t = T(WINAPI*)(TArg...);
+ const func_t func;
+ const T error_value;
+#ifdef __GNUC__
+# pragma GCC diagnostic ignored "-Wcast-function-type"
+#endif
+ winapi_func(const char *lib_name, const char *func_name, func_t func_proto,
+ T error_value = T(__HRESULT_FROM_WIN32(ERROR_PROC_NOT_FOUND))) :
+ func(reinterpret_cast<func_t>(GetProcAddress(LoadLibraryA(lib_name), func_name))),
+ error_value(error_value)
+ {
+ std::ignore = func_proto;
+ }
+#ifdef __GNUC__
+# pragma GCC diagnostic pop
+#endif
+ T invoke(TArg... arg)
+ {
+ if (!func)
+ return error_value;
+ return func(arg...);
+ }
+};
+
+#define FN(fn) #fn,fn
+
+BOOL WINAPI UiaClientsAreListening()
+{
+ static auto func = winapi_func("uiautomationcore", FN(UiaClientsAreListening), BOOL(false));
+ return func.invoke();
+}
+
+LRESULT WINAPI UiaReturnRawElementProvider(
+ HWND hwnd, WPARAM wParam, LPARAM lParam, IRawElementProviderSimple *el)
+{
+ static auto func = winapi_func("uiautomationcore", FN(UiaReturnRawElementProvider));
+ return func.invoke(hwnd, wParam, lParam, el);
+}
+
+HRESULT WINAPI UiaHostProviderFromHwnd(HWND hwnd, IRawElementProviderSimple **ppProvider)
+{
+ static auto func = winapi_func("uiautomationcore", FN(UiaHostProviderFromHwnd));
+ return func.invoke(hwnd, ppProvider);
+}
+
+HRESULT WINAPI UiaRaiseAutomationPropertyChangedEvent(
+ IRawElementProviderSimple *pProvider, PROPERTYID id, VARIANT oldValue, VARIANT newValue)
+{
+ static auto func = winapi_func("uiautomationcore", FN(UiaRaiseAutomationPropertyChangedEvent));
+ return func.invoke(pProvider, id, oldValue, newValue);
+}
+
+HRESULT WINAPI UiaRaiseAutomationEvent(IRawElementProviderSimple *pProvider, EVENTID id)
+{
+ static auto func = winapi_func("uiautomationcore", FN(UiaRaiseAutomationEvent));
+ return func.invoke(pProvider, id);
+}
+
+#endif // defined(__MINGW32__) || defined(__MINGW64__)
+
+#endif // QT_CONFIG(accessibility)
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.h
new file mode 100644
index 0000000000..a192b9b0fb
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.h
@@ -0,0 +1,70 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QWINDOWSUIAUTOMATION_H
+#define QWINDOWSUIAUTOMATION_H
+
+#include <QtGui/qtguiglobal.h>
+#if QT_CONFIG(accessibility)
+
+#include <uiautomation.h>
+
+#if defined(__MINGW32__) || defined(__MINGW64__)
+
+#define UIA_SelectionPattern2Id 10034
+#define UIA_IsReadOnlyAttributeId 40015
+#define UIA_StrikethroughStyleAttributeId 40026
+#define UIA_CaretPositionAttributeId 40038
+
+enum CaretPosition {
+ CaretPosition_Unknown = 0,
+ CaretPosition_EndOfLine = 1,
+ CaretPosition_BeginningOfLine = 2
+};
+
+enum TextDecorationLineStyle {
+ TextDecorationLineStyle_None = 0,
+ TextDecorationLineStyle_Single = 1,
+ TextDecorationLineStyle_WordsOnly = 2,
+ TextDecorationLineStyle_Double = 3,
+ TextDecorationLineStyle_Dot = 4,
+ TextDecorationLineStyle_Dash = 5,
+ TextDecorationLineStyle_DashDot = 6,
+ TextDecorationLineStyle_DashDotDot = 7,
+ TextDecorationLineStyle_Wavy = 8,
+ TextDecorationLineStyle_ThickSingle = 9,
+ TextDecorationLineStyle_DoubleWavy = 11,
+ TextDecorationLineStyle_ThickWavy = 12,
+ TextDecorationLineStyle_LongDash = 13,
+ TextDecorationLineStyle_ThickDash = 14,
+ TextDecorationLineStyle_ThickDashDot = 15,
+ TextDecorationLineStyle_ThickDashDotDot = 16,
+ TextDecorationLineStyle_ThickDot = 17,
+ TextDecorationLineStyle_ThickLongDash = 18,
+ TextDecorationLineStyle_Other = -1
+};
+
+BOOL WINAPI UiaClientsAreListening();
+
+#ifndef __ISelectionProvider2_INTERFACE_DEFINED__
+#define __ISelectionProvider2_INTERFACE_DEFINED__
+DEFINE_GUID(IID_ISelectionProvider2, 0x14f68475, 0xee1c, 0x44f6, 0xa8, 0x69, 0xd2, 0x39, 0x38, 0x1f, 0x0f, 0xe7);
+MIDL_INTERFACE("14f68475-ee1c-44f6-a869-d239381f0fe7")
+ISelectionProvider2 : public ISelectionProvider
+{
+public:
+ virtual HRESULT STDMETHODCALLTYPE get_FirstSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **retVal) = 0;
+ virtual HRESULT STDMETHODCALLTYPE get_LastSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **retVal) = 0;
+ virtual HRESULT STDMETHODCALLTYPE get_CurrentSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **retVal) = 0;
+ virtual HRESULT STDMETHODCALLTYPE get_ItemCount(__RPC__out int *retVal) = 0;
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(ISelectionProvider2, 0x14f68475, 0xee1c, 0x44f6, 0xa8, 0x69, 0xd2, 0x39, 0x38, 0x1f, 0x0f, 0xe7)
+#endif
+#endif // __ISelectionProvider2_INTERFACE_DEFINED__
+
+#endif // defined(__MINGW32__) || defined(__MINGW64__)
+
+#endif // QT_CONFIG(accessibility)
+
+#endif // QWINDOWSUIAUTOMATION_H
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiavalueprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiavalueprovider.h
index 2f52019d33..8c0a6b8ee7 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiavalueprovider.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiavalueprovider.h
@@ -13,8 +13,7 @@ QT_BEGIN_NAMESPACE
// Implements the Value control pattern provider.
// Supported for all controls that can return text(QAccessible::Value).
-class QWindowsUiaValueProvider : public QWindowsUiaBaseProvider,
- public QWindowsComBase<IValueProvider>
+class QWindowsUiaValueProvider : public QWindowsUiaBaseProvider, public QComObject<IValueProvider>
{
Q_DISABLE_COPY_MOVE(QWindowsUiaValueProvider)
public:
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.h
index e277b3f5c5..89a1655232 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.h
@@ -11,8 +11,7 @@
QT_BEGIN_NAMESPACE
-class QWindowsUiaWindowProvider : public QWindowsUiaBaseProvider,
- public QWindowsComBase<IWindowProvider>
+class QWindowsUiaWindowProvider : public QWindowsUiaBaseProvider, public QComObject<IWindowProvider>
{
Q_DISABLE_COPY(QWindowsUiaWindowProvider)
public: