mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-05-21 19:05:00 +00:00
* Play Time * SDL * QT * Sort / Formatting * Timer 1 minute * remove the seconds removes the seconds from the screen, but in the play_time.txt file it continues to record the seconds for better accuracy, and the screen is cleaner * fixes the invisible 0 * SDL . . . * Fix Timer
285 lines
12 KiB
C++
285 lines
12 KiB
C++
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include "common/path_util.h"
|
|
#include "common/string_util.h"
|
|
#include "game_list_frame.h"
|
|
|
|
GameListFrame::GameListFrame(std::shared_ptr<GameInfoClass> game_info_get, QWidget* parent)
|
|
: QTableWidget(parent), m_game_info(game_info_get) {
|
|
icon_size = Config::getIconSize();
|
|
this->setShowGrid(false);
|
|
this->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
|
this->setSelectionBehavior(QAbstractItemView::SelectRows);
|
|
this->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
this->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
|
this->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
|
|
this->verticalScrollBar()->installEventFilter(this);
|
|
this->verticalScrollBar()->setSingleStep(20);
|
|
this->horizontalScrollBar()->setSingleStep(20);
|
|
this->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
|
|
this->verticalHeader()->setVisible(false);
|
|
this->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
this->horizontalHeader()->setHighlightSections(false);
|
|
this->horizontalHeader()->setSortIndicatorShown(true);
|
|
this->horizontalHeader()->setStretchLastSection(true);
|
|
this->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
this->setColumnCount(9);
|
|
this->setColumnWidth(1, 300); // Name
|
|
this->setColumnWidth(2, 120); // Serial
|
|
this->setColumnWidth(3, 90); // Region
|
|
this->setColumnWidth(4, 90); // Firmware
|
|
this->setColumnWidth(5, 90); // Size
|
|
this->setColumnWidth(6, 90); // Version
|
|
this->setColumnWidth(7, 100); // Play Time
|
|
QStringList headers;
|
|
headers << tr("Icon") << tr("Name") << tr("Serial") << tr("Region") << tr("Firmware")
|
|
<< tr("Size") << tr("Version") << tr("Play Time") << tr("Path");
|
|
this->setHorizontalHeaderLabels(headers);
|
|
this->horizontalHeader()->setSortIndicatorShown(true);
|
|
this->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
|
|
this->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed);
|
|
this->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Fixed);
|
|
PopulateGameList();
|
|
|
|
connect(this, &QTableWidget::currentCellChanged, this, &GameListFrame::onCurrentCellChanged);
|
|
connect(this->verticalScrollBar(), &QScrollBar::valueChanged, this,
|
|
&GameListFrame::RefreshListBackgroundImage);
|
|
connect(this->horizontalScrollBar(), &QScrollBar::valueChanged, this,
|
|
&GameListFrame::RefreshListBackgroundImage);
|
|
|
|
this->horizontalHeader()->setSortIndicatorShown(true);
|
|
this->horizontalHeader()->setSectionsClickable(true);
|
|
QObject::connect(
|
|
this->horizontalHeader(), &QHeaderView::sectionClicked, this, [this](int columnIndex) {
|
|
if (ListSortedAsc) {
|
|
SortNameDescending(columnIndex);
|
|
this->horizontalHeader()->setSortIndicator(columnIndex, Qt::DescendingOrder);
|
|
ListSortedAsc = false;
|
|
} else {
|
|
SortNameAscending(columnIndex);
|
|
this->horizontalHeader()->setSortIndicator(columnIndex, Qt::AscendingOrder);
|
|
ListSortedAsc = true;
|
|
}
|
|
this->clearContents();
|
|
PopulateGameList();
|
|
});
|
|
|
|
connect(this, &QTableWidget::customContextMenuRequested, this, [=, this](const QPoint& pos) {
|
|
m_gui_context_menus.RequestGameMenu(pos, m_game_info->m_games, this, true);
|
|
});
|
|
}
|
|
|
|
void GameListFrame::onCurrentCellChanged(int currentRow, int currentColumn, int previousRow,
|
|
int previousColumn) {
|
|
QTableWidgetItem* item = this->item(currentRow, currentColumn);
|
|
if (!item) {
|
|
return;
|
|
}
|
|
SetListBackgroundImage(item);
|
|
PlayBackgroundMusic(item);
|
|
}
|
|
|
|
void GameListFrame::PlayBackgroundMusic(QTableWidgetItem* item) {
|
|
if (!item || !Config::getPlayBGM()) {
|
|
BackgroundMusicPlayer::getInstance().stopMusic();
|
|
return;
|
|
}
|
|
QString snd0path;
|
|
Common::FS::PathToQString(snd0path, m_game_info->m_games[item->row()].snd0_path);
|
|
BackgroundMusicPlayer::getInstance().playMusic(snd0path);
|
|
}
|
|
|
|
void GameListFrame::PopulateGameList() {
|
|
this->setRowCount(m_game_info->m_games.size());
|
|
ResizeIcons(icon_size);
|
|
|
|
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, 2, QString::fromStdString(m_game_info->m_games[i].serial));
|
|
SetRegionFlag(i, 3, QString::fromStdString(m_game_info->m_games[i].region));
|
|
SetTableItem(i, 4, QString::fromStdString(m_game_info->m_games[i].fw));
|
|
SetTableItem(i, 5, QString::fromStdString(m_game_info->m_games[i].size));
|
|
SetTableItem(i, 6, QString::fromStdString(m_game_info->m_games[i].version));
|
|
|
|
QString playTime = GetPlayTime(m_game_info->m_games[i].serial);
|
|
if (playTime.isEmpty()) {
|
|
m_game_info->m_games[i].play_time = "0:00:00";
|
|
SetTableItem(i, 7, "0");
|
|
} else {
|
|
QStringList timeParts = playTime.split(':');
|
|
int hours = timeParts[0].toInt();
|
|
int minutes = timeParts[1].toInt();
|
|
int seconds = timeParts[2].toInt();
|
|
|
|
QString formattedPlayTime;
|
|
if (hours > 0) {
|
|
formattedPlayTime += QString("%1h ").arg(hours);
|
|
}
|
|
if (minutes > 0) {
|
|
formattedPlayTime += QString("%1m ").arg(minutes);
|
|
}
|
|
|
|
formattedPlayTime = formattedPlayTime.trimmed();
|
|
m_game_info->m_games[i].play_time = playTime.toStdString();
|
|
if (formattedPlayTime.isEmpty()) {
|
|
SetTableItem(i, 7, "0");
|
|
} else {
|
|
SetTableItem(i, 7, formattedPlayTime);
|
|
}
|
|
}
|
|
|
|
QString path;
|
|
Common::FS::PathToQString(path, m_game_info->m_games[i].path);
|
|
SetTableItem(i, 8, path);
|
|
}
|
|
}
|
|
|
|
void GameListFrame::SetListBackgroundImage(QTableWidgetItem* item) {
|
|
if (!item) {
|
|
// handle case where no item was clicked
|
|
return;
|
|
}
|
|
|
|
QString pic1Path;
|
|
Common::FS::PathToQString(pic1Path, m_game_info->m_games[item->row()].pic_path);
|
|
const auto blurredPic1Path = Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) /
|
|
m_game_info->m_games[item->row()].serial / "pic1.png";
|
|
QString blurredPic1PathQt;
|
|
Common::FS::PathToQString(blurredPic1PathQt, blurredPic1Path);
|
|
|
|
backgroundImage = QImage(blurredPic1PathQt);
|
|
if (backgroundImage.isNull()) {
|
|
QImage image(pic1Path);
|
|
backgroundImage = m_game_list_utils.BlurImage(image, image.rect(), 16);
|
|
|
|
std::filesystem::path img_path =
|
|
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) /
|
|
m_game_info->m_games[item->row()].serial;
|
|
std::filesystem::create_directories(img_path);
|
|
if (!backgroundImage.save(blurredPic1PathQt, "PNG")) {
|
|
// qDebug() << "Error: Unable to save image.";
|
|
}
|
|
}
|
|
RefreshListBackgroundImage();
|
|
}
|
|
|
|
void GameListFrame::RefreshListBackgroundImage() {
|
|
if (!backgroundImage.isNull()) {
|
|
QPalette palette;
|
|
palette.setBrush(QPalette::Base,
|
|
QBrush(backgroundImage.scaled(size(), Qt::IgnoreAspectRatio)));
|
|
QColor transparentColor = QColor(135, 206, 235, 40);
|
|
palette.setColor(QPalette::Highlight, transparentColor);
|
|
this->setPalette(palette);
|
|
}
|
|
}
|
|
|
|
void GameListFrame::SortNameAscending(int columnIndex) {
|
|
std::sort(m_game_info->m_games.begin(), m_game_info->m_games.end(),
|
|
[columnIndex](const GameInfo& a, const GameInfo& b) {
|
|
return CompareStringsAscending(a, b, columnIndex);
|
|
});
|
|
}
|
|
|
|
void GameListFrame::SortNameDescending(int columnIndex) {
|
|
std::sort(m_game_info->m_games.begin(), m_game_info->m_games.end(),
|
|
[columnIndex](const GameInfo& a, const GameInfo& b) {
|
|
return CompareStringsDescending(a, b, columnIndex);
|
|
});
|
|
}
|
|
|
|
void GameListFrame::ResizeIcons(int iconSize) {
|
|
for (int index = 0; auto& game : m_game_info->m_games) {
|
|
QImage scaledPixmap = game.icon.scaled(QSize(iconSize, iconSize), Qt::KeepAspectRatio,
|
|
Qt::SmoothTransformation);
|
|
QTableWidgetItem* iconItem = new QTableWidgetItem();
|
|
this->verticalHeader()->resizeSection(index, scaledPixmap.height());
|
|
this->horizontalHeader()->resizeSection(0, scaledPixmap.width());
|
|
iconItem->setData(Qt::DecorationRole, scaledPixmap);
|
|
this->setItem(index, 0, iconItem);
|
|
index++;
|
|
}
|
|
this->horizontalHeader()->setSectionResizeMode(8, QHeaderView::ResizeToContents);
|
|
}
|
|
|
|
void GameListFrame::SetTableItem(int row, int column, QString itemStr) {
|
|
QTableWidgetItem* item = new QTableWidgetItem();
|
|
QWidget* widget = new QWidget(this);
|
|
QVBoxLayout* layout = new QVBoxLayout(widget);
|
|
QLabel* label = new QLabel(itemStr, widget);
|
|
|
|
label->setStyleSheet("color: white; font-size: 16px; font-weight: bold;");
|
|
|
|
// 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 != 8 && column != 1)
|
|
layout->setAlignment(Qt::AlignCenter);
|
|
widget->setLayout(layout);
|
|
this->setItem(row, column, item);
|
|
this->setCellWidget(row, column, widget);
|
|
}
|
|
|
|
void GameListFrame::SetRegionFlag(int row, int column, QString itemStr) {
|
|
QTableWidgetItem* item = new QTableWidgetItem();
|
|
QImage scaledPixmap;
|
|
if (itemStr == "Japan") {
|
|
scaledPixmap = QImage(":images/flag_jp.png");
|
|
} else if (itemStr == "Europe") {
|
|
scaledPixmap = QImage(":images/flag_eu.png");
|
|
} else if (itemStr == "USA") {
|
|
scaledPixmap = QImage(":images/flag_us.png");
|
|
} else if (itemStr == "Asia") {
|
|
scaledPixmap = QImage(":images/flag_china.png");
|
|
} else if (itemStr == "World") {
|
|
scaledPixmap = QImage(":images/flag_world.png");
|
|
} else {
|
|
scaledPixmap = QImage(":images/flag_unk.png");
|
|
}
|
|
QWidget* widget = new QWidget(this);
|
|
QVBoxLayout* layout = new QVBoxLayout(widget);
|
|
QLabel* label = new QLabel(widget);
|
|
label->setPixmap(QPixmap::fromImage(scaledPixmap));
|
|
layout->setAlignment(Qt::AlignCenter);
|
|
layout->addWidget(label);
|
|
widget->setLayout(layout);
|
|
this->setItem(row, column, item);
|
|
this->setCellWidget(row, column, widget);
|
|
}
|
|
|
|
QString GameListFrame::GetPlayTime(const std::string& serial) {
|
|
QString playTime;
|
|
const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
|
|
QString filePath = QString::fromStdString((user_dir / "play_time.txt").string());
|
|
|
|
QFile file(filePath);
|
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
return playTime;
|
|
}
|
|
|
|
while (!file.atEnd()) {
|
|
QByteArray line = file.readLine();
|
|
QString lineStr = QString::fromUtf8(line).trimmed();
|
|
|
|
QStringList parts = lineStr.split(' ');
|
|
if (parts.size() >= 2) {
|
|
QString fileSerial = parts[0];
|
|
QString time = parts[1];
|
|
|
|
if (fileSerial == QString::fromStdString(serial)) {
|
|
playTime = time;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
file.close();
|
|
return playTime;
|
|
}
|