feat(ui): Fix .png & .svg rendering issue for game_list / Introduce Play Time editing in Custom Metadata

This commit is contained in:
Collecting
2026-02-15 20:09:58 -05:00
parent 637418dbaf
commit 659b23d642
8 changed files with 140 additions and 78 deletions

View File

@@ -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$<$<CONFIG:Debug>:d>.*
Qt6Widgets$<$<CONFIG:Debug>:d>.*
Qt6Network$<$<CONFIG:Debug>:d>.*
Qt6Svg$<$<CONFIG:Debug>: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$<$<CONFIG:Debug>:d>.*
qgif$<$<CONFIG:Debug>:d>.*
qpng$<$<CONFIG:Debug>:d>.*
qsvg$<$<CONFIG:Debug>:d>.*
)
windows_copy_files(citron ${Qt6_ICONENGINES_DIR} ${ICONENGINES}
qsvgicon$<$<CONFIG:Debug>:d>.*
)
# Copy TLS plugins for SSL/HTTPS support (required for auto updater)
windows_copy_files(citron ${Qt6_TLS_DIR} ${TLS}

View File

@@ -1,3 +1,4 @@
// SPDX-FileCopyrightText: 2026 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <QFileDialog>
@@ -8,14 +9,20 @@
#include <QPushButton>
#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<Ui::CustomMetadataDialog>()), 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<int>(hours));
ui->playtime_minutes->setValue(static_cast<int>(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<u64>(ui->playtime_hours->value());
const u64 minutes = static_cast<u64>(ui->playtime_minutes->value());
return (hours * 3600) + (minutes * 60);
}
bool CustomMetadataDialog::WasReset() const {
return was_reset;
}

View File

@@ -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:

View File

@@ -1,4 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
SPDX-FileCopyrightText: 2026 citron Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later
-->
<ui version="4.0">
<class>CustomMetadataDialog</class>
<widget class="QDialog" name="CustomMetadataDialog">
@@ -7,37 +11,64 @@
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
<height>350</height>
</rect>
</property>
<property name="windowTitle">
<string>Edit Game Metadata</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QVBoxLayout" name="mainLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Title:</string>
</property>
</widget>
</item>
<item>
<item row="0" column="1">
<widget class="QLineEdit" name="title_edit"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<item row="1" column="0">
<widget class="QLabel" name="playtime_label">
<property name="text">
<string>Playtime:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="playtime_inputs_layout">
<item>
<widget class="QSpinBox" name="playtime_hours">
<property name="suffix">
<string> Hours</string>
</property>
<property name="maximum">
<number>99999</number>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="playtime_minutes">
<property name="suffix">
<string> Minutes</string>
</property>
<property name="maximum">
<number>59</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Icon:</string>
</property>
</widget>
</item>
<item>
<item row="2" column="1">
<widget class="QPushButton" name="select_icon_button">
<property name="text">
<string>Select Icon...</string>
@@ -46,74 +77,69 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="previewLayout">
<item>
<spacer name="leftSpacer">
<property name="orientation">
<set>Qt::Horizontal</set>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="icon_preview">
<property name="minimumSize">
<size>
<width>128</width>
<height>128</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>128</width>
<height>128</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="rightSpacer">
<property name="orientation">
<set>Qt::Horizontal</set>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<spacer name="spacer_preview_top">
<property name="orientation">
<set>Qt::Vertical</set>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="preview_container_layout">
<item>
<spacer name="horizontalSpacer_left">
<property name="orientation">
<set>Qt::Horizontal</set>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="icon_preview">
<property name="minimumSize">
<size>
<width>128</width>
<height>128</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>128</width>
<height>128</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_right">
<property name="orientation">
<set>Qt::Horizontal</set>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<set>Qt::Vertical</set>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">

View File

@@ -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 <random>
@@ -967,7 +967,7 @@ GameList::GameList(std::shared_ptr<FileSys::VfsFilesystem> 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();

View File

@@ -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;
};

View File

@@ -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();

View File

@@ -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();