From 564f33e8b29a6f73050f4da3f843b942aaf0d739 Mon Sep 17 00:00:00 2001
From: Sine Striker <trueful@163.com>
Date: 周三, 08 5月 2024 19:36:16 +0800
Subject: [PATCH] Improve event filter handling

---
 src/core/contexts/abstractwindowcontext.cpp |   49 +++------------
 src/widgets/widgetwindowagent_win.cpp       |   20 +++++-
 src/core/qwkglobal.cpp                      |   50 ++++++++++++++++
 src/core/contexts/abstractwindowcontext_p.h |    7 +-
 src/core/CMakeLists.txt                     |    1 
 src/core/qwkglobal_p.h                      |    4 +
 src/core/contexts/win32windowcontext.cpp    |   16 ++--
 7 files changed, 94 insertions(+), 53 deletions(-)

diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 9abe22e..69f5c42 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -6,6 +6,7 @@
 set(_src
     qwkglobal.h
     qwkglobal_p.h
+    qwkglobal.cpp
     windowagentbase.h
     windowagentbase_p.h
     windowagentbase.cpp
diff --git a/src/core/contexts/abstractwindowcontext.cpp b/src/core/contexts/abstractwindowcontext.cpp
index 1c11015..71818c2 100644
--- a/src/core/contexts/abstractwindowcontext.cpp
+++ b/src/core/contexts/abstractwindowcontext.cpp
@@ -12,39 +12,6 @@
 
 namespace QWK {
 
-    namespace {
-
-        class WindowEventFilter : public QObject {
-        public:
-            explicit WindowEventFilter(QWindow *window, AbstractWindowContext *ctx,
-                                       QObject *parent = nullptr)
-                : QObject(parent), ctx(ctx), window(window) {
-                if (window)
-                    window->installEventFilter(this);
-            }
-
-            inline void setWindow(QWindow *win) {
-                if (auto oldWin = window.data(); oldWin) {
-                    oldWin->removeEventFilter(this);
-                }
-                window = win;
-                if (win) {
-                    win->installEventFilter(this);
-                }
-            }
-
-        protected:
-            bool eventFilter(QObject *obj, QEvent *event) override {
-                return ctx->sharedDispatch(obj, event);
-            }
-
-        protected:
-            AbstractWindowContext *ctx;
-            QPointer<QWindow> window;
-        };
-
-    }
-
     AbstractWindowContext::AbstractWindowContext() = default;
 
     AbstractWindowContext::~AbstractWindowContext() = default;
@@ -56,7 +23,6 @@
         m_host = host;
         m_delegate.reset(delegate);
         m_winIdChangeEventFilter.reset(delegate->createWinIdEventFilter(host, this));
-        m_windowEventFilter = std::make_unique<WindowEventFilter>(m_windowHandle, this);
         notifyWinIdChange();
     }
 
@@ -238,17 +204,17 @@
         // platform window will be removed, and the WinId will be set to 0. After that, when the
         // QWidget is shown again, the whole things will be recreated again.
         // As a result, we must update our WindowContext each time the WinId changes.
+        if (m_windowHandle) {
+            removeEventFilter(m_windowHandle);
+        }
         m_windowHandle = m_delegate->hostWindow(m_host);
-
-        auto windowEventFilter = static_cast<WindowEventFilter *>(m_windowEventFilter.get());
-        windowEventFilter->setWindow(nullptr);
 
         if (oldWinId != m_windowId) {
             winIdChanged(m_windowId, oldWinId);
         }
 
         if (m_windowHandle) {
-            windowEventFilter->setWindow(m_windowHandle);
+            m_windowHandle->installEventFilter(this);
 
             // Refresh window attributes
             auto attributes = m_windowAttributes;
@@ -299,6 +265,13 @@
         return true;
     }
 
+    bool AbstractWindowContext::eventFilter(QObject *obj, QEvent *event) {
+        if (obj == m_windowHandle && sharedDispatch(obj, event)) {
+            return true;
+        }
+        return QObject::eventFilter(obj, event);
+    }
+
     bool AbstractWindowContext::windowAttributeChanged(const QString &key,
                                                        const QVariant &attribute,
                                                        const QVariant &oldAttribute) {
diff --git a/src/core/contexts/abstractwindowcontext_p.h b/src/core/contexts/abstractwindowcontext_p.h
index 52eb5cc..27ee162 100644
--- a/src/core/contexts/abstractwindowcontext_p.h
+++ b/src/core/contexts/abstractwindowcontext_p.h
@@ -83,6 +83,9 @@
         virtual bool setWindowAttribute(const QString &key, const QVariant &attribute);
 
     protected:
+        bool eventFilter(QObject *obj, QEvent *event) override;
+
+    protected:
         virtual void winIdChanged(WId winId, WId oldWinId) = 0;
         virtual bool windowAttributeChanged(const QString &key, const QVariant &attribute,
                                             const QVariant &oldAttribute);
@@ -90,7 +93,7 @@
     protected:
         QObject *m_host{};
         std::unique_ptr<WindowItemDelegate> m_delegate;
-        QWindow *m_windowHandle{};
+        QPointer<QWindow> m_windowHandle;
         WId m_windowId{};
 
         QSet<const QObject *> m_hitTestVisibleItems;
@@ -102,8 +105,6 @@
         std::array<QObject *, WindowAgentBase::Close + 1> m_systemButtons{};
 
         QVariantHash m_windowAttributes;
-
-        std::unique_ptr<QObject> m_windowEventFilter;
         std::unique_ptr<WinIdChangeEventFilter> m_winIdChangeEventFilter;
     };
 
diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp
index 9d37d7c..a75efe7 100644
--- a/src/core/contexts/win32windowcontext.cpp
+++ b/src/core/contexts/win32windowcontext.cpp
@@ -699,9 +699,9 @@
                 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));
@@ -1263,7 +1263,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
@@ -1563,8 +1563,8 @@
                 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 isTitleBar = isInTitleBarDraggableArea(qtScenePos);
@@ -2104,8 +2104,8 @@
         switch (message) {
             case WM_RBUTTONUP: {
                 const POINT nativeLocalPos = getNativePosFromMouse();
-                const QPoint qtScenePos =
-                    QHighDpi::fromNativeLocalPosition(point2qpoint(nativeLocalPos), m_windowHandle);
+                const QPoint qtScenePos = QHighDpi::fromNativeLocalPosition(
+                    point2qpoint(nativeLocalPos), m_windowHandle.data());
                 WindowAgentBase::SystemButton sysButtonType = WindowAgentBase::Unknown;
                 if (isInTitleBarDraggableArea(qtScenePos) ||
                     (isInSystemButtons(qtScenePos, &sysButtonType) &&
@@ -2217,7 +2217,7 @@
                     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) {
diff --git a/src/core/qwkglobal.cpp b/src/core/qwkglobal.cpp
new file mode 100644
index 0000000..617df8a
--- /dev/null
+++ b/src/core/qwkglobal.cpp
@@ -0,0 +1,50 @@
+// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
+// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
+// SPDX-License-Identifier: Apache-2.0
+
+#include "qwkglobal_p.h"
+
+#include <QtCore/QCoreApplication>
+
+#include <QtCore/private/qobject_p.h>
+
+namespace QWK {
+
+    class HackedObject : public QObject {
+    public:
+        QObjectPrivate *d_func() const {
+            return static_cast<QObjectPrivate *>(d_ptr.data());
+        }
+    };
+
+    bool forwardObjectEventFilters(QObject *currentFilter, QObject *receiver, QEvent *event) {
+        // https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/corelib/kernel/qcoreapplication.cpp#L1244
+        // Send the event through the rest event filters
+        auto d = static_cast<HackedObject *>(receiver)->d_func();
+        bool findCurrent = false;
+        if (receiver != QCoreApplication::instance() && d->extraData) {
+            for (qsizetype i = 0; i < d->extraData->eventFilters.size(); ++i) {
+                QObject *obj = d->extraData->eventFilters.at(i);
+                if (!findCurrent) {
+                    if (obj == currentFilter) {
+                        findCurrent = true; // Will start to filter from the next one
+                    }
+                    continue;
+                }
+
+                if (!obj)
+                    continue;
+                if (static_cast<HackedObject *>(obj)->d_func()->threadData.loadRelaxed() !=
+                    d->threadData.loadRelaxed()) {
+                    qWarning(
+                        "QCoreApplication: Object event filter cannot be in a different thread.");
+                    continue;
+                }
+                if (obj->eventFilter(receiver, event))
+                    return true;
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/src/core/qwkglobal_p.h b/src/core/qwkglobal_p.h
index 25c3f82..57b499f 100644
--- a/src/core/qwkglobal_p.h
+++ b/src/core/qwkglobal_p.h
@@ -62,6 +62,10 @@
         return event->screenPos().toPoint();
 #endif
     }
+
+    // Be careful when apply this function to a widget
+    QWK_CORE_EXPORT bool forwardObjectEventFilters(QObject *currentFilter, QObject *receiver,
+                                                   QEvent *event);
 }
 
 #endif // QWKGLOBAL_P_H
diff --git a/src/widgets/widgetwindowagent_win.cpp b/src/widgets/widgetwindowagent_win.cpp
index 706e865..d3e8ecd 100644
--- a/src/widgets/widgetwindowagent_win.cpp
+++ b/src/widgets/widgetwindowagent_win.cpp
@@ -8,6 +8,8 @@
 #include <QtCore/QDateTime>
 #include <QtGui/QPainter>
 
+#include <QtCore/private/qcoreapplication_p.h>
+
 #include <QWKCore/qwindowkit_windows.h>
 #include <QWKCore/private/qwkglobal_p.h>
 #include <QWKCore/private/windows10borderhandler_p.h>
@@ -62,8 +64,13 @@
         }
 
         inline void forwardEventToWidgetAndDraw(QWidget *w, QEvent *event) {
-            // Let the widget paint first
-            static_cast<QObject *>(w)->event(event);
+            // https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/widgets/kernel/qapplication.cpp#L3286
+            // Deliver the event
+            if (!forwardObjectEventFilters(ctx, w, event)) {
+                // Let the widget paint first
+                std::ignore = static_cast<QObject *>(w)->event(event);
+                QCoreApplicationPrivate::setEventSpontaneous(event, false);
+            }
 
             // Due to the timer or user action, Qt will repaint some regions spontaneously,
             // even if there is no WM_PAINT message, we must wait for it to finish painting
@@ -72,8 +79,13 @@
         }
 
         inline void forwardEventToWindowAndDraw(QWindow *window, QEvent *event) {
-            // Let Qt paint first
-            static_cast<QObject *>(window)->event(event);
+            // https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/widgets/kernel/qapplication.cpp#L3286
+            // Deliver the event;
+            if (!forwardObjectEventFilters(ctx, window, event)) {
+                // Let Qt paint first
+                std::ignore = static_cast<QObject *>(window)->event(event);
+                QCoreApplicationPrivate::setEventSpontaneous(event, false);
+            }
 
             // Upon receiving the WM_PAINT message, Qt will repaint the entire view, and we
             // must wait for it to finish painting before drawing this top border area.

--
Gitblit v1.9.1