From 29901fc2a97eedd3c914f807d1819c9ea7e69973 Mon Sep 17 00:00:00 2001 From: Sine Striker <trueful@163.com> Date: 周一, 25 12月 2023 17:53:34 +0800 Subject: [PATCH] Optimize Windows 10 border handling --- src/core/contexts/abstractwindowcontext.cpp | 61 +++++-- src/widgets/widgetwindowagent_win.cpp | 57 +++++- src/core/contexts/abstractwindowcontext_p.h | 6 src/core/shared/qwkwindowsextra_p.h | 17 + src/widgets/widgetwindowagent_p.h | 4 src/core/contexts/win32windowcontext.cpp | 155 ++++++++++++------ src/core/contexts/win32windowcontext_p.h | 13 src/core/kernel/sharedeventfilter_p.h | 57 +++++++ src/widgets/widgetwindowagent.cpp | 2 src/core/kernel/nativeeventfilter.cpp | 22 +- src/core/kernel/nativeeventfilter_p.h | 9 src/core/kernel/sharedeventfilter.cpp | 44 +++++ src/core/CMakeLists.txt | 3 examples/mainwindow/mainwindow.cpp | 12 + 14 files changed, 344 insertions(+), 118 deletions(-) diff --git a/examples/mainwindow/mainwindow.cpp b/examples/mainwindow/mainwindow.cpp index a1a39dd..6a7435e 100644 --- a/examples/mainwindow/mainwindow.cpp +++ b/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 diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 6e20cf1..e801dd2 100644 --- a/src/core/CMakeLists.txt +++ b/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 diff --git a/src/core/contexts/abstractwindowcontext.cpp b/src/core/contexts/abstractwindowcontext.cpp index de9674c..8672faf 100644 --- a/src/core/contexts/abstractwindowcontext.cpp +++ b/src/core/contexts/abstractwindowcontext.cpp @@ -9,26 +9,48 @@ namespace QWK { - class WinIdChangeEventFilter : public QObject { - public: - explicit WinIdChangeEventFilter(QObject *widget, AbstractWindowContext *ctx, - QObject *parent = nullptr) - : QObject(parent), ctx(ctx) { - widget->installEventFilter(this); - } + namespace { - protected: - bool eventFilter(QObject *obj, QEvent *event) override { - Q_UNUSED(obj) - if (event->type() == QEvent::WinIdChange) { - ctx->notifyWinIdChange(); + class WinIdChangeEventFilter : public QObject { + public: + explicit WinIdChangeEventFilter(QObject *widget, AbstractWindowContext *ctx, + QObject *parent = nullptr) + : QObject(parent), ctx(ctx) { + widget->installEventFilter(this); } - return false; - } - protected: - AbstractWindowContext *ctx; - }; + protected: + bool eventFilter(QObject *obj, QEvent *event) override { + Q_UNUSED(obj) + if (event->type() == QEvent::WinIdChange) { + ctx->notifyWinIdChange(); + } + return false; + } + + protected: + 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; @@ -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(); diff --git a/src/core/contexts/abstractwindowcontext_p.h b/src/core/contexts/abstractwindowcontext_p.h index f8ea17e..ec4937a 100644 --- a/src/core/contexts/abstractwindowcontext_p.h +++ b/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; }; diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index 9b92b0d..6dec43f 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/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))); + } + } diff --git a/src/core/contexts/win32windowcontext_p.h b/src/core/contexts/win32windowcontext_p.h index 3af0e13..baa0254 100644 --- a/src/core/contexts/win32windowcontext_p.h +++ b/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; }; } diff --git a/src/core/kernel/nativeeventfilter.cpp b/src/core/kernel/nativeeventfilter.cpp index 5ad57d1..babc55c 100644 --- a/src/core/kernel/nativeeventfilter.cpp +++ b/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; diff --git a/src/core/kernel/nativeeventfilter_p.h b/src/core/kernel/nativeeventfilter_p.h index a2e852e..569ea4a 100644 --- a/src/core/kernel/nativeeventfilter_p.h +++ b/src/core/kernel/nativeeventfilter_p.h @@ -22,11 +22,8 @@ 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, - QT_NATIVE_EVENT_RESULT_TYPE *result); + virtual bool nativeDispatch(const QByteArray &eventType, void *message, + QT_NATIVE_EVENT_RESULT_TYPE *result); public: void installNativeEventFilter(NativeEventFilter *filter); @@ -50,7 +47,7 @@ QT_NATIVE_EVENT_RESULT_TYPE *result) = 0; protected: - NativeEventDispatcher *m_dispatcher; + NativeEventDispatcher *m_nativeDispatcher; friend class NativeEventDispatcher; diff --git a/src/core/kernel/sharedeventfilter.cpp b/src/core/kernel/sharedeventfilter.cpp new file mode 100644 index 0000000..8b4dc46 --- /dev/null +++ b/src/core/kernel/sharedeventfilter.cpp @@ -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; + } + +} \ No newline at end of file diff --git a/src/core/kernel/sharedeventfilter_p.h b/src/core/kernel/sharedeventfilter_p.h new file mode 100644 index 0000000..4865756 --- /dev/null +++ b/src/core/kernel/sharedeventfilter_p.h @@ -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 diff --git a/src/core/shared/qwkwindowsextra_p.h b/src/core/shared/qwkwindowsextra_p.h index ede119c..15fe4e7 100644 --- a/src/core/shared/qwkwindowsextra_p.h +++ b/src/core/shared/qwkwindowsextra_p.h @@ -145,9 +145,9 @@ // Win10 1809 (10.0.17763) using RefreshImmersiveColorPolicyStatePtr = VOID(WINAPI *)(VOID); // Ordinal 104 - using AllowDarkModeForWindowPtr = BOOL(WINAPI *)(HWND, BOOL); // Ordinal 133 - using AllowDarkModeForAppPtr = BOOL(WINAPI *)(BOOL); // Ordinal 135 - using FlushMenuThemesPtr = VOID(WINAPI *)(VOID); // Ordinal 136 + using AllowDarkModeForWindowPtr = BOOL(WINAPI *)(HWND, BOOL); // Ordinal 133 + using AllowDarkModeForAppPtr = BOOL(WINAPI *)(BOOL); // Ordinal 135 + using FlushMenuThemesPtr = VOID(WINAPI *)(VOID); // Ordinal 136 // Win10 1903 (10.0.18362) using SetPreferredAppModePtr = PREFERRED_APP_MODE(WINAPI *)(PREFERRED_APP_MODE); // Ordinal 135 @@ -213,7 +213,7 @@ #undef DYNAMIC_API_RESOLVE -#define UNDOC_API_RESOLVE(DLL, NAME, ORDINAL) \ +#define UNDOC_API_RESOLVE(DLL, NAME, ORDINAL) \ p##NAME = reinterpret_cast<decltype(p##NAME)>(DLL.resolve(MAKEINTRESOURCEA(ORDINAL))) QSystemLibrary uxtheme(QStringLiteral("uxtheme")); @@ -302,6 +302,15 @@ 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) { // NULL handle is allowed here. return QLatin1String("0x") + diff --git a/src/widgets/widgetwindowagent.cpp b/src/widgets/widgetwindowagent.cpp index b510efb..5422694 100644 --- a/src/widgets/widgetwindowagent.cpp +++ b/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; diff --git a/src/widgets/widgetwindowagent_p.h b/src/widgets/widgetwindowagent_p.h index ac4d653..c5b01be 100644 --- a/src/widgets/widgetwindowagent_p.h +++ b/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 }; diff --git a/src/widgets/widgetwindowagent_win.cpp b/src/widgets/widgetwindowagent_win.cpp index dc35a17..9e49d38 100644 --- a/src/widgets/widgetwindowagent_win.cpp +++ b/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 } -- Gitblit v1.9.1