Sine Striker
2023-12-25 29901fc2a97eedd3c914f807d1819c9ea7e69973
Optimize Windows 10 border handling
12个文件已修改
2个文件已添加
418 ■■■■ 已修改文件
examples/mainwindow/mainwindow.cpp 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/CMakeLists.txt 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/abstractwindowcontext.cpp 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/abstractwindowcontext_p.h 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/win32windowcontext.cpp 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/win32windowcontext_p.h 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/kernel/nativeeventfilter.cpp 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/kernel/nativeeventfilter_p.h 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/kernel/sharedeventfilter.cpp 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/kernel/sharedeventfilter_p.h 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/shared/qwkwindowsextra_p.h 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/widgets/widgetwindowagent.cpp 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/widgets/widgetwindowagent_p.h 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/widgets/widgetwindowagent_win.cpp 57 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/mainwindow/mainwindow.cpp
@@ -20,6 +20,10 @@
#include <widgetframe/windowbar.h>
#include <widgetframe/windowbutton.h>
#ifdef Q_OS_WINDOWS
#  include <QWKCore/qwindowkit_windows.h>
#endif
class ClockWidget : public QLabel {
public:
    explicit ClockWidget(QWidget *parent = nullptr) : QLabel(parent) {
@@ -116,7 +120,13 @@
    windowAgent->setup(this);
#ifdef Q_OS_WIN
    windowAgent->setWindowAttribute(QStringLiteral("dark-mode"), true);
    if (QWK::IsWindows10OrGreater_Real() && !QWK::IsWindows11OrGreater_Real()) {
        // windowAgent->setWindowAttribute(QStringLiteral("dark-mode"), true);
        // https://github.com/microsoft/terminal/blob/71a6f26e6ece656084e87de1a528c4a8072eeabd/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp#L940
        // Must call DWM API to extend top frame to client area
        windowAgent->setWindowAttribute(QStringLiteral("extra-margins"), true);
    }
#endif
    // 2. Construct your title bar
src/core/CMakeLists.txt
@@ -13,6 +13,8 @@
    windowitemdelegate.cpp
    kernel/nativeeventfilter_p.h
    kernel/nativeeventfilter.cpp
    kernel/sharedeventfilter_p.h
    kernel/sharedeventfilter.cpp
    shared/systemwindow_p.h
    contexts/abstractwindowcontext_p.h
    contexts/abstractwindowcontext.cpp
@@ -44,6 +46,7 @@
            contexts/win32windowcontext_p.h
            contexts/win32windowcontext.cpp
        )
        list(APPEND _links_private uxtheme)
    elseif(APPLE)
        list(APPEND _src
            contexts/cocoawindowcontext_p.h
src/core/contexts/abstractwindowcontext.cpp
@@ -9,6 +9,8 @@
namespace QWK {
    namespace {
    class WinIdChangeEventFilter : public QObject {
    public:
        explicit WinIdChangeEventFilter(QObject *widget, AbstractWindowContext *ctx,
@@ -30,6 +32,26 @@
        AbstractWindowContext *ctx;
    };
        class WindowEventFilter : public QObject {
        public:
            explicit WindowEventFilter(QWindow *window, AbstractWindowContext *ctx,
                                       QObject *parent = nullptr)
                : QObject(parent), ctx(ctx), window(window) {
                window->installEventFilter(this);
            }
        protected:
            bool eventFilter(QObject *obj, QEvent *event) override {
                return ctx->sharedDispatch(obj, event);
            }
        protected:
            AbstractWindowContext *ctx;
            QWindow *window;
        };
    }
    AbstractWindowContext::AbstractWindowContext() = default;
    AbstractWindowContext::~AbstractWindowContext() = default;
@@ -45,6 +67,7 @@
        m_windowHandle = m_delegate->hostWindow(m_host);
        if (m_windowHandle) {
            winIdChanged();
            m_windowEventFilter = std::make_unique<WindowEventFilter>(m_windowHandle, this);
        }
    }
@@ -245,9 +268,11 @@
        m_windowHandle = m_delegate->hostWindow(m_host);
        if (oldWindow == m_windowHandle)
            return;
        m_windowEventFilter.reset();
        winIdChanged();
        if (m_windowHandle) {
            m_windowEventFilter = std::make_unique<WindowEventFilter>(m_windowHandle, this);
            // Refresh window attributes
            auto attributes = m_windowAttributes;
            m_windowAttributes.clear();
src/core/contexts/abstractwindowcontext_p.h
@@ -20,11 +20,14 @@
#include <QWKCore/windowagentbase.h>
#include <QWKCore/private/nativeeventfilter_p.h>
#include <QWKCore/private/sharedeventfilter_p.h>
#include <QWKCore/private/windowitemdelegate_p.h>
namespace QWK {
    class QWK_CORE_EXPORT AbstractWindowContext : public QObject, public NativeEventDispatcher {
    class QWK_CORE_EXPORT AbstractWindowContext : public QObject,
                                                  public NativeEventDispatcher,
                                                  public SharedEventDispatcher {
        Q_OBJECT
    public:
        AbstractWindowContext();
@@ -93,6 +96,7 @@
        QVariantHash m_windowAttributes;
        std::unique_ptr<QObject> m_windowEventFilter;
        std::unique_ptr<QObject> m_winIdChangeEventFilter;
    };
src/core/contexts/win32windowcontext.cpp
@@ -729,27 +729,49 @@
                return;
            }
                //            case AbstractWindowContext::DrawWindows10BackgroundHook: {
                // #if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDER)
                //                if (!m_windowHandle)
                //                    return;
                //
                //                auto hWnd = reinterpret_cast<HWND>(windowId);
                //                HDC hdc = ::GetDC(hWnd);
                //                RECT windowRect{};
                //                ::GetClientRect(hWnd, &windowRect);
                //                RECT rcRest = {
                //                    0,
                //                    int(getWindowFrameBorderThickness(hWnd)),
                //                    RECT_WIDTH(windowRect),
                //                    RECT_HEIGHT(windowRect),
                //                };
                //                HBRUSH blueBrush = ::CreateSolidBrush(RGB(0, 0, 255));
                //
                //                // To hide the original title bar, we have to paint on top of it
                //                with
                //                // the alpha component set to 255. This is a hack to do it with
                //                GDI.
                //                // See NonClientIslandWindow::_UpdateFrameMargins for more
                //                information. HDC opaqueDc; BP_PAINTPARAMS params =
                //                {sizeof(params), BPPF_NOCLIP | BPPF_ERASE}; auto buf =
                //                BeginBufferedPaint(hdc, &rcRest, BPBF_TOPDOWNDIB, &params,
                //                &opaqueDc); if (!buf || !opaqueDc) {
                //                    return;
                //                }
                //
                //                ::FillRect(opaqueDc, &rcRest, blueBrush);
                //                ::BufferedPaintSetAlpha(buf, nullptr, 255);
                //                ::EndBufferedPaint(buf, TRUE);
                //
                //                ::DeleteObject(blueBrush);
                //                ::ReleaseDC(hWnd, hdc);
                // #endif
                //                return;
                //            }
            default:
                break;
        }
        AbstractWindowContext::virtual_hook(id, data);
    }
    bool Win32WindowContext::needBorderPainter() const {
        Q_UNUSED(this)
        return isSystemBorderEnabled() && !isWin11OrGreater();
    }
    int Win32WindowContext::borderThickness() const {
        return int(getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId)));
    }
    void Win32WindowContext::resume(const QByteArray &eventType, void *message,
                                    QT_NATIVE_EVENT_RESULT_TYPE *result) {
        const auto msg = static_cast<const MSG *>(message);
        LRESULT res =
            ::CallWindowProcW(g_qtWindowProc, msg->hwnd, msg->message, msg->wParam, msg->lParam);
        *result = decltype(*result)(res);
    }
    void Win32WindowContext::winIdChanged() {
@@ -768,13 +790,7 @@
        auto hWnd = reinterpret_cast<HWND>(winId);
        if (!isSystemBorderEnabled()) {
            static constexpr const MARGINS margins = {1, 1, 1, 1};
            DynamicApis::instance().pDwmExtendFrameIntoClientArea(hWnd, &margins);
        } else if (isWin10OrGreater() && !isWin11OrGreater()) {
            // https://github.com/microsoft/terminal/blob/71a6f26e6ece656084e87de1a528c4a8072eeabd/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp#L940
            // Must call DWM API to extend top frame to client area
            static constexpr const MARGINS margins = {0, 0, 1, 0};
            DynamicApis::instance().pDwmExtendFrameIntoClientArea(hWnd, &margins);
            setWindowAttribute("extra-margins", true);
        }
        {
@@ -839,7 +855,7 @@
            msg.wParam = wParam;
            msg.lParam = lParam;
            QT_NATIVE_EVENT_RESULT_TYPE res = 0;
            if (dispatch(QByteArrayLiteral("windows_generic_MSG"), &msg, &res)) {
            if (nativeDispatch(QByteArrayLiteral("windows_generic_MSG"), &msg, &res)) {
                *result = LRESULT(res);
                return true;
            }
@@ -851,9 +867,43 @@
                                                    const QVariant &oldAttribute) {
        const auto hwnd = reinterpret_cast<HWND>(m_windowHandle->winId());
        const DynamicApis &apis = DynamicApis::instance();
        static constexpr const MARGINS extendMargins = {-1, -1, -1, -1};
        static const auto defaultMargins =
            isSystemBorderEnabled() ? MARGINS{0, 0, 0, 0} : MARGINS{1, 1, 1, 1};
        static constexpr const MARGINS defaultEmptyMargins = {0, 0, 0, 0};
        static constexpr const MARGINS defaultExtraMargins = {1, 1, 1, 1};
        static constexpr const MARGINS extendedMargins = {-1, -1, -1, -1};
        if (key == QStringLiteral("extra-margins")) {
            if (isWin11OrGreater())
                return false;
            hasExtraMargins = attribute.toBool();
            DynamicApis::instance().pDwmExtendFrameIntoClientArea(
                hwnd, hasExtraMargins ? &defaultExtraMargins : &defaultEmptyMargins);
            return true;
        }
        if (key == QStringLiteral("dark-mode")) {
            if (!isWin101809OrGreater()) {
                return false;
            }
            BOOL enable = attribute.toBool();
            if (isWin101903OrGreater()) {
                apis.pSetPreferredAppMode(enable ? PAM_AUTO : PAM_DEFAULT);
            } else {
                apis.pAllowDarkModeForApp(enable);
            }
            for (const auto attr : {
                     _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1,
                     _DWMWA_USE_IMMERSIVE_DARK_MODE,
                 }) {
                apis.pDwmSetWindowAttribute(hwnd, attr, &enable, sizeof(enable));
            }
            apis.pFlushMenuThemes();
            return true;
        }
        // For Win11 or later
        static const auto &defaultMargins =
            isSystemBorderEnabled() ? defaultExtraMargins : defaultEmptyMargins;
        if (key == QStringLiteral("mica")) {
            if (!isWin11OrGreater()) {
                return false;
@@ -861,7 +911,7 @@
            if (attribute.toBool()) {
                // We need to extend the window frame into the whole client area to be able
                // to see the blurred window background.
                apis.pDwmExtendFrameIntoClientArea(hwnd, &extendMargins);
                apis.pDwmExtendFrameIntoClientArea(hwnd, &extendedMargins);
                if (isWin1122H2OrGreater()) {
                    // Use official DWM API to enable Mica, available since Windows 11 22H2
                    // (10.0.22621).
@@ -886,14 +936,16 @@
                apis.pDwmExtendFrameIntoClientArea(hwnd, &defaultMargins);
            }
            return true;
        } else if (key == QStringLiteral("mica-alt")) {
        }
        if (key == QStringLiteral("mica-alt")) {
            if (!isWin1122H2OrGreater()) {
                return false;
            }
            if (attribute.toBool()) {
                // We need to extend the window frame into the whole client area to be able
                // to see the blurred window background.
                apis.pDwmExtendFrameIntoClientArea(hwnd, &extendMargins);
                apis.pDwmExtendFrameIntoClientArea(hwnd, &extendedMargins);
                // Use official DWM API to enable Mica Alt, available since Windows 11 22H2
                // (10.0.22621).
                const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_TABBEDWINDOW;
@@ -906,14 +958,16 @@
                apis.pDwmExtendFrameIntoClientArea(hwnd, &defaultMargins);
            }
            return true;
        } else if (key == QStringLiteral("acrylic-material")) {
        }
        if (key == QStringLiteral("acrylic-material")) {
            if (!isWin11OrGreater()) {
                return false;
            }
            if (attribute.toBool()) {
                // We need to extend the window frame into the whole client area to be able
                // to see the blurred window background.
                apis.pDwmExtendFrameIntoClientArea(hwnd, &extendMargins);
                apis.pDwmExtendFrameIntoClientArea(hwnd, &extendedMargins);
                const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_TRANSIENTWINDOW;
                apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType,
@@ -951,13 +1005,15 @@
                apis.pDwmExtendFrameIntoClientArea(hwnd, &defaultMargins);
            }
            return true;
        } else if (key == QStringLiteral("dwm-blur")) {
        }
        if (key == QStringLiteral("dwm-blur")) {
            // TODO: Optimize
            // Currently not available!!!
            if (attribute.toBool()) {
                // We need to extend the window frame into the whole client area to be able
                // to see the blurred window background.
                apis.pDwmExtendFrameIntoClientArea(hwnd, &extendMargins);
                apis.pDwmExtendFrameIntoClientArea(hwnd, &extendedMargins);
                if (isWin8OrGreater()) {
                    ACCENT_POLICY policy{};
                    policy.dwAccentState = ACCENT_ENABLE_BLURBEHIND;
@@ -991,26 +1047,6 @@
                }
                apis.pDwmExtendFrameIntoClientArea(hwnd, &defaultMargins);
            }
            return true;
        } else if (key == QStringLiteral("dark-mode")) {
            if (!isWin101809OrGreater()) {
                return false;
            }
            BOOL enable = attribute.toBool();
            if (isWin101903OrGreater()) {
                apis.pSetPreferredAppMode(enable ? PAM_AUTO : PAM_DEFAULT);
            } else {
                apis.pAllowDarkModeForApp(enable);
            }
            for (const auto attr : {
                     _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1,
                     _DWMWA_USE_IMMERSIVE_DARK_MODE,
                 }) {
                apis.pDwmSetWindowAttribute(hwnd, attr, &enable, sizeof(enable));
            }
            apis.pFlushMenuThemes();
            return true;
        }
        return false;
@@ -2046,4 +2082,13 @@
        return false;
    }
    bool Win32WindowContext::needBorderPainter() const {
        Q_UNUSED(this)
        return isSystemBorderEnabled() && !isWin11OrGreater();
    }
    int Win32WindowContext::borderThickness() const {
        return int(getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId)));
    }
}
src/core/contexts/win32windowcontext_p.h
@@ -35,12 +35,6 @@
        QString key() const override;
        void virtual_hook(int id, void *data) override;
        bool needBorderPainter() const;
        int borderThickness() const;
        void resume(const QByteArray &eventType, void *message,
                    QT_NATIVE_EVENT_RESULT_TYPE *result) override;
    protected:
        void winIdChanged() override;
        bool windowAttributeChanged(const QString &key, const QVariant &attribute,
@@ -65,6 +59,11 @@
        bool nonClientCalcSizeHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
                                      LRESULT *result);
    public:
        bool needBorderPainter() const;
        int borderThickness() const;
    protected:
        WId windowId = 0;
@@ -76,6 +75,8 @@
        bool mouseLeaveBlocked = false;
        bool centered = false;
        bool hasExtraMargins = false;
    };
}
src/core/kernel/nativeeventfilter.cpp
@@ -5,23 +5,23 @@
namespace QWK {
    NativeEventFilter::NativeEventFilter() : m_dispatcher(nullptr) {
    NativeEventFilter::NativeEventFilter() : m_nativeDispatcher(nullptr) {
    }
    NativeEventFilter::~NativeEventFilter() {
        if (m_dispatcher)
            m_dispatcher->removeNativeEventFilter(this);
        if (m_nativeDispatcher)
            m_nativeDispatcher->removeNativeEventFilter(this);
    }
    NativeEventDispatcher::NativeEventDispatcher() = default;
    NativeEventDispatcher::~NativeEventDispatcher() {
        for (const auto &observer : std::as_const(m_nativeEventFilters)) {
            observer->m_dispatcher = nullptr;
            observer->m_nativeDispatcher = nullptr;
        }
    }
    bool NativeEventDispatcher::dispatch(const QByteArray &eventType, void *message,
    bool NativeEventDispatcher::nativeDispatch(const QByteArray &eventType, void *message,
                                         QT_NATIVE_EVENT_RESULT_TYPE *result) {
        for (const auto &ef : std::as_const(m_nativeEventFilters)) {
            if (ef->nativeEventFilter(eventType, message, result))
@@ -30,23 +30,19 @@
        return false;
    }
    void NativeEventDispatcher::resume(const QByteArray &eventType, void *message,
                                       QT_NATIVE_EVENT_RESULT_TYPE *result) {
    }
    void NativeEventDispatcher::installNativeEventFilter(NativeEventFilter *filter) {
        if (!filter || filter->m_dispatcher)
        if (!filter || filter->m_nativeDispatcher)
            return;
        m_nativeEventFilters.append(filter);
        filter->m_dispatcher = this;
        filter->m_nativeDispatcher = this;
    }
    void NativeEventDispatcher::removeNativeEventFilter(NativeEventFilter *filter) {
        if (!m_nativeEventFilters.removeOne(filter)) {
            return;
        }
        filter->m_dispatcher = nullptr;
        filter->m_nativeDispatcher = nullptr;
    }
@@ -64,7 +60,7 @@
        bool nativeEventFilter(const QByteArray &eventType, void *message,
                               QT_NATIVE_EVENT_RESULT_TYPE *result) override {
            return dispatch(eventType, message, result);
            return nativeDispatch(eventType, message, result);
        }
        static AppMasterNativeEventFilter *instance;
src/core/kernel/nativeeventfilter_p.h
@@ -22,10 +22,7 @@
        virtual ~NativeEventDispatcher();
    public:
        virtual bool dispatch(const QByteArray &eventType, void *message,
                              QT_NATIVE_EVENT_RESULT_TYPE *result);
        virtual void resume(const QByteArray &eventType, void *message,
        virtual bool nativeDispatch(const QByteArray &eventType, void *message,
                            QT_NATIVE_EVENT_RESULT_TYPE *result);
    public:
@@ -50,7 +47,7 @@
                                       QT_NATIVE_EVENT_RESULT_TYPE *result) = 0;
    protected:
        NativeEventDispatcher *m_dispatcher;
        NativeEventDispatcher *m_nativeDispatcher;
        friend class NativeEventDispatcher;
src/core/kernel/sharedeventfilter.cpp
New file
@@ -0,0 +1,44 @@
#include "sharedeventfilter_p.h"
namespace QWK {
    SharedEventFilter::SharedEventFilter() : m_sharedDispatcher(nullptr) {
    }
    SharedEventFilter::~SharedEventFilter() {
        if (m_sharedDispatcher)
            m_sharedDispatcher->removeSharedEventFilter(this);
    }
    SharedEventDispatcher::SharedEventDispatcher() = default;
    SharedEventDispatcher::~SharedEventDispatcher() {
        for (const auto &observer : std::as_const(m_sharedEventFilters)) {
            observer->m_sharedDispatcher = nullptr;
        }
    }
    bool SharedEventDispatcher::sharedDispatch(QObject *obj, QEvent *event) {
        for (const auto &ef : std::as_const(m_sharedEventFilters)) {
            if (ef->sharedEventFilter(obj, event))
                return true;
        }
        return false;
    }
    void SharedEventDispatcher::installSharedEventFilter(SharedEventFilter *filter) {
        if (!filter || filter->m_sharedDispatcher)
            return;
        m_sharedEventFilters.append(filter);
        filter->m_sharedDispatcher = this;
    }
    void SharedEventDispatcher::removeSharedEventFilter(SharedEventFilter *filter) {
        if (!m_sharedEventFilters.removeOne(filter)) {
            return;
        }
        filter->m_sharedDispatcher = nullptr;
    }
}
src/core/kernel/sharedeventfilter_p.h
New file
@@ -0,0 +1,57 @@
#ifndef SHAREDEVENTFILTER_P_H
#define SHAREDEVENTFILTER_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 <QWKCore/qwkglobal.h>
namespace QWK {
    class SharedEventFilter;
    class QWK_CORE_EXPORT SharedEventDispatcher {
    public:
        SharedEventDispatcher();
        virtual ~SharedEventDispatcher();
    public:
        virtual bool sharedDispatch(QObject *obj, QEvent *event);
    public:
        void installSharedEventFilter(SharedEventFilter *filter);
        void removeSharedEventFilter(SharedEventFilter *filter);
    protected:
        QVector<SharedEventFilter *> m_sharedEventFilters;
        friend class SharedEventFilter;
        Q_DISABLE_COPY(SharedEventDispatcher)
    };
    class QWK_CORE_EXPORT SharedEventFilter {
    public:
        SharedEventFilter();
        virtual ~SharedEventFilter();
    public:
        virtual bool sharedEventFilter(QObject *obj, QEvent *event) = 0;
    protected:
        SharedEventDispatcher *m_sharedDispatcher;
        friend class SharedEventDispatcher;
        Q_DISABLE_COPY(SharedEventFilter)
    };
}
#endif // SHAREDEVENTFILTER_P_H
src/core/shared/qwkwindowsextra_p.h
@@ -300,6 +300,15 @@
    static inline constexpr RECT qrect2rect(const QRect &qrect) {
        return RECT{LONG(qrect.left()), LONG(qrect.top()), LONG(qrect.right()),
                    LONG(qrect.bottom())};
    }
    static inline constexpr QMargins margins2qmargins(const MARGINS &margins) {
        return {margins.cxLeftWidth, margins.cyTopHeight, margins.cxRightWidth,
                margins.cyBottomHeight};
    }
    static inline constexpr MARGINS qmargins2margins(const QMargins &qmargins) {
        return MARGINS{qmargins.left(), qmargins.right(), qmargins.top(), qmargins.bottom()};
    }
    static inline /*constexpr*/ QString hwnd2str(const WId windowId) {
src/widgets/widgetwindowagent.cpp
@@ -39,7 +39,7 @@
        d->setup(w, new WidgetItemDelegate());
        d->hostWidget = w;
#ifdef Q_OS_WINDOWS
#if defined(Q_OS_WINDOWS) && QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDER)
        d->setupWindows10BorderWorkaround();
#endif
        return true;
src/widgets/widgetwindowagent_p.h
@@ -10,6 +10,7 @@
// version without notice, or may even be removed.
//
#include <QWKCore/qwkconfig.h>
#include <QWKCore/private/windowagentbase_p.h>
#include <QWKWidgets/widgetwindowagent.h>
@@ -25,12 +26,13 @@
        // Host
        QWidget *hostWidget{};
#ifdef Q_OS_MAC
        QWidget *systemButtonAreaWidget{};
        std::unique_ptr<QObject> systemButtonAreaWidgetEventFilter;
#endif
#ifdef Q_OS_WINDOWS
#if defined(Q_OS_WINDOWS) && QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDER)
        void setupWindows10BorderWorkaround();
#endif
    };
src/widgets/widgetwindowagent_win.cpp
@@ -1,9 +1,10 @@
#include "widgetwindowagent_p.h"
#include <QtCore/QDebug>
#include <QtCore/QDateTime>
#include <QtGui/QPainter>
#include <QWKCore/qwindowkit_windows.h>
#include <QWKCore/qwkconfig.h>
#include <QWKCore/private/nativeeventfilter_p.h>
namespace QWK {
@@ -12,19 +13,21 @@
    class WidgetBorderHandler;
    class WidgetBorderHandler : public QObject, public NativeEventFilter {
    class WidgetBorderHandler : public QObject, public NativeEventFilter, public SharedEventFilter {
    public:
        explicit WidgetBorderHandler(QWidget *widget, AbstractWindowContext *ctx)
            : QObject(ctx), widget(widget), ctx(ctx) {
            widget->installEventFilter(this);
            ctx->installNativeEventFilter(this);
            ctx->installSharedEventFilter(this);
            updateGeometry();
        }
        inline bool isNormalWindow() const {
            return widget->windowState() &
                   (Qt::WindowMinimized | Qt::WindowMaximized | Qt::WindowFullScreen);
            return !(widget->windowState() &
                     (Qt::WindowMinimized | Qt::WindowMaximized | Qt::WindowFullScreen));
        }
        void updateGeometry() {
@@ -66,19 +69,48 @@
                    }
                    break;
                }
#  if 0
                case WM_ACTIVATE: {
                    if (LOWORD(msg->wParam) == WA_INACTIVE) {
                        // 窗口失去激活状态
                    } else {
                        // 窗口被激活
                    }
                    break;
                }
#  endif
                case WM_PAINT: {
                default:
                    break;
            }
            return false;
        }
        bool sharedEventFilter(QObject *obj, QEvent *event) override {
            Q_UNUSED(obj)
            auto window = widget->windowHandle();
            if (event->type() == QEvent::Expose) {
                // Qt will absolutely send a QExposeEvent to the QWindow when it receives a
                // WM_PAINT message. When the control flow enters the expose handler, Qt must
                // have already called BeginPaint() and it's the best time for us to draw the
                // top border.
                auto ee = static_cast<QExposeEvent *>(event);
                if (isNormalWindow() && window->isExposed() && !ee->region().isNull()) {
                    // Friend class helping to call `event`
                    class HackedWindow : public QWindow {
                    public:
                        friend class QWK::WidgetBorderHandler;
                    };
                    // Let Qt paint first
                    m_dispatcher->resume(eventType, message, result);
                    static_cast<HackedWindow *>(window)->event(event);
                    // Upon receiving the WM_PAINT message, Qt will redraw the entire view, and we
                    // must wait for it to finish redrawing before drawing this top border area
                    ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook2, nullptr);
                    return true;
                }
                default:
                    break;
            }
            return false;
        }
@@ -87,6 +119,9 @@
            Q_UNUSED(obj)
            switch (event->type()) {
                case QEvent::UpdateRequest: {
                    if (!isNormalWindow())
                        break;
                    // Friend class helping to call `event`
                    class HackedWidget : public QWidget {
                    public:
@@ -123,16 +158,14 @@
        QWidget *widget;
        AbstractWindowContext *ctx;
    };
#endif
    void WidgetWindowAgentPrivate::setupWindows10BorderWorkaround() {
#if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDER)
        // Install painting hook
        auto ctx = context.get();
        if (ctx->property("needBorderPainter").toBool()) {
            std::ignore = new WidgetBorderHandler(hostWidget, ctx);
        }
#endif
    }
#endif
}