From 8521bbf2f335984542f2071825d7383548862b7a Mon Sep 17 00:00:00 2001 From: Yuhang Zhao <zhaoyuhang@rankyee.com> Date: 周三, 06 12月 2023 10:46:55 +0800 Subject: [PATCH] minor tweaks --- src/core/contexts/win32windowcontext.cpp | 186 ++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 176 insertions(+), 10 deletions(-) diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index d3b34ec..2f16af9 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/src/core/contexts/win32windowcontext.cpp @@ -1,4 +1,5 @@ #include "win32windowcontext_p.h" +#include "qwkcoreglobal_p.h" #include <optional> @@ -7,14 +8,24 @@ #include <QtCore/QCoreApplication> #include <QtCore/QOperatingSystemVersion> #include <QtCore/QScopeGuard> +#include <QtCore/QTimer> #include <QtCore/private/qsystemlibrary_p.h> #include <QtGui/private/qhighdpiscaling_p.h> - -#include "qwkcoreglobal_p.h" +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +# include <QtGui/private/qguiapplication_p.h> +#endif +#include <QtGui/qpa/qplatformwindow.h> +#if QT_VERSION < QT_VERSION_CHECK(6, 2, 0) +# include <QtGui/qpa/qplatformnativeinterface.h> +#else +# include <QtGui/qpa/qplatformwindow_p.h> +#endif #include <shellscalingapi.h> #include <dwmapi.h> + +Q_DECLARE_METATYPE(QMargins) namespace QWK { @@ -26,8 +37,15 @@ static WNDPROC g_qtWindowProc = nullptr; // Original Qt window proc function + static struct QWK_Hook { + QWK_Hook() { + qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); + } + } g_hook{}; + struct DynamicApis { decltype(&::DwmFlush) pDwmFlush = nullptr; + decltype(&::DwmIsCompositionEnabled) pDwmIsCompositionEnabled = nullptr; decltype(&::GetDpiForWindow) pGetDpiForWindow = nullptr; decltype(&::GetSystemMetricsForDpi) pGetSystemMetricsForDpi = nullptr; decltype(&::GetDpiForMonitor) pGetDpiForMonitor = nullptr; @@ -45,6 +63,8 @@ QSystemLibrary dwmapi(QStringLiteral("dwmapi.dll")); pDwmFlush = reinterpret_cast<decltype(pDwmFlush)>(dwmapi.resolve("DwmFlush")); + pDwmIsCompositionEnabled = reinterpret_cast<decltype(pDwmIsCompositionEnabled)>( + dwmapi.resolve("DwmIsCompositionEnabled")); } ~DynamicApis() = default; @@ -138,6 +158,12 @@ return hwnd2str(reinterpret_cast<WId>(hwnd)); } + static inline bool isWin8OrGreater() { + static const bool result = + QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8; + return result; + } + static inline bool isWin8Point1OrGreater() { static const bool result = QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8_1; @@ -150,10 +176,32 @@ return result; } + static inline bool isDwmCompositionEnabled() { + if (isWin8OrGreater()) { + return true; + } + const DynamicApis &apis = DynamicApis::instance(); + if (!apis.pDwmIsCompositionEnabled) { + return false; + } + BOOL enabled = FALSE; + return SUCCEEDED(apis.pDwmIsCompositionEnabled(&enabled)) && enabled; + } + + static inline void triggerFrameChange(HWND hwnd) { + Q_ASSERT(hwnd); + if (!hwnd) { + return; + } + ::SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | + SWP_FRAMECHANGED); + } + static inline quint32 getDpiForWindow(HWND hwnd) { Q_ASSERT(hwnd); if (!hwnd) { - return USER_DEFAULT_SCREEN_DPI; + return 0; } const DynamicApis &apis = DynamicApis::instance(); if (apis.pGetDpiForWindow) { // Win10 @@ -186,6 +234,53 @@ } else { return ::GetSystemMetrics(SM_CXSIZEFRAME) + ::GetSystemMetrics(SM_CXPADDEDBORDER); } + } + + static inline quint32 getTitleBarHeight(HWND hwnd) { + Q_ASSERT(hwnd); + if (!hwnd) { + return 0; + } + const auto captionHeight = [hwnd]() -> int { + const DynamicApis &apis = DynamicApis::instance(); + if (apis.pGetSystemMetricsForDpi) { + const quint32 dpi = getDpiForWindow(hwnd); + return apis.pGetSystemMetricsForDpi(SM_CYCAPTION, dpi); + } else { + return ::GetSystemMetrics(SM_CYCAPTION); + } + }(); + return captionHeight + getResizeBorderThickness(hwnd); + } + + static inline void updateInternalWindowFrameMargins(HWND hwnd, QWindow *window) { + Q_ASSERT(hwnd); + Q_ASSERT(window); + if (!hwnd || !window) { + return; + } + const auto margins = [hwnd]() -> QMargins { + const int titleBarHeight = getTitleBarHeight(hwnd); + if (isWin10OrGreater()) { + return { 0, -titleBarHeight, 0, 0 }; + } else { + const int frameSize = getResizeBorderThickness(hwnd); + return { -frameSize, -titleBarHeight, -frameSize, -frameSize }; + } + }(); + const QVariant marginsVar = QVariant::fromValue(margins); + window->setProperty("_q_windowsCustomMargins", marginsVar); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + if (QPlatformWindow *platformWindow = window->handle()) { + if (const auto ni = QGuiApplication::platformNativeInterface()) { + ni->setWindowProperty(platformWindow, QStringLiteral("_q_windowsCustomMargins"), marginsVar); + } + } +#else + if (const auto platformWindow = dynamic_cast<QNativeInterface::Private::QWindowsWindow *>(window->handle())) { + platformWindow->setCustomMargins(margins); + } +#endif } static inline std::optional<MONITORINFOEXW> getMonitorForWindow(HWND hwnd) { @@ -266,7 +361,7 @@ case HTBORDER: return Win32WindowContext::FixedBorder; default: - break; + break; // unreachable } return Win32WindowContext::Outside; } @@ -424,8 +519,8 @@ return ::CallWindowProcW(g_qtWindowProc, hWnd, message, wParam, lParam); } - Win32WindowContext::Win32WindowContext(QWindow *window, WindowItemDelegate *delegate) - : AbstractWindowContext(window, delegate) { + Win32WindowContext::Win32WindowContext(QObject *host, WindowItemDelegate *delegate) + : AbstractWindowContext(host, delegate) { } Win32WindowContext::~Win32WindowContext() { @@ -441,10 +536,16 @@ } bool Win32WindowContext::setup() { - auto winId = m_windowHandle->winId(); + if (!m_windowHandle) { + return false; + } // Install window hook + auto winId = m_windowHandle->winId(); auto hWnd = reinterpret_cast<HWND>(winId); + + // Inform Qt we want and have set custom margins + updateInternalWindowFrameMargins(hWnd, m_windowHandle); // Store original window proc if (!g_qtWindowProc) { @@ -464,6 +565,8 @@ // Save window handle mapping g_wndProcHash->insert(hWnd, this); + + QTimer::singleShot(0, m_windowHandle, [hWnd](){ triggerFrameChange(hWnd); }); return true; } @@ -1087,7 +1190,7 @@ // window can behave just like a normal Win32 window even if we now // doesn't have a title bar at all. Most importantly, the flicker and // jitter during window resizing is totally gone now. The disadvantage - // is we have to draw a top frame border ourself. Previously I thought + // is we have to draw a top frame border ourselves. Previously I thought // we have to do the black magic in WM_PAINT just like what Windows // Terminal does, however, later I found that if we choose a proper // color, our homemade top border can almost have exactly the same @@ -1109,7 +1212,7 @@ QPoint qtScenePos = fromNativeLocalPosition( m_windowHandle, QPoint(nativeLocalPos.x, nativeLocalPos.y)); - bool isFixedSize = /*isWindowFixedSize()*/ false; // ### FIXME + bool isFixedSize = m_delegate->isHostSizeFixed(m_host); bool isTitleBar = isInTitleBarDraggableArea(qtScenePos); bool dontOverrideCursor = false; // ### TODO @@ -1165,6 +1268,8 @@ break; case CoreWindowAgent::Unknown: break; + default: + break; // unreachable } } if (*result == HTNOWHERE) { @@ -1224,6 +1329,7 @@ return true; } *result = HTCLIENT; + return true; } else { if (full) { *result = HTCLIENT; @@ -1285,12 +1391,72 @@ return true; } *result = HTCLIENT; + return true; } - return true; } default: break; } + if (!isWin10OrGreater()) { + switch (message) { + case WM_NCUAHDRAWCAPTION: + case WM_NCUAHDRAWFRAME: { + // These undocumented messages are sent to draw themed window + // borders. Block them to prevent drawing borders over the client + // area. + *result = FALSE; + return true; + } + case WM_NCPAINT: { + // 杈规闃村奖澶勪簬闈炲鎴峰尯鐨勮寖鍥达紝鍥犳濡傛灉鐩存帴闃绘闈炲鎴峰尯鐨勭粯鍒讹紝浼氬鑷磋竟妗嗛槾褰变涪澶� + + if (!isDwmCompositionEnabled()) { + // Only block WM_NCPAINT when DWM composition is disabled. If + // it's blocked when DWM composition is enabled, the frame + // shadow won't be drawn. + *result = FALSE; + return true; + } else { + break; + } + } + case WM_NCACTIVATE: { + if (isDwmCompositionEnabled()) { + // DefWindowProc won't repaint the window border if lParam (normally a HRGN) + // is -1. See the following link's "lParam" section: + // https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-ncactivate + // Don't use "*result = 0" here, otherwise the window won't respond to the + // window activation state change. + *result = ::DefWindowProcW(hWnd, WM_NCACTIVATE, wParam, -1); + } else { + if (wParam == FALSE) { + *result = TRUE; + } else { + *result = FALSE; + } + } + return true; + } + case WM_SETICON: + case WM_SETTEXT: { + // Disable painting while these messages are handled to prevent them + // from drawing a window caption over the client area. + const auto oldStyle = static_cast<DWORD>(::GetWindowLongPtrW(hWnd, GWL_STYLE)); + // Prevent Windows from drawing the default title bar by temporarily + // toggling the WS_VISIBLE style. + const DWORD newStyle = (oldStyle & ~WS_VISIBLE); + ::SetWindowLongPtrW(hWnd, GWL_STYLE, static_cast<LONG_PTR>(newStyle)); + triggerFrameChange(hWnd); + const LRESULT originalResult = ::DefWindowProcW(hWnd, message, wParam, lParam); + ::SetWindowLongPtrW(hWnd, GWL_STYLE, static_cast<LONG_PTR>(oldStyle)); + triggerFrameChange(hWnd); + *result = originalResult; + return true; + } + default: + break; + } + } return false; } -- Gitblit v1.9.1