Map UDS events to trophies from dumped metadata; trophy viewer/overview improvements (#1088)

* Map UDS events to trophies via dumped metadata and redesign trophy UI

Correlate each UDS event to its stat ID (uds00.ucp) and trophy target (trophy00.ucp) instead of a hard-coded event, store trophy JSON with unlock date/time outside _SaveData, and add a PS5-style toast, viewer and overview.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Fix trophy rule evaluation and share progress accounting

* Match trophy and reload toolbar styling

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: nmzik <Nmzik@mail.ru>
This commit is contained in:
Felipe
2026-10-06 10:45:26 -03:00
committed by GitHub
parent 7d2422abbe
commit fb7ff7ec03
16 changed files with 1076 additions and 182 deletions
@@ -169,6 +169,28 @@
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="trophy_overview_button">
<property name="minimumSize">
<size>
<width>36</width>
<height>36</height>
</size>
</property>
<property name="toolTip">
<string>View saved trophies</string>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
+6 -1
View File
@@ -1 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="#000" d="M17.65 6.35A7.96 7.96 0 0 0 12 4a8 8 0 1 0 7.73 10h-2.08A6 6 0 1 1 12 6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<g fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
<path d="M26 16a10 10 0 1 1-2.93-7.07L27 13"/>
<path d="M27 5v8h-8"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 232 B

After

Width:  |  Height:  |  Size: 252 B

+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<g fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
<path d="M9 5h14v8a7 7 0 0 1-14 0Z"/>
<path d="M9 8H5v3a4 4 0 0 0 4 4M23 8h4v3a4 4 0 0 1-4 4"/>
<path d="M16 20v4M12 28v-4h8v4M11 28h10"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 325 B

@@ -43,6 +43,7 @@ public:
bool EnsureGameDirectory();
void ScanGameDirectory();
void ViewTrophies();
void ViewTrophyOverview();
signals:
@@ -3,6 +3,8 @@
#include <QDialog>
#include <vector>
class QTabWidget;
class QWidget;
@@ -14,6 +16,8 @@ public:
static bool HasTrophyData(const Configuration* info);
static void ShowForGame(const Configuration* info, const QString& runtime_directory,
QWidget* parent);
static void ShowOverview(const std::vector<const Configuration*>& games,
const QString& runtime_directory, QWidget* parent);
private:
bool LoadGame(const Configuration& info, const QString& runtime_directory, QString& error);
+2
View File
@@ -5,6 +5,8 @@
<file alias="input-mapping.svg">icons/input-mapping.svg</file>
<file alias="refresh.svg">icons/refresh.svg</file>
<file alias="remove-configuration.svg">icons/remove-configuration.svg</file>
<file alias="trophy.svg">icons/trophy.svg</file>
<file alias="hidden-trophy.png">../../assets/images/hidden-trophy.png</file>
</qresource>
<qresource prefix="/styles">
<file alias="launcher.qss">styles/launcher.qss</file>
@@ -56,6 +56,7 @@
#endif
#include <memory>
#include <vector>
#include "ui_configuration_list_widget.h"
@@ -241,6 +242,8 @@ ConfigurationListWidget::ConfigurationListWidget(QWidget* parent)
&ConfigurationListWidget::edit_configuration);
connect(m_ui->delete_button, &QToolButton::clicked, this,
&ConfigurationListWidget::delete_configuartion);
connect(m_ui->trophy_overview_button, &QToolButton::clicked, this,
&ConfigurationListWidget::ViewTrophyOverview);
connect(m_ui->cfgs_list, &QTreeWidget::currentItemChanged, this,
&ConfigurationListWidget::SelectItem);
connect(m_ui->cfgs_list, &QTreeWidget::itemDoubleClicked, this,
@@ -293,6 +296,7 @@ void ConfigurationListWidget::UpdateToolbarIcons() {
set_icon(m_ui->input_mapping_button, QStringLiteral(":/icons/input-mapping.svg"));
set_icon(m_ui->edit_button, QStringLiteral(":/icons/edit-configuration.svg"));
set_icon(m_ui->delete_button, QStringLiteral(":/icons/remove-configuration.svg"));
set_icon(m_ui->trophy_overview_button, QStringLiteral(":/icons/trophy.svg"));
}
void ConfigurationListWidget::WriteSettings() {
@@ -840,6 +844,20 @@ void ConfigurationListWidget::ViewTrophies() {
TrophyViewerDialog::ShowForGame(config.get(), m_runtime_directory, this);
}
void ConfigurationListWidget::ViewTrophyOverview() {
std::vector<std::unique_ptr<Configuration>> configurations;
std::vector<const Configuration*> games;
configurations.reserve(static_cast<size_t>(m_ui->cfgs_list->topLevelItemCount()));
games.reserve(static_cast<size_t>(m_ui->cfgs_list->topLevelItemCount()));
for (int index = 0; index < m_ui->cfgs_list->topLevelItemCount(); ++index) {
const auto* item =
static_cast<const ConfigurationItem*>(m_ui->cfgs_list->topLevelItem(index));
configurations.push_back(CreateConfiguration(*item));
games.push_back(configurations.back().get());
}
TrophyViewerDialog::ShowOverview(games, m_runtime_directory, this);
}
void ConfigurationListWidget::open_game_folder() {
auto* item = static_cast<ConfigurationItem*>(m_ui->cfgs_list->currentItem());
if (item == nullptr) {
+350 -66
View File
@@ -6,18 +6,23 @@
#include "gameContent.h"
#include <QAbstractItemView>
#include <QDateTime>
#include <QDialogButtonBox>
#include <QFileInfo>
#include <QFont>
#include <QHeaderView>
#include <QIcon>
#include <QHBoxLayout>
#include <QImage>
#include <QLabel>
#include <QListWidget>
#include <QMessageBox>
#include <QPainter>
#include <QPainterPath>
#include <QPixmap>
#include <QProgressBar>
#include <QRegularExpression>
#include <QSize>
#include <QStringList>
#include <QTabBar>
#include <QTabWidget>
#include <QTableWidget>
#include <QVBoxLayout>
namespace {
@@ -49,26 +54,6 @@ static QString GradeToText(int grade) {
}
}
static void PrepareTable(QTableWidget* table) {
table->setHorizontalHeaderLabels({QObject::tr("Unlocked"), QObject::tr("Trophy"),
QObject::tr("Name"), QObject::tr("Description")});
table->setAlternatingRowColors(true);
table->setEditTriggers(QAbstractItemView::NoEditTriggers);
table->setSelectionBehavior(QAbstractItemView::SelectRows);
table->setSelectionMode(QAbstractItemView::SingleSelection);
table->setIconSize(QSize(96, 96));
table->setShowGrid(false);
table->setWordWrap(true);
table->verticalHeader()->setVisible(false);
table->verticalHeader()->setDefaultSectionSize(112);
table->horizontalHeader()->setStretchLastSection(true);
table->horizontalHeader()->setHighlightSections(false);
table->setColumnWidth(0, 100);
table->setColumnWidth(1, 132);
table->setColumnWidth(2, 260);
table->setColumnWidth(3, 480);
}
static QString TrophyTooltip(const Common::Trophies::Trophy& trophy) {
QStringList lines {QString::fromStdString(trophy.name),
QString::fromStdString(trophy.description),
@@ -102,6 +87,225 @@ bool TrophyViewerDialog::HasTrophyData(const Configuration* info) {
return !FindTrophyFiles(info).isEmpty();
}
namespace {
using Common::Trophies::Progress;
Common::Trophies::UnlockData LoadUnlocks(const Configuration& info,
const QString& runtime_directory, const QString& file) {
const auto label = TrophyFilePattern.match(QFileInfo(file).fileName()).captured(1).toUInt();
return Common::Trophies::LoadUnlockData(Common::Trophies::UnlocksPath(
GameContent::ToPath(runtime_directory), info.title_id.toStdString(), info.user_id, label));
}
Progress GetGameProgress(const Configuration* info, const QString& runtime_directory) {
Progress result;
if (info == nullptr) {
return result;
}
const auto reader = Common::OpenArchive(GameContent::ToPath(info->basedir));
for (const auto& file: FindTrophyFiles(info)) {
const auto package =
Common::Trophies::LoadPackage(GameContent::ToPath(file), info->console_language);
const auto progress =
Common::Trophies::GetProgress(package, LoadUnlocks(*info, runtime_directory, file));
result.total += progress.total;
result.earned += progress.earned;
for (size_t grade = 1; grade < result.total_grade.size(); ++grade) {
result.total_grade[grade] += progress.total_grade[grade];
result.earned_grade[grade] += progress.earned_grade[grade];
}
}
return result;
}
QPixmap MakeCupIcon(const QColor& color, int size) {
QPixmap pixmap(size, size);
pixmap.fill(Qt::transparent);
QPainter painter(&pixmap);
painter.setRenderHint(QPainter::Antialiasing);
const double u = size / 20.0;
painter.setPen(Qt::NoPen);
painter.setBrush(color);
QPainterPath cup;
cup.moveTo(5 * u, 2 * u);
cup.lineTo(15 * u, 2 * u);
cup.lineTo(14 * u, 9 * u);
cup.quadTo(10 * u, 13 * u, 6 * u, 9 * u);
cup.closeSubpath();
painter.drawPath(cup);
painter.setPen(QPen(color, 1.6 * u));
painter.setBrush(Qt::NoBrush);
painter.drawArc(QRectF(1.5 * u, 3 * u, 5 * u, 5 * u), 90 * 16, 180 * 16);
painter.drawArc(QRectF(13.5 * u, 3 * u, 5 * u, 5 * u), 90 * 16, -180 * 16);
painter.drawLine(QPointF(10 * u, 12 * u), QPointF(10 * u, 16 * u));
painter.setPen(Qt::NoPen);
painter.setBrush(color);
painter.drawRoundedRect(QRectF(6 * u, 16 * u, 8 * u, 2.5 * u), u, u);
return pixmap;
}
QColor GradeColor(int grade) {
switch (grade) {
case 1: return QColor(0x7fc8ff);
case 2: return QColor(0xf5c542);
case 3: return QColor(0xc4cad2);
default: return QColor(0xcd7f4f);
}
}
QWidget* MakeGradeCount(QWidget* parent, int grade, int value, int icon_size, int font_size) {
auto* box = new QWidget(parent);
auto* row = new QHBoxLayout(box);
row->setContentsMargins(0, 0, 0, 0);
auto* icon = new QLabel(box);
icon->setPixmap(MakeCupIcon(GradeColor(grade), icon_size));
auto* text = new QLabel(QString::number(value), box);
text->setStyleSheet(QStringLiteral("font-size: %1px;").arg(font_size));
row->addWidget(icon);
row->addWidget(text);
return box;
}
QPixmap LoadGameArt(const Configuration* game, int height) {
QPixmap art;
const auto data = GameContent::ReadFile(game->basedir, QStringLiteral("sce_sys/icon0.png"),
GameContent::MaxImageSize);
if (!data.isEmpty() && art.loadFromData(data)) {
return art.scaledToHeight(height, Qt::SmoothTransformation);
}
return {};
}
} // namespace
void TrophyViewerDialog::ShowOverview(const std::vector<const Configuration*>& games,
const QString& runtime_directory, QWidget* parent) {
QDialog dialog(parent);
dialog.setWindowTitle(QObject::tr("Trophies"));
dialog.resize(820, 640);
dialog.setStyleSheet(QStringLiteral(
"QDialog { background: palette(window); }"
"QLabel { color: palette(text); }"
"QListWidget { background: transparent; border: none; outline: none; }"
"QListWidget::item { border-bottom: 1px solid palette(mid); padding: 4px; }"
"QListWidget::item:selected, QListWidget::item:hover { background: palette(midlight); "
"border: 1px solid palette(highlight); }"
"QProgressBar { background: palette(mid); border: none; height: 4px; }"
"QProgressBar::chunk { background: palette(highlight); }"));
auto* layout = new QVBoxLayout(&dialog);
struct GameProgress {
const Configuration* game;
Progress progress;
};
std::vector<GameProgress> trophy_games;
Progress totals;
for (const auto* game: games) {
const auto progress = GetGameProgress(game, runtime_directory);
if (progress.total == 0) {
continue;
}
trophy_games.push_back({game, progress});
totals.earned += progress.earned;
for (size_t grade = 1; grade < totals.earned_grade.size(); ++grade) {
totals.earned_grade[grade] += progress.earned_grade[grade];
}
}
auto* header = new QHBoxLayout;
auto* title = new QLabel(QObject::tr("Trophies"), &dialog);
title->setStyleSheet(QStringLiteral("font-size: 30px;"));
header->addWidget(title);
header->addStretch(1);
auto* total = new QLabel(QObject::tr("Total %1").arg(totals.earned), &dialog);
total->setAlignment(Qt::AlignCenter);
total->setStyleSheet(QStringLiteral("font-size: 16px;"));
header->addWidget(total);
for (int grade = 1; grade <= 4; ++grade) {
header->addWidget(MakeGradeCount(&dialog, grade, totals.earned_grade[grade], 32, 18));
}
layout->addLayout(header);
auto* list = new QListWidget(&dialog);
list->setSelectionMode(QAbstractItemView::SingleSelection);
list->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
layout->addWidget(list, 1);
for (size_t index = 0; index < trophy_games.size(); ++index) {
const auto& [game, progress] = trophy_games[index];
auto* item = new QListWidgetItem(list);
item->setSizeHint(QSize(760, 112));
item->setData(Qt::UserRole, static_cast<int>(index));
auto* row_widget = new QWidget(list);
row_widget->setAttribute(Qt::WA_TransparentForMouseEvents);
auto* row = new QHBoxLayout(row_widget);
auto* art = new QLabel(row_widget);
art->setFixedSize(80, 80);
art->setAlignment(Qt::AlignCenter);
const auto pixmap = LoadGameArt(game, 80);
if (!pixmap.isNull()) {
art->setPixmap(pixmap);
} else {
art->setStyleSheet(QStringLiteral("background: palette(mid);"));
}
row->addWidget(art);
const auto name = !game->name.isEmpty() ? game->name : game->title_id;
auto* text = new QLabel(QStringLiteral("%1\n%2").arg(name, game->title_id), row_widget);
text->setStyleSheet(QStringLiteral("font-size: 18px;"));
row->addWidget(text, 1);
auto* percent_box = new QVBoxLayout;
percent_box->setSpacing(2);
percent_box->setContentsMargins(0, 0, 0, 0);
auto* percent = new QLabel(QStringLiteral("%1%").arg(progress.Percentage()), row_widget);
percent->setAlignment(Qt::AlignRight);
percent->setStyleSheet(QStringLiteral("font-size: 24px;"));
percent->setMinimumHeight(34);
auto* earned = new QLabel(
QObject::tr("Earned %1/%2").arg(progress.earned).arg(progress.total), row_widget);
earned->setAlignment(Qt::AlignRight);
earned->setMinimumHeight(18);
auto* bar = new QProgressBar(row_widget);
bar->setRange(0, 100);
bar->setValue(progress.Percentage());
bar->setTextVisible(false);
bar->setFixedWidth(130);
percent_box->addWidget(percent);
percent_box->addWidget(earned);
percent_box->addWidget(bar);
row->addLayout(percent_box);
for (int grade = 1; grade <= 4; ++grade) {
if (grade == 1 && progress.total_grade[1] == 0) {
continue;
}
row->addWidget(MakeGradeCount(row_widget, grade, progress.earned_grade[grade], 24, 18));
}
list->setItemWidget(item, row_widget);
}
if (trophy_games.empty()) {
auto* empty =
new QLabel(QObject::tr("No games with trophies found in the game folders."), &dialog);
empty->setAlignment(Qt::AlignCenter);
layout->insertWidget(1, empty);
list->hide();
}
connect(list, &QListWidget::itemClicked, &dialog,
[&dialog, &trophy_games, &runtime_directory](QListWidgetItem* item) {
const auto index = item->data(Qt::UserRole).toInt();
if (index >= 0 && static_cast<size_t>(index) < trophy_games.size()) {
ShowForGame(trophy_games[static_cast<size_t>(index)].game, runtime_directory,
&dialog);
}
});
auto* buttons = new QDialogButtonBox(QDialogButtonBox::Close, &dialog);
connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
layout->addWidget(buttons);
dialog.exec();
}
void TrophyViewerDialog::ShowForGame(const Configuration* info, const QString& runtime_directory,
QWidget* parent) {
if (info == nullptr) {
@@ -139,51 +343,131 @@ bool TrophyViewerDialog::LoadGame(const Configuration& info, const QString& runt
errors.append(tr("Could not read trophy package %1.").arg(QFileInfo(file).fileName()));
continue;
}
const auto label = TrophyFilePattern.match(QFileInfo(file).fileName()).captured(1).toUInt();
const auto unlocked = Common::Trophies::LoadUnlocks(
Common::Trophies::UnlocksPath(GameContent::ToPath(runtime_directory),
info.title_id.toStdString(), info.user_id, label));
auto* table = new QTableWidget(static_cast<int>(package.trophies.size()), 4, m_tabs);
PrepareTable(table);
int row_index = 0;
for (const auto& [id, trophy]: package.trophies) {
const bool trophy_unlocked = unlocked.contains(id);
auto* status = new QTableWidgetItem(trophy_unlocked ? tr("UNLOCKED") : tr("LOCKED"));
status->setTextAlignment(Qt::AlignCenter);
auto status_font = status->font();
status_font.setBold(true);
status->setFont(status_font);
table->setItem(row_index, 0, status);
const auto unlocks = LoadUnlocks(info, runtime_directory, file);
const auto counts = Common::Trophies::GetProgress(package, unlocks);
auto* page = new QWidget(m_tabs);
auto* page_layout = new QVBoxLayout(page);
auto* icon_item = new QTableWidgetItem;
icon_item->setTextAlignment(Qt::AlignCenter);
QPixmap icon;
if (icon.loadFromData(reinterpret_cast<const uchar*>(trophy.icon_png.data()),
static_cast<uint>(trophy.icon_png.size()))) {
if (!trophy_unlocked) {
icon = QPixmap::fromImage(
icon.toImage().convertToFormat(QImage::Format_Grayscale8));
}
icon_item->setIcon(QIcon(icon));
}
table->setItem(row_index, 1, icon_item);
const auto name_text = trophy.name.empty() ? (trophy.hidden ? tr("Hidden Trophy")
: tr("Trophy %1").arg(id))
: QString::fromStdString(trophy.name);
auto* name = new QTableWidgetItem(name_text);
auto font = name->font();
font.setBold(true);
name->setFont(font);
name->setToolTip(TrophyTooltip(trophy));
auto* detail = new QTableWidgetItem(QString::fromStdString(trophy.description));
detail->setToolTip(name->toolTip());
table->setItem(row_index, 2, name);
table->setItem(row_index, 3, detail);
++row_index;
auto* header = new QHBoxLayout;
auto* art = new QLabel(page);
if (const auto pixmap = LoadGameArt(&info, 56); !pixmap.isNull()) {
art->setPixmap(pixmap);
header->addWidget(art);
}
m_tabs->addTab(table, tr("Trophy %1").arg(label));
auto* title = new QLabel(QString::fromStdString(package.title), page);
title->setStyleSheet(QStringLiteral("font-size: 28px;"));
header->addWidget(title, 1);
const auto make_stat = [page](const QString& label, const QString& value) {
auto* box = new QVBoxLayout;
auto* name = new QLabel(label, page);
auto* number = new QLabel(value, page);
name->setStyleSheet(QStringLiteral("font-size: 14px;"));
number->setStyleSheet(QStringLiteral("font-size: 22px;"));
box->addWidget(name);
box->addWidget(number);
return box;
};
header->addLayout(
make_stat(tr("Progress"), QStringLiteral("%1%").arg(counts.Percentage())));
header->addLayout(
make_stat(tr("Earned"), QStringLiteral("%1/%2").arg(counts.earned).arg(counts.total)));
for (int grade = 1; grade <= 4; ++grade) {
if (counts.total_grade[grade] > 0) {
header->addWidget(MakeGradeCount(page, grade, counts.earned_grade[grade], 30, 20));
}
}
page_layout->addLayout(header);
auto* progress = new QProgressBar(page);
progress->setRange(0, 100);
progress->setValue(counts.Percentage());
progress->setTextVisible(false);
page_layout->addWidget(progress);
auto* count_label = new QLabel(tr("All trophies: %1").arg(counts.total), page);
page_layout->addWidget(count_label);
auto* list = new QListWidget(page);
list->setSelectionMode(QAbstractItemView::NoSelection);
list->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
list->setSpacing(4);
page_layout->addWidget(list, 1);
for (const auto& [id, trophy]: package.trophies) {
const bool trophy_unlocked = unlocks.unlocked.contains(id);
const auto hidden_locked = trophy.hidden && !trophy_unlocked;
auto* item = new QListWidgetItem(list);
item->setSizeHint(QSize(900, 96));
auto* card = new QWidget(list);
card->setStyleSheet(
QStringLiteral("QWidget#card { background: palette(base); border-radius: 12px; }"));
card->setObjectName(QStringLiteral("card"));
auto* row = new QHBoxLayout(card);
auto* icon = new QLabel(card);
icon->setFixedSize(76, 76);
icon->setAlignment(Qt::AlignCenter);
QPixmap pixmap;
if (hidden_locked) {
pixmap = QPixmap(QStringLiteral(":/icons/hidden-trophy.png"));
} else if (pixmap.loadFromData(reinterpret_cast<const uchar*>(trophy.icon_png.data()),
static_cast<uint>(trophy.icon_png.size())) &&
!trophy_unlocked) {
pixmap =
QPixmap::fromImage(pixmap.toImage().convertToFormat(QImage::Format_Grayscale8));
}
if (!pixmap.isNull()) {
icon->setPixmap(
pixmap.scaled(76, 76, Qt::KeepAspectRatio, Qt::SmoothTransformation));
}
row->addWidget(icon);
auto* left = new QVBoxLayout;
const auto name_text = hidden_locked ? tr("Hidden trophy")
: trophy.name.empty() ? tr("Trophy %1").arg(id)
: QString::fromStdString(trophy.name);
auto* name = new QLabel(name_text, card);
name->setStyleSheet(QStringLiteral("font-size: 20px; font-weight: bold;"));
left->addWidget(name);
if (!hidden_locked) {
auto* grade_row = new QHBoxLayout;
auto* cup = new QLabel(card);
cup->setPixmap(MakeCupIcon(GradeColor(trophy.grade), 18));
auto* grade_text = new QLabel(GradeToText(trophy.grade), card);
grade_text->setStyleSheet(QStringLiteral("font-size: 15px;"));
grade_row->addWidget(cup);
grade_row->addWidget(grade_text);
grade_row->addStretch(1);
left->addLayout(grade_row);
}
row->addLayout(left, 2);
auto* right = new QVBoxLayout;
QString date_text;
if (const auto found = unlocks.timestamps.find(id); found != unlocks.timestamps.end()) {
date_text = QDateTime::fromMSecsSinceEpoch(
static_cast<qint64>(
(found->second - Common::Trophies::UnixEpochTick) / 1000))
.toLocalTime()
.toString(QStringLiteral("yyyy-MM-dd HH:mm:ss"));
} else if (trophy_unlocked) {
date_text = tr("Earned");
}
auto* date = new QLabel(date_text, card);
date->setAlignment(Qt::AlignRight);
date->setStyleSheet(QStringLiteral("font-size: 15px;"));
auto* detail = new QLabel(
hidden_locked ? QString {} : QString::fromStdString(trophy.description), card);
detail->setWordWrap(true);
detail->setAlignment(Qt::AlignLeft | Qt::AlignTop);
detail->setStyleSheet(QStringLiteral("font-size: 17px;"));
right->addWidget(date);
right->addWidget(detail, 1);
row->addLayout(right, 3);
card->setToolTip(hidden_locked ? name_text : TrophyTooltip(trophy));
list->setItemWidget(item, card);
}
m_tabs->addTab(page, QString::fromStdString(package.title));
}
m_tabs->tabBar()->setVisible(m_tabs->count() > 1);
if (m_tabs->count() == 0) {
error = errors.join(QLatin1Char('\n'));
return false;
+6
View File
@@ -39,9 +39,11 @@ QLineEdit#search_line_edit:focus {
border-color: palette(highlight);
}
QToolButton#refresh_button,
QToolButton#global_settings_button,
QToolButton#input_mapping_button,
QToolButton#edit_button,
QToolButton#trophy_overview_button,
QToolButton#delete_button {
background: transparent;
border: 1px solid transparent;
@@ -49,17 +51,21 @@ QToolButton#delete_button {
padding: 3px;
}
QToolButton#refresh_button:hover,
QToolButton#global_settings_button:hover,
QToolButton#input_mapping_button:hover,
QToolButton#edit_button:hover,
QToolButton#trophy_overview_button:hover,
QToolButton#delete_button:hover {
background: palette(button);
border-color: palette(mid);
}
QToolButton#refresh_button:pressed,
QToolButton#global_settings_button:pressed,
QToolButton#input_mapping_button:pressed,
QToolButton#edit_button:pressed,
QToolButton#trophy_overview_button:pressed,
QToolButton#delete_button:pressed {
background: palette(midlight);
}