From 2d223770ee33ddf7d93d83bc2cb538bfd66d38d9 Mon Sep 17 00:00:00 2001 From: Sine Striker <trueful@163.com> Date: 周三, 08 5月 2024 11:30:55 +0800 Subject: [PATCH] update --- src/core/contexts/win32windowcontext.cpp | 122 +++++++++++++++++++++++++++++----------- 1 files changed, 89 insertions(+), 33 deletions(-) diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index 171a348..93a186c 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/src/core/contexts/win32windowcontext.cpp @@ -1369,20 +1369,38 @@ // how to avoid doing this. // ### FIXME FIXME FIXME *result = ::DefWindowProcW(hWnd, WM_NCMOUSEMOVE, wParam, lParam); - } else if (lastHitTestResultRaw == HTSYSMENU) { - if (message == WM_NCLBUTTONDOWN) { - // A message of WM_SYSCOMMAND with SC_MOUSEMENU will be sent by Windows, - // and the current control flow will be blocked by the menu while - // Windows will create and execute a new event loop until the menu - // returns - iconButtonClickTime = ::GetTickCount64(); - *result = ::DefWindowProcW(hWnd, message, wParam, lParam); - iconButtonClickTime = 0; - } else if (message == WM_NCLBUTTONDBLCLK) { - // A message of WM_SYSCOMMAND with SC_CLOSE will be sent by Windows - *result = ::DefWindowProcW(hWnd, message, wParam, lParam); - } else { - *result = FALSE; + emulateClientAreaMessage(hWnd, message, wParam, lParam); + return true; + } + + if (lastHitTestResultRaw == HTSYSMENU) { + switch (message) { + case WM_NCLBUTTONDOWN: + if (iconButtonClickLevel == 0) { + // A message of WM_SYSCOMMAND with SC_MOUSEMENU will be sent by + // Windows, and the current control flow will be blocked by the + // menu while Windows will create and execute a new event loop + // until the menu returns + iconButtonClickTime = ::GetTickCount64(); + *result = + ::DefWindowProcW(hWnd, WM_NCLBUTTONDOWN, wParam, lParam); + if (iconButtonClickLevel == 2) { + ::PostMessageW(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0); + } + // No need to reset `iconButtonClickLevel`, if it has value, + // there must be another incoming WM_NCLBUTTONDOWN + } else { + iconButtonClickLevel = 0; + } + break; + case WM_NCLBUTTONDBLCLK: + // A message of WM_SYSCOMMAND with SC_CLOSE will be sent by Windows + *result = ::DefWindowProcW(hWnd, message, wParam, lParam); + break; + default: + *result = FALSE; + emulateClientAreaMessage(hWnd, message, wParam, lParam); + break; } } else { // According to MSDN, we should return non-zero for X button messages to @@ -1392,8 +1410,8 @@ (((message >= WM_NCXBUTTONDOWN) && (message <= WM_NCXBUTTONDBLCLK)) ? TRUE : FALSE); + emulateClientAreaMessage(hWnd, message, wParam, lParam); } - emulateClientAreaMessage(hWnd, message, wParam, lParam); return true; } break; @@ -2126,29 +2144,67 @@ break; } if (shouldShowSystemMenu) { - bool triggeredByIconButton = iconButtonClickTime > 0; - if (triggeredByIconButton) { - // TODO: Adjust `nativeGlobalPos` to (0, realTitleBarHeight) - // hint: use m_delgegate->mapGeometryToScene + static HHOOK mouseHook = nullptr; + static std::optional<POINT> mouseClickPos; + bool mouseHookedLocal = false; + + // The menu is triggered by a click on icon button + if (iconButtonClickTime > 0) { + POINT menuPos{0, static_cast<LONG>(getTitleBarHeight(hWnd))}; + if (const auto tb = titleBar()) { + auto titleBarHeight = qreal(m_delegate->mapGeometryToScene(tb).height()); + titleBarHeight *= m_windowHandle->devicePixelRatio(); + menuPos.y = qRound(titleBarHeight); + } + ::ClientToScreen(hWnd, &menuPos); + nativeGlobalPos = menuPos; + + // Install mouse hook + if (!mouseHook) { + mouseHook = ::SetWindowsHookExW( + WH_MOUSE, + [](int nCode, WPARAM wParam, LPARAM lParam) { + if (nCode >= 0) { + if (wParam == WM_LBUTTONDOWN || wParam == WM_LBUTTONDBLCLK) { + auto pMouseStruct = reinterpret_cast<MOUSEHOOKSTRUCT *>(lParam); + if (pMouseStruct) { + mouseClickPos = pMouseStruct->pt; + } + } + } + return ::CallNextHookEx(nullptr, nCode, wParam, lParam); + }, + nullptr, ::GetCurrentThreadId()); + mouseHookedLocal = true; + } } + bool res = showSystemMenu_sys(hWnd, nativeGlobalPos, broughtByKeyboard, m_delegate->isHostSizeFixed(m_host)); - // Emulate the Windows SYSMENU button's behavior - static uint32_t doubleClickTime = ::GetDoubleClickTime(); - if (triggeredByIconButton && !res && - ::GetTickCount64() - iconButtonClickTime <= doubleClickTime) { - POINT nativeGlobalPos; - ::GetCursorPos(&nativeGlobalPos); - POINT nativeLocalPos = nativeGlobalPos; - ::ScreenToClient(hWnd, &nativeLocalPos); - QPoint qtScenePos = - QHighDpi::fromNativeLocalPosition(point2qpoint(nativeLocalPos), m_windowHandle); - WindowAgentBase::SystemButton sysButtonType = WindowAgentBase::Unknown; - if (isInSystemButtons(qtScenePos, &sysButtonType) && - sysButtonType == WindowAgentBase::WindowIcon) { - ::PostMessageW(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0); + // Uninstall mouse hook and check if it's a double-click + if (mouseHookedLocal) { + ::UnhookWindowsHookEx(mouseHook); + + // Emulate the Windows icon button's behavior + static uint32_t doubleClickTime = ::GetDoubleClickTime(); + if (!res && mouseClickPos.has_value()) { + POINT nativeLocalPos = mouseClickPos.value(); + ::ScreenToClient(hWnd, &nativeLocalPos); + QPoint qtScenePos = QHighDpi::fromNativeLocalPosition( + point2qpoint(nativeLocalPos), m_windowHandle); + WindowAgentBase::SystemButton sysButtonType = WindowAgentBase::Unknown; + if (isInSystemButtons(qtScenePos, &sysButtonType) && + sysButtonType == WindowAgentBase::WindowIcon) { + iconButtonClickLevel = 1; + if (::GetTickCount64() - iconButtonClickTime <= doubleClickTime) { + iconButtonClickLevel = 2; + } + } } + + mouseHook = nullptr; + mouseClickPos.reset(); } // QPA's internal code will handle system menu events separately, and its -- Gitblit v1.9.1