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 c2a151c270..952b3a6b41 100644
--- a/Source/Core/DolphinQt/MenuBar.cpp
+++ b/Source/Core/DolphinQt/MenuBar.cpp
@@ -64,6 +64,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"
@@ -464,7 +465,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());
@@ -719,7 +721,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())
@@ -745,7 +748,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())
@@ -779,7 +783,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();
@@ -807,7 +812,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..cb18cc6be8
--- /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 QMenu that will not be automatically dismissed when one of its checkable
+// actions are triggered.
+class NonAutodismissibleMenu : public QMenu
+{
+public:
+ using QMenu::QMenu;
+
+protected:
+ void mouseReleaseEvent(QMouseEvent* event) override;
+};
+
+} // namespace QtUtils