Fix QMenu (+other theme) sizes on Windows multiscreen systems

The reason this worked before is unclear. It could be suspected
that we have made a dpi awareness change or Microsoft changed
the behavior of the OpenThemeData function.

Regardless, we expect the result to match the primary display which
OpenThemeData doesn't do (anymore). Instead it returns a value based on
the hwnd screen (which btw didn't always match the widget) and the cache
system would then re-use that theme also for hwnds on other screens.

The most obvious solution is to use OpenThemeDataForDpi to make sure
we get a theme result matching the primary sceen. Then our correction
of the result by with multiplying
QWindowsStylePrivate::nativeMetricScaleFactor(widget)
works again.

This fix does not only fix QMenu sizes. It fixes the size for all
widgets that use this theme function, which could return near
random results before.

We load this library dynamically since MinGW 11.2.0 won't link with it.

[ChangeLog][QWidgets][QMenu] Fixed menu sizes on Windows systems
with more screens.

Fixes: QTBUG-112911
Pick-to: 6.5
Change-Id: I8fdfde2ef5b2aa407cbc74c85afe2c0b74026cff
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
Reviewed-by: Yuhang Zhao <yuhangzhao@deepin.org>
Reviewed-by: Santhosh Kumar <santhosh.kumar.selvaraj@qt.io>
This commit is contained in:
Thorbjørn Lund Martsum 2023-04-19 08:21:50 +02:00
parent dcff882f30
commit 8c0153a526
2 changed files with 33 additions and 1 deletions

View File

@ -12,6 +12,7 @@
#include <private/qstylehelper_p.h>
#include <qpa/qplatformnativeinterface.h>
#include <private/qapplication_p.h>
#include <private/qsystemlibrary_p.h>
#include "qdrawutil.h" // for now
#include <qbackingstore.h>
@ -220,6 +221,33 @@ bool QWindowsVistaStylePrivate::transitionsEnabled() const
return false;
}
HTHEME QWindowsVistaStylePrivate::openThemeForPrimaryScreenDpi(HWND hwnd, const wchar_t *name)
{
// We want to call OpenThemeDataForDpi, but it won't link with MinGW (11.2.0), so we
// dynamically load this.
using FuncThemeDpi = decltype(&::OpenThemeDataForDpi);
// Only try to initialize openThemeForDpiFunc once. If it fails, it will likely keep failing.
const FuncThemeDpi uninitializedFunction = reinterpret_cast<FuncThemeDpi>(1);
static FuncThemeDpi openThemeForDpiFunc = uninitializedFunction;
if (openThemeForDpiFunc == uninitializedFunction) {
QSystemLibrary uxthemeLib(L"uxtheme.dll");
openThemeForDpiFunc = reinterpret_cast<FuncThemeDpi>(uxthemeLib.resolve("OpenThemeDataForDpi"));
if (!openThemeForDpiFunc) {
qWarning() << "QWindowsVistaStylePrivate: Load OpenThemeDataForDpi in uxtheme.dll failed";
}
}
// If we have screens and the openThemeDataForDpi function then use it :).
if (openThemeForDpiFunc && QGuiApplication::primaryScreen()) {
const int dpi = qRound(QGuiApplication::primaryScreen()->handle()->logicalDpi().first);
return openThemeForDpiFunc(hwnd, name, dpi);
}
// In case of any issues we fall back to use the plain/old OpenThemeData.
return OpenThemeData(hwnd, name);
}
int QWindowsVistaStylePrivate::pixelMetricFromSystemDp(QStyle::PixelMetric pm, const QStyleOption *option, const QWidget *widget)
{
switch (pm) {
@ -323,7 +351,8 @@ HTHEME QWindowsVistaStylePrivate::createTheme(int theme, HWND hwnd)
const wchar_t *name = themeNames[theme];
if (theme == VistaTreeViewTheme && QWindowsVistaStylePrivate::initVistaTreeViewTheming())
hwnd = QWindowsVistaStylePrivate::m_vistaTreeViewHelper;
m_themes[theme] = OpenThemeData(hwnd, name);
// Use dpi from primary screen in theme.
m_themes[theme] = openThemeForPrimaryScreenDpi(hwnd, name);
if (Q_UNLIKELY(!m_themes[theme]))
qErrnoWarning("OpenThemeData() failed for theme %d (%s).",
theme, qPrintable(themeName(theme)));

View File

@ -77,6 +77,7 @@
#endif
#include <qlabel.h>
#include <qheaderview.h>
#include <uxtheme.h>
QT_BEGIN_NAMESPACE
@ -153,6 +154,8 @@ public:
QTime animationTime() const;
bool transitionsEnabled() const;
static HTHEME openThemeForPrimaryScreenDpi(HWND hwnd, const wchar_t *name);
private:
static bool initVistaTreeViewTheming();
static void cleanupVistaTreeViewTheming();