From a130fef6776e7fffd7d7ed350b31840862252c00 Mon Sep 17 00:00:00 2001 From: Yuhang Zhao Date: Wed, 23 Aug 2023 18:04:31 +0800 Subject: [PATCH] win: refactor --- include/FramelessHelper/Core/utils.h | 7 +- .../Widgets/private/standardsystembutton_p.h | 6 +- .../Widgets/standardsystembutton.h | 8 +- src/core/framelesshelper_win.cpp | 200 ++++----- src/core/utils.cpp | 154 ------- src/core/utils_linux.cpp | 6 - src/core/utils_mac.mm | 6 - src/core/utils_win.cpp | 422 ++++++++++++++++-- src/quick/framelessquickhelper.cpp | 61 --- src/widgets/framelesswidgetshelper.cpp | 72 +-- src/widgets/standardsystembutton.cpp | 28 +- 11 files changed, 512 insertions(+), 458 deletions(-) diff --git a/include/FramelessHelper/Core/utils.h b/include/FramelessHelper/Core/utils.h index ed5f759f..57ea6eb5 100644 --- a/include/FramelessHelper/Core/utils.h +++ b/include/FramelessHelper/Core/utils.h @@ -89,11 +89,6 @@ FRAMELESSHELPER_CORE_API void moveWindowToDesktopCenter( [[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 bool underMouse, const bool hoverEnabled); #ifdef Q_OS_WINDOWS [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowsVersionOrGreater(const Global::WindowsVersion version); @@ -151,7 +146,7 @@ FRAMELESSHELPER_CORE_API void emulateQtMouseEvent( [[nodiscard]] FRAMELESSHELPER_CORE_API QPoint getWindowPlacementOffset(const WId windowId); [[nodiscard]] FRAMELESSHELPER_CORE_API QRect getWindowRestoreGeometry(const WId windowId); [[nodiscard]] FRAMELESSHELPER_CORE_API bool removeMicaWindow(const WId windowId); -[[nodiscard]] FRAMELESSHELPER_CORE_API quint64 getMouseButtonsAndModifiers(const bool async); +[[nodiscard]] FRAMELESSHELPER_CORE_API quint64 getKeyState(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isValidWindow(const WId windowId, const bool checkVisible, const bool checkTopLevel); [[nodiscard]] FRAMELESSHELPER_CORE_API bool updateFramebufferTransparency(const WId windowId); [[nodiscard]] FRAMELESSHELPER_CORE_API QMargins getWindowSystemFrameMargins(const WId windowId); diff --git a/include/FramelessHelper/Widgets/private/standardsystembutton_p.h b/include/FramelessHelper/Widgets/private/standardsystembutton_p.h index 1765aeff..f09996ad 100644 --- a/include/FramelessHelper/Widgets/private/standardsystembutton_p.h +++ b/include/FramelessHelper/Widgets/private/standardsystembutton_p.h @@ -63,7 +63,7 @@ class FRAMELESSHELPER_WIDGETS_API StandardSystemButtonPrivate : public QObject Q_NODISCARD QColor getActiveForegroundColor() const; Q_NODISCARD QColor getInactiveForegroundColor() const; Q_NODISCARD bool isActive() const; - Q_NODISCARD int iconSize2() const; + Q_NODISCARD int glyphSize() const; void setHoverColor(const QColor &value); void setPressColor(const QColor &value); @@ -71,7 +71,7 @@ class FRAMELESSHELPER_WIDGETS_API StandardSystemButtonPrivate : public QObject void setActiveForegroundColor(const QColor &value); void setInactiveForegroundColor(const QColor &value); void setActive(const bool value); - void setIconSize2(const int value); + void setGlyphSize(const int value); void paintEventHandler(QPaintEvent *event); @@ -88,7 +88,7 @@ class FRAMELESSHELPER_WIDGETS_API StandardSystemButtonPrivate : public QObject QColor m_activeForegroundColor = {}; QColor m_inactiveForegroundColor = {}; bool m_active = false; - std::optional m_iconSize2 = std::nullopt; + std::optional m_glyphSize = std::nullopt; }; FRAMELESSHELPER_END_NAMESPACE diff --git a/include/FramelessHelper/Widgets/standardsystembutton.h b/include/FramelessHelper/Widgets/standardsystembutton.h index f8494b45..fc3412a8 100644 --- a/include/FramelessHelper/Widgets/standardsystembutton.h +++ b/include/FramelessHelper/Widgets/standardsystembutton.h @@ -44,7 +44,7 @@ class FRAMELESSHELPER_WIDGETS_API StandardSystemButton : public QPushButton Q_PROPERTY(QColor activeForegroundColor READ activeForegroundColor WRITE setActiveForegroundColor NOTIFY activeForegroundColorChanged FINAL) Q_PROPERTY(QColor inactiveForegroundColor READ inactiveForegroundColor WRITE setInactiveForegroundColor NOTIFY inactiveForegroundColorChanged FINAL) Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged FINAL) - Q_PROPERTY(int iconSize2 READ iconSize2 WRITE setIconSize2 NOTIFY iconSize2Changed FINAL) + Q_PROPERTY(int glyphSize READ glyphSize WRITE setGlyphSize NOTIFY glyphSizeChanged FINAL) public: explicit StandardSystemButton(QWidget *parent = nullptr); @@ -60,7 +60,7 @@ class FRAMELESSHELPER_WIDGETS_API StandardSystemButton : public QPushButton Q_NODISCARD QColor activeForegroundColor() const; Q_NODISCARD QColor inactiveForegroundColor() const; Q_NODISCARD bool isActive() const; - Q_NODISCARD int iconSize2() const; + Q_NODISCARD int glyphSize() const; public Q_SLOTS: void setButtonType(const Global::SystemButtonType value); @@ -71,7 +71,7 @@ public Q_SLOTS: void setActiveForegroundColor(const QColor &value); void setInactiveForegroundColor(const QColor &value); void setActive(const bool value); - void setIconSize2(const int value); + void setGlyphSize(const int value); protected: void paintEvent(QPaintEvent *event) override; @@ -85,7 +85,7 @@ public Q_SLOTS: void activeForegroundColorChanged(); void inactiveForegroundColorChanged(); void activeChanged(); - void iconSize2Changed(); + void glyphSizeChanged(); private: QScopedPointer d_ptr; diff --git a/src/core/framelesshelper_win.cpp b/src/core/framelesshelper_win.cpp index 4e6e9e9d..bd6a9086 100644 --- a/src/core/framelesshelper_win.cpp +++ b/src/core/framelesshelper_win.cpp @@ -30,6 +30,7 @@ #include "winverhelper_p.h" #include "framelesshelper_windows.h" #include "framelesshelpercore_global_p.h" +#include "scopeguard_p.h" #include #include #include @@ -85,10 +86,20 @@ FRAMELESSHELPER_STRING_CONSTANT(DestroyWindow) FRAMELESSHELPER_STRING_CONSTANT(GetWindowPlacement) FRAMELESSHELPER_STRING_CONSTANT(SetWindowPlacement) +enum class WindowPart : quint8 +{ + NotInterested, + ClientArea, + ChromeButton, + ResizeBorder, + FixBorder, + TitleBar +}; + struct FramelessWin32HelperData { SystemParameters params = {}; - bool trackingMouse = false; + int hitTestResult = HTNOWHERE; Dpi dpi = {}; #if (QT_VERSION < QT_VERSION_CHECK(6, 5, 1)) QRect restoreGeometry = {}; @@ -146,6 +157,36 @@ Q_GLOBAL_STATIC(FramelessWin32HelperInternal, g_framelessWin32HelperData) return result; } +[[nodiscard]] static inline WindowPart getHittedWindowPart(const LRESULT hitTestResult) +{ + switch (hitTestResult) { + case HTCLIENT: + return WindowPart::ClientArea; + case HTCAPTION: + return WindowPart::TitleBar; + case HTSYSMENU: + case HTHELP: + case HTREDUCE: + case HTZOOM: + case HTCLOSE: + return WindowPart::ChromeButton; + case HTLEFT: + case HTRIGHT: + case HTTOP: + case HTTOPLEFT: + case HTTOPRIGHT: + case HTBOTTOM: + case HTBOTTOMLEFT: + case HTBOTTOMRIGHT: + return WindowPart::ResizeBorder; + case HTBORDER: + return WindowPart::FixBorder; + default: + break; + } + return WindowPart::NotInterested; +} + FramelessHelperWin::FramelessHelperWin() : QAbstractNativeEventFilter() {} FramelessHelperWin::~FramelessHelperWin() = default; @@ -288,48 +329,12 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me }; #endif // (QT_VERSION < QT_VERSION_CHECK(6, 5, 1)) -#if 0 - const auto releaseButtons = [&data](const std::optional exclude) -> void { - static constexpr const auto defaultButtonState = ButtonState::Normal; - const SystemButtonType button = exclude.value_or(SystemButtonType::Unknown); - if (button != SystemButtonType::WindowIcon) { - data.params.setSystemButtonState(SystemButtonType::WindowIcon, defaultButtonState); - } - if (button != SystemButtonType::Help) { - data.params.setSystemButtonState(SystemButtonType::Help, defaultButtonState); - } - if (button != SystemButtonType::Minimize) { - data.params.setSystemButtonState(SystemButtonType::Minimize, defaultButtonState); - } - if (button != SystemButtonType::Maximize) { - data.params.setSystemButtonState(SystemButtonType::Maximize, defaultButtonState); - } - if (button != SystemButtonType::Restore) { - data.params.setSystemButtonState(SystemButtonType::Restore, defaultButtonState); - } - if (button != SystemButtonType::Close) { - data.params.setSystemButtonState(SystemButtonType::Close, defaultButtonState); - } - }; - const auto hoverButton = [&releaseButtons, &data](const SystemButtonType button) -> void { - releaseButtons(button); - data.params.setSystemButtonState(button, ButtonState::Hovered); - }; - const auto pressButton = [&releaseButtons, &data](const SystemButtonType button) -> void { - releaseButtons(button); - data.params.setSystemButtonState(button, ButtonState::Pressed); - }; - const auto clickButton = [&releaseButtons, &data](const SystemButtonType button) -> void { - releaseButtons(button); - data.params.setSystemButtonState(button, ButtonState::Released); - }; -#else const auto emulateClientAreaMessage = [hWnd, uMsg, wParam, lParam]() -> void { const auto wparam = [uMsg, wParam]() -> WPARAM { if (uMsg == WM_NCMOUSELEAVE) { return 0; } - const quint64 keyState = Utils::getMouseButtonsAndModifiers(false); + const quint64 keyState = Utils::getKeyState(); if ((uMsg >= WM_NCXBUTTONDOWN) && (uMsg <= WM_NCXBUTTONDBLCLK)) { const auto xButtonMask = GET_XBUTTON_WPARAM(wParam); return MAKEWPARAM(keyState, xButtonMask); @@ -348,45 +353,51 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me } return MAKELPARAM(clientPos.x, clientPos.y); }(); +#if 0 +# define SEND_MESSAGE ::SendMessageW +#else +# define SEND_MESSAGE ::PostMessageW +#endif switch (uMsg) { + case WM_NCHITTEST: // Treat hit test messages as mouse move events. case WM_NCMOUSEMOVE: - ::SendMessageW(hWnd, WM_MOUSEMOVE, wparam, lparam); + SEND_MESSAGE(hWnd, WM_MOUSEMOVE, wparam, lparam); break; case WM_NCLBUTTONDOWN: - ::SendMessageW(hWnd, WM_LBUTTONDOWN, wparam, lparam); + SEND_MESSAGE(hWnd, WM_LBUTTONDOWN, wparam, lparam); break; case WM_NCLBUTTONUP: - ::SendMessageW(hWnd, WM_LBUTTONUP, wparam, lparam); + SEND_MESSAGE(hWnd, WM_LBUTTONUP, wparam, lparam); break; case WM_NCLBUTTONDBLCLK: - ::SendMessageW(hWnd, WM_LBUTTONDBLCLK, wparam, lparam); + SEND_MESSAGE(hWnd, WM_LBUTTONDBLCLK, wparam, lparam); break; case WM_NCRBUTTONDOWN: - ::SendMessageW(hWnd, WM_RBUTTONDOWN, wparam, lparam); + SEND_MESSAGE(hWnd, WM_RBUTTONDOWN, wparam, lparam); break; case WM_NCRBUTTONUP: - ::SendMessageW(hWnd, WM_RBUTTONUP, wparam, lparam); + SEND_MESSAGE(hWnd, WM_RBUTTONUP, wparam, lparam); break; case WM_NCRBUTTONDBLCLK: - ::SendMessageW(hWnd, WM_RBUTTONDBLCLK, wparam, lparam); + SEND_MESSAGE(hWnd, WM_RBUTTONDBLCLK, wparam, lparam); break; case WM_NCMBUTTONDOWN: - ::SendMessageW(hWnd, WM_MBUTTONDOWN, wparam, lparam); + SEND_MESSAGE(hWnd, WM_MBUTTONDOWN, wparam, lparam); break; case WM_NCMBUTTONUP: - ::SendMessageW(hWnd, WM_MBUTTONUP, wparam, lparam); + SEND_MESSAGE(hWnd, WM_MBUTTONUP, wparam, lparam); break; case WM_NCMBUTTONDBLCLK: - ::SendMessageW(hWnd, WM_MBUTTONDBLCLK, wparam, lparam); + SEND_MESSAGE(hWnd, WM_MBUTTONDBLCLK, wparam, lparam); break; case WM_NCXBUTTONDOWN: - ::SendMessageW(hWnd, WM_XBUTTONDOWN, wparam, lparam); + SEND_MESSAGE(hWnd, WM_XBUTTONDOWN, wparam, lparam); break; case WM_NCXBUTTONUP: - ::SendMessageW(hWnd, WM_XBUTTONUP, wparam, lparam); + SEND_MESSAGE(hWnd, WM_XBUTTONUP, wparam, lparam); break; case WM_NCXBUTTONDBLCLK: - ::SendMessageW(hWnd, WM_XBUTTONDBLCLK, wparam, lparam); + SEND_MESSAGE(hWnd, WM_XBUTTONDBLCLK, wparam, lparam); break; #if 0 case WM_NCPOINTERUPDATE: @@ -395,16 +406,28 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me break; #endif case WM_NCMOUSEHOVER: - ::SendMessageW(hWnd, WM_MOUSEHOVER, wparam, lparam); + SEND_MESSAGE(hWnd, WM_MOUSEHOVER, wparam, lparam); break; case WM_NCMOUSELEAVE: - ::SendMessageW(hWnd, WM_MOUSELEAVE, wparam, lparam); + SEND_MESSAGE(hWnd, WM_MOUSELEAVE, wparam, lparam); break; default: Q_UNREACHABLE(); } }; -#endif + + if (isSnapLayoutEnabled() && (uMsg == WM_MOUSELEAVE)) { + // For some unknown reason, there will be many invalid mouse leave events generated + // when the mouse is hovering above our chrome buttons, which will cause Qt to ignore + // all our mouse move events and thus break the hover state of our controls. + // So we filter out these superfluous mouse leave events here. + const QPoint qtScenePos = Utils::fromNativeLocalPosition(window, QPoint{ msg->pt.x, msg->pt.y }); + SystemButtonType sysButtonType = SystemButtonType::Unknown; + if (data.params.isInsideSystemButtons(qtScenePos, &sysButtonType)) { + *result = FALSE; + return true; + } + } switch (uMsg) { #if (QT_VERSION < QT_VERSION_CHECK(5, 9, 0)) // Qt has done this for us since 5.9.0 @@ -504,8 +527,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me // implement an elaborate client-area preservation technique, and // simply return 0, which means "preserve the entire old client area // and align it with the upper-left corner of our new client area". - const auto clientRect = ((static_cast(wParam) == FALSE) ? - reinterpret_cast(lParam) : &(reinterpret_cast(lParam))->rgrc[0]); + const auto clientRect = ((wParam == FALSE) ? reinterpret_cast(lParam) : &(reinterpret_cast(lParam))->rgrc[0]); if (frameBorderVisible) { // Store the original top margin before the default window procedure applies the default frame. const LONG originalTop = clientRect->top; @@ -518,7 +540,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me // totally OK but since we want to preserve as much original frame as possible, we // can't use that solution. const LRESULT hitTestResult = ::DefWindowProcW(hWnd, WM_NCCALCSIZE, wParam, lParam); - if (hitTestResult != FALSE) { + if ((hitTestResult != HTERROR) && (hitTestResult != HTNOWHERE)) { *result = hitTestResult; return true; } @@ -657,7 +679,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me // from Windows 7 to Windows 10. Not tested on Windows 11 yet. Don't know // whether it exists on Windows XP to Windows Vista or not. static const bool needD3DWorkaround = (qEnvironmentVariableIntValue("FRAMELESSHELPER_USE_D3D_WORKAROUND") != 0); - *result = (((static_cast(wParam) == FALSE) || needD3DWorkaround) ? FALSE : WVR_REDRAW); + *result = (((wParam == FALSE) || needD3DWorkaround) ? FALSE : WVR_REDRAW); return true; } case WM_NCHITTEST: { @@ -742,6 +764,8 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me // color, our homemade top border can almost have exactly the same // appearance with the system's one. + const auto hitTestRecorder = qScopeGuard([&muData, &result](){ muData.hitTestResult = *result; }); + const auto nativeGlobalPos = POINT{ GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; POINT nativeLocalPos = nativeGlobalPos; if (::ScreenToClient(hWnd, &nativeLocalPos) == FALSE) { @@ -783,7 +807,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me const bool full = Utils::isFullScreen(windowId); const int frameSizeY = Utils::getResizeBorderThickness(windowId, false, true); const bool isTop = (nativeLocalPos.y < frameSizeY); - const bool leftButtonPressed = (Utils::getMouseButtonsAndModifiers(true) & MK_LBUTTON); + const bool leftButtonPressed = (Utils::getKeyState() & MK_LBUTTON); const bool isTitleBar = (data.params.isInsideTitleBarDraggableArea(qtScenePos) && leftButtonPressed); const bool isFixedSize = data.params.isWindowFixedSize(); const bool dontOverrideCursor = data.params.getProperty(kDontOverrideCursorVar, false).toBool(); @@ -843,20 +867,18 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me return true; } if (!isFixedSize) { - RECT clientRect = {0, 0, 0, 0}; + auto clientRect = RECT{ 0, 0, 0, 0 }; if (::GetClientRect(hWnd, &clientRect) == FALSE) { WARNING << Utils::getSystemErrorMessage(kGetClientRect); break; } - const LONG width = clientRect.right; - const LONG height = clientRect.bottom; - const bool isBottom = (nativeLocalPos.y >= (height - frameSizeY)); + const bool isBottom = (nativeLocalPos.y >= (RECT_HEIGHT(clientRect) - frameSizeY)); // Make the border a little wider to let the user easy to resize on corners. - const qreal scaleFactor = ((isTop || isBottom) ? 2.0 : 1.0); + const auto scaleFactor = ((isTop || isBottom) ? qreal(2) : qreal(1)); const int frameSizeX = Utils::getResizeBorderThickness(windowId, true, true); const int scaledFrameSizeX = std::round(qreal(frameSizeX) * scaleFactor); const bool isLeft = (nativeLocalPos.x < scaledFrameSizeX); - const bool isRight = (nativeLocalPos.x >= (width - scaledFrameSizeX)); + const bool isRight = (nativeLocalPos.x >= (RECT_WIDTH(clientRect) - scaledFrameSizeX)); if (dontOverrideCursor && (isTop || isBottom || isLeft || isRight)) { // Return HTCLIENT instead of HTBORDER here, because the mouse is // inside the window now, return HTCLIENT to let the controls @@ -925,45 +947,23 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me #endif case WM_NCMOUSEHOVER: case WM_NCMOUSELEAVE: { + const WindowPart previousWindowPart = getHittedWindowPart(data.hitTestResult); + const bool isXButtonMessage = ((uMsg >= WM_NCXBUTTONDOWN) && (uMsg <= WM_NCXBUTTONDBLCLK)); + const WindowPart nowWindowPart = getHittedWindowPart(isXButtonMessage ? GET_NCHITTEST_WPARAM(wParam) : wParam); if (uMsg == WM_NCMOUSELEAVE) { - muData.trackingMouse = false; - emulateClientAreaMessage(); - //*result = FALSE; - //return true; - } else { - if ((uMsg == WM_NCMOUSEMOVE) && !data.trackingMouse) { - TRACKMOUSEEVENT tme; - SecureZeroMemory(&tme, sizeof(tme)); - tme.cbSize = sizeof(tme); - // TME_NONCLIENT is absolutely critical here. In my experimentation, - // we'd get WM_MOUSELEAVE messages after just a HOVER_DEFAULT - // timeout even though we're not requesting TME_HOVER, which kinda - // ruined the whole point of this. - tme.dwFlags = (TME_LEAVE | TME_NONCLIENT); - tme.hwndTrack = hWnd; - tme.dwHoverTime = HOVER_DEFAULT; // We don't _really_ care about this. - if (::TrackMouseEvent(&tme) == FALSE) { - WARNING << Utils::getSystemErrorMessage(kTrackMouseEvent); + if (previousWindowPart == WindowPart::ChromeButton) { + if (nowWindowPart == WindowPart::ClientArea) { + *result = FALSE; + return true; } else { - muData.trackingMouse = true; + emulateClientAreaMessage(); } } - const bool isXButtonMessage = ((uMsg >= WM_NCXBUTTONDOWN) && (uMsg <= WM_NCXBUTTONDBLCLK)); - const auto hitTestResult = (isXButtonMessage ? GET_NCHITTEST_WPARAM(wParam) : wParam); - switch (hitTestResult) { - case HTSYSMENU: - case HTHELP: - case HTREDUCE: - case HTZOOM: - case HTCLOSE: { + } else { + if (nowWindowPart == WindowPart::ChromeButton) { emulateClientAreaMessage(); - //if ((uMsg != WM_NCMOUSEMOVE) && (uMsg != WM_NCMOUSEHOVER)) { - *result = (isXButtonMessage ? TRUE : FALSE); - return true; - //} - } - default: - break; + *result = (isXButtonMessage ? TRUE : FALSE); + return true; } } } break; @@ -1119,7 +1119,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me // window activation state change. *result = ::DefWindowProcW(hWnd, WM_NCACTIVATE, wParam, -1); } else { - if (static_cast(wParam) == FALSE) { + if (wParam == FALSE) { *result = TRUE; } else { *result = FALSE; diff --git a/src/core/utils.cpp b/src/core/utils.cpp index 09a1e0df..7578be1a 100644 --- a/src/core/utils.cpp +++ b/src/core/utils.cpp @@ -36,7 +36,6 @@ #include #include #include -#include #ifndef FRAMELESSHELPER_CORE_NO_PRIVATE # include # include @@ -631,157 +630,4 @@ 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, const bool enableHover) -{ - Q_ASSERT(target); - Q_ASSERT(window); - if (!target || !window) { - return; - } - const auto targetObj = const_cast(target); - const auto windowObj = static_cast(const_cast(window)); - const bool isWidget = target->isWidgetType(); - 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_QUICKITEM_ENTERED"; - const bool entered = target->property(kEnteredFlag).toBool(); - const bool leftButtonPressed = (queryMouseButtons() & Qt::LeftButton); - const auto sendEnterEvent = [&localPos, &scenePos, &globalPos, &modifiers, hoverEnabled](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 - QCoreApplication::sendEvent(obj, &enterEvent); - if (hoverEnabled) { -#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)) - QHoverEvent hoverEnterEvent(QEvent::HoverEnter, scenePos, globalPos, oldPos, modifiers); -#elif (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)) - QHoverEvent hoverEnterEvent(QEvent::HoverEnter, localPos, globalPos, oldPos, modifiers); -#else - QHoverEvent hoverEnterEvent(QEvent::HoverEnter, localPos, oldPos, modifiers); -#endif - QCoreApplication::sendEvent(obj, &hoverEnterEvent); - } - }; - const auto sendLeaveEvent = [&localPos, &scenePos, &globalPos, &modifiers, hoverEnabled](QObject *obj) -> void { - Q_ASSERT(obj); - if (!obj) { - return; - } - QEvent leaveEvent(QEvent::Leave); - QCoreApplication::sendEvent(obj, &leaveEvent); - if (hoverEnabled) { -#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)) - Q_UNUSED(localPos); - QHoverEvent hoverLeaveEvent(QEvent::HoverLeave, scenePos, globalPos, oldPos, modifiers); -#elif (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)) - QHoverEvent hoverLeaveEvent(QEvent::HoverLeave, localPos, globalPos, oldPos, modifiers); -#else - QHoverEvent hoverLeaveEvent(QEvent::HoverLeave, localPos, oldPos, modifiers); -#endif - QCoreApplication::sendEvent(obj, &hoverLeaveEvent); - } - }; - const auto sendMouseMoveEvent = [&localPos, &scenePos, &globalPos, &buttons, &modifiers, leftButtonPressed](QObject *obj) -> void { - Q_ASSERT(obj); - if (!obj) { - return; - } - 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 = [&localPos, &scenePos, &globalPos, &modifiers, hoverEnabled](QObject *obj) -> void { - Q_ASSERT(obj); - if (!obj) { - return; - } - if (!hoverEnabled) { - return; - } -#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)) - Q_UNUSED(localPos); - QHoverEvent event(QEvent::HoverMove, scenePos, globalPos, oldPos, modifiers); -#elif (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)) - QHoverEvent event(QEvent::HoverMove, localPos, globalPos, oldPos, modifiers); -#else - QHoverEvent event(QEvent::HoverMove, localPos, oldPos, modifiers); -#endif - 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, {}); - // Send an extra mouse release event to let the control dismiss it's hover state. - // But only when the mouse is not still hovering above the control, otherwise Qt will - // generate a mouse click event, which is not what the user would expect. - if (!underMouse) { - sendMouseReleaseEvent(targetObj); - } - if (isWidget) { - sendLeaveEvent(targetObj); - } else { - sendLeaveEvent(windowObj); - } - } break; - case ButtonState::Hovered: { - if (!entered) { - targetObj->setProperty(kEnteredFlag, true); - if (isWidget) { - sendEnterEvent(targetObj); - } else { - sendEnterEvent(windowObj); - } - } - if (isWidget) { - sendHoverMoveEvent(targetObj); - } else { - sendHoverMoveEvent(windowObj); - } - if (leftButtonPressed || mouseTrackingEnabled) { - if (isWidget) { - sendMouseMoveEvent(targetObj); - } else { - sendMouseMoveEvent(windowObj); - } - } - } break; - case ButtonState::Pressed: - // Sending mouse event to the window has no effect. - sendMousePressEvent(targetObj); - break; - case ButtonState::Released: - // Sending mouse event to the window has no effect. - sendMouseReleaseEvent(targetObj); - break; - } -} - FRAMELESSHELPER_END_NAMESPACE diff --git a/src/core/utils_linux.cpp b/src/core/utils_linux.cpp index cdd24b92..1f2dfe26 100644 --- a/src/core/utils_linux.cpp +++ b/src/core/utils_linux.cpp @@ -992,10 +992,4 @@ 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 8042ed4e..d44f6f8e 100644 --- a/src/core/utils_mac.mm +++ b/src/core/utils_mac.mm @@ -755,12 +755,6 @@ 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 a40cf7b6..7404b41a 100644 --- a/src/core/utils_win.cpp +++ b/src/core/utils_win.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #ifndef FRAMELESSHELPER_CORE_NO_PRIVATE @@ -188,6 +189,7 @@ FRAMELESSHELPER_STRING_CONSTANT(SetActiveWindow) FRAMELESSHELPER_STRING_CONSTANT(RedrawWindow) FRAMELESSHELPER_STRING_CONSTANT(ScreenToClient) FRAMELESSHELPER_STRING_CONSTANT(DwmFlush) +FRAMELESSHELPER_STRING_CONSTANT(GetCursorPos) struct Win32UtilsData { @@ -203,6 +205,300 @@ struct Win32UtilsInternal Q_GLOBAL_STATIC(Win32UtilsInternal, g_win32UtilsData) +struct Win32Message +{ + UINT Code = 0; + LPCSTR Str = nullptr; + + [[nodiscard]] friend inline constexpr bool operator==(const Win32Message &lhs, const Win32Message &rhs) noexcept + { + return (lhs.Code == rhs.Code); + } + + [[nodiscard]] friend inline constexpr bool operator!=(const Win32Message &lhs, const Win32Message &rhs) noexcept + { + return !operator==(lhs, rhs); + } + + [[nodiscard]] friend inline constexpr bool operator>(const Win32Message &lhs, const Win32Message &rhs) noexcept + { + return (lhs.Code > rhs.Code); + } + + [[nodiscard]] friend inline constexpr bool operator>=(const Win32Message &lhs, const Win32Message &rhs) noexcept + { + return (operator>(lhs, rhs) || operator==(lhs, rhs)); + } + + [[nodiscard]] friend inline constexpr bool operator<(const Win32Message &lhs, const Win32Message &rhs) noexcept + { + return (operator!=(lhs, rhs) && !operator>(lhs, rhs)); + } + + [[nodiscard]] friend inline constexpr bool operator<=(const Win32Message &lhs, const Win32Message &rhs) noexcept + { + return (operator<(lhs, rhs) || operator==(lhs, rhs)); + } +}; + +#define DEFINE_WIN32_MESSAGE(Message) Win32Message{ Message, #Message }, +static constexpr const std::array g_win32MessageMap = +{ + DEFINE_WIN32_MESSAGE(WM_NULL) + DEFINE_WIN32_MESSAGE(WM_CREATE) + DEFINE_WIN32_MESSAGE(WM_DESTROY) + DEFINE_WIN32_MESSAGE(WM_MOVE) + DEFINE_WIN32_MESSAGE(WM_SIZE) + DEFINE_WIN32_MESSAGE(WM_ACTIVATE) + DEFINE_WIN32_MESSAGE(WM_SETFOCUS) + DEFINE_WIN32_MESSAGE(WM_KILLFOCUS) + DEFINE_WIN32_MESSAGE(WM_ENABLE) + DEFINE_WIN32_MESSAGE(WM_SETREDRAW) + DEFINE_WIN32_MESSAGE(WM_SETTEXT) + DEFINE_WIN32_MESSAGE(WM_GETTEXT) + DEFINE_WIN32_MESSAGE(WM_GETTEXTLENGTH) + DEFINE_WIN32_MESSAGE(WM_PAINT) + DEFINE_WIN32_MESSAGE(WM_CLOSE) + DEFINE_WIN32_MESSAGE(WM_QUERYENDSESSION) + DEFINE_WIN32_MESSAGE(WM_QUERYOPEN) + DEFINE_WIN32_MESSAGE(WM_ENDSESSION) + DEFINE_WIN32_MESSAGE(WM_QUIT) + DEFINE_WIN32_MESSAGE(WM_ERASEBKGND) + DEFINE_WIN32_MESSAGE(WM_SYSCOLORCHANGE) + DEFINE_WIN32_MESSAGE(WM_SHOWWINDOW) + DEFINE_WIN32_MESSAGE(WM_SETTINGCHANGE) // WM_WININICHANGE + DEFINE_WIN32_MESSAGE(WM_DEVMODECHANGE) + DEFINE_WIN32_MESSAGE(WM_ACTIVATEAPP) + DEFINE_WIN32_MESSAGE(WM_FONTCHANGE) + DEFINE_WIN32_MESSAGE(WM_TIMECHANGE) + DEFINE_WIN32_MESSAGE(WM_CANCELMODE) + DEFINE_WIN32_MESSAGE(WM_SETCURSOR) + DEFINE_WIN32_MESSAGE(WM_MOUSEACTIVATE) + DEFINE_WIN32_MESSAGE(WM_CHILDACTIVATE) + DEFINE_WIN32_MESSAGE(WM_QUEUESYNC) + DEFINE_WIN32_MESSAGE(WM_GETMINMAXINFO) + DEFINE_WIN32_MESSAGE(WM_PAINTICON) + DEFINE_WIN32_MESSAGE(WM_ICONERASEBKGND) + DEFINE_WIN32_MESSAGE(WM_NEXTDLGCTL) + DEFINE_WIN32_MESSAGE(WM_SPOOLERSTATUS) + DEFINE_WIN32_MESSAGE(WM_DRAWITEM) + DEFINE_WIN32_MESSAGE(WM_MEASUREITEM) + DEFINE_WIN32_MESSAGE(WM_DELETEITEM) + DEFINE_WIN32_MESSAGE(WM_VKEYTOITEM) + DEFINE_WIN32_MESSAGE(WM_CHARTOITEM) + DEFINE_WIN32_MESSAGE(WM_SETFONT) + DEFINE_WIN32_MESSAGE(WM_GETFONT) + DEFINE_WIN32_MESSAGE(WM_SETHOTKEY) + DEFINE_WIN32_MESSAGE(WM_GETHOTKEY) + DEFINE_WIN32_MESSAGE(WM_QUERYDRAGICON) + DEFINE_WIN32_MESSAGE(WM_COMPAREITEM) + DEFINE_WIN32_MESSAGE(WM_GETOBJECT) + DEFINE_WIN32_MESSAGE(WM_COMPACTING) + DEFINE_WIN32_MESSAGE(WM_COMMNOTIFY) + DEFINE_WIN32_MESSAGE(WM_WINDOWPOSCHANGING) + DEFINE_WIN32_MESSAGE(WM_WINDOWPOSCHANGED) + DEFINE_WIN32_MESSAGE(WM_POWER) + DEFINE_WIN32_MESSAGE(WM_COPYDATA) + DEFINE_WIN32_MESSAGE(WM_CANCELJOURNAL) + DEFINE_WIN32_MESSAGE(WM_NOTIFY) + DEFINE_WIN32_MESSAGE(WM_INPUTLANGCHANGEREQUEST) + DEFINE_WIN32_MESSAGE(WM_INPUTLANGCHANGE) + DEFINE_WIN32_MESSAGE(WM_TCARD) + DEFINE_WIN32_MESSAGE(WM_HELP) + DEFINE_WIN32_MESSAGE(WM_USERCHANGED) + DEFINE_WIN32_MESSAGE(WM_NOTIFYFORMAT) + DEFINE_WIN32_MESSAGE(WM_CONTEXTMENU) + DEFINE_WIN32_MESSAGE(WM_STYLECHANGING) + DEFINE_WIN32_MESSAGE(WM_STYLECHANGED) + DEFINE_WIN32_MESSAGE(WM_DISPLAYCHANGE) + DEFINE_WIN32_MESSAGE(WM_GETICON) + DEFINE_WIN32_MESSAGE(WM_SETICON) + DEFINE_WIN32_MESSAGE(WM_NCCREATE) + DEFINE_WIN32_MESSAGE(WM_NCDESTROY) + DEFINE_WIN32_MESSAGE(WM_NCCALCSIZE) + DEFINE_WIN32_MESSAGE(WM_NCHITTEST) + DEFINE_WIN32_MESSAGE(WM_NCPAINT) + DEFINE_WIN32_MESSAGE(WM_NCACTIVATE) + DEFINE_WIN32_MESSAGE(WM_GETDLGCODE) + DEFINE_WIN32_MESSAGE(WM_SYNCPAINT) + DEFINE_WIN32_MESSAGE(WM_NCMOUSEMOVE) + DEFINE_WIN32_MESSAGE(WM_NCLBUTTONDOWN) + DEFINE_WIN32_MESSAGE(WM_NCLBUTTONUP) + DEFINE_WIN32_MESSAGE(WM_NCLBUTTONDBLCLK) + DEFINE_WIN32_MESSAGE(WM_NCRBUTTONDOWN) + DEFINE_WIN32_MESSAGE(WM_NCRBUTTONUP) + DEFINE_WIN32_MESSAGE(WM_NCRBUTTONDBLCLK) + DEFINE_WIN32_MESSAGE(WM_NCMBUTTONDOWN) + DEFINE_WIN32_MESSAGE(WM_NCMBUTTONUP) + DEFINE_WIN32_MESSAGE(WM_NCMBUTTONDBLCLK) + DEFINE_WIN32_MESSAGE(WM_NCXBUTTONDOWN) + DEFINE_WIN32_MESSAGE(WM_NCXBUTTONUP) + DEFINE_WIN32_MESSAGE(WM_NCXBUTTONDBLCLK) + DEFINE_WIN32_MESSAGE(WM_INPUT_DEVICE_CHANGE) + DEFINE_WIN32_MESSAGE(WM_INPUT) + DEFINE_WIN32_MESSAGE(WM_KEYDOWN) // WM_KEYFIRST + DEFINE_WIN32_MESSAGE(WM_KEYUP) + DEFINE_WIN32_MESSAGE(WM_CHAR) + DEFINE_WIN32_MESSAGE(WM_DEADCHAR) + DEFINE_WIN32_MESSAGE(WM_SYSKEYDOWN) + DEFINE_WIN32_MESSAGE(WM_SYSKEYUP) + DEFINE_WIN32_MESSAGE(WM_SYSCHAR) + DEFINE_WIN32_MESSAGE(WM_SYSDEADCHAR) + DEFINE_WIN32_MESSAGE(WM_UNICHAR) // WM_KEYLAST + DEFINE_WIN32_MESSAGE(WM_IME_STARTCOMPOSITION) + DEFINE_WIN32_MESSAGE(WM_IME_ENDCOMPOSITION) + DEFINE_WIN32_MESSAGE(WM_IME_COMPOSITION) // WM_IME_KEYLAST + DEFINE_WIN32_MESSAGE(WM_INITDIALOG) + DEFINE_WIN32_MESSAGE(WM_COMMAND) + DEFINE_WIN32_MESSAGE(WM_SYSCOMMAND) + DEFINE_WIN32_MESSAGE(WM_TIMER) + DEFINE_WIN32_MESSAGE(WM_HSCROLL) + DEFINE_WIN32_MESSAGE(WM_VSCROLL) + DEFINE_WIN32_MESSAGE(WM_INITMENU) + DEFINE_WIN32_MESSAGE(WM_INITMENUPOPUP) + DEFINE_WIN32_MESSAGE(WM_GESTURE) + DEFINE_WIN32_MESSAGE(WM_GESTURENOTIFY) + DEFINE_WIN32_MESSAGE(WM_MENUSELECT) + DEFINE_WIN32_MESSAGE(WM_MENUCHAR) + DEFINE_WIN32_MESSAGE(WM_ENTERIDLE) + DEFINE_WIN32_MESSAGE(WM_MENURBUTTONUP) + DEFINE_WIN32_MESSAGE(WM_MENUDRAG) + DEFINE_WIN32_MESSAGE(WM_MENUGETOBJECT) + DEFINE_WIN32_MESSAGE(WM_UNINITMENUPOPUP) + DEFINE_WIN32_MESSAGE(WM_MENUCOMMAND) + DEFINE_WIN32_MESSAGE(WM_CHANGEUISTATE) + DEFINE_WIN32_MESSAGE(WM_UPDATEUISTATE) + DEFINE_WIN32_MESSAGE(WM_QUERYUISTATE) + DEFINE_WIN32_MESSAGE(WM_CTLCOLORMSGBOX) + DEFINE_WIN32_MESSAGE(WM_CTLCOLOREDIT) + DEFINE_WIN32_MESSAGE(WM_CTLCOLORLISTBOX) + DEFINE_WIN32_MESSAGE(WM_CTLCOLORBTN) + DEFINE_WIN32_MESSAGE(WM_CTLCOLORDLG) + DEFINE_WIN32_MESSAGE(WM_CTLCOLORSCROLLBAR) + DEFINE_WIN32_MESSAGE(WM_CTLCOLORSTATIC) + DEFINE_WIN32_MESSAGE(MN_GETHMENU) + DEFINE_WIN32_MESSAGE(WM_MOUSEMOVE) // WM_MOUSEFIRST + DEFINE_WIN32_MESSAGE(WM_LBUTTONDOWN) + DEFINE_WIN32_MESSAGE(WM_LBUTTONUP) + DEFINE_WIN32_MESSAGE(WM_LBUTTONDBLCLK) + DEFINE_WIN32_MESSAGE(WM_RBUTTONDOWN) + DEFINE_WIN32_MESSAGE(WM_RBUTTONUP) + DEFINE_WIN32_MESSAGE(WM_RBUTTONDBLCLK) + DEFINE_WIN32_MESSAGE(WM_MBUTTONDOWN) + DEFINE_WIN32_MESSAGE(WM_MBUTTONUP) + DEFINE_WIN32_MESSAGE(WM_MBUTTONDBLCLK) + DEFINE_WIN32_MESSAGE(WM_MOUSEWHEEL) + DEFINE_WIN32_MESSAGE(WM_XBUTTONDOWN) + DEFINE_WIN32_MESSAGE(WM_XBUTTONUP) + DEFINE_WIN32_MESSAGE(WM_XBUTTONDBLCLK) + DEFINE_WIN32_MESSAGE(WM_MOUSEHWHEEL) // WM_MOUSELAST + DEFINE_WIN32_MESSAGE(WM_PARENTNOTIFY) + DEFINE_WIN32_MESSAGE(WM_ENTERMENULOOP) + DEFINE_WIN32_MESSAGE(WM_EXITMENULOOP) + DEFINE_WIN32_MESSAGE(WM_NEXTMENU) + DEFINE_WIN32_MESSAGE(WM_SIZING) + DEFINE_WIN32_MESSAGE(WM_CAPTURECHANGED) + DEFINE_WIN32_MESSAGE(WM_MOVING) + DEFINE_WIN32_MESSAGE(WM_POWERBROADCAST) + DEFINE_WIN32_MESSAGE(WM_DEVICECHANGE) + DEFINE_WIN32_MESSAGE(WM_MDICREATE) + DEFINE_WIN32_MESSAGE(WM_MDIDESTROY) + DEFINE_WIN32_MESSAGE(WM_MDIACTIVATE) + DEFINE_WIN32_MESSAGE(WM_MDIRESTORE) + DEFINE_WIN32_MESSAGE(WM_MDINEXT) + DEFINE_WIN32_MESSAGE(WM_MDIMAXIMIZE) + DEFINE_WIN32_MESSAGE(WM_MDITILE) + DEFINE_WIN32_MESSAGE(WM_MDICASCADE) + DEFINE_WIN32_MESSAGE(WM_MDIICONARRANGE) + DEFINE_WIN32_MESSAGE(WM_MDIGETACTIVE) + DEFINE_WIN32_MESSAGE(WM_MDISETMENU) + DEFINE_WIN32_MESSAGE(WM_ENTERSIZEMOVE) + DEFINE_WIN32_MESSAGE(WM_EXITSIZEMOVE) + DEFINE_WIN32_MESSAGE(WM_DROPFILES) + DEFINE_WIN32_MESSAGE(WM_MDIREFRESHMENU) + DEFINE_WIN32_MESSAGE(WM_POINTERDEVICECHANGE) + DEFINE_WIN32_MESSAGE(WM_POINTERDEVICEINRANGE) + DEFINE_WIN32_MESSAGE(WM_POINTERDEVICEOUTOFRANGE) + DEFINE_WIN32_MESSAGE(WM_TOUCH) + DEFINE_WIN32_MESSAGE(WM_NCPOINTERUPDATE) + DEFINE_WIN32_MESSAGE(WM_NCPOINTERDOWN) + DEFINE_WIN32_MESSAGE(WM_NCPOINTERUP) + DEFINE_WIN32_MESSAGE(WM_POINTERUPDATE) + DEFINE_WIN32_MESSAGE(WM_POINTERDOWN) + DEFINE_WIN32_MESSAGE(WM_POINTERUP) + DEFINE_WIN32_MESSAGE(WM_POINTERENTER) + DEFINE_WIN32_MESSAGE(WM_POINTERLEAVE) + DEFINE_WIN32_MESSAGE(WM_POINTERACTIVATE) + DEFINE_WIN32_MESSAGE(WM_POINTERCAPTURECHANGED) + DEFINE_WIN32_MESSAGE(WM_TOUCHHITTESTING) + DEFINE_WIN32_MESSAGE(WM_POINTERWHEEL) + DEFINE_WIN32_MESSAGE(WM_POINTERHWHEEL) + DEFINE_WIN32_MESSAGE(DM_POINTERHITTEST) + DEFINE_WIN32_MESSAGE(WM_POINTERROUTEDTO) + DEFINE_WIN32_MESSAGE(WM_POINTERROUTEDAWAY) + DEFINE_WIN32_MESSAGE(WM_POINTERROUTEDRELEASED) + DEFINE_WIN32_MESSAGE(WM_IME_SETCONTEXT) + DEFINE_WIN32_MESSAGE(WM_IME_NOTIFY) + DEFINE_WIN32_MESSAGE(WM_IME_CONTROL) + DEFINE_WIN32_MESSAGE(WM_IME_COMPOSITIONFULL) + DEFINE_WIN32_MESSAGE(WM_IME_SELECT) + DEFINE_WIN32_MESSAGE(WM_IME_CHAR) + DEFINE_WIN32_MESSAGE(WM_IME_REQUEST) + DEFINE_WIN32_MESSAGE(WM_IME_KEYDOWN) + DEFINE_WIN32_MESSAGE(WM_IME_KEYUP) + DEFINE_WIN32_MESSAGE(WM_MOUSEHOVER) + DEFINE_WIN32_MESSAGE(WM_MOUSELEAVE) + DEFINE_WIN32_MESSAGE(WM_NCMOUSEHOVER) + DEFINE_WIN32_MESSAGE(WM_NCMOUSELEAVE) + DEFINE_WIN32_MESSAGE(WM_WTSSESSION_CHANGE) + DEFINE_WIN32_MESSAGE(WM_TABLET_FIRST) + DEFINE_WIN32_MESSAGE(WM_TABLET_LAST) + DEFINE_WIN32_MESSAGE(WM_DPICHANGED) + DEFINE_WIN32_MESSAGE(WM_DPICHANGED_BEFOREPARENT) + DEFINE_WIN32_MESSAGE(WM_DPICHANGED_AFTERPARENT) + DEFINE_WIN32_MESSAGE(WM_GETDPISCALEDSIZE) + DEFINE_WIN32_MESSAGE(WM_CUT) + DEFINE_WIN32_MESSAGE(WM_COPY) + DEFINE_WIN32_MESSAGE(WM_PASTE) + DEFINE_WIN32_MESSAGE(WM_CLEAR) + DEFINE_WIN32_MESSAGE(WM_UNDO) + DEFINE_WIN32_MESSAGE(WM_RENDERFORMAT) + DEFINE_WIN32_MESSAGE(WM_RENDERALLFORMATS) + DEFINE_WIN32_MESSAGE(WM_DESTROYCLIPBOARD) + DEFINE_WIN32_MESSAGE(WM_DRAWCLIPBOARD) + DEFINE_WIN32_MESSAGE(WM_PAINTCLIPBOARD) + DEFINE_WIN32_MESSAGE(WM_VSCROLLCLIPBOARD) + DEFINE_WIN32_MESSAGE(WM_SIZECLIPBOARD) + DEFINE_WIN32_MESSAGE(WM_ASKCBFORMATNAME) + DEFINE_WIN32_MESSAGE(WM_CHANGECBCHAIN) + DEFINE_WIN32_MESSAGE(WM_HSCROLLCLIPBOARD) + DEFINE_WIN32_MESSAGE(WM_QUERYNEWPALETTE) + DEFINE_WIN32_MESSAGE(WM_PALETTEISCHANGING) + DEFINE_WIN32_MESSAGE(WM_PALETTECHANGED) + DEFINE_WIN32_MESSAGE(WM_HOTKEY) + DEFINE_WIN32_MESSAGE(WM_PRINT) + DEFINE_WIN32_MESSAGE(WM_PRINTCLIENT) + DEFINE_WIN32_MESSAGE(WM_APPCOMMAND) + DEFINE_WIN32_MESSAGE(WM_THEMECHANGED) + DEFINE_WIN32_MESSAGE(WM_CLIPBOARDUPDATE) + DEFINE_WIN32_MESSAGE(WM_DWMCOMPOSITIONCHANGED) + DEFINE_WIN32_MESSAGE(WM_DWMNCRENDERINGCHANGED) + DEFINE_WIN32_MESSAGE(WM_DWMCOLORIZATIONCOLORCHANGED) + DEFINE_WIN32_MESSAGE(WM_DWMWINDOWMAXIMIZEDCHANGE) + DEFINE_WIN32_MESSAGE(WM_DWMSENDICONICTHUMBNAIL) + DEFINE_WIN32_MESSAGE(WM_DWMSENDICONICLIVEPREVIEWBITMAP) + DEFINE_WIN32_MESSAGE(WM_GETTITLEBARINFOEX) + DEFINE_WIN32_MESSAGE(WM_HANDHELDFIRST) + DEFINE_WIN32_MESSAGE(WM_HANDHELDLAST) + DEFINE_WIN32_MESSAGE(WM_AFXFIRST) + DEFINE_WIN32_MESSAGE(WM_AFXLAST) + DEFINE_WIN32_MESSAGE(WM_PENWINFIRST) + DEFINE_WIN32_MESSAGE(WM_PENWINLAST) + DEFINE_WIN32_MESSAGE(WM_APP) + DEFINE_WIN32_MESSAGE(WM_USER) +}; +#undef DEFINE_WIN32_MESSAGE + [[nodiscard]] bool operator==(const POINT &lhs, const POINT &rhs) noexcept { return ((lhs.x == rhs.x) && (lhs.y == rhs.y)); @@ -530,6 +826,12 @@ Q_GLOBAL_STATIC(Win32UtilsInternal, g_win32UtilsData) } } +[[nodiscard]] static inline bool isWin32MessageDebuggingEnabled() +{ + static const bool result = (qEnvironmentVariableIntValue("FRAMELESSHELPER_ENABLE_WIN32_MESSAGE_DEBUGGING") != 0); + return result; +} + [[nodiscard]] static inline QByteArray qtNativeEventType() { static const auto result = FRAMELESSHELPER_BYTEARRAY_LITERAL("windows_generic_MSG"); @@ -556,6 +858,26 @@ Q_GLOBAL_STATIC(Win32UtilsInternal, g_win32UtilsData) return false; } +[[nodiscard]] static inline bool isMouseMessage(const UINT message, bool *isNonClient = nullptr) +{ + if (((message >= WM_MOUSEFIRST) && (message <= WM_MOUSELAST)) + || ((message == WM_MOUSEHOVER) || (message == WM_MOUSELEAVE))) { + if (isNonClient) { + *isNonClient = false; + } + return true; + } + if (((message >= WM_NCMOUSEMOVE) && (message <= WM_NCMBUTTONDBLCLK)) + || ((message >= WM_NCXBUTTONDOWN) && (message <= WM_NCXBUTTONDBLCLK)) + || ((message == WM_NCMOUSEHOVER) || (message == WM_NCMOUSELEAVE))) { + if (isNonClient) { + *isNonClient = true; + } + return true; + } + return false; +} + [[nodiscard]] static inline bool usePureQtImplementation() { static const bool result = FramelessConfig::instance()->isSet(Option::UseCrossPlatformQtImplementation); @@ -569,6 +891,61 @@ Q_GLOBAL_STATIC(Win32UtilsInternal, g_win32UtilsData) if (!hWnd) { return 0; } + if (isWin32MessageDebuggingEnabled()) { + const auto it = std::find(g_win32MessageMap.cbegin(), g_win32MessageMap.cend(), Win32Message{ uMsg, nullptr }); + if (it != g_win32MessageMap.cend()) { + QString text = {}; + QTextStream stream(&text); + stream << "Win32 message received: " << it->Str << " (0x" + << QString::number(uMsg, 16).toUpper().rightJustified(4, u'0') << ')'; + bool isNonClientMouseMessage = false; + if (isMouseMessage(uMsg, &isNonClientMouseMessage)) { + if (isNonClientMouseMessage) { + const auto screenPos = [uMsg, lParam]() -> POINT { + if (uMsg == WM_NCMOUSELEAVE) { + const DWORD dwScreenPos = ::GetMessagePos(); + return POINT{ GET_X_LPARAM(dwScreenPos), GET_Y_LPARAM(dwScreenPos) }; + } else { + return POINT{ GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + } + }(); + POINT clientPos = screenPos; + if (::ScreenToClient(hWnd, &clientPos) == FALSE) { + WARNING << Utils::getSystemErrorMessage(kScreenToClient); + clientPos = {}; + } + stream << ", screen coordinate: POINT(x: " << screenPos.x << ", y: " + << screenPos.y << "), client coordinate: POINT(x: " + << clientPos.x << ", y: " << clientPos.y << ')'; + } else { + const auto clientPos = [hWnd, uMsg, lParam]() -> POINT { + if (uMsg == WM_MOUSELEAVE) { + const DWORD dwScreenPos = ::GetMessagePos(); + const auto screenPos = POINT{ GET_X_LPARAM(dwScreenPos), GET_Y_LPARAM(dwScreenPos) }; + POINT clientPos = screenPos; + if (::ScreenToClient(hWnd, &clientPos) == FALSE) { + WARNING << Utils::getSystemErrorMessage(kScreenToClient); + return {}; + } else { + return clientPos; + } + } else { + return POINT{ GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + } + }(); + POINT screenPos = clientPos; + if (::ClientToScreen(hWnd, &screenPos) == FALSE) { + WARNING << Utils::getSystemErrorMessage(kClientToScreen); + screenPos = {}; + } + stream << ", screen coordinate: POINT(x: " << screenPos.x << ", y: " + << screenPos.y << "), client coordinate: POINT(x: " + << clientPos.x << ", y: " << clientPos.y << ')'; + } + } + DEBUG.noquote() << text; + } + } const auto windowId = reinterpret_cast(hWnd); const auto it = g_win32UtilsData()->data.constFind(windowId); if (it == g_win32UtilsData()->data.constEnd()) { @@ -593,9 +970,16 @@ Q_GLOBAL_STATIC(Win32UtilsInternal, g_win32UtilsData) message.wParam = wParam; message.lParam = lParam; message.time = ::GetMessageTime(); +#if 1 const DWORD dwScreenPos = ::GetMessagePos(); message.pt.x = GET_X_LPARAM(dwScreenPos); message.pt.y = GET_Y_LPARAM(dwScreenPos); +#else + if (::GetCursorPos(&message.pt) == FALSE) { + WARNING << Utils::getSystemErrorMessage(kGetCursorPos); + message.pt = {}; + } +#endif if (!isNonClientMessage(uMsg)) { if (::ScreenToClient(hWnd, &message.pt) == FALSE) { WARNING << Utils::getSystemErrorMessage(kScreenToClient); @@ -2649,12 +3033,11 @@ bool Utils::removeMicaWindow(const WId windowId) return true; } -quint64 Utils::getMouseButtonsAndModifiers(const bool async) +quint64 Utils::getKeyState() { quint64 result = 0; - const auto get = [async](const int virtualKey) -> bool { - const auto stateCode = (async ? ::GetAsyncKeyState(virtualKey) : ::GetKeyState(virtualKey)); - return (stateCode < 0); + const auto get = [](const int virtualKey) -> bool { + return (::GetAsyncKeyState(virtualKey) < 0); }; const bool buttonSwapped = (::GetSystemMetrics(SM_SWAPBUTTON) != FALSE); if (get(VK_LBUTTON)) { @@ -2663,12 +3046,6 @@ quint64 Utils::getMouseButtonsAndModifiers(const bool async) if (get(VK_RBUTTON)) { result |= (buttonSwapped ? MK_LBUTTON : MK_RBUTTON); } - if (get(VK_SHIFT)) { - result |= MK_SHIFT; - } - if (get(VK_CONTROL)) { - result |= MK_CONTROL; - } if (get(VK_MBUTTON)) { result |= MK_MBUTTON; } @@ -2681,31 +3058,6 @@ quint64 Utils::getMouseButtonsAndModifiers(const bool async) return result; } -Qt::MouseButtons Utils::queryMouseButtons() -{ - const quint64 buttonMask = getMouseButtonsAndModifiers(false); - 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 7dca97c4..9f697f67 100644 --- a/src/quick/framelessquickhelper.cpp +++ b/src/quick/framelessquickhelper.cpp @@ -853,69 +853,8 @@ bool FramelessQuickHelperPrivate::shouldIgnoreMouseEvents(const QPoint &pos) con void FramelessQuickHelperPrivate::setSystemButtonState(const QuickGlobal::SystemButtonType button, const QuickGlobal::ButtonState state) { -#ifdef FRAMELESSHELPER_QUICK_NO_PRIVATE Q_UNUSED(button); Q_UNUSED(state); -#else // !FRAMELESSHELPER_QUICK_NO_PRIVATE - Q_ASSERT(button != QuickGlobal::SystemButtonType::Unknown); - if (button == QuickGlobal::SystemButtonType::Unknown) { - return; - } - const FramelessQuickHelperData *data = getWindowData(); - if (!data) { - return; - } - QQuickItem *quickButton = nullptr; - switch (button) { - case QuickGlobal::SystemButtonType::WindowIcon: - quickButton = data->windowIconButton; - break; - case QuickGlobal::SystemButtonType::Help: - quickButton = data->contextHelpButton; - break; - case QuickGlobal::SystemButtonType::Minimize: - quickButton = data->minimizeButton; - break; - case QuickGlobal::SystemButtonType::Maximize: - case QuickGlobal::SystemButtonType::Restore: - quickButton = data->maximizeButton; - break; - case QuickGlobal::SystemButtonType::Close: - quickButton = data->closeButton; - break; - case QuickGlobal::SystemButtonType::Unknown: - Q_UNREACHABLE_RETURN(void(0)); - } - if (!quickButton) { - return; - } - const auto updateButtonState = [state](QQuickItem *btn) -> void { - Q_ASSERT(btn); - if (!btn) { - return; - } - const QQuickWindow *window = btn->window(); - Q_ASSERT(window); - if (!window) { - return; - } - const QScreen *screen = (window->screen() ? window->screen() : QGuiApplication::primaryScreen()); - 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, true); - }; - updateButtonState(quickButton); -#endif // FRAMELESSHELPER_QUICK_NO_PRIVATE } const FramelessQuickHelperData *FramelessQuickHelperPrivate::getWindowData() const diff --git a/src/widgets/framelesswidgetshelper.cpp b/src/widgets/framelesswidgetshelper.cpp index f4a73f6f..bced1cb6 100644 --- a/src/widgets/framelesswidgetshelper.cpp +++ b/src/widgets/framelesswidgetshelper.cpp @@ -803,76 +803,8 @@ bool FramelessWidgetsHelperPrivate::shouldIgnoreMouseEvents(const QPoint &pos) c void FramelessWidgetsHelperPrivate::setSystemButtonState(const SystemButtonType button, const ButtonState state) { - Q_ASSERT(button != SystemButtonType::Unknown); - if (button == SystemButtonType::Unknown) { - return; - } - const FramelessWidgetsHelperData *data = getWindowData(); - if (!data) { - return; - } - QWidget *widgetButton = nullptr; - switch (button) { - case SystemButtonType::WindowIcon: - if (data->windowIconButton) { - widgetButton = data->windowIconButton; - } - break; - case SystemButtonType::Help: - if (data->contextHelpButton) { - widgetButton = data->contextHelpButton; - } - break; - case SystemButtonType::Minimize: - if (data->minimizeButton) { - widgetButton = data->minimizeButton; - } - break; - case SystemButtonType::Maximize: - case SystemButtonType::Restore: - if (data->maximizeButton) { - widgetButton = data->maximizeButton; - } - break; - case SystemButtonType::Close: - if (data->closeButton) { - widgetButton = data->closeButton; - } - break; - case SystemButtonType::Unknown: - Q_UNREACHABLE_RETURN(void(0)); - } - if (!widgetButton) { - return; - } - const auto updateButtonState = [state](QWidget *btn) -> void { - Q_ASSERT(btn); - if (!btn) { - return; - } - const QWidget *window = btn->window(); - Q_ASSERT(window); - if (!window) { - return; - } -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - const QScreen *screen = window->screen(); -#else - const QScreen *screen = QGuiApplication::primaryScreen(); -#endif - const QPoint globalPos = (screen ? QCursor::pos(screen) : QCursor::pos()); - const QPoint localPos = btn->mapFromGlobal(globalPos); - const QPoint scenePos = window->mapFromGlobal(globalPos); -#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); + Q_UNUSED(button); + Q_UNUSED(state); } void FramelessWidgetsHelperPrivate::moveWindowToDesktopCenter() diff --git a/src/widgets/standardsystembutton.cpp b/src/widgets/standardsystembutton.cpp index 4735a78b..95e0141d 100644 --- a/src/widgets/standardsystembutton.cpp +++ b/src/widgets/standardsystembutton.cpp @@ -154,9 +154,9 @@ bool StandardSystemButtonPrivate::isActive() const return m_active; } -int StandardSystemButtonPrivate::iconSize2() const +int StandardSystemButtonPrivate::glyphSize() const { - return m_iconSize2.value_or(FramelessManagerPrivate::getIconFont().pointSize()); + return m_glyphSize.value_or(FramelessManagerPrivate::getIconFont().pointSize()); } void StandardSystemButtonPrivate::setHoverColor(const QColor &value) @@ -245,19 +245,19 @@ void StandardSystemButtonPrivate::setActive(const bool value) Q_EMIT q->activeChanged(); } -void StandardSystemButtonPrivate::setIconSize2(const int value) +void StandardSystemButtonPrivate::setGlyphSize(const int value) { Q_ASSERT(value > 0); if (value <= 0) { return; } - if (iconSize2() == value) { + if (glyphSize() == value) { return; } - m_iconSize2 = value; + m_glyphSize = value; Q_Q(StandardSystemButton); q->update(); - Q_EMIT q->iconSize2Changed(); + Q_EMIT q->glyphSizeChanged(); } void StandardSystemButtonPrivate::paintEventHandler(QPaintEvent *event) @@ -300,8 +300,8 @@ void StandardSystemButtonPrivate::paintEventHandler(QPaintEvent *event) }()); painter.setFont([this]() -> QFont { QFont font = FramelessManagerPrivate::getIconFont(); - if (m_iconSize2.has_value()) { - font.setPointSize(m_iconSize2.value()); + if (m_glyphSize.has_value()) { + font.setPointSize(m_glyphSize.value()); } return font; }()); @@ -316,8 +316,10 @@ void StandardSystemButtonPrivate::initialize() FramelessManagerPrivate::initializeIconFont(); Q_Q(StandardSystemButton); q->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - q->setFixedSize(kDefaultSystemButtonSize); + q->setFixedSize(getRecommendedButtonSize()); q->setIconSize(kDefaultSystemButtonIconSize); + q->setMouseTracking(true); + q->setAttribute(Qt::WA_Hover); } StandardSystemButton::StandardSystemButton(QWidget *parent) @@ -405,10 +407,10 @@ bool StandardSystemButton::isActive() const return d->isActive(); } -int StandardSystemButton::iconSize2() const +int StandardSystemButton::glyphSize() const { Q_D(const StandardSystemButton); - return d->iconSize2(); + return d->glyphSize(); } void StandardSystemButton::setPressColor(const QColor &value) @@ -441,10 +443,10 @@ void StandardSystemButton::setActive(const bool value) d->setActive(value); } -void StandardSystemButton::setIconSize2(const int value) +void StandardSystemButton::setGlyphSize(const int value) { Q_D(StandardSystemButton); - d->setIconSize2(value); + d->setGlyphSize(value); } void StandardSystemButton::paintEvent(QPaintEvent *event)