diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt
index e7e70c508b..b0c6829de2 100644
--- a/Source/Core/DolphinQt/CMakeLists.txt
+++ b/Source/Core/DolphinQt/CMakeLists.txt
@@ -265,6 +265,8 @@ add_executable(dolphin-emu
FIFO/FIFOAnalyzer.h
FIFO/FIFOPlayerWindow.cpp
FIFO/FIFOPlayerWindow.h
+ GameCount.cpp
+ GameCount.h
GameList/GameList.cpp
GameList/GameList.h
GameList/GameListModel.cpp
diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj
index a9459c971a..1a3cdfef23 100644
--- a/Source/Core/DolphinQt/DolphinQt.vcxproj
+++ b/Source/Core/DolphinQt/DolphinQt.vcxproj
@@ -165,6 +165,7 @@
+
@@ -395,6 +396,7 @@
+
diff --git a/Source/Core/DolphinQt/GameCount.cpp b/Source/Core/DolphinQt/GameCount.cpp
new file mode 100644
index 0000000000..081d8a26df
--- /dev/null
+++ b/Source/Core/DolphinQt/GameCount.cpp
@@ -0,0 +1,33 @@
+// Copyright 2026 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "DolphinQt/GameCount.h"
+
+#include
+#include
+#include
+
+GameCount::GameCount(QWidget* const parent) : QStatusBar(parent)
+{
+ setSizeGripEnabled(false);
+ QFontMetrics font_metrics(font());
+ const int margin = font_metrics.height() / 5;
+ layout()->setContentsMargins(margin, margin, margin, margin);
+}
+
+void GameCount::OnGameCountUpdated(const int total_games, const int visible_games)
+{
+ const int filtered_games = total_games - visible_games;
+
+ if (filtered_games > 0)
+ {
+ showMessage(tr("%1 game(s) in your collection (%2 visible, %3 filtered)")
+ .arg(total_games)
+ .arg(visible_games)
+ .arg(filtered_games));
+ }
+ else
+ {
+ showMessage(tr("%1 game(s) in your collection").arg(total_games));
+ }
+}
diff --git a/Source/Core/DolphinQt/GameCount.h b/Source/Core/DolphinQt/GameCount.h
new file mode 100644
index 0000000000..19ad0c1a98
--- /dev/null
+++ b/Source/Core/DolphinQt/GameCount.h
@@ -0,0 +1,15 @@
+// Copyright 2026 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include
+
+class GameCount : public QStatusBar
+{
+ Q_OBJECT
+public:
+ explicit GameCount(QWidget* parent = nullptr);
+
+ void OnGameCountUpdated(int total_games, int visible_games);
+};
diff --git a/Source/Core/DolphinQt/GameList/GameList.cpp b/Source/Core/DolphinQt/GameList/GameList.cpp
index c63230aa55..dad4bc2681 100644
--- a/Source/Core/DolphinQt/GameList/GameList.cpp
+++ b/Source/Core/DolphinQt/GameList/GameList.cpp
@@ -139,6 +139,8 @@ GameList::GameList(QWidget* parent) : QStackedWidget(parent), m_model(this)
connect(m_grid, &QListView::doubleClicked, this, &GameList::GameSelected);
connect(&m_model, &QAbstractItemModel::rowsInserted, this, &GameList::ConsiderViewChange);
connect(&m_model, &QAbstractItemModel::rowsRemoved, this, &GameList::ConsiderViewChange);
+ connect(&m_model, &QAbstractItemModel::rowsInserted, this, &GameList::UpdateGameCount);
+ connect(&m_model, &QAbstractItemModel::rowsRemoved, this, &GameList::UpdateGameCount);
addWidget(m_list);
addWidget(m_grid);
@@ -1043,6 +1045,8 @@ void GameList::OnGameListVisibilityChanged()
{
m_list_proxy->invalidate();
m_grid_proxy->invalidate();
+
+ UpdateGameCount();
}
void GameList::OnSectionResized(int index, int, int)
@@ -1171,6 +1175,15 @@ void GameList::SetSearchTerm(const QString& term)
m_grid_proxy->invalidate();
UpdateColumnVisibility();
+ UpdateGameCount();
+}
+
+void GameList::UpdateGameCount() const
+{
+ const int total_games = m_model.rowCount(QModelIndex{});
+ const int visible_games = m_list_proxy->rowCount();
+
+ emit GameCountUpdated(total_games, visible_games);
}
void GameList::ZoomIn()
diff --git a/Source/Core/DolphinQt/GameList/GameList.h b/Source/Core/DolphinQt/GameList/GameList.h
index df771bba87..94de2eec35 100644
--- a/Source/Core/DolphinQt/GameList/GameList.h
+++ b/Source/Core/DolphinQt/GameList/GameList.h
@@ -43,6 +43,8 @@ public:
void OnColumnVisibilityToggled(const QString& row, bool visible);
void OnGameListVisibilityChanged();
+ void UpdateGameCount() const;
+
void resizeEvent(QResizeEvent* event) override;
void PurgeCache();
@@ -51,6 +53,7 @@ public:
signals:
void GameSelected();
+ void GameCountUpdated(int total_games, int visible_games) const;
void OnStartWithRiivolution(const UICommon::GameFile& game);
void NetPlayHost(const UICommon::GameFile& game);
void SelectionChanged(std::shared_ptr game_file);
diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp
index 962f75e96c..c3f5b4148c 100644
--- a/Source/Core/DolphinQt/MainWindow.cpp
+++ b/Source/Core/DolphinQt/MainWindow.cpp
@@ -95,6 +95,7 @@
#include "DolphinQt/EmulatedUSB/WiiSpeakWindow.h"
#include "DolphinQt/FIFO/FIFOPlayerWindow.h"
#include "DolphinQt/GCMemcardManager.h"
+#include "DolphinQt/GameCount.h"
#include "DolphinQt/GameList/GameList.h"
#include "DolphinQt/Host.h"
#include "DolphinQt/HotkeyScheduler.h"
@@ -449,6 +450,7 @@ void MainWindow::CreateComponents()
m_menu_bar = new MenuBar(this);
m_tool_bar = new ToolBar(this);
m_search_bar = new SearchBar(this);
+ m_game_count = new GameCount(this);
m_game_list = new GameList(this);
m_render_widget = new RenderWidget;
m_stack = new QStackedWidget(this);
@@ -738,9 +740,30 @@ void MainWindow::ConnectStack()
layout->addWidget(m_game_list);
layout->addWidget(m_search_bar);
+ layout->addWidget(m_game_count);
+ layout->setSpacing(0);
layout->setContentsMargins(0, 0, 0, 0);
connect(m_search_bar, &SearchBar::Search, m_game_list, &GameList::SetSearchTerm);
+ connect(m_game_list, &GameList::GameCountUpdated, m_game_count, &GameCount::OnGameCountUpdated);
+
+ m_game_list->UpdateGameCount();
+
+ const auto update_spacing = [this](const bool game_count_is_visible) {
+ // The bottom margin of the search bar and the top margin of the game count are both suitable
+ // when the other widget is hidden, but when both are visible the gap created by the combination
+ // is too large. To fix this we set the bottom margin of the search bar to 0 when the game count
+ // is visible and set it to the top margin when the game count is hidden.
+ m_game_count->setVisible(game_count_is_visible);
+ auto* const search_layout = m_search_bar->layout();
+ QMargins search_margins = search_layout->contentsMargins();
+ const int new_bottom_margin = game_count_is_visible ? 0 : search_margins.top();
+ search_margins.setBottom(new_bottom_margin);
+ search_layout->setContentsMargins(search_margins);
+ };
+ update_spacing(Settings::Instance().IsGameCountVisible());
+
+ connect(&Settings::Instance(), &Settings::GameCountVisibilityChanged, update_spacing);
m_stack->addWidget(widget);
diff --git a/Source/Core/DolphinQt/MainWindow.h b/Source/Core/DolphinQt/MainWindow.h
index c0d5bd110e..93490eaad6 100644
--- a/Source/Core/DolphinQt/MainWindow.h
+++ b/Source/Core/DolphinQt/MainWindow.h
@@ -31,6 +31,7 @@ class CodeWidget;
class DiscordHandler;
class DragEnterEvent;
class FreeLookWindow;
+class GameCount;
class GameList;
class GBATASInputWindow;
class GCTASInputWindow;
@@ -241,6 +242,7 @@ private:
MenuBar* m_menu_bar;
SearchBar* m_search_bar;
GameList* m_game_list;
+ GameCount* m_game_count;
RenderWidget* m_render_widget = nullptr;
bool m_rendering_to_main;
bool m_stop_confirm_showing = false;
diff --git a/Source/Core/DolphinQt/MenuBar.cpp b/Source/Core/DolphinQt/MenuBar.cpp
index 04c1b3eecd..5015eb5fee 100644
--- a/Source/Core/DolphinQt/MenuBar.cpp
+++ b/Source/Core/DolphinQt/MenuBar.cpp
@@ -595,6 +595,13 @@ void MenuBar::AddViewMenu()
AddShowPlatformsMenu(view_menu);
AddShowRegionsMenu(view_menu);
+ view_menu->addSeparator();
+ QAction* const show_game_count = view_menu->addAction(tr("Show Game Count"));
+ show_game_count->setCheckable(true);
+ show_game_count->setChecked(Settings::Instance().IsGameCountVisible());
+ connect(show_game_count, &QAction::toggled, &Settings::Instance(),
+ &Settings::SetGameCountVisible);
+
view_menu->addSeparator();
QAction* const purge_action =
view_menu->addAction(tr("Purge Game List Cache"), this, &MenuBar::PurgeGameListCache);
diff --git a/Source/Core/DolphinQt/Settings.cpp b/Source/Core/DolphinQt/Settings.cpp
index 3046009fbb..396cd14ae5 100644
--- a/Source/Core/DolphinQt/Settings.cpp
+++ b/Source/Core/DolphinQt/Settings.cpp
@@ -514,6 +514,21 @@ void Settings::SetAutoRefreshEnabled(bool enabled)
emit AutoRefreshToggled(enabled);
}
+bool Settings::IsGameCountVisible() const
+{
+ return GetQSettings().value(QStringLiteral("GameCount/Visible"), true).toBool();
+}
+
+void Settings::SetGameCountVisible(const bool visible)
+{
+ if (IsGameCountVisible() == visible)
+ return;
+
+ GetQSettings().setValue(QStringLiteral("GameCount/Visible"), visible);
+
+ emit GameCountVisibilityChanged(visible);
+}
+
QString Settings::GetDefaultGame() const
{
return QString::fromStdString(Config::Get(Config::MAIN_DEFAULT_ISO));
@@ -815,6 +830,7 @@ void Settings::RefreshWidgetVisibility()
emit DebugModeToggled(IsDebugModeEnabled());
emit LogVisibilityChanged(IsLogVisible());
emit LogConfigVisibilityChanged(IsLogConfigVisible());
+ emit GameCountVisibilityChanged(IsGameCountVisible());
}
void Settings::SetDebugFont(QFont font)
diff --git a/Source/Core/DolphinQt/Settings.h b/Source/Core/DolphinQt/Settings.h
index 97d3b751cd..2c721a8489 100644
--- a/Source/Core/DolphinQt/Settings.h
+++ b/Source/Core/DolphinQt/Settings.h
@@ -110,6 +110,8 @@ public:
void ReloadTitleDB();
bool IsAutoRefreshEnabled() const;
void SetAutoRefreshEnabled(bool enabled);
+ bool IsGameCountVisible() const;
+ void SetGameCountVisible(bool visible);
// Emulation
int GetStateSlot() const;
@@ -228,6 +230,7 @@ signals:
void DevicesChanged();
void WiiSpeakMuteChanged(bool muted);
void EnableGfxModsChanged(bool enabled);
+ void GameCountVisibilityChanged(bool visible);
private:
Settings();