From e95e2bfd29b64d3144515c30fb9cf76adb75c6c3 Mon Sep 17 00:00:00 2001
From: Sine Striker <trueful@163.com>
Date: 周二, 07 5月 2024 21:44:21 +0800
Subject: [PATCH] minor tweaks

---
 src/core/contexts/win32windowcontext.cpp |   90 +++++++++++++++++++++++++++++++++++---------
 1 files changed, 71 insertions(+), 19 deletions(-)

diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp
index 811fd44..171a348 100644
--- a/src/core/contexts/win32windowcontext.cpp
+++ b/src/core/contexts/win32windowcontext.cpp
@@ -233,14 +233,15 @@
         apis.ptimeEndPeriod(ms_granularity);
     }
 
-    static void showSystemMenu2(HWND hWnd, const POINT &pos, const bool selectFirstEntry,
-                                const bool fixedSize) {
+    // Returns false if the menu is canceled
+    static bool showSystemMenu_sys(HWND hWnd, const POINT &pos, const bool selectFirstEntry,
+                                   const bool fixedSize) {
         HMENU hMenu = ::GetSystemMenu(hWnd, FALSE);
         if (!hMenu) {
             // The corresponding window doesn't have a system menu, most likely due to the
             // lack of the "WS_SYSMENU" window style. This situation should not be treated
             // as an error so just ignore it and return early.
-            return;
+            return true;
         }
 
         const bool maxOrFull = IsMaximized(hWnd) || isFullScreen(hWnd);
@@ -284,7 +285,8 @@
         // Popup the system menu at the required position.
         const auto result = ::TrackPopupMenu(
             hMenu,
-            (TPM_RETURNCMD | (QGuiApplication::isRightToLeft() ? TPM_RIGHTALIGN : TPM_LEFTALIGN)),
+            (TPM_RETURNCMD | (QGuiApplication::isRightToLeft() ? TPM_RIGHTALIGN : TPM_LEFTALIGN) |
+             TPM_RIGHTBUTTON),
             pos.x, pos.y, 0, hWnd, nullptr);
 
         // Unhighlight the first menu item after the popup menu is closed, otherwise it will keep
@@ -293,11 +295,12 @@
 
         if (!result) {
             // The user canceled the menu, no need to continue.
-            return;
+            return false;
         }
 
         // Send the command that the user chooses to the corresponding window.
         ::PostMessageW(hWnd, WM_SYSCOMMAND, result, 0);
+        return true;
     }
 
     static inline Win32WindowContext::WindowPart getHitWindowPart(int hitTestResult) {
@@ -694,8 +697,8 @@
 #else
                 const QPoint nativeGlobalPos = QHighDpi::toNativePixels(pos, m_windowHandle);
 #endif
-                showSystemMenu2(hWnd, qpoint2point(nativeGlobalPos), false,
-                                m_delegate->isHostSizeFixed(m_host));
+                std::ignore = showSystemMenu_sys(hWnd, qpoint2point(nativeGlobalPos), false,
+                                                 m_delegate->isHostSizeFixed(m_host));
                 return;
             }
 
@@ -813,7 +816,6 @@
                        ? int(getWindowFrameBorderThickness(reinterpret_cast<HWND>(m_windowId)))
                        : 0;
         }
-
         return AbstractWindowContext::windowAttribute(key);
     }
 
@@ -821,6 +823,7 @@
         // Reset the context data
         mouseLeaveBlocked = false;
         lastHitTestResult = WindowPart::Outside;
+        lastHitTestResultRaw = HTNOWHERE;
 
         if (!isSystemBorderEnabled()) {
             m_delegate->setWindowFlags(m_host, m_delegate->getWindowFlags(m_host) |
@@ -1299,9 +1302,8 @@
     case WM_NCPOINTERUP:
 #endif
             case WM_NCMOUSEHOVER: {
-                const WindowPart currentWindowPart = lastHitTestResult;
                 if (message == WM_NCMOUSEMOVE) {
-                    if (currentWindowPart != WindowPart::ChromeButton) {
+                    if (lastHitTestResult != WindowPart::ChromeButton) {
                         // https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/widgets/kernel/qwidgetwindow.cpp#L472
                         // When the mouse press event arrives, QWidgetWindow will implicitly grab
                         // the top widget right under the mouse, and set `qt_button_down` to this
@@ -1360,14 +1362,28 @@
                     }
                 }
 
-                if (currentWindowPart == WindowPart::ChromeButton) {
-                    emulateClientAreaMessage(hWnd, message, wParam, lParam);
+                if (lastHitTestResult == WindowPart::ChromeButton) {
                     if (message == WM_NCMOUSEMOVE) {
                         // ### FIXME FIXME FIXME
                         // ### FIXME: Calling DefWindowProc() here is really dangerous, investigate
                         // 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;
+                        }
                     } else {
                         // According to MSDN, we should return non-zero for X button messages to
                         // indicate we have handled these messages (due to historical reasons), for
@@ -1377,6 +1393,7 @@
                                  ? TRUE
                                  : FALSE);
                     }
+                    emulateClientAreaMessage(hWnd, message, wParam, lParam);
                     return true;
                 }
                 break;
@@ -1505,7 +1522,8 @@
                 // color, our homemade top border can almost have exactly the same
                 // appearance with the system's one.
                 [[maybe_unused]] const auto &hitTestRecorder = qScopeGuard([this, result]() {
-                    lastHitTestResult = getHitWindowPart(int(*result)); //
+                    lastHitTestResultRaw = int(*result);
+                    lastHitTestResult = getHitWindowPart(lastHitTestResultRaw);
                 });
 
                 POINT nativeGlobalPos{GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
@@ -2054,6 +2072,7 @@
         bool shouldShowSystemMenu = false;
         bool broughtByKeyboard = false;
         POINT nativeGlobalPos{};
+
         switch (message) {
             case WM_RBUTTONUP: {
                 const POINT nativeLocalPos = getNativePosFromMouse();
@@ -2075,10 +2094,20 @@
             }
             case WM_SYSCOMMAND: {
                 const WPARAM filteredWParam = (wParam & 0xFFF0);
-                if ((filteredWParam == SC_KEYMENU) && (lParam == VK_SPACE)) {
-                    shouldShowSystemMenu = true;
-                    broughtByKeyboard = true;
-                    nativeGlobalPos = getNativeGlobalPosFromKeyboard();
+                switch (filteredWParam) {
+                    case SC_MOUSEMENU:
+                        shouldShowSystemMenu = true;
+                        nativeGlobalPos = getNativeGlobalPosFromKeyboard();
+                        break;
+                    case SC_KEYMENU:
+                        if (lParam == VK_SPACE) {
+                            shouldShowSystemMenu = true;
+                            broughtByKeyboard = true;
+                            nativeGlobalPos = getNativeGlobalPosFromKeyboard();
+                        }
+                        break;
+                    default:
+                        break;
                 }
                 break;
             }
@@ -2097,8 +2126,31 @@
                 break;
         }
         if (shouldShowSystemMenu) {
-            showSystemMenu2(hWnd, nativeGlobalPos, broughtByKeyboard,
-                            m_delegate->isHostSizeFixed(m_host));
+            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));
+
+            // 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);
+                }
+            }
+
             // QPA's internal code will handle system menu events separately, and its
             // behavior is not what we would want to see because it doesn't know our
             // window doesn't have any window frame now, so return early here to avoid

--
Gitblit v1.9.1