From 2c8bd8ce8c3cb8826347e3959538ee0ddc609ee1 Mon Sep 17 00:00:00 2001 From: Yuhang Zhao Date: Fri, 18 Aug 2023 17:25:13 +0800 Subject: [PATCH] win: minor improvements --- include/FramelessHelper/Core/utils.h | 2 +- src/core/framelesshelper_win.cpp | 14 ++- src/core/utils.cpp | 120 +++++++++++++++++++------ src/quick/framelessquickhelper.cpp | 11 +-- src/widgets/framelesswidgetshelper.cpp | 2 +- 5 files changed, 107 insertions(+), 42 deletions(-) diff --git a/include/FramelessHelper/Core/utils.h b/include/FramelessHelper/Core/utils.h index 3e0535c5..ddbbab0d 100644 --- a/include/FramelessHelper/Core/utils.h +++ b/include/FramelessHelper/Core/utils.h @@ -91,7 +91,7 @@ FRAMELESSHELPER_CORE_API void registerThemeChangeNotification(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowTransparent(const QWindow *window); FRAMELESSHELPER_CORE_API void emulateQtMouseEvent( const QObject *target, const QWindow *window, const Global::ButtonState buttonState, - const QPoint &globalPos, const QPoint &scenePos, const QPoint &localPos, const bool underMouse); + const QPoint &globalPos, const QPoint &scenePos, const QPoint &localPos); #ifdef Q_OS_WINDOWS [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowsVersionOrGreater(const Global::WindowsVersion version); diff --git a/src/core/framelesshelper_win.cpp b/src/core/framelesshelper_win.cpp index a7f68241..2e1eae54 100644 --- a/src/core/framelesshelper_win.cpp +++ b/src/core/framelesshelper_win.cpp @@ -315,9 +315,11 @@ Q_GLOBAL_STATIC(FramelessWin32HelperInternal, g_framelessWin32HelperData) // top resize border. switch (wParam) { case HTTOP: - case HTCAPTION: + case HTCAPTION: { + releaseButtons(std::nullopt); // Pass caption-related nonclient messages to the parent window. return SendMessageW(parentWindowHandle, uMsg, wParam, lParam); + } // The buttons won't work as you'd expect; we need to handle those // ourselves. case HTSYSMENU: @@ -336,6 +338,7 @@ Q_GLOBAL_STATIC(FramelessWin32HelperInternal, g_framelessWin32HelperData) pressButton(SystemButtonType::Close); break; default: + releaseButtons(std::nullopt); break; } return 0; @@ -348,9 +351,11 @@ Q_GLOBAL_STATIC(FramelessWin32HelperInternal, g_framelessWin32HelperData) // to the root HWND. switch (wParam) { case HTTOP: - case HTCAPTION: + case HTCAPTION: { + releaseButtons(std::nullopt); // Pass caption-related nonclient messages to the parent window. return SendMessageW(parentWindowHandle, uMsg, wParam, lParam); + } // The buttons won't work as you'd expect; we need to handle those ourselves. case HTSYSMENU: clickButton(SystemButtonType::WindowIcon); @@ -368,6 +373,7 @@ Q_GLOBAL_STATIC(FramelessWin32HelperInternal, g_framelessWin32HelperData) clickButton(SystemButtonType::Close); break; default: + releaseButtons(std::nullopt); break; } return 0; @@ -376,8 +382,10 @@ Q_GLOBAL_STATIC(FramelessWin32HelperInternal, g_framelessWin32HelperData) // - we don't need to handle these. case WM_NCRBUTTONDOWN: case WM_NCRBUTTONDBLCLK: - case WM_NCRBUTTONUP: + case WM_NCRBUTTONUP: { + releaseButtons(std::nullopt); return SendMessageW(parentWindowHandle, uMsg, wParam, lParam); + } default: break; } diff --git a/src/core/utils.cpp b/src/core/utils.cpp index 2194555c..5de80b10 100644 --- a/src/core/utils.cpp +++ b/src/core/utils.cpp @@ -631,52 +631,118 @@ bool Utils::isWindowTransparent(const QWindow *window) return window->format().hasAlpha(); } -void Utils::emulateQtMouseEvent(const QObject *target, const QWindow *window, const ButtonState buttonState, - const QPoint &globalPos, const QPoint &scenePos, const QPoint &localPos, const bool underMouse) +void Utils::emulateQtMouseEvent(const QObject *target, const QWindow *window, + const ButtonState buttonState, const QPoint &globalPos, const QPoint &scenePos, const QPoint &localPos) { Q_ASSERT(target); Q_ASSERT(window); if (!target || !window) { return; } - const auto object = const_cast(target); - const auto candidateObject = const_cast(window); + const auto targetObj = const_cast(target); + const auto windowObj = static_cast(const_cast(window)); + const bool isWidget = target->isWidgetType(); + const bool mouseTrackingEnabled = (isWidget ? target->property("mouseTracking").toBool() : true); static constexpr const QPoint oldPos = {}; // Not needed. static constexpr const Qt::MouseButton button = Qt::LeftButton; const Qt::MouseButtons buttons = QGuiApplication::mouseButtons(); const Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers(); - switch (buttonState) { - case ButtonState::Normal: { + static constexpr const char kEnteredFlag[] = "__FRAMELESSHELPER_WIDGET_ITEM_ENTERED"; + const bool entered = target->property(kEnteredFlag).toBool(); + const auto sendEnterEvent = [&localPos, &scenePos, &globalPos, &modifiers](QObject *obj) -> void { + Q_ASSERT(obj); + if (!obj) { + return; + } +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QEnterEvent enterEvent(localPos, scenePos, globalPos); +#else + QEvent enterEvent(QEvent::Enter); +#endif + QHoverEvent hoverEnterEvent(QEvent::HoverEnter, scenePos, globalPos, oldPos, modifiers); + QCoreApplication::sendEvent(obj, &enterEvent); + QCoreApplication::sendEvent(obj, &hoverEnterEvent); + }; + const auto sendLeaveEvent = [&scenePos, &globalPos, &modifiers](QObject *obj) -> void { + Q_ASSERT(obj); + if (!obj) { + return; + } QEvent leaveEvent(QEvent::Leave); QHoverEvent hoverLeaveEvent(QEvent::HoverLeave, scenePos, globalPos, oldPos, modifiers); - QCoreApplication::sendEvent(object, &leaveEvent); - QCoreApplication::sendEvent(object, &hoverLeaveEvent); + QCoreApplication::sendEvent(obj, &leaveEvent); + QCoreApplication::sendEvent(obj, &hoverLeaveEvent); + }; + const auto sendMouseMoveEvent = [&localPos, &scenePos, &globalPos, &buttons, &modifiers](QObject *obj) -> void { + Q_ASSERT(obj); + if (!obj) { + return; + } + QMouseEvent event(QEvent::MouseMove, localPos, scenePos, globalPos, Qt::NoButton, buttons, modifiers); + QCoreApplication::sendEvent(obj, &event); + }; + const auto sendHoverMoveEvent = [&scenePos, &globalPos, &modifiers](QObject *obj) -> void { + Q_ASSERT(obj); + if (!obj) { + return; + } + QHoverEvent event(QEvent::HoverMove, scenePos, globalPos, oldPos, modifiers); + QCoreApplication::sendEvent(obj, &event); + }; + const auto sendMousePressEvent = [&localPos, &scenePos, &globalPos, &buttons, &modifiers](QObject *obj) -> void { + Q_ASSERT(obj); + if (!obj) { + return; + } + QMouseEvent event(QEvent::MouseButtonPress, localPos, scenePos, globalPos, button, buttons, modifiers); + QCoreApplication::sendEvent(obj, &event); + }; + const auto sendMouseReleaseEvent = [&localPos, &scenePos, &globalPos, &buttons, &modifiers](QObject *obj) -> void { + Q_ASSERT(obj); + if (!obj) { + return; + } + QMouseEvent event(QEvent::MouseButtonRelease, localPos, scenePos, globalPos, button, buttons, modifiers); + QCoreApplication::sendEvent(obj, &event); + }; + switch (buttonState) { + case ButtonState::Normal: { + targetObj->setProperty(kEnteredFlag, {}); +// sendMouseReleaseEvent(windowObj); +// if (isWidget) { +// sendMouseReleaseEvent(targetObj); +// } + sendLeaveEvent(windowObj); + if (isWidget) { + sendLeaveEvent(targetObj); + } } break; case ButtonState::Hovered: { - const auto receiver = (target->isWidgetType() ? object : static_cast(candidateObject)); - if (underMouse) { - QMouseEvent mouseMoveEvent(QEvent::MouseMove, localPos, scenePos, globalPos, Qt::NoButton, buttons, modifiers); - QHoverEvent hoverMoveEvent(QEvent::HoverMove, scenePos, globalPos, oldPos, modifiers); - QCoreApplication::sendEvent(receiver, &mouseMoveEvent); - QCoreApplication::sendEvent(receiver, &hoverMoveEvent); - } else { -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - QEnterEvent enterEvent(localPos, scenePos, globalPos); -#else - QEvent enterEvent(QEvent::Enter); -#endif - QHoverEvent hoverEnterEvent(QEvent::HoverEnter, scenePos, globalPos, oldPos, modifiers); - QCoreApplication::sendEvent(receiver, &enterEvent); - QCoreApplication::sendEvent(receiver, &hoverEnterEvent); + if (!entered) { + targetObj->setProperty(kEnteredFlag, true); + sendEnterEvent(windowObj); + if (isWidget) { + sendEnterEvent(targetObj); + } + } + sendHoverMoveEvent(windowObj); + if (isWidget) { + sendHoverMoveEvent(targetObj); + } + if (mouseTrackingEnabled) { + sendMouseMoveEvent(windowObj); + if (isWidget) { + sendMouseMoveEvent(targetObj); + } } } break; case ButtonState::Pressed: { - QMouseEvent event(QEvent::MouseButtonPress, localPos, scenePos, globalPos, button, buttons, modifiers); - QCoreApplication::sendEvent(object, &event); + sendMousePressEvent(windowObj); + sendMousePressEvent(targetObj); } break; case ButtonState::Released: { - QMouseEvent event(QEvent::MouseButtonRelease, localPos, scenePos, globalPos, button, buttons, modifiers); - QCoreApplication::sendEvent(object, &event); + sendMouseReleaseEvent(windowObj); + sendMouseReleaseEvent(targetObj); } break; } } diff --git a/src/quick/framelessquickhelper.cpp b/src/quick/framelessquickhelper.cpp index b8a2cf9c..8b2f8495 100644 --- a/src/quick/framelessquickhelper.cpp +++ b/src/quick/framelessquickhelper.cpp @@ -903,16 +903,7 @@ void FramelessQuickHelperPrivate::setSystemButtonState(const QuickGlobal::System const QPoint globalPos = (screen ? QCursor::pos(screen) : QCursor::pos()); const QPoint localPos = btn->mapFromGlobal(globalPos).toPoint(); const QPoint scenePos = window->mapFromGlobal(globalPos); - const auto underMouse = [btn, &globalPos]() -> bool { - const QPointF originPoint = btn->mapToGlobal(QPointF{ 0, 0 }); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) - const QSizeF size = btn->size(); -#else - const auto size = QSizeF{ btn->width(), btn->height() }; -#endif - return QRectF{ originPoint, size }.contains(globalPos); - }(); - Utils::emulateQtMouseEvent(btn, window, FRAMELESSHELPER_ENUM_QUICK_TO_CORE(ButtonState, state), globalPos, scenePos, localPos, underMouse); + Utils::emulateQtMouseEvent(btn, window, FRAMELESSHELPER_ENUM_QUICK_TO_CORE(ButtonState, state), globalPos, scenePos, localPos); }; updateButtonState(quickButton); #endif // FRAMELESSHELPER_QUICK_NO_PRIVATE diff --git a/src/widgets/framelesswidgetshelper.cpp b/src/widgets/framelesswidgetshelper.cpp index ebccbddb..a4580346 100644 --- a/src/widgets/framelesswidgetshelper.cpp +++ b/src/widgets/framelesswidgetshelper.cpp @@ -863,7 +863,7 @@ void FramelessWidgetsHelperPrivate::setSystemButtonState(const SystemButtonType const QPoint globalPos = (screen ? QCursor::pos(screen) : QCursor::pos()); const QPoint localPos = btn->mapFromGlobal(globalPos); const QPoint scenePos = window->mapFromGlobal(globalPos); - Utils::emulateQtMouseEvent(btn, window->windowHandle(), state, globalPos, scenePos, localPos, btn->underMouse()); + Utils::emulateQtMouseEvent(btn, window->windowHandle(), state, globalPos, scenePos, localPos); }; updateButtonState(widgetButton); }