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