diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt
index b6e1659d4f..c72277c1f3 100644
--- a/Source/Core/DolphinQt/CMakeLists.txt
+++ b/Source/Core/DolphinQt/CMakeLists.txt
@@ -314,6 +314,8 @@ add_executable(dolphin-emu
QtUtils/ImageConverter.h
QtUtils/ModalMessageBox.cpp
QtUtils/ModalMessageBox.h
+ QtUtils/NonAutodismissibleMenu.cpp
+ QtUtils/NonAutodismissibleMenu.h
QtUtils/NonDefaultQPushButton.cpp
QtUtils/NonDefaultQPushButton.h
QtUtils/ParallelProgressDialog.h
diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj
index 41349a4cee..6bc6246661 100644
--- a/Source/Core/DolphinQt/DolphinQt.vcxproj
+++ b/Source/Core/DolphinQt/DolphinQt.vcxproj
@@ -195,6 +195,7 @@
+
diff --git a/Source/Core/DolphinQt/MenuBar.cpp b/Source/Core/DolphinQt/MenuBar.cpp
index 232ea1e337..6097467108 100644
--- a/Source/Core/DolphinQt/MenuBar.cpp
+++ b/Source/Core/DolphinQt/MenuBar.cpp
@@ -63,6 +63,7 @@
#include "DolphinQt/NANDRepairDialog.h"
#include "DolphinQt/QtUtils/DolphinFileDialog.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h"
+#include "DolphinQt/QtUtils/NonAutodismissibleMenu.h"
#include "DolphinQt/QtUtils/ParallelProgressDialog.h"
#include "DolphinQt/QtUtils/QueueOnObject.h"
#include "DolphinQt/QtUtils/SetWindowDecorations.h"
@@ -461,7 +462,8 @@ void MenuBar::UpdateStateSlotMenu()
void MenuBar::AddViewMenu()
{
- QMenu* view_menu = addMenu(tr("&View"));
+ auto* const view_menu{new QtUtils::NonAutodismissibleMenu(tr("&View"), this)};
+ addMenu(view_menu);
QAction* show_log = view_menu->addAction(tr("Show &Log"));
show_log->setCheckable(true);
show_log->setChecked(Settings::Instance().IsLogVisible());
@@ -716,7 +718,8 @@ void MenuBar::AddListColumnsMenu(QMenu* view_menu)
{tr("Tags"), &Config::MAIN_GAMELIST_COLUMN_TAGS}};
QActionGroup* column_group = new QActionGroup(this);
- m_cols_menu = view_menu->addMenu(tr("List Columns"));
+ m_cols_menu = new QtUtils::NonAutodismissibleMenu(tr("List Columns"), view_menu);
+ view_menu->addMenu(m_cols_menu);
column_group->setExclusive(false);
for (const auto& key : columns.keys())
@@ -742,7 +745,8 @@ void MenuBar::AddShowPlatformsMenu(QMenu* view_menu)
{tr("Show ELF/DOL"), &Config::MAIN_GAMELIST_LIST_ELF_DOL}};
QActionGroup* platform_group = new QActionGroup(this);
- QMenu* plat_menu = view_menu->addMenu(tr("Show Platforms"));
+ auto* const plat_menu{new QtUtils::NonAutodismissibleMenu(tr("Show Platforms"), view_menu)};
+ view_menu->addMenu(plat_menu);
platform_group->setExclusive(false);
for (const auto& key : platform_map.keys())
@@ -776,7 +780,8 @@ void MenuBar::AddShowRegionsMenu(QMenu* view_menu)
{tr("Show World"), &Config::MAIN_GAMELIST_LIST_WORLD},
{tr("Show Unknown"), &Config::MAIN_GAMELIST_LIST_UNKNOWN}};
- QMenu* const region_menu = view_menu->addMenu(tr("Show Regions"));
+ auto* const region_menu{new QtUtils::NonAutodismissibleMenu(tr("Show Regions"), view_menu)};
+ view_menu->addMenu(region_menu);
const QAction* const show_all_regions = region_menu->addAction(tr("Show All"));
const QAction* const hide_all_regions = region_menu->addAction(tr("Hide All"));
region_menu->addSeparator();
@@ -804,7 +809,8 @@ void MenuBar::AddShowRegionsMenu(QMenu* view_menu)
void MenuBar::AddMovieMenu()
{
- auto* movie_menu = addMenu(tr("&Movie"));
+ auto* const movie_menu{new QtUtils::NonAutodismissibleMenu(tr("&Movie"), this)};
+ addMenu(movie_menu);
m_recording_start =
movie_menu->addAction(tr("Start Re&cording Input"), this, [this] { emit StartRecording(); });
m_recording_play =
diff --git a/Source/Core/DolphinQt/QtUtils/NonAutodismissibleMenu.cpp b/Source/Core/DolphinQt/QtUtils/NonAutodismissibleMenu.cpp
new file mode 100644
index 0000000000..c7aa14389d
--- /dev/null
+++ b/Source/Core/DolphinQt/QtUtils/NonAutodismissibleMenu.cpp
@@ -0,0 +1,28 @@
+// Copyright 2025 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "DolphinQt/QtUtils/NonAutodismissibleMenu.h"
+
+#include
+#include
+
+namespace QtUtils
+{
+
+void NonAutodismissibleMenu::mouseReleaseEvent(QMouseEvent* const event)
+{
+ if (!event)
+ return;
+
+ QAction* const action{activeAction()};
+ if (action && action->isEnabled() && action->isCheckable())
+ {
+ action->trigger();
+ event->accept();
+ return;
+ }
+
+ QMenu::mouseReleaseEvent(event);
+}
+
+} // namespace QtUtils
diff --git a/Source/Core/DolphinQt/QtUtils/NonAutodismissibleMenu.h b/Source/Core/DolphinQt/QtUtils/NonAutodismissibleMenu.h
new file mode 100644
index 0000000000..34475d6eb2
--- /dev/null
+++ b/Source/Core/DolphinQt/QtUtils/NonAutodismissibleMenu.h
@@ -0,0 +1,22 @@
+// Copyright 2025 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include
+
+namespace QtUtils
+{
+
+/// A menu widget based on \c QMenu that will not be automatically dismissed when one of its
+/// \em checkable actions are triggered.
+class NonAutodismissibleMenu : public QMenu
+{
+public:
+ using QMenu::QMenu;
+
+protected:
+ void mouseReleaseEvent(QMouseEvent* event) override;
+};
+
+} // namespace QtUtils