From a203e2c3ca8bf3f7cc9b6ea6c4915cc44ce913a5 Mon Sep 17 00:00:00 2001 From: Yuhang Zhao Date: Thu, 20 Apr 2023 16:06:19 +0800 Subject: [PATCH] win32: fix restore geometry bug The upstream fix has not been merged yet, however, it will be in 6.5.1 for sure. Fixes: #20 Signed-off-by: Yuhang Zhao <2546789017@qq.com> --- CMakeLists.txt | 6 ++-- cmake | 2 +- include/FramelessHelper/Core/utils.h | 2 ++ src/core/framelesshelper_win.cpp | 52 +++++++++++++++++++++++++-- src/core/utils_win.cpp | 53 ++++++++++++++++++++++++++++ 5 files changed, 109 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a7708fbe..a6d98060 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -161,10 +161,10 @@ if(NOT FRAMELESSHELPER_NO_SUMMARY) message("Toolchain file: ${CMAKE_TOOLCHAIN_FILE}") message("------------------------------ Qt -------------------------------") query_qt_paths(SDK_DIR __qt_inst_dir) + query_qt_library_info(VERSION __qt_version STATIC __qt_static_lib) message("Qt SDK installation directory: ${__qt_inst_dir}") - message("Qt SDK version: ${QT_VERSION}") - query_qt_library_info(STATIC __qt_lib_type) - if(__qt_lib_type) + message("Qt SDK version: ${__qt_version}") + if(__qt_static_lib) message("Qt SDK library type: static") else() message("Qt SDK library type: shared") diff --git a/cmake b/cmake index 60329b55..be821fca 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 60329b55dcfaf41da63f134a90ddce82dc9b6c4a +Subproject commit be821fca7e90858343540a487d663dca52bf0cd5 diff --git a/include/FramelessHelper/Core/utils.h b/include/FramelessHelper/Core/utils.h index af10a806..8012c750 100644 --- a/include/FramelessHelper/Core/utils.h +++ b/include/FramelessHelper/Core/utils.h @@ -135,6 +135,8 @@ FRAMELESSHELPER_CORE_API void fixupChildWindowsDpiMessage(const WId windowId); FRAMELESSHELPER_CORE_API void fixupDialogsDpiScaling(); FRAMELESSHELPER_CORE_API void setDarkModeAllowedForApp(const bool allow = true); FRAMELESSHELPER_CORE_API void bringWindowToFront(const WId windowId); +[[nodiscard]] FRAMELESSHELPER_CORE_API QPoint getWindowPlacementOffset(const WId windowId); +[[nodiscard]] FRAMELESSHELPER_CORE_API QRect getWindowRestoreFrameGeometry(const WId windowId); #endif // Q_OS_WINDOWS #ifdef Q_OS_LINUX diff --git a/src/core/framelesshelper_win.cpp b/src/core/framelesshelper_win.cpp index a32a6901..892585ea 100644 --- a/src/core/framelesshelper_win.cpp +++ b/src/core/framelesshelper_win.cpp @@ -84,6 +84,8 @@ FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent) FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) FRAMELESSHELPER_STRING_CONSTANT(UnregisterClassW) FRAMELESSHELPER_STRING_CONSTANT(DestroyWindow) +FRAMELESSHELPER_STRING_CONSTANT(GetWindowPlacement) +FRAMELESSHELPER_STRING_CONSTANT(SetWindowPlacement) [[maybe_unused]] static constexpr const char kFallbackTitleBarErrorMessage[] = "FramelessHelper is unable to create the fallback title bar window, and thus the snap layout feature will be disabled" " unconditionally. You can ignore this error and continue running your application, nothing else will be affected, " @@ -98,6 +100,7 @@ struct Win32HelperData bool trackingMouse = false; WId fallbackTitleBarWindowId = 0; Dpi dpi = {}; + QRect restoreGeometry = {}; }; struct Win32Helper @@ -1184,10 +1187,11 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me #if (QT_VERSION <= QT_VERSION_CHECK(6, 4, 2)) // We need to wait until Qt has handled this message, otherwise everything // we have done here will always be overwritten. - QTimer::singleShot(0, qApp, [data](){ // Copy the variables intentionally, otherwise they'll go out of scope when Qt finally use them. + QWindow *window = data.params.getWindowHandle(); + QTimer::singleShot(0, qApp, [window](){ // Sync the internal window frame margins with the latest DPI, otherwise // we will get wrong window sizes after the DPI change. - Utils::updateInternalWindowFrameMargins(data.params.getWindowHandle(), true); + Utils::updateInternalWindowFrameMargins(window, true); }); #endif // (QT_VERSION <= QT_VERSION_CHECK(6, 4, 2)) } break; @@ -1195,6 +1199,50 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me // Re-apply the custom window frame if recovered from the basic theme. Utils::updateWindowFrameMargins(windowId, false); } break; +#if (QT_VERSION < QT_VERSION_CHECK(6, 5, 1)) + case WM_ENTERSIZEMOVE: + case WM_EXITSIZEMOVE: { + if (!Utils::isWindowNoState(windowId)) { + break; + } + const QRect rect = Utils::getWindowRestoreFrameGeometry(windowId); + if (rect.isNull() || !rect.isValid()) { + WARNING << "The calculated restore geometry is invalid."; + break; + } + const QMutexLocker locker(&g_win32Helper()->mutex); + g_win32Helper()->data[windowId].restoreGeometry = rect; + } break; + case WM_SIZE: { + if (wParam != SIZE_MAXIMIZED) { + break; + } + if (data.restoreGeometry.isNull() || !data.restoreGeometry.isValid()) { + const QRect rect = Utils::getWindowRestoreFrameGeometry(windowId); + if (rect.isValid() && !rect.isNull()) { + const QMutexLocker locker(&g_win32Helper()->mutex); + g_win32Helper()->data[windowId].restoreGeometry = rect; + } else { + WARNING << "The calculated restore geometry is invalid."; + } + break; + } + WINDOWPLACEMENT wp; + SecureZeroMemory(&wp, sizeof(wp)); + wp.length = sizeof(wp); + if (GetWindowPlacement(hWnd, &wp) == FALSE) { + WARNING << Utils::getSystemErrorMessage(kGetWindowPlacement); + break; + } + wp.rcNormalPosition = { + data.restoreGeometry.left(), data.restoreGeometry.top(), + data.restoreGeometry.right(), data.restoreGeometry.bottom() + }; + if (SetWindowPlacement(hWnd, &wp) == FALSE) { + WARNING << Utils::getSystemErrorMessage(kSetWindowPlacement); + } + } break; +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 5, 1)) default: break; } diff --git a/src/core/utils_win.cpp b/src/core/utils_win.cpp index 625eec8c..180166ff 100644 --- a/src/core/utils_win.cpp +++ b/src/core/utils_win.cpp @@ -2390,4 +2390,57 @@ void Utils::bringWindowToFront(const WId windowId) moveWindowToMonitor(hwnd, activeMonitor.value()); } +QPoint Utils::getWindowPlacementOffset(const WId windowId) +{ + Q_ASSERT(windowId); + if (!windowId) { + return {}; + } + const auto hwnd = reinterpret_cast(windowId); + SetLastError(ERROR_SUCCESS); + const auto exStyle = static_cast(GetWindowLongPtrW(hwnd, GWL_EXSTYLE)); + if (exStyle == 0) { + WARNING << getSystemErrorMessage(kGetWindowLongPtrW); + return {}; + } + // Tool windows are special and they don't need any offset. + if (exStyle & WS_EX_TOOLWINDOW) { + return {}; + } + const HMONITOR mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY); + if (!mon) { + WARNING << getSystemErrorMessage(kMonitorFromWindow); + return {}; + } + MONITORINFOEXW mi; + SecureZeroMemory(&mi, sizeof(mi)); + mi.cbSize = sizeof(mi); + if (GetMonitorInfoW(mon, &mi) == FALSE) { + WARNING << getSystemErrorMessage(kGetMonitorInfoW); + return {}; + } + return {mi.rcWork.left - mi.rcMonitor.left, mi.rcWork.top - mi.rcMonitor.top}; +} + +QRect Utils::getWindowRestoreFrameGeometry(const WId windowId) +{ + Q_ASSERT(windowId); + if (!windowId) { + return {}; + } + const auto hwnd = reinterpret_cast(windowId); + WINDOWPLACEMENT wp; + SecureZeroMemory(&wp, sizeof(wp)); + wp.length = sizeof(wp); + if (GetWindowPlacement(hwnd, &wp) == FALSE) { + WARNING << getSystemErrorMessage(kGetWindowPlacement); + return {}; + } + const RECT rawRect = wp.rcNormalPosition; + const QPoint topLeft = {rawRect.left, rawRect.top}; + const QSize size = {rawRect.right - rawRect.left, rawRect.bottom - rawRect.top}; + const QPoint offset = getWindowPlacementOffset(windowId); + return QRect{topLeft, size}.translated(offset); +} + FRAMELESSHELPER_END_NAMESPACE