mirror of
https://git.citron-emu.org/citron/emulator
synced 2025-12-19 10:43:33 +00:00
Merge pull request 'fix/properties-window' (#1) from fix/properties-window into main
Reviewed-on: https://git.citron-emu.org/Citron/Emulator/pulls/1
This commit is contained in:
@@ -14,14 +14,17 @@
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <QAbstractButton>
|
||||
#include <QButtonGroup>
|
||||
#include <QCheckBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFileDialog>
|
||||
#include <QFileInfo>
|
||||
#include <QGraphicsPixmapItem>
|
||||
#include <QMessageBox>
|
||||
#include <QMetaObject>
|
||||
#include <QProgressDialog>
|
||||
#include <QPushButton>
|
||||
#include <QResizeEvent>
|
||||
#include <QScrollArea>
|
||||
#include <QString>
|
||||
#include <QTabBar>
|
||||
@@ -64,72 +67,81 @@
|
||||
ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::string& file_name_,
|
||||
std::vector<VkDeviceInfo::Record>& vk_device_records,
|
||||
Core::System& system_)
|
||||
: QDialog(parent),
|
||||
ui(std::make_unique<Ui::ConfigurePerGame>()), title_id{title_id_}, file_name{file_name_}, system{system_},
|
||||
builder{std::make_unique<ConfigurationShared::Builder>(this, !system_.IsPoweredOn())},
|
||||
tab_group{std::make_shared<std::vector<ConfigurationShared::Tab*>>()} ,
|
||||
rainbow_timer{new QTimer(this)} {
|
||||
: QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id{title_id_},
|
||||
file_name{file_name_}, system{system_},
|
||||
builder{std::make_unique<ConfigurationShared::Builder>(this, !system_.IsPoweredOn())},
|
||||
tab_group{std::make_shared<std::vector<ConfigurationShared::Tab*>>()},
|
||||
rainbow_timer{new QTimer(this)} {
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name));
|
||||
const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename())
|
||||
: fmt::format("{:016X}", title_id);
|
||||
: fmt::format("{:016X}", title_id);
|
||||
game_config = std::make_unique<QtConfig>(config_file_name, Config::ConfigType::PerGameConfig);
|
||||
|
||||
// Create tab instances
|
||||
addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this);
|
||||
audio_tab = std::make_unique<ConfigureAudio>(system_, tab_group, *builder, this);
|
||||
cpu_tab = std::make_unique<ConfigureCpu>(system_, tab_group, *builder, this);
|
||||
graphics_advanced_tab =
|
||||
std::make_unique<ConfigureGraphicsAdvanced>(system_, tab_group, *builder, this);
|
||||
std::make_unique<ConfigureGraphicsAdvanced>(system_, tab_group, *builder, this);
|
||||
graphics_tab = std::make_unique<ConfigureGraphics>(
|
||||
system_, vk_device_records, [&]() { graphics_advanced_tab->ExposeComputeOption(); },
|
||||
[](Settings::AspectRatio, Settings::ResolutionSetup) {}, tab_group, *builder, this);
|
||||
[](Settings::AspectRatio, Settings::ResolutionSetup) {}, tab_group, *builder, this);
|
||||
input_tab = std::make_unique<ConfigureInputPerGame>(system_, game_config.get(), this);
|
||||
linux_tab = std::make_unique<ConfigureLinuxTab>(system_, tab_group, *builder, this);
|
||||
system_tab = std::make_unique<ConfigureSystem>(system_, tab_group, *builder, this);
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
if (!UISettings::values.per_game_configure_geometry.isEmpty()) {
|
||||
restoreGeometry(UISettings::values.per_game_configure_geometry);
|
||||
}
|
||||
|
||||
ApplyStaticTheme();
|
||||
UpdateTheme(); // Run once to set initial colors
|
||||
UpdateTheme();
|
||||
connect(rainbow_timer, &QTimer::timeout, this, &ConfigurePerGame::UpdateTheme);
|
||||
|
||||
setMinimumHeight(400);
|
||||
button_group = new QButtonGroup(this);
|
||||
button_group->setExclusive(true);
|
||||
|
||||
layout()->setSizeConstraint(QLayout::SetDefaultConstraint);
|
||||
const auto add_tab = [&](QWidget* widget, const QString& title) {
|
||||
auto button = new QPushButton(title, this);
|
||||
button->setCheckable(true);
|
||||
button->setObjectName(QStringLiteral("tabButton"));
|
||||
button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
|
||||
ui->tabWidget->addTab(addons_tab.get(), tr("Add-Ons"));
|
||||
ui->tabButtonsLayout->addWidget(button);
|
||||
button_group->addButton(button);
|
||||
|
||||
QScrollArea* system_scroll_area = new QScrollArea(this);
|
||||
system_scroll_area->setWidgetResizable(true);
|
||||
system_scroll_area->setWidget(system_tab.get());
|
||||
ui->tabWidget->addTab(system_scroll_area, tr("System"));
|
||||
QScrollArea* scroll_area = new QScrollArea(this);
|
||||
scroll_area->setWidgetResizable(true);
|
||||
scroll_area->setWidget(widget);
|
||||
ui->stackedWidget->addWidget(scroll_area);
|
||||
|
||||
ui->tabWidget->addTab(cpu_tab.get(), tr("CPU"));
|
||||
connect(button, &QPushButton::clicked, this, [this, scroll_area]() {
|
||||
ui->stackedWidget->setCurrentWidget(scroll_area);
|
||||
});
|
||||
};
|
||||
|
||||
QScrollArea* graphics_scroll_area = new QScrollArea(this);
|
||||
graphics_scroll_area->setWidgetResizable(true);
|
||||
graphics_scroll_area->setWidget(graphics_tab.get());
|
||||
ui->tabWidget->addTab(graphics_scroll_area, tr("Graphics"));
|
||||
|
||||
QScrollArea* graphics_advanced_scroll_area = new QScrollArea(this);
|
||||
graphics_advanced_scroll_area->setWidgetResizable(true);
|
||||
graphics_advanced_scroll_area->setWidget(graphics_advanced_tab.get());
|
||||
ui->tabWidget->addTab(graphics_advanced_scroll_area, tr("Adv. Graphics"));
|
||||
|
||||
ui->tabWidget->addTab(audio_tab.get(), tr("Audio"));
|
||||
ui->tabWidget->addTab(input_tab.get(), tr("Input Profiles"));
|
||||
|
||||
linux_tab->setVisible(false);
|
||||
add_tab(addons_tab.get(), tr("Add-Ons"));
|
||||
add_tab(system_tab.get(), tr("System"));
|
||||
add_tab(cpu_tab.get(), tr("CPU"));
|
||||
add_tab(graphics_tab.get(), tr("Graphics"));
|
||||
add_tab(graphics_advanced_tab.get(), tr("Adv. Graphics"));
|
||||
add_tab(audio_tab.get(), tr("Audio"));
|
||||
add_tab(input_tab.get(), tr("Input Profiles"));
|
||||
#ifdef __unix__
|
||||
linux_tab->setVisible(true);
|
||||
ui->tabWidget->addTab(linux_tab.get(), tr("Linux"));
|
||||
add_tab(linux_tab.get(), tr("Linux"));
|
||||
#endif
|
||||
|
||||
ui->tabButtonsLayout->addStretch();
|
||||
|
||||
if (auto first_button = qobject_cast<QPushButton*>(button_group->buttons().first())) {
|
||||
first_button->setChecked(true);
|
||||
first_button->click();
|
||||
}
|
||||
|
||||
setFocusPolicy(Qt::ClickFocus);
|
||||
setWindowTitle(tr("Properties"));
|
||||
|
||||
addons_tab->SetTitleId(title_id);
|
||||
|
||||
scene = new QGraphicsScene;
|
||||
@@ -137,11 +149,9 @@ rainbow_timer{new QTimer(this)} {
|
||||
|
||||
if (system.IsPoweredOn()) {
|
||||
QPushButton* apply_button = ui->buttonBox->addButton(QDialogButtonBox::Apply);
|
||||
connect(apply_button, &QAbstractButton::clicked, this,
|
||||
&ConfigurePerGame::HandleApplyButtonClicked);
|
||||
connect(apply_button, &QAbstractButton::clicked, this, &ConfigurePerGame::HandleApplyButtonClicked);
|
||||
}
|
||||
|
||||
// Connect trim XCI button
|
||||
connect(ui->trim_xci_button, &QPushButton::clicked, this, &ConfigurePerGame::OnTrimXCI);
|
||||
|
||||
LoadConfiguration();
|
||||
@@ -166,20 +176,18 @@ void ConfigurePerGame::ApplyConfiguration() {
|
||||
if (Settings::IsDockedMode() && Settings::values.players.GetValue()[0].controller_type ==
|
||||
Settings::ControllerType::Handheld) {
|
||||
Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld);
|
||||
Settings::values.use_docked_mode.SetGlobal(true);
|
||||
}
|
||||
Settings::values.use_docked_mode.SetGlobal(true);
|
||||
}
|
||||
|
||||
system.ApplySettings();
|
||||
Settings::LogSettings();
|
||||
|
||||
game_config->SaveAllValues();
|
||||
system.ApplySettings();
|
||||
Settings::LogSettings();
|
||||
game_config->SaveAllValues();
|
||||
}
|
||||
|
||||
void ConfigurePerGame::changeEvent(QEvent* event) {
|
||||
if (event->type() == QEvent::LanguageChange) {
|
||||
RetranslateUI();
|
||||
}
|
||||
|
||||
QDialog::changeEvent(event);
|
||||
}
|
||||
|
||||
@@ -197,89 +205,46 @@ void ConfigurePerGame::LoadFromFile(FileSys::VirtualFile file_) {
|
||||
LoadConfiguration();
|
||||
}
|
||||
|
||||
void ConfigurePerGame::ApplyStaticTheme() {
|
||||
QString raw_stylesheet = property("templateStyleSheet").toString();
|
||||
QString processed_stylesheet = raw_stylesheet;
|
||||
|
||||
QColor accent_color(Theme::GetAccentColor());
|
||||
QString accent_color_hover = accent_color.lighter(115).name(QColor::HexRgb);
|
||||
QString accent_color_pressed = accent_color.darker(120).name(QColor::HexRgb);
|
||||
|
||||
processed_stylesheet.replace(QStringLiteral("%%ACCENT_COLOR%%"), accent_color.name(QColor::HexRgb));
|
||||
processed_stylesheet.replace(QStringLiteral("%%ACCENT_COLOR_HOVER%%"), accent_color_hover);
|
||||
processed_stylesheet.replace(QStringLiteral("%%ACCENT_COLOR_PRESSED%%"), accent_color_pressed);
|
||||
|
||||
setStyleSheet(processed_stylesheet);
|
||||
// Pass the processed stylesheet to the child tabs ONCE
|
||||
graphics_tab->SetTemplateStyleSheet(processed_stylesheet);
|
||||
system_tab->SetTemplateStyleSheet(processed_stylesheet);
|
||||
audio_tab->SetTemplateStyleSheet(processed_stylesheet);
|
||||
cpu_tab->SetTemplateStyleSheet(processed_stylesheet);
|
||||
graphics_advanced_tab->SetTemplateStyleSheet(processed_stylesheet);
|
||||
}
|
||||
|
||||
void ConfigurePerGame::UpdateTheme() {
|
||||
if (!UISettings::values.enable_rainbow_mode.GetValue()) {
|
||||
QString accent_color_str;
|
||||
if (UISettings::values.enable_rainbow_mode.GetValue()) {
|
||||
rainbow_hue += 0.003f;
|
||||
if (rainbow_hue > 1.0f) {
|
||||
rainbow_hue = 0.0f;
|
||||
}
|
||||
QColor accent_color = QColor::fromHsvF(rainbow_hue, 0.8f, 1.0f);
|
||||
accent_color_str = accent_color.name(QColor::HexRgb);
|
||||
if (!rainbow_timer->isActive()) {
|
||||
rainbow_timer->start(150);
|
||||
}
|
||||
} else {
|
||||
if (rainbow_timer->isActive()) {
|
||||
rainbow_timer->stop();
|
||||
ApplyStaticTheme();
|
||||
}
|
||||
return;
|
||||
accent_color_str = Theme::GetAccentColor();
|
||||
}
|
||||
|
||||
rainbow_hue += 0.003f; // Even slower color transition for better performance
|
||||
if (rainbow_hue > 1.0f) {
|
||||
rainbow_hue = 0.0f;
|
||||
QColor accent_color(accent_color_str);
|
||||
const QString accent_color_hover = accent_color.lighter(115).name(QColor::HexRgb);
|
||||
const QString accent_color_pressed = accent_color.darker(120).name(QColor::HexRgb);
|
||||
|
||||
static QString cached_template_style_sheet;
|
||||
if (cached_template_style_sheet.isEmpty()) {
|
||||
cached_template_style_sheet = property("templateStyleSheet").toString();
|
||||
}
|
||||
|
||||
QColor accent_color = QColor::fromHsvF(rainbow_hue, 0.8f, 1.0f);
|
||||
QColor accent_color_hover = accent_color.lighter(115);
|
||||
QColor accent_color_pressed = accent_color.darker(120);
|
||||
QString style_sheet = cached_template_style_sheet;
|
||||
style_sheet.replace(QStringLiteral("%%ACCENT_COLOR%%"), accent_color_str);
|
||||
style_sheet.replace(QStringLiteral("%%ACCENT_COLOR_HOVER%%"), accent_color_hover);
|
||||
style_sheet.replace(QStringLiteral("%%ACCENT_COLOR_PRESSED%%"), accent_color_pressed);
|
||||
setStyleSheet(style_sheet);
|
||||
|
||||
// Cache color names to avoid repeated string operations
|
||||
const QString accent_color_name = accent_color.name(QColor::HexRgb);
|
||||
const QString accent_color_hover_name = accent_color_hover.name(QColor::HexRgb);
|
||||
const QString accent_color_pressed_name = accent_color_pressed.name(QColor::HexRgb);
|
||||
|
||||
// Efficiently update only the necessary widgets
|
||||
QString tab_style = QStringLiteral(
|
||||
"QTabBar::tab:selected { background-color: %1; border-color: %1; }")
|
||||
.arg(accent_color_name);
|
||||
ui->tabWidget->tabBar()->setStyleSheet(tab_style);
|
||||
|
||||
QString button_style = QStringLiteral(
|
||||
"QPushButton { background-color: %1; color: #ffffff; border: none; padding: 10px 20px; border-radius: 6px; font-weight: bold; min-height: 20px; }"
|
||||
"QPushButton:hover { background-color: %2; }"
|
||||
"QPushButton:pressed { background-color: %3; }")
|
||||
.arg(accent_color_name)
|
||||
.arg(accent_color_hover_name)
|
||||
.arg(accent_color_pressed_name);
|
||||
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setStyleSheet(button_style);
|
||||
ui->buttonBox->button(QDialogButtonBox::Cancel)->setStyleSheet(button_style);
|
||||
if (auto* apply_button = ui->buttonBox->button(QDialogButtonBox::Apply)) {
|
||||
apply_button->setStyleSheet(button_style);
|
||||
}
|
||||
|
||||
// Apply rainbow mode to the Trim XCI button
|
||||
ui->trim_xci_button->setStyleSheet(button_style);
|
||||
|
||||
// Create a temporary full stylesheet for the child tabs to update their internal widgets
|
||||
QString child_stylesheet = property("templateStyleSheet").toString();
|
||||
child_stylesheet.replace(QStringLiteral("%%ACCENT_COLOR%%"), accent_color_name);
|
||||
child_stylesheet.replace(QStringLiteral("%%ACCENT_COLOR_HOVER%%"), accent_color_hover_name);
|
||||
child_stylesheet.replace(QStringLiteral("%%ACCENT_COLOR_PRESSED%%"), accent_color_pressed_name);
|
||||
|
||||
// Pass the updated stylesheet to the child tabs
|
||||
graphics_tab->SetTemplateStyleSheet(child_stylesheet);
|
||||
system_tab->SetTemplateStyleSheet(child_stylesheet);
|
||||
audio_tab->SetTemplateStyleSheet(child_stylesheet);
|
||||
cpu_tab->SetTemplateStyleSheet(child_stylesheet);
|
||||
graphics_advanced_tab->SetTemplateStyleSheet(child_stylesheet);
|
||||
|
||||
if (!rainbow_timer->isActive()) {
|
||||
rainbow_timer->start(150); // Further optimized 150ms interval for better performance
|
||||
}
|
||||
// This is the crucial part that passes the theme to the child tabs
|
||||
graphics_tab->SetTemplateStyleSheet(style_sheet);
|
||||
system_tab->SetTemplateStyleSheet(style_sheet);
|
||||
audio_tab->SetTemplateStyleSheet(style_sheet);
|
||||
cpu_tab->SetTemplateStyleSheet(style_sheet);
|
||||
graphics_advanced_tab->SetTemplateStyleSheet(style_sheet);
|
||||
}
|
||||
|
||||
void ConfigurePerGame::LoadConfiguration() {
|
||||
@@ -294,145 +259,108 @@ void ConfigurePerGame::LoadConfiguration() {
|
||||
|
||||
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
const auto loader = Loader::GetLoader(system, file);
|
||||
const auto control = pm.GetControlMetadata();
|
||||
const auto loader = Loader::GetLoader(system, file);
|
||||
|
||||
if (control.first != nullptr) {
|
||||
ui->display_version->setText(QString::fromStdString(control.first->GetVersionString()));
|
||||
ui->display_name->setText(QString::fromStdString(control.first->GetApplicationName()));
|
||||
ui->display_developer->setText(QString::fromStdString(control.first->GetDeveloperName()));
|
||||
} else {
|
||||
std::string title;
|
||||
if (loader->ReadTitle(title) == Loader::ResultStatus::Success)
|
||||
ui->display_name->setText(QString::fromStdString(title));
|
||||
if (control.first != nullptr) {
|
||||
ui->display_version->setText(QString::fromStdString(control.first->GetVersionString()));
|
||||
ui->display_name->setText(QString::fromStdString(control.first->GetApplicationName()));
|
||||
ui->display_developer->setText(QString::fromStdString(control.first->GetDeveloperName()));
|
||||
} else {
|
||||
std::string title;
|
||||
if (loader->ReadTitle(title) == Loader::ResultStatus::Success)
|
||||
ui->display_name->setText(QString::fromStdString(title));
|
||||
|
||||
FileSys::NACP nacp;
|
||||
if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success)
|
||||
ui->display_developer->setText(QString::fromStdString(nacp.GetDeveloperName()));
|
||||
FileSys::NACP nacp;
|
||||
if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success)
|
||||
ui->display_developer->setText(QString::fromStdString(nacp.GetDeveloperName()));
|
||||
|
||||
ui->display_version->setText(QStringLiteral("1.0.0"));
|
||||
ui->display_version->setText(QStringLiteral("1.0.0"));
|
||||
}
|
||||
|
||||
bool has_icon = false;
|
||||
if (control.second != nullptr) {
|
||||
const auto bytes = control.second->ReadAllBytes();
|
||||
if (map.loadFromData(bytes.data(), static_cast<u32>(bytes.size()))) {
|
||||
has_icon = true;
|
||||
}
|
||||
|
||||
if (control.second != nullptr) {
|
||||
scene->clear();
|
||||
|
||||
QPixmap map;
|
||||
const auto bytes = control.second->ReadAllBytes();
|
||||
map.loadFromData(bytes.data(), static_cast<u32>(bytes.size()));
|
||||
|
||||
scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
|
||||
Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
} else {
|
||||
std::vector<u8> bytes;
|
||||
if (loader->ReadIcon(bytes) == Loader::ResultStatus::Success) {
|
||||
scene->clear();
|
||||
|
||||
QPixmap map;
|
||||
map.loadFromData(bytes.data(), static_cast<u32>(bytes.size()));
|
||||
|
||||
scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
|
||||
Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
} else {
|
||||
std::vector<u8> bytes;
|
||||
if (loader->ReadIcon(bytes) == Loader::ResultStatus::Success) {
|
||||
if (map.loadFromData(bytes.data(), static_cast<u32>(bytes.size()))) {
|
||||
has_icon = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui->display_filename->setText(QString::fromStdString(file->GetName()));
|
||||
if (has_icon) {
|
||||
scene->clear();
|
||||
scene->addPixmap(map);
|
||||
ui->icon_view->fitInView(scene->itemsBoundingRect(), Qt::KeepAspectRatio);
|
||||
}
|
||||
|
||||
ui->display_format->setText(
|
||||
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())));
|
||||
ui->display_filename->setText(QString::fromStdString(file->GetName()));
|
||||
ui->display_format->setText(
|
||||
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())));
|
||||
const auto valueText = ReadableByteSize(file->GetSize());
|
||||
ui->display_size->setText(valueText);
|
||||
|
||||
const auto valueText = ReadableByteSize(file->GetSize());
|
||||
ui->display_size->setText(valueText);
|
||||
std::string base_build_id_hex;
|
||||
std::string update_build_id_hex;
|
||||
const auto file_type = loader->GetFileType();
|
||||
|
||||
std::string base_build_id_hex;
|
||||
std::string update_build_id_hex;
|
||||
|
||||
const auto file_type = loader->GetFileType();
|
||||
|
||||
if (file_type == Loader::FileType::NSO) {
|
||||
if (file->GetSize() >= 0x100) {
|
||||
if (file_type == Loader::FileType::NSO) {
|
||||
if (file->GetSize() >= 0x100) {
|
||||
std::array<u8, 0x100> header_data{};
|
||||
if (file->ReadBytes(header_data.data(), 0x100, 0) == 0x100) {
|
||||
std::array<u8, 0x20> build_id{};
|
||||
std::memcpy(build_id.data(), header_data.data() + 0x40, 0x20);
|
||||
base_build_id_hex = Common::HexToString(build_id, false);
|
||||
}
|
||||
}
|
||||
} else if (file_type == Loader::FileType::DeconstructedRomDirectory) {
|
||||
const auto main_dir = file->GetContainingDirectory();
|
||||
if (main_dir) {
|
||||
const auto main_nso = main_dir->GetFile("main");
|
||||
if (main_nso && main_nso->GetSize() >= 0x100) {
|
||||
std::array<u8, 0x100> header_data{};
|
||||
if (file->ReadBytes(header_data.data(), 0x100, 0) == 0x100) {
|
||||
if (main_nso->ReadBytes(header_data.data(), 0x100, 0) == 0x100) {
|
||||
std::array<u8, 0x20> build_id{};
|
||||
std::memcpy(build_id.data(), header_data.data() + 0x40, 0x20);
|
||||
base_build_id_hex = Common::HexToString(build_id, false);
|
||||
}
|
||||
}
|
||||
} else if (file_type == Loader::FileType::DeconstructedRomDirectory) {
|
||||
const auto main_dir = file->GetContainingDirectory();
|
||||
if (main_dir) {
|
||||
const auto main_nso = main_dir->GetFile("main");
|
||||
if (main_nso && main_nso->GetSize() >= 0x100) {
|
||||
std::array<u8, 0x100> header_data{};
|
||||
if (main_nso->ReadBytes(header_data.data(), 0x100, 0) == 0x100) {
|
||||
std::array<u8, 0x20> build_id{};
|
||||
std::memcpy(build_id.data(), header_data.data() + 0x40, 0x20);
|
||||
base_build_id_hex = Common::HexToString(build_id, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
if (file_type == Loader::FileType::XCI) {
|
||||
try {
|
||||
FileSys::XCI xci_temp(file);
|
||||
if (xci_temp.GetStatus() == Loader::ResultStatus::Success) {
|
||||
FileSys::XCI xci(file, title_id, 0);
|
||||
if (xci.GetStatus() == Loader::ResultStatus::Success) {
|
||||
auto program_nca = xci.GetNCAByType(FileSys::NCAContentType::Program);
|
||||
if (program_nca && program_nca->GetStatus() == Loader::ResultStatus::Success) {
|
||||
auto exefs = program_nca->GetExeFS();
|
||||
if (exefs) {
|
||||
auto main_nso = exefs->GetFile("main");
|
||||
if (main_nso && main_nso->GetSize() >= 0x100) {
|
||||
std::array<u8, 0x100> header_data{};
|
||||
if (main_nso->ReadBytes(header_data.data(), 0x100, 0) == 0x100) {
|
||||
std::array<u8, 0x20> build_id{};
|
||||
std::memcpy(build_id.data(), header_data.data() + 0x40, 0x20);
|
||||
base_build_id_hex = Common::HexToString(build_id, false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
if (file_type == Loader::FileType::XCI) {
|
||||
try {
|
||||
FileSys::XCI xci_temp(file);
|
||||
if (xci_temp.GetStatus() == Loader::ResultStatus::Success) {
|
||||
FileSys::XCI xci(file, title_id, 0);
|
||||
if (xci.GetStatus() == Loader::ResultStatus::Success) {
|
||||
auto program_nca = xci.GetNCAByType(FileSys::NCAContentType::Program);
|
||||
if (program_nca && program_nca->GetStatus() == Loader::ResultStatus::Success) {
|
||||
auto exefs = program_nca->GetExeFS();
|
||||
if (exefs) {
|
||||
auto main_nso = exefs->GetFile("main");
|
||||
if (main_nso && main_nso->GetSize() >= 0x100) {
|
||||
std::array<u8, 0x100> header_data{};
|
||||
if (main_nso->ReadBytes(header_data.data(), 0x100, 0) == 0x100) {
|
||||
std::array<u8, 0x20> build_id{};
|
||||
std::memcpy(build_id.data(), header_data.data() + 0x40, 0x20);
|
||||
base_build_id_hex = Common::HexToString(build_id, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
const auto& content_provider = system.GetContentProvider();
|
||||
auto base_nca = content_provider.GetEntry(title_id, FileSys::ContentRecordType::Program);
|
||||
if (base_nca && base_nca->GetStatus() == Loader::ResultStatus::Success) {
|
||||
auto exefs = base_nca->GetExeFS();
|
||||
if (exefs) {
|
||||
auto main_nso = exefs->GetFile("main");
|
||||
if (main_nso && main_nso->GetSize() >= 0x100) {
|
||||
std::array<u8, 0x100> header_data{};
|
||||
if (main_nso->ReadBytes(header_data.data(), 0x100, 0) == 0x100) {
|
||||
std::array<u8, 0x20> build_id{};
|
||||
std::memcpy(build_id.data(), header_data.data() + 0x40, 0x20);
|
||||
base_build_id_hex = Common::HexToString(build_id, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (file_type == Loader::FileType::NSP) {
|
||||
FileSys::NSP nsp(file);
|
||||
if (nsp.GetStatus() == Loader::ResultStatus::Success) {
|
||||
auto exefs = nsp.GetExeFS();
|
||||
if (exefs) {
|
||||
auto main_nso = exefs->GetFile("main");
|
||||
if (main_nso && main_nso->GetSize() >= 0x100) {
|
||||
std::array<u8, 0x100> header_data{};
|
||||
if (main_nso->ReadBytes(header_data.data(), 0x100, 0) == 0x100) {
|
||||
std::array<u8, 0x20> build_id{};
|
||||
std::memcpy(build_id.data(), header_data.data() + 0x40, 0x20);
|
||||
base_build_id_hex = Common::HexToString(build_id, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (file_type == Loader::FileType::NCA) {
|
||||
FileSys::NCA nca(file);
|
||||
if (nca.GetStatus() == Loader::ResultStatus::Success) {
|
||||
auto exefs = nca.GetExeFS();
|
||||
} catch (...) {
|
||||
const auto& content_provider = system.GetContentProvider();
|
||||
auto base_nca = content_provider.GetEntry(title_id, FileSys::ContentRecordType::Program);
|
||||
if (base_nca && base_nca->GetStatus() == Loader::ResultStatus::Success) {
|
||||
auto exefs = base_nca->GetExeFS();
|
||||
if (exefs) {
|
||||
auto main_nso = exefs->GetFile("main");
|
||||
if (main_nso && main_nso->GetSize() >= 0x100) {
|
||||
@@ -446,124 +374,160 @@ void ConfigurePerGame::LoadConfiguration() {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
} else if (file_type == Loader::FileType::NSP) {
|
||||
FileSys::NSP nsp(file);
|
||||
if (nsp.GetStatus() == Loader::ResultStatus::Success) {
|
||||
auto exefs = nsp.GetExeFS();
|
||||
if (exefs) {
|
||||
auto main_nso = exefs->GetFile("main");
|
||||
if (main_nso && main_nso->GetSize() >= 0x100) {
|
||||
std::array<u8, 0x100> header_data{};
|
||||
if (main_nso->ReadBytes(header_data.data(), 0x100, 0) == 0x100) {
|
||||
std::array<u8, 0x20> build_id{};
|
||||
std::memcpy(build_id.data(), header_data.data() + 0x40, 0x20);
|
||||
base_build_id_hex = Common::HexToString(build_id, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (file_type == Loader::FileType::NCA) {
|
||||
FileSys::NCA nca(file);
|
||||
if (nca.GetStatus() == Loader::ResultStatus::Success) {
|
||||
auto exefs = nca.GetExeFS();
|
||||
if (exefs) {
|
||||
auto main_nso = exefs->GetFile("main");
|
||||
if (main_nso && main_nso->GetSize() >= 0x100) {
|
||||
std::array<u8, 0x100> header_data{};
|
||||
if (main_nso->ReadBytes(header_data.data(), 0x100, 0) == 0x100) {
|
||||
std::array<u8, 0x20> build_id{};
|
||||
std::memcpy(build_id.data(), header_data.data() + 0x40, 0x20);
|
||||
base_build_id_hex = Common::HexToString(build_id, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const FileSys::PatchManager pm_update{title_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
|
||||
const auto update_version = pm_update.GetGameVersion();
|
||||
if (update_version.has_value() && update_version.value() > 0) {
|
||||
const auto& content_provider = system.GetContentProvider();
|
||||
const auto update_title_id = FileSys::GetUpdateTitleID(title_id);
|
||||
auto update_nca = content_provider.GetEntry(update_title_id, FileSys::ContentRecordType::Program);
|
||||
|
||||
if (update_nca && update_nca->GetStatus() == Loader::ResultStatus::Success) {
|
||||
auto exefs = update_nca->GetExeFS();
|
||||
if (exefs) {
|
||||
auto main_nso = exefs->GetFile("main");
|
||||
if (main_nso && main_nso->GetSize() >= 0x100) {
|
||||
std::array<u8, 0x100> header_data{};
|
||||
if (main_nso->ReadBytes(header_data.data(), 0x100, 0) == 0x100) {
|
||||
std::array<u8, 0x20> build_id{};
|
||||
std::memcpy(build_id.data(), header_data.data() + 0x40, 0x20);
|
||||
update_build_id_hex = Common::HexToString(build_id, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (update_build_id_hex.empty()) {
|
||||
const auto& content_provider = system.GetContentProvider();
|
||||
const auto update_title_id = FileSys::GetUpdateTitleID(title_id);
|
||||
auto update_nca = content_provider.GetEntry(update_title_id, FileSys::ContentRecordType::Program);
|
||||
|
||||
if (update_nca && update_nca->GetStatus() == Loader::ResultStatus::Success) {
|
||||
auto exefs = update_nca->GetExeFS();
|
||||
if (exefs) {
|
||||
auto main_nso = exefs->GetFile("main");
|
||||
if (main_nso && main_nso->GetSize() >= 0x100) {
|
||||
std::array<u8, 0x100> header_data{};
|
||||
if (main_nso->ReadBytes(header_data.data(), 0x100, 0) == 0x100) {
|
||||
std::array<u8, 0x20> build_id{};
|
||||
std::memcpy(build_id.data(), header_data.data() + 0x40, 0x20);
|
||||
update_build_id_hex = Common::HexToString(build_id, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (update_build_id_hex.empty()) {
|
||||
const auto patches = pm_update.GetPatches();
|
||||
for (const auto& patch : patches) {
|
||||
if (patch.type == FileSys::PatchType::Update && patch.enabled) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
|
||||
const auto& system_build_id = system.GetApplicationProcessBuildID();
|
||||
const auto system_build_id_hex = Common::HexToString(system_build_id, false);
|
||||
try {
|
||||
const FileSys::PatchManager pm_update{title_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
|
||||
if (!system_build_id_hex.empty() && system_build_id_hex != std::string(64, '0')) {
|
||||
if (!base_build_id_hex.empty() && system_build_id_hex != base_build_id_hex) {
|
||||
update_build_id_hex = system_build_id_hex;
|
||||
} else if (base_build_id_hex.empty()) {
|
||||
base_build_id_hex = system_build_id_hex;
|
||||
const auto update_version = pm_update.GetGameVersion();
|
||||
if (update_version.has_value() && update_version.value() > 0) {
|
||||
const auto& content_provider = system.GetContentProvider();
|
||||
const auto update_title_id = FileSys::GetUpdateTitleID(title_id);
|
||||
auto update_nca = content_provider.GetEntry(update_title_id, FileSys::ContentRecordType::Program);
|
||||
|
||||
if (update_nca && update_nca->GetStatus() == Loader::ResultStatus::Success) {
|
||||
auto exefs = update_nca->GetExeFS();
|
||||
if (exefs) {
|
||||
auto main_nso = exefs->GetFile("main");
|
||||
if (main_nso && main_nso->GetSize() >= 0x100) {
|
||||
std::array<u8, 0x100> header_data{};
|
||||
if (main_nso->ReadBytes(header_data.data(), 0x100, 0) == 0x100) {
|
||||
std::array<u8, 0x20> build_id{};
|
||||
std::memcpy(build_id.data(), header_data.data() + 0x40, 0x20);
|
||||
update_build_id_hex = Common::HexToString(build_id, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool update_detected = false;
|
||||
if (update_build_id_hex.empty() && !base_build_id_hex.empty()) {
|
||||
const auto update_version = pm.GetGameVersion();
|
||||
if (update_version.has_value() && update_version.value() > 0) {
|
||||
update_detected = true;
|
||||
}
|
||||
if (update_build_id_hex.empty()) {
|
||||
const auto& content_provider = system.GetContentProvider();
|
||||
const auto update_title_id = FileSys::GetUpdateTitleID(title_id);
|
||||
auto update_nca = content_provider.GetEntry(update_title_id, FileSys::ContentRecordType::Program);
|
||||
|
||||
const auto patches = pm.GetPatches();
|
||||
if (update_nca && update_nca->GetStatus() == Loader::ResultStatus::Success) {
|
||||
auto exefs = update_nca->GetExeFS();
|
||||
if (exefs) {
|
||||
auto main_nso = exefs->GetFile("main");
|
||||
if (main_nso && main_nso->GetSize() >= 0x100) {
|
||||
std::array<u8, 0x100> header_data{};
|
||||
if (main_nso->ReadBytes(header_data.data(), 0x100, 0) == 0x100) {
|
||||
std::array<u8, 0x20> build_id{};
|
||||
std::memcpy(build_id.data(), header_data.data() + 0x40, 0x20);
|
||||
update_build_id_hex = Common::HexToString(build_id, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (update_build_id_hex.empty()) {
|
||||
const auto patches = pm_update.GetPatches();
|
||||
for (const auto& patch : patches) {
|
||||
if (patch.type == FileSys::PatchType::Update && patch.enabled) {
|
||||
update_detected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
bool has_base = !base_build_id_hex.empty() && base_build_id_hex != std::string(64, '0');
|
||||
bool has_update = !update_build_id_hex.empty() && update_build_id_hex != std::string(64, '0');
|
||||
const auto& system_build_id = system.GetApplicationProcessBuildID();
|
||||
const auto system_build_id_hex = Common::HexToString(system_build_id, false);
|
||||
|
||||
if (has_base) {
|
||||
ui->display_build_id->setText(QString::fromStdString(base_build_id_hex));
|
||||
} else {
|
||||
ui->display_build_id->setText(tr("Not Available"));
|
||||
if (!system_build_id_hex.empty() && system_build_id_hex != std::string(64, '0')) {
|
||||
if (!base_build_id_hex.empty() && system_build_id_hex != base_build_id_hex) {
|
||||
update_build_id_hex = system_build_id_hex;
|
||||
} else if (base_build_id_hex.empty()) {
|
||||
base_build_id_hex = system_build_id_hex;
|
||||
}
|
||||
}
|
||||
|
||||
bool update_detected = false;
|
||||
if (update_build_id_hex.empty() && !base_build_id_hex.empty()) {
|
||||
const auto update_version = pm.GetGameVersion();
|
||||
if (update_version.has_value() && update_version.value() > 0) {
|
||||
update_detected = true;
|
||||
}
|
||||
|
||||
if (has_update) {
|
||||
ui->display_update_build_id->setText(QString::fromStdString(update_build_id_hex));
|
||||
} else if (update_detected) {
|
||||
ui->display_update_build_id->setText(tr("Available (Run game to show)"));
|
||||
} else {
|
||||
ui->display_update_build_id->setText(tr("Not Available"));
|
||||
const auto patches = pm.GetPatches();
|
||||
for (const auto& patch : patches) {
|
||||
if (patch.type == FileSys::PatchType::Update && patch.enabled) {
|
||||
update_detected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool has_base = !base_build_id_hex.empty() && base_build_id_hex != std::string(64, '0');
|
||||
bool has_update = !update_build_id_hex.empty() && update_build_id_hex != std::string(64, '0');
|
||||
|
||||
if (has_base) {
|
||||
ui->display_build_id->setText(QString::fromStdString(base_build_id_hex));
|
||||
} else {
|
||||
ui->display_build_id->setText(tr("Not Available"));
|
||||
}
|
||||
|
||||
if (has_update) {
|
||||
ui->display_update_build_id->setText(QString::fromStdString(update_build_id_hex));
|
||||
} else if (update_detected) {
|
||||
ui->display_update_build_id->setText(tr("Available (Run game to show)"));
|
||||
} else {
|
||||
ui->display_update_build_id->setText(tr("Not Available"));
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigurePerGame::resizeEvent(QResizeEvent* event) {
|
||||
QDialog::resizeEvent(event);
|
||||
if (scene && !scene->items().isEmpty()) {
|
||||
ui->icon_view->fitInView(scene->itemsBoundingRect(), Qt::KeepAspectRatio);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigurePerGame::OnTrimXCI() {
|
||||
// Use the stored file name from the constructor
|
||||
if (file_name.empty()) {
|
||||
QMessageBox::warning(this, tr("Trim XCI File"), tr("No file path available."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert to filesystem path with proper Unicode support
|
||||
const std::filesystem::path filepath = file_name;
|
||||
|
||||
// Check if the file is an XCI file
|
||||
const std::string extension = filepath.extension().string();
|
||||
if (extension != ".xci" && extension != ".XCI") {
|
||||
QMessageBox::warning(this, tr("Trim XCI File"),
|
||||
@@ -571,14 +535,12 @@ void ConfigurePerGame::OnTrimXCI() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if file exists
|
||||
if (!std::filesystem::exists(filepath)) {
|
||||
QMessageBox::warning(this, tr("Trim XCI File"),
|
||||
tr("The game file no longer exists."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize the trimmer
|
||||
Common::XCITrimmer trimmer(filepath);
|
||||
if (!trimmer.IsValid()) {
|
||||
QMessageBox::warning(this, tr("Trim XCI File"),
|
||||
@@ -592,7 +554,6 @@ void ConfigurePerGame::OnTrimXCI() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Show file information
|
||||
const u64 current_size_mb = trimmer.GetFileSize() / (1024 * 1024);
|
||||
const u64 data_size_mb = trimmer.GetDataSize() / (1024 * 1024);
|
||||
const u64 savings_mb = trimmer.GetDiskSpaceSavings() / (1024 * 1024);
|
||||
@@ -605,7 +566,6 @@ void ConfigurePerGame::OnTrimXCI() {
|
||||
"This will remove unused space from the XCI file."
|
||||
).arg(current_size_mb).arg(data_size_mb).arg(savings_mb);
|
||||
|
||||
// Create custom message box with three options
|
||||
QMessageBox msgBox(this);
|
||||
msgBox.setWindowTitle(tr("Trim XCI File"));
|
||||
msgBox.setText(info_message);
|
||||
@@ -641,31 +601,24 @@ void ConfigurePerGame::OnTrimXCI() {
|
||||
Common::U16StringFromBuffer(output_filename.utf16(), output_filename.size())};
|
||||
}
|
||||
|
||||
// Pre-translate strings for use in lambda
|
||||
const QString checking_text = tr("Checking free space...");
|
||||
const QString copying_text = tr("Copying file...");
|
||||
|
||||
// Track last operation to detect changes
|
||||
size_t last_total = 0;
|
||||
QString current_operation;
|
||||
|
||||
// Show progress dialog
|
||||
QProgressDialog progress_dialog(tr("Preparing to trim XCI file..."), tr("Cancel"), 0, 100, this);
|
||||
progress_dialog.setWindowTitle(tr("Trim XCI File"));
|
||||
progress_dialog.setWindowModality(Qt::WindowModal);
|
||||
progress_dialog.setMinimumDuration(0);
|
||||
progress_dialog.show();
|
||||
|
||||
// Progress callback
|
||||
auto progress_callback = [&](size_t current, size_t total) {
|
||||
if (total > 0) {
|
||||
// Detect operation change (when total changes significantly)
|
||||
if (total != last_total) {
|
||||
last_total = total;
|
||||
if (current == 0 || current == total) {
|
||||
// Likely switched operations
|
||||
if (total < current_size_mb * 1024 * 1024) {
|
||||
// Smaller total = checking padding
|
||||
current_operation = checking_text;
|
||||
}
|
||||
}
|
||||
@@ -674,7 +627,6 @@ void ConfigurePerGame::OnTrimXCI() {
|
||||
const int percent = static_cast<int>((current * 100) / total);
|
||||
progress_dialog.setValue(percent);
|
||||
|
||||
// Update label text based on operation
|
||||
if (!current_operation.isEmpty()) {
|
||||
const QString current_mb = QString::number(current / (1024.0 * 1024.0), 'f', 1);
|
||||
const QString total_mb = QString::number(total / (1024.0 * 1024.0), 'f', 1);
|
||||
@@ -695,16 +647,13 @@ void ConfigurePerGame::OnTrimXCI() {
|
||||
QCoreApplication::processEvents();
|
||||
};
|
||||
|
||||
// Cancel callback
|
||||
auto cancel_callback = [&]() -> bool {
|
||||
return progress_dialog.wasCanceled();
|
||||
};
|
||||
|
||||
// Perform the trim operation
|
||||
const auto result = trimmer.Trim(progress_callback, cancel_callback, output_path);
|
||||
progress_dialog.close();
|
||||
|
||||
// Show result
|
||||
if (result == Common::XCITrimmer::OperationOutcome::Successful) {
|
||||
const QString success_message = is_save_as ?
|
||||
tr("XCI file successfully trimmed and saved as:\n%1")
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <QDialog>
|
||||
#include <QList>
|
||||
#include <QPixmap>
|
||||
|
||||
#include "configuration/shared_widget.h"
|
||||
#include "core/file_sys/vfs/vfs_types.h"
|
||||
@@ -19,12 +20,16 @@
|
||||
#include "citron/configuration/qt_config.h"
|
||||
#include "citron/configuration/shared_translation.h"
|
||||
|
||||
class QButtonGroup;
|
||||
class QGraphicsScene;
|
||||
class QTimer;
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace InputCommon {
|
||||
class InputSubsystem;
|
||||
class InputSubsystem;
|
||||
}
|
||||
|
||||
class ConfigurePerGameAddons;
|
||||
@@ -36,22 +41,14 @@ class ConfigureInputPerGame;
|
||||
class ConfigureLinuxTab;
|
||||
class ConfigureSystem;
|
||||
|
||||
class QGraphicsScene;
|
||||
class QStandardItem;
|
||||
class QStandardItemModel;
|
||||
class QTreeView;
|
||||
class QVBoxLayout;
|
||||
class QTimer; // Forward declaration for the timer
|
||||
|
||||
namespace Ui {
|
||||
class ConfigurePerGame;
|
||||
class ConfigurePerGame;
|
||||
}
|
||||
|
||||
class ConfigurePerGame : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// Cannot use std::filesystem::path due to https://bugreports.qt.io/browse/QTBUG-73263
|
||||
explicit ConfigurePerGame(QWidget* parent, u64 title_id_, const std::string& file_name_,
|
||||
std::vector<VkDeviceInfo::Record>& vk_device_records,
|
||||
Core::System& system_);
|
||||
@@ -64,13 +61,14 @@ public slots:
|
||||
void accept() override;
|
||||
void OnTrimXCI();
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
|
||||
private:
|
||||
void changeEvent(QEvent* event) override;
|
||||
void RetranslateUI();
|
||||
void HandleApplyButtonClicked();
|
||||
void LoadConfiguration();
|
||||
|
||||
// New, efficient theme update functions
|
||||
void ApplyStaticTheme();
|
||||
void UpdateTheme();
|
||||
|
||||
@@ -97,4 +95,7 @@ private:
|
||||
|
||||
QTimer* rainbow_timer;
|
||||
float rainbow_hue = 0.0f;
|
||||
|
||||
QButtonGroup* button_group;
|
||||
QPixmap map;
|
||||
};
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>900</width>
|
||||
<height>0</height>
|
||||
<width>640</width>
|
||||
<height>480</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
<string>Properties</string>
|
||||
</property>
|
||||
<property name="templateStyleSheet" stdset="0">
|
||||
<string>QDialog {
|
||||
@@ -96,100 +96,34 @@
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
QPushButton.tabButton {
|
||||
background-color: #383838;
|
||||
color: #ffffff;
|
||||
padding: 10px 14px;
|
||||
margin: 2px;
|
||||
border-top-left-radius: 8px;
|
||||
border-top-right-radius: 8px;
|
||||
border-bottom-left-radius: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
min-width: 85px;
|
||||
max-width: 160px;
|
||||
font-weight: 500;
|
||||
border: 1px solid #3d3d3d;
|
||||
text-align: center;
|
||||
QPushButton#tabButton {
|
||||
background-color: #383838;
|
||||
color: #ffffff;
|
||||
padding: 8px 18px;
|
||||
margin-right: 2px;
|
||||
border-top-left-radius: 8px;
|
||||
border-top-right-radius: 8px;
|
||||
border-bottom: none;
|
||||
min-width: 100px;
|
||||
font-weight: bold;
|
||||
font-size: 10pt;
|
||||
border: 1px solid #3d3d3d;
|
||||
}
|
||||
|
||||
QPushButton.tabButton:checked {
|
||||
background-color: %%ACCENT_COLOR%%;
|
||||
color: #ffffff;
|
||||
font-weight: bold;
|
||||
border-color: %%ACCENT_COLOR%%;
|
||||
QPushButton#tabButton:checked {
|
||||
background-color: %%ACCENT_COLOR%%;
|
||||
color: #ffffff;
|
||||
font-weight: bold;
|
||||
border-color: %%ACCENT_COLOR%%;
|
||||
}
|
||||
|
||||
QPushButton.tabButton:hover:!checked {
|
||||
background-color: #4d4d4d;
|
||||
border-color: #5d5d5d;
|
||||
QPushButton#tabButton:hover:!checked {
|
||||
background-color: #4d4d4d;
|
||||
border-color: #5d5d5d;
|
||||
}
|
||||
|
||||
QPushButton.tabButton:pressed {
|
||||
background-color: %%ACCENT_COLOR_PRESSED%%;
|
||||
}
|
||||
|
||||
QTabWidget {
|
||||
background-color: #2b2b2b;
|
||||
border: none;
|
||||
}
|
||||
|
||||
QTabWidget::pane {
|
||||
border: 1px solid #3d3d3d;
|
||||
background-color: #2b2b2b;
|
||||
border-radius: 8px;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
QTabWidget::tab-bar {
|
||||
alignment: left;
|
||||
}
|
||||
|
||||
QTabBar {
|
||||
background-color: #2b2b2b;
|
||||
border: none;
|
||||
}
|
||||
|
||||
QTabBar::tab {
|
||||
background-color: #383838;
|
||||
color: #ffffff;
|
||||
padding: 8px 14px;
|
||||
margin-right: 2px;
|
||||
border-top-left-radius: 8px;
|
||||
border-top-right-radius: 8px;
|
||||
min-width: 85px;
|
||||
font-weight: 500;
|
||||
border: 1px solid #3d3d3d;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
QTabBar::tab:selected {
|
||||
background-color: %%ACCENT_COLOR%%;
|
||||
color: #ffffff;
|
||||
font-weight: bold;
|
||||
border-color: %%ACCENT_COLOR%%;
|
||||
}
|
||||
|
||||
QTabBar::tab:hover:!selected {
|
||||
background-color: #4d4d4d;
|
||||
border-color: #5d5d5d;
|
||||
}
|
||||
|
||||
QTabBar QToolButton {
|
||||
background-color: #383838;
|
||||
border: 1px solid #3d3d3d;
|
||||
border-radius: 4px;
|
||||
padding: 4px;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
QTabBar QToolButton:hover {
|
||||
background-color: #4d4d4d;
|
||||
border-color: %%ACCENT_COLOR%%;
|
||||
}
|
||||
|
||||
QTabBar::scroller {
|
||||
width: 30px;
|
||||
QPushButton#tabButton:pressed {
|
||||
background-color: %%ACCENT_COLOR_PRESSED%%;
|
||||
}
|
||||
|
||||
QGroupBox {
|
||||
@@ -374,10 +308,20 @@
|
||||
}
|
||||
|
||||
QSlider::groove:horizontal {
|
||||
border: 1px solid #5d5d5d;
|
||||
height: 8px;
|
||||
background-color: #3d3d3d;
|
||||
border-radius: 4px;
|
||||
height: 8px;
|
||||
background: #3d3d3d;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #5d5d5d;
|
||||
}
|
||||
|
||||
QSlider::sub-page:horizontal {
|
||||
background: %%ACCENT_COLOR%%;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
QSlider::add-page:horizontal {
|
||||
background: #3d3d3d;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
QSlider::handle:horizontal {
|
||||
@@ -447,9 +391,6 @@
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAsNeeded</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@@ -459,9 +400,15 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>318</width>
|
||||
<height>538</height>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>600</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
@@ -484,7 +431,7 @@
|
||||
<item alignment="Qt::AlignHCenter">
|
||||
<widget class="QGraphicsView" name="icon_view">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@@ -516,180 +463,97 @@
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="6" column="1">
|
||||
<widget class="QLineEdit" name="display_size">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="enabled"><bool>true</bool></property>
|
||||
<property name="readOnly"><bool>true</bool></property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="display_version">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="enabled"><bool>true</bool></property>
|
||||
<property name="readOnly"><bool>true</bool></property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label"><property name="text"><string>Name</string></property></widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Title ID</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_4"><property name="text"><string>Title ID</string></property></widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLineEdit" name="display_title_id">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="enabled"><bool>true</bool></property>
|
||||
<property name="readOnly"><bool>true</bool></property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QLineEdit" name="display_filename">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="enabled"><bool>true</bool></property>
|
||||
<property name="readOnly"><bool>true</bool></property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLineEdit" name="display_format">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="enabled"><bool>true</bool></property>
|
||||
<property name="readOnly"><bool>true</bool></property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Filename</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_7"><property name="text"><string>Filename</string></property></widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="display_name">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="enabled"><bool>true</bool></property>
|
||||
<property name="readOnly"><bool>true</bool></property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="display_developer">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="enabled"><bool>true</bool></property>
|
||||
<property name="readOnly"><bool>true</bool></property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Format</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_5"><property name="text"><string>Format</string></property></widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Version</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_3"><property name="text"><string>Version</string></property></widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Size</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_6"><property name="text"><string>Size</string></property></widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Developer</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_2"><property name="text"><string>Developer</string></property></widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="label_build_id">
|
||||
<property name="text">
|
||||
<string>Base Build ID</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_build_id"><property name="text"><string>Base Build ID</string></property></widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QLineEdit" name="display_build_id">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="enabled"><bool>true</bool></property>
|
||||
<property name="readOnly"><bool>true</bool></property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QLabel" name="label_update_build_id">
|
||||
<property name="text">
|
||||
<string>Update Build ID</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_update_build_id"><property name="text"><string>Update Build ID</string></property></widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QLineEdit" name="display_update_build_id">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="enabled"><bool>true</bool></property>
|
||||
<property name="readOnly"><bool>true</bool></property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="trim_xci_button">
|
||||
<property name="text">
|
||||
<string>Trim XCI File</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Remove unused space from XCI file to reduce file size</string>
|
||||
</property>
|
||||
<property name="text"><string>Trim XCI File</string></property>
|
||||
<property name="toolTip"><string>Remove unused space from XCI file to reduce file size</string></property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="orientation"><enum>Qt::Vertical</enum></property>
|
||||
<property name="sizeHint" stdset="0"><size><width>20</width><height>40</height></size></property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -702,16 +566,19 @@
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="VerticalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="tabScrollArea">
|
||||
<widget class="QScrollArea" name="tabButtonsScrollArea">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
@@ -721,45 +588,37 @@
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="tabScrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>560</width>
|
||||
<height>538</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="tabScrollAreaLayout">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<property name="usesScrollButtons">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="documentMode">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="tabsClosable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<widget class="QWidget" name="tabButtonsContainer">
|
||||
<layout class="QHBoxLayout" name="tabButtonsLayout">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="stackedWidget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
Reference in New Issue
Block a user