From 41e91bb39a896d8bd442c48d1228b4d13723da30 Mon Sep 17 00:00:00 2001 From: Yuhang Zhao Date: Tue, 25 Apr 2023 15:19:31 +0800 Subject: [PATCH] win: fix wrong restore geometry when after DPI changes --- .../Core/framelesshelpercore_global.h | 10 +++++ include/FramelessHelper/Core/utils.h | 2 + src/core/framelesshelper_win.cpp | 29 ++++++++------ src/core/framelesshelpercore_global.cpp | 7 +++- src/core/utils.cpp | 38 +++++++++++++++++++ 5 files changed, 74 insertions(+), 12 deletions(-) diff --git a/include/FramelessHelper/Core/framelesshelpercore_global.h b/include/FramelessHelper/Core/framelesshelpercore_global.h index 59d4a69..2a43d4d 100644 --- a/include/FramelessHelper/Core/framelesshelpercore_global.h +++ b/include/FramelessHelper/Core/framelesshelpercore_global.h @@ -442,6 +442,16 @@ struct Dpi { quint32 x = 0; quint32 y = 0; + + [[nodiscard]] friend constexpr bool operator==(const Dpi &lhs, const Dpi &rhs) noexcept + { + return ((lhs.x == rhs.x) && (lhs.y == rhs.y)); + } + + [[nodiscard]] friend constexpr bool operator!=(const Dpi &lhs, const Dpi &rhs) noexcept + { + return !operator==(lhs, rhs); + } }; } // namespace Global diff --git a/include/FramelessHelper/Core/utils.h b/include/FramelessHelper/Core/utils.h index 20d71c1..0d6d66c 100644 --- a/include/FramelessHelper/Core/utils.h +++ b/include/FramelessHelper/Core/utils.h @@ -81,6 +81,8 @@ FRAMELESSHELPER_CORE_API void registerThemeChangeNotification(); [[nodiscard]] FRAMELESSHELPER_CORE_API QPoint fromNativeLocalPosition(const QWindow *window, const QPoint &point); [[nodiscard]] FRAMELESSHELPER_CORE_API QPoint fromNativeGlobalPosition(const QWindow *window, const QPoint &point); [[nodiscard]] FRAMELESSHELPER_CORE_API int horizontalAdvance(const QFontMetrics &fm, const QString &str); +[[nodiscard]] FRAMELESSHELPER_CORE_API qreal getRelativeScaleFactor(const quint32 oldDpi, const quint32 newDpi); +[[nodiscard]] FRAMELESSHELPER_CORE_API QSize rescaleSize(const QSize &oldSize, const quint32 oldDpi, const quint32 newDpi); #ifdef Q_OS_WINDOWS [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowsVersionOrGreater(const Global::WindowsVersion version); diff --git a/src/core/framelesshelper_win.cpp b/src/core/framelesshelper_win.cpp index 69c315f..afd473d 100644 --- a/src/core/framelesshelper_win.cpp +++ b/src/core/framelesshelper_win.cpp @@ -1144,16 +1144,12 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me *result = FALSE; // Use the default linear DPI scaling provided by Windows. return true; // Jump over Qt's wrong handling logic. } - const QSizeF oldSize = {qreal(RECT_WIDTH(clientRect)), qreal(RECT_HEIGHT(clientRect))}; - static constexpr const auto defaultDpi = qreal(USER_DEFAULT_SCREEN_DPI); - // We need to round the scale factor according to Qt's rounding policy. - const qreal oldDpr = Utils::roundScaleFactor(qreal(data.dpi.x) / defaultDpi); const auto newDpi = UINT(wParam); - const qreal newDpr = Utils::roundScaleFactor(qreal(newDpi) / defaultDpi); - const QSizeF newSize = (oldSize / oldDpr * newDpr); + const QSize oldSize = {RECT_WIDTH(clientRect), RECT_HEIGHT(clientRect)}; + const QSize newSize = Utils::rescaleSize(oldSize, data.dpi.x, newDpi); const auto suggestedSize = reinterpret_cast(lParam); - suggestedSize->cx = std::round(newSize.width()); - suggestedSize->cy = std::round(newSize.height()); + suggestedSize->cx = newSize.width(); + suggestedSize->cy = newSize.height(); // If the window frame is visible, we need to expand the suggested size, currently // it's pure client size, we need to add the frame size to it. Windows expects a // full window size, including the window frame. @@ -1171,10 +1167,21 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me } #endif // (QT_VERSION <= QT_VERSION_CHECK(6, 4, 2)) case WM_DPICHANGED: { - const Dpi dpi = {UINT(LOWORD(wParam)), UINT(HIWORD(wParam))}; - DEBUG.noquote() << "New DPI for window" << hwnd2str(hWnd) << "is" << dpi; + const Dpi oldDpi = data.dpi; + const Dpi newDpi = {UINT(LOWORD(wParam)), UINT(HIWORD(wParam))}; + if (Q_UNLIKELY(newDpi == oldDpi)) { + WARNING << "Wrong WM_DPICHANGED received: same DPI."; + break; + } + DEBUG.noquote() << "New DPI for window" << hwnd2str(hWnd) + << "is" << newDpi << "(was" << oldDpi << ")."; g_win32Helper()->mutex.lock(); - g_win32Helper()->data[windowId].dpi = dpi; + g_win32Helper()->data[windowId].dpi = newDpi; + if (data.restoreGeometry.isValid() && !data.restoreGeometry.isNull()) { + // Update the window size only. The position should not be changed. + g_win32Helper()->data[windowId].restoreGeometry.setSize( + Utils::rescaleSize(data.restoreGeometry.size(), oldDpi.x, newDpi.x)); + } g_win32Helper()->mutex.unlock(); #if (QT_VERSION <= QT_VERSION_CHECK(6, 4, 2)) // We need to wait until Qt has handled this message, otherwise everything diff --git a/src/core/framelesshelpercore_global.cpp b/src/core/framelesshelpercore_global.cpp index 93f5a7c..ec35204 100644 --- a/src/core/framelesshelpercore_global.cpp +++ b/src/core/framelesshelpercore_global.cpp @@ -87,7 +87,12 @@ QDebug operator<<(QDebug d, const FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::Ver QDebug operator<<(QDebug d, const FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::Dpi &dpi) { const QDebugStateSaver saver(d); - const qreal scaleFactor = (qreal(dpi.x) / qreal(96)); +#ifdef Q_OS_MACOS + static constexpr const auto defaultDpi = quint32(72); +#else // !Q_OS_MACOS + static constexpr const auto defaultDpi = quint32(96); +#endif // Q_OS_MACOS + const qreal scaleFactor = (qreal(dpi.x) / qreal(defaultDpi)); d.nospace().noquote() << "Dpi(" << "x: " << dpi.x << ", " << "y: " << dpi.y << ", " diff --git a/src/core/utils.cpp b/src/core/utils.cpp index 15c88d1..4bb13b0 100644 --- a/src/core/utils.cpp +++ b/src/core/utils.cpp @@ -522,4 +522,42 @@ int Utils::horizontalAdvance(const QFontMetrics &fm, const QString &str) #endif // (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) } +qreal Utils::getRelativeScaleFactor(const quint32 oldDpi, const quint32 newDpi) +{ + if (newDpi == oldDpi) { + return qreal(1); + } +#ifdef Q_OS_MACOS + static constexpr const auto defaultDpi = quint32(72); +#else // !Q_OS_MACOS + static constexpr const auto defaultDpi = quint32(96); +#endif // Q_OS_MACOS + if ((oldDpi < defaultDpi) || (newDpi < defaultDpi)) { + return qreal(1); + } + // We need to round the scale factor according to Qt's rounding policy. + const qreal oldDpr = roundScaleFactor(qreal(oldDpi) / qreal(defaultDpi)); + const qreal newDpr = roundScaleFactor(qreal(newDpi) / qreal(defaultDpi)); + return qreal(newDpr / oldDpr); +} + +QSize Utils::rescaleSize(const QSize &oldSize, const quint32 oldDpi, const quint32 newDpi) +{ + if (oldSize.isEmpty()) { + return {}; + } + if (newDpi == oldDpi) { + return oldSize; + } + const qreal scaleFactor = getRelativeScaleFactor(oldDpi, newDpi); + if (qFuzzyIsNull(scaleFactor)) { + return {}; + } + if (qFuzzyCompare(scaleFactor, qreal(1))) { + return oldSize; + } + const QSizeF newSize = QSizeF(oldSize) * scaleFactor; + return newSize.toSize(); // The numbers will be rounded to the nearest integer. +} + FRAMELESSHELPER_END_NAMESPACE