From 514585335150697d2720e59f715a22a54b16fcb3 Mon Sep 17 00:00:00 2001 From: sowens99 Date: Thu, 23 Sep 2021 21:13:28 -0400 Subject: [PATCH 1/2] Bug: fix unhide on mouse movement only responding to clicks Previously the unhide of movement mouse_timer reset occurred within case MouseButtonPress. Additionally, there was a redundant expression in the if statement for cursor locking. --- Source/Core/DolphinQt/RenderWidget.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Source/Core/DolphinQt/RenderWidget.cpp b/Source/Core/DolphinQt/RenderWidget.cpp index b3977b507b..f8dfb4ddd8 100644 --- a/Source/Core/DolphinQt/RenderWidget.cpp +++ b/Source/Core/DolphinQt/RenderWidget.cpp @@ -362,16 +362,18 @@ bool RenderWidget::event(QEvent* event) { // Lock the cursor with any mouse button click (behave the same as window focus change). // This event is occasionally missed because isActiveWindow is laggy - if (Settings::Instance().GetLockCursor() && event->type() == QEvent::MouseButtonPress) + if (Settings::Instance().GetLockCursor()) { SetCursorLocked(true); } - // Unhide on movement - if (!Settings::Instance().GetHideCursor()) - { - setCursor(Qt::ArrowCursor); - m_mouse_timer->start(MOUSE_HIDE_DELAY); - } + } + break; + case QEvent::MouseMove: + // Unhide on movement + if (!Settings::Instance().GetHideCursor()) + { + setCursor(Qt::ArrowCursor); + m_mouse_timer->start(MOUSE_HIDE_DELAY); } break; case QEvent::WinIdChange: From 2aa400e72f401ac42de2545a72210d49be2d56af Mon Sep 17 00:00:00 2001 From: sowens99 Date: Wed, 22 Sep 2021 23:57:52 -0400 Subject: [PATCH 2/2] Add option for Never Hide Mouse Cursor Instead of having a single GUI checkbox for "Always Hide Mouse Cursor", I have instead opted to use radio buttons so the user can swap between different states of mouse visibility. "Movement" is the default behavior, "Never" will hide the mouse cursor the entire time the game is running, and "Always" will keep the mouse cursor always visible. --- Source/Core/Core/ConfigManager.cpp | 4 +- Source/Core/Core/ConfigManager.h | 9 ++- Source/Core/DolphinNoGUI/PlatformX11.cpp | 13 ++-- Source/Core/DolphinQt/RenderWidget.cpp | 21 ++++--- Source/Core/DolphinQt/RenderWidget.h | 1 + Source/Core/DolphinQt/Settings.cpp | 12 ++-- Source/Core/DolphinQt/Settings.h | 8 ++- .../Core/DolphinQt/Settings/InterfacePane.cpp | 59 +++++++++++++++---- .../Core/DolphinQt/Settings/InterfacePane.h | 8 ++- 9 files changed, 99 insertions(+), 36 deletions(-) diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index 9eeba47706..56985b1c3d 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -145,7 +145,7 @@ void SConfig::SaveInterfaceSettings(IniFile& ini) IniFile::Section* interface = ini.GetOrCreateSection("Interface"); interface->Set("ConfirmStop", bConfirmStop); - interface->Set("HideCursor", bHideCursor); + interface->Set("CursorVisibility", m_show_cursor); interface->Set("LockCursor", bLockCursor); interface->Set("LanguageCode", m_InterfaceLanguage); interface->Set("ExtendedFPSInfo", m_InterfaceExtendedFPSInfo); @@ -399,7 +399,7 @@ void SConfig::LoadInterfaceSettings(IniFile& ini) IniFile::Section* interface = ini.GetOrCreateSection("Interface"); interface->Get("ConfirmStop", &bConfirmStop, true); - interface->Get("HideCursor", &bHideCursor, false); + interface->Get("CursorVisibility", &m_show_cursor, ShowCursor::OnMovement); interface->Get("LockCursor", &bLockCursor, false); interface->Get("LanguageCode", &m_InterfaceLanguage, ""); interface->Get("ExtendedFPSInfo", &m_InterfaceExtendedFPSInfo, false); diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h index c6d954a398..50d8ebd3fe 100644 --- a/Source/Core/Core/ConfigManager.h +++ b/Source/Core/Core/ConfigManager.h @@ -146,7 +146,14 @@ struct SConfig // Interface settings bool bConfirmStop = false; - bool bHideCursor = false; + + enum class ShowCursor + { + Never, + Constantly, + OnMovement, + } m_show_cursor; + bool bLockCursor = false; std::string theme_name; diff --git a/Source/Core/DolphinNoGUI/PlatformX11.cpp b/Source/Core/DolphinNoGUI/PlatformX11.cpp index a47d1f0d84..4ba2c1b3b9 100644 --- a/Source/Core/DolphinNoGUI/PlatformX11.cpp +++ b/Source/Core/DolphinNoGUI/PlatformX11.cpp @@ -70,7 +70,7 @@ PlatformX11::~PlatformX11() if (m_display) { - if (SConfig::GetInstance().bHideCursor) + if (SConfig::GetInstance().m_show_cursor == SConfig::ShowCursor::Never) XFreeCursor(m_display, m_blank_cursor); XCloseDisplay(m_display); @@ -115,7 +115,7 @@ bool PlatformX11::Init() m_xrr_config = new X11Utils::XRRConfiguration(m_display, m_window); #endif - if (SConfig::GetInstance().bHideCursor) + if (SConfig::GetInstance().m_show_cursor == SConfig::ShowCursor::Never) { // make a blank cursor Pixmap Blank; @@ -200,13 +200,13 @@ void PlatformX11::ProcessEvents() { if (Core::GetState() == Core::State::Running) { - if (SConfig::GetInstance().bHideCursor) + if (SConfig::GetInstance().m_show_cursor == SConfig::ShowCursor::Never) XUndefineCursor(m_display, m_window); Core::SetState(Core::State::Paused); } else { - if (SConfig::GetInstance().bHideCursor) + if (SConfig::GetInstance().m_show_cursor == SConfig::ShowCursor::Never) XDefineCursor(m_display, m_window, m_blank_cursor); Core::SetState(Core::State::Running); } @@ -243,14 +243,15 @@ void PlatformX11::ProcessEvents() case FocusIn: { m_window_focus = true; - if (SConfig::GetInstance().bHideCursor && Core::GetState() != Core::State::Paused) + if (SConfig::GetInstance().m_show_cursor == SConfig::ShowCursor::Never && + Core::GetState() != Core::State::Paused) XDefineCursor(m_display, m_window, m_blank_cursor); } break; case FocusOut: { m_window_focus = false; - if (SConfig::GetInstance().bHideCursor) + if (SConfig::GetInstance().m_show_cursor == SConfig::ShowCursor::Never) XUndefineCursor(m_display, m_window); } break; diff --git a/Source/Core/DolphinQt/RenderWidget.cpp b/Source/Core/DolphinQt/RenderWidget.cpp index f8dfb4ddd8..0343eda8fa 100644 --- a/Source/Core/DolphinQt/RenderWidget.cpp +++ b/Source/Core/DolphinQt/RenderWidget.cpp @@ -83,7 +83,7 @@ RenderWidget::RenderWidget(QWidget* parent) : QWidget(parent) m_mouse_timer->setSingleShot(true); setMouseTracking(true); - connect(&Settings::Instance(), &Settings::HideCursorChanged, this, + connect(&Settings::Instance(), &Settings::CursorVisibilityChanged, this, &RenderWidget::OnHideCursorChanged); connect(&Settings::Instance(), &Settings::LockCursorChanged, this, &RenderWidget::OnLockCursorChanged); @@ -139,6 +139,7 @@ void RenderWidget::OnHideCursorChanged() { UpdateCursor(); } + void RenderWidget::OnLockCursorChanged() { SetCursorLocked(false); @@ -155,14 +156,16 @@ void RenderWidget::UpdateCursor() // on top of the game window in the background const bool keep_on_top = (windowFlags() & Qt::WindowStaysOnTopHint) != 0; const bool should_hide = - Settings::Instance().GetHideCursor() && + (Settings::Instance().GetCursorVisibility() == SConfig::ShowCursor::Never) && (keep_on_top || SConfig::GetInstance().m_BackgroundInput || isActiveWindow()); setCursor(should_hide ? Qt::BlankCursor : Qt::ArrowCursor); } else { - setCursor((m_cursor_locked && Settings::Instance().GetHideCursor()) ? Qt::BlankCursor : - Qt::ArrowCursor); + setCursor((m_cursor_locked && + Settings::Instance().GetCursorVisibility() == SConfig::ShowCursor::Never) ? + Qt::BlankCursor : + Qt::ArrowCursor); } } @@ -185,7 +188,8 @@ void RenderWidget::HandleCursorTimer() { if (!isActiveWindow()) return; - if (!Settings::Instance().GetLockCursor() || m_cursor_locked) + if ((!Settings::Instance().GetLockCursor() || m_cursor_locked) && + Settings::Instance().GetCursorVisibility() == SConfig::ShowCursor::OnMovement) { setCursor(Qt::BlankCursor); } @@ -268,7 +272,7 @@ void RenderWidget::SetCursorLocked(bool locked, bool follow_aspect_ratio) { m_cursor_locked = true; - if (Settings::Instance().GetHideCursor()) + if (Settings::Instance().GetCursorVisibility() != SConfig::ShowCursor::Constantly) { setCursor(Qt::BlankCursor); } @@ -370,7 +374,7 @@ bool RenderWidget::event(QEvent* event) break; case QEvent::MouseMove: // Unhide on movement - if (!Settings::Instance().GetHideCursor()) + if (Settings::Instance().GetCursorVisibility() == SConfig::ShowCursor::OnMovement) { setCursor(Qt::ArrowCursor); m_mouse_timer->start(MOUSE_HIDE_DELAY); @@ -381,7 +385,8 @@ bool RenderWidget::event(QEvent* event) break; case QEvent::Show: // Don't do if "stay on top" changed (or was true) - if (Settings::Instance().GetLockCursor() && Settings::Instance().GetHideCursor() && + if (Settings::Instance().GetLockCursor() && + Settings::Instance().GetCursorVisibility() != SConfig::ShowCursor::Constantly && !m_dont_lock_cursor_on_show) { // Auto lock when this window is shown (it was hidden) diff --git a/Source/Core/DolphinQt/RenderWidget.h b/Source/Core/DolphinQt/RenderWidget.h index 051ca5de87..c44f673cb2 100644 --- a/Source/Core/DolphinQt/RenderWidget.h +++ b/Source/Core/DolphinQt/RenderWidget.h @@ -35,6 +35,7 @@ signals: private: void HandleCursorTimer(); void OnHideCursorChanged(); + void OnNeverHideCursorChanged(); void OnLockCursorChanged(); void OnKeepOnTopChanged(bool top); void UpdateCursor(); diff --git a/Source/Core/DolphinQt/Settings.cpp b/Source/Core/DolphinQt/Settings.cpp index 964b881bf4..3958a9a255 100644 --- a/Source/Core/DolphinQt/Settings.cpp +++ b/Source/Core/DolphinQt/Settings.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -325,15 +326,16 @@ void Settings::SetStateSlot(int slot) GetQSettings().setValue(QStringLiteral("Emulation/StateSlot"), slot); } -void Settings::SetHideCursor(bool hide_cursor) +void Settings::SetCursorVisibility(SConfig::ShowCursor hideCursor) { - SConfig::GetInstance().bHideCursor = hide_cursor; - emit HideCursorChanged(); + SConfig::GetInstance().m_show_cursor = hideCursor; + + emit CursorVisibilityChanged(); } -bool Settings::GetHideCursor() const +SConfig::ShowCursor Settings::GetCursorVisibility() const { - return SConfig::GetInstance().bHideCursor; + return SConfig::GetInstance().m_show_cursor; } void Settings::SetLockCursor(bool lock_cursor) diff --git a/Source/Core/DolphinQt/Settings.h b/Source/Core/DolphinQt/Settings.h index 241dacffc8..3ec5e04834 100644 --- a/Source/Core/DolphinQt/Settings.h +++ b/Source/Core/DolphinQt/Settings.h @@ -7,8 +7,10 @@ #include #include +#include #include +#include "Core/ConfigManager.h" #include "DiscIO/Enums.h" namespace Core @@ -97,8 +99,8 @@ public: void SetUSBKeyboardConnected(bool connected); // Graphics - void SetHideCursor(bool hide_cursor); - bool GetHideCursor() const; + void SetCursorVisibility(SConfig::ShowCursor hideCursor); + SConfig::ShowCursor GetCursorVisibility() const; void SetLockCursor(bool lock_cursor); bool GetLockCursor() const; void SetKeepWindowOnTop(bool top); @@ -168,7 +170,7 @@ signals: void MetadataRefreshRequested(); void MetadataRefreshCompleted(); void AutoRefreshToggled(bool enabled); - void HideCursorChanged(); + void CursorVisibilityChanged(); void LockCursorChanged(); void KeepWindowOnTopChanged(bool top); void VolumeChanged(int volume); diff --git a/Source/Core/DolphinQt/Settings/InterfacePane.cpp b/Source/Core/DolphinQt/Settings/InterfacePane.cpp index 05c643a5a4..a42dc90a52 100644 --- a/Source/Core/DolphinQt/Settings/InterfacePane.cpp +++ b/Source/Core/DolphinQt/Settings/InterfacePane.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -169,23 +170,36 @@ void InterfacePane::CreateInGame() m_checkbox_enable_osd = new QCheckBox(tr("Show On-Screen Display Messages")); m_checkbox_show_active_title = new QCheckBox(tr("Show Active Title in Window Title")); m_checkbox_pause_on_focus_lost = new QCheckBox(tr("Pause on Focus Loss")); - m_checkbox_hide_mouse = new QCheckBox(tr("Always Hide Mouse Cursor")); - m_checkbox_lock_mouse = new QCheckBox(tr("Lock Mouse Cursor")); - m_checkbox_hide_mouse->setToolTip( - tr("Will immediately hide the Mouse Cursor when it hovers on top of the Render Widget, " - "otherwise " - "there is a delay.\nIf \"Lock Mouse Cursor\" is enabled, it will hide on Mouse locked")); + auto* mouse_groupbox = new QGroupBox(tr("Mouse Cursor Visibility")); + auto* m_vboxlayout_hide_mouse = new QVBoxLayout; + mouse_groupbox->setLayout(m_vboxlayout_hide_mouse); + + m_radio_cursor_visible_movement = new QRadioButton(tr("On Movement")); + m_radio_cursor_visible_movement->setToolTip( + tr("Mouse Cursor hides after inactivity and returns upon Mouse Cursor movement.")); + m_radio_cursor_visible_never = new QRadioButton(tr("Never")); + m_radio_cursor_visible_never->setToolTip( + tr("Mouse Cursor will never be visible while a game is running.")); + m_radio_cursor_visible_always = new QRadioButton(tr("Always")); + m_radio_cursor_visible_always->setToolTip(tr("Mouse Cursor will always be visible.")); + + m_vboxlayout_hide_mouse->addWidget(m_radio_cursor_visible_movement); + m_vboxlayout_hide_mouse->addWidget(m_radio_cursor_visible_never); + m_vboxlayout_hide_mouse->addWidget(m_radio_cursor_visible_always); + + m_checkbox_lock_mouse = new QCheckBox(tr("Lock Mouse Cursor")); m_checkbox_lock_mouse->setToolTip(tr("Will lock the Mouse Cursor to the Render Widget as long as " "it has focus. You can set a hotkey to unlock it.")); + mouse_groupbox->setLayout(m_vboxlayout_hide_mouse); groupbox_layout->addWidget(m_checkbox_top_window); groupbox_layout->addWidget(m_checkbox_confirm_on_stop); groupbox_layout->addWidget(m_checkbox_use_panic_handlers); groupbox_layout->addWidget(m_checkbox_enable_osd); groupbox_layout->addWidget(m_checkbox_show_active_title); groupbox_layout->addWidget(m_checkbox_pause_on_focus_lost); - groupbox_layout->addWidget(m_checkbox_hide_mouse); + groupbox_layout->addWidget(mouse_groupbox); #ifdef _WIN32 groupbox_layout->addWidget(m_checkbox_lock_mouse); #endif @@ -211,8 +225,12 @@ void InterfacePane::ConnectLayout() connect(m_checkbox_show_active_title, &QCheckBox::toggled, this, &InterfacePane::OnSaveConfig); connect(m_checkbox_enable_osd, &QCheckBox::toggled, this, &InterfacePane::OnSaveConfig); connect(m_checkbox_pause_on_focus_lost, &QCheckBox::toggled, this, &InterfacePane::OnSaveConfig); - connect(m_checkbox_hide_mouse, &QCheckBox::toggled, &Settings::Instance(), - &Settings::SetHideCursor); + connect(m_radio_cursor_visible_movement, &QRadioButton::toggled, this, + &InterfacePane::OnCursorVisibleMovement); + connect(m_radio_cursor_visible_never, &QRadioButton::toggled, this, + &InterfacePane::OnCursorVisibleNever); + connect(m_radio_cursor_visible_always, &QRadioButton::toggled, this, + &InterfacePane::OnCursorVisibleAlways); connect(m_checkbox_lock_mouse, &QCheckBox::toggled, &Settings::Instance(), &Settings::SetLockCursor); connect(m_checkbox_use_userstyle, &QCheckBox::toggled, this, &InterfacePane::OnSaveConfig); @@ -250,7 +268,13 @@ void InterfacePane::LoadConfig() m_checkbox_pause_on_focus_lost->setChecked(startup_params.m_PauseOnFocusLost); m_checkbox_use_covers->setChecked(Config::Get(Config::MAIN_USE_GAME_COVERS)); m_checkbox_focused_hotkeys->setChecked(Config::Get(Config::MAIN_FOCUSED_HOTKEYS)); - m_checkbox_hide_mouse->setChecked(Settings::Instance().GetHideCursor()); + m_radio_cursor_visible_movement->setChecked(Settings::Instance().GetCursorVisibility() == + SConfig::ShowCursor::OnMovement); + m_radio_cursor_visible_always->setChecked(Settings::Instance().GetCursorVisibility() == + SConfig::ShowCursor::Constantly); + m_radio_cursor_visible_never->setChecked(Settings::Instance().GetCursorVisibility() == + SConfig::ShowCursor::Never); + m_checkbox_lock_mouse->setChecked(Settings::Instance().GetLockCursor()); m_checkbox_disable_screensaver->setChecked(Config::Get(Config::MAIN_DISABLE_SCREENSAVER)); } @@ -300,3 +324,18 @@ void InterfacePane::OnSaveConfig() settings.SaveSettings(); } + +void InterfacePane::OnCursorVisibleMovement() +{ + Settings::Instance().SetCursorVisibility(SConfig::ShowCursor::OnMovement); +} + +void InterfacePane::OnCursorVisibleNever() +{ + Settings::Instance().SetCursorVisibility(SConfig::ShowCursor::Never); +} + +void InterfacePane::OnCursorVisibleAlways() +{ + Settings::Instance().SetCursorVisibility(SConfig::ShowCursor::Constantly); +} diff --git a/Source/Core/DolphinQt/Settings/InterfacePane.h b/Source/Core/DolphinQt/Settings/InterfacePane.h index 405bcced05..d751c507c5 100644 --- a/Source/Core/DolphinQt/Settings/InterfacePane.h +++ b/Source/Core/DolphinQt/Settings/InterfacePane.h @@ -8,6 +8,7 @@ class QCheckBox; class QComboBox; class QLabel; +class QRadioButton; class QVBoxLayout; class InterfacePane final : public QWidget @@ -23,6 +24,9 @@ private: void ConnectLayout(); void LoadConfig(); void OnSaveConfig(); + void OnCursorVisibleMovement(); + void OnCursorVisibleNever(); + void OnCursorVisibleAlways(); QVBoxLayout* m_main_layout; QComboBox* m_combobox_language; @@ -43,6 +47,8 @@ private: QCheckBox* m_checkbox_enable_osd; QCheckBox* m_checkbox_show_active_title; QCheckBox* m_checkbox_pause_on_focus_lost; - QCheckBox* m_checkbox_hide_mouse; + QRadioButton* m_radio_cursor_visible_movement; + QRadioButton* m_radio_cursor_visible_never; + QRadioButton* m_radio_cursor_visible_always; QCheckBox* m_checkbox_lock_mouse; };