From 3d0d0c24adf5001be1c42b2fa86129b1b258cf70 Mon Sep 17 00:00:00 2001
From: Sine Striker <trueful@163.com>
Date: 周一, 04 12月 2023 20:31:36 +0800
Subject: [PATCH] Fix NCHITTEST

---
 src/core/contexts/abstractwindowcontext.cpp |    7 +
 src/core/contexts/abstractwindowcontext_p.h |    4 
 src/core/CMakeLists.txt                     |    1 
 src/core/qwkcoreglobal.cpp                  |   74 ++++++++++++++++++
 src/core/qwkcoreglobal_p.h                  |   12 ++
 src/core/contexts/win32windowcontext.cpp    |   91 ++++++++++++----------
 6 files changed, 144 insertions(+), 45 deletions(-)

diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index d5940f8..654a259 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -6,6 +6,7 @@
 set(_src
     qwkcoreglobal.h
     qwkcoreglobal_p.h
+    qwkcoreglobal.cpp
     corewindowagent.h
     corewindowagent_p.h
     corewindowagent.cpp
diff --git a/src/core/contexts/abstractwindowcontext.cpp b/src/core/contexts/abstractwindowcontext.cpp
index a3d2bba..2e32d25 100644
--- a/src/core/contexts/abstractwindowcontext.cpp
+++ b/src/core/contexts/abstractwindowcontext.cpp
@@ -2,7 +2,12 @@
 
 namespace QWK {
 
-    AbstractWindowContext::~AbstractWindowContext() = default;
+    AbstractWindowContext::AbstractWindowContext(QWindow *window, WindowItemDelegate *delegate)
+        : m_windowHandle(window), m_delegate(delegate) {
+    }
+
+    AbstractWindowContext::~AbstractWindowContext() {
+    }
 
     void AbstractWindowContext::setupWindow(QWindow *window) {
         Q_ASSERT(window);
diff --git a/src/core/contexts/abstractwindowcontext_p.h b/src/core/contexts/abstractwindowcontext_p.h
index 54471fe..a92d91d 100644
--- a/src/core/contexts/abstractwindowcontext_p.h
+++ b/src/core/contexts/abstractwindowcontext_p.h
@@ -16,9 +16,7 @@
     class QWK_CORE_EXPORT AbstractWindowContext : public QObject {
         Q_OBJECT
     public:
-        inline AbstractWindowContext(QWindow *window, WindowItemDelegate *delegate)
-            : m_windowHandle(window), m_delegate(delegate) {
-        }
+        AbstractWindowContext(QWindow *window, WindowItemDelegate *delegate);
         ~AbstractWindowContext() override;
 
     public:
diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp
index 09c2b83..d3b34ec 100644
--- a/src/core/contexts/win32windowcontext.cpp
+++ b/src/core/contexts/win32windowcontext.cpp
@@ -6,6 +6,7 @@
 #include <QtCore/QAbstractNativeEventFilter>
 #include <QtCore/QCoreApplication>
 #include <QtCore/QOperatingSystemVersion>
+#include <QtCore/QScopeGuard>
 
 #include <QtCore/private/qsystemlibrary_p.h>
 #include <QtGui/private/qhighdpiscaling_p.h>
@@ -213,8 +214,7 @@
         return (windowRect == mi.value_or(MONITORINFOEXW{}).rcMonitor);
     }
 
-    static inline bool isWindowNoState(HWND hwnd)
-    {
+    static inline bool isWindowNoState(HWND hwnd) {
         Q_ASSERT(hwnd);
         if (!hwnd) {
             return false;
@@ -332,11 +332,11 @@
 
         static inline void install() {
             instance = new WindowsNativeEventFilter();
-            qApp->installNativeEventFilter(instance);
+            installNativeEventFilter(instance);
         }
 
         static inline void uninstall() {
-            qApp->removeNativeEventFilter(instance);
+            removeNativeEventFilter(instance);
             delete instance;
             instance = nullptr;
         }
@@ -1093,39 +1093,44 @@
                 // color, our homemade top border can almost have exactly the same
                 // appearance with the system's one.
 
-                const auto nativeGlobalPos = POINT{ GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
+                [[maybe_unused]] const auto &hitTestRecorder = qScopeGuard([this, result]() {
+                    lastHitTestResult = getHitWindowPart(int(*result)); //
+                });
+
+                POINT nativeGlobalPos{GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
                 POINT nativeLocalPos = nativeGlobalPos;
                 ::ScreenToClient(hWnd, &nativeLocalPos);
 
-                auto clientRect = RECT{ 0, 0, 0, 0 };
+                RECT clientRect{0, 0, 0, 0};
                 ::GetClientRect(hWnd, &clientRect);
-                const auto clientWidth = RECT_WIDTH(clientRect);
-                const auto clientHeight = RECT_HEIGHT(clientRect);
+                auto clientWidth = RECT_WIDTH(clientRect);
+                auto clientHeight = RECT_HEIGHT(clientRect);
 
-                const QPoint qtScenePos = fromNativeLocalPosition(m_windowHandle, QPoint(nativeLocalPos.x, nativeLocalPos.y));
+                QPoint qtScenePos = fromNativeLocalPosition(
+                    m_windowHandle, QPoint(nativeLocalPos.x, nativeLocalPos.y));
 
-                const bool isFixedSize = /*isWindowFixedSize()*/false; // ### FIXME
-                const bool isTitleBar = isInTitleBarDraggableArea(qtScenePos);
-                const bool dontOverrideCursor = false; // ### TODO
+                bool isFixedSize = /*isWindowFixedSize()*/ false; // ### FIXME
+                bool isTitleBar = isInTitleBarDraggableArea(qtScenePos);
+                bool dontOverrideCursor = false;                  // ### TODO
 
                 CoreWindowAgent::SystemButton sysButtonType = CoreWindowAgent::Unknown;
                 if (!isFixedSize && isInSystemButtons(qtScenePos, &sysButtonType)) {
-                    // Firstly, we set the hit test result to a default value to be able to detect whether we
-                    // have changed it or not afterwards.
+                    // Firstly, we set the hit test result to a default value to be able to detect
+                    // whether we have changed it or not afterwards.
                     *result = HTNOWHERE;
-                    // Even if the mouse is inside the chrome button area now, we should still allow the user
-                    // to be able to resize the window with the top or right window border, this is also the
-                    // normal behavior of a native Win32 window (but only when the window is not maximized/
-                    // fullscreened/minimized, of course).
+                    // Even if the mouse is inside the chrome button area now, we should still allow
+                    // the user to be able to resize the window with the top or right window border,
+                    // this is also the normal behavior of a native Win32 window (but only when the
+                    // window is not maximized/fullscreen/minimized, of course).
                     if (isWindowNoState(hWnd)) {
                         static constexpr const int kBorderSize = 2;
-                        const bool isTop = (nativeLocalPos.y <= kBorderSize);
-                        const bool isRight = (nativeLocalPos.x >= (clientWidth - kBorderSize));
+                        bool isTop = (nativeLocalPos.y <= kBorderSize);
+                        bool isRight = (nativeLocalPos.x >= (clientWidth - kBorderSize));
                         if (isTop || isRight) {
                             if (dontOverrideCursor) {
-                                // The user doesn't want the window to be resized, so we tell Windows we are
-                                // in the client area so that the controls beneath the mouse cursor can still
-                                // be hovered or clicked.
+                                // The user doesn't want the window to be resized, so we tell
+                                // Windows we are in the client area so that the controls beneath
+                                // the mouse cursor can still be hovered or clicked.
                                 *result = (isTitleBar ? HTCAPTION : HTCLIENT);
                             } else {
                                 if (isTop && isRight) {
@@ -1139,8 +1144,9 @@
                         }
                     }
                     if (*result == HTNOWHERE) {
-                        // OK, we are now really inside one of the chrome buttons, tell Windows the exact role of our button.
-                        // The Snap Layout feature introduced in Windows 11 won't work without this.
+                        // OK, we are now really inside one of the chrome buttons, tell Windows the
+                        // exact role of our button. The Snap Layout feature introduced in Windows
+                        // 11 won't work without this.
                         switch (sysButtonType) {
                             case CoreWindowAgent::WindowIcon:
                                 *result = HTSYSMENU;
@@ -1162,30 +1168,33 @@
                         }
                     }
                     if (*result == HTNOWHERE) {
-                        // OK, it seems we are not inside the window resize area, nor inside the chrome buttons,
-                        // tell Windows we are in the client area to let Qt handle this event.
+                        // OK, it seems we are not inside the window resize area, nor inside the
+                        // chrome buttons, tell Windows we are in the client area to let Qt handle
+                        // this event.
                         *result = HTCLIENT;
                     }
                     return true;
                 }
-                // OK, we are not inside any chrome buttons, try to find out which part of the window
-                // are we hitting.
+                // OK, we are not inside any chrome buttons, try to find out which part of the
+                // window are we hitting.
 
-                const bool max = IsMaximized(hWnd);
-                const bool full = isFullScreen(hWnd);
-                const int frameSize = getResizeBorderThickness(hWnd);
-                const bool isTop = (nativeLocalPos.y < frameSize);
+                bool max = IsMaximized(hWnd);
+                bool full = isFullScreen(hWnd);
+                int frameSize = getResizeBorderThickness(hWnd);
+                bool isTop = (nativeLocalPos.y < frameSize);
 
                 if (isWin10OrGreater()) {
                     // This will handle the left, right and bottom parts of the frame
                     // because we didn't change them.
-                    const LRESULT originalHitTestResult = ::DefWindowProcW(hWnd, WM_NCHITTEST, 0, lParam);
+                    LRESULT originalHitTestResult = ::DefWindowProcW(hWnd, WM_NCHITTEST, 0, lParam);
                     if (originalHitTestResult != HTCLIENT) {
-                        // Even if the window is not resizable, we still can't return HTCLIENT here because
-                        // when we enter this code path, it means the mouse cursor is outside the window,
-                        // that is, the three transparent window resize area. Returning HTCLIENT will confuse
-                        // Windows, we can't put our controls there anyway.
-                        *result = ((isFixedSize || dontOverrideCursor) ? HTBORDER : originalHitTestResult);
+                        // Even if the window is not resizable, we still can't return HTCLIENT here
+                        // because when we enter this code path, it means the mouse cursor is
+                        // outside the window, that is, the three transparent window resize area.
+                        // Returning HTCLIENT will confuse Windows, we can't put our controls there
+                        // anyway.
+                        *result = ((isFixedSize || dontOverrideCursor) ? HTBORDER
+                                                                       : originalHitTestResult);
                         return true;
                     }
                     if (full) {
@@ -1205,7 +1214,9 @@
                         // Return HTCLIENT instead of HTBORDER here, because the mouse is
                         // inside our homemade title bar now, return HTCLIENT to let our
                         // title bar can still capture mouse events.
-                        *result = ((isFixedSize || dontOverrideCursor) ? (isTitleBar ? HTCAPTION : HTCLIENT) : HTTOP);
+                        *result = ((isFixedSize || dontOverrideCursor)
+                                       ? (isTitleBar ? HTCAPTION : HTCLIENT)
+                                       : HTTOP);
                         return true;
                     }
                     if (isTitleBar) {
diff --git a/src/core/qwkcoreglobal.cpp b/src/core/qwkcoreglobal.cpp
new file mode 100644
index 0000000..9047b5d
--- /dev/null
+++ b/src/core/qwkcoreglobal.cpp
@@ -0,0 +1,74 @@
+#include "qwkcoreglobal_p.h"
+
+#include <QtCore/QAbstractNativeEventFilter>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QSet>
+
+namespace QWK {
+
+    // Avoid adding multiple global native event filters to QCoreApplication
+    // in this library.
+    class NativeEventFilterManager : public QAbstractNativeEventFilter {
+    public:
+        NativeEventFilterManager() {
+            qApp->installNativeEventFilter(this);
+        }
+
+        ~NativeEventFilterManager() override {
+            qApp->removeNativeEventFilter(this);
+        }
+
+        bool nativeEventFilter(const QByteArray &eventType, void *message,
+                               QT_NATIVE_EVENT_RESULT_TYPE *result) override {
+            for (const auto &child : qAsConst(m_children)) {
+                if (child->nativeEventFilter(eventType, message, result)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        inline int count() const {
+            return m_children.size();
+        }
+
+        inline void addChild(QAbstractNativeEventFilter *child) {
+            m_children.append(child);
+        }
+
+        inline void removeChild(QAbstractNativeEventFilter *child) {
+            m_children.removeOne(child);
+        }
+
+        static NativeEventFilterManager *instance;
+
+    protected:
+        QVector<QAbstractNativeEventFilter *> m_children;
+    };
+
+    NativeEventFilterManager *NativeEventFilterManager::instance = nullptr;
+
+    void installNativeEventFilter(QAbstractNativeEventFilter *eventFilter) {
+        if (!eventFilter) {
+            return;
+        }
+
+        if (!NativeEventFilterManager::instance) {
+            NativeEventFilterManager::instance = new NativeEventFilterManager();
+        }
+        NativeEventFilterManager::instance->addChild(eventFilter);
+    }
+
+    void removeNativeEventFilter(QAbstractNativeEventFilter *eventFilter) {
+        if (!eventFilter || !NativeEventFilterManager::instance) {
+            return;
+        }
+        NativeEventFilterManager::instance->removeChild(eventFilter);
+
+        if (NativeEventFilterManager::instance->count() == 0) {
+            delete NativeEventFilterManager::instance;
+            NativeEventFilterManager::instance = nullptr;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/core/qwkcoreglobal_p.h b/src/core/qwkcoreglobal_p.h
index 52e9653..d18b54f 100644
--- a/src/core/qwkcoreglobal_p.h
+++ b/src/core/qwkcoreglobal_p.h
@@ -3,6 +3,7 @@
 
 #include <QtCore/QEvent>
 #include <QtCore/QLoggingCategory>
+#include <QtCore/QAbstractNativeEventFilter>
 #include <QtGui/QtEvents>
 
 #include <QWKCore/qwkcoreglobal.h>
@@ -22,7 +23,16 @@
 #define QWK_WARNING  qCWarning(qWindowKitLog)
 #define QWK_CRITICAL qCCritical(qWindowKitLog)
 #if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
-#  define QWK_FATAL  qCFatal(qWindowKitLog)
+#  define QWK_FATAL qCFatal(qWindowKitLog)
 #endif
 
+
+namespace QWK {
+
+    QWK_CORE_EXPORT void installNativeEventFilter(QAbstractNativeEventFilter *eventFilter);
+
+    QWK_CORE_EXPORT void removeNativeEventFilter(QAbstractNativeEventFilter *eventFilter);
+
+}
+
 #endif // QWKCOREGLOBAL_P_H

--
Gitblit v1.9.1