Sine Striker
2024-05-09 3fbcf1aa8f91a804af50fad4e2b122f698259c94
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
@@ -1382,13 +1389,17 @@
                                    // 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) {
                                    *result = ::DefWindowProcW(hWnd, message, wParam, lParam);
                                    iconButtonClickTime = 0;
                                    if (iconButtonClickLevel & IconButtonTriggersClose) {
                                        ::PostMessageW(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0);
                                    }
                                    // No need to reset `iconButtonClickLevel`, if it has value,
                                    // there must be another incoming WM_NCLBUTTONDOWN
                                    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;
                                }
@@ -1553,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
@@ -2094,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);
@@ -2146,6 +2160,7 @@
        if (shouldShowSystemMenu) {
            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
@@ -2165,11 +2180,23 @@
                        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;
                                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);
@@ -2179,32 +2206,36 @@
                }
            }
            bool res = showSystemMenu_sys(hWnd, nativeGlobalPos, broughtByKeyboard,
                                          m_delegate->isHostSizeFixed(m_host));
            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
                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);
                        point2qpoint(nativeLocalPos), m_windowHandle.data());
                    WindowAgentBase::SystemButton sysButtonType = WindowAgentBase::Unknown;
                    if (isInSystemButtons(qtScenePos, &sysButtonType) &&
                        sysButtonType == WindowAgentBase::WindowIcon) {
                        iconButtonClickLevel = 1;
                        if (::GetTickCount64() - iconButtonClickTime <= doubleClickTime) {
                            iconButtonClickLevel = 2;
                        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