From 3d0d0c24adf5001be1c42b2fa86129b1b258cf70 Mon Sep 17 00:00:00 2001 From: Sine Striker <trueful@163.com> Date: 周一, 04 12月 2023 20:31:36 +0800 Subject: [PATCH] Fix NCHITTEST --- src/core/contexts/abstractwindowcontext.cpp | 7 + src/core/contexts/abstractwindowcontext_p.h | 4 src/core/CMakeLists.txt | 1 src/core/qwkcoreglobal.cpp | 74 ++++++++++++++++++ src/core/qwkcoreglobal_p.h | 12 ++ src/core/contexts/win32windowcontext.cpp | 91 ++++++++++++---------- 6 files changed, 144 insertions(+), 45 deletions(-) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index d5940f8..654a259 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -6,6 +6,7 @@ set(_src qwkcoreglobal.h qwkcoreglobal_p.h + qwkcoreglobal.cpp corewindowagent.h corewindowagent_p.h corewindowagent.cpp diff --git a/src/core/contexts/abstractwindowcontext.cpp b/src/core/contexts/abstractwindowcontext.cpp index a3d2bba..2e32d25 100644 --- a/src/core/contexts/abstractwindowcontext.cpp +++ b/src/core/contexts/abstractwindowcontext.cpp @@ -2,7 +2,12 @@ namespace QWK { - AbstractWindowContext::~AbstractWindowContext() = default; + AbstractWindowContext::AbstractWindowContext(QWindow *window, WindowItemDelegate *delegate) + : m_windowHandle(window), m_delegate(delegate) { + } + + AbstractWindowContext::~AbstractWindowContext() { + } void AbstractWindowContext::setupWindow(QWindow *window) { Q_ASSERT(window); diff --git a/src/core/contexts/abstractwindowcontext_p.h b/src/core/contexts/abstractwindowcontext_p.h index 54471fe..a92d91d 100644 --- a/src/core/contexts/abstractwindowcontext_p.h +++ b/src/core/contexts/abstractwindowcontext_p.h @@ -16,9 +16,7 @@ class QWK_CORE_EXPORT AbstractWindowContext : public QObject { Q_OBJECT public: - inline AbstractWindowContext(QWindow *window, WindowItemDelegate *delegate) - : m_windowHandle(window), m_delegate(delegate) { - } + AbstractWindowContext(QWindow *window, WindowItemDelegate *delegate); ~AbstractWindowContext() override; public: diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index 09c2b83..d3b34ec 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/src/core/contexts/win32windowcontext.cpp @@ -6,6 +6,7 @@ #include <QtCore/QAbstractNativeEventFilter> #include <QtCore/QCoreApplication> #include <QtCore/QOperatingSystemVersion> +#include <QtCore/QScopeGuard> #include <QtCore/private/qsystemlibrary_p.h> #include <QtGui/private/qhighdpiscaling_p.h> @@ -213,8 +214,7 @@ return (windowRect == mi.value_or(MONITORINFOEXW{}).rcMonitor); } - static inline bool isWindowNoState(HWND hwnd) - { + static inline bool isWindowNoState(HWND hwnd) { Q_ASSERT(hwnd); if (!hwnd) { return false; @@ -332,11 +332,11 @@ static inline void install() { instance = new WindowsNativeEventFilter(); - qApp->installNativeEventFilter(instance); + installNativeEventFilter(instance); } static inline void uninstall() { - qApp->removeNativeEventFilter(instance); + removeNativeEventFilter(instance); delete instance; instance = nullptr; } @@ -1093,39 +1093,44 @@ // color, our homemade top border can almost have exactly the same // appearance with the system's one. - const auto nativeGlobalPos = POINT{ GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + [[maybe_unused]] const auto &hitTestRecorder = qScopeGuard([this, result]() { + lastHitTestResult = getHitWindowPart(int(*result)); // + }); + + POINT nativeGlobalPos{GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; POINT nativeLocalPos = nativeGlobalPos; ::ScreenToClient(hWnd, &nativeLocalPos); - auto clientRect = RECT{ 0, 0, 0, 0 }; + RECT clientRect{0, 0, 0, 0}; ::GetClientRect(hWnd, &clientRect); - const auto clientWidth = RECT_WIDTH(clientRect); - const auto clientHeight = RECT_HEIGHT(clientRect); + auto clientWidth = RECT_WIDTH(clientRect); + auto clientHeight = RECT_HEIGHT(clientRect); - const QPoint qtScenePos = fromNativeLocalPosition(m_windowHandle, QPoint(nativeLocalPos.x, nativeLocalPos.y)); + QPoint qtScenePos = fromNativeLocalPosition( + m_windowHandle, QPoint(nativeLocalPos.x, nativeLocalPos.y)); - const bool isFixedSize = /*isWindowFixedSize()*/false; // ### FIXME - const bool isTitleBar = isInTitleBarDraggableArea(qtScenePos); - const bool dontOverrideCursor = false; // ### TODO + bool isFixedSize = /*isWindowFixedSize()*/ false; // ### FIXME + bool isTitleBar = isInTitleBarDraggableArea(qtScenePos); + bool dontOverrideCursor = false; // ### TODO CoreWindowAgent::SystemButton sysButtonType = CoreWindowAgent::Unknown; if (!isFixedSize && isInSystemButtons(qtScenePos, &sysButtonType)) { - // Firstly, we set the hit test result to a default value to be able to detect whether we - // have changed it or not afterwards. + // Firstly, we set the hit test result to a default value to be able to detect + // whether we have changed it or not afterwards. *result = HTNOWHERE; - // Even if the mouse is inside the chrome button area now, we should still allow the user - // to be able to resize the window with the top or right window border, this is also the - // normal behavior of a native Win32 window (but only when the window is not maximized/ - // fullscreened/minimized, of course). + // Even if the mouse is inside the chrome button area now, we should still allow + // the user to be able to resize the window with the top or right window border, + // this is also the normal behavior of a native Win32 window (but only when the + // window is not maximized/fullscreen/minimized, of course). if (isWindowNoState(hWnd)) { static constexpr const int kBorderSize = 2; - const bool isTop = (nativeLocalPos.y <= kBorderSize); - const bool isRight = (nativeLocalPos.x >= (clientWidth - kBorderSize)); + bool isTop = (nativeLocalPos.y <= kBorderSize); + bool isRight = (nativeLocalPos.x >= (clientWidth - kBorderSize)); if (isTop || isRight) { if (dontOverrideCursor) { - // The user doesn't want the window to be resized, so we tell Windows we are - // in the client area so that the controls beneath the mouse cursor can still - // be hovered or clicked. + // The user doesn't want the window to be resized, so we tell + // Windows we are in the client area so that the controls beneath + // the mouse cursor can still be hovered or clicked. *result = (isTitleBar ? HTCAPTION : HTCLIENT); } else { if (isTop && isRight) { @@ -1139,8 +1144,9 @@ } } if (*result == HTNOWHERE) { - // OK, we are now really inside one of the chrome buttons, tell Windows the exact role of our button. - // The Snap Layout feature introduced in Windows 11 won't work without this. + // OK, we are now really inside one of the chrome buttons, tell Windows the + // exact role of our button. The Snap Layout feature introduced in Windows + // 11 won't work without this. switch (sysButtonType) { case CoreWindowAgent::WindowIcon: *result = HTSYSMENU; @@ -1162,30 +1168,33 @@ } } if (*result == HTNOWHERE) { - // OK, it seems we are not inside the window resize area, nor inside the chrome buttons, - // tell Windows we are in the client area to let Qt handle this event. + // OK, it seems we are not inside the window resize area, nor inside the + // chrome buttons, tell Windows we are in the client area to let Qt handle + // this event. *result = HTCLIENT; } return true; } - // OK, we are not inside any chrome buttons, try to find out which part of the window - // are we hitting. + // OK, we are not inside any chrome buttons, try to find out which part of the + // window are we hitting. - const bool max = IsMaximized(hWnd); - const bool full = isFullScreen(hWnd); - const int frameSize = getResizeBorderThickness(hWnd); - const bool isTop = (nativeLocalPos.y < frameSize); + bool max = IsMaximized(hWnd); + bool full = isFullScreen(hWnd); + int frameSize = getResizeBorderThickness(hWnd); + bool isTop = (nativeLocalPos.y < frameSize); if (isWin10OrGreater()) { // This will handle the left, right and bottom parts of the frame // because we didn't change them. - const LRESULT originalHitTestResult = ::DefWindowProcW(hWnd, WM_NCHITTEST, 0, lParam); + LRESULT originalHitTestResult = ::DefWindowProcW(hWnd, WM_NCHITTEST, 0, lParam); if (originalHitTestResult != HTCLIENT) { - // Even if the window is not resizable, we still can't return HTCLIENT here because - // when we enter this code path, it means the mouse cursor is outside the window, - // that is, the three transparent window resize area. Returning HTCLIENT will confuse - // Windows, we can't put our controls there anyway. - *result = ((isFixedSize || dontOverrideCursor) ? HTBORDER : originalHitTestResult); + // Even if the window is not resizable, we still can't return HTCLIENT here + // because when we enter this code path, it means the mouse cursor is + // outside the window, that is, the three transparent window resize area. + // Returning HTCLIENT will confuse Windows, we can't put our controls there + // anyway. + *result = ((isFixedSize || dontOverrideCursor) ? HTBORDER + : originalHitTestResult); return true; } if (full) { @@ -1205,7 +1214,9 @@ // Return HTCLIENT instead of HTBORDER here, because the mouse is // inside our homemade title bar now, return HTCLIENT to let our // title bar can still capture mouse events. - *result = ((isFixedSize || dontOverrideCursor) ? (isTitleBar ? HTCAPTION : HTCLIENT) : HTTOP); + *result = ((isFixedSize || dontOverrideCursor) + ? (isTitleBar ? HTCAPTION : HTCLIENT) + : HTTOP); return true; } if (isTitleBar) { diff --git a/src/core/qwkcoreglobal.cpp b/src/core/qwkcoreglobal.cpp new file mode 100644 index 0000000..9047b5d --- /dev/null +++ b/src/core/qwkcoreglobal.cpp @@ -0,0 +1,74 @@ +#include "qwkcoreglobal_p.h" + +#include <QtCore/QAbstractNativeEventFilter> +#include <QtCore/QCoreApplication> +#include <QtCore/QSet> + +namespace QWK { + + // Avoid adding multiple global native event filters to QCoreApplication + // in this library. + class NativeEventFilterManager : public QAbstractNativeEventFilter { + public: + NativeEventFilterManager() { + qApp->installNativeEventFilter(this); + } + + ~NativeEventFilterManager() override { + qApp->removeNativeEventFilter(this); + } + + bool nativeEventFilter(const QByteArray &eventType, void *message, + QT_NATIVE_EVENT_RESULT_TYPE *result) override { + for (const auto &child : qAsConst(m_children)) { + if (child->nativeEventFilter(eventType, message, result)) { + return true; + } + } + return false; + } + + inline int count() const { + return m_children.size(); + } + + inline void addChild(QAbstractNativeEventFilter *child) { + m_children.append(child); + } + + inline void removeChild(QAbstractNativeEventFilter *child) { + m_children.removeOne(child); + } + + static NativeEventFilterManager *instance; + + protected: + QVector<QAbstractNativeEventFilter *> m_children; + }; + + NativeEventFilterManager *NativeEventFilterManager::instance = nullptr; + + void installNativeEventFilter(QAbstractNativeEventFilter *eventFilter) { + if (!eventFilter) { + return; + } + + if (!NativeEventFilterManager::instance) { + NativeEventFilterManager::instance = new NativeEventFilterManager(); + } + NativeEventFilterManager::instance->addChild(eventFilter); + } + + void removeNativeEventFilter(QAbstractNativeEventFilter *eventFilter) { + if (!eventFilter || !NativeEventFilterManager::instance) { + return; + } + NativeEventFilterManager::instance->removeChild(eventFilter); + + if (NativeEventFilterManager::instance->count() == 0) { + delete NativeEventFilterManager::instance; + NativeEventFilterManager::instance = nullptr; + } + } + +} \ No newline at end of file diff --git a/src/core/qwkcoreglobal_p.h b/src/core/qwkcoreglobal_p.h index 52e9653..d18b54f 100644 --- a/src/core/qwkcoreglobal_p.h +++ b/src/core/qwkcoreglobal_p.h @@ -3,6 +3,7 @@ #include <QtCore/QEvent> #include <QtCore/QLoggingCategory> +#include <QtCore/QAbstractNativeEventFilter> #include <QtGui/QtEvents> #include <QWKCore/qwkcoreglobal.h> @@ -22,7 +23,16 @@ #define QWK_WARNING qCWarning(qWindowKitLog) #define QWK_CRITICAL qCCritical(qWindowKitLog) #if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) -# define QWK_FATAL qCFatal(qWindowKitLog) +# define QWK_FATAL qCFatal(qWindowKitLog) #endif + +namespace QWK { + + QWK_CORE_EXPORT void installNativeEventFilter(QAbstractNativeEventFilter *eventFilter); + + QWK_CORE_EXPORT void removeNativeEventFilter(QAbstractNativeEventFilter *eventFilter); + +} + #endif // QWKCOREGLOBAL_P_H -- Gitblit v1.9.1