Custom Trophy images / sound | and improvements (#2539)

* Custom Trophy images

* text and button - settings

* Description

* +

* plural

* translation for 'Trophy earned!'

* Revert: translation for 'Trophy earned!'

* play audio

* fixes crash due to having too many trophies

The game 'My Name is Mayo' has so many trophies in sequence that when overlapping them, the emulator ended up crashing, so if there is something on the screen and a new trophies are achieved, it will clear and show the new one.

* Animations, config: position, duration

* -

* TR

* fix sdl/qt

* clang \O/

* Side menu with filter options. Sorting

* +TR

* fix showHiddenCheck

* Time Unlocked

* Fixes ghost text, larger image, black text in light theme

* Button - Delete Trophy

* limits the width of Description - showMaximized

* changing column positions

* useEuropeanDateFormat

en_US, zh_CN, zh_TW, ja_JP, ko_KR, lt_LT, nb_NO, nl_NL
useEuropeanDateFormat = false
This commit is contained in:
DanielSvoboda 2025-02-28 03:31:42 -03:00 committed by GitHub
parent 63b50ff92c
commit 5e5ca2138e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 620 additions and 104 deletions

View file

@ -7,7 +7,6 @@ BackgroundMusicPlayer::BackgroundMusicPlayer(QObject* parent) : QObject(parent)
m_mediaPlayer = new QMediaPlayer(this);
m_audioOutput = new QAudioOutput(this);
m_mediaPlayer->setAudioOutput(m_audioOutput);
m_mediaPlayer->setLoops(QMediaPlayer::Infinite);
}
void BackgroundMusicPlayer::setVolume(int volume) {
@ -16,7 +15,7 @@ void BackgroundMusicPlayer::setVolume(int volume) {
m_audioOutput->setVolume(linearVolume);
}
void BackgroundMusicPlayer::playMusic(const QString& snd0path) {
void BackgroundMusicPlayer::playMusic(const QString& snd0path, bool loops) {
if (snd0path.isEmpty()) {
stopMusic();
return;
@ -28,6 +27,12 @@ void BackgroundMusicPlayer::playMusic(const QString& snd0path) {
return;
}
if (loops) {
m_mediaPlayer->setLoops(QMediaPlayer::Infinite);
} else {
m_mediaPlayer->setLoops(1);
}
m_currentMusic = newMusic;
m_mediaPlayer->setSource(newMusic);
m_mediaPlayer->play();

View file

@ -17,7 +17,7 @@ public:
}
void setVolume(int volume);
void playMusic(const QString& snd0path);
void playMusic(const QString& snd0path, bool loops = true);
void stopMusic();
private:

View file

@ -97,11 +97,13 @@ public:
QAction* deleteUpdate = new QAction(tr("Delete Update"), widget);
QAction* deleteSaveData = new QAction(tr("Delete Save Data"), widget);
QAction* deleteDLC = new QAction(tr("Delete DLC"), widget);
QAction* deleteTrophy = new QAction(tr("Delete Trophy"), widget);
deleteMenu->addAction(deleteGame);
deleteMenu->addAction(deleteUpdate);
deleteMenu->addAction(deleteSaveData);
deleteMenu->addAction(deleteDLC);
deleteMenu->addAction(deleteTrophy);
menu.addMenu(deleteMenu);
@ -380,9 +382,9 @@ public:
}
if (selected == deleteGame || selected == deleteUpdate || selected == deleteDLC ||
selected == deleteSaveData) {
selected == deleteSaveData || selected == deleteTrophy) {
bool error = false;
QString folder_path, game_update_path, dlc_path, save_data_path;
QString folder_path, game_update_path, dlc_path, save_data_path, trophy_data_path;
Common::FS::PathToQString(folder_path, m_games[itemID].path);
game_update_path = folder_path + "-UPDATE";
Common::FS::PathToQString(
@ -391,6 +393,11 @@ public:
Common::FS::PathToQString(save_data_path,
Common::FS::GetUserPath(Common::FS::PathType::UserDir) /
"savedata/1" / m_games[itemID].serial);
Common::FS::PathToQString(trophy_data_path,
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) /
m_games[itemID].serial / "TrophyFiles");
QString message_type = tr("Game");
if (selected == deleteUpdate) {
@ -420,6 +427,16 @@ public:
folder_path = save_data_path;
message_type = tr("Save Data");
}
} else if (selected == deleteTrophy) {
if (!std::filesystem::exists(Common::FS::PathFromQString(trophy_data_path))) {
QMessageBox::critical(
nullptr, tr("Error"),
QString(tr("This game has no saved trophies to delete!")));
error = true;
} else {
folder_path = trophy_data_path;
message_type = tr("Trophy");
}
}
if (!error) {
QString gameName = QString::fromStdString(m_games[itemID].name);

View file

@ -225,6 +225,17 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices,
Config::setShowBackgroundImage(state == Qt::Checked);
});
}
// User TAB
{
connect(ui->OpenCustomTrophyLocationButton, &QPushButton::clicked, this, []() {
QString userPath;
Common::FS::PathToQString(userPath,
Common::FS::GetUserPath(Common::FS::PathType::CustomTrophy));
QDesktopServices::openUrl(QUrl::fromLocalFile(userPath));
});
}
// Input TAB
{
connect(ui->hideCursorComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
@ -280,8 +291,8 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices,
connect(ui->OpenLogLocationButton, &QPushButton::clicked, this, []() {
QString userPath;
Common::FS::PathToQString(userPath,
Common::FS::GetUserPath(Common::FS::PathType::UserDir));
QDesktopServices::openUrl(QUrl::fromLocalFile(userPath + "/log"));
Common::FS::GetUserPath(Common::FS::PathType::LogDir));
QDesktopServices::openUrl(QUrl::fromLocalFile(userPath));
});
}
@ -308,6 +319,9 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices,
ui->checkCompatibilityOnStartupCheckBox->installEventFilter(this);
ui->updateCompatibilityButton->installEventFilter(this);
// User
ui->OpenCustomTrophyLocationButton->installEventFilter(this);
// Input
ui->hideCursorGroupBox->installEventFilter(this);
ui->idleTimeoutGroupBox->installEventFilter(this);
@ -403,6 +417,9 @@ void SettingsDialog::LoadValuesFromConfig() {
ui->playBGMCheckBox->setChecked(toml::find_or<bool>(data, "General", "playBGM", false));
ui->disableTrophycheckBox->setChecked(
toml::find_or<bool>(data, "General", "isTrophyPopupDisabled", false));
ui->popUpDurationSpinBox->setValue(Config::getTrophyNotificationDuration());
ui->radioButton_Left->setChecked(Config::leftSideTrophy());
ui->radioButton_Right->setChecked(!ui->radioButton_Left->isChecked());
ui->BGMVolumeSlider->setValue(toml::find_or<int>(data, "General", "BGMvolume", 50));
ui->discordRPCCheckbox->setChecked(
toml::find_or<bool>(data, "General", "enableDiscordRPC", true));
@ -593,6 +610,11 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) {
text = tr("Update Compatibility Database:\\nImmediately update the compatibility database.");
}
//User
if (elementName == "OpenCustomTrophyLocationButton") {
text = tr("Open the custom trophy images/sounds folder:\\nYou can add custom images to the trophies and an audio.\\nAdd the files to custom_trophy with the following names:\\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png");
}
// Input
if (elementName == "hideCursorGroupBox") {
text = tr("Hide Cursor:\\nChoose when the cursor will disappear:\\nNever: You will always see the mouse.\\nidle: Set a time for it to disappear after being idle.\\nAlways: you will never see the mouse.");
@ -683,6 +705,8 @@ void SettingsDialog::UpdateSettings() {
screenModeMap.value(ui->displayModeComboBox->currentText()).toStdString());
Config::setIsMotionControlsEnabled(ui->motionControlsCheckBox->isChecked());
Config::setisTrophyPopupDisabled(ui->disableTrophycheckBox->isChecked());
Config::setTrophyNotificationDuration(ui->popUpDurationSpinBox->value());
Config::setLeftSideTrophy(ui->radioButton_Left->isChecked());
Config::setPlayBGM(ui->playBGMCheckBox->isChecked());
Config::setAllowHDR(ui->enableHDRCheckBox->isChecked());
Config::setLogType(logTypeMap.value(ui->logTypeComboBox->currentText()).toStdString());

View file

@ -59,7 +59,7 @@
</size>
</property>
<property name="currentIndex">
<number>6</number>
<number>0</number>
</property>
<widget class="QScrollArea" name="generalTab">
<property name="widgetResizable">
@ -73,8 +73,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>718</width>
<height>332</height>
<width>946</width>
<height>545</height>
</rect>
</property>
<layout class="QVBoxLayout" name="generalTabVLayout" stretch="0">
@ -454,8 +454,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>646</width>
<height>395</height>
<width>946</width>
<height>545</height>
</rect>
</property>
<layout class="QVBoxLayout" name="guiTabVLayout" stretch="0">
@ -903,8 +903,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>545</width>
<height>141</height>
<width>946</width>
<height>545</height>
</rect>
</property>
<layout class="QVBoxLayout" name="graphicsTabVLayout" stretch="0,0">
@ -1198,8 +1198,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>234</width>
<height>292</height>
<width>946</width>
<height>545</height>
</rect>
</property>
<layout class="QVBoxLayout" name="userTabVLayout" stretch="0,0,1">
@ -1264,30 +1264,121 @@
<item>
<widget class="QCheckBox" name="disableTrophycheckBox">
<property name="text">
<string>Disable Trophy Pop-ups</string>
<string>Disable Trophy Notification</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_Trophy">
<layout class="QHBoxLayout" name="horizontalLayout_SidePopUps">
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_SidePopUps">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Trophy Notification Position</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButton_Left">
<property name="text">
<string>Left</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButton_Right">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Right</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_PopUpsDuration">
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_PopUpDuration">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Notification Duration</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="popUpDurationSpinBox">
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_trophyKey">
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_Trophy">
<property name="text">
<string>Trophy Key</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="trophyKeyLineEdit">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<bold>false</bold>
</font>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="OpenCustomTrophyLocationButton">
<property name="text">
<string>Trophy Key</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="trophyKeyLineEdit">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<bold>false</bold>
</font>
<string>Open the custom trophy images/sounds folder</string>
</property>
</widget>
</item>
@ -1342,8 +1433,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>455</width>
<height>252</height>
<width>946</width>
<height>545</height>
</rect>
</property>
<layout class="QVBoxLayout" name="inputTabVLayout" stretch="0,0">
@ -1626,8 +1717,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>216</width>
<height>254</height>
<width>946</width>
<height>545</height>
</rect>
</property>
<layout class="QVBoxLayout" name="pathsTabLayout">
@ -1717,7 +1808,7 @@
<x>0</x>
<y>0</y>
<width>946</width>
<height>536</height>
<height>545</height>
</rect>
</property>
<layout class="QVBoxLayout" name="debugTabVLayout" stretch="0,0">

View file

@ -775,6 +775,10 @@
<source>Delete DLC</source>
<translation>Delete DLC</translation>
</message>
<message>
<source>Delete Trophy</source>
<translation>Delete Trophy</translation>
</message>
<message>
<source>Compatibility...</source>
<translation>Compatibility...</translation>
@ -859,10 +863,18 @@
<source>This game has no save data to delete!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This game has no saved trophies to delete!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Save Data</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Trophy</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SFO Viewer for </source>
<translation type="unfinished"></translation>
@ -1311,6 +1323,10 @@
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<source>Open the custom trophy images/sounds folder</source>
<translation>Open the custom trophy images/sounds folder</translation>
</message>
<message>
<source>Logger</source>
<translation>Logger</translation>
@ -1476,8 +1492,8 @@
<translation>Title Music</translation>
</message>
<message>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
<source>Disable Trophy Notification</source>
<translation>Disable Trophy Notification</translation>
</message>
<message>
<source>Background Image</source>
@ -1611,6 +1627,10 @@
<source>Update Compatibility Database:\nImmediately update the compatibility database.</source>
<translation>Update Compatibility Database:\nImmediately update the compatibility database.</translation>
</message>
<message>
<source>Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png</source>
<translation>Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\nthophy.mp3, bronze.png, gold.png, platinum.png, silver.png</translation>
</message>
<message>
<source>Never</source>
<translation>Never</translation>
@ -1803,6 +1823,22 @@
<source>Separate Log Files:\nWrites a separate logfile for each game.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Trophy Notification Position</source>
<translation>Trophy Notification Position</translation>
</message>
<message>
<source>Left</source>
<translation>Left</translation>
</message>
<message>
<source>Right</source>
<translation>Right</translation>
</message>
<message>
<source>Notification Duration</source>
<translation>Notification Duration</translation>
</message>
</context>
<context>
<name>TrophyViewer</name>
@ -1810,5 +1846,21 @@
<source>Trophy Viewer</source>
<translation>Trophy Viewer</translation>
</message>
<message>
<source>Progress</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Show Earned Trophies</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Show Not Earned Trophies</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Show Hidden Trophies</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>
</TS>

View file

@ -1,27 +1,169 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <fstream>
#include <QCheckBox>
#include <QDockWidget>
#include <QMessageBox>
#include <cmrc/cmrc.hpp>
#include <common/config.h>
#include "common/path_util.h"
#include "main_window_themes.h"
#include "trophy_viewer.h"
namespace fs = std::filesystem;
CMRC_DECLARE(res);
// true: European format; false: American format
bool useEuropeanDateFormat = true;
void TrophyViewer::updateTrophyInfo() {
int total = 0;
int unlocked = 0;
// Cycles through each tab (table) of the QTabWidget
for (int i = 0; i < tabWidget->count(); i++) {
QTableWidget* table = qobject_cast<QTableWidget*>(tabWidget->widget(i));
if (table) {
total += table->rowCount();
for (int row = 0; row < table->rowCount(); ++row) {
QString cellText;
// The "Unlocked" column can be a widget or a simple item
QWidget* widget = table->cellWidget(row, 0);
if (widget) {
// Looks for the QLabel inside the widget (as defined in SetTableItem)
QLabel* label = widget->findChild<QLabel*>();
if (label) {
cellText = label->text();
}
} else {
QTableWidgetItem* item = table->item(row, 0);
if (item) {
cellText = item->text();
}
}
if (cellText == "unlocked")
unlocked++;
}
}
}
int progress = (total > 0) ? (unlocked * 100 / total) : 0;
trophyInfoLabel->setText(
QString(tr("Progress") + ": %1% (%2/%3)").arg(progress).arg(unlocked).arg(total));
}
void TrophyViewer::updateTableFilters() {
bool showEarned = showEarnedCheck->isChecked();
bool showNotEarned = showNotEarnedCheck->isChecked();
bool showHidden = showHiddenCheck->isChecked();
// Cycles through each tab of the QTabWidget
for (int i = 0; i < tabWidget->count(); ++i) {
QTableWidget* table = qobject_cast<QTableWidget*>(tabWidget->widget(i));
if (!table)
continue;
for (int row = 0; row < table->rowCount(); ++row) {
QString unlockedText;
// Gets the text of the "Unlocked" column (index 0)
QWidget* widget = table->cellWidget(row, 0);
if (widget) {
QLabel* label = widget->findChild<QLabel*>();
if (label)
unlockedText = label->text();
} else {
QTableWidgetItem* item = table->item(row, 0);
if (item)
unlockedText = item->text();
}
QString hiddenText;
// Gets the text of the "Hidden" column (index 7)
QWidget* hiddenWidget = table->cellWidget(row, 7);
if (hiddenWidget) {
QLabel* label = hiddenWidget->findChild<QLabel*>();
if (label)
hiddenText = label->text();
} else {
QTableWidgetItem* item = table->item(row, 7);
if (item)
hiddenText = item->text();
}
bool visible = true;
if (unlockedText == "unlocked" && !showEarned)
visible = false;
if (unlockedText == "locked" && !showNotEarned)
visible = false;
if (hiddenText.toLower() == "yes" && !showHidden)
visible = false;
table->setRowHidden(row, !visible);
}
}
}
TrophyViewer::TrophyViewer(QString trophyPath, QString gameTrpPath) : QMainWindow() {
this->setWindowTitle(tr("Trophy Viewer"));
this->setAttribute(Qt::WA_DeleteOnClose);
tabWidget = new QTabWidget(this);
auto lan = Config::getEmulatorLanguage();
if (lan == "en_US" || lan == "zh_CN" || lan == "zh_TW" || lan == "ja_JP" || lan == "ko_KR" ||
lan == "lt_LT" || lan == "nb_NO" || lan == "nl_NL") {
useEuropeanDateFormat = false;
}
gameTrpPath_ = gameTrpPath;
headers << "Unlocked"
<< "Trophy"
<< "Name"
<< "Description"
<< "Time Unlocked"
<< "Type"
<< "ID"
<< "Hidden"
<< "Type"
<< "PID";
PopulateTrophyWidget(trophyPath);
QDockWidget* trophyInfoDock = new QDockWidget("", this);
QWidget* dockWidget = new QWidget(trophyInfoDock);
QVBoxLayout* dockLayout = new QVBoxLayout(dockWidget);
dockLayout->setAlignment(Qt::AlignTop);
trophyInfoLabel = new QLabel(tr("Progress") + ": 0% (0/0)", dockWidget);
trophyInfoLabel->setStyleSheet(
"font-weight: bold; font-size: 16px; color: white; background: #333; padding: 5px;");
dockLayout->addWidget(trophyInfoLabel);
// Creates QCheckBox to filter trophies
showEarnedCheck = new QCheckBox(tr("Show Earned Trophies"), dockWidget);
showNotEarnedCheck = new QCheckBox(tr("Show Not Earned Trophies"), dockWidget);
showHiddenCheck = new QCheckBox(tr("Show Hidden Trophies"), dockWidget);
// Defines the initial states (all checked)
showEarnedCheck->setChecked(true);
showNotEarnedCheck->setChecked(true);
showHiddenCheck->setChecked(false);
// Adds checkboxes to the layout
dockLayout->addWidget(showEarnedCheck);
dockLayout->addWidget(showNotEarnedCheck);
dockLayout->addWidget(showHiddenCheck);
dockWidget->setLayout(dockLayout);
trophyInfoDock->setWidget(dockWidget);
// Adds the dock to the left area
this->addDockWidget(Qt::LeftDockWidgetArea, trophyInfoDock);
// Connects checkbox signals to update trophy display
connect(showEarnedCheck, &QCheckBox::stateChanged, this, &TrophyViewer::updateTableFilters);
connect(showNotEarnedCheck, &QCheckBox::stateChanged, this, &TrophyViewer::updateTableFilters);
connect(showHiddenCheck, &QCheckBox::stateChanged, this, &TrophyViewer::updateTableFilters);
updateTrophyInfo();
updateTableFilters();
}
void TrophyViewer::PopulateTrophyWidget(QString title) {
@ -68,6 +210,7 @@ void TrophyViewer::PopulateTrophyWidget(QString title) {
QStringList trpPid;
QStringList trophyNames;
QStringList trophyDetails;
QStringList trpTimeUnlocked;
QString xmlPath = trpDir + "/Xml/TROP.XML";
QFile file(xmlPath);
@ -84,14 +227,35 @@ void TrophyViewer::PopulateTrophyWidget(QString title) {
trpHidden.append(reader.attributes().value("hidden").toString());
trpType.append(reader.attributes().value("ttype").toString());
trpPid.append(reader.attributes().value("pid").toString());
if (reader.attributes().hasAttribute("unlockstate")) {
if (reader.attributes().value("unlockstate").toString() == "true") {
trpUnlocked.append("unlocked");
} else {
trpUnlocked.append("locked");
}
if (reader.attributes().hasAttribute("timestamp")) {
QString ts = reader.attributes().value("timestamp").toString();
if (ts.length() > 10)
trpTimeUnlocked.append("unknown");
else {
bool ok;
qint64 timestampInt = ts.toLongLong(&ok);
if (ok) {
QDateTime dt = QDateTime::fromSecsSinceEpoch(timestampInt);
QString format = useEuropeanDateFormat ? "dd/MM/yyyy HH:mm:ss"
: "MM/dd/yyyy HH:mm:ss";
trpTimeUnlocked.append(dt.toString(format));
} else {
trpTimeUnlocked.append("unknown");
}
}
} else {
trpTimeUnlocked.append("");
}
} else {
trpUnlocked.append("locked");
trpTimeUnlocked.append("");
}
}
@ -105,7 +269,7 @@ void TrophyViewer::PopulateTrophyWidget(QString title) {
}
QTableWidget* tableWidget = new QTableWidget(this);
tableWidget->setShowGrid(false);
tableWidget->setColumnCount(8);
tableWidget->setColumnCount(9);
tableWidget->setHorizontalHeaderLabels(headers);
tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
@ -113,6 +277,8 @@ void TrophyViewer::PopulateTrophyWidget(QString title) {
tableWidget->horizontalHeader()->setStretchLastSection(true);
tableWidget->verticalHeader()->setVisible(false);
tableWidget->setRowCount(icons.size());
tableWidget->setSortingEnabled(true);
for (int row = 0; auto& icon : icons) {
QTableWidgetItem* item = new QTableWidgetItem();
item->setData(Qt::DecorationRole, icon);
@ -122,15 +288,34 @@ void TrophyViewer::PopulateTrophyWidget(QString title) {
const std::string filename = GetTrpType(trpType[row].at(0));
QTableWidgetItem* typeitem = new QTableWidgetItem();
auto resource = cmrc::res::get_filesystem();
std::string resourceString = "src/images/" + filename;
auto file = resource.open(resourceString);
std::vector<char> imgdata(file.begin(), file.end());
QImage type_icon = QImage::fromData(imgdata).scaled(QSize(64, 64), Qt::KeepAspectRatio,
Qt::SmoothTransformation);
const auto CustomTrophy_Dir =
Common::FS::GetUserPath(Common::FS::PathType::CustomTrophy);
std::string customPath;
if (fs::exists(CustomTrophy_Dir / filename)) {
customPath = (CustomTrophy_Dir / filename).string();
}
std::vector<char> imgdata;
if (!customPath.empty()) {
std::ifstream file(customPath, std::ios::binary);
if (file) {
imgdata = std::vector<char>(std::istreambuf_iterator<char>(file),
std::istreambuf_iterator<char>());
}
} else {
auto resource = cmrc::res::get_filesystem();
std::string resourceString = "src/images/" + filename;
auto file = resource.open(resourceString);
imgdata = std::vector<char>(file.begin(), file.end());
}
QImage type_icon = QImage::fromData(imgdata).scaled(
QSize(100, 100), Qt::KeepAspectRatio, Qt::SmoothTransformation);
typeitem->setData(Qt::DecorationRole, type_icon);
typeitem->setFlags(typeitem->flags() & ~Qt::ItemIsEditable);
tableWidget->setItem(row, 6, typeitem);
tableWidget->setItem(row, 5, typeitem);
std::string detailString = trophyDetails[row].toStdString();
std::size_t newline_pos = 0;
@ -143,46 +328,45 @@ void TrophyViewer::PopulateTrophyWidget(QString title) {
SetTableItem(tableWidget, row, 0, trpUnlocked[row]);
SetTableItem(tableWidget, row, 2, trophyNames[row]);
SetTableItem(tableWidget, row, 3, QString::fromStdString(detailString));
SetTableItem(tableWidget, row, 4, trpId[row]);
SetTableItem(tableWidget, row, 5, trpHidden[row]);
SetTableItem(tableWidget, row, 7, trpPid[row]);
SetTableItem(tableWidget, row, 4, trpTimeUnlocked[row]);
SetTableItem(tableWidget, row, 6, trpId[row]);
SetTableItem(tableWidget, row, 7, trpHidden[row]);
SetTableItem(tableWidget, row, 8, trpPid[row]);
}
tableWidget->verticalHeader()->resizeSection(row, icon.height());
row++;
}
tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
int width = 16;
for (int i = 0; i < 8; i++) {
for (int i = 0; i < 9; i++) {
width += tableWidget->horizontalHeader()->sectionSize(i);
}
tableWidget->resize(width, 720);
tabWidget->addTab(tableWidget,
tabName.insert(6, " ").replace(0, 1, tabName.at(0).toUpper()));
this->resize(width + 20, 720);
this->showMaximized();
tableWidget->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Fixed);
tableWidget->setColumnWidth(3, 650);
}
this->setCentralWidget(tabWidget);
}
void TrophyViewer::SetTableItem(QTableWidget* parent, int row, int column, QString str) {
QWidget* widget = new QWidget();
QVBoxLayout* layout = new QVBoxLayout();
QLabel* label = new QLabel(str);
QTableWidgetItem* item = new QTableWidgetItem();
label->setWordWrap(true);
label->setStyleSheet("color: white; font-size: 15px; font-weight: bold;");
QTableWidgetItem* item = new QTableWidgetItem(str);
// Create shadow effect
QGraphicsDropShadowEffect* shadowEffect = new QGraphicsDropShadowEffect();
shadowEffect->setBlurRadius(5); // Set the blur radius of the shadow
shadowEffect->setColor(QColor(0, 0, 0, 160)); // Set the color and opacity of the shadow
shadowEffect->setOffset(2, 2); // Set the offset of the shadow
label->setGraphicsEffect(shadowEffect); // Apply shadow effect to the QLabel
layout->addWidget(label);
if (column != 1 && column != 2 && column != 3)
layout->setAlignment(Qt::AlignCenter);
widget->setLayout(layout);
item->setTextAlignment(Qt::AlignCenter);
item->setFont(QFont("Arial", 12, QFont::Bold));
Theme theme = static_cast<Theme>(Config::getMainWindowTheme());
if (theme == Theme::Light) {
item->setForeground(QBrush(Qt::black));
} else {
item->setForeground(QBrush(Qt::white));
}
parent->setItem(row, column, item);
parent->setCellWidget(row, column, widget);
}

View file

@ -23,6 +23,10 @@ class TrophyViewer : public QMainWindow {
public:
explicit TrophyViewer(QString trophyPath, QString gameTrpPath);
void updateTrophyInfo();
void updateTableFilters();
private:
void PopulateTrophyWidget(QString title);
void SetTableItem(QTableWidget* parent, int row, int column, QString str);
@ -31,6 +35,10 @@ private:
QStringList headers;
QString gameTrpPath_;
TRP trp;
QLabel* trophyInfoLabel;
QCheckBox* showEarnedCheck;
QCheckBox* showNotEarnedCheck;
QCheckBox* showHiddenCheck;
std::string GetTrpType(const QChar trp_) {
switch (trp_.toLatin1()) {