From a51b5688e1c33d3ce96b48c869603b00f908f513 Mon Sep 17 00:00:00 2001
From: Sine Striker <trueful@163.com>
Date: 周三, 13 12月 2023 04:16:37 +0800
Subject: [PATCH] Make better code structure for border handlers

---
 src/core/contexts/abstractwindowcontext.cpp |    8 
 src/widgets/widgetwindowagent_win.cpp       |  104 ++++++++
 qmsetup                                     |    2 
 src/core/contexts/abstractwindowcontext_p.h |    8 
 src/quick/CMakeLists.txt                    |    4 
 src/quick/quickwindowagent_win.cpp          |  110 +++++++++
 src/quick/quickwindowagent_p.h              |    4 
 src/quick/qwkquickglobal.cpp                |    4 
 src/quick/quickitemdelegate.cpp             |    2 
 src/widgets/widgetwindowagent_p.h           |    4 
 src/core/contexts/win32windowcontext.cpp    |  138 +++++++----
 src/core/kernel/eventobserver_p.h           |   47 +++
 /dev/null                                   |   46 ---
 src/core/contexts/win32windowcontext_p.h    |   10 
 src/widgets/widgetwindowagent.cpp           |   59 ----
 src/core/kernel/nativeeventfilter.cpp       |    2 
 src/core/kernel/nativeeventfilter_p.h       |    6 
 src/widgets/CMakeLists.txt                  |    4 
 src/core/CMakeLists.txt                     |    7 
 src/core/kernel/eventobserver.cpp           |   44 +++
 src/quick/quickwindowagent.cpp              |   72 -----
 21 files changed, 437 insertions(+), 248 deletions(-)

diff --git a/qmsetup b/qmsetup
index 54a068c..8afc40a 160000
--- a/qmsetup
+++ b/qmsetup
@@ -1 +1 @@
-Subproject commit 54a068c54436d87329e483dedc63b594be1762e0
+Subproject commit 8afc40a5443899b779957fb006098ce8155aacee
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index a0f812d..c3e7cf1 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -12,12 +12,12 @@
     windowagentbase.cpp
     windowitemdelegate_p.h
     windowitemdelegate.cpp
-    kernel/nativeeventfilter.h
+    kernel/eventobserver_p.h
+    kernel/eventobserver.cpp
+    kernel/nativeeventfilter_p.h
     kernel/nativeeventfilter.cpp
     contexts/abstractwindowcontext_p.h
     contexts/abstractwindowcontext.cpp
-    platforms/win10borderhandler_p.h
-    platforms/win10borderhandler.cpp
 )
 
 if(WIN32)
@@ -26,7 +26,6 @@
         qwindowkit_windows.cpp
         contexts/win32windowcontext_p.h
         contexts/win32windowcontext.cpp
-        platforms
     )
 else()
     list(APPEND _src
diff --git a/src/core/contexts/abstractwindowcontext.cpp b/src/core/contexts/abstractwindowcontext.cpp
index afcdbdc..2a68081 100644
--- a/src/core/contexts/abstractwindowcontext.cpp
+++ b/src/core/contexts/abstractwindowcontext.cpp
@@ -98,10 +98,6 @@
         return true;
     }
 
-    void AbstractWindowContext::showSystemMenu(const QPoint &pos) {
-        virtual_hook(ShowSystemMenuHook, &const_cast<QPoint &>(pos));
-    }
-
     QRegion AbstractWindowContext::hitTestShape() const {
         if (hitTestVisibleShapeDirty) {
             hitTestVisibleShape = {};
@@ -204,4 +200,8 @@
         }
     }
 
+    void AbstractWindowContext::showSystemMenu(const QPoint &pos) {
+        virtual_hook(ShowSystemMenuHook, &const_cast<QPoint &>(pos));
+    }
+
 }
\ No newline at end of file
diff --git a/src/core/contexts/abstractwindowcontext_p.h b/src/core/contexts/abstractwindowcontext_p.h
index 275caa9..2934b4d 100644
--- a/src/core/contexts/abstractwindowcontext_p.h
+++ b/src/core/contexts/abstractwindowcontext_p.h
@@ -9,11 +9,12 @@
 #include <QtGui/QPolygon>
 
 #include <QWKCore/windowagentbase.h>
+#include <QWKCore/private/eventobserver_p.h>
 #include <QWKCore/private/windowitemdelegate_p.h>
 
 namespace QWK {
 
-    class QWK_CORE_EXPORT AbstractWindowContext : public QObject {
+    class QWK_CORE_EXPORT AbstractWindowContext : public QObject, public EventDispatcher {
         Q_OBJECT
     public:
         AbstractWindowContext();
@@ -35,8 +36,6 @@
         inline QObject *titleBar() const;
         bool setTitleBar(QObject *obj);
 
-        void showSystemMenu(const QPoint &pos);
-
         QRegion hitTestShape() const;
         bool isInSystemButtons(const QPoint &pos, WindowAgentBase::SystemButton *button) const;
         bool isInTitleBarDraggableArea(const QPoint &pos) const;
@@ -47,9 +46,12 @@
             CentralizeHook = 1,
             ShowSystemMenuHook,
             DefaultColorsHook,
+            DrawWindows10BorderHook, // Only works on Windows 10
         };
         virtual void virtual_hook(int id, void *data);
 
+        void showSystemMenu(const QPoint &pos);
+
     protected:
         virtual bool setupHost() = 0;
 
diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp
index 372d0c5..a706835 100644
--- a/src/core/contexts/win32windowcontext.cpp
+++ b/src/core/contexts/win32windowcontext.cpp
@@ -4,6 +4,7 @@
 
 #include <QtCore/QHash>
 #include <QtCore/QScopeGuard>
+#include <QtCore/QTimer>
 #include <QtGui/QGuiApplication>
 #include <QtGui/QPainter>
 #include <QtGui/QPalette>
@@ -26,10 +27,8 @@
 #include <dwmapi.h>
 #include <timeapi.h>
 
-#include "nativeeventfilter.h"
+#include "nativeeventfilter_p.h"
 #include "qwkglobal_p.h"
-
-#include "win10borderhandler_p.h"
 
 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
 Q_DECLARE_METATYPE(QMargins)
@@ -814,6 +813,45 @@
                 return;
             }
 
+            case DrawWindows10BorderHook: {
+                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>(m_windowHandle->winId());
+
+                QPen pen;
+                pen.setWidth(getWindowFrameBorderThickness(hwnd) * 2);
+
+                const bool dark = isDarkThemeActive() && isDarkWindowFrameEnabled(hwnd);
+                if (m_delegate->isWindowActive(m_host)) {
+                    if (isWindowFrameBorderColorized()) {
+                        pen.setColor(getAccentColor());
+                    } else {
+                        static QColor frameBorderActiveColorLight(kWindowsColorSet.activeLight);
+                        static QColor frameBorderActiveColorDark(kWindowsColorSet.activeDark);
+                        pen.setColor(dark ? frameBorderActiveColorDark
+                                          : frameBorderActiveColorLight);
+                    }
+                } else {
+                    static QColor frameBorderInactiveColorLight(kWindowsColorSet.inactiveLight);
+                    static QColor frameBorderInactiveColorDark(kWindowsColorSet.inactiveDark);
+                    pen.setColor(dark ? frameBorderInactiveColorDark
+                                      : frameBorderInactiveColorLight);
+                }
+                painter.save();
+
+                // ### TODO: do we need to enable or disable it?
+                painter.setRenderHint(QPainter::Antialiasing);
+
+                painter.setPen(pen);
+                painter.drawLine(QLine{
+                    QPoint{0,                       0},
+                    QPoint{m_windowHandle->width(), 0}
+                });
+                painter.restore();
+            }
+
             default: {
                 // unreachable
                 break;
@@ -822,14 +860,12 @@
         AbstractWindowContext::virtual_hook(id, data);
     }
 
-    bool Win32WindowContext::needWin10BorderHandler() const {
+    bool Win32WindowContext::needBorderPainter() const {
         return isWin10OrGreater() && !isWin11OrGreater();
     }
 
-    void Win32WindowContext::setWin10BorderHandler(Win10BorderHandler *handler) {
-        win10BorderHandler.reset(handler);
-        handler->setBorderThickness(
-            int(getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId))));
+    int Win32WindowContext::borderThickness() const {
+        return getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId));
     }
 
     bool Win32WindowContext::setupHost() {
@@ -905,8 +941,8 @@
     }
 
     QWK_USED static constexpr const struct {
-        const WPARAM wParam = 0xF1C9ADD4;
-        const LPARAM lParam = 0xAFB6F4C6;
+        const WPARAM wParam = MAKEWPARAM(44500, 61897);
+        const LPARAM lParam = MAKELPARAM(62662, 44982); // Not used. Reserve for future use.
     } kMessageTag;
 
     static inline quint64 getKeyState() {
@@ -1887,81 +1923,83 @@
         return false;
     }
 
-    bool Win32WindowContext::themeStuffHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
-                           LRESULT *resul)
-    {
+    bool Win32WindowContext::themeStuffHandler(HWND hWnd, UINT message, WPARAM wParam,
+                                               LPARAM lParam, LRESULT *resul) {
         switch (message) {
             case WM_DPICHANGED: {
                 const auto dpiX = UINT(LOWORD(wParam));
                 const auto dpiY = UINT(HIWORD(wParam));
+
+                QEvent e(QEvent::ScreenChangeInternal);
+                dispatch(&e);
                 break;
             }
+
             case WM_THEMECHANGED:
             case WM_SYSCOLORCHANGE: {
+                QEvent e(QEvent::UpdateRequest);
+                dispatch(&e);
                 break;
             }
+
             case WM_DWMCOLORIZATIONCOLORCHANGED: {
                 const QColor color = QColor::fromRgba(wParam);
                 const auto blendWithOpacity = *reinterpret_cast<LPBOOL>(lParam);
+
+                QEvent e(QEvent::UpdateRequest);
+                dispatch(&e);
                 break;
             }
+
             case WM_SETTINGCHANGE: {
-                if (!wParam && lParam && std::wcscmp(reinterpret_cast<LPCWSTR>(lParam), L"ImmersiveColorSet") == 0) {
+                if (!wParam && lParam &&
+                    std::wcscmp(reinterpret_cast<LPCWSTR>(lParam), L"ImmersiveColorSet") == 0) {
                     const QColor color = getAccentColor();
                 }
+
+                QEvent e(QEvent::UpdateRequest);
+                dispatch(&e);
                 break;
             }
+
             case WM_SIZE: {
                 const bool max = wParam == SIZE_MAXIMIZED;
                 const bool min = wParam == SIZE_MINIMIZED;
                 const bool full = isFullScreen(hWnd);
+
+                Qt::WindowStates states{};
+                if (max) {
+                    states |= Qt::WindowMaximized;
+                }
+                if (min) {
+                    states |= Qt::WindowMinimized;
+                }
+                if (full) {
+                    states |= Qt::WindowFullScreen;
+                }
+
+                QTimer::singleShot(0, this, [this, states] {
+                    QWindowStateChangeEvent e(states);
+                    dispatch(&e);
+                });
                 break;
             }
+
             case WM_ACTIVATE: {
                 const auto state = LOWORD(wParam);
                 const bool active = state == WA_ACTIVE || state == WA_CLICKACTIVE;
+                Q_UNUSED(state)
+
+                QTimer::singleShot(0, this, [this, active] {
+                    QEvent e(active ? QEvent::WindowActivate : QEvent::WindowDeactivate);
+                    dispatch(&e);
+                });
                 break;
             }
             default:
                 break;
         }
         return false;
-    }
-
-    void Win10BorderHandler::paintBorder(QPainter &painter, const QRect &rect,
-                                         const QRegion &region) {
-        Q_UNUSED(rect)
-        Q_UNUSED(region)
-
-        QPen pen;
-        pen.setWidth(m_borderThickness * 2);
-
-        const bool dark = isDarkThemeActive() &&
-                          isDarkWindowFrameEnabled(reinterpret_cast<HWND>(m_window->winId()));
-        if (isActive()) {
-            if (isWindowFrameBorderColorized()) {
-                pen.setColor(getAccentColor());
-            } else {
-                static QColor frameBorderActiveColorLight(kWindowsColorSet.activeLight);
-                static QColor frameBorderActiveColorDark(kWindowsColorSet.activeDark);
-                pen.setColor(dark ? frameBorderActiveColorDark : frameBorderActiveColorLight);
-            }
-        } else {
-            static QColor frameBorderInactiveColorLight(kWindowsColorSet.inactiveLight);
-            static QColor frameBorderInactiveColorDark(kWindowsColorSet.inactiveDark);
-            pen.setColor(dark ? frameBorderInactiveColorDark : frameBorderInactiveColorLight);
-        }
-        painter.save();
-
-        // ### TODO: do we need to enable or disable it?
-        painter.setRenderHint(QPainter::Antialiasing);
-
-        painter.setPen(pen);
-        painter.drawLine(QLine{
-            QPoint{0,                 0},
-            QPoint{m_window->width(), 0}
-        });
-        painter.restore();
     }
 
 }
diff --git a/src/core/contexts/win32windowcontext_p.h b/src/core/contexts/win32windowcontext_p.h
index ee6769e..eb03cad 100644
--- a/src/core/contexts/win32windowcontext_p.h
+++ b/src/core/contexts/win32windowcontext_p.h
@@ -6,10 +6,10 @@
 
 namespace QWK {
 
-    class Win10BorderHandler;
-
     class QWK_CORE_EXPORT Win32WindowContext : public AbstractWindowContext {
         Q_OBJECT
+        Q_PROPERTY(bool needBorderPainter READ needBorderPainter FINAL)
+        Q_PROPERTY(int borderThickness READ borderThickness FINAL)
     public:
         Win32WindowContext();
         ~Win32WindowContext() override;
@@ -26,8 +26,8 @@
         QString key() const override;
         void virtual_hook(int id, void *data) override;
 
-        Q_INVOKABLE bool needWin10BorderHandler() const;
-        Q_INVOKABLE void setWin10BorderHandler(Win10BorderHandler *handler);
+        bool needBorderPainter() const;
+        int borderThickness() const;
 
     protected:
         bool setupHost() override;
@@ -65,8 +65,6 @@
         bool mouseLeaveBlocked = false;
 
         bool centered = false;
-
-        std::unique_ptr<Win10BorderHandler> win10BorderHandler;
     };
 
 }
diff --git a/src/core/kernel/eventobserver.cpp b/src/core/kernel/eventobserver.cpp
new file mode 100644
index 0000000..b55686f
--- /dev/null
+++ b/src/core/kernel/eventobserver.cpp
@@ -0,0 +1,44 @@
+#include "eventobserver_p.h"
+
+namespace QWK {
+
+    EventObserver::EventObserver() : m_dispatcher(nullptr) {
+    }
+
+    EventObserver::~EventObserver() {
+        if (m_dispatcher)
+            m_dispatcher->removeObserver(this);
+    }
+
+    EventDispatcher::EventDispatcher() = default;
+
+    EventDispatcher::~EventDispatcher() {
+        for (const auto &observer : qAsConst(m_observers)) {
+            observer->m_dispatcher = nullptr;
+        }
+    }
+
+    bool EventDispatcher::dispatch(QEvent *event) {
+        for (const auto &observer : qAsConst(m_observers)) {
+            if (observer->observe(event))
+                return true;
+        }
+        return true;
+    }
+
+    void EventDispatcher::addObserver(EventObserver *observer) {
+        if (!observer || observer->m_dispatcher)
+            return;
+
+        m_observers.append(observer);
+        observer->m_dispatcher = this;
+    }
+
+    void EventDispatcher::removeObserver(EventObserver *observer) {
+        if (!m_observers.removeOne(observer)) {
+            return;
+        }
+        observer->m_dispatcher = nullptr;
+    }
+
+}
\ No newline at end of file
diff --git a/src/core/kernel/eventobserver_p.h b/src/core/kernel/eventobserver_p.h
new file mode 100644
index 0000000..558b549
--- /dev/null
+++ b/src/core/kernel/eventobserver_p.h
@@ -0,0 +1,47 @@
+#ifndef EVENTFILTER_P_H
+#define EVENTFILTER_P_H
+
+#include <QtGui/QtEvents>
+
+#include <QWKCore/qwkglobal.h>
+
+namespace QWK {
+
+    class EventDispatcher;
+
+    class QWK_CORE_EXPORT EventObserver {
+    public:
+        EventObserver();
+        virtual ~EventObserver();
+
+    protected:
+        virtual bool observe(QEvent *event) = 0;
+
+    protected:
+        EventDispatcher *m_dispatcher;
+
+        Q_DISABLE_COPY(EventObserver)
+
+        friend class EventDispatcher;
+    };
+
+    class QWK_CORE_EXPORT EventDispatcher {
+    public:
+        EventDispatcher();
+        virtual ~EventDispatcher();
+
+        virtual bool dispatch(QEvent *event);
+
+    public:
+        void addObserver(EventObserver *observer);
+        void removeObserver(EventObserver *observer);
+
+    protected:
+        QVector<EventObserver *> m_observers;
+
+        Q_DISABLE_COPY(EventDispatcher)
+    };
+
+}
+
+#endif // EVENTFILTER_P_H
diff --git a/src/core/kernel/nativeeventfilter.cpp b/src/core/kernel/nativeeventfilter.cpp
index 446388f..5aee4ef 100644
--- a/src/core/kernel/nativeeventfilter.cpp
+++ b/src/core/kernel/nativeeventfilter.cpp
@@ -1,4 +1,4 @@
-#include "nativeeventfilter.h"
+#include "nativeeventfilter_p.h"
 
 #include <QtCore/QAbstractNativeEventFilter>
 #include <QtCore/QCoreApplication>
diff --git a/src/core/kernel/nativeeventfilter.h b/src/core/kernel/nativeeventfilter_p.h
similarity index 81%
rename from src/core/kernel/nativeeventfilter.h
rename to src/core/kernel/nativeeventfilter_p.h
index 103c025..b7ce167 100644
--- a/src/core/kernel/nativeeventfilter.h
+++ b/src/core/kernel/nativeeventfilter_p.h
@@ -1,5 +1,5 @@
-#ifndef NATIVEEVENTFILTER_H
-#define NATIVEEVENTFILTER_H
+#ifndef NATIVEEVENTFILTER_P_H
+#define NATIVEEVENTFILTER_P_H
 
 #include <QWKCore/qwkglobal.h>
 
@@ -20,4 +20,4 @@
 
 }
 
-#endif // NATIVEEVENTFILTER_H
+#endif // NATIVEEVENTFILTER_P_H
diff --git a/src/core/platforms/win10borderhandler.cpp b/src/core/platforms/win10borderhandler.cpp
deleted file mode 100644
index 7acdc3d..0000000
--- a/src/core/platforms/win10borderhandler.cpp
+++ /dev/null
@@ -1,10 +0,0 @@
-#include "win10borderhandler_p.h"
-
-namespace QWK {
-
-    Win10BorderHandler::Win10BorderHandler(QWindow *window) : m_window(window), m_borderThickness(0) {
-    }
-
-    Win10BorderHandler::~Win10BorderHandler() = default;
-
-}
\ No newline at end of file
diff --git a/src/core/platforms/win10borderhandler_p.h b/src/core/platforms/win10borderhandler_p.h
deleted file mode 100644
index 6491d15..0000000
--- a/src/core/platforms/win10borderhandler_p.h
+++ /dev/null
@@ -1,46 +0,0 @@
-#ifndef WIN10BORDERHANDLER_P_H
-#define WIN10BORDERHANDLER_P_H
-
-#include <QtGui/QPainter>
-
-#include <QWKCore/qwkcoreglobal.h>
-
-namespace QWK {
-
-    class QWK_CORE_EXPORT Win10BorderHandler {
-    public:
-        Win10BorderHandler(QWindow *window);
-        virtual ~Win10BorderHandler();
-
-    public:
-        virtual void updateGeometry() = 0;
-        virtual void requestUpdate() = 0;
-
-        virtual bool isActive() const = 0;
-
-        inline int borderThickness() const;
-        inline void setBorderThickness(int borderThickness);
-
-    protected:
-        // implemented in `win32windowcontext.cpp`
-        void paintBorder(QPainter &painter, const QRect &rect, const QRegion &region);
-
-    protected:
-        QWindow *m_window;
-        int m_borderThickness;
-
-        Q_DISABLE_COPY(Win10BorderHandler)
-    };
-
-    inline int Win10BorderHandler::borderThickness() const {
-        return m_borderThickness;
-    }
-
-    inline void Win10BorderHandler::setBorderThickness(int borderThickness) {
-        m_borderThickness = borderThickness;
-        updateGeometry();
-    }
-
-}
-
-#endif // WIN10BORDERHANDLER_P_H
diff --git a/src/quick/CMakeLists.txt b/src/quick/CMakeLists.txt
index 1d61653..9389c8b 100644
--- a/src/quick/CMakeLists.txt
+++ b/src/quick/CMakeLists.txt
@@ -13,6 +13,10 @@
     quickwindowagent.cpp
 )
 
+if(WIN32)
+    list(APPEND _src quickwindowagent_win.cpp)
+endif()
+
 qwk_add_library(${PROJECT_NAME} AUTOGEN
     SOURCES ${_src}
     LINKS QWKCore
diff --git a/src/quick/quickitemdelegate.cpp b/src/quick/quickitemdelegate.cpp
index 8165ff8..4c9b565 100644
--- a/src/quick/quickitemdelegate.cpp
+++ b/src/quick/quickitemdelegate.cpp
@@ -39,7 +39,7 @@
     }
 
     bool QuickItemDelegate::isWindowActive(const QObject *host) const {
-        return static_cast<QQuickWindow *>(const_cast<QObject *>(host))->isActive();
+        return static_cast<const QQuickWindow *>(host)->isActive();
     }
 
 }
\ No newline at end of file
diff --git a/src/quick/quickwindowagent.cpp b/src/quick/quickwindowagent.cpp
index 747daf0..9422f65 100644
--- a/src/quick/quickwindowagent.cpp
+++ b/src/quick/quickwindowagent.cpp
@@ -2,69 +2,11 @@
 #include "quickwindowagent_p.h"
 
 #include <QtQuick/QQuickWindow>
-#include <QtQuick/QQuickPaintedItem>
-#include <QtQuick/private/qquickitem_p.h>
 #include <QtQuick/private/qquickanchors_p.h>
-
-#ifdef Q_OS_WINDOWS
-#  include <QWKCore/private/win10borderhandler_p.h>
-#endif
 
 #include "quickitemdelegate_p.h"
 
 namespace QWK {
-
-    class BorderItem : public QQuickPaintedItem, public Win10BorderHandler {
-        Q_OBJECT
-    public:
-        explicit BorderItem(QQuickItem *parent = nullptr);
-        ~BorderItem() override;
-
-        void updateGeometry() override;
-        void requestUpdate() override;
-
-        bool isActive() const override;
-
-    public:
-        void paint(QPainter *painter) override;
-    };
-
-    BorderItem::BorderItem(QQuickItem *parent)
-        : Win10BorderHandler(parent->window()), QQuickPaintedItem(parent) {
-        setAntialiasing(true);   // ### FIXME: do we need to enable or disable this?
-        setMipmap(true);         // ### FIXME: do we need to enable or disable this?
-        setFillColor({});        // Will improve the performance a little bit.
-        setOpaquePainting(true); // Will also improve the performance, we don't draw
-                                 // semi-transparent borders of course.
-
-        auto parentPri = QQuickItemPrivate::get(parent);
-        auto anchors = QQuickItemPrivate::get(this)->anchors();
-        anchors->setTop(parentPri->top());
-        anchors->setLeft(parentPri->left());
-        anchors->setRight(parentPri->right());
-
-        setZ(10);
-    }
-
-    BorderItem::~BorderItem() = default;
-
-    void BorderItem::updateGeometry() {
-        setHeight(m_borderThickness);
-    }
-
-    void BorderItem::requestUpdate() {
-        update();
-    }
-
-    bool BorderItem::isActive() const {
-        return static_cast<QQuickWindow *>(m_window)->isActive();
-    }
-
-    void BorderItem::paint(QPainter *painter) {
-        QRect rect(QPoint(0, 0), size().toSize());
-        QRegion region(rect);
-        Win10BorderHandler::paintBorder(*painter, rect, region);
-    }
 
     QuickWindowAgentPrivate::QuickWindowAgentPrivate() {
     }
@@ -99,15 +41,7 @@
         d->hostWindow = window;
 
 #ifdef Q_OS_WINDOWS
-        // Install painting hook
-        if (bool needPaintBorder;
-            QMetaObject::invokeMethod(d->context.get(), "needWin10BorderHandler",
-                                      Qt::DirectConnection, Q_RETURN_ARG(bool, needPaintBorder)),
-            needPaintBorder) {
-            QMetaObject::invokeMethod(
-                d->context.get(), "setWin10BorderHandler", Qt::DirectConnection,
-                Q_ARG(Win10BorderHandler *, new BorderItem(window->contentItem())));
-        }
+        d->setupWindows10BorderWorkaround();
 #endif
         return true;
     }
@@ -158,6 +92,4 @@
         d.init();
     }
 
-}
-
-#include "quickwindowagent.moc"
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/quick/quickwindowagent_p.h b/src/quick/quickwindowagent_p.h
index 243f534..1aecc10 100644
--- a/src/quick/quickwindowagent_p.h
+++ b/src/quick/quickwindowagent_p.h
@@ -16,6 +16,10 @@
 
         // Host
         QQuickWindow *hostWindow{};
+
+#ifdef Q_OS_WINDOWS
+        void setupWindows10BorderWorkaround();
+#endif
     };
 
 }
diff --git a/src/quick/quickwindowagent_win.cpp b/src/quick/quickwindowagent_win.cpp
new file mode 100644
index 0000000..8e57584
--- /dev/null
+++ b/src/quick/quickwindowagent_win.cpp
@@ -0,0 +1,110 @@
+#include "quickwindowagent_p.h"
+
+#include <QtQuick/QQuickPaintedItem>
+#include <QtQuick/private/qquickitem_p.h>
+
+#include <QWKCore/private/eventobserver_p.h>
+
+namespace QWK {
+
+    class BorderItem : public QQuickPaintedItem, public EventObserver {
+        Q_OBJECT
+    public:
+        explicit BorderItem(QQuickItem *parent, AbstractWindowContext *context);
+        ~BorderItem() override;
+
+        void updateGeometry();
+
+    public:
+        void paint(QPainter *painter) override;
+        void itemChange(ItemChange change, const ItemChangeData &data) override;
+
+    protected:
+        bool observe(QEvent *event) override;
+
+        AbstractWindowContext *context;
+
+    private:
+        void _q_windowActivityChanged();
+    };
+
+    BorderItem::BorderItem(QQuickItem *parent, AbstractWindowContext *context)
+        : QQuickPaintedItem(parent), context(context) {
+        setAntialiasing(true);   // ### FIXME: do we need to enable or disable this?
+        setMipmap(true);         // ### FIXME: do we need to enable or disable this?
+        setFillColor({});        // Will improve the performance a little bit.
+        setOpaquePainting(true); // Will also improve the performance, we don't draw
+                                 // semi-transparent borders of course.
+
+        auto parentPri = QQuickItemPrivate::get(parent);
+        auto anchors = QQuickItemPrivate::get(this)->anchors();
+        anchors->setTop(parentPri->top());
+        anchors->setLeft(parentPri->left());
+        anchors->setRight(parentPri->right());
+
+        setZ(10);
+
+        context->addObserver(this);
+        connect(window(), &QQuickWindow::activeChanged, this,
+                &BorderItem::_q_windowActivityChanged);
+        updateGeometry();
+    }
+
+    BorderItem::~BorderItem() = default;
+
+    void BorderItem::updateGeometry() {
+        setHeight(context->property("borderThickness").toInt());
+    }
+
+    void BorderItem::paint(QPainter *painter) {
+        QRect rect(QPoint(0, 0), size().toSize());
+        QRegion region(rect);
+        void *args[] = {
+            painter,
+            &rect,
+            &region,
+        };
+        context->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook, args);
+    }
+
+    void BorderItem::itemChange(ItemChange change, const ItemChangeData &data) {
+        QQuickPaintedItem::itemChange(change, data);
+        switch (change) {
+            case ItemVisibleHasChanged:
+            case ItemDevicePixelRatioHasChanged: {
+                updateGeometry();
+                break;
+            }
+            default:
+                break;
+        }
+    }
+
+    bool BorderItem::observe(QEvent *event) {
+        switch (event->type()) {
+            case QEvent::UpdateRequest: {
+                update();
+                break;
+            }
+
+            default:
+                break;
+        }
+        return false;
+    }
+
+    void BorderItem::_q_windowActivityChanged() {
+        update();
+    }
+
+    void QuickWindowAgentPrivate::setupWindows10BorderWorkaround() {
+        // Install painting hook
+        auto ctx = context.get();
+        if (ctx->property("needBorderPainter").toBool()) {
+            std::ignore = new BorderItem(hostWindow->contentItem(), ctx);
+        }
+    }
+
+}
+
+#include "quickwindowagent_win.moc"
\ No newline at end of file
diff --git a/src/quick/qwkquickglobal.cpp b/src/quick/qwkquickglobal.cpp
index 834dd6d..1748ca8 100644
--- a/src/quick/qwkquickglobal.cpp
+++ b/src/quick/qwkquickglobal.cpp
@@ -1,7 +1,9 @@
 #include "qwkquickglobal.h"
-#include "quickwindowagent.h"
+
 #include <QtQml/QQmlEngine>
 
+#include "quickwindowagent.h"
+
 namespace QWK {
 
     static constexpr const char kModuleUri[] = "QWindowKit";
diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt
index 4153460..ba6dd89 100644
--- a/src/widgets/CMakeLists.txt
+++ b/src/widgets/CMakeLists.txt
@@ -12,6 +12,10 @@
     widgetwindowagent.cpp
 )
 
+if(WIN32)
+    list(APPEND _src widgetwindowagent_win.cpp)
+endif()
+
 qwk_add_library(${PROJECT_NAME} AUTOGEN
     SOURCES ${_src}
     LINKS QWKCore
diff --git a/src/widgets/widgetwindowagent.cpp b/src/widgets/widgetwindowagent.cpp
index e869010..ff76aef 100644
--- a/src/widgets/widgetwindowagent.cpp
+++ b/src/widgets/widgetwindowagent.cpp
@@ -5,58 +5,9 @@
 #include <QtGui/QPainter>
 #include <QtCore/QDebug>
 
-#ifdef Q_OS_WINDOWS
-#  include <QWKCore/private/win10borderhandler_p.h>
-#endif
-
 #include "widgetitemdelegate_p.h"
 
 namespace QWK {
-
-#ifdef Q_OS_WINDOWS
-    class WidgetBorderHandler : public QObject, public Win10BorderHandler {
-    public:
-        explicit WidgetBorderHandler(QWidget *widget)
-            : Win10BorderHandler(widget->windowHandle()), widget(widget) {
-            widget->installEventFilter(this);
-        }
-
-        void updateGeometry() override {
-            if (widget->windowState() & (Qt::WindowMaximized | Qt::WindowFullScreen)) {
-                widget->setContentsMargins({});
-            } else {
-                widget->setContentsMargins({0, int(m_borderThickness), 0, 0});
-            }
-        }
-
-        void requestUpdate() override {
-            widget->update();
-        }
-
-        bool isActive() const override {
-            return widget->isActiveWindow();
-        }
-
-    protected:
-        bool eventFilter(QObject *obj, QEvent *event) override {
-            switch (event->type()) {
-                case QEvent::Paint: {
-                    if (widget->windowState() & (Qt::WindowMaximized | Qt::WindowFullScreen))
-                        break;
-                    auto paintEvent = static_cast<QPaintEvent *>(event);
-                    QPainter painter(widget);
-                    paintBorder(painter, paintEvent->rect(), paintEvent->region());
-                    return true;
-                }
-                default:
-                    break;
-            }
-            return false;
-        }
-
-        QWidget *widget;
-    };
-#endif
 
     WidgetWindowAgentPrivate::WidgetWindowAgentPrivate() {
     }
@@ -94,15 +45,7 @@
         d->hostWidget = w;
 
 #ifdef Q_OS_WINDOWS
-        // Install painting hook
-        if (bool needPaintBorder;
-            QMetaObject::invokeMethod(d->context.get(), "needWin10BorderHandler",
-                                      Qt::DirectConnection, Q_RETURN_ARG(bool, needPaintBorder)),
-            needPaintBorder) {
-            QMetaObject::invokeMethod(d->context.get(), "setWin10BorderHandler",
-                                      Qt::DirectConnection,
-                                      Q_ARG(Win10BorderHandler *, new WidgetBorderHandler(w)));
-        }
+        d->setupWindows10BorderWorkaround();
 #endif
         return true;
     }
diff --git a/src/widgets/widgetwindowagent_p.h b/src/widgets/widgetwindowagent_p.h
index d433064..5b60770 100644
--- a/src/widgets/widgetwindowagent_p.h
+++ b/src/widgets/widgetwindowagent_p.h
@@ -16,6 +16,10 @@
 
         // Host
         QWidget *hostWidget{};
+
+#ifdef Q_OS_WINDOWS
+        void setupWindows10BorderWorkaround();
+#endif
     };
 
 }
diff --git a/src/widgets/widgetwindowagent_win.cpp b/src/widgets/widgetwindowagent_win.cpp
new file mode 100644
index 0000000..58d52dd
--- /dev/null
+++ b/src/widgets/widgetwindowagent_win.cpp
@@ -0,0 +1,104 @@
+#include "widgetwindowagent_p.h"
+
+#include <QtGui/QPainter>
+
+#include <QWKCore/private/eventobserver_p.h>
+
+namespace QWK {
+
+    class WidgetBorderHandler : public QObject, public EventObserver {
+        Q_OBJECT
+    public:
+        explicit WidgetBorderHandler(QWidget *widget, AbstractWindowContext *ctx,
+                                     QObject *parent = nullptr)
+            : QObject(parent), widget(widget), ctx(ctx) {
+            widget->installEventFilter(this);
+
+            ctx->addObserver(this);
+            updateGeometry();
+        }
+
+        void updateGeometry() {
+            if (widget->windowState() & (Qt::WindowMaximized | Qt::WindowFullScreen)) {
+                widget->setContentsMargins({});
+            } else {
+                widget->setContentsMargins({
+                    0,
+                    ctx->property("borderThickness").toInt(),
+                    0,
+                    0,
+                });
+            }
+        }
+
+    protected:
+        bool observe(QEvent *event) override {
+            switch (event->type()) {
+                case QEvent::UpdateRequest: {
+                    widget->update();
+                    break;
+                }
+
+                case QEvent::ScreenChangeInternal: {
+                    updateGeometry();
+                    break;
+                }
+
+                default:
+                    break;
+            }
+            return false;
+        }
+
+        bool eventFilter(QObject *obj, QEvent *event) override {
+            switch (event->type()) {
+                case QEvent::Paint: {
+                    if (widget->windowState() & (Qt::WindowMaximized | Qt::WindowFullScreen))
+                        break;
+
+                    auto paintEvent = static_cast<QPaintEvent *>(event);
+                    auto rect = paintEvent->rect();
+                    auto region = paintEvent->region();
+
+                    QPainter painter(widget);
+                    void *args[] = {
+                        &painter,
+                        &rect,
+                        &region,
+                    };
+                    ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook, args);
+                    return true;
+                }
+
+                case QEvent::WindowStateChange: {
+                    updateGeometry();
+                    break;
+                }
+
+                case QEvent::WindowActivate:
+                case QEvent::WindowDeactivate: {
+                    widget->update();
+                    break;
+                }
+
+                default:
+                    break;
+            }
+            return false;
+        }
+
+        QWidget *widget;
+        AbstractWindowContext *ctx;
+    };
+
+    void WidgetWindowAgentPrivate::setupWindows10BorderWorkaround() {
+        // Install painting hook
+        auto ctx = context.get();
+        if (ctx->property("needBorderPainter").toBool()) {
+            std::ignore = new WidgetBorderHandler(hostWidget, ctx, ctx);
+        }
+    }
+
+}
+
+#include "widgetwindowagent_win.moc"
\ No newline at end of file

--
Gitblit v1.9.1