Add Leaderboards tab to Achievement dialog

A new tab is added to the Achievements dialog to chart out the leaderboards in a table. Each row of the table contains the leaderboard information and up to four relevant entries, varying based on how many entries are in the leaderboard, whether or not the player has a submitted score, and where in the leaderboard the player's score is.
This commit is contained in:
LillyJadeKatrin 2023-06-17 11:47:56 -04:00 committed by Admiral H. Curtiss
parent 04df930e0d
commit b824d55093
No known key found for this signature in database
GPG Key ID: F051B4C4044F33FB
6 changed files with 164 additions and 0 deletions

View File

@ -0,0 +1,127 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef USE_RETRO_ACHIEVEMENTS
#include "DolphinQt/Achievements/AchievementLeaderboardWidget.h"
#include <QCheckBox>
#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QString>
#include <QVBoxLayout>
#include <fmt/format.h>
#include <rcheevos/include/rc_api_runtime.h>
#include <rcheevos/include/rc_api_user.h>
#include <rcheevos/include/rc_runtime.h>
#include "Common/CommonTypes.h"
#include "Core/AchievementManager.h"
#include "Core/Config/AchievementSettings.h"
#include "Core/Config/MainSettings.h"
#include "Core/Core.h"
#include "DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h"
#include "DolphinQt/QtUtils/ClearLayoutRecursively.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h"
#include "DolphinQt/QtUtils/NonDefaultQPushButton.h"
#include "DolphinQt/QtUtils/SignalBlocking.h"
#include "DolphinQt/Settings.h"
AchievementLeaderboardWidget::AchievementLeaderboardWidget(QWidget* parent) : QWidget(parent)
{
m_common_box = new QGroupBox();
m_common_layout = new QGridLayout();
{
std::lock_guard lg{*AchievementManager::GetInstance()->GetLock()};
UpdateData();
}
m_common_box->setLayout(m_common_layout);
auto* layout = new QVBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->setAlignment(Qt::AlignTop);
layout->addWidget(m_common_box);
setLayout(layout);
}
void AchievementLeaderboardWidget::UpdateData()
{
ClearLayoutRecursively(m_common_layout);
if (!AchievementManager::GetInstance()->IsGameLoaded())
return;
const auto& leaderboards = AchievementManager::GetInstance()->GetLeaderboardsInfo();
int row = 0;
for (const auto& board_row : leaderboards)
{
const AchievementManager::LeaderboardStatus& board = board_row.second;
QLabel* a_title = new QLabel(QString::fromStdString(board.name));
QLabel* a_description = new QLabel(QString::fromStdString(board.description));
QVBoxLayout* a_col_left = new QVBoxLayout();
a_col_left->addWidget(a_title);
a_col_left->addWidget(a_description);
if (row > 0)
{
QFrame* a_divider = new QFrame();
a_divider->setFrameShape(QFrame::HLine);
m_common_layout->addWidget(a_divider, row - 1, 0);
}
m_common_layout->addLayout(a_col_left, row, 0);
// Each leaderboard entry is displayed with four values. These are *generally* intended to be,
// in order, the first place entry, the entry one above the player, the player's entry, and
// the entry one below the player.
// Edge cases:
// * If there are fewer than four entries in the leaderboard, all entries will be shown in
// order and the remainder of the list will be padded with empty values.
// * If the player does not currently have a score in the leaderboard, or is in the top 3,
// the four slots will be the top four players in order.
// * If the player is last place, the player will be in the fourth slot, and the second and
// third slots will be the two players above them. The first slot will always be first place.
std::array<u32, 4> to_display{1, 2, 3, 4};
if (board.player_index > to_display.size() - 1)
{
// If the rank one below than the player is found, offset = 1.
u32 offset = static_cast<u32>(board.entries.count(board.player_index + 1));
// Example: player is 10th place but not last
// to_display = {1, 10-3+1+1, 10-3+1+2, 10-3+1+3} = {1, 9, 10, 11}
// Example: player is 15th place and is last
// to_display = {1, 15-3+0+1, 15-3+0+2, 15-3+0+3} = {1, 13, 14, 15}
for (size_t i = 1; i < to_display.size(); ++i)
to_display[i] = board.player_index - 3 + offset + static_cast<u32>(i);
}
for (size_t i = 0; i < to_display.size(); ++i)
{
u32 index = to_display[i];
QLabel* a_rank = new QLabel(QStringLiteral("---"));
QLabel* a_username = new QLabel(QStringLiteral("---"));
QLabel* a_score = new QLabel(QStringLiteral("---"));
const auto it = board.entries.find(index);
if (it != board.entries.end())
{
a_rank->setText(tr("Rank %1").arg(it->second.rank));
a_username->setText(QString::fromStdString(it->second.username));
a_score->setText(QString::fromUtf8(it->second.score.data()));
}
QVBoxLayout* a_col = new QVBoxLayout();
a_col->addWidget(a_rank);
a_col->addWidget(a_username);
a_col->addWidget(a_score);
if (row > 0)
{
QFrame* a_divider = new QFrame();
a_divider->setFrameShape(QFrame::HLine);
m_common_layout->addWidget(a_divider, row - 1, static_cast<int>(i) + 1);
}
m_common_layout->addLayout(a_col, row, static_cast<int>(i) + 1);
}
row += 2;
}
}
#endif // USE_RETRO_ACHIEVEMENTS

View File

@ -0,0 +1,24 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#ifdef USE_RETRO_ACHIEVEMENTS
#include <QWidget>
class QGroupBox;
class QGridLayout;
class AchievementLeaderboardWidget final : public QWidget
{
Q_OBJECT
public:
explicit AchievementLeaderboardWidget(QWidget* parent);
void UpdateData();
private:
QGroupBox* m_common_box;
QGridLayout* m_common_layout;
};
#endif // USE_RETRO_ACHIEVEMENTS

View File

@ -11,6 +11,7 @@
#include <QVBoxLayout>
#include "DolphinQt/Achievements/AchievementHeaderWidget.h"
#include "DolphinQt/Achievements/AchievementLeaderboardWidget.h"
#include "DolphinQt/Achievements/AchievementProgressWidget.h"
#include "DolphinQt/Achievements/AchievementSettingsWidget.h"
#include "DolphinQt/QtUtils/QueueOnObject.h"
@ -42,10 +43,14 @@ void AchievementsWindow::CreateMainLayout()
m_header_widget = new AchievementHeaderWidget(this);
m_tab_widget = new QTabWidget();
m_progress_widget = new AchievementProgressWidget(m_tab_widget);
m_leaderboard_widget = new AchievementLeaderboardWidget(m_tab_widget);
m_tab_widget->addTab(
GetWrappedWidget(new AchievementSettingsWidget(m_tab_widget, this), this, 125, 100),
tr("Settings"));
m_tab_widget->addTab(GetWrappedWidget(m_progress_widget, this, 125, 100), tr("Progress"));
m_tab_widget->setTabVisible(1, AchievementManager::GetInstance()->IsGameLoaded());
m_tab_widget->addTab(GetWrappedWidget(m_leaderboard_widget, this, 125, 100), tr("Leaderboards"));
m_tab_widget->setTabVisible(2, AchievementManager::GetInstance()->IsGameLoaded());
m_button_box = new QDialogButtonBox(QDialogButtonBox::Close);
@ -70,6 +75,8 @@ void AchievementsWindow::UpdateData()
// Settings tab handles its own updates ... indeed, that calls this
m_progress_widget->UpdateData();
m_tab_widget->setTabVisible(1, AchievementManager::GetInstance()->IsGameLoaded());
m_leaderboard_widget->UpdateData();
m_tab_widget->setTabVisible(2, AchievementManager::GetInstance()->IsGameLoaded());
}
update();
}

View File

@ -10,6 +10,7 @@
#include "DolphinQt/QtUtils/QueueOnObject.h"
class AchievementHeaderWidget;
class AchievementLeaderboardWidget;
class AchievementProgressWidget;
class QDialogButtonBox;
class QTabWidget;
@ -30,6 +31,7 @@ private:
AchievementHeaderWidget* m_header_widget;
QTabWidget* m_tab_widget;
AchievementProgressWidget* m_progress_widget;
AchievementLeaderboardWidget* m_leaderboard_widget;
QDialogButtonBox* m_button_box;
};

View File

@ -30,6 +30,8 @@ add_executable(dolphin-emu
CheatsManager.h
Achievements/AchievementHeaderWidget.cpp
Achievements/AchievementHeaderWidget.h
Achievements/AchievementLeaderboardWidget.cpp
Achievements/AchievementLeaderboardWidget.h
Achievements/AchievementProgressWidget.cpp
Achievements/AchievementProgressWidget.h
Achievements/AchievementSettingsWidget.cpp

View File

@ -51,6 +51,7 @@
<ClCompile Include="CheatSearchWidget.cpp" />
<ClCompile Include="CheatsManager.cpp" />
<ClCompile Include="Achievements\AchievementHeaderWidget.cpp" />
<ClCompile Include="Achievements\AchievementLeaderboardWidget.cpp" />
<ClCompile Include="Achievements\AchievementProgressWidget.cpp" />
<ClCompile Include="Achievements\AchievementSettingsWidget.cpp" />
<ClCompile Include="Achievements\AchievementsWindow.cpp" />
@ -261,6 +262,7 @@
<QtMoc Include="CheatSearchWidget.h" />
<QtMoc Include="CheatsManager.h" />
<QtMoc Include="Achievements\AchievementHeaderWidget.h" />
<QtMoc Include="Achievements\AchievementLeaderboardWidget.h" />
<QtMoc Include="Achievements\AchievementProgressWidget.h" />
<QtMoc Include="Achievements\AchievementSettingsWidget.h" />
<QtMoc Include="Achievements\AchievementsWindow.h" />