From 29901fc2a97eedd3c914f807d1819c9ea7e69973 Mon Sep 17 00:00:00 2001
From: Sine Striker <trueful@163.com>
Date: 周一, 25 12月 2023 17:53:34 +0800
Subject: [PATCH] Optimize Windows 10 border handling

---
 src/core/contexts/abstractwindowcontext.cpp |   61 +++++--
 src/widgets/widgetwindowagent_win.cpp       |   57 +++++-
 src/core/contexts/abstractwindowcontext_p.h |    6 
 src/core/shared/qwkwindowsextra_p.h         |   17 +
 src/widgets/widgetwindowagent_p.h           |    4 
 src/core/contexts/win32windowcontext.cpp    |  155 ++++++++++++------
 src/core/contexts/win32windowcontext_p.h    |   13 
 src/core/kernel/sharedeventfilter_p.h       |   57 +++++++
 src/widgets/widgetwindowagent.cpp           |    2 
 src/core/kernel/nativeeventfilter.cpp       |   22 +-
 src/core/kernel/nativeeventfilter_p.h       |    9 
 src/core/kernel/sharedeventfilter.cpp       |   44 +++++
 src/core/CMakeLists.txt                     |    3 
 examples/mainwindow/mainwindow.cpp          |   12 +
 14 files changed, 344 insertions(+), 118 deletions(-)

diff --git a/examples/mainwindow/mainwindow.cpp b/examples/mainwindow/mainwindow.cpp
index a1a39dd..6a7435e 100644
--- a/examples/mainwindow/mainwindow.cpp
+++ b/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
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 6e20cf1..e801dd2 100644
--- a/src/core/CMakeLists.txt
+++ b/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
diff --git a/src/core/contexts/abstractwindowcontext.cpp b/src/core/contexts/abstractwindowcontext.cpp
index de9674c..8672faf 100644
--- a/src/core/contexts/abstractwindowcontext.cpp
+++ b/src/core/contexts/abstractwindowcontext.cpp
@@ -9,26 +9,48 @@
 
 namespace QWK {
 
-    class WinIdChangeEventFilter : public QObject {
-    public:
-        explicit WinIdChangeEventFilter(QObject *widget, AbstractWindowContext *ctx,
-                                        QObject *parent = nullptr)
-            : QObject(parent), ctx(ctx) {
-            widget->installEventFilter(this);
-        }
+    namespace {
 
-    protected:
-        bool eventFilter(QObject *obj, QEvent *event) override {
-            Q_UNUSED(obj)
-            if (event->type() == QEvent::WinIdChange) {
-                ctx->notifyWinIdChange();
+        class WinIdChangeEventFilter : public QObject {
+        public:
+            explicit WinIdChangeEventFilter(QObject *widget, AbstractWindowContext *ctx,
+                                            QObject *parent = nullptr)
+                : QObject(parent), ctx(ctx) {
+                widget->installEventFilter(this);
             }
-            return false;
-        }
 
-    protected:
-        AbstractWindowContext *ctx;
-    };
+        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:
+            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;
 
@@ -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();
diff --git a/src/core/contexts/abstractwindowcontext_p.h b/src/core/contexts/abstractwindowcontext_p.h
index f8ea17e..ec4937a 100644
--- a/src/core/contexts/abstractwindowcontext_p.h
+++ b/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;
     };
 
diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp
index 9b92b0d..6dec43f 100644
--- a/src/core/contexts/win32windowcontext.cpp
+++ b/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)));
+    }
+
 }
diff --git a/src/core/contexts/win32windowcontext_p.h b/src/core/contexts/win32windowcontext_p.h
index 3af0e13..baa0254 100644
--- a/src/core/contexts/win32windowcontext_p.h
+++ b/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;
     };
 
 }
diff --git a/src/core/kernel/nativeeventfilter.cpp b/src/core/kernel/nativeeventfilter.cpp
index 5ad57d1..babc55c 100644
--- a/src/core/kernel/nativeeventfilter.cpp
+++ b/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;
diff --git a/src/core/kernel/nativeeventfilter_p.h b/src/core/kernel/nativeeventfilter_p.h
index a2e852e..569ea4a 100644
--- a/src/core/kernel/nativeeventfilter_p.h
+++ b/src/core/kernel/nativeeventfilter_p.h
@@ -22,11 +22,8 @@
         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,
-                            QT_NATIVE_EVENT_RESULT_TYPE *result);
+        virtual bool nativeDispatch(const QByteArray &eventType, void *message,
+                                    QT_NATIVE_EVENT_RESULT_TYPE *result);
 
     public:
         void installNativeEventFilter(NativeEventFilter *filter);
@@ -50,7 +47,7 @@
                                        QT_NATIVE_EVENT_RESULT_TYPE *result) = 0;
 
     protected:
-        NativeEventDispatcher *m_dispatcher;
+        NativeEventDispatcher *m_nativeDispatcher;
 
         friend class NativeEventDispatcher;
 
diff --git a/src/core/kernel/sharedeventfilter.cpp b/src/core/kernel/sharedeventfilter.cpp
new file mode 100644
index 0000000..8b4dc46
--- /dev/null
+++ b/src/core/kernel/sharedeventfilter.cpp
@@ -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;
+    }
+
+}
\ No newline at end of file
diff --git a/src/core/kernel/sharedeventfilter_p.h b/src/core/kernel/sharedeventfilter_p.h
new file mode 100644
index 0000000..4865756
--- /dev/null
+++ b/src/core/kernel/sharedeventfilter_p.h
@@ -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
diff --git a/src/core/shared/qwkwindowsextra_p.h b/src/core/shared/qwkwindowsextra_p.h
index ede119c..15fe4e7 100644
--- a/src/core/shared/qwkwindowsextra_p.h
+++ b/src/core/shared/qwkwindowsextra_p.h
@@ -145,9 +145,9 @@
 
     // Win10 1809 (10.0.17763)
     using RefreshImmersiveColorPolicyStatePtr = VOID(WINAPI *)(VOID); // Ordinal 104
-    using AllowDarkModeForWindowPtr = BOOL(WINAPI *)(HWND, BOOL); // Ordinal 133
-    using AllowDarkModeForAppPtr = BOOL(WINAPI *)(BOOL); // Ordinal 135
-    using FlushMenuThemesPtr = VOID(WINAPI *)(VOID); // Ordinal 136
+    using AllowDarkModeForWindowPtr = BOOL(WINAPI *)(HWND, BOOL);     // Ordinal 133
+    using AllowDarkModeForAppPtr = BOOL(WINAPI *)(BOOL);              // Ordinal 135
+    using FlushMenuThemesPtr = VOID(WINAPI *)(VOID);                  // Ordinal 136
     // Win10 1903 (10.0.18362)
     using SetPreferredAppModePtr = PREFERRED_APP_MODE(WINAPI *)(PREFERRED_APP_MODE); // Ordinal 135
 
@@ -213,7 +213,7 @@
 
 #undef DYNAMIC_API_RESOLVE
 
-#define UNDOC_API_RESOLVE(DLL, NAME, ORDINAL)                                                             \
+#define UNDOC_API_RESOLVE(DLL, NAME, ORDINAL)                                                      \
     p##NAME = reinterpret_cast<decltype(p##NAME)>(DLL.resolve(MAKEINTRESOURCEA(ORDINAL)))
 
                 QSystemLibrary uxtheme(QStringLiteral("uxtheme"));
@@ -302,6 +302,15 @@
                     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) {
         // NULL handle is allowed here.
         return QLatin1String("0x") +
diff --git a/src/widgets/widgetwindowagent.cpp b/src/widgets/widgetwindowagent.cpp
index b510efb..5422694 100644
--- a/src/widgets/widgetwindowagent.cpp
+++ b/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;
diff --git a/src/widgets/widgetwindowagent_p.h b/src/widgets/widgetwindowagent_p.h
index ac4d653..c5b01be 100644
--- a/src/widgets/widgetwindowagent_p.h
+++ b/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
     };
diff --git a/src/widgets/widgetwindowagent_win.cpp b/src/widgets/widgetwindowagent_win.cpp
index dc35a17..9e49d38 100644
--- a/src/widgets/widgetwindowagent_win.cpp
+++ b/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
 
 }

--
Gitblit v1.9.1