Sine Striker
2024-02-22 77bce901860548cd84c1680672ec75fbd6184cf8
Add new winIdChange event filter
14个文件已修改
2个文件已添加
294 ■■■■ 已修改文件
src/core/CMakeLists.txt 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/abstractwindowcontext.cpp 44 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/abstractwindowcontext_p.h 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/cocoawindowcontext.mm 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/cocoawindowcontext_p.h 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/qtwindowcontext.cpp 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/qtwindowcontext_p.h 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/win32windowcontext.cpp 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/win32windowcontext_p.h 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/kernel/winidchangeeventfilter.cpp 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/kernel/winidchangeeventfilter_p.h 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/windowitemdelegate.cpp 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/windowitemdelegate_p.h 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/widgets/widgetitemdelegate.cpp 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/widgets/widgetitemdelegate_p.h 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/widgets/widgetwindowagent.cpp 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/CMakeLists.txt
@@ -15,6 +15,8 @@
    kernel/nativeeventfilter.cpp
    kernel/sharedeventfilter_p.h
    kernel/sharedeventfilter.cpp
    kernel/winidchangeeventfilter_p.h
    kernel/winidchangeeventfilter.cpp
    shared/systemwindow_p.h
    contexts/abstractwindowcontext_p.h
    contexts/abstractwindowcontext.cpp
src/core/contexts/abstractwindowcontext.cpp
@@ -7,34 +7,12 @@
#include <QtGui/QPen>
#include <QtGui/QPainter>
#include <QtGui/QScreen>
#include <memory>
#include "qwkglobal_p.h"
namespace QWK {
    namespace {
        class WinIdChangeEventFilter : public QObject {
        public:
            explicit WinIdChangeEventFilter(QObject *widget, AbstractWindowContext *ctx,
                                            QObject *parent = nullptr)
                : QObject(parent), ctx(ctx) {
                widget->installEventFilter(this);
            }
        protected:
            bool eventFilter(QObject *obj, QEvent *event) override {
                Q_UNUSED(obj)
                if (event->type() == QEvent::WinIdChange) {
                    ctx->notifyWinIdChange();
                }
                return false;
            }
        protected:
            AbstractWindowContext *ctx;
        };
        class WindowEventFilter : public QObject {
        public:
@@ -77,16 +55,10 @@
        }
        m_host = host;
        m_delegate.reset(delegate);
        m_winIdChangeEventFilter = std::make_unique<WinIdChangeEventFilter>(host, this);
        m_winIdChangeEventFilter.reset(delegate->createWinIdEventFilter(host, this));
        m_windowEventFilter = std::make_unique<WindowEventFilter>(m_windowHandle, this);
        m_windowHandle = m_delegate->hostWindow(m_host);
        if (m_windowHandle) {
            winIdChanged();
        notifyWinIdChange();
        }
    }
    bool AbstractWindowContext::setHitTestVisible(const QObject *obj, bool visible) {
        Q_ASSERT(obj);
@@ -259,15 +231,21 @@
    }
    void AbstractWindowContext::notifyWinIdChange() {
        auto windowEventFilter = static_cast<WindowEventFilter *>(m_windowEventFilter.get());
        windowEventFilter->setWindow(nullptr);
        auto oldWinId = m_windowId;
        m_windowId = m_winIdChangeEventFilter->winId();
        // In Qt6, after QWidget::close() is called, the related QWindow's all surfaces and the
        // 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.
        m_windowHandle = m_delegate->hostWindow(m_host);
        winIdChanged();
        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);
src/core/contexts/abstractwindowcontext_p.h
@@ -26,6 +26,7 @@
#include <QWKCore/private/nativeeventfilter_p.h>
#include <QWKCore/private/sharedeventfilter_p.h>
#include <QWKCore/private/windowitemdelegate_p.h>
#include <QWKCore/private/winidchangeeventfilter_p.h>
namespace QWK {
@@ -81,7 +82,7 @@
        virtual bool setWindowAttribute(const QString &key, const QVariant &attribute);
    protected:
        virtual void winIdChanged() = 0;
        virtual void winIdChanged(WId winId, WId oldWinId) = 0;
        virtual bool windowAttributeChanged(const QString &key, const QVariant &attribute,
                                            const QVariant &oldAttribute);
@@ -89,6 +90,7 @@
        QObject *m_host{};
        std::unique_ptr<WindowItemDelegate> m_delegate;
        QWindow *m_windowHandle{};
        WId m_windowId{};
        QSet<const QObject *> m_hitTestVisibleItems;
#ifdef Q_OS_MAC
@@ -101,7 +103,7 @@
        QVariantHash m_windowAttributes;
        std::unique_ptr<QObject> m_windowEventFilter;
        std::unique_ptr<QObject> m_winIdChangeEventFilter;
        std::unique_ptr<WinIdChangeEventFilter> m_winIdChangeEventFilter;
    };
    inline QObject *AbstractWindowContext::host() const {
src/core/contexts/cocoawindowcontext.mm
@@ -664,7 +664,7 @@
    }
    CocoaWindowContext::~CocoaWindowContext() {
        releaseWindowProxy(windowId);
        releaseWindowProxy(m_windowId);
    }
    QString CocoaWindowContext::key() const {
@@ -674,7 +674,7 @@
    void CocoaWindowContext::virtual_hook(int id, void *data) {
        switch (id) {
            case SystemButtonAreaChangedHook: {
                ensureWindowProxy(windowId)->setScreenRectCallback(m_systemButtonAreaCallback);
                ensureWindowProxy(m_windowId)->setScreenRectCallback(m_systemButtonAreaCallback);
                return;
            }
@@ -686,18 +686,17 @@
    QVariant CocoaWindowContext::windowAttribute(const QString &key) const {
        if (key == QStringLiteral("title-bar-height")) {
            if (!m_windowHandle)
                return 0;
            return ensureWindowProxy(windowId)->titleBarHeight();
            if (!m_windowId)
                return {};
            return ensureWindowProxy(m_windowId)->titleBarHeight();
        }
        return AbstractWindowContext::windowAttribute(key);
    }
    void CocoaWindowContext::winIdChanged() {
    void CocoaWindowContext::winIdChanged(WId winId, WId oldWinId) {
        // If the original window id is valid, remove all resources related
        if (windowId) {
            releaseWindowProxy(windowId);
            windowId = 0;
        if (oldWinId) {
            releaseWindowProxy(oldWinId);
        }
        if (!m_windowHandle) {
@@ -705,8 +704,7 @@
        }
        // Allocate new resources
        windowId = m_windowHandle->winId();
        ensureWindowProxy(windowId)->setSystemTitleBarVisible(false);
        ensureWindowProxy(winId)->setSystemTitleBarVisible(false);
    }
    bool CocoaWindowContext::windowAttributeChanged(const QString &key, const QVariant &attribute,
@@ -716,7 +714,7 @@
        if (key == QStringLiteral("no-system-buttons")) {
            if (attribute.type() != QVariant::Bool)
                return false;
            ensureWindowProxy(windowId)->setSystemButtonVisible(!attribute.toBool());
            ensureWindowProxy(m_windowId)->setSystemButtonVisible(!attribute.toBool());
            return true;
        }
@@ -743,7 +741,7 @@
            } else {
                return false;
            }
            return ensureWindowProxy(windowId)->setBlurEffect(mode);
            return ensureWindowProxy(m_windowId)->setBlurEffect(mode);
        }
        return false;
src/core/contexts/cocoawindowcontext_p.h
@@ -30,13 +30,11 @@
        QVariant windowAttribute(const QString &key) const override;
    protected:
        void winIdChanged() override;
        void winIdChanged(WId winId, WId oldWinId) override;
        bool windowAttributeChanged(const QString &key, const QVariant &attribute,
                                    const QVariant &oldAttribute) override;
    protected:
        WId windowId = 0;
        std::unique_ptr<SharedEventFilter> cocoaWindowEventFilter;
    };
src/core/contexts/qtwindowcontext.cpp
@@ -253,7 +253,7 @@
        AbstractWindowContext::virtual_hook(id, data);
    }
    void QtWindowContext::winIdChanged() {
    void QtWindowContext::winIdChanged(WId winId, WId oldWinId) {
        if (!m_windowHandle) {
            m_delegate->setWindowFlags(m_host, m_delegate->getWindowFlags(m_host) &
                                                   ~Qt::FramelessWindowHint);
src/core/contexts/qtwindowcontext_p.h
@@ -28,7 +28,7 @@
        void virtual_hook(int id, void *data) override;
    protected:
        void winIdChanged() override;
        void winIdChanged(WId winId, WId oldWinId) override;
    protected:
        std::unique_ptr<SharedEventFilter> qtWindowEventFilter;
src/core/contexts/win32windowcontext.cpp
@@ -321,7 +321,7 @@
            case HTBORDER:
                return Win32WindowContext::FixedBorder;
            default:
                // unreachable
                Q_UNREACHABLE();
                break;
        }
        return Win32WindowContext::Outside;
@@ -540,8 +540,8 @@
    }
    Win32WindowContext::~Win32WindowContext() {
        if (windowId) {
            removeManagedWindow(reinterpret_cast<HWND>(windowId));
        if (m_windowId) {
            removeManagedWindow(reinterpret_cast<HWND>(m_windowId));
        }
    }
@@ -552,19 +552,19 @@
    void Win32WindowContext::virtual_hook(int id, void *data) {
        switch (id) {
            case RaiseWindowHook: {
                if (!windowId)
                if (!m_windowId)
                    return;
                m_delegate->setWindowVisible(m_host, true);
                const auto hwnd = reinterpret_cast<HWND>(windowId);
                const auto hwnd = reinterpret_cast<HWND>(m_windowId);
                bringWindowToFront(hwnd);
                return;
            }
            case ShowSystemMenuHook: {
                if (!windowId)
                if (!m_windowId)
                    return;
                const auto &pos = *static_cast<const QPoint *>(data);
                auto hWnd = reinterpret_cast<HWND>(windowId);
                auto hWnd = reinterpret_cast<HWND>(m_windowId);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
                const QPoint nativeGlobalPos =
                    QHighDpi::toNativeGlobalPosition(pos, m_windowHandle);
@@ -588,14 +588,14 @@
#if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS)
            case DrawWindows10BorderHook: {
                if (!windowId)
                if (!m_windowId)
                    return;
                auto args = static_cast<void **>(data);
                auto &painter = *static_cast<QPainter *>(args[0]);
                const auto &rect = *static_cast<const QRect *>(args[1]);
                const auto &region = *static_cast<const QRegion *>(args[2]);
                const auto hwnd = reinterpret_cast<HWND>(windowId);
                const auto hwnd = reinterpret_cast<HWND>(m_windowId);
                QPen pen;
                pen.setWidth(int(getWindowFrameBorderThickness(hwnd)) * 2);
@@ -638,7 +638,7 @@
                // https://docs.microsoft.com/en-us/windows/win32/dwm/customframe#extending-the-client-frame
                // Draw a black rectangle to make Windows native top border show
                auto hWnd = reinterpret_cast<HWND>(windowId);
                auto hWnd = reinterpret_cast<HWND>(m_windowId);
                HDC hdc = ::GetDC(hWnd);
                RECT windowRect{};
                ::GetClientRect(hWnd, &windowRect);
@@ -667,7 +667,7 @@
                return {};
            RECT frame{};
            auto hwnd = reinterpret_cast<HWND>(windowId);
            auto hwnd = reinterpret_cast<HWND>(m_windowId);
            // According to MSDN, WS_OVERLAPPED is not allowed for AdjustWindowRect.
            auto style = static_cast<DWORD>(::GetWindowLongPtrW(hwnd, GWL_STYLE) & ~WS_OVERLAPPED);
            auto exStyle = static_cast<DWORD>(::GetWindowLongPtrW(hwnd, GWL_EXSTYLE));
@@ -687,28 +687,25 @@
        if (key == QStringLiteral("border-thickness")) {
            return m_windowHandle
                       ? int(getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId)))
                       ? int(getWindowFrameBorderThickness(reinterpret_cast<HWND>(m_windowId)))
                       : 0;
        }
        return AbstractWindowContext::windowAttribute(key);
    }
    void Win32WindowContext::winIdChanged() {
    void Win32WindowContext::winIdChanged(WId winId, WId oldWinId) {
        // If the original window id is valid, remove all resources related
        if (windowId) {
            removeManagedWindow(reinterpret_cast<HWND>(windowId));
            windowId = 0;
        if (oldWinId) {
            removeManagedWindow(reinterpret_cast<HWND>(oldWinId));
        }
        if (!m_windowHandle) {
        if (!m_windowHandle || ! winId) {
            return;
        }
        // Install window hook
        auto winId = m_windowHandle->winId();
        auto hWnd = reinterpret_cast<HWND>(winId);
        if (!isSystemBorderEnabled()) {
            static auto margins = QVariant::fromValue(QMargins(1, 1, 1, 1));
@@ -732,14 +729,15 @@
        // Add managed window
        addManagedWindow(m_windowHandle, hWnd, this);
        // Cache win id
        windowId = winId;
    }
    bool Win32WindowContext::windowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
                                        LRESULT *result) {
        *result = FALSE;
        if (message == WM_DESTROY) {
            qDebug() << "WM_DESTROY";
        }
        // We should skip these messages otherwise we will get crashes.
        // NOTE: WM_QUIT won't be posted to the WindowProc function.
@@ -757,6 +755,20 @@
        if (!isValidWindow(hWnd, false, true)) {
            return false;
        }
        switch (message) {
            case WM_NCMOUSELEAVE:
                qDebug() << "WM_NCMOUSELEAVE" << mouseLeaveBlocked;
                break;
            case WM_MOUSEHOVER:
                qDebug() << "WM_MOUSEHOVER" << mouseLeaveBlocked;
                break;
            case WM_NCMOUSEHOVER:
                qDebug() << "WM_NCMOUSEHOVER" << mouseLeaveBlocked;
                break;
            default:
                break;
        }
        // Test snap layout
@@ -794,7 +806,7 @@
                                                    const QVariant &oldAttribute) {
        Q_UNUSED(oldAttribute)
        const auto hwnd = reinterpret_cast<HWND>(m_windowHandle->winId());
        const auto hwnd = reinterpret_cast<HWND>(m_windowId);
        const DynamicApis &apis = DynamicApis::instance();
        static constexpr const MARGINS extendedMargins = {-1, -1, -1, -1};
        const auto &restoreMargins = [this, &apis, hwnd]() {
@@ -1144,6 +1156,8 @@
                    }
                }
                mouseLeaveBlocked = false;
                qDebug() << "WM_MOUSELEAVE";
                break;
            }
@@ -1152,6 +1166,7 @@
                // we unset `mouseLeaveBlocked` mark and pretend as if Qt has received
                // WM_MOUSELEAVE.
                if (lastHitTestResult != WindowPart::ChromeButton && mouseLeaveBlocked) {
                    qDebug() << lastHitTestResult << "Track";
                    mouseLeaveBlocked = false;
                    requestForMouseLeaveMessage(hWnd, false);
                }
@@ -1240,7 +1255,7 @@
                    // If the mouse is entering the client area, there must be a WM_NCHITTEST
                    // setting it to `Client` before the WM_NCMOUSELEAVE comes; if the mouse is
                    // leaving the window, current window part remains as `Outside`.
                    lastHitTestResult = WindowPart::Outside;
                    // lastHitTestResult = WindowPart::Outside;
                }
                if (currentWindowPart == WindowPart::ChromeButton) {
src/core/contexts/win32windowcontext_p.h
@@ -33,6 +33,7 @@
            FixedBorder,
            TitleBar,
        };
        Q_ENUM(WindowPart)
        QString key() const override;
        void virtual_hook(int id, void *data) override;
@@ -40,7 +41,7 @@
        QVariant windowAttribute(const QString &key) const override;
    protected:
        void winIdChanged() override;
        void winIdChanged(WId winId, WId oldWinId) override;
        bool windowAttributeChanged(const QString &key, const QVariant &attribute,
                                    const QVariant &oldAttribute) override;
@@ -64,8 +65,6 @@
                                      LRESULT *result);
    protected:
        WId windowId = 0;
        // The last hit test result, helpful to handle WM_MOUSEMOVE and WM_NCMOUSELEAVE.
        WindowPart lastHitTestResult = WindowPart::Outside;
src/core/kernel/winidchangeeventfilter.cpp
New file
@@ -0,0 +1,37 @@
// 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 "winidchangeeventfilter_p.h"
#include <qpa/qplatformwindow.h>
#include "abstractwindowcontext_p.h"
namespace QWK {
    WId WindowWinIdChangeEventFilter::winId() const {
        auto win = static_cast<QWindow *>(host);
        if (isAboutToBeDestroyed)
            return 0;
        if (auto platformWindow = win->handle())
            return platformWindow->winId();
        return 0;
    }
    bool WindowWinIdChangeEventFilter::eventFilter(QObject *obj, QEvent *event) {
        Q_UNUSED(obj)
        if (event->type() == QEvent::PlatformSurface) {
            auto e = static_cast<QPlatformSurfaceEvent *>(event);
            if (e->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
                isAboutToBeDestroyed = true;
                context->notifyWinIdChange();
                isAboutToBeDestroyed = false;
            } else {
                context->notifyWinIdChange();
            }
        }
        return false;
    }
}
src/core/kernel/winidchangeeventfilter_p.h
New file
@@ -0,0 +1,56 @@
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
// SPDX-License-Identifier: Apache-2.0
#ifndef WINIDCHANGEEVENTFILTER_P_H
#define WINIDCHANGEEVENTFILTER_P_H
//
//  W A R N I N G !!!
//  -----------------
//
// This file is not part of the QWindowKit API. It is used purely as an
// implementation detail. This header file may change from version to
// version without notice, or may even be removed.
//
#include <QtGui/QWindow>
#include <QWKCore/qwkglobal.h>
namespace QWK {
    class AbstractWindowContext;
    class WinIdChangeEventFilter : public QObject {
    public:
        WinIdChangeEventFilter(QObject *host, AbstractWindowContext *context)
            : host(host), context(context) {
        }
        virtual WId winId() const = 0;
    protected:
        QObject *host;
        AbstractWindowContext *context;
    };
    class QWK_CORE_EXPORT WindowWinIdChangeEventFilter : public WinIdChangeEventFilter {
    public:
        WindowWinIdChangeEventFilter(QObject *host, AbstractWindowContext *context)
            : WinIdChangeEventFilter(host, context), win(static_cast<QWindow *>(host)),
              isAboutToBeDestroyed(false) {
        }
        WId winId() const override;
    protected:
        QWindow *win;
        bool isAboutToBeDestroyed;
        bool eventFilter(QObject *obj, QEvent *event) override;
    };
}
#endif // WINIDCHANGEEVENTFILTER_P_H
src/core/windowitemdelegate.cpp
@@ -14,4 +14,10 @@
        Q_UNUSED(host);
    }
    WinIdChangeEventFilter *
        WindowItemDelegate::createWinIdEventFilter(QObject *host,
                                                   AbstractWindowContext *context) const {
        return new WindowWinIdChangeEventFilter(host, context);
    }
}
src/core/windowitemdelegate_p.h
@@ -18,6 +18,7 @@
#include <QtCore/QPoint>
#include <QtGui/QWindow>
#include <QWKCore/private/winidchangeeventfilter_p.h>
#include <QWKCore/qwkglobal.h>
namespace QWK {
@@ -52,6 +53,10 @@
        virtual void setGeometry(QObject *host, const QRect &rect) = 0;
        virtual void bringWindowToTop(QObject *host) const = 0;
        // Factories
        virtual WinIdChangeEventFilter *
            createWinIdEventFilter(QObject *host, AbstractWindowContext *context) const;
    private:
        Q_DISABLE_COPY(WindowItemDelegate)
    };
src/widgets/widgetitemdelegate.cpp
@@ -5,12 +5,37 @@
#include "widgetitemdelegate_p.h"
#include <QtGui/QMouseEvent>
#include <QtWidgets/QWidget>
#include <QtWidgets/QApplication>
#include <QtWidgets/QWidget>
#include <QWKCore/private/abstractwindowcontext_p.h>
extern Q_DECL_IMPORT QWidget *qt_button_down;
namespace QWK {
    class WidgetWinIdChangeEventFilter : public WinIdChangeEventFilter {
    public:
        explicit WidgetWinIdChangeEventFilter(QObject *host, AbstractWindowContext *ctx)
            : WinIdChangeEventFilter(host, ctx), widget(static_cast<QWidget *>(host)) {
            widget->installEventFilter(this);
        }
        WId winId() const override {
            return widget->effectiveWinId();
        }
    protected:
        bool eventFilter(QObject *obj, QEvent *event) override {
            Q_UNUSED(obj)
            if (event->type() == QEvent::WinIdChange) {
                context->notifyWinIdChange();
            }
            return false;
        }
        QWidget *widget;
    };
    WidgetItemDelegate::WidgetItemDelegate() = default;
@@ -112,4 +137,10 @@
        static_cast<QWidget *>(host)->raise();
    }
    WinIdChangeEventFilter *
        WidgetItemDelegate::createWinIdEventFilter(QObject *host,
                                                   AbstractWindowContext *context) const {
        return new WidgetWinIdChangeEventFilter(host, context);
    }
}
src/widgets/widgetitemdelegate_p.h
@@ -48,6 +48,9 @@
        void setWindowVisible(QObject *host, bool visible) const override;
        void setGeometry(QObject *host, const QRect &rect) override;
        void bringWindowToTop(QObject *host) const override;
        WinIdChangeEventFilter *
            createWinIdEventFilter(QObject *host, AbstractWindowContext *context) const override;
    };
}
src/widgets/widgetwindowagent.cpp
@@ -56,7 +56,7 @@
        }
        w->setAttribute(Qt::WA_DontCreateNativeAncestors);
        w->setAttribute(Qt::WA_NativeWindow);
        w->setAttribute(Qt::WA_NativeWindow); // Create new window id
        d->setup(w, new WidgetItemDelegate());
        d->hostWidget = w;