Favorites in the game list (#2649) (#3071)
Some checks failed
Build and Release / reuse (push) Has been cancelled
Build and Release / clang-format (push) Has been cancelled
Build and Release / get-info (push) Has been cancelled
Build and Release / windows-sdl (push) Has been cancelled
Build and Release / windows-qt (push) Has been cancelled
Build and Release / macos-sdl (push) Has been cancelled
Build and Release / macos-qt (push) Has been cancelled
Build and Release / linux-sdl (push) Has been cancelled
Build and Release / linux-qt (push) Has been cancelled
Build and Release / linux-sdl-gcc (push) Has been cancelled
Build and Release / linux-qt-gcc (push) Has been cancelled
Build and Release / pre-release (push) Has been cancelled

* Favorites in the game list (#2649)

Changed how favorites are saved to match PR #2984. Adjusted the favorite
icon size. Fixed bug where favorites were inconsistent when changing to
list mode. Instantly sort list when adding or removing a favorite.

Co-authored-by: David Antunes <david.f.antunes@tecnico.ulisboa.pt>

* fix formatting

* Favorites in the game list (#2649)

Fixed issue where background change was inconsistent while adding
favorites, unselect row when adding favorites, cleaned code, changed
right click menu options to match the game's favorite status.

* fixed right click bug

* keep row selection when adding favorites

* fixed sorting on game grid after using search bar

* change the way favorites are saved to match #3119
This commit is contained in:
davidantunes23 2025-06-22 10:53:47 +01:00 committed by GitHub
parent 669b19c2f3
commit 4bfa8c9fc7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 203 additions and 31 deletions

View file

@ -7,8 +7,8 @@ path = [
"CMakeSettings.json", "CMakeSettings.json",
".github/FUNDING.yml", ".github/FUNDING.yml",
".github/shadps4.png", ".github/shadps4.png",
".github/workflows/scripts/update_translation.sh", ".github/workflows/scripts/update_translation.sh",
".github/workflows/update_translation.yml", ".github/workflows/update_translation.yml",
".gitmodules", ".gitmodules",
"dist/MacOSBundleInfo.plist.in", "dist/MacOSBundleInfo.plist.in",
"dist/net.shadps4.shadPS4.desktop", "dist/net.shadps4.shadPS4.desktop",
@ -29,6 +29,7 @@ path = [
"src/images/discord.png", "src/images/discord.png",
"src/images/dump_icon.png", "src/images/dump_icon.png",
"src/images/exit_icon.png", "src/images/exit_icon.png",
"src/images/favorite_icon.png",
"src/images/file_icon.png", "src/images/file_icon.png",
"src/images/trophy_icon.png", "src/images/trophy_icon.png",
"src/images/flag_china.png", "src/images/flag_china.png",
@ -71,7 +72,7 @@ path = [
"src/images/youtube.svg", "src/images/youtube.svg",
"src/shadps4.qrc", "src/shadps4.qrc",
"src/shadps4.rc", "src/shadps4.rc",
"src/qt_gui/translations/update_translation.sh", "src/qt_gui/translations/update_translation.sh",
] ]
precedence = "aggregate" precedence = "aggregate"
SPDX-FileCopyrightText = "shadPS4 Emulator Project" SPDX-FileCopyrightText = "shadPS4 Emulator Project"

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -36,6 +36,7 @@ GameGridFrame::GameGridFrame(std::shared_ptr<gui_settings> gui_settings,
connect(this, &QTableWidget::customContextMenuRequested, this, [=, this](const QPoint& pos) { connect(this, &QTableWidget::customContextMenuRequested, this, [=, this](const QPoint& pos) {
m_gui_context_menus.RequestGameMenu(pos, m_game_info->m_games, m_compat_info, m_gui_context_menus.RequestGameMenu(pos, m_game_info->m_games, m_compat_info,
m_gui_settings, this, false); m_gui_settings, this, false);
PopulateGameGrid(m_game_info->m_games, false);
}); });
} }
@ -89,10 +90,13 @@ void GameGridFrame::PopulateGameGrid(QVector<GameInfo> m_games_search, bool from
this->crtColumn = -1; this->crtColumn = -1;
QVector<GameInfo> m_games_; QVector<GameInfo> m_games_;
this->clearContents(); this->clearContents();
if (fromSearch) if (fromSearch) {
SortByFavorite(&m_games_search);
m_games_ = m_games_search; m_games_ = m_games_search;
else } else {
SortByFavorite(&(m_game_info->m_games));
m_games_ = m_game_info->m_games; m_games_ = m_game_info->m_games;
}
m_games_shared = std::make_shared<QVector<GameInfo>>(m_games_); m_games_shared = std::make_shared<QVector<GameInfo>>(m_games_);
icon_size = icon_size =
m_gui_settings->GetValue(gui::gg_icon_size).toInt(); // update icon size for resize event. m_gui_settings->GetValue(gui::gg_icon_size).toInt(); // update icon size for resize event.
@ -111,14 +115,21 @@ void GameGridFrame::PopulateGameGrid(QVector<GameInfo> m_games_search, bool from
for (int i = 0; i < m_games_.size(); i++) { for (int i = 0; i < m_games_.size(); i++) {
QWidget* widget = new QWidget(); QWidget* widget = new QWidget();
QVBoxLayout* layout = new QVBoxLayout(); QVBoxLayout* layout = new QVBoxLayout();
QLabel* image_label = new QLabel();
QWidget* image_container = new QWidget();
image_container->setFixedSize(icon_size, icon_size);
QLabel* image_label = new QLabel(image_container);
QImage icon = m_games_[gameCounter].icon.scaled( QImage icon = m_games_[gameCounter].icon.scaled(
QSize(icon_size, icon_size), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); QSize(icon_size, icon_size), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
image_label->setFixedSize(icon.width(), icon.height()); image_label->setFixedSize(icon.width(), icon.height());
image_label->setPixmap(QPixmap::fromImage(icon)); image_label->setPixmap(QPixmap::fromImage(icon));
image_label->move(0, 0);
SetFavoriteIcon(image_container, m_games_, gameCounter);
QLabel* name_label = new QLabel(QString::fromStdString(m_games_[gameCounter].serial)); QLabel* name_label = new QLabel(QString::fromStdString(m_games_[gameCounter].serial));
name_label->setAlignment(Qt::AlignHCenter); name_label->setAlignment(Qt::AlignHCenter);
layout->addWidget(image_label); layout->addWidget(image_container);
layout->addWidget(name_label); layout->addWidget(name_label);
// Resizing of font-size. // Resizing of font-size.
@ -226,3 +237,43 @@ void GameGridFrame::resizeEvent(QResizeEvent* event) {
bool GameGridFrame::IsValidCellSelected() { bool GameGridFrame::IsValidCellSelected() {
return validCellSelected; return validCellSelected;
} }
void GameGridFrame::SetFavoriteIcon(QWidget* parentWidget, QVector<GameInfo> m_games_,
int gameCounter) {
QString serialStr = QString::fromStdString(m_games_[gameCounter].serial);
QList<QString> list = gui_settings::Var2List(m_gui_settings->GetValue(gui::favorites_list));
bool isFavorite = list.contains(serialStr);
QLabel* label = new QLabel(parentWidget);
label->setPixmap(QPixmap(":images/favorite_icon.png")
.scaled(icon_size / 3.8, icon_size / 3.8, Qt::KeepAspectRatio,
Qt::SmoothTransformation));
label->move(icon_size - icon_size / 4, 2);
label->raise();
label->setVisible(isFavorite);
label->setObjectName("favoriteIcon");
}
void GameGridFrame::SortByFavorite(QVector<GameInfo>* game_list) {
std::sort(game_list->begin(), game_list->end(), [this](const GameInfo& a, const GameInfo& b) {
return this->CompareWithFavorite(a, b);
});
}
bool GameGridFrame::CompareWithFavorite(GameInfo a, GameInfo b) {
std::string serial_a = a.serial;
std::string serial_b = b.serial;
QString serialStr_a = QString::fromStdString(a.serial);
QString serialStr_b = QString::fromStdString(b.serial);
QList<QString> list = gui_settings::Var2List(m_gui_settings->GetValue(gui::favorites_list));
bool isFavorite_a = list.contains(serialStr_a);
bool isFavorite_b = list.contains(serialStr_b);
if (isFavorite_a != isFavorite_b) {
return isFavorite_a;
} else {
std::string name_a = a.name, name_b = b.name;
std::transform(name_a.begin(), name_a.end(), name_a.begin(), ::tolower);
std::transform(name_b.begin(), name_b.end(), name_b.begin(), ::tolower);
return name_a < name_b;
}
}

View file

@ -39,6 +39,8 @@ private:
int m_last_opacity = -1; // Track last opacity to avoid unnecessary recomputation int m_last_opacity = -1; // Track last opacity to avoid unnecessary recomputation
std::filesystem::path m_current_game_path; // Track current game path to detect changes std::filesystem::path m_current_game_path; // Track current game path to detect changes
std::shared_ptr<gui_settings> m_gui_settings; std::shared_ptr<gui_settings> m_gui_settings;
void SetFavoriteIcon(QWidget* parentWidget, QVector<GameInfo> m_games_, int gameCounter);
bool CompareWithFavorite(GameInfo a, GameInfo b);
public: public:
explicit GameGridFrame(std::shared_ptr<gui_settings> gui_settings, explicit GameGridFrame(std::shared_ptr<gui_settings> gui_settings,
@ -47,6 +49,7 @@ public:
QWidget* parent = nullptr); QWidget* parent = nullptr);
void PopulateGameGrid(QVector<GameInfo> m_games, bool fromSearch); void PopulateGameGrid(QVector<GameInfo> m_games, bool fromSearch);
bool IsValidCellSelected(); bool IsValidCellSelected();
void SortByFavorite(QVector<GameInfo>* game_list);
bool cellClicked = false; bool cellClicked = false;
int icon_size; int icon_size;

View file

@ -16,6 +16,7 @@ GameListFrame::GameListFrame(std::shared_ptr<gui_settings> gui_settings,
: QTableWidget(parent), m_gui_settings(std::move(gui_settings)), m_game_info(game_info_get), : QTableWidget(parent), m_gui_settings(std::move(gui_settings)), m_game_info(game_info_get),
m_compat_info(compat_info_get) { m_compat_info(compat_info_get) {
icon_size = m_gui_settings->GetValue(gui::gl_icon_size).toInt(); icon_size = m_gui_settings->GetValue(gui::gl_icon_size).toInt();
last_favorite = "";
this->setShowGrid(false); this->setShowGrid(false);
this->setEditTriggers(QAbstractItemView::NoEditTriggers); this->setEditTriggers(QAbstractItemView::NoEditTriggers);
this->setSelectionBehavior(QAbstractItemView::SelectRows); this->setSelectionBehavior(QAbstractItemView::SelectRows);
@ -30,9 +31,8 @@ GameListFrame::GameListFrame(std::shared_ptr<gui_settings> gui_settings,
this->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); this->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
this->horizontalHeader()->setHighlightSections(false); this->horizontalHeader()->setHighlightSections(false);
this->horizontalHeader()->setSortIndicatorShown(true); this->horizontalHeader()->setSortIndicatorShown(true);
this->horizontalHeader()->setStretchLastSection(true);
this->setContextMenuPolicy(Qt::CustomContextMenu); this->setContextMenuPolicy(Qt::CustomContextMenu);
this->setColumnCount(10); this->setColumnCount(11);
this->setColumnWidth(1, 300); // Name this->setColumnWidth(1, 300); // Name
this->setColumnWidth(2, 140); // Compatibility this->setColumnWidth(2, 140); // Compatibility
this->setColumnWidth(3, 120); // Serial this->setColumnWidth(3, 120); // Serial
@ -41,14 +41,18 @@ GameListFrame::GameListFrame(std::shared_ptr<gui_settings> gui_settings,
this->setColumnWidth(6, 90); // Size this->setColumnWidth(6, 90); // Size
this->setColumnWidth(7, 90); // Version this->setColumnWidth(7, 90); // Version
this->setColumnWidth(8, 120); // Play Time this->setColumnWidth(8, 120); // Play Time
this->setColumnWidth(10, 90); // Favorite
QStringList headers; QStringList headers;
headers << tr("Icon") << tr("Name") << tr("Compatibility") << tr("Serial") << tr("Region") headers << tr("Icon") << tr("Name") << tr("Compatibility") << tr("Serial") << tr("Region")
<< tr("Firmware") << tr("Size") << tr("Version") << tr("Play Time") << tr("Path"); << tr("Firmware") << tr("Size") << tr("Version") << tr("Play Time") << tr("Path")
<< tr("Favorite");
this->setHorizontalHeaderLabels(headers); this->setHorizontalHeaderLabels(headers);
this->horizontalHeader()->setSortIndicatorShown(true); this->horizontalHeader()->setSortIndicatorShown(true);
this->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); this->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
this->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Fixed); this->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Fixed);
this->horizontalHeader()->setSectionResizeMode(4, QHeaderView::Fixed); this->horizontalHeader()->setSectionResizeMode(4, QHeaderView::Fixed);
this->horizontalHeader()->setSectionResizeMode(9, QHeaderView::Stretch);
this->horizontalHeader()->setSectionResizeMode(10, QHeaderView::Fixed);
PopulateGameList(); PopulateGameList();
connect(this, &QTableWidget::currentCellChanged, this, &GameListFrame::onCurrentCellChanged); connect(this, &QTableWidget::currentCellChanged, this, &GameListFrame::onCurrentCellChanged);
@ -65,18 +69,24 @@ GameListFrame::GameListFrame(std::shared_ptr<gui_settings> gui_settings,
SortNameDescending(columnIndex); SortNameDescending(columnIndex);
this->horizontalHeader()->setSortIndicator(columnIndex, Qt::DescendingOrder); this->horizontalHeader()->setSortIndicator(columnIndex, Qt::DescendingOrder);
ListSortedAsc = false; ListSortedAsc = false;
sortColumn = columnIndex;
} else { } else {
SortNameAscending(columnIndex); SortNameAscending(columnIndex);
this->horizontalHeader()->setSortIndicator(columnIndex, Qt::AscendingOrder); this->horizontalHeader()->setSortIndicator(columnIndex, Qt::AscendingOrder);
ListSortedAsc = true; ListSortedAsc = true;
sortColumn = columnIndex;
} }
this->clearContents(); this->clearContents();
PopulateGameList(false); PopulateGameList(false);
}); });
connect(this, &QTableWidget::customContextMenuRequested, this, [=, this](const QPoint& pos) { connect(this, &QTableWidget::customContextMenuRequested, this, [=, this](const QPoint& pos) {
m_gui_context_menus.RequestGameMenu(pos, m_game_info->m_games, m_compat_info, int changedFavorite = m_gui_context_menus.RequestGameMenu(
m_gui_settings, this, true); pos, m_game_info->m_games, m_compat_info, m_gui_settings, this, true);
if (changedFavorite) {
last_favorite = m_game_info->m_games[this->currentRow()].serial;
PopulateGameList(false);
}
}); });
connect(this, &QTableWidget::cellClicked, this, [=, this](int row, int column) { connect(this, &QTableWidget::cellClicked, this, [=, this](int row, int column) {
@ -84,6 +94,19 @@ GameListFrame::GameListFrame(std::shared_ptr<gui_settings> gui_settings,
auto url_issues = "https://github.com/shadps4-emu/shadps4-game-compatibility/issues/"; auto url_issues = "https://github.com/shadps4-emu/shadps4-game-compatibility/issues/";
QDesktopServices::openUrl( QDesktopServices::openUrl(
QUrl(url_issues + m_game_info->m_games[row].compatibility.issue_number)); QUrl(url_issues + m_game_info->m_games[row].compatibility.issue_number));
} else if (column == 10) {
last_favorite = m_game_info->m_games[row].serial;
QString serialStr = QString::fromStdString(last_favorite);
QList<QString> list =
gui_settings::Var2List(m_gui_settings->GetValue(gui::favorites_list));
bool isFavorite = list.contains(serialStr);
if (isFavorite) {
list.removeOne(serialStr);
} else {
list.append(serialStr);
}
m_gui_settings->SetValue(gui::favorites_list, gui_settings::List2Var(list));
PopulateGameList(false);
} }
}); });
} }
@ -118,10 +141,7 @@ void GameListFrame::PopulateGameList(bool isInitialPopulation) {
this->setRowCount(m_game_info->m_games.size()); this->setRowCount(m_game_info->m_games.size());
ResizeIcons(icon_size); ResizeIcons(icon_size);
if (isInitialPopulation) { ApplyLastSorting(isInitialPopulation);
SortNameAscending(1); // Column 1 = Name
ResizeIcons(icon_size);
}
for (int i = 0; i < m_game_info->m_games.size(); i++) { for (int i = 0; i < m_game_info->m_games.size(); i++) {
SetTableItem(i, 1, QString::fromStdString(m_game_info->m_games[i].name)); SetTableItem(i, 1, QString::fromStdString(m_game_info->m_games[i].name));
@ -130,6 +150,11 @@ void GameListFrame::PopulateGameList(bool isInitialPopulation) {
SetTableItem(i, 5, QString::fromStdString(m_game_info->m_games[i].fw)); SetTableItem(i, 5, QString::fromStdString(m_game_info->m_games[i].fw));
SetTableItem(i, 6, QString::fromStdString(m_game_info->m_games[i].size)); SetTableItem(i, 6, QString::fromStdString(m_game_info->m_games[i].size));
SetTableItem(i, 7, QString::fromStdString(m_game_info->m_games[i].version)); SetTableItem(i, 7, QString::fromStdString(m_game_info->m_games[i].version));
SetFavoriteIcon(i, 10);
if (m_game_info->m_games[i].serial == last_favorite && !isInitialPopulation) {
this->setCurrentCell(i, 10);
}
m_game_info->m_games[i].compatibility = m_game_info->m_games[i].compatibility =
m_compat_info->GetCompatibilityInfo(m_game_info->m_games[i].serial); m_compat_info->GetCompatibilityInfo(m_game_info->m_games[i].serial);
@ -227,20 +252,50 @@ void GameListFrame::resizeEvent(QResizeEvent* event) {
RefreshListBackgroundImage(); RefreshListBackgroundImage();
} }
bool GameListFrame::CompareWithFavorite(GameInfo a, GameInfo b, int columnIndex, bool ascending) {
std::string serial_a = a.serial;
std::string serial_b = b.serial;
QString serialStr_a = QString::fromStdString(a.serial);
QString serialStr_b = QString::fromStdString(b.serial);
QList<QString> list = gui_settings::Var2List(m_gui_settings->GetValue(gui::favorites_list));
bool isFavorite_a = list.contains(serialStr_a);
bool isFavorite_b = list.contains(serialStr_b);
if (isFavorite_a != isFavorite_b) {
return isFavorite_a;
} else if (ascending) {
return CompareStringsAscending(a, b, columnIndex);
} else {
return CompareStringsDescending(a, b, columnIndex);
}
}
void GameListFrame::SortNameAscending(int columnIndex) { void GameListFrame::SortNameAscending(int columnIndex) {
std::sort(m_game_info->m_games.begin(), m_game_info->m_games.end(), std::sort(m_game_info->m_games.begin(), m_game_info->m_games.end(),
[columnIndex](const GameInfo& a, const GameInfo& b) { [this, columnIndex](const GameInfo& a, const GameInfo& b) {
return CompareStringsAscending(a, b, columnIndex); return this->CompareWithFavorite(a, b, columnIndex, true);
}); });
} }
void GameListFrame::SortNameDescending(int columnIndex) { void GameListFrame::SortNameDescending(int columnIndex) {
std::sort(m_game_info->m_games.begin(), m_game_info->m_games.end(), std::sort(m_game_info->m_games.begin(), m_game_info->m_games.end(),
[columnIndex](const GameInfo& a, const GameInfo& b) { [this, columnIndex](const GameInfo& a, const GameInfo& b) {
return CompareStringsDescending(a, b, columnIndex); return this->CompareWithFavorite(a, b, columnIndex, false);
}); });
} }
void GameListFrame::ApplyLastSorting(bool isInitialPopulation) {
if (isInitialPopulation) {
SortNameAscending(1); // Column 1 = Name
ResizeIcons(icon_size);
} else if (ListSortedAsc) {
SortNameAscending(sortColumn);
ResizeIcons(icon_size);
} else {
SortNameDescending(sortColumn);
ResizeIcons(icon_size);
}
}
void GameListFrame::ResizeIcons(int iconSize) { void GameListFrame::ResizeIcons(int iconSize) {
for (int index = 0; auto& game : m_game_info->m_games) { for (int index = 0; auto& game : m_game_info->m_games) {
QImage scaledPixmap = game.icon.scaled(QSize(iconSize, iconSize), Qt::KeepAspectRatio, QImage scaledPixmap = game.icon.scaled(QSize(iconSize, iconSize), Qt::KeepAspectRatio,
@ -391,6 +446,35 @@ void GameListFrame::SetRegionFlag(int row, int column, QString itemStr) {
this->setCellWidget(row, column, widget); this->setCellWidget(row, column, widget);
} }
void GameListFrame::SetFavoriteIcon(int row, int column) {
QString serialStr = QString::fromStdString(m_game_info->m_games[row].serial);
QList<QString> list = gui_settings::Var2List(m_gui_settings->GetValue(gui::favorites_list));
bool isFavorite = list.contains(serialStr);
QTableWidgetItem* item = new QTableWidgetItem();
QImage scaledPixmap = QImage(":images/favorite_icon.png");
scaledPixmap = scaledPixmap.scaledToHeight(this->columnWidth(column) / 2.5);
scaledPixmap = scaledPixmap.scaledToWidth(this->columnWidth(column) / 2.5);
QWidget* widget = new QWidget(this);
QVBoxLayout* layout = new QVBoxLayout(widget);
QLabel* label = new QLabel(widget);
label->setPixmap(QPixmap::fromImage(scaledPixmap));
label->setObjectName("favoriteIcon");
label->setVisible(isFavorite);
layout->setAlignment(Qt::AlignCenter);
layout->addWidget(label);
widget->setLayout(layout);
this->setItem(row, column, item);
this->setCellWidget(row, column, widget);
if (column > 0) {
this->horizontalHeader()->setSectionResizeMode(column - 1, QHeaderView::Stretch);
}
}
QString GameListFrame::GetPlayTime(const std::string& serial) { QString GameListFrame::GetPlayTime(const std::string& serial) {
QString playTime; QString playTime;
const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
@ -423,4 +507,4 @@ QString GameListFrame::GetPlayTime(const std::string& serial) {
QTableWidgetItem* GameListFrame::GetCurrentItem() { QTableWidgetItem* GameListFrame::GetCurrentItem() {
return m_current_item; return m_current_item;
} }

View file

@ -42,11 +42,13 @@ public Q_SLOTS:
private: private:
void SetTableItem(int row, int column, QString itemStr); void SetTableItem(int row, int column, QString itemStr);
void SetRegionFlag(int row, int column, QString itemStr); void SetRegionFlag(int row, int column, QString itemStr);
void SetFavoriteIcon(int row, int column);
void SetCompatibilityItem(int row, int column, CompatibilityEntry entry); void SetCompatibilityItem(int row, int column, CompatibilityEntry entry);
QString GetPlayTime(const std::string& serial); QString GetPlayTime(const std::string& serial);
QList<QAction*> m_columnActs; QList<QAction*> m_columnActs;
GameInfoClass* game_inf_get = nullptr; GameInfoClass* game_inf_get = nullptr;
bool ListSortedAsc = true; bool ListSortedAsc = true;
int sortColumn = 1;
QTableWidgetItem* m_current_item = nullptr; QTableWidgetItem* m_current_item = nullptr;
int m_last_opacity = -1; // Track last opacity to avoid unnecessary recomputation int m_last_opacity = -1; // Track last opacity to avoid unnecessary recomputation
std::filesystem::path m_current_game_path; // Track current game path to detect changes std::filesystem::path m_current_game_path; // Track current game path to detect changes
@ -55,6 +57,7 @@ private:
public: public:
void PopulateGameList(bool isInitialPopulation = true); void PopulateGameList(bool isInitialPopulation = true);
void ResizeIcons(int iconSize); void ResizeIcons(int iconSize);
void ApplyLastSorting(bool isInitialPopulation);
QTableWidgetItem* GetCurrentItem(); QTableWidgetItem* GetCurrentItem();
QImage backgroundImage; QImage backgroundImage;
GameListUtils m_game_list_utils; GameListUtils m_game_list_utils;
@ -63,6 +66,7 @@ public:
std::shared_ptr<CompatibilityInfoClass> m_compat_info; std::shared_ptr<CompatibilityInfoClass> m_compat_info;
int icon_size; int icon_size;
std::string last_favorite;
static float parseAsFloat(const std::string& str, const int& offset) { static float parseAsFloat(const std::string& str, const int& offset) {
return std::stof(str.substr(0, str.size() - offset)); return std::stof(str.substr(0, str.size() - offset));
@ -130,4 +134,6 @@ public:
return false; return false;
} }
} }
bool CompareWithFavorite(GameInfo a, GameInfo b, int columnIndex, bool ascending);
}; };

View file

@ -16,6 +16,7 @@
#include "common/scm_rev.h" #include "common/scm_rev.h"
#include "compatibility_info.h" #include "compatibility_info.h"
#include "game_info.h" #include "game_info.h"
#include "gui_settings.h"
#include "trophy_viewer.h" #include "trophy_viewer.h"
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
@ -30,13 +31,13 @@
class GuiContextMenus : public QObject { class GuiContextMenus : public QObject {
Q_OBJECT Q_OBJECT
public: public:
void RequestGameMenu(const QPoint& pos, QVector<GameInfo>& m_games, int RequestGameMenu(const QPoint& pos, QVector<GameInfo>& m_games,
std::shared_ptr<CompatibilityInfoClass> m_compat_info, std::shared_ptr<CompatibilityInfoClass> m_compat_info,
std::shared_ptr<gui_settings> settings, QTableWidget* widget, std::shared_ptr<gui_settings> settings, QTableWidget* widget, bool isList) {
bool isList) {
QPoint global_pos = widget->viewport()->mapToGlobal(pos); QPoint global_pos = widget->viewport()->mapToGlobal(pos);
std::shared_ptr<gui_settings> m_gui_settings = std::move(settings); std::shared_ptr<gui_settings> m_gui_settings = std::move(settings);
int itemID = 0; int itemID = 0;
int changedFavorite = 0;
if (isList) { if (isList) {
itemID = widget->currentRow(); itemID = widget->currentRow();
} else { } else {
@ -45,7 +46,7 @@ public:
// Do not show the menu if no item is selected // Do not show the menu if no item is selected
if (itemID < 0 || itemID >= m_games.size()) { if (itemID < 0 || itemID >= m_games.size()) {
return; return changedFavorite;
} }
// Setup menu. // Setup menu.
@ -65,11 +66,22 @@ public:
menu.addMenu(openFolderMenu); menu.addMenu(openFolderMenu);
QString serialStr = QString::fromStdString(m_games[itemID].serial);
QList<QString> list = gui_settings::Var2List(m_gui_settings->GetValue(gui::favorites_list));
bool isFavorite = list.contains(serialStr);
QAction* toggleFavorite;
if (isFavorite) {
toggleFavorite = new QAction(tr("Remove from Favorites"), widget);
} else {
toggleFavorite = new QAction(tr("Add to Favorites"), widget);
}
QAction createShortcut(tr("Create Shortcut"), widget); QAction createShortcut(tr("Create Shortcut"), widget);
QAction openCheats(tr("Cheats / Patches"), widget); QAction openCheats(tr("Cheats / Patches"), widget);
QAction openSfoViewer(tr("SFO Viewer"), widget); QAction openSfoViewer(tr("SFO Viewer"), widget);
QAction openTrophyViewer(tr("Trophy Viewer"), widget); QAction openTrophyViewer(tr("Trophy Viewer"), widget);
menu.addAction(toggleFavorite);
menu.addAction(&createShortcut); menu.addAction(&createShortcut);
menu.addAction(&openCheats); menu.addAction(&openCheats);
menu.addAction(&openSfoViewer); menu.addAction(&openSfoViewer);
@ -130,7 +142,7 @@ public:
// Show menu. // Show menu.
auto selected = menu.exec(global_pos); auto selected = menu.exec(global_pos);
if (!selected) { if (!selected) {
return; return changedFavorite;
} }
if (selected == openGameFolder) { if (selected == openGameFolder) {
@ -303,6 +315,16 @@ public:
} }
} }
if (selected == toggleFavorite) {
if (isFavorite) {
list.removeOne(serialStr);
} else {
list.append(serialStr);
}
m_gui_settings->SetValue(gui::favorites_list, gui_settings::List2Var(list));
changedFavorite = 1;
}
if (selected == &openCheats) { if (selected == &openCheats) {
QString gameName = QString::fromStdString(m_games[itemID].name); QString gameName = QString::fromStdString(m_games[itemID].name);
QString gameSerial = QString::fromStdString(m_games[itemID].serial); QString gameSerial = QString::fromStdString(m_games[itemID].serial);
@ -588,6 +610,7 @@ public:
QUrl(url_issues + m_games[itemID].compatibility.issue_number)); QUrl(url_issues + m_games[itemID].compatibility.issue_number));
} }
} }
return changedFavorite;
} }
int GetRowIndex(QTreeWidget* treeWidget, QTreeWidgetItem* item) { int GetRowIndex(QTreeWidget* treeWidget, QTreeWidgetItem* item) {

View file

@ -12,6 +12,7 @@ const QString general_settings = "general_settings";
const QString main_window = "main_window"; const QString main_window = "main_window";
const QString game_list = "game_list"; const QString game_list = "game_list";
const QString game_grid = "game_grid"; const QString game_grid = "game_grid";
const QString favorites = "favorites";
// general // general
const gui_value gen_checkForUpdates = gui_value(general_settings, "checkForUpdates", false); const gui_value gen_checkForUpdates = gui_value(general_settings, "checkForUpdates", false);
@ -41,6 +42,10 @@ const gui_value gl_backgroundMusicVolume = gui_value(game_list, "backgroundMusic
const gui_value gg_icon_size = gui_value(game_grid, "icon_size", 69); const gui_value gg_icon_size = gui_value(game_grid, "icon_size", 69);
const gui_value gg_slider_pos = gui_value(game_grid, "slider_pos", 0); const gui_value gg_slider_pos = gui_value(game_grid, "slider_pos", 0);
// favorites list
const gui_value favorites_list =
gui_value(favorites, "favoritesList", QVariant::fromValue(QList<QString>()));
} // namespace gui } // namespace gui
class gui_settings : public settings { class gui_settings : public settings {

View file

@ -561,10 +561,8 @@ void MainWindow::CreateConnects() {
m_game_grid_frame->hide(); m_game_grid_frame->hide();
m_elf_viewer->hide(); m_elf_viewer->hide();
m_game_list_frame->show(); m_game_list_frame->show();
if (m_game_list_frame->item(0, 0) == nullptr) { m_game_list_frame->clearContents();
m_game_list_frame->clearContents(); m_game_list_frame->PopulateGameList();
m_game_list_frame->PopulateGameList();
}
isTableList = true; isTableList = true;
m_gui_settings->SetValue(gui::gl_mode, 0); m_gui_settings->SetValue(gui::gl_mode, 0);
int slider_pos = m_gui_settings->GetValue(gui::gl_slider_pos).toInt(); int slider_pos = m_gui_settings->GetValue(gui::gl_slider_pos).toInt();

View file

@ -36,6 +36,7 @@
<file>images/KBM.png</file> <file>images/KBM.png</file>
<file>images/fullscreen_icon.png</file> <file>images/fullscreen_icon.png</file>
<file>images/refreshlist_icon.png</file> <file>images/refreshlist_icon.png</file>
<file>images/favorite_icon.png</file>
<file>images/trophy_icon.png</file> <file>images/trophy_icon.png</file>
</qresource> </qresource>
</RCC> </RCC>