From 3fbcf1aa8f91a804af50fad4e2b122f698259c94 Mon Sep 17 00:00:00 2001 From: Sine Striker <trueful@163.com> Date: ćšć, 09 5æ 2024 23:45:28 +0800 Subject: [PATCH] Remove `isHostSizeFixed` in delegate --- src/core/contexts/win32windowcontext.cpp | 179 ++++++++++++++++++++++++++++++++++++++++++++--------------- 1 files changed, 133 insertions(+), 46 deletions(-) diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index 171a348..84c80ef 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; @@ -619,6 +625,7 @@ // Try hooked procedure and save result LRESULT result; if (ctx->windowProc(hWnd, message, wParam, lParam, &result)) { + // https://github.com/stdware/qwindowkit/issues/45 // Forward the event to user-defined native event filters, there may be some messages // that need to be processed by the user. std::ignore = @@ -693,12 +700,12 @@ auto hWnd = reinterpret_cast<HWND>(m_windowId); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) const QPoint nativeGlobalPos = - QHighDpi::toNativeGlobalPosition(pos, m_windowHandle); + QHighDpi::toNativeGlobalPosition(pos, m_windowHandle.data()); #else - const QPoint nativeGlobalPos = QHighDpi::toNativePixels(pos, m_windowHandle); + const QPoint nativeGlobalPos = QHighDpi::toNativePixels(pos, m_windowHandle.data()); #endif std::ignore = showSystemMenu_sys(hWnd, qpoint2point(nativeGlobalPos), false, - m_delegate->isHostSizeFixed(m_host)); + isHostSizeFixed()); return; } @@ -1257,7 +1264,7 @@ POINT screenPoint{GET_X_LPARAM(dwScreenPos), GET_Y_LPARAM(dwScreenPos)}; ::ScreenToClient(hWnd, &screenPoint); QPoint qtScenePos = QHighDpi::fromNativeLocalPosition(point2qpoint(screenPoint), - m_windowHandle); + m_windowHandle.data()); auto dummy = WindowAgentBase::Unknown; if (isInSystemButtons(qtScenePos, &dummy)) { // We must record whether the last WM_MOUSELEAVE was filtered, because if @@ -1369,20 +1376,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 +1421,8 @@ (((message >= WM_NCXBUTTONDOWN) && (message <= WM_NCXBUTTONDBLCLK)) ? TRUE : FALSE); + emulateClientAreaMessage(hWnd, message, wParam, lParam); } - emulateClientAreaMessage(hWnd, message, wParam, lParam); return true; } break; @@ -1535,10 +1564,10 @@ auto clientWidth = RECT_WIDTH(clientRect); auto clientHeight = RECT_HEIGHT(clientRect); - QPoint qtScenePos = - QHighDpi::fromNativeLocalPosition(point2qpoint(nativeLocalPos), m_windowHandle); + QPoint qtScenePos = QHighDpi::fromNativeLocalPosition(point2qpoint(nativeLocalPos), + m_windowHandle.data()); - bool isFixedSize = m_delegate->isHostSizeFixed(m_host); + bool isFixedSize = isHostSizeFixed(); bool isTitleBar = isInTitleBarDraggableArea(qtScenePos); bool dontOverrideCursor = false; // ### TODO @@ -2076,9 +2105,12 @@ switch (message) { case WM_RBUTTONUP: { const POINT nativeLocalPos = getNativePosFromMouse(); - const QPoint qtScenePos = - QHighDpi::fromNativeLocalPosition(point2qpoint(nativeLocalPos), m_windowHandle); - if (isInTitleBarDraggableArea(qtScenePos)) { + const QPoint qtScenePos = QHighDpi::fromNativeLocalPosition( + point2qpoint(nativeLocalPos), m_windowHandle.data()); + WindowAgentBase::SystemButton sysButtonType = WindowAgentBase::Unknown; + if (isInTitleBarDraggableArea(qtScenePos) || + (isInSystemButtons(qtScenePos, &sysButtonType) && + sysButtonType == WindowAgentBase::WindowIcon)) { shouldShowSystemMenu = true; nativeGlobalPos = nativeLocalPos; ::ClientToScreen(hWnd, &nativeGlobalPos); @@ -2126,29 +2158,84 @@ break; } if (shouldShowSystemMenu) { - bool triggeredByIconButton = iconButtonClickTime > 0; - if (triggeredByIconButton) { - // TODO: Adjust `nativeGlobalPos` to (0, realTitleBarHeight) - // hint: use m_delgegate->mapGeometryToScene - } - bool res = showSystemMenu_sys(hWnd, nativeGlobalPos, broughtByKeyboard, - m_delegate->isHostSizeFixed(m_host)); + static HHOOK mouseHook = nullptr; + static std::optional<POINT> mouseClickPos; + static bool mouseDoubleClicked = false; + bool mouseHookedLocal = false; - // 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); + // 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, isHostSizeFixed()); + + // 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.data()); + 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