Improve event filter handling
| | |
| | | set(_src |
| | | qwkglobal.h |
| | | qwkglobal_p.h |
| | | qwkglobal.cpp |
| | | windowagentbase.h |
| | | windowagentbase_p.h |
| | | windowagentbase.cpp |
| | |
| | | |
| | | 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; |
| | |
| | | m_host = host; |
| | | m_delegate.reset(delegate); |
| | | m_winIdChangeEventFilter.reset(delegate->createWinIdEventFilter(host, this)); |
| | | m_windowEventFilter = std::make_unique<WindowEventFilter>(m_windowHandle, this); |
| | | notifyWinIdChange(); |
| | | } |
| | | |
| | |
| | | // 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; |
| | |
| | | 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) { |
| | |
| | | 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); |
| | |
| | | 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; |
| | |
| | | std::array<QObject *, WindowAgentBase::Close + 1> m_systemButtons{}; |
| | | |
| | | QVariantHash m_windowAttributes; |
| | | |
| | | std::unique_ptr<QObject> m_windowEventFilter; |
| | | std::unique_ptr<WinIdChangeEventFilter> m_winIdChangeEventFilter; |
| | | }; |
| | | |
| | |
| | | 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)); |
| | |
| | | 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 |
| | |
| | | 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); |
| | |
| | | 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) && |
| | |
| | | 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) { |
New file |
| | |
| | | // 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; |
| | | } |
| | | |
| | | } |
| | |
| | | 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 |
| | |
| | | #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> |
| | |
| | | } |
| | | |
| | | 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 |
| | |
| | | } |
| | | |
| | | 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. |