Sine Striker
2023-12-12 0586c98f90866e4bc9f0dfe73aefb0a07c56697e
Add win10 border handler
9个文件已修改
2个文件已添加
396 ■■■■ 已修改文件
src/core/CMakeLists.txt 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/abstractwindowcontext.cpp 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/abstractwindowcontext_p.h 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/win32windowcontext.cpp 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/win32windowcontext_p.h 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/platforms/win10borderhandler.cpp 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/platforms/win10borderhandler_p.h 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/quick/quickwindowagent.cpp 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/quick/quickwindowagent_p.h 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/widgets/widgetwindowagent.cpp 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/widgets/widgetwindowagent_p.h 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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
)
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;
        }
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);
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();
    }
}
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;
    };
}
src/core/platforms/win10borderhandler.cpp
New file
@@ -0,0 +1,10 @@
#include "win10borderhandler_p.h"
namespace QWK {
    Win10BorderHandler::Win10BorderHandler(QWindow *window) : m_window(window), m_borderThickness(0) {
    }
    Win10BorderHandler::~Win10BorderHandler() = default;
}
src/core/platforms/win10borderhandler_p.h
New file
@@ -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
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;
    }
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;
    };
}
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;
    }
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;
    };
}