From a0bede6ff6a700a6eea9702c49d378f07de22f63 Mon Sep 17 00:00:00 2001 From: Sine Striker <trueful@163.com> Date: 周三, 06 12月 2023 12:11:37 +0800 Subject: [PATCH] Fix title bar hover bug --- src/core/contexts/win32windowcontext.cpp | 101 +++++++++++++++++++++++++++++++++++++++++++++----- 1 files changed, 90 insertions(+), 11 deletions(-) diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index dcb6eb8..8800a7f 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/src/core/contexts/win32windowcontext.cpp @@ -1,20 +1,31 @@ #include "win32windowcontext_p.h" +#include "qwkcoreglobal_p.h" #include <optional> #include <QtCore/QHash> #include <QtCore/QAbstractNativeEventFilter> -#include <QtCore/QCoreApplication> #include <QtCore/QOperatingSystemVersion> #include <QtCore/QScopeGuard> +#include <QtCore/QTimer> +#include <QtGui/QGuiApplication> #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 { @@ -25,6 +36,12 @@ Q_GLOBAL_STATIC(WndProcHash, g_wndProcHash) 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; @@ -184,7 +201,7 @@ 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 @@ -217,6 +234,55 @@ } 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) { @@ -345,11 +411,14 @@ public: bool nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result) override { + Q_UNUSED(eventType); + // It has been observed that the pointer that Qt gives us is sometimes null on some // machines. We need to guard against it in such scenarios. if (!result) { return false; } + if (lastMessageHandled) { *result = static_cast<QT_NATIVE_EVENT_RESULT_TYPE>(lastMessageResult); return true; @@ -450,8 +519,11 @@ // TODO: Determine whether to show system menu // ... - // Since Qt does the necessary processing of the message afterward, we still need to + // Since Qt does the necessary processing of the WM_NCCALCSIZE afterward, we still need to // continue dispatching it. + if (message != WM_NCCALCSIZE && handled) { + return result; + } return ::CallWindowProcW(g_qtWindowProc, hWnd, message, wParam, lParam); } @@ -480,6 +552,9 @@ 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) { g_qtWindowProc = reinterpret_cast<WNDPROC>(::GetWindowLongPtrW(hWnd, GWLP_WNDPROC)); @@ -498,6 +573,8 @@ // Save window handle mapping g_wndProcHash->insert(hWnd, this); + + QTimer::singleShot(0, m_windowHandle, [hWnd]() { triggerFrameChange(hWnd); }); return true; } @@ -691,9 +768,10 @@ // actually lost the hover state. So we filter out these superfluous mouse leave // events here to avoid this issue. DWORD dwScreenPos = ::GetMessagePos(); + POINT screenPoint{GET_X_LPARAM(dwScreenPos), GET_Y_LPARAM(dwScreenPos)}; + ::ScreenToClient(hWnd, &screenPoint); QPoint qtScenePos = - fromNativeLocalPosition(m_windowHandle, QPoint(GET_X_LPARAM(dwScreenPos), - GET_Y_LPARAM(dwScreenPos))); + fromNativeLocalPosition(m_windowHandle, {screenPoint.x, screenPoint.y}); auto dummy = CoreWindowAgent::Unknown; if (isInSystemButtons(qtScenePos, &dummy)) { // We must record whether the last WM_MOUSELEAVE was filtered, because if @@ -1143,9 +1221,9 @@ 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 + bool dontOverrideCursor = false; // ### TODO CoreWindowAgent::SystemButton sysButtonType = CoreWindowAgent::Unknown; if (!isFixedSize && isInSystemButtons(qtScenePos, &sysButtonType)) { @@ -1260,6 +1338,7 @@ return true; } *result = HTCLIENT; + return true; } else { if (full) { *result = HTCLIENT; @@ -1321,8 +1400,8 @@ return true; } *result = HTCLIENT; + return true; } - return true; } default: break; @@ -1390,4 +1469,4 @@ return false; } -} \ No newline at end of file +} -- Gitblit v1.9.1