diff --git a/CMakeModules/CopyCitronQt6Deps.cmake b/CMakeModules/CopyCitronQt6Deps.cmake index f4e1771d9..79699abfd 100644 --- a/CMakeModules/CopyCitronQt6Deps.cmake +++ b/CMakeModules/CopyCitronQt6Deps.cmake @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2025 citron Emulator Project +# SPDX-FileCopyrightText: 2026 citron Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later function(copy_citron_Qt6_deps target_dir) @@ -13,11 +13,13 @@ function(copy_citron_Qt6_deps target_dir) set(Qt6_PLATFORMS_DIR "${Qt6_DIR}/../../../plugins/platforms/") set(Qt6_STYLES_DIR "${Qt6_DIR}/../../../plugins/styles/") set(Qt6_IMAGEFORMATS_DIR "${Qt6_DIR}/../../../plugins/imageformats/") + set(Qt6_ICONENGINES_DIR "${Qt6_DIR}/../../../plugins/iconengines/") set(Qt6_TLS_DIR "${Qt6_DIR}/../../../plugins/tls/") set(Qt6_RESOURCES_DIR "${Qt6_DIR}/../../../resources/") set(PLATFORMS ${DLL_DEST}plugins/platforms/) set(STYLES ${DLL_DEST}plugins/styles/) set(IMAGEFORMATS ${DLL_DEST}plugins/imageformats/) + set(ICONENGINES ${DLL_DEST}plugins/iconengines/) set(TLS ${DLL_DEST}tls/) if (MSVC) @@ -26,6 +28,7 @@ function(copy_citron_Qt6_deps target_dir) Qt6Gui$<$:d>.* Qt6Widgets$<$:d>.* Qt6Network$<$:d>.* + Qt6Svg$<$:d>.* ) if (CITRON_USE_QT_MULTIMEDIA) windows_copy_files(${target_dir} ${Qt6_DLL_DIR} ${DLL_DEST} @@ -51,6 +54,11 @@ function(copy_citron_Qt6_deps target_dir) windows_copy_files(citron ${Qt6_IMAGEFORMATS_DIR} ${IMAGEFORMATS} qjpeg$<$:d>.* qgif$<$:d>.* + qpng$<$:d>.* + qsvg$<$:d>.* + ) + windows_copy_files(citron ${Qt6_ICONENGINES_DIR} ${ICONENGINES} + qsvgicon$<$:d>.* ) # Copy TLS plugins for SSL/HTTPS support (required for auto updater) windows_copy_files(citron ${Qt6_TLS_DIR} ${TLS} diff --git a/src/citron/custom_metadata_dialog.cpp b/src/citron/custom_metadata_dialog.cpp index 16a7fa775..9094d3214 100644 --- a/src/citron/custom_metadata_dialog.cpp +++ b/src/citron/custom_metadata_dialog.cpp @@ -1,3 +1,4 @@ +// SPDX-FileCopyrightText: 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include @@ -8,14 +9,20 @@ #include #include "citron/custom_metadata.h" #include "citron/custom_metadata_dialog.h" +#include "common/common_types.h" #include "ui_custom_metadata_dialog.h" CustomMetadataDialog::CustomMetadataDialog(QWidget* parent, u64 program_id_, - const std::string& current_title) + const std::string& current_title, u64 current_play_time) : QDialog(parent), ui(std::make_unique()), program_id(program_id_) { ui->setupUi(this); ui->title_edit->setText(QString::fromStdString(current_title)); + const u64 hours = current_play_time / 3600; + const u64 minutes = (current_play_time % 3600) / 60; + ui->playtime_hours->setValue(static_cast(hours)); + ui->playtime_minutes->setValue(static_cast(minutes)); + if (auto current_icon_path = Citron::CustomMetadata::GetInstance().GetCustomIconPath(program_id)) { icon_path = *current_icon_path; @@ -42,6 +49,12 @@ std::string CustomMetadataDialog::GetIconPath() const { return icon_path; } +u64 CustomMetadataDialog::GetPlayTime() const { + const u64 hours = static_cast(ui->playtime_hours->value()); + const u64 minutes = static_cast(ui->playtime_minutes->value()); + return (hours * 3600) + (minutes * 60); +} + bool CustomMetadataDialog::WasReset() const { return was_reset; } diff --git a/src/citron/custom_metadata_dialog.h b/src/citron/custom_metadata_dialog.h index 218ba2153..d61093891 100644 --- a/src/citron/custom_metadata_dialog.h +++ b/src/citron/custom_metadata_dialog.h @@ -1,3 +1,4 @@ +// SPDX-FileCopyrightText: 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -16,12 +17,13 @@ class CustomMetadataDialog : public QDialog { Q_OBJECT public: - explicit CustomMetadataDialog(QWidget* parent, u64 program_id, - const std::string& current_title); + explicit CustomMetadataDialog(QWidget* parent, u64 program_id, const std::string& current_title, + u64 current_play_time); ~CustomMetadataDialog() override; [[nodiscard]] std::string GetTitle() const; [[nodiscard]] std::string GetIconPath() const; + [[nodiscard]] u64 GetPlayTime() const; [[nodiscard]] bool WasReset() const; private slots: diff --git a/src/citron/custom_metadata_dialog.ui b/src/citron/custom_metadata_dialog.ui index 2894c3b48..cb87cff9a 100644 --- a/src/citron/custom_metadata_dialog.ui +++ b/src/citron/custom_metadata_dialog.ui @@ -1,4 +1,8 @@ + CustomMetadataDialog @@ -7,37 +11,64 @@ 0 0 400 - 300 + 350 Edit Game Metadata - + - - + + Title: - + - - - - - + + + + Playtime: + + + + + + + + + Hours + + + 99999 + + + + + + + Minutes + + + 59 + + + + + + Icon: - + Select Icon... @@ -46,74 +77,69 @@ - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 128 - 128 - - - - - 128 - 128 - - - - - - - false - - - Qt::AlignCenter - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - + Qt::Vertical + + QSizePolicy::Fixed + 20 - 40 + 10 + + + + + + Qt::Horizontal + + + + + + + + 128 + 128 + + + + + 128 + 128 + + + + + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + + + + + + Qt::Vertical + + + diff --git a/src/citron/game_list.cpp b/src/citron/game_list.cpp index fce7e00ab..936a4c38b 100644 --- a/src/citron/game_list.cpp +++ b/src/citron/game_list.cpp @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: 2015 Citra Emulator Project -// SPDX-FileCopyrightText: 2025 citron Emulator Project +// SPDX-FileCopyrightText: 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include @@ -967,7 +967,7 @@ GameList::GameList(std::shared_ptr vfs_, // Surprise Me button - positioned after sort button btn_surprise_me = new QToolButton(toolbar); QIcon surprise_icon(QStringLiteral(":/dist/dice.svg")); - if (surprise_icon.isNull() || surprise_icon.availableSizes().isEmpty()) { + if (surprise_icon.isNull()) { // Fallback to theme icon or standard icon on Windows where SVG may not load surprise_icon = QIcon::fromTheme(QStringLiteral("media-playlist-shuffle")); if (surprise_icon.isNull()) { @@ -1623,7 +1623,8 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri QAction* properties = context_menu.addAction(tr("Properties")); connect(edit_metadata, &QAction::triggered, [this, program_id, game_name] { - CustomMetadataDialog dialog(this, program_id, game_name.toStdString()); + const u64 current_play_time = play_time_manager.GetPlayTime(program_id); + CustomMetadataDialog dialog(this, program_id, game_name.toStdString(), current_play_time); if (dialog.exec() == QDialog::Accepted) { auto& custom_metadata = Citron::CustomMetadata::GetInstance(); if (dialog.WasReset()) { @@ -1634,6 +1635,7 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri if (!icon_path.empty()) { custom_metadata.SetCustomIcon(program_id, icon_path); } + play_time_manager.SetPlayTime(program_id, dialog.GetPlayTime()); } if (main_window) { main_window->RefreshGameList(); diff --git a/src/citron/game_list.h b/src/citron/game_list.h index 76a0f2292..f0aa77a04 100644 --- a/src/citron/game_list.h +++ b/src/citron/game_list.h @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: 2015 Citra Emulator Project -// SPDX-FileCopyrightText: 2025 citron Emulator Project +// SPDX-FileCopyrightText: 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -232,7 +232,7 @@ private: friend class GameListSearchField; - const PlayTime::PlayTimeManager& play_time_manager; + PlayTime::PlayTimeManager& play_time_manager; Core::System& system; }; diff --git a/src/citron/play_time_manager.cpp b/src/citron/play_time_manager.cpp index 6bcd06550..72821a3ce 100644 --- a/src/citron/play_time_manager.cpp +++ b/src/citron/play_time_manager.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-FileCopyrightText: 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "citron/play_time_manager.h" #include "common/alignment.h" #include "common/fs/file.h" #include "common/fs/fs.h" @@ -9,7 +11,6 @@ #include "common/settings.h" #include "common/thread.h" #include "core/hle/service/acc/profile_manager.h" -#include "citron/play_time_manager.h" namespace PlayTime { @@ -159,6 +160,14 @@ u64 PlayTimeManager::GetPlayTime(u64 program_id) const { } } +void PlayTimeManager::SetPlayTime(u64 program_id, u64 play_time) { + if (program_id == 0) { + return; + } + database[program_id] = play_time; + Save(); +} + void PlayTimeManager::ResetProgramPlayTime(u64 program_id) { database.erase(program_id); Save(); diff --git a/src/citron/play_time_manager.h b/src/citron/play_time_manager.h index 7ced9162d..db770ec99 100644 --- a/src/citron/play_time_manager.h +++ b/src/citron/play_time_manager.h @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-FileCopyrightText: 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -30,6 +31,7 @@ public: CITRON_NON_MOVEABLE(PlayTimeManager); u64 GetPlayTime(u64 program_id) const; + void SetPlayTime(u64 program_id, u64 play_time); void ResetProgramPlayTime(u64 program_id); void SetProgramId(u64 program_id); void Start();