mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-07-13 13:15:59 +00:00
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:
parent
63b50ff92c
commit
5e5ca2138e
15 changed files with 620 additions and 104 deletions
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue