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