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>
This commit is contained in:
Yuhang Zhao 2023-04-20 16:06:19 +08:00 committed by Yuhang Zhao
parent 8930ea128f
commit a203e2c3ca
5 changed files with 109 additions and 6 deletions

View File

@ -161,10 +161,10 @@ if(NOT FRAMELESSHELPER_NO_SUMMARY)
message("Toolchain file: ${CMAKE_TOOLCHAIN_FILE}") message("Toolchain file: ${CMAKE_TOOLCHAIN_FILE}")
message("------------------------------ Qt -------------------------------") message("------------------------------ Qt -------------------------------")
query_qt_paths(SDK_DIR __qt_inst_dir) 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 installation directory: ${__qt_inst_dir}")
message("Qt SDK version: ${QT_VERSION}") message("Qt SDK version: ${__qt_version}")
query_qt_library_info(STATIC __qt_lib_type) if(__qt_static_lib)
if(__qt_lib_type)
message("Qt SDK library type: static") message("Qt SDK library type: static")
else() else()
message("Qt SDK library type: shared") message("Qt SDK library type: shared")

2
cmake

@ -1 +1 @@
Subproject commit 60329b55dcfaf41da63f134a90ddce82dc9b6c4a Subproject commit be821fca7e90858343540a487d663dca52bf0cd5

View File

@ -135,6 +135,8 @@ FRAMELESSHELPER_CORE_API void fixupChildWindowsDpiMessage(const WId windowId);
FRAMELESSHELPER_CORE_API void fixupDialogsDpiScaling(); FRAMELESSHELPER_CORE_API void fixupDialogsDpiScaling();
FRAMELESSHELPER_CORE_API void setDarkModeAllowedForApp(const bool allow = true); FRAMELESSHELPER_CORE_API void setDarkModeAllowedForApp(const bool allow = true);
FRAMELESSHELPER_CORE_API void bringWindowToFront(const WId windowId); 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 #endif // Q_OS_WINDOWS
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX

View File

@ -84,6 +84,8 @@ FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent)
FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) FRAMELESSHELPER_STRING_CONSTANT(FindWindowW)
FRAMELESSHELPER_STRING_CONSTANT(UnregisterClassW) FRAMELESSHELPER_STRING_CONSTANT(UnregisterClassW)
FRAMELESSHELPER_STRING_CONSTANT(DestroyWindow) FRAMELESSHELPER_STRING_CONSTANT(DestroyWindow)
FRAMELESSHELPER_STRING_CONSTANT(GetWindowPlacement)
FRAMELESSHELPER_STRING_CONSTANT(SetWindowPlacement)
[[maybe_unused]] static constexpr const char kFallbackTitleBarErrorMessage[] = [[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" "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, " " 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; bool trackingMouse = false;
WId fallbackTitleBarWindowId = 0; WId fallbackTitleBarWindowId = 0;
Dpi dpi = {}; Dpi dpi = {};
QRect restoreGeometry = {};
}; };
struct Win32Helper struct Win32Helper
@ -1184,10 +1187,11 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
#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
// we have done here will always be overwritten. // 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 // Sync the internal window frame margins with the latest DPI, otherwise
// we will get wrong window sizes after the DPI change. // 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)) #endif // (QT_VERSION <= QT_VERSION_CHECK(6, 4, 2))
} break; } 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. // Re-apply the custom window frame if recovered from the basic theme.
Utils::updateWindowFrameMargins(windowId, false); Utils::updateWindowFrameMargins(windowId, false);
} break; } 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: default:
break; break;
} }

View File

@ -2390,4 +2390,57 @@ void Utils::bringWindowToFront(const WId windowId)
moveWindowToMonitor(hwnd, activeMonitor.value()); moveWindowToMonitor(hwnd, activeMonitor.value());
} }
QPoint Utils::getWindowPlacementOffset(const WId windowId)
{
Q_ASSERT(windowId);
if (!windowId) {
return {};
}
const auto hwnd = reinterpret_cast<HWND>(windowId);
SetLastError(ERROR_SUCCESS);
const auto exStyle = static_cast<DWORD>(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<HWND>(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 FRAMELESSHELPER_END_NAMESPACE