From 2f6c83c095724bbba0f43b2f2893ba73c17949a6 Mon Sep 17 00:00:00 2001
From: Zhao Yuhang <2546789017@qq.com>
Date: 周一, 11 12月 2023 21:57:40 +0800
Subject: [PATCH] add quick border

---
 src/core/contexts/abstractwindowcontext.cpp |   13 +---
 src/widgets/widgetwindowagent.cpp           |   10 +-
 src/core/contexts/abstractwindowcontext_p.h |    1 
 src/quick/quickwindowagent_p.h              |    4 +
 src/core/contexts/win32windowcontext.cpp    |   25 ++++++--
 src/quick/quickwindowagent.cpp              |   78 +++++++++++++++++++++++++
 6 files changed, 110 insertions(+), 21 deletions(-)

diff --git a/src/core/contexts/abstractwindowcontext.cpp b/src/core/contexts/abstractwindowcontext.cpp
index 18ed37f..49eb0c5 100644
--- a/src/core/contexts/abstractwindowcontext.cpp
+++ b/src/core/contexts/abstractwindowcontext.cpp
@@ -181,20 +181,15 @@
 
     void AbstractWindowContext::virtual_hook(int id, void *data) {
         switch (id) {
-            case ShowSystemMenuHook: {
-                const auto &pos = *reinterpret_cast<const QPoint *>(data);
-                std::ignore = pos;
-                return;
-            }
             case NeedsDrawBordersHook: {
-                auto &result = *reinterpret_cast<bool *>(data);
+                auto &result = *static_cast<bool *>(data);
                 result = false;
                 return;
             }
             case DrawBordersHook: {
-                auto args = reinterpret_cast<void **>(data);
-                auto &painter = *reinterpret_cast<QPainter *>(args[0]);
-                auto &rect = *reinterpret_cast<const QRect *>(args[1]);
+                auto args = static_cast<void **>(data);
+                auto &painter = *static_cast<QPainter *>(args[0]);
+                const auto &rect = *static_cast<const QRect *>(args[1]);
 
                 // Top
                 painter.setPen(kSampleColorSet.grass);
diff --git a/src/core/contexts/abstractwindowcontext_p.h b/src/core/contexts/abstractwindowcontext_p.h
index a7b6767..3c04073 100644
--- a/src/core/contexts/abstractwindowcontext_p.h
+++ b/src/core/contexts/abstractwindowcontext_p.h
@@ -48,6 +48,7 @@
             ShowSystemMenuHook,
             NeedsDrawBordersHook,
             DrawBordersHook,
+            QueryBorderThicknessHook
         };
         virtual void virtual_hook(int id, void *data);
 
diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp
index 603079c..78a54c5 100644
--- a/src/core/contexts/win32windowcontext.cpp
+++ b/src/core/contexts/win32windowcontext.cpp
@@ -781,22 +781,22 @@
                 return;
             }
             case ShowSystemMenuHook: {
-                const auto &pos = *reinterpret_cast<const QPoint *>(data);
+                const auto &pos = *static_cast<const QPoint *>(data);
                 auto hWnd = reinterpret_cast<HWND>(m_windowHandle->winId());
                 showSystemMenu2(hWnd, qpoint2point(pos), false,
                                 m_delegate->isHostSizeFixed(m_host));
                 return;
             }
             case NeedsDrawBordersHook: {
-                auto &result = *reinterpret_cast<bool *>(data);
+                auto &result = *static_cast<bool *>(data);
                 result = isWin10OrGreater() && !isWin11OrGreater();
                 return;
             }
             case DrawBordersHook: {
-                auto args = reinterpret_cast<void **>(data);
-                auto &painter = *reinterpret_cast<QPainter *>(args[0]);
-                auto &rect = *reinterpret_cast<const QRect *>(args[1]);
-                auto &region = *reinterpret_cast<const QRegion *>(args[2]);
+                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));
@@ -827,6 +827,19 @@
                 painter.restore();
                 return;
             }
+            case QueryBorderThicknessHook: {
+                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);
+                if (requireNative) {
+                    thickness = nativeThickness;
+                } else {
+                    thickness = QHighDpi::fromNativePixels(nativeThickness, m_windowHandle);
+                }
+                return;
+            }
             default: {
                 // unreachable
                 break;
diff --git a/src/quick/quickwindowagent.cpp b/src/quick/quickwindowagent.cpp
index 2bc9bca..74a032b 100644
--- a/src/quick/quickwindowagent.cpp
+++ b/src/quick/quickwindowagent.cpp
@@ -3,8 +3,81 @@
 #include "quickitemdelegate_p.h"
 
 #include <QtQuick/QQuickWindow>
+#include <QtQuick/QQuickPaintedItem>
+#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuick/private/qquickanchors_p.h>
 
 namespace QWK {
+
+    class BorderItem : public QQuickPaintedItem {
+        Q_OBJECT
+
+    public:
+        explicit BorderItem(AbstractWindowContext *ctx, QQuickItem *parent = nullptr);
+        ~BorderItem() override;
+
+        void updateHeight();
+
+        void paint(QPainter *painter) override;
+
+        void itemChange(ItemChange change, const ItemChangeData &data) override;
+
+    private:
+        AbstractWindowContext *context;
+    };
+
+    BorderItem::BorderItem(AbstractWindowContext *ctx, QQuickItem *parent) : QQuickPaintedItem(parent), context(ctx) {
+        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(std::numeric_limits<qreal>::max());
+    }
+
+    BorderItem::~BorderItem() = default;
+
+    void BorderItem::updateHeight() {
+        bool native = false;
+        quint32 thickness = 0;
+        void *args[] = { &native, &thickness };
+        context->virtual_hook(AbstractWindowContext::QueryBorderThicknessHook, &args);
+        setHeight(thickness);
+    }
+
+    void BorderItem::paint(QPainter *painter) {
+        auto rect = QRect{ QPoint{ 0, 0}, size().toSize() };
+        auto region = QRegion{ 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, [this](){ update(); });
+                }
+                Q_FALLTHROUGH();
+            case ItemVisibleHasChanged:
+            case ItemDevicePixelRatioHasChanged:
+                updateHeight();
+                break;
+            default:
+                break;
+        }
+    }
 
     QuickWindowAgentPrivate::QuickWindowAgentPrivate() {
     }
@@ -13,6 +86,7 @@
     }
 
     void QuickWindowAgentPrivate::init() {
+        borderItem = std::make_unique<BorderItem>(context.get(), hostWindow->contentItem());
     }
 
     QuickWindowAgent::QuickWindowAgent(QObject *parent)
@@ -34,7 +108,7 @@
         }
 
         if (!d->setup(window, new QuickItemDelegate())) {
-            return true;
+            return false;
         }
         d->hostWindow = window;
         return true;
@@ -87,3 +161,5 @@
     }
 
 }
+
+#include "quickwindowagent.moc"
\ No newline at end of file
diff --git a/src/quick/quickwindowagent_p.h b/src/quick/quickwindowagent_p.h
index 243f534..d457690 100644
--- a/src/quick/quickwindowagent_p.h
+++ b/src/quick/quickwindowagent_p.h
@@ -6,6 +6,8 @@
 
 namespace QWK {
 
+    class BorderItem;
+
     class QuickWindowAgentPrivate : public WindowAgentBasePrivate {
         Q_DECLARE_PUBLIC(QuickWindowAgent)
     public:
@@ -16,6 +18,8 @@
 
         // Host
         QQuickWindow *hostWindow{};
+
+        std::unique_ptr<BorderItem> borderItem;
     };
 
 }
diff --git a/src/widgets/widgetwindowagent.cpp b/src/widgets/widgetwindowagent.cpp
index 43528b5..e859fbd 100644
--- a/src/widgets/widgetwindowagent.cpp
+++ b/src/widgets/widgetwindowagent.cpp
@@ -18,16 +18,16 @@
         bool eventFilter(QObject *obj, QEvent *event) override {
             switch (event->type()) {
                 case QEvent::Paint: {
-                    auto e = static_cast<QPaintEvent *>(event);
+                    auto pe = static_cast<QPaintEvent *>(event);
                     QPainter painter(widget);
-                    QRect rect = e->rect();
-                    QRegion region = e->region();
-                    void *a[] = {
+                    QRect rect = pe->rect();
+                    QRegion region = pe->region();
+                    void *args[] = {
                         &painter,
                         &rect,
                         &region,
                     };
-                    ctx->virtual_hook(AbstractWindowContext::DrawBordersHook, a);
+                    ctx->virtual_hook(AbstractWindowContext::DrawBordersHook, args);
                     return true;
                 }
                 default:

--
Gitblit v1.9.1