From 0586c98f90866e4bc9f0dfe73aefb0a07c56697e Mon Sep 17 00:00:00 2001
From: Sine Striker <trueful@163.com>
Date: 周二, 12 12月 2023 15:44:14 +0800
Subject: [PATCH] Add win10 border handler

---
 src/core/contexts/abstractwindowcontext.cpp |   32 +--
 src/core/contexts/win32windowcontext_p.h    |    7 +
 src/widgets/widgetwindowagent.cpp           |   74 ++++------
 src/core/contexts/abstractwindowcontext_p.h |    5 
 src/core/CMakeLists.txt                     |    5 
 src/core/platforms/win10borderhandler.cpp   |   10 +
 src/quick/quickwindowagent_p.h              |    4 
 src/widgets/widgetwindowagent_p.h           |    4 
 src/core/contexts/win32windowcontext.cpp    |  123 +++++++---------
 src/core/platforms/win10borderhandler_p.h   |   46 ++++++
 src/quick/quickwindowagent.cpp              |   86 ++++-------
 11 files changed, 198 insertions(+), 198 deletions(-)

diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 837baa0..a0f812d 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -16,6 +16,8 @@
     kernel/nativeeventfilter.cpp
     contexts/abstractwindowcontext_p.h
     contexts/abstractwindowcontext.cpp
+    platforms/win10borderhandler_p.h
+    platforms/win10borderhandler.cpp
 )
 
 if(WIN32)
@@ -24,6 +26,7 @@
         qwindowkit_windows.cpp
         contexts/win32windowcontext_p.h
         contexts/win32windowcontext.cpp
+        platforms
     )
 else()
     list(APPEND _src
@@ -45,7 +48,7 @@
     LINKS
     QT_LINKS Core Gui
     QT_INCLUDE_PRIVATE Core Gui
-    INCLUDE_PRIVATE kernel contexts
+    INCLUDE_PRIVATE kernel contexts platforms
     PREFIX QWK_CORE
 )
 
diff --git a/src/core/contexts/abstractwindowcontext.cpp b/src/core/contexts/abstractwindowcontext.cpp
index d3e8c0f..3c902e9 100644
--- a/src/core/contexts/abstractwindowcontext.cpp
+++ b/src/core/contexts/abstractwindowcontext.cpp
@@ -180,31 +180,25 @@
 
     void AbstractWindowContext::virtual_hook(int id, void *data) {
         switch (id) {
-            case NeedsDrawBordersHook: {
-                auto &result = *static_cast<bool *>(data);
-                result = false;
-                return;
+            case CentralizeHook: {
+                // TODO: Qt
+                break;
             }
 
-            case BorderThicknessHook: {
-                auto args = static_cast<void **>(data);
-                const bool requireNative = *static_cast<const bool *>(args[0]);
-                quint32 &thickness = *static_cast<quint32 *>(args[1]);
-                std::ignore = requireNative;
-                thickness = 1;
-                return;
+            case ShowSystemMenuHook: {
+                // TODO: Qt
+                break;
             }
 
-            case BorderColorsHook: {
-                auto arr = *reinterpret_cast<QList<QColor> *>(data);
-                arr.clear();
-                arr.push_back(kSampleColorSet.activeLight);
-                arr.push_back(kSampleColorSet.activeDark);
-                arr.push_back(kSampleColorSet.inactiveLight);
-                arr.push_back(kSampleColorSet.inactiveDark);
+            case DefaultColorsHook: {
+                auto map = *reinterpret_cast<QMap<QString, QColor> *>(data);
+                map.clear();
+                map.insert("activeLight", kSampleColorSet.activeLight);
+                map.insert("activeDark", kSampleColorSet.activeDark);
+                map.insert("inactiveLight", kSampleColorSet.inactiveLight);
+                map.insert("inactiveDark", kSampleColorSet.inactiveDark);
                 return;
             }
-
             default:
                 break;
         }
diff --git a/src/core/contexts/abstractwindowcontext_p.h b/src/core/contexts/abstractwindowcontext_p.h
index 967d002..275caa9 100644
--- a/src/core/contexts/abstractwindowcontext_p.h
+++ b/src/core/contexts/abstractwindowcontext_p.h
@@ -46,10 +46,7 @@
         enum WindowContextHook {
             CentralizeHook = 1,
             ShowSystemMenuHook,
-            NeedsDrawBordersHook,
-            BorderThicknessHook,
-            BorderColorsHook,
-            DrawBordersHook,
+            DefaultColorsHook,
         };
         virtual void virtual_hook(int id, void *data);
 
diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp
index d6e361a..8ed1f39 100644
--- a/src/core/contexts/win32windowcontext.cpp
+++ b/src/core/contexts/win32windowcontext.cpp
@@ -29,6 +29,8 @@
 #include "nativeeventfilter.h"
 #include "qwkglobal_p.h"
 
+#include "win10borderhandler_p.h"
+
 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
 Q_DECLARE_METATYPE(QMargins)
 #endif
@@ -802,74 +804,13 @@
                 return;
             }
 
-            case NeedsDrawBordersHook: {
-                auto &result = *static_cast<bool *>(data);
-                result = isWin10OrGreater() && !isWin11OrGreater();
-                return;
-            }
-
-            case BorderThicknessHook: {
-                auto args = static_cast<void **>(data);
-                const bool requireNative = *static_cast<const bool *>(args[0]);
-                quint32 &thickness = *static_cast<quint32 *>(args[1]);
-                const auto hwnd = reinterpret_cast<HWND>(m_windowHandle->winId());
-                const auto nativeThickness = getWindowFrameBorderThickness(hwnd);
-                thickness = requireNative
-                                ? nativeThickness
-                                : QHighDpi::fromNativePixels(nativeThickness, m_windowHandle);
-                return;
-            }
-
-            case BorderColorsHook: {
-                auto arr = *reinterpret_cast<QList<QColor> *>(data);
-                arr.clear();
-                arr.push_back(kWindowsColorSet.activeLight);
-                arr.push_back(kWindowsColorSet.activeDark);
-                arr.push_back(kWindowsColorSet.inactiveLight);
-                arr.push_back(kWindowsColorSet.inactiveDark);
-                return;
-            }
-
-            case DrawBordersHook: {
-                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;
-                const auto borderThickness = int(QHighDpi::fromNativePixels(
-                    getWindowFrameBorderThickness(hwnd), m_windowHandle));
-                pen.setWidth(borderThickness * 2);
-                const bool active = m_delegate->isWindowActive(m_host);
-                const bool dark = isDarkThemeActive() && isDarkWindowFrameEnabled(hwnd);
-
-                if (active) {
-                    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{rect.width(), 0}
-                });
-                painter.restore();
+            case DefaultColorsHook: {
+                auto map = *reinterpret_cast<QMap<QString, QColor> *>(data);
+                map.clear();
+                map.insert("activeLight", kWindowsColorSet.activeLight);
+                map.insert("activeDark", kWindowsColorSet.activeDark);
+                map.insert("inactiveLight", kWindowsColorSet.inactiveLight);
+                map.insert("inactiveDark", kWindowsColorSet.inactiveDark);
                 return;
             }
 
@@ -879,6 +820,16 @@
             }
         }
         AbstractWindowContext::virtual_hook(id, data);
+    }
+
+    bool Win32WindowContext::needWin10BorderHandler() const {
+        return isWin10OrGreater() && !isWin11OrGreater();
+    }
+
+    void Win32WindowContext::setWin10BorderHandler(Win10BorderHandler *handler) {
+        win10BorderHandler.reset(handler);
+        handler->setBorderThickness(
+            int(getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId))));
     }
 
     bool Win32WindowContext::setupHost() {
@@ -1932,4 +1883,40 @@
         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 a56aac8..b390c93 100644
--- a/src/core/contexts/win32windowcontext_p.h
+++ b/src/core/contexts/win32windowcontext_p.h
@@ -6,6 +6,8 @@
 
 namespace QWK {
 
+    class Win10BorderHandler;
+
     class QWK_CORE_EXPORT Win32WindowContext : public AbstractWindowContext {
         Q_OBJECT
     public:
@@ -23,6 +25,9 @@
 
         QString key() const override;
         void virtual_hook(int id, void *data) override;
+
+        Q_INVOKABLE bool needWin10BorderHandler() const;
+        Q_INVOKABLE void setWin10BorderHandler(Win10BorderHandler *handler);
 
     protected:
         bool setupHost() override;
@@ -57,6 +62,8 @@
         bool mouseLeaveBlocked = false;
 
         bool centered = false;
+
+        std::unique_ptr<Win10BorderHandler> win10BorderHandler;
     };
 
 }
diff --git a/src/core/platforms/win10borderhandler.cpp b/src/core/platforms/win10borderhandler.cpp
new file mode 100644
index 0000000..7acdc3d
--- /dev/null
+++ b/src/core/platforms/win10borderhandler.cpp
@@ -0,0 +1,10 @@
+#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
new file mode 100644
index 0000000..6491d15
--- /dev/null
+++ b/src/core/platforms/win10borderhandler_p.h
@@ -0,0 +1,46 @@
+#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/quickwindowagent.cpp b/src/quick/quickwindowagent.cpp
index 1b29b42..747daf0 100644
--- a/src/quick/quickwindowagent.cpp
+++ b/src/quick/quickwindowagent.cpp
@@ -1,34 +1,36 @@
 #include "quickwindowagent.h"
 #include "quickwindowagent_p.h"
-#include "quickitemdelegate_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 {
+    class BorderItem : public QQuickPaintedItem, public Win10BorderHandler {
         Q_OBJECT
     public:
-        explicit BorderItem(AbstractWindowContext *ctx, QQuickItem *parent = nullptr);
+        explicit BorderItem(QQuickItem *parent = nullptr);
         ~BorderItem() override;
 
-        void updateHeight();
+        void updateGeometry() override;
+        void requestUpdate() override;
+
+        bool isActive() const override;
 
     public:
         void paint(QPainter *painter) override;
-        void itemChange(ItemChange change, const ItemChangeData &data) override;
-
-    private:
-        AbstractWindowContext *context;
-
-        void _q_windowActivityChanged();
     };
 
-    BorderItem::BorderItem(AbstractWindowContext *ctx, QQuickItem *parent)
-        : QQuickPaintedItem(parent), context(ctx) {
+    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.
@@ -46,48 +48,22 @@
 
     BorderItem::~BorderItem() = default;
 
-    void BorderItem::updateHeight() {
-        bool native = false;
-        quint32 thickness = 0;
-        void *args[] = {
-            &native,
-            &thickness,
-        };
-        context->virtual_hook(AbstractWindowContext::BorderThicknessHook, &args);
-        setHeight(thickness);
+    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);
-        void *args[] = {
-            painter,
-            &rect,
-            &region,
-        };
-        context->virtual_hook(AbstractWindowContext::DrawBordersHook, args);
-    }
-
-    void BorderItem::itemChange(ItemChange change, const ItemChangeData &data) {
-        QQuickPaintedItem::itemChange(change, data);
-        switch (change) {
-            case ItemSceneChange:
-                if (data.window) {
-                    connect(data.window, &QQuickWindow::activeChanged, this,
-                            &BorderItem::_q_windowActivityChanged);
-                }
-                Q_FALLTHROUGH();
-            case ItemVisibleHasChanged:
-            case ItemDevicePixelRatioHasChanged:
-                updateHeight();
-                break;
-            default:
-                break;
-        }
-    }
-
-    void BorderItem::_q_windowActivityChanged() {
-        update();
+        Win10BorderHandler::paintBorder(*painter, rect, region);
     }
 
     QuickWindowAgentPrivate::QuickWindowAgentPrivate() {
@@ -122,11 +98,17 @@
         }
         d->hostWindow = window;
 
-        if (bool needPaintBorder = false;
-            d->context->virtual_hook(AbstractWindowContext::NeedsDrawBordersHook, &needPaintBorder),
+#ifdef Q_OS_WINDOWS
+        // Install painting hook
+        if (bool needPaintBorder;
+            QMetaObject::invokeMethod(d->context.get(), "needWin10BorderHandler",
+                                      Qt::DirectConnection, Q_RETURN_ARG(bool, needPaintBorder)),
             needPaintBorder) {
-            d->borderItem = std::make_unique<BorderItem>(d->context.get(), window->contentItem());
+            QMetaObject::invokeMethod(
+                d->context.get(), "setWin10BorderHandler", Qt::DirectConnection,
+                Q_ARG(Win10BorderHandler *, new BorderItem(window->contentItem())));
         }
+#endif
         return true;
     }
 
diff --git a/src/quick/quickwindowagent_p.h b/src/quick/quickwindowagent_p.h
index d457690..243f534 100644
--- a/src/quick/quickwindowagent_p.h
+++ b/src/quick/quickwindowagent_p.h
@@ -6,8 +6,6 @@
 
 namespace QWK {
 
-    class BorderItem;
-
     class QuickWindowAgentPrivate : public WindowAgentBasePrivate {
         Q_DECLARE_PUBLIC(QuickWindowAgent)
     public:
@@ -18,8 +16,6 @@
 
         // Host
         QQuickWindow *hostWindow{};
-
-        std::unique_ptr<BorderItem> borderItem;
     };
 
 }
diff --git a/src/widgets/widgetwindowagent.cpp b/src/widgets/widgetwindowagent.cpp
index ea8f6df..e869010 100644
--- a/src/widgets/widgetwindowagent.cpp
+++ b/src/widgets/widgetwindowagent.cpp
@@ -5,34 +5,36 @@
 #include <QtGui/QPainter>
 #include <QtCore/QDebug>
 
+#ifdef Q_OS_WINDOWS
+#  include <QWKCore/private/win10borderhandler_p.h>
+#endif
+
 #include "widgetitemdelegate_p.h"
 
 namespace QWK {
 
-    class WidgetBorderHandler : public QObject {
+#ifdef Q_OS_WINDOWS
+    class WidgetBorderHandler : public QObject, public Win10BorderHandler {
     public:
-        WidgetBorderHandler(QWidget *widget, AbstractWindowContext *ctx)
-            : widget(widget), ctx(ctx), m_thickness(0) {
-            updateThickness();
+        explicit WidgetBorderHandler(QWidget *widget)
+            : Win10BorderHandler(widget->windowHandle()), widget(widget) {
             widget->installEventFilter(this);
         }
 
-        void updateThickness() {
-            // Query thickness
-            bool native = false;
-            void *a[] = {
-                &native,
-                &m_thickness,
-            };
-            ctx->virtual_hook(AbstractWindowContext::BorderThicknessHook, &a);
-        }
-
-        void updateMargins() {
+        void updateGeometry() override {
             if (widget->windowState() & (Qt::WindowMaximized | Qt::WindowFullScreen)) {
                 widget->setContentsMargins({});
             } else {
-                widget->setContentsMargins({0, int(m_thickness), 0, 0});
+                widget->setContentsMargins({0, int(m_borderThickness), 0, 0});
             }
+        }
+
+        void requestUpdate() override {
+            widget->update();
+        }
+
+        bool isActive() const override {
+            return widget->isActiveWindow();
         }
 
     protected:
@@ -41,33 +43,11 @@
                 case QEvent::Paint: {
                     if (widget->windowState() & (Qt::WindowMaximized | Qt::WindowFullScreen))
                         break;
-
                     auto paintEvent = static_cast<QPaintEvent *>(event);
                     QPainter painter(widget);
-                    QRect rect = paintEvent->rect();
-                    QRegion region = paintEvent->region();
-                    void *args[] = {
-                        &painter,
-                        &rect,
-                        &region,
-                    };
-                    ctx->virtual_hook(AbstractWindowContext::DrawBordersHook, args);
+                    paintBorder(painter, paintEvent->rect(), paintEvent->region());
                     return true;
                 }
-
-                case QEvent::WindowStateChange: {
-                    updateMargins();
-                    break;
-                }
-
-                case QEvent::WindowActivate:
-                case QEvent::WindowDeactivate: {
-                    widget->update();
-                    break;
-                }
-
-                    // TODO: Handle DPI Change
-
                 default:
                     break;
             }
@@ -75,9 +55,8 @@
         }
 
         QWidget *widget;
-        AbstractWindowContext *ctx;
-        quint32 m_thickness;
     };
+#endif
 
     WidgetWindowAgentPrivate::WidgetWindowAgentPrivate() {
     }
@@ -114,14 +93,17 @@
         }
         d->hostWidget = w;
 
+#ifdef Q_OS_WINDOWS
         // Install painting hook
-        if (bool needPaintBorder = false;
-            d->context->virtual_hook(AbstractWindowContext::NeedsDrawBordersHook, &needPaintBorder),
+        if (bool needPaintBorder;
+            QMetaObject::invokeMethod(d->context.get(), "needWin10BorderHandler",
+                                      Qt::DirectConnection, Q_RETURN_ARG(bool, needPaintBorder)),
             needPaintBorder) {
-            auto borderHandler = std::make_unique<WidgetBorderHandler>(w, d->context.get());
-            borderHandler->updateMargins();
-            d->borderHandler = std::move(borderHandler);
+            QMetaObject::invokeMethod(d->context.get(), "setWin10BorderHandler",
+                                      Qt::DirectConnection,
+                                      Q_ARG(Win10BorderHandler *, new WidgetBorderHandler(w)));
         }
+#endif
         return true;
     }
 
diff --git a/src/widgets/widgetwindowagent_p.h b/src/widgets/widgetwindowagent_p.h
index cd1e5c1..d433064 100644
--- a/src/widgets/widgetwindowagent_p.h
+++ b/src/widgets/widgetwindowagent_p.h
@@ -6,8 +6,6 @@
 
 namespace QWK {
 
-    class WidgetBorderHandler;
-
     class WidgetWindowAgentPrivate : public WindowAgentBasePrivate {
         Q_DECLARE_PUBLIC(WidgetWindowAgent)
     public:
@@ -18,8 +16,6 @@
 
         // Host
         QWidget *hostWidget{};
-
-        std::unique_ptr<WidgetBorderHandler> borderHandler;
     };
 
 }

--
Gitblit v1.9.1