win: fix wrong restore geometry when after DPI changes

This commit is contained in:
Yuhang Zhao 2023-04-25 15:19:31 +08:00
parent 30d2261baf
commit 41e91bb39a
5 changed files with 74 additions and 12 deletions

View File

@ -442,6 +442,16 @@ struct Dpi
{ {
quint32 x = 0; quint32 x = 0;
quint32 y = 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 } // namespace Global

View File

@ -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 fromNativeLocalPosition(const QWindow *window, const QPoint &point);
[[nodiscard]] FRAMELESSHELPER_CORE_API QPoint fromNativeGlobalPosition(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 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 #ifdef Q_OS_WINDOWS
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowsVersionOrGreater(const Global::WindowsVersion version); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowsVersionOrGreater(const Global::WindowsVersion version);

View File

@ -1144,16 +1144,12 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
*result = FALSE; // Use the default linear DPI scaling provided by Windows. *result = FALSE; // Use the default linear DPI scaling provided by Windows.
return true; // Jump over Qt's wrong handling logic. 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 auto newDpi = UINT(wParam);
const qreal newDpr = Utils::roundScaleFactor(qreal(newDpi) / defaultDpi); const QSize oldSize = {RECT_WIDTH(clientRect), RECT_HEIGHT(clientRect)};
const QSizeF newSize = (oldSize / oldDpr * newDpr); const QSize newSize = Utils::rescaleSize(oldSize, data.dpi.x, newDpi);
const auto suggestedSize = reinterpret_cast<LPSIZE>(lParam); const auto suggestedSize = reinterpret_cast<LPSIZE>(lParam);
suggestedSize->cx = std::round(newSize.width()); suggestedSize->cx = newSize.width();
suggestedSize->cy = std::round(newSize.height()); suggestedSize->cy = newSize.height();
// If the window frame is visible, we need to expand the suggested size, currently // 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 // it's pure client size, we need to add the frame size to it. Windows expects a
// full window size, including the window frame. // 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)) #endif // (QT_VERSION <= QT_VERSION_CHECK(6, 4, 2))
case WM_DPICHANGED: { case WM_DPICHANGED: {
const Dpi dpi = {UINT(LOWORD(wParam)), UINT(HIWORD(wParam))}; const Dpi oldDpi = data.dpi;
DEBUG.noquote() << "New DPI for window" << hwnd2str(hWnd) << "is" << 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()->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(); g_win32Helper()->mutex.unlock();
#if (QT_VERSION <= QT_VERSION_CHECK(6, 4, 2)) #if (QT_VERSION <= QT_VERSION_CHECK(6, 4, 2))
// We need to wait until Qt has handled this message, otherwise everything // We need to wait until Qt has handled this message, otherwise everything

View File

@ -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) QDebug operator<<(QDebug d, const FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::Dpi &dpi)
{ {
const QDebugStateSaver saver(d); 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(" d.nospace().noquote() << "Dpi("
<< "x: " << dpi.x << ", " << "x: " << dpi.x << ", "
<< "y: " << dpi.y << ", " << "y: " << dpi.y << ", "

View File

@ -522,4 +522,42 @@ int Utils::horizontalAdvance(const QFontMetrics &fm, const QString &str)
#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) #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 FRAMELESSHELPER_END_NAMESPACE