Windows style: paint treeview indicators with correct dpr

The QTreeView indicators in windows style are drawn with the help of
windows themes. Sadly, indicators have to be drawn in the explorer
style.

This was already done correctly for visible windows. Hidden windows, however, using this drawing method, were not dpi-aware.
As a consequence, the dpi of the primary screen was used for drawing. This lead to wrong results on a second monitor with a different dpi.

Create a hidden window for every screen and use this for the painting
instead.

The fix can't be covered by an autotest. It will result in baseline test
mismatches, which must be accepted as new baselines.

Fixes: QTBUG-126533
Change-Id: Ie86cdb69f4cef1b293a5ac65541e5ada12959180
Reviewed-by: Axel Spoerl <axel.spoerl@qt.io>
(cherry picked from commit 7f08822c499f624246d3f4c35dd97a7ba81d85ae)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Christian Ehrlicher 2024-06-22 07:52:12 +02:00 committed by Qt Cherry-pick Bot
parent 2a677940a0
commit 68608585a5
3 changed files with 25 additions and 23 deletions

View File

@ -23,7 +23,7 @@ HTHEME QWindowsThemeData::handle()
return nullptr;
if (!htheme)
htheme = QWindowsVistaStylePrivate::createTheme(theme, QWindowsVistaStylePrivate::winId(widget));
htheme = QWindowsVistaStylePrivate::createTheme(theme, widget);
return htheme;
}

View File

@ -54,7 +54,7 @@ static const int windowsRightBorder = 15; // right border on windows
// QWindowsVistaStylePrivate -------------------------------------------------------------------------
// Static initializations
HWND QWindowsVistaStylePrivate::m_vistaTreeViewHelper = nullptr;
QVarLengthFlatMap<const QScreen *, HWND, 4> QWindowsVistaStylePrivate::m_vistaTreeViewHelpers;
bool QWindowsVistaStylePrivate::useVistaTheme = false;
Q_CONSTINIT QBasicAtomicInt QWindowsVistaStylePrivate::ref = Q_BASIC_ATOMIC_INITIALIZER(-1); // -1 based refcounting
@ -100,7 +100,7 @@ static inline Qt::Orientation progressBarOrientation(const QStyleOption *option
* a non-visible window handle, open the theme on it and insert it into
* the cache so that it is found by QWindowsThemeData::handle() first.
*/
static inline HWND createTreeViewHelperWindow()
static inline HWND createTreeViewHelperWindow(const QScreen *screen)
{
using QWindowsApplication = QNativeInterface::Private::QWindowsApplication;
@ -108,6 +108,10 @@ static inline HWND createTreeViewHelperWindow()
if (auto nativeWindowsApp = dynamic_cast<QWindowsApplication *>(QGuiApplicationPrivate::platformIntegration()))
result = nativeWindowsApp->createMessageWindow(QStringLiteral("QTreeViewThemeHelperWindowClass"),
QStringLiteral("QTreeViewThemeHelperWindow"));
const auto topLeft = screen->geometry().topLeft();
// make it a top-level window and move it the the correct screen to paint with the correct dpr later on
SetParent(result, NULL);
MoveWindow(result, topLeft.x(), topLeft.y(), 10, 10, FALSE);
return result;
}
@ -257,30 +261,26 @@ int QWindowsVistaStylePrivate::fixedPixelMetric(QStyle::PixelMetric pm)
return QWindowsVistaStylePrivate::InvalidMetric;
}
bool QWindowsVistaStylePrivate::initVistaTreeViewTheming()
bool QWindowsVistaStylePrivate::initVistaTreeViewTheming(const QScreen *screen)
{
if (m_vistaTreeViewHelper)
if (m_vistaTreeViewHelpers.contains(screen))
return true;
m_vistaTreeViewHelper = createTreeViewHelperWindow();
if (!m_vistaTreeViewHelper) {
qWarning("Unable to create the treeview helper window.");
return false;
}
if (FAILED(SetWindowTheme(m_vistaTreeViewHelper, L"explorer", nullptr))) {
HWND helper = createTreeViewHelperWindow(screen);
if (FAILED(SetWindowTheme(helper, L"explorer", nullptr)))
{
qErrnoWarning("SetWindowTheme() failed.");
cleanupVistaTreeViewTheming();
return false;
}
m_vistaTreeViewHelpers.insert(screen, helper);
return true;
}
void QWindowsVistaStylePrivate::cleanupVistaTreeViewTheming()
{
if (m_vistaTreeViewHelper) {
DestroyWindow(m_vistaTreeViewHelper);
m_vistaTreeViewHelper = nullptr;
}
for (auto it = m_vistaTreeViewHelpers.begin(); it != m_vistaTreeViewHelpers.end(); ++it)
DestroyWindow(it.value());
m_vistaTreeViewHelpers.clear();
}
/* \internal
@ -294,10 +294,12 @@ void QWindowsVistaStylePrivate::cleanupHandleMap()
QWindowsVistaStylePrivate::cleanupVistaTreeViewTheming();
}
HTHEME QWindowsVistaStylePrivate::createTheme(int theme, HWND hwnd)
HTHEME QWindowsVistaStylePrivate::createTheme(int theme, const QWidget *widget)
{
if (theme == VistaTreeViewTheme && QWindowsVistaStylePrivate::initVistaTreeViewTheming())
hwnd = QWindowsVistaStylePrivate::m_vistaTreeViewHelper;
const QScreen *screen = widget ? widget->screen() : qApp->primaryScreen();
HWND hwnd = QWindowsVistaStylePrivate::winId(widget);
if (theme == VistaTreeViewTheme && QWindowsVistaStylePrivate::initVistaTreeViewTheming(screen))
hwnd = QWindowsVistaStylePrivate::m_vistaTreeViewHelpers.value(screen);
return QWindowsThemeCache::createTheme(theme, hwnd);
}

View File

@ -25,7 +25,7 @@
#include <qpixmapcache.h>
#include <qstyleoption.h>
#include <QtWidgets/private/qwindowsstyle_p_p.h>
#include <qmap.h>
#include <QtCore/private/qflatmap_p.h>
#if QT_CONFIG(pushbutton)
#include <qpushbutton.h>
@ -114,7 +114,7 @@ public:
~QWindowsVistaStylePrivate()
{ cleanup(); }
static HTHEME createTheme(int theme, HWND hwnd);
static HTHEME createTheme(int theme, const QWidget *widget);
static QString themeName(int theme);
static bool isItemViewDelegateLineEdit(const QWidget *widget);
static int pixelMetricFromSystemDp(QStyle::PixelMetric pm, const QStyleOption *option = nullptr, const QWidget *widget = nullptr);
@ -154,7 +154,7 @@ public:
bool transitionsEnabled() const;
private:
static bool initVistaTreeViewTheming();
static bool initVistaTreeViewTheming(const QScreen *screen);
static void cleanupVistaTreeViewTheming();
static QBasicAtomicInt ref;
@ -168,7 +168,7 @@ private:
int bufferW = 0;
int bufferH = 0;
static HWND m_vistaTreeViewHelper;
static QVarLengthFlatMap<const QScreen *, HWND, 4> m_vistaTreeViewHelpers;
};
QT_END_NAMESPACE