Handle Windows theme changes in central theme listener
Instead of having each window listen for theme changes we now have a single theme listener window, similar to the way we handle screen management. This reduces the amount of redundant callbacks to QWSI for theme changes somewhat, but Windows still emits several redundant events even for a single window. We want the theme change event to be synchronous, so there is no obvious way to debounce these events, besides the clearing of the message queue that we already do in this patch. Since we don't know exactly what part of the theme changed we can't bail out of the theme change event to QWSI based on the color scheme being the same, as the accent color or other parts of the theme might have changed. Change-Id: I827fa50effadf8a8e674a03ddc72958c60310f48 Reviewed-by: Oliver Wolff <oliver.wolff@qt.io> Reviewed-by: Zhao Yuhang <2546789017@qq.com>
This commit is contained in:
parent
3a21b4bcaf
commit
fdfed82675
@ -178,7 +178,7 @@ static const char *findWMstr(uint msg)
|
|||||||
{ 0x0014, "WM_ERASEBKGND" },
|
{ 0x0014, "WM_ERASEBKGND" },
|
||||||
{ 0x0015, "WM_SYSCOLORCHANGE" },
|
{ 0x0015, "WM_SYSCOLORCHANGE" },
|
||||||
{ 0x0018, "WM_SHOWWINDOW" },
|
{ 0x0018, "WM_SHOWWINDOW" },
|
||||||
{ 0x001A, "WM_WININICHANGE" },
|
{ 0x001A, "WM_SETTINGCHANGE" },
|
||||||
{ 0x001B, "WM_DEVMODECHANGE" },
|
{ 0x001B, "WM_DEVMODECHANGE" },
|
||||||
{ 0x001C, "WM_ACTIVATEAPP" },
|
{ 0x001C, "WM_ACTIVATEAPP" },
|
||||||
{ 0x001D, "WM_FONTCHANGE" },
|
{ 0x001D, "WM_FONTCHANGE" },
|
||||||
@ -410,6 +410,7 @@ static const char *findWMstr(uint msg)
|
|||||||
{ 0x0317, "WM_PRINT" },
|
{ 0x0317, "WM_PRINT" },
|
||||||
{ 0x0318, "WM_PRINTCLIENT" },
|
{ 0x0318, "WM_PRINTCLIENT" },
|
||||||
{ 0x0319, "WM_APPCOMMAND" },
|
{ 0x0319, "WM_APPCOMMAND" },
|
||||||
|
{ 0x0320, "WM_DWMCOLORIZATIONCOLORCHANGED" },
|
||||||
{ 0x031A, "WM_THEMECHANGED" },
|
{ 0x031A, "WM_THEMECHANGED" },
|
||||||
{ 0x0358, "WM_HANDHELDFIRST" },
|
{ 0x0358, "WM_HANDHELDFIRST" },
|
||||||
{ 0x0359, "WM_HANDHELDFIRST + 1" },
|
{ 0x0359, "WM_HANDHELDFIRST + 1" },
|
||||||
@ -804,6 +805,10 @@ QString decodeMSG(const MSG& msg)
|
|||||||
if (const char *logoffOption = sessionMgrLogOffOption(uint(wParam)))
|
if (const char *logoffOption = sessionMgrLogOffOption(uint(wParam)))
|
||||||
parameters += QLatin1StringView(logoffOption);
|
parameters += QLatin1StringView(logoffOption);
|
||||||
break;
|
break;
|
||||||
|
case WM_SETTINGCHANGE:
|
||||||
|
parameters = "wParam"_L1 + wParamS + " lParam("_L1
|
||||||
|
+ QString::fromWCharArray(reinterpret_cast<LPCWSTR>(lParam)) + u')';
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
parameters = "wParam"_L1 + wParamS + " lParam"_L1 + lParamS;
|
parameters = "wParam"_L1 + wParamS + " lParam"_L1 + lParamS;
|
||||||
break;
|
break;
|
||||||
|
@ -75,6 +75,7 @@ Q_LOGGING_CATEGORY(lcQpaAccessibility, "qt.qpa.accessibility")
|
|||||||
Q_LOGGING_CATEGORY(lcQpaUiAutomation, "qt.qpa.uiautomation")
|
Q_LOGGING_CATEGORY(lcQpaUiAutomation, "qt.qpa.uiautomation")
|
||||||
Q_LOGGING_CATEGORY(lcQpaTrayIcon, "qt.qpa.trayicon")
|
Q_LOGGING_CATEGORY(lcQpaTrayIcon, "qt.qpa.trayicon")
|
||||||
Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen")
|
Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen")
|
||||||
|
Q_LOGGING_CATEGORY(lcQpaTheme, "qt.qpa.theme")
|
||||||
|
|
||||||
int QWindowsContext::verbose = 0;
|
int QWindowsContext::verbose = 0;
|
||||||
|
|
||||||
@ -195,6 +196,9 @@ QWindowsContext::~QWindowsContext()
|
|||||||
if (d->m_powerDummyWindow)
|
if (d->m_powerDummyWindow)
|
||||||
DestroyWindow(d->m_powerDummyWindow);
|
DestroyWindow(d->m_powerDummyWindow);
|
||||||
|
|
||||||
|
if (QWindowsTheme *theme = QWindowsTheme::instance())
|
||||||
|
theme->destroyThemeChangeWindow();
|
||||||
|
|
||||||
d->m_screenManager.destroyWindow();
|
d->m_screenManager.destroyWindow();
|
||||||
|
|
||||||
unregisterWindowClasses();
|
unregisterWindowClasses();
|
||||||
@ -1062,11 +1066,6 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
|
|||||||
#endif
|
#endif
|
||||||
case QtWindows::SettingChangedEvent: {
|
case QtWindows::SettingChangedEvent: {
|
||||||
QWindowsWindow::settingsChanged();
|
QWindowsWindow::settingsChanged();
|
||||||
// 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)) {
|
|
||||||
QWindowsTheme::handleSettingsChanged();
|
|
||||||
}
|
|
||||||
return d->m_screenManager.handleScreenChanges();
|
return d->m_screenManager.handleScreenChanges();
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -1231,10 +1230,6 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
|
|||||||
QWindowSystemInterface::handleCloseEvent(platformWindow->window());
|
QWindowSystemInterface::handleCloseEvent(platformWindow->window());
|
||||||
return true;
|
return true;
|
||||||
case QtWindows::ThemeChanged: {
|
case QtWindows::ThemeChanged: {
|
||||||
QWindowsThemeCache::clearThemeCache(platformWindow->handle());
|
|
||||||
// Switch from Aero to Classic changes margins.
|
|
||||||
if (QWindowsTheme *theme = QWindowsTheme::instance())
|
|
||||||
theme->windowsThemeChanged(platformWindow->window());
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case QtWindows::CompositionSettingsChanged:
|
case QtWindows::CompositionSettingsChanged:
|
||||||
|
@ -29,6 +29,7 @@ Q_DECLARE_LOGGING_CATEGORY(lcQpaAccessibility)
|
|||||||
Q_DECLARE_LOGGING_CATEGORY(lcQpaUiAutomation)
|
Q_DECLARE_LOGGING_CATEGORY(lcQpaUiAutomation)
|
||||||
Q_DECLARE_LOGGING_CATEGORY(lcQpaTrayIcon)
|
Q_DECLARE_LOGGING_CATEGORY(lcQpaTrayIcon)
|
||||||
Q_DECLARE_LOGGING_CATEGORY(lcQpaScreen)
|
Q_DECLARE_LOGGING_CATEGORY(lcQpaScreen)
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(lcQpaTheme)
|
||||||
|
|
||||||
class QWindow;
|
class QWindow;
|
||||||
class QPlatformScreen;
|
class QPlatformScreen;
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
#include <private/qhighdpiscaling_p.h>
|
#include <private/qhighdpiscaling_p.h>
|
||||||
#include <private/qwinregistry_p.h>
|
#include <private/qwinregistry_p.h>
|
||||||
#include <QtCore/private/qfunctions_win_p.h>
|
#include <QtCore/private/qfunctions_win_p.h>
|
||||||
|
#include <QtGui/private/qwindowsthemecache_p.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
@ -480,6 +481,39 @@ static inline QPalette *menuBarPalette(const QPalette &menuPalette, bool light)
|
|||||||
const char *QWindowsTheme::name = "windows";
|
const char *QWindowsTheme::name = "windows";
|
||||||
QWindowsTheme *QWindowsTheme::m_instance = nullptr;
|
QWindowsTheme *QWindowsTheme::m_instance = nullptr;
|
||||||
|
|
||||||
|
extern "C" LRESULT QT_WIN_CALLBACK qThemeChangeObserverWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
switch (message) {
|
||||||
|
case WM_SETTINGCHANGE:
|
||||||
|
// Only refresh the 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)))
|
||||||
|
break;
|
||||||
|
Q_FALLTHROUGH();
|
||||||
|
case WM_THEMECHANGED:
|
||||||
|
case WM_SYSCOLORCHANGE:
|
||||||
|
case WM_DWMCOLORIZATIONCOLORCHANGED:
|
||||||
|
qCDebug(lcQpaTheme) << "Handling theme change due to"
|
||||||
|
<< qUtf8Printable(decodeMSG(MSG{hwnd, message, wParam, lParam, 0, {0, 0}}).trimmed());
|
||||||
|
QWindowsTheme::handleThemeChange();
|
||||||
|
|
||||||
|
MSG msg; // Clear the message queue, we've already reacted to the change
|
||||||
|
while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE));
|
||||||
|
|
||||||
|
// FIXME: Despite clearing the message queue above, Windows will send
|
||||||
|
// us redundant theme change events for our single window. We want the
|
||||||
|
// theme change delivery to be synchronous, so we can't easily debounce
|
||||||
|
// them by peeking into the QWSI event queue, but perhaps there are other
|
||||||
|
// ways.
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefWindowProc(hwnd, message, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
QWindowsTheme::QWindowsTheme()
|
QWindowsTheme::QWindowsTheme()
|
||||||
{
|
{
|
||||||
m_instance = this;
|
m_instance = this;
|
||||||
@ -489,6 +523,16 @@ QWindowsTheme::QWindowsTheme()
|
|||||||
std::fill(m_palettes, m_palettes + NPalettes, nullptr);
|
std::fill(m_palettes, m_palettes + NPalettes, nullptr);
|
||||||
refresh();
|
refresh();
|
||||||
refreshIconPixmapSizes();
|
refreshIconPixmapSizes();
|
||||||
|
|
||||||
|
auto className = QWindowsContext::instance()->registerWindowClass(
|
||||||
|
QWindowsContext::classNamePrefix() + QLatin1String("ThemeChangeObserverWindow"),
|
||||||
|
qThemeChangeObserverWndProc);
|
||||||
|
// HWND_MESSAGE windows do not get the required theme events,
|
||||||
|
// so we use a real top-level window that we never show.
|
||||||
|
m_themeChangeObserver = CreateWindowEx(0, reinterpret_cast<LPCWSTR>(className.utf16()),
|
||||||
|
nullptr, WS_TILED, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
|
||||||
|
nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
|
||||||
|
Q_ASSERT(m_themeChangeObserver);
|
||||||
}
|
}
|
||||||
|
|
||||||
QWindowsTheme::~QWindowsTheme()
|
QWindowsTheme::~QWindowsTheme()
|
||||||
@ -498,6 +542,13 @@ QWindowsTheme::~QWindowsTheme()
|
|||||||
m_instance = nullptr;
|
m_instance = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QWindowsTheme::destroyThemeChangeWindow()
|
||||||
|
{
|
||||||
|
qCDebug(lcQpaTheme) << "Destroying theme change window";
|
||||||
|
DestroyWindow(m_themeChangeObserver);
|
||||||
|
m_themeChangeObserver = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
static inline QStringList iconThemeSearchPaths()
|
static inline QStringList iconThemeSearchPaths()
|
||||||
{
|
{
|
||||||
const QFileInfo appDir(QCoreApplication::applicationDirPath() + "/icons"_L1);
|
const QFileInfo appDir(QCoreApplication::applicationDirPath() + "/icons"_L1);
|
||||||
@ -594,7 +645,7 @@ Qt::ColorScheme QWindowsTheme::effectiveColorScheme()
|
|||||||
void QWindowsTheme::requestColorScheme(Qt::ColorScheme scheme)
|
void QWindowsTheme::requestColorScheme(Qt::ColorScheme scheme)
|
||||||
{
|
{
|
||||||
s_colorSchemeOverride = scheme;
|
s_colorSchemeOverride = scheme;
|
||||||
handleSettingsChanged();
|
handleThemeChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
Qt::ContrastPreference QWindowsTheme::contrastPreference() const
|
Qt::ContrastPreference QWindowsTheme::contrastPreference() const
|
||||||
@ -603,23 +654,27 @@ Qt::ContrastPreference QWindowsTheme::contrastPreference() const
|
|||||||
: Qt::ContrastPreference::NoPreference;
|
: Qt::ContrastPreference::NoPreference;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QWindowsTheme::handleSettingsChanged()
|
void QWindowsTheme::handleThemeChange()
|
||||||
{
|
{
|
||||||
|
QWindowsThemeCache::clearAllThemeCaches();
|
||||||
|
|
||||||
const auto oldColorScheme = s_colorScheme;
|
const auto oldColorScheme = s_colorScheme;
|
||||||
s_colorScheme = Qt::ColorScheme::Unknown; // make effectiveColorScheme() query registry
|
s_colorScheme = Qt::ColorScheme::Unknown; // make effectiveColorScheme() query registry
|
||||||
const auto newColorScheme = effectiveColorScheme();
|
s_colorScheme = effectiveColorScheme();
|
||||||
const bool colorSchemeChanged = newColorScheme != oldColorScheme;
|
if (s_colorScheme != oldColorScheme) {
|
||||||
s_colorScheme = newColorScheme;
|
// Only propagate color scheme changes if the scheme actually changed
|
||||||
if (!colorSchemeChanged)
|
auto integration = QWindowsIntegration::instance();
|
||||||
return;
|
integration->updateApplicationBadge();
|
||||||
auto integration = QWindowsIntegration::instance();
|
|
||||||
integration->updateApplicationBadge();
|
for (QWindowsWindow *w : std::as_const(QWindowsContext::instance()->windows()))
|
||||||
if (integration->darkModeHandling().testFlag(QWindowsApplication::DarkModeStyle)) {
|
w->setDarkBorder(s_colorScheme == Qt::ColorScheme::Dark);
|
||||||
QWindowsTheme::instance()->refresh();
|
|
||||||
QWindowSystemInterface::handleThemeChange<QWindowSystemInterface::SynchronousDelivery>();
|
|
||||||
}
|
}
|
||||||
for (QWindowsWindow *w : std::as_const(QWindowsContext::instance()->windows()))
|
|
||||||
w->setDarkBorder(s_colorScheme == Qt::ColorScheme::Dark);
|
// But always reset palette and fonts, and signal the theme
|
||||||
|
// change, as other parts of the theme could have changed,
|
||||||
|
// such as the accent color.
|
||||||
|
QWindowsTheme::instance()->refresh();
|
||||||
|
QWindowSystemInterface::handleThemeChange<QWindowSystemInterface::SynchronousDelivery>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QWindowsTheme::clearPalettes()
|
void QWindowsTheme::clearPalettes()
|
||||||
@ -784,12 +839,6 @@ QPlatformSystemTrayIcon *QWindowsTheme::createPlatformSystemTrayIcon() const
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void QWindowsTheme::windowsThemeChanged(QWindow * window)
|
|
||||||
{
|
|
||||||
refresh();
|
|
||||||
QWindowSystemInterface::handleThemeChange(window);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int fileIconSizes[FileIconSizeCount];
|
static int fileIconSizes[FileIconSizeCount];
|
||||||
|
|
||||||
void QWindowsTheme::refreshIconPixmapSizes()
|
void QWindowsTheme::refreshIconPixmapSizes()
|
||||||
|
@ -35,7 +35,7 @@ public:
|
|||||||
void requestColorScheme(Qt::ColorScheme scheme) override;
|
void requestColorScheme(Qt::ColorScheme scheme) override;
|
||||||
Qt::ContrastPreference contrastPreference() const override;
|
Qt::ContrastPreference contrastPreference() const override;
|
||||||
|
|
||||||
static void handleSettingsChanged();
|
static void handleThemeChange();
|
||||||
|
|
||||||
const QPalette *palette(Palette type = SystemPalette) const override
|
const QPalette *palette(Palette type = SystemPalette) const override
|
||||||
{ return m_palettes[type]; }
|
{ return m_palettes[type]; }
|
||||||
@ -47,7 +47,6 @@ public:
|
|||||||
QIcon fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions iconOptions = {}) const override;
|
QIcon fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions iconOptions = {}) const override;
|
||||||
QIconEngine *createIconEngine(const QString &iconName) const override;
|
QIconEngine *createIconEngine(const QString &iconName) const override;
|
||||||
|
|
||||||
void windowsThemeChanged(QWindow *window);
|
|
||||||
void displayChanged() { refreshIconPixmapSizes(); }
|
void displayChanged() { refreshIconPixmapSizes(); }
|
||||||
|
|
||||||
QList<QSize> availableFileIconSizes() const { return m_fileIconSizes; }
|
QList<QSize> availableFileIconSizes() const { return m_fileIconSizes; }
|
||||||
@ -83,9 +82,14 @@ private:
|
|||||||
static inline Qt::ColorScheme s_colorScheme = Qt::ColorScheme::Unknown;
|
static inline Qt::ColorScheme s_colorScheme = Qt::ColorScheme::Unknown;
|
||||||
static inline Qt::ColorScheme s_colorSchemeOverride = Qt::ColorScheme::Unknown;
|
static inline Qt::ColorScheme s_colorSchemeOverride = Qt::ColorScheme::Unknown;
|
||||||
|
|
||||||
|
friend class QWindowsContext;
|
||||||
|
|
||||||
QPalette *m_palettes[NPalettes];
|
QPalette *m_palettes[NPalettes];
|
||||||
QFont *m_fonts[NFonts];
|
QFont *m_fonts[NFonts];
|
||||||
QList<QSize> m_fileIconSizes;
|
QList<QSize> m_fileIconSizes;
|
||||||
|
|
||||||
|
HWND m_themeChangeObserver = nullptr;
|
||||||
|
void destroyThemeChangeWindow();
|
||||||
};
|
};
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
Loading…
x
Reference in New Issue
Block a user