diff --git a/include/FramelessHelper/Core/utils.h b/include/FramelessHelper/Core/utils.h index ddbbab0d..ed7c810e 100644 --- a/include/FramelessHelper/Core/utils.h +++ b/include/FramelessHelper/Core/utils.h @@ -89,9 +89,11 @@ FRAMELESSHELPER_CORE_API void registerThemeChangeNotification(); [[nodiscard]] FRAMELESSHELPER_CORE_API quint32 defaultScreenDpi(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowAccelerated(const QWindow *window); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowTransparent(const QWindow *window); +[[nodiscard]] FRAMELESSHELPER_CORE_API Qt::MouseButtons queryMouseButtons(); 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 QPoint &globalPos, const QPoint &scenePos, const QPoint &localPos, + const bool underMouse, const bool hoverEnabled); #ifdef Q_OS_WINDOWS [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowsVersionOrGreater(const Global::WindowsVersion version); diff --git a/src/core/utils.cpp b/src/core/utils.cpp index 5de80b10..31318ff2 100644 --- a/src/core/utils.cpp +++ b/src/core/utils.cpp @@ -631,8 +631,8 @@ 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) +void Utils::emulateQtMouseEvent(const QObject *target, const QWindow *window, const ButtonState buttonState, + const QPoint &globalPos, const QPoint &scenePos, const QPoint &localPos, const bool underMouse, const bool enableHover) { Q_ASSERT(target); Q_ASSERT(window); @@ -642,14 +642,17 @@ void Utils::emulateQtMouseEvent(const QObject *target, const QWindow *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 char kMouseTrackingProp[] = "mouseTracking"; + const bool mouseTrackingEnabled = (isWidget ? target->property(kMouseTrackingProp).toBool() : true); + const bool hoverEnabled = (isWidget ? enableHover : 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(); - static constexpr const char kEnteredFlag[] = "__FRAMELESSHELPER_WIDGET_ITEM_ENTERED"; + static constexpr const char kEnteredFlag[] = "__FRAMELESSHELPER_WIDGET_QUICKITEM_ENTERED"; const bool entered = target->property(kEnteredFlag).toBool(); - const auto sendEnterEvent = [&localPos, &scenePos, &globalPos, &modifiers](QObject *obj) -> void { + const bool leftButtonPressed = (queryMouseButtons() & Qt::LeftButton); + const auto sendEnterEvent = [&localPos, &scenePos, &globalPos, &modifiers, hoverEnabled](QObject *obj) -> void { Q_ASSERT(obj); if (!obj) { return; @@ -659,33 +662,41 @@ void Utils::emulateQtMouseEvent(const QObject *target, const QWindow *window, #else QEvent enterEvent(QEvent::Enter); #endif - QHoverEvent hoverEnterEvent(QEvent::HoverEnter, scenePos, globalPos, oldPos, modifiers); QCoreApplication::sendEvent(obj, &enterEvent); - QCoreApplication::sendEvent(obj, &hoverEnterEvent); + if (hoverEnabled) { + QHoverEvent hoverEnterEvent(QEvent::HoverEnter, scenePos, globalPos, oldPos, modifiers); + QCoreApplication::sendEvent(obj, &hoverEnterEvent); + } }; - const auto sendLeaveEvent = [&scenePos, &globalPos, &modifiers](QObject *obj) -> void { + const auto sendLeaveEvent = [&scenePos, &globalPos, &modifiers, hoverEnabled](QObject *obj) -> void { Q_ASSERT(obj); if (!obj) { return; } QEvent leaveEvent(QEvent::Leave); - QHoverEvent hoverLeaveEvent(QEvent::HoverLeave, scenePos, globalPos, oldPos, modifiers); QCoreApplication::sendEvent(obj, &leaveEvent); - QCoreApplication::sendEvent(obj, &hoverLeaveEvent); + if (hoverEnabled) { + QHoverEvent hoverLeaveEvent(QEvent::HoverLeave, scenePos, globalPos, oldPos, modifiers); + QCoreApplication::sendEvent(obj, &hoverLeaveEvent); + } }; - const auto sendMouseMoveEvent = [&localPos, &scenePos, &globalPos, &buttons, &modifiers](QObject *obj) -> void { + const auto sendMouseMoveEvent = [&localPos, &scenePos, &globalPos, &buttons, &modifiers, leftButtonPressed](QObject *obj) -> void { Q_ASSERT(obj); if (!obj) { return; } - QMouseEvent event(QEvent::MouseMove, localPos, scenePos, globalPos, Qt::NoButton, buttons, modifiers); + const Qt::MouseButton actualButton = (leftButtonPressed ? button : Qt::NoButton); + QMouseEvent event(QEvent::MouseMove, localPos, scenePos, globalPos, actualButton, buttons, modifiers); QCoreApplication::sendEvent(obj, &event); }; - const auto sendHoverMoveEvent = [&scenePos, &globalPos, &modifiers](QObject *obj) -> void { + const auto sendHoverMoveEvent = [&scenePos, &globalPos, &modifiers, hoverEnabled](QObject *obj) -> void { Q_ASSERT(obj); if (!obj) { return; } + if (!hoverEnabled) { + return; + } QHoverEvent event(QEvent::HoverMove, scenePos, globalPos, oldPos, modifiers); QCoreApplication::sendEvent(obj, &event); }; @@ -697,53 +708,59 @@ void Utils::emulateQtMouseEvent(const QObject *target, const QWindow *window, 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 { + const auto sendMouseReleaseEvent = [&localPos, &scenePos, &globalPos, &buttons, &modifiers](QObject *obj, const bool fake = false) -> void { Q_ASSERT(obj); if (!obj) { return; } - QMouseEvent event(QEvent::MouseButtonRelease, localPos, scenePos, globalPos, button, buttons, modifiers); + static constexpr const auto fakePos = QPoint{ -999, -999 }; + const QPoint tweakedLocalPos = (fake ? fakePos : localPos); + const QPoint tweakedScenePos = (fake ? fakePos : scenePos); + const QPoint tweakedGlobalPos = (fake ? fakePos : globalPos); + QMouseEvent event(QEvent::MouseButtonRelease, tweakedLocalPos, tweakedScenePos, tweakedGlobalPos, button, buttons, modifiers); QCoreApplication::sendEvent(obj, &event); }; switch (buttonState) { case ButtonState::Normal: { targetObj->setProperty(kEnteredFlag, {}); -// sendMouseReleaseEvent(windowObj); -// if (isWidget) { -// sendMouseReleaseEvent(targetObj); -// } - sendLeaveEvent(windowObj); + // Send an extra mouse release event to let the control dismiss it's hover state. + sendMouseReleaseEvent(targetObj, true); if (isWidget) { sendLeaveEvent(targetObj); + } else { + sendLeaveEvent(windowObj); } } break; case ButtonState::Hovered: { if (!entered) { targetObj->setProperty(kEnteredFlag, true); - sendEnterEvent(windowObj); if (isWidget) { sendEnterEvent(targetObj); + } else { + sendEnterEvent(windowObj); } } - sendHoverMoveEvent(windowObj); if (isWidget) { sendHoverMoveEvent(targetObj); + } else { + sendHoverMoveEvent(windowObj); } - if (mouseTrackingEnabled) { - sendMouseMoveEvent(windowObj); + if (leftButtonPressed || mouseTrackingEnabled) { if (isWidget) { sendMouseMoveEvent(targetObj); + } else { + sendMouseMoveEvent(windowObj); } } } break; - case ButtonState::Pressed: { - sendMousePressEvent(windowObj); + case ButtonState::Pressed: + // Sending mouse event to the window has no effect. sendMousePressEvent(targetObj); - } break; - case ButtonState::Released: { - sendMouseReleaseEvent(windowObj); + break; + case ButtonState::Released: + // Sending mouse event to the window has no effect. sendMouseReleaseEvent(targetObj); - } break; + break; } } diff --git a/src/core/utils_linux.cpp b/src/core/utils_linux.cpp index af50c23e..8b7235a3 100644 --- a/src/core/utils_linux.cpp +++ b/src/core/utils_linux.cpp @@ -987,4 +987,10 @@ bool Utils::setPlatformPropertiesForWindow(QWindow *window, const QVariantHash & return true; } +Qt::MouseButtons Utils::queryMouseButtons() +{ + // ### FIXME + return {}; +} + FRAMELESSHELPER_END_NAMESPACE diff --git a/src/core/utils_mac.mm b/src/core/utils_mac.mm index 628bdc96..1c6cf878 100644 --- a/src/core/utils_mac.mm +++ b/src/core/utils_mac.mm @@ -752,6 +752,12 @@ static inline void cleanupProxy() return (active ? getAccentColor() : kDefaultDarkGrayColor); } +Qt::MouseButtons Utils::queryMouseButtons() +{ + // ### FIXME + return {}; +} + FRAMELESSHELPER_END_NAMESPACE #include "utils_mac.moc" diff --git a/src/core/utils_win.cpp b/src/core/utils_win.cpp index f02a72cd..a8eca5ec 100644 --- a/src/core/utils_win.cpp +++ b/src/core/utils_win.cpp @@ -2571,6 +2571,31 @@ quint64 Utils::queryMouseButtonState() return result; } +Qt::MouseButtons Utils::queryMouseButtons() +{ + const quint64 buttonMask = queryMouseButtonState(); + if (buttonMask == 0) { + return {}; + } + Qt::MouseButtons buttons = {}; + if (buttonMask & MK_LBUTTON) { + buttons |= Qt::LeftButton; + } + if (buttonMask & MK_RBUTTON) { + buttons |= Qt::RightButton; + } + if (buttonMask & MK_MBUTTON) { + buttons |= Qt::MiddleButton; + } + if (buttonMask & MK_XBUTTON1) { + buttons |= Qt::XButton1; + } + if (buttonMask & MK_XBUTTON2) { + buttons |= Qt::XButton2; + } + return buttons; +} + bool Utils::isValidWindow(const WId windowId, const bool checkVisible, const bool checkTopLevel) { Q_ASSERT(windowId); diff --git a/src/quick/framelessquickhelper.cpp b/src/quick/framelessquickhelper.cpp index 8b2f8495..303b64d5 100644 --- a/src/quick/framelessquickhelper.cpp +++ b/src/quick/framelessquickhelper.cpp @@ -903,7 +903,16 @@ 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); - Utils::emulateQtMouseEvent(btn, window, FRAMELESSHELPER_ENUM_QUICK_TO_CORE(ButtonState, state), globalPos, scenePos, localPos); + 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, true); }; updateButtonState(quickButton); #endif // FRAMELESSHELPER_QUICK_NO_PRIVATE diff --git a/src/widgets/framelesswidgetshelper.cpp b/src/widgets/framelesswidgetshelper.cpp index a4580346..abd71742 100644 --- a/src/widgets/framelesswidgetshelper.cpp +++ b/src/widgets/framelesswidgetshelper.cpp @@ -863,7 +863,14 @@ 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); +#if 0 + const auto underMouse = [btn, &globalPos]() -> bool { + const QPoint originPoint = btn->mapToGlobal(QPoint{ 0, 0 }); + return QRect{ originPoint, btn->size() }.contains(globalPos); + }(); +#endif + const bool hoverEnabled = btn->testAttribute(Qt::WA_Hover); + Utils::emulateQtMouseEvent(btn, window->windowHandle(), state, globalPos, scenePos, localPos, btn->underMouse(), hoverEnabled); }; updateButtonState(widgetButton); }