examples/mainwindow/mainwindow.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/core/CMakeLists.txt | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/core/contexts/abstractwindowcontext.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/core/contexts/abstractwindowcontext_p.h | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/core/contexts/win32windowcontext.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/core/contexts/win32windowcontext_p.h | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/core/kernel/nativeeventfilter.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/core/kernel/nativeeventfilter_p.h | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/core/kernel/sharedeventfilter.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/core/kernel/sharedeventfilter_p.h | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/core/shared/qwkwindowsextra_p.h | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/widgets/widgetwindowagent.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/widgets/widgetwindowagent_p.h | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/widgets/widgetwindowagent_win.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
examples/mainwindow/mainwindow.cpp
@@ -20,6 +20,10 @@ #include <widgetframe/windowbar.h> #include <widgetframe/windowbutton.h> #ifdef Q_OS_WINDOWS # include <QWKCore/qwindowkit_windows.h> #endif class ClockWidget : public QLabel { public: explicit ClockWidget(QWidget *parent = nullptr) : QLabel(parent) { @@ -116,7 +120,13 @@ windowAgent->setup(this); #ifdef Q_OS_WIN windowAgent->setWindowAttribute(QStringLiteral("dark-mode"), true); if (QWK::IsWindows10OrGreater_Real() && !QWK::IsWindows11OrGreater_Real()) { // windowAgent->setWindowAttribute(QStringLiteral("dark-mode"), true); // https://github.com/microsoft/terminal/blob/71a6f26e6ece656084e87de1a528c4a8072eeabd/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp#L940 // Must call DWM API to extend top frame to client area windowAgent->setWindowAttribute(QStringLiteral("extra-margins"), true); } #endif // 2. Construct your title bar src/core/CMakeLists.txt
@@ -13,6 +13,8 @@ windowitemdelegate.cpp kernel/nativeeventfilter_p.h kernel/nativeeventfilter.cpp kernel/sharedeventfilter_p.h kernel/sharedeventfilter.cpp shared/systemwindow_p.h contexts/abstractwindowcontext_p.h contexts/abstractwindowcontext.cpp @@ -44,6 +46,7 @@ contexts/win32windowcontext_p.h contexts/win32windowcontext.cpp ) list(APPEND _links_private uxtheme) elseif(APPLE) list(APPEND _src contexts/cocoawindowcontext_p.h src/core/contexts/abstractwindowcontext.cpp
@@ -9,6 +9,8 @@ namespace QWK { namespace { class WinIdChangeEventFilter : public QObject { public: explicit WinIdChangeEventFilter(QObject *widget, AbstractWindowContext *ctx, @@ -30,6 +32,26 @@ AbstractWindowContext *ctx; }; class WindowEventFilter : public QObject { public: explicit WindowEventFilter(QWindow *window, AbstractWindowContext *ctx, QObject *parent = nullptr) : QObject(parent), ctx(ctx), window(window) { window->installEventFilter(this); } protected: bool eventFilter(QObject *obj, QEvent *event) override { return ctx->sharedDispatch(obj, event); } protected: AbstractWindowContext *ctx; QWindow *window; }; } AbstractWindowContext::AbstractWindowContext() = default; AbstractWindowContext::~AbstractWindowContext() = default; @@ -45,6 +67,7 @@ m_windowHandle = m_delegate->hostWindow(m_host); if (m_windowHandle) { winIdChanged(); m_windowEventFilter = std::make_unique<WindowEventFilter>(m_windowHandle, this); } } @@ -245,9 +268,11 @@ m_windowHandle = m_delegate->hostWindow(m_host); if (oldWindow == m_windowHandle) return; m_windowEventFilter.reset(); winIdChanged(); if (m_windowHandle) { m_windowEventFilter = std::make_unique<WindowEventFilter>(m_windowHandle, this); // Refresh window attributes auto attributes = m_windowAttributes; m_windowAttributes.clear(); src/core/contexts/abstractwindowcontext_p.h
@@ -20,11 +20,14 @@ #include <QWKCore/windowagentbase.h> #include <QWKCore/private/nativeeventfilter_p.h> #include <QWKCore/private/sharedeventfilter_p.h> #include <QWKCore/private/windowitemdelegate_p.h> namespace QWK { class QWK_CORE_EXPORT AbstractWindowContext : public QObject, public NativeEventDispatcher { class QWK_CORE_EXPORT AbstractWindowContext : public QObject, public NativeEventDispatcher, public SharedEventDispatcher { Q_OBJECT public: AbstractWindowContext(); @@ -93,6 +96,7 @@ QVariantHash m_windowAttributes; std::unique_ptr<QObject> m_windowEventFilter; std::unique_ptr<QObject> m_winIdChangeEventFilter; }; src/core/contexts/win32windowcontext.cpp
@@ -729,27 +729,49 @@ return; } // case AbstractWindowContext::DrawWindows10BackgroundHook: { // #if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDER) // if (!m_windowHandle) // return; // // auto hWnd = reinterpret_cast<HWND>(windowId); // HDC hdc = ::GetDC(hWnd); // RECT windowRect{}; // ::GetClientRect(hWnd, &windowRect); // RECT rcRest = { // 0, // int(getWindowFrameBorderThickness(hWnd)), // RECT_WIDTH(windowRect), // RECT_HEIGHT(windowRect), // }; // HBRUSH blueBrush = ::CreateSolidBrush(RGB(0, 0, 255)); // // // To hide the original title bar, we have to paint on top of it // with // // the alpha component set to 255. This is a hack to do it with // GDI. // // See NonClientIslandWindow::_UpdateFrameMargins for more // information. HDC opaqueDc; BP_PAINTPARAMS params = // {sizeof(params), BPPF_NOCLIP | BPPF_ERASE}; auto buf = // BeginBufferedPaint(hdc, &rcRest, BPBF_TOPDOWNDIB, ¶ms, // &opaqueDc); if (!buf || !opaqueDc) { // return; // } // // ::FillRect(opaqueDc, &rcRest, blueBrush); // ::BufferedPaintSetAlpha(buf, nullptr, 255); // ::EndBufferedPaint(buf, TRUE); // // ::DeleteObject(blueBrush); // ::ReleaseDC(hWnd, hdc); // #endif // return; // } default: break; } AbstractWindowContext::virtual_hook(id, data); } bool Win32WindowContext::needBorderPainter() const { Q_UNUSED(this) return isSystemBorderEnabled() && !isWin11OrGreater(); } int Win32WindowContext::borderThickness() const { return int(getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId))); } void Win32WindowContext::resume(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result) { const auto msg = static_cast<const MSG *>(message); LRESULT res = ::CallWindowProcW(g_qtWindowProc, msg->hwnd, msg->message, msg->wParam, msg->lParam); *result = decltype(*result)(res); } void Win32WindowContext::winIdChanged() { @@ -768,13 +790,7 @@ auto hWnd = reinterpret_cast<HWND>(winId); if (!isSystemBorderEnabled()) { static constexpr const MARGINS margins = {1, 1, 1, 1}; DynamicApis::instance().pDwmExtendFrameIntoClientArea(hWnd, &margins); } else if (isWin10OrGreater() && !isWin11OrGreater()) { // https://github.com/microsoft/terminal/blob/71a6f26e6ece656084e87de1a528c4a8072eeabd/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp#L940 // Must call DWM API to extend top frame to client area static constexpr const MARGINS margins = {0, 0, 1, 0}; DynamicApis::instance().pDwmExtendFrameIntoClientArea(hWnd, &margins); setWindowAttribute("extra-margins", true); } { @@ -839,7 +855,7 @@ msg.wParam = wParam; msg.lParam = lParam; QT_NATIVE_EVENT_RESULT_TYPE res = 0; if (dispatch(QByteArrayLiteral("windows_generic_MSG"), &msg, &res)) { if (nativeDispatch(QByteArrayLiteral("windows_generic_MSG"), &msg, &res)) { *result = LRESULT(res); return true; } @@ -851,9 +867,43 @@ const QVariant &oldAttribute) { const auto hwnd = reinterpret_cast<HWND>(m_windowHandle->winId()); const DynamicApis &apis = DynamicApis::instance(); static constexpr const MARGINS extendMargins = {-1, -1, -1, -1}; static const auto defaultMargins = isSystemBorderEnabled() ? MARGINS{0, 0, 0, 0} : MARGINS{1, 1, 1, 1}; static constexpr const MARGINS defaultEmptyMargins = {0, 0, 0, 0}; static constexpr const MARGINS defaultExtraMargins = {1, 1, 1, 1}; static constexpr const MARGINS extendedMargins = {-1, -1, -1, -1}; if (key == QStringLiteral("extra-margins")) { if (isWin11OrGreater()) return false; hasExtraMargins = attribute.toBool(); DynamicApis::instance().pDwmExtendFrameIntoClientArea( hwnd, hasExtraMargins ? &defaultExtraMargins : &defaultEmptyMargins); return true; } if (key == QStringLiteral("dark-mode")) { if (!isWin101809OrGreater()) { return false; } BOOL enable = attribute.toBool(); if (isWin101903OrGreater()) { apis.pSetPreferredAppMode(enable ? PAM_AUTO : PAM_DEFAULT); } else { apis.pAllowDarkModeForApp(enable); } for (const auto attr : { _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, _DWMWA_USE_IMMERSIVE_DARK_MODE, }) { apis.pDwmSetWindowAttribute(hwnd, attr, &enable, sizeof(enable)); } apis.pFlushMenuThemes(); return true; } // For Win11 or later static const auto &defaultMargins = isSystemBorderEnabled() ? defaultExtraMargins : defaultEmptyMargins; if (key == QStringLiteral("mica")) { if (!isWin11OrGreater()) { return false; @@ -861,7 +911,7 @@ if (attribute.toBool()) { // We need to extend the window frame into the whole client area to be able // to see the blurred window background. apis.pDwmExtendFrameIntoClientArea(hwnd, &extendMargins); apis.pDwmExtendFrameIntoClientArea(hwnd, &extendedMargins); if (isWin1122H2OrGreater()) { // Use official DWM API to enable Mica, available since Windows 11 22H2 // (10.0.22621). @@ -886,14 +936,16 @@ apis.pDwmExtendFrameIntoClientArea(hwnd, &defaultMargins); } return true; } else if (key == QStringLiteral("mica-alt")) { } if (key == QStringLiteral("mica-alt")) { if (!isWin1122H2OrGreater()) { return false; } if (attribute.toBool()) { // We need to extend the window frame into the whole client area to be able // to see the blurred window background. apis.pDwmExtendFrameIntoClientArea(hwnd, &extendMargins); apis.pDwmExtendFrameIntoClientArea(hwnd, &extendedMargins); // Use official DWM API to enable Mica Alt, available since Windows 11 22H2 // (10.0.22621). const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_TABBEDWINDOW; @@ -906,14 +958,16 @@ apis.pDwmExtendFrameIntoClientArea(hwnd, &defaultMargins); } return true; } else if (key == QStringLiteral("acrylic-material")) { } if (key == QStringLiteral("acrylic-material")) { if (!isWin11OrGreater()) { return false; } if (attribute.toBool()) { // We need to extend the window frame into the whole client area to be able // to see the blurred window background. apis.pDwmExtendFrameIntoClientArea(hwnd, &extendMargins); apis.pDwmExtendFrameIntoClientArea(hwnd, &extendedMargins); const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_TRANSIENTWINDOW; apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType, @@ -951,13 +1005,15 @@ apis.pDwmExtendFrameIntoClientArea(hwnd, &defaultMargins); } return true; } else if (key == QStringLiteral("dwm-blur")) { } if (key == QStringLiteral("dwm-blur")) { // TODO: Optimize // Currently not available!!! if (attribute.toBool()) { // We need to extend the window frame into the whole client area to be able // to see the blurred window background. apis.pDwmExtendFrameIntoClientArea(hwnd, &extendMargins); apis.pDwmExtendFrameIntoClientArea(hwnd, &extendedMargins); if (isWin8OrGreater()) { ACCENT_POLICY policy{}; policy.dwAccentState = ACCENT_ENABLE_BLURBEHIND; @@ -991,26 +1047,6 @@ } apis.pDwmExtendFrameIntoClientArea(hwnd, &defaultMargins); } return true; } else if (key == QStringLiteral("dark-mode")) { if (!isWin101809OrGreater()) { return false; } BOOL enable = attribute.toBool(); if (isWin101903OrGreater()) { apis.pSetPreferredAppMode(enable ? PAM_AUTO : PAM_DEFAULT); } else { apis.pAllowDarkModeForApp(enable); } for (const auto attr : { _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, _DWMWA_USE_IMMERSIVE_DARK_MODE, }) { apis.pDwmSetWindowAttribute(hwnd, attr, &enable, sizeof(enable)); } apis.pFlushMenuThemes(); return true; } return false; @@ -2046,4 +2082,13 @@ return false; } bool Win32WindowContext::needBorderPainter() const { Q_UNUSED(this) return isSystemBorderEnabled() && !isWin11OrGreater(); } int Win32WindowContext::borderThickness() const { return int(getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId))); } } src/core/contexts/win32windowcontext_p.h
@@ -35,12 +35,6 @@ QString key() const override; void virtual_hook(int id, void *data) override; bool needBorderPainter() const; int borderThickness() const; void resume(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result) override; protected: void winIdChanged() override; bool windowAttributeChanged(const QString &key, const QVariant &attribute, @@ -65,6 +59,11 @@ bool nonClientCalcSizeHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result); public: bool needBorderPainter() const; int borderThickness() const; protected: WId windowId = 0; @@ -76,6 +75,8 @@ bool mouseLeaveBlocked = false; bool centered = false; bool hasExtraMargins = false; }; } src/core/kernel/nativeeventfilter.cpp
@@ -5,23 +5,23 @@ namespace QWK { NativeEventFilter::NativeEventFilter() : m_dispatcher(nullptr) { NativeEventFilter::NativeEventFilter() : m_nativeDispatcher(nullptr) { } NativeEventFilter::~NativeEventFilter() { if (m_dispatcher) m_dispatcher->removeNativeEventFilter(this); if (m_nativeDispatcher) m_nativeDispatcher->removeNativeEventFilter(this); } NativeEventDispatcher::NativeEventDispatcher() = default; NativeEventDispatcher::~NativeEventDispatcher() { for (const auto &observer : std::as_const(m_nativeEventFilters)) { observer->m_dispatcher = nullptr; observer->m_nativeDispatcher = nullptr; } } bool NativeEventDispatcher::dispatch(const QByteArray &eventType, void *message, bool NativeEventDispatcher::nativeDispatch(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result) { for (const auto &ef : std::as_const(m_nativeEventFilters)) { if (ef->nativeEventFilter(eventType, message, result)) @@ -30,23 +30,19 @@ return false; } void NativeEventDispatcher::resume(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result) { } void NativeEventDispatcher::installNativeEventFilter(NativeEventFilter *filter) { if (!filter || filter->m_dispatcher) if (!filter || filter->m_nativeDispatcher) return; m_nativeEventFilters.append(filter); filter->m_dispatcher = this; filter->m_nativeDispatcher = this; } void NativeEventDispatcher::removeNativeEventFilter(NativeEventFilter *filter) { if (!m_nativeEventFilters.removeOne(filter)) { return; } filter->m_dispatcher = nullptr; filter->m_nativeDispatcher = nullptr; } @@ -64,7 +60,7 @@ bool nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result) override { return dispatch(eventType, message, result); return nativeDispatch(eventType, message, result); } static AppMasterNativeEventFilter *instance; src/core/kernel/nativeeventfilter_p.h
@@ -22,10 +22,7 @@ virtual ~NativeEventDispatcher(); public: virtual bool dispatch(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result); virtual void resume(const QByteArray &eventType, void *message, virtual bool nativeDispatch(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result); public: @@ -50,7 +47,7 @@ QT_NATIVE_EVENT_RESULT_TYPE *result) = 0; protected: NativeEventDispatcher *m_dispatcher; NativeEventDispatcher *m_nativeDispatcher; friend class NativeEventDispatcher; src/core/kernel/sharedeventfilter.cpp
New file @@ -0,0 +1,44 @@ #include "sharedeventfilter_p.h" namespace QWK { SharedEventFilter::SharedEventFilter() : m_sharedDispatcher(nullptr) { } SharedEventFilter::~SharedEventFilter() { if (m_sharedDispatcher) m_sharedDispatcher->removeSharedEventFilter(this); } SharedEventDispatcher::SharedEventDispatcher() = default; SharedEventDispatcher::~SharedEventDispatcher() { for (const auto &observer : std::as_const(m_sharedEventFilters)) { observer->m_sharedDispatcher = nullptr; } } bool SharedEventDispatcher::sharedDispatch(QObject *obj, QEvent *event) { for (const auto &ef : std::as_const(m_sharedEventFilters)) { if (ef->sharedEventFilter(obj, event)) return true; } return false; } void SharedEventDispatcher::installSharedEventFilter(SharedEventFilter *filter) { if (!filter || filter->m_sharedDispatcher) return; m_sharedEventFilters.append(filter); filter->m_sharedDispatcher = this; } void SharedEventDispatcher::removeSharedEventFilter(SharedEventFilter *filter) { if (!m_sharedEventFilters.removeOne(filter)) { return; } filter->m_sharedDispatcher = nullptr; } } src/core/kernel/sharedeventfilter_p.h
New file @@ -0,0 +1,57 @@ #ifndef SHAREDEVENTFILTER_P_H #define SHAREDEVENTFILTER_P_H // // W A R N I N G !!! // ----------------- // // This file is not part of the QWindowKit API. It is used purely as an // implementation detail. This header file may change from version to // version without notice, or may even be removed. // #include <QWKCore/qwkglobal.h> namespace QWK { class SharedEventFilter; class QWK_CORE_EXPORT SharedEventDispatcher { public: SharedEventDispatcher(); virtual ~SharedEventDispatcher(); public: virtual bool sharedDispatch(QObject *obj, QEvent *event); public: void installSharedEventFilter(SharedEventFilter *filter); void removeSharedEventFilter(SharedEventFilter *filter); protected: QVector<SharedEventFilter *> m_sharedEventFilters; friend class SharedEventFilter; Q_DISABLE_COPY(SharedEventDispatcher) }; class QWK_CORE_EXPORT SharedEventFilter { public: SharedEventFilter(); virtual ~SharedEventFilter(); public: virtual bool sharedEventFilter(QObject *obj, QEvent *event) = 0; protected: SharedEventDispatcher *m_sharedDispatcher; friend class SharedEventDispatcher; Q_DISABLE_COPY(SharedEventFilter) }; } #endif // SHAREDEVENTFILTER_P_H src/core/shared/qwkwindowsextra_p.h
@@ -300,6 +300,15 @@ static inline constexpr RECT qrect2rect(const QRect &qrect) { return RECT{LONG(qrect.left()), LONG(qrect.top()), LONG(qrect.right()), LONG(qrect.bottom())}; } static inline constexpr QMargins margins2qmargins(const MARGINS &margins) { return {margins.cxLeftWidth, margins.cyTopHeight, margins.cxRightWidth, margins.cyBottomHeight}; } static inline constexpr MARGINS qmargins2margins(const QMargins &qmargins) { return MARGINS{qmargins.left(), qmargins.right(), qmargins.top(), qmargins.bottom()}; } static inline /*constexpr*/ QString hwnd2str(const WId windowId) { src/widgets/widgetwindowagent.cpp
@@ -39,7 +39,7 @@ d->setup(w, new WidgetItemDelegate()); d->hostWidget = w; #ifdef Q_OS_WINDOWS #if defined(Q_OS_WINDOWS) && QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDER) d->setupWindows10BorderWorkaround(); #endif return true; src/widgets/widgetwindowagent_p.h
@@ -10,6 +10,7 @@ // version without notice, or may even be removed. // #include <QWKCore/qwkconfig.h> #include <QWKCore/private/windowagentbase_p.h> #include <QWKWidgets/widgetwindowagent.h> @@ -25,12 +26,13 @@ // Host QWidget *hostWidget{}; #ifdef Q_OS_MAC QWidget *systemButtonAreaWidget{}; std::unique_ptr<QObject> systemButtonAreaWidgetEventFilter; #endif #ifdef Q_OS_WINDOWS #if defined(Q_OS_WINDOWS) && QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDER) void setupWindows10BorderWorkaround(); #endif }; src/widgets/widgetwindowagent_win.cpp
@@ -1,9 +1,10 @@ #include "widgetwindowagent_p.h" #include <QtCore/QDebug> #include <QtCore/QDateTime> #include <QtGui/QPainter> #include <QWKCore/qwindowkit_windows.h> #include <QWKCore/qwkconfig.h> #include <QWKCore/private/nativeeventfilter_p.h> namespace QWK { @@ -12,19 +13,21 @@ class WidgetBorderHandler; class WidgetBorderHandler : public QObject, public NativeEventFilter { class WidgetBorderHandler : public QObject, public NativeEventFilter, public SharedEventFilter { public: explicit WidgetBorderHandler(QWidget *widget, AbstractWindowContext *ctx) : QObject(ctx), widget(widget), ctx(ctx) { widget->installEventFilter(this); ctx->installNativeEventFilter(this); ctx->installSharedEventFilter(this); updateGeometry(); } inline bool isNormalWindow() const { return widget->windowState() & (Qt::WindowMinimized | Qt::WindowMaximized | Qt::WindowFullScreen); return !(widget->windowState() & (Qt::WindowMinimized | Qt::WindowMaximized | Qt::WindowFullScreen)); } void updateGeometry() { @@ -66,19 +69,48 @@ } break; } # if 0 case WM_ACTIVATE: { if (LOWORD(msg->wParam) == WA_INACTIVE) { // 窗口失去激活状态 } else { // 窗口被激活 } break; } # endif case WM_PAINT: { default: break; } return false; } bool sharedEventFilter(QObject *obj, QEvent *event) override { Q_UNUSED(obj) auto window = widget->windowHandle(); if (event->type() == QEvent::Expose) { // Qt will absolutely send a QExposeEvent to the QWindow when it receives a // WM_PAINT message. When the control flow enters the expose handler, Qt must // have already called BeginPaint() and it's the best time for us to draw the // top border. auto ee = static_cast<QExposeEvent *>(event); if (isNormalWindow() && window->isExposed() && !ee->region().isNull()) { // Friend class helping to call `event` class HackedWindow : public QWindow { public: friend class QWK::WidgetBorderHandler; }; // Let Qt paint first m_dispatcher->resume(eventType, message, result); static_cast<HackedWindow *>(window)->event(event); // Upon receiving the WM_PAINT message, Qt will redraw the entire view, and we // must wait for it to finish redrawing before drawing this top border area ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook2, nullptr); return true; } default: break; } return false; } @@ -87,6 +119,9 @@ Q_UNUSED(obj) switch (event->type()) { case QEvent::UpdateRequest: { if (!isNormalWindow()) break; // Friend class helping to call `event` class HackedWidget : public QWidget { public: @@ -123,16 +158,14 @@ QWidget *widget; AbstractWindowContext *ctx; }; #endif void WidgetWindowAgentPrivate::setupWindows10BorderWorkaround() { #if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDER) // Install painting hook auto ctx = context.get(); if (ctx->property("needBorderPainter").toBool()) { std::ignore = new WidgetBorderHandler(hostWidget, ctx); } #endif } #endif }