From 0e9c2e428fb61953fd8f152125897646fe6fd337 Mon Sep 17 00:00:00 2001
From: Yuhang Zhao <zhaoyuhang@rankyee.com>
Date: 摹曛, 21 12月 2023 16:44:30 +0800
Subject: [PATCH] add mica mica alt acrylic

---
 src/core/contexts/win32windowcontext.cpp |  365 +++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 334 insertions(+), 31 deletions(-)

diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp
index 0e84db1..76510ee 100644
--- a/src/core/contexts/win32windowcontext.cpp
+++ b/src/core/contexts/win32windowcontext.cpp
@@ -62,6 +62,82 @@
         _DWMWA_MICA_EFFECT = 1029
     };
 
+    // Types used with DWMWA_SYSTEMBACKDROP_TYPE
+    enum _DWM_SYSTEMBACKDROP_TYPE {
+        _DWMSBT_AUTO,             // [Default] Let DWM automatically decide the system-drawn backdrop for this window.
+        _DWMSBT_NONE,             // [Disable] Do not draw any system backdrop.
+        _DWMSBT_MAINWINDOW,       // [Mica] Draw the backdrop material effect corresponding to a long-lived window.
+        _DWMSBT_TRANSIENTWINDOW,  // [Acrylic] Draw the backdrop material effect corresponding to a transient window.
+        _DWMSBT_TABBEDWINDOW,     // [Mica Alt] Draw the backdrop material effect corresponding to a window with a tabbed title bar.
+    };
+
+    enum WINDOWCOMPOSITIONATTRIB {
+        WCA_UNDEFINED = 0,
+        WCA_NCRENDERING_ENABLED = 1,
+        WCA_NCRENDERING_POLICY = 2,
+        WCA_TRANSITIONS_FORCEDISABLED = 3,
+        WCA_ALLOW_NCPAINT = 4,
+        WCA_CAPTION_BUTTON_BOUNDS = 5,
+        WCA_NONCLIENT_RTL_LAYOUT = 6,
+        WCA_FORCE_ICONIC_REPRESENTATION = 7,
+        WCA_EXTENDED_FRAME_BOUNDS = 8,
+        WCA_HAS_ICONIC_BITMAP = 9,
+        WCA_THEME_ATTRIBUTES = 10,
+        WCA_NCRENDERING_EXILED = 11,
+        WCA_NCADORNMENTINFO = 12,
+        WCA_EXCLUDED_FROM_LIVEPREVIEW = 13,
+        WCA_VIDEO_OVERLAY_ACTIVE = 14,
+        WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15,
+        WCA_DISALLOW_PEEK = 16,
+        WCA_CLOAK = 17,
+        WCA_CLOAKED = 18,
+        WCA_ACCENT_POLICY = 19,
+        WCA_FREEZE_REPRESENTATION = 20,
+        WCA_EVER_UNCLOAKED = 21,
+        WCA_VISUAL_OWNER = 22,
+        WCA_HOLOGRAPHIC = 23,
+        WCA_EXCLUDED_FROM_DDA = 24,
+        WCA_PASSIVEUPDATEMODE = 25,
+        WCA_USEDARKMODECOLORS = 26,
+        WCA_CORNER_STYLE = 27,
+        WCA_PART_COLOR = 28,
+        WCA_DISABLE_MOVESIZE_FEEDBACK = 29,
+        WCA_LAST = 30
+    };
+
+    enum ACCENT_STATE {
+        ACCENT_DISABLED = 0,
+        ACCENT_ENABLE_GRADIENT = 1,
+        ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
+        ACCENT_ENABLE_BLURBEHIND = 3, // Traditional DWM blur
+        ACCENT_ENABLE_ACRYLICBLURBEHIND = 4, // RS4 1803
+        ACCENT_ENABLE_HOST_BACKDROP = 5, // RS5 1809
+        ACCENT_INVALID_STATE = 6 // Using this value will remove the window background
+    };
+
+    enum ACCENT_FLAG {
+        ACCENT_NONE = 0,
+        ACCENT_ENABLE_ACRYLIC = 1,
+        ACCENT_ENABLE_ACRYLIC_WITH_LUMINOSITY = 482
+    };
+
+    struct ACCENT_POLICY {
+        DWORD dwAccentState;
+        DWORD dwAccentFlags;
+        DWORD dwGradientColor; // #AABBGGRR
+        DWORD dwAnimationId;
+    };
+    using PACCENT_POLICY = ACCENT_POLICY *;
+
+    struct WINDOWCOMPOSITIONATTRIBDATA {
+        WINDOWCOMPOSITIONATTRIB Attrib;
+        PVOID pvData;
+        SIZE_T cbData;
+    };
+    using PWINDOWCOMPOSITIONATTRIBDATA = WINDOWCOMPOSITIONATTRIBDATA *;
+
+    using SetWindowCompositionAttributePtr = BOOL(WINAPI *)(HWND, PWINDOWCOMPOSITIONATTRIBDATA);
+
     // The thickness of an auto-hide taskbar in pixels.
     static constexpr const quint8 kAutoHideTaskBarThickness = 2;
 
@@ -105,6 +181,7 @@
         DYNAMIC_API_DECLARE(DwmGetCompositionTimingInfo);
         DYNAMIC_API_DECLARE(DwmGetWindowAttribute);
         DYNAMIC_API_DECLARE(DwmSetWindowAttribute);
+        DYNAMIC_API_DECLARE(DwmExtendFrameIntoClientArea);
         DYNAMIC_API_DECLARE(GetDpiForWindow);
         DYNAMIC_API_DECLARE(GetSystemMetricsForDpi);
         DYNAMIC_API_DECLARE(GetDpiForMonitor);
@@ -114,6 +191,8 @@
 
 #undef DYNAMIC_API_DECLARE
 
+        SetWindowCompositionAttributePtr pSetWindowCompositionAttribute = nullptr;
+
     private:
         DynamicApis() {
 #define DYNAMIC_API_RESOLVE(DLL, NAME)                                                             \
@@ -122,6 +201,7 @@
             QSystemLibrary user32(QStringLiteral("user32"));
             DYNAMIC_API_RESOLVE(user32, GetDpiForWindow);
             DYNAMIC_API_RESOLVE(user32, GetSystemMetricsForDpi);
+            DYNAMIC_API_RESOLVE(user32, SetWindowCompositionAttribute);
 
             QSystemLibrary shcore(QStringLiteral("shcore"));
             DYNAMIC_API_RESOLVE(shcore, GetDpiForMonitor);
@@ -132,6 +212,7 @@
             DYNAMIC_API_RESOLVE(dwmapi, DwmGetCompositionTimingInfo);
             DYNAMIC_API_RESOLVE(dwmapi, DwmGetWindowAttribute);
             DYNAMIC_API_RESOLVE(dwmapi, DwmSetWindowAttribute);
+            DYNAMIC_API_RESOLVE(dwmapi, DwmExtendFrameIntoClientArea);
 
             QSystemLibrary winmm(QStringLiteral("winmm"));
             DYNAMIC_API_RESOLVE(winmm, timeGetDevCaps);
@@ -243,6 +324,11 @@
 
     static inline bool isWin11OrGreater() {
         static const bool result = IsWindows11OrGreater_Real();
+        return result;
+    }
+
+    static inline bool isWin1122H2OrGreater() {
+        static const bool result = IsWindows1122H2OrGreater_Real();
         return result;
     }
 
@@ -379,16 +465,7 @@
                getSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi);
     }
 
-    static void updateInternalWindowFrameMargins(HWND hwnd, QWindow *window) {
-        const auto margins = [hwnd]() -> QMargins {
-            const auto titleBarHeight = int(getTitleBarHeight(hwnd));
-            if (isWin10OrGreater()) {
-                return {0, -titleBarHeight, 0, 0};
-            } else {
-                const auto frameSize = int(getResizeBorderThickness(hwnd));
-                return {-frameSize, -titleBarHeight, -frameSize, -frameSize};
-            }
-        }();
+    static void setInternalWindowFrameMargins(QWindow *window, const QMargins &margins) {
         const QVariant marginsVar = QVariant::fromValue(margins);
         window->setProperty("_q_windowsCustomMargins", marginsVar);
 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
@@ -414,10 +491,10 @@
         monitorInfo.cbSize = sizeof(monitorInfo);
         ::GetMonitorInfoW(monitor, &monitorInfo);
         return monitorInfo;
-    };
+    }
 
-    static inline void moveToDesktopCenter(HWND hwnd) {
-        const auto monitorInfo = getMonitorForWindow(hwnd);
+    static inline void moveWindowToDesktopCenter(HWND hwnd) {
+        MONITORINFOEXW monitorInfo = getMonitorForWindow(hwnd);
         RECT windowRect{};
         ::GetWindowRect(hwnd, &windowRect);
         const auto newX = monitorInfo.rcMonitor.left +
@@ -426,6 +503,71 @@
                           (RECT_HEIGHT(monitorInfo.rcMonitor) - RECT_HEIGHT(windowRect)) / 2;
         ::SetWindowPos(hwnd, nullptr, newX, newY, 0, 0,
                        SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
+    }
+
+    static inline void moveWindowToMonitor(HWND hwnd, const MONITORINFOEXW &activeMonitor) {
+        RECT currentMonitorRect = getMonitorForWindow(hwnd).rcMonitor;
+        RECT activeMonitorRect = activeMonitor.rcMonitor;
+        // We are in the same monitor, nothing to adjust here.
+        if (currentMonitorRect == activeMonitorRect) {
+            return;
+        }
+        RECT currentWindowRect{};
+        ::GetWindowRect(hwnd, &currentWindowRect);
+        auto newWindowX =
+            activeMonitorRect.left + (currentWindowRect.left - currentMonitorRect.left);
+        auto newWindowY = activeMonitorRect.top + (currentWindowRect.top - currentMonitorRect.top);
+        ::SetWindowPos(hwnd, nullptr, newWindowX, newWindowY, RECT_WIDTH(currentWindowRect),
+                       RECT_HEIGHT(currentWindowRect),
+                       SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
+    }
+
+    static inline void bringWindowToFront(HWND hwnd) {
+        HWND oldForegroundWindow = ::GetForegroundWindow();
+        if (!oldForegroundWindow) {
+            // The foreground window can be NULL, it's not an API error.
+            return;
+        }
+        MONITORINFOEXW activeMonitor = getMonitorForWindow(oldForegroundWindow);
+        // We need to show the window first, otherwise we won't be able to bring it to front.
+        if (!::IsWindowVisible(hwnd)) {
+            ::ShowWindow(hwnd, SW_SHOW);
+        }
+        if (IsMinimized(hwnd)) {
+            // Restore the window if it is minimized.
+            ::ShowWindow(hwnd, SW_RESTORE);
+            // Once we've been restored, throw us on the active monitor.
+            moveWindowToMonitor(hwnd, activeMonitor);
+            // When the window is restored, it will always become the foreground window.
+            // So return early here, we don't need the following code to bring it to front.
+            return;
+        }
+        // OK, our window is not minimized, so now we will try to bring it to front manually.
+        // First try to send a message to the current foreground window to check whether
+        // it is currently hanging or not.
+        if (!::SendMessageTimeoutW(oldForegroundWindow, WM_NULL, 0, 0,
+                                   SMTO_BLOCK | SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG, 1000,
+                                   nullptr)) {
+            // The foreground window hangs, can't activate current window.
+            return;
+        }
+        DWORD windowThreadProcessId = ::GetWindowThreadProcessId(oldForegroundWindow, nullptr);
+        DWORD currentThreadId = ::GetCurrentThreadId();
+        // We won't be able to change a window's Z order if it's not our own window,
+        // so we use this small technique to pretend the foreground window is ours.
+        ::AttachThreadInput(windowThreadProcessId, currentThreadId, TRUE);
+
+        [[maybe_unused]] const auto &cleaner =
+            qScopeGuard([windowThreadProcessId, currentThreadId]() {
+                ::AttachThreadInput(windowThreadProcessId, currentThreadId, FALSE); //
+            });
+
+        ::BringWindowToTop(hwnd);
+        // Activate the window too. This will force us to the virtual desktop this
+        // window is on, if it's on another virtual desktop.
+        ::SetActiveWindow(hwnd);
+        // Throw us on the active monitor.
+        moveWindowToMonitor(hwnd, activeMonitor);
     }
 
     static inline bool isFullScreen(HWND hwnd) {
@@ -764,7 +906,20 @@
         return ::CallWindowProcW(g_qtWindowProc, hWnd, message, wParam, lParam);
     }
 
-    static inline void addManagedWindow(HWND hWnd, Win32WindowContext *ctx) {
+    static inline void addManagedWindow(QWindow *window, HWND hWnd, Win32WindowContext *ctx) {
+        const auto margins = [hWnd]() -> QMargins {
+            const auto titleBarHeight = int(getTitleBarHeight(hWnd));
+            if (isWin10OrGreater()) {
+                return {0, -titleBarHeight, 0, 0};
+            } else {
+                const auto frameSize = int(getResizeBorderThickness(hWnd));
+                return {-frameSize, -titleBarHeight, -frameSize, -frameSize};
+            }
+        }();
+
+        // Inform Qt we want and have set custom margins
+        setInternalWindowFrameMargins(window, margins);
+
         // Store original window proc
         if (!g_qtWindowProc) {
             g_qtWindowProc = reinterpret_cast<WNDPROC>(::GetWindowLongPtrW(hWnd, GWLP_WNDPROC));
@@ -807,17 +962,24 @@
     void Win32WindowContext::virtual_hook(int id, void *data) {
         switch (id) {
             case CentralizeHook: {
+                if (!windowId)
+                    return;
                 const auto hwnd = reinterpret_cast<HWND>(windowId);
-                moveToDesktopCenter(hwnd);
+                moveWindowToDesktopCenter(hwnd);
                 return;
             }
 
             case RaiseWindowHook: {
-                // FIXME
+                if (!windowId)
+                    return;
+                const auto hwnd = reinterpret_cast<HWND>(windowId);
+                bringWindowToFront(hwnd);
                 return;
             }
 
             case ShowSystemMenuHook: {
+                if (!windowId)
+                    return;
                 const auto &pos = *static_cast<const QPoint *>(data);
                 auto hWnd = reinterpret_cast<HWND>(windowId);
 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
@@ -837,15 +999,105 @@
                 const auto &newVar = *static_cast<const QVariant *>(args[1]);
                 const auto &oldVar = *static_cast<const QVariant *>(args[2]);
 
-                if (key == QStringLiteral("no-frame-shadow")) {
+                if (!windowId)
+                    return;
+                const auto hwnd = reinterpret_cast<HWND>(windowId);
+
+                const DynamicApis &apis = DynamicApis::instance();
+
+                if (key == QStringLiteral("frame-shadow")) {
                     if (newVar.toBool()) {
                         // TODO: set off
                     } else {
                         // TODO: set on
                     }
+                } else if (key == QStringLiteral("mica")) {
+                    if (!isWin11OrGreater()) {
+                        return;
+                    }
+                    if (newVar.toBool()) {
+                        // We need to extend the window frame into the whole client area to be able
+                        // to see the blurred window background.
+                        static constexpr const MARGINS margins = {-1, -1, -1, -1};
+                        apis.pDwmExtendFrameIntoClientArea(hwnd, &margins);
+                        if (isWin1122H2OrGreater()) {
+                            // Use official DWM API to enable Mica, available since Windows 11 22H2 (10.0.22621).
+                            const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_MAINWINDOW;
+                            apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType, sizeof(backdropType));
+                        } else {
+                            // Use undocumented DWM API to enable Mica, available since Windows 11 (10.0.22000).
+                            const BOOL enable = TRUE;
+                            apis.pDwmSetWindowAttribute(hwnd, _DWMWA_MICA_EFFECT, &enable, sizeof(enable));
+                        }
+                    } else {
+                        if (isWin1122H2OrGreater()) {
+                            const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_AUTO;
+                            apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType, sizeof(backdropType));
+                        } else {
+                            const BOOL enable = FALSE;
+                            apis.pDwmSetWindowAttribute(hwnd, _DWMWA_MICA_EFFECT, &enable, sizeof(enable));
+                        }
+                        static constexpr const MARGINS margins = {0, 0, 0, 0};
+                        apis.pDwmExtendFrameIntoClientArea(hwnd, &margins);
+                    }
+                } else if (key == QStringLiteral("mica-alt")) {
+                    if (!isWin1122H2OrGreater()) {
+                        return;
+                    }
+                    if (newVar.toBool()) {
+                        // We need to extend the window frame into the whole client area to be able
+                        // to see the blurred window background.
+                        static constexpr const MARGINS margins = {-1, -1, -1, -1};
+                        apis.pDwmExtendFrameIntoClientArea(hwnd, &margins);
+                        // Use official DWM API to enable Mica Alt, available since Windows 11 22H2 (10.0.22621).
+                        const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_TABBEDWINDOW;
+                        apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType, sizeof(backdropType));
+                    } else {
+                        const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_AUTO;
+                        apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType, sizeof(backdropType));
+                        static constexpr const MARGINS margins = {0, 0, 0, 0};
+                        apis.pDwmExtendFrameIntoClientArea(hwnd, &margins);
+                    }
+                } else if (key == QStringLiteral("acrylic-material")) {
+                    if (newVar.toBool()) {
+                        // We need to extend the window frame into the whole client area to be able
+                        // to see the blurred window background.
+                        static constexpr const MARGINS margins = {-1, -1, -1, -1};
+                        apis.pDwmExtendFrameIntoClientArea(hwnd, &margins);
+                        if (isWin11OrGreater()) {
+                            const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_TRANSIENTWINDOW;
+                            apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType, sizeof(backdropType));
+                        } else {
+                            ACCENT_POLICY policy{};
+                            policy.dwAccentState = ACCENT_ENABLE_ACRYLICBLURBEHIND;
+                            policy.dwAccentFlags = ACCENT_ENABLE_ACRYLIC_WITH_LUMINOSITY;
+                            // This API expects the #AABBGGRR format.
+                            //policy.dwGradientColor = DWORD(qRgba(gradientColor.blue(), gradientColor.green(), gradientColor.red(), gradientColor.alpha()));
+                            WINDOWCOMPOSITIONATTRIBDATA wcad{};
+                            wcad.Attrib = WCA_ACCENT_POLICY;
+                            wcad.pvData = &policy;
+                            wcad.cbData = sizeof(policy);
+                            apis.pSetWindowCompositionAttribute(hwnd, &wcad);
+                        }
+                    } else {
+                        if (isWin11OrGreater()) {
+                            const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_AUTO;
+                            apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType, sizeof(backdropType));
+                        } else {
+                            ACCENT_POLICY policy{};
+                            policy.dwAccentState = ACCENT_DISABLED;
+                            policy.dwAccentFlags = ACCENT_NONE;
+                            WINDOWCOMPOSITIONATTRIBDATA wcad{};
+                            wcad.Attrib = WCA_ACCENT_POLICY;
+                            wcad.pvData = &policy;
+                            wcad.cbData = sizeof(policy);
+                            apis.pSetWindowCompositionAttribute(hwnd, &wcad);
+                        }
+                        static constexpr const MARGINS margins = {0, 0, 0, 0};
+                        apis.pDwmExtendFrameIntoClientArea(hwnd, &margins);
+                    }
                 }
-
-                break;
+                return;
             }
 
             case DefaultColorsHook: {
@@ -859,6 +1111,9 @@
             }
 
             case DrawWindows10BorderHook: {
+                if (!windowId)
+                    return;
+
                 auto args = static_cast<void **>(data);
                 auto &painter = *static_cast<QPainter *>(args[0]);
                 const auto &rect = *static_cast<const QRect *>(args[1]);
@@ -898,10 +1153,8 @@
                 return;
             }
 
-            default: {
-                // unreachable
+            default:
                 break;
-            }
         }
         AbstractWindowContext::virtual_hook(id, data);
     }
@@ -914,8 +1167,13 @@
         return getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId));
     }
 
-    void Win32WindowContext::winIdChanged(QWindow *oldWindow) {
-        removeManagedWindow(reinterpret_cast<HWND>(windowId));
+    void Win32WindowContext::winIdChanged() {
+        // If the original window id is valid, remove all resources related
+        if (windowId) {
+            removeManagedWindow(reinterpret_cast<HWND>(windowId));
+            windowId = 0;
+        }
+
         if (!m_windowHandle) {
             return;
         }
@@ -934,11 +1192,8 @@
         }
 #endif
 
-        // Inform Qt we want and have set custom margins
-        updateInternalWindowFrameMargins(hWnd, m_windowHandle);
-
         // Add managed window
-        addManagedWindow(hWnd, this);
+        addManagedWindow(m_windowHandle, hWnd, this);
 
         // Cache win id
         windowId = winId;
@@ -1202,6 +1457,53 @@
                 const WindowPart currentWindowPart = lastHitTestResult;
                 if (message == WM_NCMOUSEMOVE) {
                     if (currentWindowPart != 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
+                        // widget. At this time, no other widgets will accept the mouse event until
+                        // QWidgetWindow receives the mouse release event, then set `qt_button_down`
+                        // to null.
+
+                        // Imagine the following situation, now the main window has a pop-up menu,
+                        // the focus is not on the main window, if we click outside the pop-up menu,
+                        // the menu will close, which seems to be completely fine. But if we close
+                        // the menu by clicking on the title bar draggable area, then other widgets
+                        // won't accept the mouse message afterwards.
+
+                        // Here's the reason.
+                        // When the mouse is placed in the draggable area of the title bar, there
+                        // are two situations.
+
+                        // 1. If the focus is on the main window, and the last result of
+                        // WM_NCHITTEST is HTCAPTION, the mouse click event in the title bar is
+                        // taken over by Windows and Qt does not receive the mouse click event.
+
+                        // 2. If the main window has a pop-up menu, it is completely different. When
+                        // the mouse is pressed on the title bar, Windows sends the WM_LBUTTONDOWN
+                        // message to the window plane of the pop-up menu, the menu is closed, but
+                        // Qt will continue to forward the event to the QWidget under the mouse, and
+                        // the event will be processed by QWidgetWindow, causing the title bar
+                        // widget to be implicitly grabbed. After the menu is closed, Windows
+                        // immediately sends WM_NCHITTEST, because the mouse is in the title bar
+                        // draggable area, the result is HTCAPTION, so when the mouse is released,
+                        // Windows sends WM_NCLBUTTONUP, which is a non-client message, and it
+                        // will be ignored by Qt. As a consequence, QWidgetWindow can't receive a
+                        // mouse release message in the client area, so the grab remains, and other
+                        // widgets cannot receive mouse events.
+
+                        // Since we didn't watch the menu window, we cannot capture any mouse
+                        // press events sent by Windows, so we cannot solve this problem by
+                        // recording mouse events. Fortunately, we found that the main window will
+                        // receive a WM_NCMOUSEMOVE message immediately after the menu is closed, so
+                        // we just manually send a mouse release event when this message arrives and
+                        // set qt_button_down to null. Don't worry, when receiving WM_NCMOUSEMOVE,
+                        // there shouldn't be any control in the state of being grabbed.
+
+                        // In the native window, although QWidgetWindow handles the forwarded mouse
+                        // press event when the menu is closed, since the native title bar is not a
+                        // QWidget, no widget will be grabbed, and `qt_button_down` remains empty,
+                        // the above problems would not arise.
+
                         m_delegate->resetQtGrabbedControl(m_host);
                         if (mouseLeaveBlocked) {
                             emulateClientAreaMessage(hWnd, message, wParam, lParam,
@@ -1264,6 +1566,8 @@
                         // window from client area, which means we will get previous window part as
                         // HTCLIENT if the mouse leaves window from client area and enters window
                         // from non-client area, but it has no bad effect.
+
+                        // Why do we need to call this function here?
                         m_delegate->resetQtGrabbedControl(m_host);
                     }
                 }
@@ -1286,7 +1590,7 @@
                     // function.
                     if (wParam && !lParam) {
                         centered = true;
-                        moveToDesktopCenter(hWnd);
+                        moveWindowToDesktopCenter(hWnd);
                     }
                 }
                 break;
@@ -1373,7 +1677,6 @@
                 // Terminal does, however, later I found that if we choose a proper
                 // 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)); //
                 });
@@ -1496,7 +1799,7 @@
                         *result = (isTitleBar ? HTCAPTION : HTCLIENT);
                         return true;
                     }
-                    // At this point, we know that the cursor is inside the client area
+                    // At this point, we know that the cursor is inside the client area,
                     // so it has to be either the little border at the top of our custom
                     // title bar or the drag bar. Apparently, it must be the drag bar or
                     // the little border at the top which the user can use to move or

--
Gitblit v1.9.1