[core, android] Initial playtime implementation #2535
9 changed files with 132 additions and 4 deletions
|
@ -154,6 +154,11 @@ u64 PlayTimeManager::GetPlayTime(u64 program_id) const {
|
|||
}
|
||||
}
|
||||
|
||||
void PlayTimeManager::SetPlayTime(u64 program_id, u64 play_time) {
|
||||
database[program_id] = play_time;
|
||||
Save();
|
||||
}
|
||||
|
||||
void PlayTimeManager::ResetProgramPlayTime(u64 program_id) {
|
||||
database.erase(program_id);
|
||||
Save();
|
||||
|
|
|
@ -34,6 +34,7 @@ public:
|
|||
u64 GetPlayTime(u64 program_id) const;
|
||||
void ResetProgramPlayTime(u64 program_id);
|
||||
void SetProgramId(u64 program_id);
|
||||
void SetPlayTime(u64 program_id, u64 play_time);
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "qt_playtime_manager.h"
|
||||
|
||||
namespace QtCommon::PlayTimeManager {
|
||||
|
||||
QString ReadablePlayTime(qulonglong time_seconds) {
|
||||
if (time_seconds == 0) {
|
||||
return {};
|
||||
|
@ -17,3 +19,24 @@ QString ReadablePlayTime(qulonglong time_seconds) {
|
|||
.arg(value, 0, 'f', !is_minutes && time_seconds % 60 != 0)
|
||||
.arg(QString::fromUtf8(unit));
|
||||
}
|
||||
|
||||
QString GetPlayTimeUnit(qulonglong time_seconds, TimeUnit unit) {
|
||||
switch (unit) {
|
||||
case TimeUnit::Hours: {
|
||||
const qulonglong hours = time_seconds / 3600;
|
||||
return QString::number(hours);
|
||||
}
|
||||
case TimeUnit::Minutes: {
|
||||
const qulonglong minutes = (time_seconds % 3600) / 60;
|
||||
return QString::number(minutes);
|
||||
}
|
||||
case TimeUnit::Seconds: {
|
||||
const qulonglong seconds = time_seconds % 60;
|
||||
return QString::number(seconds);
|
||||
}
|
||||
default:
|
||||
return QStringLiteral("0");
|
||||
}
|
||||
}
|
||||
crueter marked this conversation as resolved
|
||||
|
||||
} // namespace QtCommon::PlayTimeManager
|
|
@ -1,8 +1,20 @@
|
|||
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace QtCommon::PlayTimeManager {
|
||||
|
||||
enum class TimeUnit {
|
||||
Hours,
|
||||
Minutes,
|
||||
Seconds
|
||||
};
|
||||
|
||||
// Converts a length of time in seconds into a readable format
|
||||
QString ReadablePlayTime(qulonglong time_seconds);
|
||||
QString ReadablePlayTime(qulonglong time_seconds);
|
||||
|
||||
// Returns play time hours/minutes/seconds as a string
|
||||
QString GetPlayTimeUnit(qulonglong time_seconds, TimeUnit unit);
|
||||
|
||||
} // namespace QtCommon::PlayTimeManager
|
|
@ -557,13 +557,15 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
|
|||
QAction* remove_update = remove_menu->addAction(tr("Remove Installed Update"));
|
||||
QAction* remove_dlc = remove_menu->addAction(tr("Remove All Installed DLC"));
|
||||
QAction* remove_custom_config = remove_menu->addAction(tr("Remove Custom Configuration"));
|
||||
QAction* remove_play_time_data = remove_menu->addAction(tr("Remove Play Time Data"));
|
||||
QAction* remove_cache_storage = remove_menu->addAction(tr("Remove Cache Storage"));
|
||||
QAction* remove_gl_shader_cache = remove_menu->addAction(tr("Remove OpenGL Pipeline Cache"));
|
||||
QAction* remove_vk_shader_cache = remove_menu->addAction(tr("Remove Vulkan Pipeline Cache"));
|
||||
remove_menu->addSeparator();
|
||||
QAction* remove_shader_cache = remove_menu->addAction(tr("Remove All Pipeline Caches"));
|
||||
QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents"));
|
||||
QMenu* play_time_menu = context_menu.addMenu(tr("Manage Play Time"));
|
||||
QAction* set_play_time = play_time_menu->addAction(tr("Set Play Time Data"));
|
||||
crueter marked this conversation as resolved
crueter
commented
"Edit" would work better here "Edit" would work better here
|
||||
QAction* remove_play_time_data = play_time_menu->addAction(tr("Remove Play Time Data"));
|
||||
QMenu* dump_romfs_menu = context_menu.addMenu(tr("Dump RomFS"));
|
||||
QAction* dump_romfs = dump_romfs_menu->addAction(tr("Dump RomFS"));
|
||||
QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC"));
|
||||
|
@ -629,6 +631,8 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
|
|||
connect(remove_custom_config, &QAction::triggered, [this, program_id, path]() {
|
||||
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::CustomConfiguration, path);
|
||||
});
|
||||
connect(set_play_time, &QAction::triggered,
|
||||
[this, program_id]() { emit SetPlayTimeRequested(program_id); });
|
||||
connect(remove_play_time_data, &QAction::triggered,
|
||||
[this, program_id]() { emit RemovePlayTimeRequested(program_id); });
|
||||
connect(remove_cache_storage, &QAction::triggered, [this, program_id, path] {
|
||||
|
|
|
@ -104,6 +104,7 @@ signals:
|
|||
void RemoveFileRequested(u64 program_id, QtCommon::Game::GameListRemoveTarget target,
|
||||
const std::string& game_path);
|
||||
void RemovePlayTimeRequested(u64 program_id);
|
||||
void SetPlayTimeRequested(u64 program_id);
|
||||
void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target);
|
||||
void VerifyIntegrityRequested(const std::string& game_path);
|
||||
void CopyTIDRequested(u64 program_id);
|
||||
|
|
|
@ -241,7 +241,7 @@ public:
|
|||
|
||||
void setData(const QVariant& value, int role) override {
|
||||
qulonglong time_seconds = value.toULongLong();
|
||||
GameListItem::setData(ReadablePlayTime(time_seconds), Qt::DisplayRole);
|
||||
GameListItem::setData(QtCommon::PlayTimeManager::ReadablePlayTime(time_seconds), Qt::DisplayRole);
|
||||
GameListItem::setData(value, PlayTimeRole);
|
||||
}
|
||||
|
||||
|
|
|
@ -1574,6 +1574,8 @@ void GMainWindow::ConnectWidgetEvents() {
|
|||
connect(game_list, &GameList::RemoveFileRequested, this, &GMainWindow::OnGameListRemoveFile);
|
||||
connect(game_list, &GameList::RemovePlayTimeRequested, this,
|
||||
&GMainWindow::OnGameListRemovePlayTimeData);
|
||||
connect(game_list, &GameList::SetPlayTimeRequested, this,
|
||||
&GMainWindow::OnGameListSetPlayTime);
|
||||
connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS);
|
||||
connect(game_list, &GameList::VerifyIntegrityRequested, this,
|
||||
&GMainWindow::OnGameListVerifyIntegrity);
|
||||
|
@ -2634,6 +2636,85 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, QtCommon::Game::GameListR
|
|||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnGameListSetPlayTime(u64 program_id) {
|
||||
QDialog dialog(this);
|
||||
dialog.setWindowTitle(tr("Set Play Time Data"));
|
||||
dialog.setModal(true);
|
||||
crueter marked this conversation as resolved
crueter
commented
We can probably make this a .ui file We can probably make this a .ui file
|
||||
|
||||
auto* layout = new QVBoxLayout(&dialog);
|
||||
auto* input_layout = new QHBoxLayout();
|
||||
auto* hours_edit = new QLineEdit(&dialog);
|
||||
auto* minutes_edit = new QLineEdit(&dialog);
|
||||
auto* seconds_edit = new QLineEdit(&dialog);
|
||||
crueter marked this conversation as resolved
crueter
commented
These can be spin boxes These can be spin boxes
|
||||
|
||||
hours_edit->setValidator(new QIntValidator(0, 9999, hours_edit));
|
||||
minutes_edit->setValidator(new QIntValidator(0, 59, minutes_edit));
|
||||
seconds_edit->setValidator(new QIntValidator(0, 59, seconds_edit));
|
||||
crueter marked this conversation as resolved
crueter
commented
And if we do go with QSpinBox you can just set the range and this won't be necessary And if we do go with QSpinBox you can just set the range and this won't be necessary
|
||||
|
||||
using QtCommon::PlayTimeManager::TimeUnit;
|
||||
const u64 current_play_time = play_time_manager->GetPlayTime(program_id);
|
||||
auto getTimeUnit = [current_play_time](TimeUnit unit) {
|
||||
return QtCommon::PlayTimeManager::GetPlayTimeUnit(current_play_time, unit);
|
||||
};
|
||||
|
||||
hours_edit->setText(getTimeUnit(TimeUnit::Hours));
|
||||
minutes_edit->setText(getTimeUnit(TimeUnit::Minutes));
|
||||
seconds_edit->setText(getTimeUnit(TimeUnit::Seconds));
|
||||
|
||||
input_layout->addWidget(new QLabel(tr("Hours:"), &dialog));
|
||||
input_layout->addWidget(hours_edit);
|
||||
input_layout->addWidget(new QLabel(tr("Minutes:"), &dialog));
|
||||
input_layout->addWidget(minutes_edit);
|
||||
input_layout->addWidget(new QLabel(tr("Seconds:"), &dialog));
|
||||
input_layout->addWidget(seconds_edit);
|
||||
|
||||
layout->addLayout(input_layout);
|
||||
|
||||
auto* error_label = new QLabel(&dialog);
|
||||
error_label->setStyleSheet(QStringLiteral("QLabel { color : red; }"));
|
||||
error_label->setVisible(false);
|
||||
layout->addWidget(error_label);
|
||||
|
||||
auto* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &dialog);
|
||||
layout->addWidget(button_box);
|
||||
|
||||
connect(button_box, &QDialogButtonBox::accepted, [&]() {
|
||||
const int hours = hours_edit->text().toInt();
|
||||
const int minutes = minutes_edit->text().toInt();
|
||||
const int seconds = seconds_edit->text().toInt();
|
||||
crueter marked this conversation as resolved
crueter
commented
Theoretically, this "should" be done as text is edited rather than as it's accepted. Theoretically, this "should" be done as text is edited rather than as it's accepted.
|
||||
|
||||
if (hours < 0 || hours > 9999) {
|
||||
error_label->setText(tr("Hours must be between 0 and 9999."));
|
||||
error_label->setVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (minutes < 0 || minutes > 59) {
|
||||
error_label->setText(tr("Minutes must be between 0 and 59."));
|
||||
error_label->setVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (seconds < 0 || seconds > 59) {
|
||||
error_label->setText(tr("Seconds must be between 0 and 59."));
|
||||
error_label->setVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
u64 total_seconds = static_cast<u64>(hours) * 3600 + static_cast<u64>(minutes) * 60 + static_cast<u64>(seconds);
|
||||
play_time_manager->SetPlayTime(program_id, total_seconds);
|
||||
game_list->PopulateAsync(UISettings::values.game_dirs);
|
||||
|
||||
dialog.accept();
|
||||
});
|
||||
|
||||
connect(button_box, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
|
||||
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GMainWindow::OnGameListRemovePlayTimeData(u64 program_id) {
|
||||
if (QMessageBox::question(this, tr("Remove Play Time Data"), tr("Reset play time?"),
|
||||
QMessageBox::Yes | QMessageBox::No,
|
||||
|
|
|
@ -345,6 +345,7 @@ private slots:
|
|||
void OnGameListRemoveFile(u64 program_id, QtCommon::Game::GameListRemoveTarget target,
|
||||
const std::string& game_path);
|
||||
void OnGameListRemovePlayTimeData(u64 program_id);
|
||||
void OnGameListSetPlayTime(u64 program_id);
|
||||
void OnGameListDumpRomFS(u64 program_id, const std::string& game_path, DumpRomFSTarget target);
|
||||
void OnGameListVerifyIntegrity(const std::string& game_path);
|
||||
void OnGameListCopyTID(u64 program_id);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue
In these cases I would recommend making them FrontendCommon methods that return std::strings. For the ReadablePlayTime you can use fmt::format like in my DataManager PR