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 ®ion = *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 ®ion) { + 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 ®ion); + + 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, - ®ion, - }; - 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, - ®ion, - }; - 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