GameList: Add favorites to game list

Signed-off-by: SternXD <[email protected]>
This commit is contained in:
SternXD
2026-10-06 18:02:48 +02:00
committed by lightningterror
parent 9bb37321a0
commit b011ba063c
15 changed files with 237 additions and 16 deletions
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.3.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path fill="rgb(255, 212, 59)" stroke="rgb(20, 20, 20)" stroke-width="24" stroke-linecap="round" stroke-linejoin="round" d="M341.5 45.1C337.4 37.1 329.1 32 320.1 32C311.1 32 302.8 37.1 298.7 45.1L225.1 189.3L65.2 214.7C56.3 216.1 48.9 222.4 46.1 231C43.3 239.6 45.6 249 51.9 255.4L166.3 369.9L141.1 529.8C139.7 538.7 143.4 547.7 150.7 553C158 558.3 167.6 559.1 175.7 555L320.1 481.6L464.4 555C472.4 559.1 482.1 558.3 489.4 553C496.7 547.7 500.4 538.8 499 529.8L473.7 369.9L588.1 255.4C594.5 249 596.7 239.6 593.9 231C591.1 222.4 583.8 216.1 574.8 214.7L415 189.3L341.5 45.1z"/></svg>

After

Width:  |  Height:  |  Size: 796 B

+4
View File
@@ -305,6 +305,10 @@ void Host::CancelGameListRefresh()
{
}
void Host::OnGameListFavoriteChanged(const std::string& path)
{
}
bool Host::IsFullscreen()
{
return false;
+24
View File
@@ -242,6 +242,8 @@ QVariant GameListModel::data(const QModelIndex& index, const int role) const
const GameList::Entry* ge = GameList::GetEntryByIndex(row);
if (!ge)
return QVariant();
if (role == FavoriteRole)
return ge->is_favorite;
// See: https://doc.qt.io/qt-6/qt.html#ItemDataRole-enum
switch (role)
@@ -283,6 +285,11 @@ QVariant GameListModel::data(const QModelIndex& index, const int role) const
{
switch (index.column())
{
case Column_Title:
if (ge->is_favorite)
return m_favorite_pixmap;
return QVariant();
case Column_Type:
return m_type_pixmaps[static_cast<u32>(ge->type)];
@@ -368,6 +375,21 @@ void GameListModel::refresh()
endResetModel();
}
void GameListModel::refreshFavorite(const std::string& path)
{
const auto lock = GameList::GetLock();
const u32 count = GameList::GetEntryCount();
for (u32 row = 0; row < count; row++)
{
const GameList::Entry* entry = GameList::GetEntryByIndex(row);
if (entry && entry->path == path)
{
emit dataChanged(index(static_cast<int>(row), 0), index(static_cast<int>(row), Column_Count - 1));
break;
}
}
}
bool GameListModel::titlesLessThan(const int left_row, const int right_row) const
{
if (left_row < 0 || left_row >= static_cast<int>(GameList::GetEntryCount()) || right_row < 0 ||
@@ -525,6 +547,8 @@ void GameListModel::loadCommonImages()
loadThemeSpecificImages();
const QString base_path(QtHost::GetResourcesBasePath());
m_favorite_pixmap = QIcon(QStringLiteral("%1/icons/star.svg").arg(base_path)).pixmap(QSize(20, 20), m_dpr);
for (u32 rating = 1; rating < GameList::CompatibilityRatingCount; rating++)
m_compatibility_pixmaps[rating] = QIcon((QStringLiteral("%1/icons/star-%2.svg").arg(base_path).arg(rating - 1))).pixmap(QSize(88, 16), m_dpr);
+3
View File
@@ -20,6 +20,7 @@ class GameListModel final : public QAbstractTableModel
Q_OBJECT
public:
static constexpr int FavoriteRole = Qt::UserRole + 1;
enum Column : int
{
Column_Type,
@@ -54,6 +55,7 @@ public:
__fi const QString& getColumnDisplayName(int column) { return m_column_display_names[column]; }
void refresh();
void refreshFavorite(const std::string& path);
void reloadThemeSpecificImages();
bool titlesLessThan(int left_row, int right_row) const;
@@ -100,6 +102,7 @@ private:
std::array<QPixmap, static_cast<u32>(GameList::Region::Count)> m_region_pixmaps;
QPixmap m_placeholder_pixmap;
QPixmap m_loading_pixmap;
QPixmap m_favorite_pixmap;
qreal m_dpr;
std::array<QPixmap, static_cast<int>(GameList::CompatibilityRatingCount)> m_compatibility_pixmaps;
+68 -1
View File
@@ -35,6 +35,7 @@
#include <QtWidgets/QMenu>
#include <QtWidgets/QScrollBar>
#include <QtWidgets/QStyledItemDelegate>
#include <QtWidgets/QToolButton>
#include <QShortcut>
static const char* SUPPORTED_FORMATS_STRING = QT_TRANSLATE_NOOP(GameListWidget,
@@ -94,15 +95,26 @@ public:
m_filter_name = name;
endFilterChange(Direction::Rows);
}
void setFilterFavoritesOnly(bool enabled)
{
beginFilterChange();
m_filter_favorites_only = enabled;
endFilterChange(Direction::Rows);
}
bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override
{
if (m_filter_type != GameList::EntryType::Count ||
m_filter_region != GameList::Region::Count ||
!m_filter_name.isEmpty())
!m_filter_name.isEmpty() ||
m_filter_favorites_only)
{
const auto lock = GameList::GetLock();
const GameList::Entry* entry = GameList::GetEntryByIndex(source_row);
if (!entry)
return false;
if (m_filter_favorites_only && !entry->is_favorite)
return false;
if (m_filter_type != GameList::EntryType::Count && entry->type != m_filter_type)
return false;
if (m_filter_region != GameList::Region::Count && entry->region != m_filter_region)
@@ -120,6 +132,11 @@ public:
bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const override
{
const bool left_favorite = source_left.data(GameListModel::FavoriteRole).toBool();
const bool right_favorite = source_right.data(GameListModel::FavoriteRole).toBool();
if (left_favorite != right_favorite)
return (sortOrder() == Qt::AscendingOrder) ? left_favorite : !left_favorite;
return m_model->lessThan(source_left, source_right, source_left.column());
}
@@ -128,6 +145,7 @@ private:
GameList::EntryType m_filter_type = GameList::EntryType::Count;
GameList::Region m_filter_region = GameList::Region::Count;
QString m_filter_name;
bool m_filter_favorites_only = false;
};
namespace
@@ -226,6 +244,38 @@ namespace
painter->restore();
}
};
class FavoriteCoverDelegate final : public QStyledItemDelegate
{
public:
using QStyledItemDelegate::QStyledItemDelegate;
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override
{
QStyledItemDelegate::paint(painter, option, index);
if (!index.data(GameListModel::FavoriteRole).toBool())
return;
QStyleOptionViewItem view_option(option);
initStyleOption(&view_option, index);
const QStyle* style = option.widget ? option.widget->style() : QApplication::style();
const QRect decoration_rect = style->subElementRect(QStyle::SE_ItemViewItemDecoration, &view_option, option.widget);
const QPixmap cover = qvariant_cast<QPixmap>(index.data(Qt::DecorationRole));
if (cover.isNull() || decoration_rect.isEmpty())
return;
const QRect cover_rect = QStyle::alignedRect(option.direction, option.decorationAlignment,
cover.deviceIndependentSize().toSize(), decoration_rect);
const int star_size = std::clamp(qRound(static_cast<qreal>(cover_rect.width()) * 0.12), 9, 42);
const int margin = std::max(3, star_size / 3);
painter->save();
const QRect star_rect(cover_rect.right() - star_size - margin, cover_rect.top() + margin, star_size, star_size);
static const QIcon star_icon(QStringLiteral("%1/icons/star.svg").arg(QtHost::GetResourcesBasePath()));
const QPixmap star_pixmap = star_icon.pixmap(QSize(star_size, star_size), option.widget ? option.widget->devicePixelRatio() : 1.0);
painter->drawPixmap(star_rect.topLeft(), star_pixmap);
painter->restore();
}
};
} // namespace
GameListWidget::GameListWidget(QWidget* parent /* = nullptr */)
@@ -280,6 +330,22 @@ void GameListWidget::initialize()
m_sort_model->setFilterName(text);
});
const bool favorites_only = Host::GetBaseBoolSettingValue("GameList", "FavoritesOnly", false);
QToolButton* filter_favorites = new QToolButton(this);
filter_favorites->setCheckable(true);
filter_favorites->setAutoRaise(true);
filter_favorites->setToolTip(tr("Show Favorites Only"));
filter_favorites->setIcon(QIcon(QStringLiteral("%1/icons/star.svg").arg(QtHost::GetResourcesBasePath())));
filter_favorites->setChecked(favorites_only);
m_sort_model->setFilterFavoritesOnly(favorites_only);
m_ui.horizontalLayout_2->insertWidget(0, filter_favorites);
connect(filter_favorites, &QToolButton::toggled, this, [this](bool checked) {
m_sort_model->setFilterFavoritesOnly(checked);
Host::SetBaseBoolSettingValue("GameList", "FavoritesOnly", checked);
Host::CommitBaseSettingChanges();
});
connect(new QShortcut(QKeySequence::Find, this), &QShortcut::activated, [this]() {
m_ui.searchText->setFocus();
});
@@ -343,6 +409,7 @@ void GameListWidget::initialize()
m_list_view = new GameListGridListView(m_ui.stack);
m_list_view->setModel(m_sort_model);
m_list_view->setModelColumn(GameListModel::Column_Cover);
m_list_view->setItemDelegate(new FavoriteCoverDelegate(m_list_view));
m_list_view->setSelectionMode(QAbstractItemView::SingleSelection);
m_list_view->setViewMode(QListView::IconMode);
m_list_view->setResizeMode(QListView::Adjust);
+14
View File
@@ -5,6 +5,7 @@
#include "AutoUpdaterDialog.h"
#include "CoverDownloadDialog.h"
#include "DisplayWidget.h"
#include "GameList/GameListModel.h"
#include "GameList/GameListRefreshThread.h"
#include "GameList/GameListWidget.h"
#include "LogWindow.h"
@@ -1542,6 +1543,13 @@ void MainWindow::refreshGameList(bool invalidate_cache, bool popup_on_error)
m_game_list_widget->refresh(invalidate_cache, popup_on_error);
}
void MainWindow::refreshGameListFavorite(const std::string& path)
{
m_game_list_widget->getModel()->refreshFavorite(path);
if (MTGS::IsOpen())
Host::RunOnGSThread(&FullscreenUI::GameListFavoriteChanged);
}
void MainWindow::cancelGameListRefresh()
{
m_game_list_widget->cancelRefresh();
@@ -1855,6 +1863,7 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
if (entry.has_value())
{
const bool is_favorite = entry->is_favorite;
QAction* action = menu.addAction(tr("Properties..."));
action->setEnabled(!entry->serial.empty() || entry->type == GameList::EntryType::ELF);
if (action->isEnabled())
@@ -1881,6 +1890,11 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
connect(menu.addAction(tr("Exclude From List")), &QAction::triggered,
[this, entry]() { getSettingsWindow()->getGameListSettingsWidget()->addExcludedPath(entry->path); });
action = menu.addAction(is_favorite ? tr("Remove from Favorites") : tr("Add to Favorites"));
connect(action, &QAction::triggered, this, [entry, is_favorite]() {
GameList::SaveFavoriteForPath(entry->path, !is_favorite);
});
const time_t entry_played_time = GameList::GetCachedPlayedTimeForSerial(entry->serial);
// Best two options given zero play time are to grey this out or to not show it at all.
if (entry_played_time)
+3
View File
@@ -113,6 +113,9 @@ public:
/// Rescans a single file. NOTE: Happens on UI thread.
void rescanFile(const std::string& path);
/// Updates a favorite in the game list. NOTE: Happens on UI thread.
void refreshGameListFavorite(const std::string& path);
/// Start a file from a user action (e.g. dragging a file onto the main window or with macOS open with)
bool startFile(const QString& path);
+5
View File
@@ -1268,6 +1268,11 @@ void Host::CancelGameListRefresh()
QMetaObject::invokeMethod(g_main_window, "cancelGameListRefresh", Qt::BlockingQueuedConnection);
}
void Host::OnGameListFavoriteChanged(const std::string& path)
{
QtHost::RunOnUIThread([path]() { g_main_window->refreshGameListFavorite(path); });
}
void Host::RequestExitApplication(bool allow_confirm)
{
QMetaObject::invokeMethod(g_main_window, "requestExit", Qt::QueuedConnection, Q_ARG(bool, allow_confirm));
+35 -3
View File
@@ -65,7 +65,8 @@ namespace GameList
static bool GetGameListEntryFromCache(const std::string& path, GameList::Entry* entry);
static void ScanDirectory(const char* path, bool recursive, bool only_cache, const std::vector<std::string>& excluded_paths,
const PlayedTimeMap& played_time_map, const INISettingsInterface& custom_attributes_ini, ProgressCallback* progress);
static bool AddFileFromCache(const std::string& path, std::time_t timestamp, const PlayedTimeMap& played_time_map);
static bool AddFileFromCache(const std::string& path, std::time_t timestamp, const PlayedTimeMap& played_time_map,
const INISettingsInterface& custom_attributes_ini);
static bool ScanFile(std::string path, std::time_t timestamp, std::unique_lock<std::recursive_mutex>& lock,
const PlayedTimeMap& played_time_map, const INISettingsInterface& custom_attributes_ini);
@@ -716,7 +717,8 @@ void GameList::ScanDirectory(const char* path, bool recursive, bool only_cache,
}
std::unique_lock lock(s_mutex);
if (GetEntryForPath(ffd.FileName.c_str()) || AddFileFromCache(ffd.FileName, ffd.ModificationTime, played_time_map) || only_cache)
if (GetEntryForPath(ffd.FileName.c_str()) ||
AddFileFromCache(ffd.FileName, ffd.ModificationTime, played_time_map, custom_attributes_ini) || only_cache)
{
continue;
}
@@ -731,7 +733,8 @@ void GameList::ScanDirectory(const char* path, bool recursive, bool only_cache,
progress->PopState();
}
bool GameList::AddFileFromCache(const std::string& path, std::time_t timestamp, const PlayedTimeMap& played_time_map)
bool GameList::AddFileFromCache(const std::string& path, std::time_t timestamp, const PlayedTimeMap& played_time_map,
const INISettingsInterface& custom_attributes_ini)
{
Entry entry;
if (!GetGameListEntryFromCache(path, &entry) || entry.last_modified_time != timestamp)
@@ -747,6 +750,7 @@ bool GameList::AddFileFromCache(const std::string& path, std::time_t timestamp,
entry.last_played_time = iter->second.last_played_time;
entry.total_played_time = iter->second.total_played_time;
}
entry.is_favorite = custom_attributes_ini.GetBoolValue(EncodeIniKey(entry.path).c_str(), "Favorite", false);
s_entries.push_back(std::move(entry));
return true;
@@ -800,6 +804,7 @@ bool GameList::ScanFile(std::string path, std::time_t timestamp, std::unique_loc
entry.region = static_cast<Region>(custom_region_value);
}
}
entry.is_favorite = custom_attributes_ini.GetBoolValue(EncodeIniKey(entry.path).c_str(), "Favorite", false);
lock.lock();
@@ -1525,6 +1530,33 @@ void GameList::SaveCustomRegionForPath(const std::string& path, int custom_regio
}
}
void GameList::SaveFavoriteForPath(const std::string& path, bool favorite)
{
INISettingsInterface custom_attributes_ini(GetCustomPropertiesFile());
custom_attributes_ini.Load();
const std::string key(EncodeIniKey(path));
if (favorite)
custom_attributes_ini.SetBoolValue(key.c_str(), "Favorite", true);
else
custom_attributes_ini.DeleteValue(key.c_str(), "Favorite");
if (!custom_attributes_ini.Save())
return;
{
std::unique_lock lock(s_mutex);
for (Entry& entry : s_entries)
{
if (entry.path == path)
{
entry.is_favorite = favorite;
break;
}
}
}
Host::OnGameListFavoriteChanged(path);
}
std::string GameList::GetCustomTitleForPath(const std::string& path)
{
std::string ret;
+2
View File
@@ -74,6 +74,7 @@ namespace GameList
{
EntryType type = EntryType::PS2Disc;
Region region = Region::Other;
bool is_favorite = false;
std::string path;
std::string serial;
@@ -160,5 +161,6 @@ namespace GameList
void CheckCustomAttributesForPath(const std::string& path, bool& has_custom_title, bool& has_custom_region);
void SaveCustomTitleForPath(const std::string& path, const std::string& custom_title);
void SaveCustomRegionForPath(const std::string& path, int custom_region);
void SaveFavoriteForPath(const std::string& path, bool favorite);
std::string GetCustomTitleForPath(const std::string& path);
} // namespace GameList
+3
View File
@@ -96,6 +96,9 @@ namespace Host
/// Cancels game list refresh, if there is one in progress.
void CancelGameListRefresh();
/// Notifies the host when a favorite changes. May be called from any thread.
void OnGameListFavoriteChanged(const std::string& path);
/// Requests shut down of the current virtual machine.
void RequestVMShutdown(bool allow_confirm, bool allow_save_state, bool default_save_state);
+69 -12
View File
@@ -265,6 +265,11 @@ void FullscreenUI::PreferEnglishGameListChanged()
s_prefer_english_titles = Host::GetBaseBoolSettingValue("UI", "PreferEnglishGameList", false);
}
void FullscreenUI::GameListFavoriteChanged()
{
s_game_list_needs_sort = true;
}
// When drawing an svg to a non-integer size, we get a padded texture.
// This function crops off this padding by setting the image UV for the draw.
// We currently only use integer sizes for images, but I wrote this before checking that.
@@ -2478,13 +2483,16 @@ void FullscreenUI::PopulateGameListEntryList()
{
const int sort = Host::GetBaseIntSettingValue("UI", "FullscreenUIGameSort", 0);
const bool reverse = Host::GetBaseBoolSettingValue("UI", "FullscreenUIGameSortReverse", false);
const bool favorites_only = Host::GetBaseBoolSettingValue("UI", "FullscreenUIFavoritesOnly", false);
static int s_last_sort = -1;
static bool s_last_reverse = false;
static bool s_last_prefer_eng = false;
static bool s_last_favorites_only = false;
// Sort can be expensive, try to avoid when possible
const u32 count = GameList::GetEntryCount();
bool needs_update = sort != s_last_sort || reverse != s_last_reverse || s_last_prefer_eng != s_prefer_english_titles;
bool needs_update = s_game_list_needs_sort || sort != s_last_sort || reverse != s_last_reverse ||
s_last_prefer_eng != s_prefer_english_titles || favorites_only != s_last_favorites_only;
needs_update |= count != s_last_unsorted_entries.size();
if (!needs_update)
{
@@ -2500,10 +2508,12 @@ void FullscreenUI::PopulateGameListEntryList()
if (!needs_update)
return;
s_game_list_needs_sort = false;
s_last_sort = sort;
s_last_reverse = reverse;
s_last_prefer_eng = s_prefer_english_titles;
s_last_favorites_only = favorites_only;
s_game_list_sorted_entries.resize(count);
s_last_unsorted_entries.resize(count);
@@ -2512,9 +2522,19 @@ void FullscreenUI::PopulateGameListEntryList()
s_game_list_sorted_entries[i] = GameList::GetEntryByIndex(i);
s_last_unsorted_entries[i] = s_game_list_sorted_entries[i];
}
if (favorites_only)
{
s_game_list_sorted_entries.erase(
std::remove_if(s_game_list_sorted_entries.begin(), s_game_list_sorted_entries.end(),
[](const GameList::Entry* entry) { return !entry->is_favorite; }),
s_game_list_sorted_entries.end());
}
std::sort(s_game_list_sorted_entries.begin(), s_game_list_sorted_entries.end(),
[sort, reverse](const GameList::Entry* lhs, const GameList::Entry* rhs) {
if (lhs->is_favorite != rhs->is_favorite)
return lhs->is_favorite;
switch (sort)
{
case 0: // Type
@@ -2768,8 +2788,23 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
const std::string& title = entry->GetTitle(s_prefer_english_titles);
ImGui::PushFont(g_large_font.first, g_large_font.second);
ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, title.c_str(), title.c_str() + title.size(), nullptr,
ImVec2(0.0f, 0.0f), &title_bb);
const bool is_favorite = entry->is_favorite;
const float favorite_size = GetLineHeight(g_large_font) * 0.75f;
const float favorite_margin = LayoutScale(8.0f);
ImRect visible_title_bb(title_bb);
if (is_favorite)
visible_title_bb.Max.x -= favorite_size + (favorite_margin * 2.0f);
ImGui::RenderTextClipped(visible_title_bb.Min, visible_title_bb.Max, title.c_str(), title.c_str() + title.size(), nullptr,
ImVec2(0.0f, 0.0f), &visible_title_bb);
if (is_favorite)
{
const ImVec2 star_pos(title_bb.Max.x - favorite_size - favorite_margin,
title_bb.Min.y + (GetLineHeight(g_large_font) - favorite_size) * 0.5f);
ImGui::GetWindowDrawList()->AddText(g_large_font.first, favorite_size,
ImVec2(star_pos.x + LayoutScale(1.0f), star_pos.y + LayoutScale(1.0f)), IM_COL32(0, 0, 0, 200), ICON_PF_STAR);
ImGui::GetWindowDrawList()->AddText(g_large_font.first, favorite_size,
star_pos, IM_COL32(255, 196, 0, 255), ICON_PF_STAR);
}
ImGui::PopFont();
if (!summary.empty())
@@ -3002,6 +3037,16 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size)
bb.Max -= style.FramePadding;
DrawGameCover(entry, ImGui::GetWindowDrawList(), bb.Min, bb.Min + image_size);
if (entry->is_favorite)
{
const float star_size = std::clamp(image_width * 0.12f, LayoutScale(18.0f), LayoutScale(42.0f));
const ImVec2 star_pos(bb.Min.x + image_width - star_size - LayoutScale(6.0f), bb.Min.y + LayoutScale(6.0f));
ImGui::GetWindowDrawList()->AddText(g_medium_font.first, star_size,
ImVec2(star_pos.x + LayoutScale(1.0f), star_pos.y + LayoutScale(1.0f)),
IM_COL32(0, 0, 0, 200), ICON_PF_STAR);
ImGui::GetWindowDrawList()->AddText(g_medium_font.first, star_size, star_pos,
IM_COL32(255, 196, 0, 255), ICON_PF_STAR);
}
const bool show_titles = Host::GetBaseBoolSettingValue("UI", "FullscreenUIShowGameGridTitles", true);
@@ -3045,11 +3090,13 @@ void FullscreenUI::HandleGameListActivate(const GameList::Entry* entry)
void FullscreenUI::HandleGameListOptions(const GameList::Entry* entry)
{
const bool is_favorite = entry->is_favorite;
ImGuiFullscreen::ChoiceDialogOptions options = {
{FSUI_ICONSTR(ICON_FA_WRENCH, "Game Properties"), false},
{is_favorite ? FSUI_ICONSTR(ICON_PF_STAR, "Remove from Favorites") : FSUI_ICONSTR(ICON_PF_STAR, "Add to Favorites"), false},
{FSUI_ICONSTR(ICON_FA_PLAY, "Resume Game"), false},
{FSUI_ICONSTR(ICON_FA_ARROW_ROTATE_LEFT, "Load State"), false},
{FSUI_ICONSTR(ICON_PF_STAR, "Default Boot"), false},
{FSUI_ICONSTR(ICON_PF_START, "Default Boot"), false},
{FSUI_ICONSTR(ICON_FA_FORWARD_FAST, "Fast Boot"), false},
{FSUI_ICONSTR(ICON_FA_COMPACT_DISC, "Full Boot"), false},
};
@@ -3061,29 +3108,33 @@ void FullscreenUI::HandleGameListOptions(const GameList::Entry* entry)
const bool has_resume_state = VMManager::HasSaveStateInSlot(entry->serial.c_str(), entry->crc, -1);
OpenChoiceDialog(entry->GetTitle(s_prefer_english_titles).c_str(), false, std::move(options),
[has_resume_state, entry_path = entry->path, entry_serial = entry->serial, entry_title = entry->title, entry_played_time]
(s32 index, const std::string& title, bool checked) {
[has_resume_state, entry_path = entry->path, entry_serial = entry->serial, entry_title = entry->title, entry_played_time,
is_favorite](s32 index, const std::string& title, bool checked) {
switch (index)
{
case 0: // Open Game Properties
SwitchToGameSettings(entry_path);
break;
case 1: // Resume Game
case 1: // Add/Remove from Favorites
GameList::SaveFavoriteForPath(entry_path, !is_favorite);
s_game_list_needs_sort = true;
break;
case 2: // Resume Game
DoStartPath(entry_path, has_resume_state ? std::optional<s32>(-1) : std::optional<s32>());
break;
case 2: // Load State
case 3: // Load State
OpenLoadStateSelectorForGame(entry_path);
break;
case 3: // Default Boot
case 4: // Default Boot
DoStartPath(entry_path);
break;
case 4: // Fast Boot
case 5: // Fast Boot
DoStartPath(entry_path, std::nullopt, true);
break;
case 5: // Full Boot
case 6: // Full Boot
DoStartPath(entry_path, std::nullopt, false);
break;
case 6:
case 7:
{
// Close Menu
if (!entry_played_time)
@@ -3126,6 +3177,7 @@ void FullscreenUI::DrawGameListSettingsWindow()
if (NavButton(ICON_PF_BACKWARD, true, true))
{
s_current_main_window = MainWindowType::GameList;
s_game_list_needs_sort = true;
QueueResetFocus(FocusResetType::WindowChanged);
}
@@ -3147,6 +3199,7 @@ void FullscreenUI::DrawGameListSettingsWindow()
if (ImGui::IsWindowFocused() && WantsToCloseMenu())
{
s_current_main_window = MainWindowType::GameList;
s_game_list_needs_sort = true;
QueueResetFocus(FocusResetType::WindowChanged);
}
@@ -3265,6 +3318,9 @@ void FullscreenUI::DrawGameListSettingsWindow()
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_TAG, "Show Titles"),
FSUI_CSTR("Shows Titles for Games when in Game Grid View Mode"), "UI",
"FullscreenUIShowGameGridTitles", true);
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_PF_STAR, "Favorites Only"),
FSUI_CSTR("Only displays games marked as favorites in the list and grid."), "UI",
"FullscreenUIFavoritesOnly", false);
}
MenuHeading(FSUI_CSTR("Operations"));
@@ -3291,6 +3347,7 @@ void FullscreenUI::DrawGameListSettingsWindow()
void FullscreenUI::SwitchToGameList()
{
s_current_main_window = MainWindowType::GameList;
s_game_list_needs_sort = true;
s_game_list_view = static_cast<GameListView>(Host::GetBaseIntSettingValue("UI", "DefaultFullscreenUIGameView", 0));
{
auto lock = Host::GetSettingsLock();
+1
View File
@@ -40,6 +40,7 @@ namespace FullscreenUI
void LocaleChanged();
void GamepadLayoutChanged();
void PreferEnglishGameListChanged();
void GameListFavoriteChanged();
void Shutdown(bool clear_state);
void Render();
+1
View File
@@ -368,6 +368,7 @@ namespace FullscreenUI
inline std::unordered_map<std::string, std::string> s_cover_image_map;
inline std::vector<const GameList::Entry*> s_game_list_sorted_entries;
inline GameListView s_game_list_view = GameListView::Grid;
inline bool s_game_list_needs_sort = true;
// Cached list of unsorted game list entries; used to detect changes and re-sort when needed
inline std::vector<const GameList::Entry*> s_last_unsorted_entries;
+4
View File
@@ -173,6 +173,10 @@ void Host::CancelGameListRefresh()
{
}
void Host::OnGameListFavoriteChanged(const std::string& path)
{
}
bool Host::IsFullscreen()
{
return false;