diff --git a/Source/Core/DolphinQt/Achievements/AchievementLeaderboardWidget.cpp b/Source/Core/DolphinQt/Achievements/AchievementLeaderboardWidget.cpp new file mode 100644 index 0000000000..243bc4453c --- /dev/null +++ b/Source/Core/DolphinQt/Achievements/AchievementLeaderboardWidget.cpp @@ -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 +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#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 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(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(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(i) + 1); + } + m_common_layout->addLayout(a_col, row, static_cast(i) + 1); + } + row += 2; + } +} + +#endif // USE_RETRO_ACHIEVEMENTS diff --git a/Source/Core/DolphinQt/Achievements/AchievementLeaderboardWidget.h b/Source/Core/DolphinQt/Achievements/AchievementLeaderboardWidget.h new file mode 100644 index 0000000000..055ea6ab3f --- /dev/null +++ b/Source/Core/DolphinQt/Achievements/AchievementLeaderboardWidget.h @@ -0,0 +1,24 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#ifdef USE_RETRO_ACHIEVEMENTS +#include + +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 diff --git a/Source/Core/DolphinQt/Achievements/AchievementsWindow.cpp b/Source/Core/DolphinQt/Achievements/AchievementsWindow.cpp index d376da6109..8d01663c87 100644 --- a/Source/Core/DolphinQt/Achievements/AchievementsWindow.cpp +++ b/Source/Core/DolphinQt/Achievements/AchievementsWindow.cpp @@ -11,6 +11,7 @@ #include #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(); } diff --git a/Source/Core/DolphinQt/Achievements/AchievementsWindow.h b/Source/Core/DolphinQt/Achievements/AchievementsWindow.h index d8407a3a17..6fd7165e1f 100644 --- a/Source/Core/DolphinQt/Achievements/AchievementsWindow.h +++ b/Source/Core/DolphinQt/Achievements/AchievementsWindow.h @@ -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; }; diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt index 87fc14c766..b271fab7fb 100644 --- a/Source/Core/DolphinQt/CMakeLists.txt +++ b/Source/Core/DolphinQt/CMakeLists.txt @@ -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 diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj index f73ea5c1cc..35f1110e4c 100644 --- a/Source/Core/DolphinQt/DolphinQt.vcxproj +++ b/Source/Core/DolphinQt/DolphinQt.vcxproj @@ -51,6 +51,7 @@ + @@ -261,6 +262,7 @@ +