mirror of
https://git.eden-emu.dev/archive/citron
synced 2026-03-22 17:46:08 -04:00
feat(ui): Fix .png & .svg rendering issue for game_list / Introduce Play Time editing in Custom Metadata
This commit is contained in:
@@ -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}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user