From 5778d4540f0b522946c84c8ba601456f2d440f78 Mon Sep 17 00:00:00 2001 From: Sine Striker <trueful@163.com> Date: 周三, 08 5月 2024 13:58:59 +0800 Subject: [PATCH] Fix a mistake --- src/core/contexts/win32windowcontext.cpp | 154 ++++++++++++++++++++++++++++++++++++++++----------- 1 files changed, 120 insertions(+), 34 deletions(-) diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index 171a348..9d37d7c 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/src/core/contexts/win32windowcontext.cpp @@ -35,6 +35,12 @@ namespace QWK { + enum IconButtonClickLevelFlag { + IconButtonClicked = 1, + IconButtonDoubleClicked = 2, + IconButtonTriggersClose = 4, + }; + // The thickness of an auto-hide taskbar in pixels. static constexpr const quint8 kAutoHideTaskBarThickness = 2; @@ -1369,20 +1375,42 @@ // 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, message, wParam, lParam); + iconButtonClickTime = 0; + if (iconButtonClickLevel & IconButtonTriggersClose) { + ::PostMessageW(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0); + } + if (iconButtonClickLevel & IconButtonDoubleClicked) { + iconButtonClickLevel = 0; + } + // Otherwise, no need to reset `iconButtonClickLevel` if not to + // close, 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 +1420,8 @@ (((message >= WM_NCXBUTTONDOWN) && (message <= WM_NCXBUTTONDBLCLK)) ? TRUE : FALSE); + emulateClientAreaMessage(hWnd, message, wParam, lParam); } - emulateClientAreaMessage(hWnd, message, wParam, lParam); return true; } break; @@ -2078,7 +2106,10 @@ const POINT nativeLocalPos = getNativePosFromMouse(); const QPoint qtScenePos = QHighDpi::fromNativeLocalPosition(point2qpoint(nativeLocalPos), m_windowHandle); - if (isInTitleBarDraggableArea(qtScenePos)) { + WindowAgentBase::SystemButton sysButtonType = WindowAgentBase::Unknown; + if (isInTitleBarDraggableArea(qtScenePos) || + (isInSystemButtons(qtScenePos, &sysButtonType) && + sysButtonType == WindowAgentBase::WindowIcon)) { shouldShowSystemMenu = true; nativeGlobalPos = nativeLocalPos; ::ClientToScreen(hWnd, &nativeGlobalPos); @@ -2126,29 +2157,84 @@ 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; + static bool mouseDoubleClicked = false; + 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) { + switch (wParam) { + case WM_LBUTTONDBLCLK: + mouseDoubleClicked = true; + Q_FALLTHROUGH(); + + // case WM_POINTERDOWN: + + case WM_LBUTTONDOWN: { + auto pMouseStruct = + reinterpret_cast<MOUSEHOOKSTRUCT *>(lParam); + if (pMouseStruct) { + mouseClickPos = pMouseStruct->pt; + } + break; + } + default: + break; + } + } + 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 + 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 |= IconButtonClicked; + if (::GetTickCount64() - iconButtonClickTime <= ::GetDoubleClickTime()) { + iconButtonClickLevel |= IconButtonTriggersClose; + } + } } + + if (mouseDoubleClicked) { + iconButtonClickLevel |= IconButtonDoubleClicked; + } + + mouseHook = nullptr; + mouseClickPos.reset(); + mouseDoubleClicked = false; } // QPA's internal code will handle system menu events separately, and its -- Gitblit v1.9.1