mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-14 21:37:52 -07:00
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:
parent
04df930e0d
commit
b824d55093
@ -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
|
@ -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
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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" />
|
||||
|
Loading…
Reference in New Issue
Block a user