From 7209cf87cb035fa08e06e8705d54a8a8c08c3d03 Mon Sep 17 00:00:00 2001 From: Dentomologist Date: Wed, 4 Mar 2026 15:24:30 -0800 Subject: [PATCH] GameList: Add status bar with game count The status bar shows the number of games in your collection. If any games are hidden by the platform, region, or search filters it will also show how many games are visible and how many are filtered. The visibility of the status bar can be toggled from the menu by selecting `View`->`Show Game Count`. Implements https://bugs.dolphin-emu.org/issues/9517. --- Source/Core/DolphinQt/CMakeLists.txt | 2 ++ Source/Core/DolphinQt/DolphinQt.vcxproj | 2 ++ Source/Core/DolphinQt/GameCount.cpp | 33 +++++++++++++++++++++ Source/Core/DolphinQt/GameCount.h | 15 ++++++++++ Source/Core/DolphinQt/GameList/GameList.cpp | 13 ++++++++ Source/Core/DolphinQt/GameList/GameList.h | 3 ++ Source/Core/DolphinQt/MainWindow.cpp | 23 ++++++++++++++ Source/Core/DolphinQt/MainWindow.h | 2 ++ Source/Core/DolphinQt/MenuBar.cpp | 7 +++++ Source/Core/DolphinQt/Settings.cpp | 16 ++++++++++ Source/Core/DolphinQt/Settings.h | 3 ++ 11 files changed, 119 insertions(+) create mode 100644 Source/Core/DolphinQt/GameCount.cpp create mode 100644 Source/Core/DolphinQt/GameCount.h 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();