Refactor Leaderboard widget to allow partial updates

Similarly to the Progress widget (though without the separate object for each box, because each Leaderboard unit is just three text fields stacked vertically), AchievementLeaderboardWidget.UpdateData will now accept three options: destroy all and rebuild, update all, or update a set of rows.

As part of this, AchievementManager::GetLeaderboardsInfo has been refactored to GetLeaderboardInfo to return a single leaderboard for the ID passed in.
This commit is contained in:
LillyJadeKatrin 2024-03-09 16:46:41 -05:00
parent d2069e888d
commit c57be0efca
5 changed files with 118 additions and 70 deletions

View File

@ -358,10 +358,12 @@ AchievementManager::GetAchievementProgress(AchievementId achievement_id, u32* va
return ResponseType::SUCCESS; return ResponseType::SUCCESS;
} }
const std::unordered_map<AchievementManager::AchievementId, AchievementManager::LeaderboardStatus>& const AchievementManager::LeaderboardStatus*
AchievementManager::GetLeaderboardsInfo() const AchievementManager::GetLeaderboardInfo(AchievementManager::AchievementId leaderboard_id) const
{ {
return m_leaderboard_map; if (m_leaderboard_map.count(leaderboard_id) < 1)
return nullptr;
return &m_leaderboard_map.at(leaderboard_id);
} }
AchievementManager::RichPresence AchievementManager::GetRichPresence() const AchievementManager::RichPresence AchievementManager::GetRichPresence() const

View File

@ -143,7 +143,7 @@ public:
const UnlockStatus* GetUnlockStatus(AchievementId achievement_id) const; const UnlockStatus* GetUnlockStatus(AchievementId achievement_id) const;
AchievementManager::ResponseType GetAchievementProgress(AchievementId achievement_id, u32* value, AchievementManager::ResponseType GetAchievementProgress(AchievementId achievement_id, u32* value,
u32* target); u32* target);
const std::unordered_map<AchievementId, LeaderboardStatus>& GetLeaderboardsInfo() const; const LeaderboardStatus* GetLeaderboardInfo(AchievementId leaderboard_id) const;
RichPresence GetRichPresence() const; RichPresence GetRichPresence() const;
bool IsDisabled() const { return m_disabled; }; bool IsDisabled() const { return m_disabled; };
void SetDisabled(bool disabled); void SetDisabled(bool disabled);

View File

@ -24,10 +24,7 @@ AchievementLeaderboardWidget::AchievementLeaderboardWidget(QWidget* parent) : QW
m_common_box = new QGroupBox(); m_common_box = new QGroupBox();
m_common_layout = new QGridLayout(); m_common_layout = new QGridLayout();
{ UpdateData(true);
std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
UpdateData();
}
m_common_box->setLayout(m_common_layout); m_common_box->setLayout(m_common_layout);
@ -38,19 +35,27 @@ AchievementLeaderboardWidget::AchievementLeaderboardWidget(QWidget* parent) : QW
setLayout(layout); setLayout(layout);
} }
void AchievementLeaderboardWidget::UpdateData() void AchievementLeaderboardWidget::UpdateData(bool clean_all)
{ {
if (clean_all)
{
ClearLayoutRecursively(m_common_layout); ClearLayoutRecursively(m_common_layout);
if (!AchievementManager::GetInstance().IsGameLoaded()) auto& instance = AchievementManager::GetInstance();
if (!instance.IsGameLoaded())
return; return;
const auto& leaderboards = AchievementManager::GetInstance().GetLeaderboardsInfo();
int row = 0; rc_api_fetch_game_data_response_t* game_data;
for (const auto& board_row : leaderboards)
{ {
const AchievementManager::LeaderboardStatus& board = board_row.second; std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
QLabel* a_title = new QLabel(QString::fromStdString(board.name)); game_data = instance.GetGameData();
QLabel* a_description = new QLabel(QString::fromStdString(board.description)); }
for (u32 row = 0; row < game_data->num_leaderboards; row += 2)
{
const auto* leaderboard = game_data->leaderboards + (row / 2);
m_leaderboard_order[leaderboard->id] = row;
QLabel* a_title = new QLabel(QString::fromUtf8(leaderboard->title));
QLabel* a_description = new QLabel(QString::fromUtf8(leaderboard->description));
QVBoxLayout* a_col_left = new QVBoxLayout(); QVBoxLayout* a_col_left = new QVBoxLayout();
a_col_left->addWidget(a_title); a_col_left->addWidget(a_title);
a_col_left->addWidget(a_description); a_col_left->addWidget(a_description);
@ -61,6 +66,54 @@ void AchievementLeaderboardWidget::UpdateData()
m_common_layout->addWidget(a_divider, row - 1, 0); m_common_layout->addWidget(a_divider, row - 1, 0);
} }
m_common_layout->addLayout(a_col_left, row, 0); m_common_layout->addLayout(a_col_left, row, 0);
for (size_t ix = 0; ix < 4; ix++)
{
QVBoxLayout* a_col = new QVBoxLayout();
for (size_t jx = 0; jx < 3; jx++)
a_col->addWidget(new QLabel(QStringLiteral("---")));
if (row > 0)
{
QFrame* a_divider = new QFrame();
a_divider->setFrameShape(QFrame::HLine);
m_common_layout->addWidget(a_divider, row - 1, static_cast<int>(ix) + 1);
}
m_common_layout->addLayout(a_col, row, static_cast<int>(ix) + 1);
}
}
}
for (auto row : m_leaderboard_order)
{
UpdateRow(row.second);
}
}
void AchievementLeaderboardWidget::UpdateData(
const std::set<AchievementManager::AchievementId>& update_ids)
{
for (auto row : m_leaderboard_order)
{
if (update_ids.contains(row.first))
{
UpdateRow(row.second);
}
}
}
void AchievementLeaderboardWidget::UpdateRow(AchievementManager::AchievementId leaderboard_id)
{
const auto leaderboard_itr = m_leaderboard_order.find(leaderboard_id);
if (leaderboard_itr == m_leaderboard_order.end())
return;
const int row = leaderboard_itr->second;
const AchievementManager::LeaderboardStatus* board;
{
std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
board = AchievementManager::GetInstance().GetLeaderboardInfo(leaderboard_id);
}
if (!board)
return;
// Each leaderboard entry is displayed with four values. These are *generally* intended to be, // 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 // in order, the first place entry, the entry one above the player, the player's entry, and
// the entry one below the player. // the entry one below the player.
@ -72,43 +125,31 @@ void AchievementLeaderboardWidget::UpdateData()
// * If the player is last place, the player will be in the fourth slot, and the second and // * 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. // 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}; std::array<u32, 4> to_display{1, 2, 3, 4};
if (board.player_index > to_display.size() - 1) if (board->player_index > to_display.size() - 1)
{ {
// If the rank one below than the player is found, offset = 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)); u32 offset = static_cast<u32>(board->entries.count(board->player_index + 1));
// Example: player is 10th place but not last // 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} // 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 // 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} // 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) for (size_t ix = 1; ix < to_display.size(); ++ix)
to_display[i] = board.player_index - 3 + offset + static_cast<u32>(i); to_display[ix] = board->player_index - 3 + offset + static_cast<u32>(ix);
} }
for (size_t i = 0; i < to_display.size(); ++i) for (size_t ix = 0; ix < to_display.size(); ++ix)
{ {
u32 index = to_display[i]; const auto it = board->entries.find(to_display[ix]);
QLabel* a_rank = new QLabel(QStringLiteral("---")); if (it != board->entries.end())
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(); QVBoxLayout* a_col = new QVBoxLayout();
a_col->addWidget(a_rank); a_col->addWidget(new QLabel(tr("Rank %1").arg(it->second.rank)));
a_col->addWidget(a_username); a_col->addWidget(new QLabel(QString::fromStdString(it->second.username)));
a_col->addWidget(a_score); a_col->addWidget(new QLabel(QString::fromUtf8(it->second.score.data())));
if (row > 0) auto old_item = m_common_layout->itemAtPosition(row, static_cast<int>(ix) + 1);
{ m_common_layout->removeItem(old_item);
QFrame* a_divider = new QFrame(); ClearLayoutRecursively(static_cast<QLayout*>(old_item));
a_divider->setFrameShape(QFrame::HLine); m_common_layout->addLayout(a_col, row, static_cast<int>(ix) + 1);
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;
} }
} }

View File

@ -6,6 +6,8 @@
#ifdef USE_RETRO_ACHIEVEMENTS #ifdef USE_RETRO_ACHIEVEMENTS
#include <QWidget> #include <QWidget>
#include "Core/AchievementManager.h"
class QGroupBox; class QGroupBox;
class QGridLayout; class QGridLayout;
@ -14,11 +16,14 @@ class AchievementLeaderboardWidget final : public QWidget
Q_OBJECT Q_OBJECT
public: public:
explicit AchievementLeaderboardWidget(QWidget* parent); explicit AchievementLeaderboardWidget(QWidget* parent);
void UpdateData(); void UpdateData(bool clean_all);
void UpdateData(const std::set<AchievementManager::AchievementId>& update_ids);
void UpdateRow(AchievementManager::AchievementId leaderboard_id);
private: private:
QGroupBox* m_common_box; QGroupBox* m_common_box;
QGridLayout* m_common_layout; QGridLayout* m_common_layout;
std::map<AchievementManager::AchievementId, int> m_leaderboard_order;
}; };
#endif // USE_RETRO_ACHIEVEMENTS #endif // USE_RETRO_ACHIEVEMENTS

View File

@ -83,7 +83,7 @@ void AchievementsWindow::UpdateData()
m_settings_widget->UpdateData(); m_settings_widget->UpdateData();
m_progress_widget->UpdateData(true); m_progress_widget->UpdateData(true);
m_tab_widget->setTabVisible(1, is_game_loaded); m_tab_widget->setTabVisible(1, is_game_loaded);
m_leaderboard_widget->UpdateData(); m_leaderboard_widget->UpdateData(true);
m_tab_widget->setTabVisible(2, is_game_loaded); m_tab_widget->setTabVisible(2, is_game_loaded);
} }
update(); update();