Compare commits

...

1 Commits

Author SHA1 Message Date
Yuhang Zhao 0a33aafb5f emulate qt mouse events when hovering titlebar 2023-04-09 13:27:24 +08:00
13 changed files with 331 additions and 247 deletions

View File

@ -259,145 +259,148 @@ Q_NAMESPACE_EXPORT(FRAMELESSHELPER_CORE_API)
[[maybe_unused]] inline const QByteArray kSysMenuDisableRestoreVar [[maybe_unused]] inline const QByteArray kSysMenuDisableRestoreVar
= FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_SYSTEM_MENU_DISABLE_RESTORE"); = FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_SYSTEM_MENU_DISABLE_RESTORE");
enum class Option enum class Option : quint8
{ {
UseCrossPlatformQtImplementation = 0, UseCrossPlatformQtImplementation,
ForceHideWindowFrameBorder = 1, ForceHideWindowFrameBorder,
ForceShowWindowFrameBorder = 2, ForceShowWindowFrameBorder,
DisableWindowsSnapLayout = 3, DisableWindowsSnapLayout,
WindowUseRoundCorners = 4, WindowUseRoundCorners,
CenterWindowBeforeShow = 5, CenterWindowBeforeShow,
EnableBlurBehindWindow = 6, EnableBlurBehindWindow,
ForceNonNativeBackgroundBlur = 7, ForceNonNativeBackgroundBlur,
DisableLazyInitializationForMicaMaterial = 8, DisableLazyInitializationForMicaMaterial,
ForceNativeBackgroundBlur = 9 ForceNativeBackgroundBlur
}; };
Q_ENUM_NS(Option) Q_ENUM_NS(Option)
enum class SystemTheme enum class SystemTheme : quint8
{ {
Unknown = -1, Unknown,
Light = 0, Light,
Dark = 1, Dark,
HighContrast = 2 HighContrast
}; };
Q_ENUM_NS(SystemTheme) Q_ENUM_NS(SystemTheme)
enum class SystemButtonType enum class SystemButtonType : quint8
{ {
Unknown = -1, Unknown,
WindowIcon = 0, WindowIcon,
Help = 1, Help,
Minimize = 2, Minimize,
Maximize = 3, Maximize,
Restore = 4, Restore,
Close = 5 Close
}; };
Q_ENUM_NS(SystemButtonType) Q_ENUM_NS(SystemButtonType)
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
enum class DwmColorizationArea enum class DwmColorizationArea : quint8
{ {
None = 0, None,
StartMenu_TaskBar_ActionCenter = 1, StartMenu_TaskBar_ActionCenter,
TitleBar_WindowBorder = 2, TitleBar_WindowBorder,
All = 3 All
}; };
Q_ENUM_NS(DwmColorizationArea) Q_ENUM_NS(DwmColorizationArea)
#endif // Q_OS_WINDOWS #endif // Q_OS_WINDOWS
enum class ButtonState enum class ButtonState : quint8
{ {
Unspecified = -1, MouseEntered,
Hovered = 0, MouseLeaved,
Pressed = 1, MouseMoving,
Clicked = 2 MousePressed,
MouseReleased
}; };
Q_ENUM_NS(ButtonState) Q_ENUM_NS(ButtonState)
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
enum class WindowsVersion enum class WindowsVersion : quint8
{ {
_2000 = 0, _2000,
_XP = 1, _XP,
_XP_64 = 2, _XP_64,
_Vista,
_Vista_SP1,
_Vista_SP2,
_7,
_7_SP1,
_8,
_8_1,
_8_1_Update1,
_10_1507,
_10_1511,
_10_1607,
_10_1703,
_10_1709,
_10_1803,
_10_1809,
_10_1903,
_10_1909,
_10_2004,
_10_20H2,
_10_21H1,
_10_21H2,
_10_22H2,
_11_21H2,
_11_22H2,
_WS_03 = _XP_64, // Windows Server 2003 _WS_03 = _XP_64, // Windows Server 2003
_Vista = 3,
_Vista_SP1 = 4,
_Vista_SP2 = 5,
_7 = 6,
_7_SP1 = 7,
_8 = 8,
_8_1 = 9,
_8_1_Update1 = 10,
_10_1507 = 11,
_10_1511 = 12,
_10_1607 = 13,
_10_1703 = 14,
_10_1709 = 15,
_10_1803 = 16,
_10_1809 = 17,
_10_1903 = 18,
_10_1909 = 19,
_10_2004 = 20,
_10_20H2 = 21,
_10_21H1 = 22,
_10_21H2 = 23,
_10_22H2 = 24,
_10 = _10_1507, _10 = _10_1507,
_11_21H2 = 25,
_11_22H2 = 26,
_11 = _11_21H2, _11 = _11_21H2,
Latest = _11_22H2 Latest = _11_22H2
}; };
Q_ENUM_NS(WindowsVersion) Q_ENUM_NS(WindowsVersion)
#endif // Q_OS_WINDOWS #endif // Q_OS_WINDOWS
enum class BlurMode enum class BlurMode : quint8
{ {
Disable = 0, // Do not enable blur behind window Disable, // Do not enable blur behind window
Default = 1, // Use platform default blur mode Default, // Use platform default blur mode
Windows_Aero = 2, // Windows only, use the traditional DWM blur Windows_Aero, // Windows only, use the traditional DWM blur
Windows_Acrylic = 3, // Windows only, use the Acrylic blur Windows_Acrylic, // Windows only, use the Acrylic blur
Windows_Mica = 4, // Windows only, use the Mica material Windows_Mica, // Windows only, use the Mica material
Windows_MicaAlt = 5 // Windows only, use the Mica Alt material Windows_MicaAlt // Windows only, use the Mica Alt material
}; };
Q_ENUM_NS(BlurMode) Q_ENUM_NS(BlurMode)
enum class WallpaperAspectStyle enum class WallpaperAspectStyle : quint8
{ {
Fill = 0, // Keep aspect ratio to fill, expand/crop if necessary. Fill, // Keep aspect ratio to fill, expand/crop if necessary.
Fit = 1, // Keep aspect ratio to fill, but don't expand/crop. Fit, // Keep aspect ratio to fill, but don't expand/crop.
Stretch = 2, // Ignore aspect ratio to fill. Stretch, // Ignore aspect ratio to fill.
Tile = 3, Tile,
Center = 4, Center,
Span = 5 // ??? Span // ???
}; };
Q_ENUM_NS(WallpaperAspectStyle) Q_ENUM_NS(WallpaperAspectStyle)
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
enum class RegistryRootKey enum class RegistryRootKey : quint8
{ {
ClassesRoot = 0, ClassesRoot,
CurrentUser = 1, CurrentUser,
LocalMachine = 2, LocalMachine,
Users = 3, Users,
PerformanceData = 4, PerformanceData,
CurrentConfig = 5, CurrentConfig,
DynData = 6, DynData,
CurrentUserLocalSettings = 7, CurrentUserLocalSettings,
PerformanceText = 8, PerformanceText,
PerformanceNlsText = 9 PerformanceNlsText
}; };
Q_ENUM_NS(RegistryRootKey) Q_ENUM_NS(RegistryRootKey)
#endif // Q_OS_WINDOWS #endif // Q_OS_WINDOWS
enum class WindowEdge : quint32 enum class WindowEdge : quint8
{ {
Left = 0x00000001, Left = 1 << 0,
Top = 0x00000002, Top = 1 << 1,
Right = 0x00000004, Right = 1 << 2,
Bottom = 0x00000008 Bottom = 1 << 3
}; };
Q_ENUM_NS(WindowEdge) Q_ENUM_NS(WindowEdge)
Q_DECLARE_FLAGS(WindowEdges, WindowEdge) Q_DECLARE_FLAGS(WindowEdges, WindowEdge)
@ -405,23 +408,23 @@ Q_FLAG_NS(WindowEdges)
Q_DECLARE_OPERATORS_FOR_FLAGS(WindowEdges) Q_DECLARE_OPERATORS_FOR_FLAGS(WindowEdges)
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
enum class DpiAwareness enum class DpiAwareness : quint8
{ {
Unknown = -1, Unknown,
Unaware = 0, Unaware,
System = 1, System,
PerMonitor = 2, PerMonitor,
PerMonitorVersion2 = 3, PerMonitorVersion2,
Unaware_GdiScaled = 4 Unaware_GdiScaled
}; };
Q_ENUM_NS(DpiAwareness) Q_ENUM_NS(DpiAwareness)
#endif // Q_OS_WINDOWS #endif // Q_OS_WINDOWS
enum class WindowCornerStyle enum class WindowCornerStyle : quint8
{ {
Default = 0, Default,
Square = 1, Square,
Round = 2 Round
}; };
Q_ENUM_NS(WindowCornerStyle) Q_ENUM_NS(WindowCornerStyle)

View File

@ -52,7 +52,7 @@ using ScreenToWindowCallback = std::function<QPoint(const QPoint &)>;
using IsInsideSystemButtonsCallback = std::function<bool(const QPoint &, Global::SystemButtonType *)>; using IsInsideSystemButtonsCallback = std::function<bool(const QPoint &, Global::SystemButtonType *)>;
using IsInsideTitleBarDraggableAreaCallback = std::function<bool(const QPoint &)>; using IsInsideTitleBarDraggableAreaCallback = std::function<bool(const QPoint &)>;
using GetWindowDevicePixelRatioCallback = std::function<qreal()>; using GetWindowDevicePixelRatioCallback = std::function<qreal()>;
using SetSystemButtonStateCallback = std::function<void(const Global::SystemButtonType, const Global::ButtonState)>; using SetSystemButtonStateCallback = std::function<void(const Global::SystemButtonType, const Global::ButtonState, const QPoint &)>;
using GetWindowIdCallback = std::function<WId()>; using GetWindowIdCallback = std::function<WId()>;
using ShouldIgnoreMouseEventsCallback = std::function<bool(const QPoint &)>; using ShouldIgnoreMouseEventsCallback = std::function<bool(const QPoint &)>;
using ShowSystemMenuCallback = std::function<void(const QPoint &)>; using ShowSystemMenuCallback = std::function<void(const QPoint &)>;

View File

@ -90,7 +90,7 @@ public:
explicit QuickGlobal(QObject *parent = nullptr); explicit QuickGlobal(QObject *parent = nullptr);
~QuickGlobal() override; ~QuickGlobal() override;
enum class SystemTheme enum class SystemTheme : quint8
{ {
FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemTheme, Unknown) FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemTheme, Unknown)
FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemTheme, Light) FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemTheme, Light)
@ -99,7 +99,7 @@ public:
}; };
Q_ENUM(SystemTheme) Q_ENUM(SystemTheme)
enum class SystemButtonType enum class SystemButtonType : quint8
{ {
FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemButtonType, Unknown) FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemButtonType, Unknown)
FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemButtonType, WindowIcon) FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemButtonType, WindowIcon)
@ -111,16 +111,17 @@ public:
}; };
Q_ENUM(SystemButtonType) Q_ENUM(SystemButtonType)
enum class ButtonState enum class ButtonState : quint8
{ {
FRAMELESSHELPER_QUICK_ENUM_VALUE(ButtonState, Unspecified) FRAMELESSHELPER_QUICK_ENUM_VALUE(ButtonState, MouseEntered)
FRAMELESSHELPER_QUICK_ENUM_VALUE(ButtonState, Hovered) FRAMELESSHELPER_QUICK_ENUM_VALUE(ButtonState, MouseLeaved)
FRAMELESSHELPER_QUICK_ENUM_VALUE(ButtonState, Pressed) FRAMELESSHELPER_QUICK_ENUM_VALUE(ButtonState, MouseMoving)
FRAMELESSHELPER_QUICK_ENUM_VALUE(ButtonState, Clicked) FRAMELESSHELPER_QUICK_ENUM_VALUE(ButtonState, MousePressed)
FRAMELESSHELPER_QUICK_ENUM_VALUE(ButtonState, MouseReleased)
}; };
Q_ENUM(ButtonState) Q_ENUM(ButtonState)
enum class BlurMode enum class BlurMode : quint8
{ {
FRAMELESSHELPER_QUICK_ENUM_VALUE(BlurMode, Disable) FRAMELESSHELPER_QUICK_ENUM_VALUE(BlurMode, Disable)
FRAMELESSHELPER_QUICK_ENUM_VALUE(BlurMode, Default) FRAMELESSHELPER_QUICK_ENUM_VALUE(BlurMode, Default)
@ -131,7 +132,7 @@ public:
}; };
Q_ENUM(BlurMode) Q_ENUM(BlurMode)
enum class WindowEdge : quint32 enum class WindowEdge : quint8
{ {
FRAMELESSHELPER_QUICK_ENUM_VALUE(WindowEdge, Left) FRAMELESSHELPER_QUICK_ENUM_VALUE(WindowEdge, Left)
FRAMELESSHELPER_QUICK_ENUM_VALUE(WindowEdge, Top) FRAMELESSHELPER_QUICK_ENUM_VALUE(WindowEdge, Top)

View File

@ -93,7 +93,7 @@ private:
Q_NODISCARD bool isInSystemButtons(const QPoint &pos, QuickGlobal::SystemButtonType *button) const; Q_NODISCARD bool isInSystemButtons(const QPoint &pos, QuickGlobal::SystemButtonType *button) const;
Q_NODISCARD bool isInTitleBarDraggableArea(const QPoint &pos) const; Q_NODISCARD bool isInTitleBarDraggableArea(const QPoint &pos) const;
Q_NODISCARD bool shouldIgnoreMouseEvents(const QPoint &pos) const; Q_NODISCARD bool shouldIgnoreMouseEvents(const QPoint &pos) const;
void setSystemButtonState(const QuickGlobal::SystemButtonType button, const QuickGlobal::ButtonState state); void setSystemButtonState(const QuickGlobal::SystemButtonType button, const QuickGlobal::ButtonState state, const QPoint &globalPos);
Q_NODISCARD QuickHelperData getWindowData() const; Q_NODISCARD QuickHelperData getWindowData() const;
Q_NODISCARD QuickHelperData *getWindowDataMutable() const; Q_NODISCARD QuickHelperData *getWindowDataMutable() const;
void rebindWindow(); void rebindWindow();

View File

@ -94,7 +94,7 @@ private:
Q_NODISCARD bool isInSystemButtons(const QPoint &pos, Global::SystemButtonType *button) const; Q_NODISCARD bool isInSystemButtons(const QPoint &pos, Global::SystemButtonType *button) const;
Q_NODISCARD bool isInTitleBarDraggableArea(const QPoint &pos) const; Q_NODISCARD bool isInTitleBarDraggableArea(const QPoint &pos) const;
Q_NODISCARD bool shouldIgnoreMouseEvents(const QPoint &pos) const; Q_NODISCARD bool shouldIgnoreMouseEvents(const QPoint &pos) const;
void setSystemButtonState(const Global::SystemButtonType button, const Global::ButtonState state); void setSystemButtonState(const Global::SystemButtonType button, const Global::ButtonState state, const QPoint &globalPos);
Q_NODISCARD QWidget *findTopLevelWindow() const; Q_NODISCARD QWidget *findTopLevelWindow() const;
Q_NODISCARD WidgetsHelperData getWindowData() const; Q_NODISCARD WidgetsHelperData getWindowData() const;
Q_NODISCARD WidgetsHelperData *getWindowDataMutable() const; Q_NODISCARD WidgetsHelperData *getWindowDataMutable() const;

View File

@ -115,14 +115,14 @@ void ChromePalettePrivate::refresh()
titleBarInactiveForegroundColor_sys = kDefaultDarkGrayColor; titleBarInactiveForegroundColor_sys = kDefaultDarkGrayColor;
chromeButtonNormalColor_sys = kDefaultTransparentColor; chromeButtonNormalColor_sys = kDefaultTransparentColor;
chromeButtonHoverColor_sys = chromeButtonHoverColor_sys =
Utils::calculateSystemButtonBackgroundColor(SystemButtonType::Minimize, ButtonState::Hovered); Utils::calculateSystemButtonBackgroundColor(SystemButtonType::Minimize, ButtonState::MouseEntered);
chromeButtonPressColor_sys = chromeButtonPressColor_sys =
Utils::calculateSystemButtonBackgroundColor(SystemButtonType::Minimize, ButtonState::Pressed); Utils::calculateSystemButtonBackgroundColor(SystemButtonType::Minimize, ButtonState::MousePressed);
closeButtonNormalColor_sys = kDefaultTransparentColor; closeButtonNormalColor_sys = kDefaultTransparentColor;
closeButtonHoverColor_sys = closeButtonHoverColor_sys =
Utils::calculateSystemButtonBackgroundColor(SystemButtonType::Close, ButtonState::Hovered); Utils::calculateSystemButtonBackgroundColor(SystemButtonType::Close, ButtonState::MouseEntered);
closeButtonPressColor_sys = closeButtonPressColor_sys =
Utils::calculateSystemButtonBackgroundColor(SystemButtonType::Close, ButtonState::Pressed); Utils::calculateSystemButtonBackgroundColor(SystemButtonType::Close, ButtonState::MousePressed);
Q_Q(ChromePalette); Q_Q(ChromePalette);
Q_EMIT q->titleBarActiveBackgroundColorChanged(); Q_EMIT q->titleBarActiveBackgroundColorChanged();
Q_EMIT q->titleBarInactiveBackgroundColorChanged(); Q_EMIT q->titleBarInactiveBackgroundColorChanged();

View File

@ -158,39 +158,38 @@ Q_GLOBAL_STATIC(Win32Helper, g_win32Helper)
// Hit-testing event should not be considered as a mouse event. // Hit-testing event should not be considered as a mouse event.
const bool isMouseEvent = (((uMsg >= WM_MOUSEFIRST) && (uMsg <= WM_MOUSELAST)) || const bool isMouseEvent = (((uMsg >= WM_MOUSEFIRST) && (uMsg <= WM_MOUSELAST)) ||
((uMsg >= WM_NCMOUSEMOVE) && (uMsg <= WM_NCXBUTTONDBLCLK))); ((uMsg >= WM_NCMOUSEMOVE) && (uMsg <= WM_NCXBUTTONDBLCLK)));
const auto releaseButtons = [&data](const std::optional<SystemButtonType> exclude) -> void { const auto resetButtons = [&data](const std::optional<SystemButtonType> exclude, const QPoint &globalPos) -> void {
static constexpr const auto defaultButtonState = ButtonState::Unspecified;
const SystemButtonType button = exclude.value_or(SystemButtonType::Unknown); const SystemButtonType button = exclude.value_or(SystemButtonType::Unknown);
if (button != SystemButtonType::WindowIcon) { if (button != SystemButtonType::WindowIcon) {
data.params.setSystemButtonState(SystemButtonType::WindowIcon, defaultButtonState); data.params.setSystemButtonState(SystemButtonType::WindowIcon, ButtonState::MouseLeaved, globalPos);
} }
if (button != SystemButtonType::Help) { if (button != SystemButtonType::Help) {
data.params.setSystemButtonState(SystemButtonType::Help, defaultButtonState); data.params.setSystemButtonState(SystemButtonType::Help, ButtonState::MouseLeaved, globalPos);
} }
if (button != SystemButtonType::Minimize) { if (button != SystemButtonType::Minimize) {
data.params.setSystemButtonState(SystemButtonType::Minimize, defaultButtonState); data.params.setSystemButtonState(SystemButtonType::Minimize, ButtonState::MouseLeaved, globalPos);
} }
if (button != SystemButtonType::Maximize) { if (button != SystemButtonType::Maximize) {
data.params.setSystemButtonState(SystemButtonType::Maximize, defaultButtonState); data.params.setSystemButtonState(SystemButtonType::Maximize, ButtonState::MouseLeaved, globalPos);
} }
if (button != SystemButtonType::Restore) { if (button != SystemButtonType::Restore) {
data.params.setSystemButtonState(SystemButtonType::Restore, defaultButtonState); data.params.setSystemButtonState(SystemButtonType::Restore, ButtonState::MouseLeaved, globalPos);
} }
if (button != SystemButtonType::Close) { if (button != SystemButtonType::Close) {
data.params.setSystemButtonState(SystemButtonType::Close, defaultButtonState); data.params.setSystemButtonState(SystemButtonType::Close, ButtonState::MouseLeaved, globalPos);
} }
}; };
const auto hoverButton = [&releaseButtons, &data](const SystemButtonType button) -> void { const auto hoverButton = [&resetButtons, &data](const SystemButtonType button, const QPoint &globalPos) -> void {
releaseButtons(button); resetButtons(button, globalPos);
data.params.setSystemButtonState(button, ButtonState::Hovered); data.params.setSystemButtonState(button, ButtonState::MouseMoving, globalPos);
}; };
const auto pressButton = [&releaseButtons, &data](const SystemButtonType button) -> void { const auto pressButton = [&resetButtons, &data](const SystemButtonType button, const QPoint &globalPos) -> void {
releaseButtons(button); resetButtons(button, globalPos);
data.params.setSystemButtonState(button, ButtonState::Pressed); data.params.setSystemButtonState(button, ButtonState::MousePressed, globalPos);
}; };
const auto clickButton = [&releaseButtons, &data](const SystemButtonType button) -> void { const auto releaseButton = [&resetButtons, &data](const SystemButtonType button, const QPoint &globalPos) -> void {
releaseButtons(button); resetButtons(button, globalPos);
data.params.setSystemButtonState(button, ButtonState::Clicked); data.params.setSystemButtonState(button, ButtonState::MouseReleased, globalPos);
}; };
switch (uMsg) { switch (uMsg) {
case WM_NCHITTEST: { case WM_NCHITTEST: {
@ -235,32 +234,33 @@ Q_GLOBAL_STATIC(Win32Helper, g_win32Helper)
// it can update its visuals. // it can update its visuals.
// - If we're over a button, hover it. // - If we're over a button, hover it.
// - If we're over _anything else_, stop hovering the buttons. // - If we're over _anything else_, stop hovering the buttons.
const QPoint globalPos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
switch (wParam) { switch (wParam) {
case HTTOP: case HTTOP:
case HTCAPTION: { case HTCAPTION: {
releaseButtons(std::nullopt); resetButtons(std::nullopt, globalPos);
// Pass caption-related nonclient messages to the parent window. // Pass caption-related nonclient messages to the parent window.
// Make sure to do this for the HTTOP, which is the top resize // Make sure to do this for the HTTOP, which is the top resize
// border, so we can resize the window on the top. // border, so we can resize the window on the top.
return SendMessageW(parentWindowHandle, uMsg, wParam, lParam); return SendMessageW(parentWindowHandle, uMsg, wParam, lParam);
} }
case HTSYSMENU: case HTSYSMENU:
hoverButton(SystemButtonType::WindowIcon); hoverButton(SystemButtonType::WindowIcon, globalPos);
break; break;
case HTHELP: case HTHELP:
hoverButton(SystemButtonType::Help); hoverButton(SystemButtonType::Help, globalPos);
break; break;
case HTREDUCE: case HTREDUCE:
hoverButton(SystemButtonType::Minimize); hoverButton(SystemButtonType::Minimize, globalPos);
break; break;
case HTZOOM: case HTZOOM:
hoverButton(SystemButtonType::Maximize); hoverButton(SystemButtonType::Maximize, globalPos);
break; break;
case HTCLOSE: case HTCLOSE:
hoverButton(SystemButtonType::Close); hoverButton(SystemButtonType::Close, globalPos);
break; break;
default: default:
releaseButtons(std::nullopt); resetButtons(std::nullopt, globalPos);
break; break;
} }
// If we haven't previously asked for mouse tracking, request mouse // If we haven't previously asked for mouse tracking, request mouse
@ -291,7 +291,8 @@ Q_GLOBAL_STATIC(Win32Helper, g_win32Helper)
case WM_NCMOUSELEAVE: case WM_NCMOUSELEAVE:
case WM_MOUSELEAVE: { case WM_MOUSELEAVE: {
// When the mouse leaves the drag rect, make sure to dismiss any hover. // When the mouse leaves the drag rect, make sure to dismiss any hover.
releaseButtons(std::nullopt); const DWORD pos = GetMessagePos();
resetButtons(std::nullopt, {GET_X_LPARAM(pos), GET_Y_LPARAM(pos)});
const QMutexLocker locker(&g_win32Helper()->mutex); const QMutexLocker locker(&g_win32Helper()->mutex);
g_win32Helper()->data[parentWindowId].trackingMouse = false; g_win32Helper()->data[parentWindowId].trackingMouse = false;
} break; } break;
@ -307,6 +308,7 @@ Q_GLOBAL_STATIC(Win32Helper, g_win32Helper)
// If it's not in a caption button, then just forward the message along // If it's not in a caption button, then just forward the message along
// to the root HWND. Make sure to do this for the HTTOP, which is the // to the root HWND. Make sure to do this for the HTTOP, which is the
// top resize border. // top resize border.
const QPoint globalPos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
switch (wParam) { switch (wParam) {
case HTTOP: case HTTOP:
case HTCAPTION: case HTCAPTION:
@ -315,19 +317,19 @@ Q_GLOBAL_STATIC(Win32Helper, g_win32Helper)
// The buttons won't work as you'd expect; we need to handle those // The buttons won't work as you'd expect; we need to handle those
// ourselves. // ourselves.
case HTSYSMENU: case HTSYSMENU:
pressButton(SystemButtonType::WindowIcon); pressButton(SystemButtonType::WindowIcon, globalPos);
break; break;
case HTHELP: case HTHELP:
pressButton(SystemButtonType::Help); pressButton(SystemButtonType::Help, globalPos);
break; break;
case HTREDUCE: case HTREDUCE:
pressButton(SystemButtonType::Minimize); pressButton(SystemButtonType::Minimize, globalPos);
break; break;
case HTZOOM: case HTZOOM:
pressButton(SystemButtonType::Maximize); pressButton(SystemButtonType::Maximize, globalPos);
break; break;
case HTCLOSE: case HTCLOSE:
pressButton(SystemButtonType::Close); pressButton(SystemButtonType::Close, globalPos);
break; break;
default: default:
break; break;
@ -340,6 +342,7 @@ Q_GLOBAL_STATIC(Win32Helper, g_win32Helper)
// //
// If it's not in a caption button, then just forward the message along // If it's not in a caption button, then just forward the message along
// to the root HWND. // to the root HWND.
const QPoint globalPos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
switch (wParam) { switch (wParam) {
case HTTOP: case HTTOP:
case HTCAPTION: case HTCAPTION:
@ -347,19 +350,19 @@ Q_GLOBAL_STATIC(Win32Helper, g_win32Helper)
return SendMessageW(parentWindowHandle, uMsg, wParam, lParam); return SendMessageW(parentWindowHandle, uMsg, wParam, lParam);
// The buttons won't work as you'd expect; we need to handle those ourselves. // The buttons won't work as you'd expect; we need to handle those ourselves.
case HTSYSMENU: case HTSYSMENU:
clickButton(SystemButtonType::WindowIcon); releaseButton(SystemButtonType::WindowIcon, globalPos);
break; break;
case HTHELP: case HTHELP:
clickButton(SystemButtonType::Help); releaseButton(SystemButtonType::Help, globalPos);
break; break;
case HTREDUCE: case HTREDUCE:
clickButton(SystemButtonType::Minimize); releaseButton(SystemButtonType::Minimize, globalPos);
break; break;
case HTZOOM: case HTZOOM:
clickButton(SystemButtonType::Maximize); releaseButton(SystemButtonType::Maximize, globalPos);
break; break;
case HTCLOSE: case HTCLOSE:
clickButton(SystemButtonType::Close); releaseButton(SystemButtonType::Close, globalPos);
break; break;
default: default:
break; break;

View File

@ -127,7 +127,7 @@ static Q_LOGGING_CATEGORY(lcCoreGlobal, "wangwenx190.framelesshelper.core.global
using namespace Global; using namespace Global;
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
static_assert(std::size(WindowsVersions) == (static_cast<int>(WindowsVersion::Latest) + 1)); static_assert(std::size(WindowsVersions) == (static_cast<quint8>(WindowsVersion::Latest) + 1));
#endif #endif
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX

View File

@ -265,12 +265,12 @@ bool Utils::isThemeChangeEvent(const QEvent * const event)
QColor Utils::calculateSystemButtonBackgroundColor(const SystemButtonType button, const ButtonState state) QColor Utils::calculateSystemButtonBackgroundColor(const SystemButtonType button, const ButtonState state)
{ {
if (state == ButtonState::Unspecified) { if (state == ButtonState::MouseLeaved) {
return kDefaultTransparentColor; return kDefaultTransparentColor;
} }
const bool isClose = (button == SystemButtonType::Close); const bool isClose = (button == SystemButtonType::Close);
const bool isTitleColor = isTitleBarColorized(); const bool isTitleColor = isTitleBarColorized();
const bool isHovered = (state == ButtonState::Hovered); const bool isPressed = (state == ButtonState::MousePressed);
const auto result = [isClose, isTitleColor]() -> QColor { const auto result = [isClose, isTitleColor]() -> QColor {
if (isClose) { if (isClose) {
return kDefaultSystemCloseButtonBackgroundColor; return kDefaultSystemCloseButtonBackgroundColor;
@ -289,12 +289,12 @@ QColor Utils::calculateSystemButtonBackgroundColor(const SystemButtonType button
return kDefaultSystemButtonBackgroundColor; return kDefaultSystemButtonBackgroundColor;
}(); }();
if (isClose) { if (isClose) {
return (isHovered ? result.lighter(110) : result.lighter(140)); return (isPressed ? result.lighter(140) : result.lighter(110));
} }
if (!isTitleColor) { if (!isTitleColor) {
return (isHovered ? result.lighter(110) : result); return (isPressed ? result : result.lighter(110));
} }
return (isHovered ? result.lighter(150) : result.lighter(120)); return (isPressed ? result.lighter(120) : result.lighter(150));
} }
bool Utils::shouldAppsUseDarkMode() bool Utils::shouldAppsUseDarkMode()

View File

@ -37,6 +37,9 @@
#include <QtCore/qtimer.h> #include <QtCore/qtimer.h>
#include <QtCore/qeventloop.h> #include <QtCore/qeventloop.h>
#include <QtCore/qloggingcategory.h> #include <QtCore/qloggingcategory.h>
#include <QtCore/qcoreevent.h>
#include <QtCore/qcoreapplication.h>
#include <QtGui/qevent.h>
#ifndef FRAMELESSHELPER_QUICK_NO_PRIVATE #ifndef FRAMELESSHELPER_QUICK_NO_PRIVATE
# if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) # if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
# include <QtGui/qpa/qplatformwindow.h> // For QWINDOWSIZE_MAX # include <QtGui/qpa/qplatformwindow.h> // For QWINDOWSIZE_MAX
@ -44,8 +47,7 @@
# include <QtGui/private/qwindow_p.h> // For QWINDOWSIZE_MAX # include <QtGui/private/qwindow_p.h> // For QWINDOWSIZE_MAX
# endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) # endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
# include <QtQuick/private/qquickitem_p.h> # include <QtQuick/private/qquickitem_p.h>
# include <QtQuickTemplates2/private/qquickabstractbutton_p.h> # include <QtQuickTemplates2/private/qquickcontrol_p.h>
# include <QtQuickTemplates2/private/qquickabstractbutton_p_p.h>
#endif // FRAMELESSHELPER_QUICK_NO_PRIVATE #endif // FRAMELESSHELPER_QUICK_NO_PRIVATE
#ifndef QWINDOWSIZE_MAX #ifndef QWINDOWSIZE_MAX
@ -213,9 +215,9 @@ void FramelessQuickHelperPrivate::attach()
}; };
params.isInsideTitleBarDraggableArea = [this](const QPoint &pos) -> bool { return isInTitleBarDraggableArea(pos); }; params.isInsideTitleBarDraggableArea = [this](const QPoint &pos) -> bool { return isInTitleBarDraggableArea(pos); };
params.getWindowDevicePixelRatio = [window]() -> qreal { return window->effectiveDevicePixelRatio(); }; params.getWindowDevicePixelRatio = [window]() -> qreal { return window->effectiveDevicePixelRatio(); };
params.setSystemButtonState = [this](const SystemButtonType button, const ButtonState state) -> void { params.setSystemButtonState = [this](const SystemButtonType button, const ButtonState state, const QPoint &globalPos) -> void {
setSystemButtonState(FRAMELESSHELPER_ENUM_CORE_TO_QUICK(SystemButtonType, button), setSystemButtonState(FRAMELESSHELPER_ENUM_CORE_TO_QUICK(SystemButtonType, button),
FRAMELESSHELPER_ENUM_CORE_TO_QUICK(ButtonState, state)); FRAMELESSHELPER_ENUM_CORE_TO_QUICK(ButtonState, state), globalPos);
}; };
params.shouldIgnoreMouseEvents = [this](const QPoint &pos) -> bool { return shouldIgnoreMouseEvents(pos); }; params.shouldIgnoreMouseEvents = [this](const QPoint &pos) -> bool { return shouldIgnoreMouseEvents(pos); };
params.showSystemMenu = [this](const QPoint &pos) -> void { showSystemMenu(pos); }; params.showSystemMenu = [this](const QPoint &pos) -> void { showSystemMenu(pos); };
@ -297,6 +299,9 @@ void FramelessQuickHelperPrivate::setSystemButton(QQuickItem *item, const QuickG
data->closeButton = item; data->closeButton = item;
break; break;
} }
if (const auto control = qobject_cast<QQuickControl *>(item)) {
control->setHoverEnabled(true);
}
} }
void FramelessQuickHelperPrivate::setHitTestVisible(QQuickItem *item, const bool visible) void FramelessQuickHelperPrivate::setHitTestVisible(QQuickItem *item, const bool visible)
@ -814,7 +819,8 @@ bool FramelessQuickHelperPrivate::shouldIgnoreMouseEvents(const QPoint &pos) con
} }
void FramelessQuickHelperPrivate::setSystemButtonState(const QuickGlobal::SystemButtonType button, void FramelessQuickHelperPrivate::setSystemButtonState(const QuickGlobal::SystemButtonType button,
const QuickGlobal::ButtonState state) const QuickGlobal::ButtonState state,
const QPoint &nativeGlobalPos)
{ {
#ifdef FRAMELESSHELPER_QUICK_NO_PRIVATE #ifdef FRAMELESSHELPER_QUICK_NO_PRIVATE
Q_UNUSED(button); Q_UNUSED(button);
@ -825,76 +831,102 @@ void FramelessQuickHelperPrivate::setSystemButtonState(const QuickGlobal::System
return; return;
} }
const QuickHelperData data = getWindowData(); const QuickHelperData data = getWindowData();
QQuickAbstractButton *quickButton = nullptr; QQuickItem *quickButton = nullptr;
switch (button) { switch (button) {
case QuickGlobal::SystemButtonType::Unknown: case QuickGlobal::SystemButtonType::Unknown:
Q_UNREACHABLE_RETURN(void(0)); Q_UNREACHABLE_RETURN(void(0));
case QuickGlobal::SystemButtonType::WindowIcon: case QuickGlobal::SystemButtonType::WindowIcon:
if (data.windowIconButton) { if (data.windowIconButton) {
if (const auto btn = qobject_cast<QQuickAbstractButton *>(data.windowIconButton)) { quickButton = data.windowIconButton;
quickButton = btn;
}
} }
break; break;
case QuickGlobal::SystemButtonType::Help: case QuickGlobal::SystemButtonType::Help:
if (data.contextHelpButton) { if (data.contextHelpButton) {
if (const auto btn = qobject_cast<QQuickAbstractButton *>(data.contextHelpButton)) { quickButton = data.contextHelpButton;
quickButton = btn;
}
} }
break; break;
case QuickGlobal::SystemButtonType::Minimize: case QuickGlobal::SystemButtonType::Minimize:
if (data.minimizeButton) { if (data.minimizeButton) {
if (const auto btn = qobject_cast<QQuickAbstractButton *>(data.minimizeButton)) { quickButton = data.minimizeButton;
quickButton = btn;
}
} }
break; break;
case QuickGlobal::SystemButtonType::Maximize: case QuickGlobal::SystemButtonType::Maximize:
case QuickGlobal::SystemButtonType::Restore: case QuickGlobal::SystemButtonType::Restore:
if (data.maximizeButton) { if (data.maximizeButton) {
if (const auto btn = qobject_cast<QQuickAbstractButton *>(data.maximizeButton)) { quickButton = data.maximizeButton;
quickButton = btn;
}
} }
break; break;
case QuickGlobal::SystemButtonType::Close: case QuickGlobal::SystemButtonType::Close:
if (data.closeButton) { if (data.closeButton) {
if (const auto btn = qobject_cast<QQuickAbstractButton *>(data.closeButton)) { quickButton = data.closeButton;
quickButton = btn;
}
} }
break; break;
} }
if (quickButton) { if (!quickButton) {
const auto updateButtonState = [state](QQuickAbstractButton *btn) -> void { return;
Q_ASSERT(btn);
if (!btn) {
return;
}
switch (state) {
case QuickGlobal::ButtonState::Unspecified: {
btn->setPressed(false);
btn->setHovered(false);
} break;
case QuickGlobal::ButtonState::Hovered: {
btn->setPressed(false);
btn->setHovered(true);
} break;
case QuickGlobal::ButtonState::Pressed: {
btn->setHovered(true);
btn->setPressed(true);
} break;
case QuickGlobal::ButtonState::Clicked: {
// Clicked: pressed --> released, so behave like hovered.
btn->setPressed(false);
btn->setHovered(true);
QQuickAbstractButtonPrivate::get(btn)->click();
} break;
}
};
updateButtonState(quickButton);
} }
const auto updateButtonState = [&nativeGlobalPos, state](QQuickItem *btn) -> void {
Q_ASSERT(btn);
if (!btn) {
return;
}
const QQuickWindow * const btnWin = btn->window();
if (!btnWin) {
return;
}
const QPoint qtGlobalPos = Utils::fromNativeGlobalPosition(btnWin, nativeGlobalPos);
const QPoint scenePos = btnWin->mapFromGlobal(qtGlobalPos);
const QPoint localPos = btn->mapFromGlobal(qtGlobalPos).toPoint();
const auto sendEnterEvent = [&qtGlobalPos, &scenePos, btn]() -> void {
// QEvent::Enter event is for QWidget only and will be ignored by QQuickItem,
// but after some experiments, I found QEvent::HoverEnter event can achieve similar effect.
QHoverEvent event(QEvent::HoverEnter, scenePos, qtGlobalPos, {});
QCoreApplication::sendEvent(btn, &event);
};
const auto sendLeaveEvent = [&qtGlobalPos, &scenePos, btn]() -> void {
// QEvent::Leave event is for QWidget only and will be ignored by QQuickItem,
// but after some experiments, I found QEvent::HoverLeave event can achieve similar effect.
QHoverEvent event(QEvent::HoverLeave, scenePos, qtGlobalPos, {});
QCoreApplication::sendEvent(btn, &event);
};
const auto sendPressEvent = [&qtGlobalPos, &scenePos, &localPos, btn]() -> void {
QMouseEvent event(QEvent::MouseButtonPress, localPos, scenePos, qtGlobalPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QCoreApplication::sendEvent(btn, &event);
};
const auto sendReleaseEvent = [&qtGlobalPos, &scenePos, &localPos, btn]() -> void {
QMouseEvent event(QEvent::MouseButtonRelease, localPos, scenePos, qtGlobalPos, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
QCoreApplication::sendEvent(btn, &event);
};
const auto sendMoveEvent = [&qtGlobalPos, &scenePos, &localPos, btn]() -> void {
// According to Qt docs, we should send both MouseMove event and HoverMove event.
QMouseEvent mouseEvent(QEvent::MouseMove, localPos, scenePos, qtGlobalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier);
QCoreApplication::sendEvent(btn, &mouseEvent);
QHoverEvent hoverEvent(QEvent::HoverMove, scenePos, qtGlobalPos, {});
QCoreApplication::sendEvent(btn, &hoverEvent);
};
switch (state) {
case QuickGlobal::ButtonState::MouseEntered: {
sendEnterEvent();
// The visual state won't change without a mouse move event.
sendMoveEvent();
} break;
case QuickGlobal::ButtonState::MouseLeaved: {
// The visual state won't change without a mouse move event.
sendMoveEvent();
sendLeaveEvent();
} break;
case QuickGlobal::ButtonState::MouseMoving:
sendMoveEvent();
break;
case QuickGlobal::ButtonState::MousePressed:
sendPressEvent();
break;
case QuickGlobal::ButtonState::MouseReleased:
sendReleaseEvent();
break;
}
};
updateButtonState(quickButton);
#endif // FRAMELESSHELPER_QUICK_NO_PRIVATE #endif // FRAMELESSHELPER_QUICK_NO_PRIVATE
} }

View File

@ -268,6 +268,7 @@ void QuickStandardSystemButton::initialize()
setAntialiasing(true); setAntialiasing(true);
setSmooth(true); setSmooth(true);
setClip(true); setClip(true);
setHoverEnabled(true);
setImplicitWidth(kDefaultSystemButtonSize.width()); setImplicitWidth(kDefaultSystemButtonSize.width());
setImplicitHeight(kDefaultSystemButtonSize.height()); setImplicitHeight(kDefaultSystemButtonSize.height());

View File

@ -40,6 +40,9 @@
#include <QtCore/qtimer.h> #include <QtCore/qtimer.h>
#include <QtCore/qeventloop.h> #include <QtCore/qeventloop.h>
#include <QtCore/qloggingcategory.h> #include <QtCore/qloggingcategory.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qcoreevent.h>
#include <QtGui/qevent.h>
#include <QtGui/qwindow.h> #include <QtGui/qwindow.h>
#include <QtGui/qpalette.h> #include <QtGui/qpalette.h>
#include <QtWidgets/qwidget.h> #include <QtWidgets/qwidget.h>
@ -479,7 +482,7 @@ void FramelessWidgetsHelperPrivate::attach()
params.isInsideSystemButtons = [this](const QPoint &pos, SystemButtonType *button) -> bool { return isInSystemButtons(pos, button); }; params.isInsideSystemButtons = [this](const QPoint &pos, SystemButtonType *button) -> bool { return isInSystemButtons(pos, button); };
params.isInsideTitleBarDraggableArea = [this](const QPoint &pos) -> bool { return isInTitleBarDraggableArea(pos); }; params.isInsideTitleBarDraggableArea = [this](const QPoint &pos) -> bool { return isInTitleBarDraggableArea(pos); };
params.getWindowDevicePixelRatio = [window]() -> qreal { return window->devicePixelRatioF(); }; params.getWindowDevicePixelRatio = [window]() -> qreal { return window->devicePixelRatioF(); };
params.setSystemButtonState = [this](const SystemButtonType button, const ButtonState state) -> void { setSystemButtonState(button, state); }; params.setSystemButtonState = [this](const SystemButtonType button, const ButtonState state, const QPoint &globalPos) -> void { setSystemButtonState(button, state, globalPos); };
params.shouldIgnoreMouseEvents = [this](const QPoint &pos) -> bool { return shouldIgnoreMouseEvents(pos); }; params.shouldIgnoreMouseEvents = [this](const QPoint &pos) -> bool { return shouldIgnoreMouseEvents(pos); };
params.showSystemMenu = [this](const QPoint &pos) -> void { showSystemMenu(pos); }; params.showSystemMenu = [this](const QPoint &pos) -> void { showSystemMenu(pos); };
params.setProperty = [this](const QByteArray &name, const QVariant &value) -> void { setProperty(name, value); }; params.setProperty = [this](const QByteArray &name, const QVariant &value) -> void { setProperty(name, value); };
@ -707,7 +710,7 @@ bool FramelessWidgetsHelperPrivate::shouldIgnoreMouseEvents(const QPoint &pos) c
return ((Utils::windowStatesToWindowState(m_window->windowState()) == Qt::WindowNoState) && withinFrameBorder); return ((Utils::windowStatesToWindowState(m_window->windowState()) == Qt::WindowNoState) && withinFrameBorder);
} }
void FramelessWidgetsHelperPrivate::setSystemButtonState(const SystemButtonType button, const ButtonState state) void FramelessWidgetsHelperPrivate::setSystemButtonState(const SystemButtonType button, const ButtonState state, const QPoint &nativeGlobalPos)
{ {
Q_ASSERT(button != SystemButtonType::Unknown); Q_ASSERT(button != SystemButtonType::Unknown);
if (button == SystemButtonType::Unknown) { if (button == SystemButtonType::Unknown) {
@ -745,43 +748,79 @@ void FramelessWidgetsHelperPrivate::setSystemButtonState(const SystemButtonType
} }
break; break;
} }
if (widgetButton) { if (!widgetButton) {
const auto updateButtonState = [state](QWidget *btn) -> void { return;
Q_ASSERT(btn);
if (!btn) {
return;
}
switch (state) {
case ButtonState::Unspecified: {
QMetaObject::invokeMethod(btn, "setPressed", Q_ARG(bool, false));
QMetaObject::invokeMethod(btn, "setHovered", Q_ARG(bool, false));
} break;
case ButtonState::Hovered: {
QMetaObject::invokeMethod(btn, "setPressed", Q_ARG(bool, false));
QMetaObject::invokeMethod(btn, "setHovered", Q_ARG(bool, true));
} break;
case ButtonState::Pressed: {
QMetaObject::invokeMethod(btn, "setHovered", Q_ARG(bool, true));
QMetaObject::invokeMethod(btn, "setPressed", Q_ARG(bool, true));
} break;
case ButtonState::Clicked: {
// Clicked: pressed --> released, so behave like hovered.
QMetaObject::invokeMethod(btn, "setPressed", Q_ARG(bool, false));
QMetaObject::invokeMethod(btn, "setHovered", Q_ARG(bool, true));
// Trigger the clicked signal.
QMetaObject::invokeMethod(btn, "clicked");
} break;
}
};
if (const auto mo = widgetButton->metaObject()) {
const int pressedIndex = mo->indexOfSlot(QMetaObject::normalizedSignature("setPressed(bool)").constData());
const int hoveredIndex = mo->indexOfSlot(QMetaObject::normalizedSignature("setHovered(bool)").constData());
const int clickedIndex = mo->indexOfSignal(QMetaObject::normalizedSignature("clicked()").constData());
if ((pressedIndex >= 0) && (hoveredIndex >= 0) && (clickedIndex >= 0)) {
updateButtonState(widgetButton);
}
}
} }
const auto updateButtonState = [&nativeGlobalPos, state](QWidget *btn) -> void {
Q_ASSERT(btn);
if (!btn) {
return;
}
const QWidget * const btnWin = btn->window();
if (!btnWin) {
return;
}
const QWindow * const btnRawWin = btnWin->windowHandle();
if (!btnRawWin) {
return;
}
const QPoint qtGlobalPos = Utils::fromNativeGlobalPosition(btnRawWin, nativeGlobalPos);
const QPoint scenePos = btnWin->mapFromGlobal(qtGlobalPos);
const QPoint localPos = btn->mapFromGlobal(qtGlobalPos);
const auto sendEnterEvent = [&qtGlobalPos, &scenePos, &localPos, btn]() -> void {
QEnterEvent mouseEvent(localPos, scenePos, qtGlobalPos);
QCoreApplication::sendEvent(btn, &mouseEvent);
// We'd better send the mouse hover event at the same time, in case some widgets need it.
QHoverEvent hoverEvent(QEvent::HoverEnter, scenePos, qtGlobalPos, {});
QCoreApplication::sendEvent(btn, &hoverEvent);
};
const auto sendLeaveEvent = [&qtGlobalPos, &scenePos, btn]() -> void {
QEvent mouseEvent(QEvent::Leave);
QCoreApplication::sendEvent(btn, &mouseEvent);
// We'd better send the mouse hover event at the same time, in case some widgets need it.
QHoverEvent hoverEvent(QEvent::HoverLeave, scenePos, qtGlobalPos, {});
QCoreApplication::sendEvent(btn, &hoverEvent);
};
const auto sendPressEvent = [&qtGlobalPos, &scenePos, &localPos, btn]() -> void {
QMouseEvent event(QEvent::MouseButtonPress, localPos, scenePos, qtGlobalPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QCoreApplication::sendEvent(btn, &event);
};
const auto sendReleaseEvent = [&qtGlobalPos, &scenePos, &localPos, btn]() -> void {
QMouseEvent event(QEvent::MouseButtonRelease, localPos, scenePos, qtGlobalPos, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
QCoreApplication::sendEvent(btn, &event);
QMetaObject::invokeMethod(btn, "clicked"); // Why do we need this ???
};
const auto sendMoveEvent = [&qtGlobalPos, &scenePos, &localPos, btn]() -> void {
// According to Qt docs, we should send both MouseMove event and HoverMove event.
QMouseEvent mouseEvent(QEvent::MouseMove, localPos, scenePos, qtGlobalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier);
QCoreApplication::sendEvent(btn, &mouseEvent);
QHoverEvent hoverEvent(QEvent::HoverMove, scenePos, qtGlobalPos, {});
QCoreApplication::sendEvent(btn, &hoverEvent);
};
switch (state) {
case ButtonState::MouseEntered: {
sendEnterEvent();
// The visual state won't change without a mouse move event.
sendMoveEvent();
} break;
case ButtonState::MouseLeaved: {
// The visual state won't change without a mouse move event.
sendMoveEvent();
sendLeaveEvent();
} break;
case ButtonState::MouseMoving:
//sendMoveEvent();
sendEnterEvent();
break;
case ButtonState::MousePressed:
sendPressEvent();
break;
case ButtonState::MouseReleased:
sendReleaseEvent();
break;
}
};
updateButtonState(widgetButton);
} }
void FramelessWidgetsHelperPrivate::moveWindowToDesktopCenter() void FramelessWidgetsHelperPrivate::moveWindowToDesktopCenter()
@ -881,6 +920,8 @@ void FramelessWidgetsHelperPrivate::setSystemButton(QWidget *widget, const Syste
data->closeButton = widget; data->closeButton = widget;
break; break;
} }
widget->setMouseTracking(true);
widget->setAttribute(Qt::WA_Hover);
} }
FramelessWidgetsHelper::FramelessWidgetsHelper(QObject *parent) FramelessWidgetsHelper::FramelessWidgetsHelper(QObject *parent)

View File

@ -333,6 +333,7 @@ void StandardSystemButtonPrivate::leaveEventHandler(QEvent *event)
if (!event) { if (!event) {
return; return;
} }
setPressed(false);
setHovered(false); setHovered(false);
event->accept(); event->accept();
} }
@ -395,6 +396,8 @@ void StandardSystemButtonPrivate::initialize()
q->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); q->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
q->setFixedSize(kDefaultSystemButtonSize); q->setFixedSize(kDefaultSystemButtonSize);
q->setIconSize(kDefaultSystemButtonIconSize); q->setIconSize(kDefaultSystemButtonIconSize);
q->setMouseTracking(true);
q->setAttribute(Qt::WA_Hover);
connect(q, &StandardSystemButton::pressed, this, [this](){ setPressed(true); }); connect(q, &StandardSystemButton::pressed, this, [this](){ setPressed(true); });
connect(q, &StandardSystemButton::released, this, [this](){ setPressed(false); }); connect(q, &StandardSystemButton::released, this, [this](){ setPressed(false); });
} }