diff --git a/Source/Core/Core/AchievementManager.cpp b/Source/Core/Core/AchievementManager.cpp index 1a05af90bc..ef23dcf163 100644 --- a/Source/Core/Core/AchievementManager.cpp +++ b/Source/Core/Core/AchievementManager.cpp @@ -38,11 +38,24 @@ void AchievementManager::Init() } } +void AchievementManager::SetUpdateCallback(UpdateCallback callback) +{ + m_update_callback = std::move(callback); + m_update_callback(); +} + AchievementManager::ResponseType AchievementManager::Login(const std::string& password) { if (!m_is_runtime_initialized) return AchievementManager::ResponseType::MANAGER_NOT_INITIALIZED; - return VerifyCredentials(password); + AchievementManager::ResponseType r_type = AchievementManager::ResponseType::UNKNOWN_FAILURE; + { + std::lock_guard lg{m_lock}; + r_type = VerifyCredentials(password); + } + if (m_update_callback) + m_update_callback(); + return r_type; } void AchievementManager::LoginAsync(const std::string& password, const ResponseCallback& callback) @@ -52,7 +65,14 @@ void AchievementManager::LoginAsync(const std::string& password, const ResponseC callback(AchievementManager::ResponseType::MANAGER_NOT_INITIALIZED); return; } - m_queue.EmplaceItem([this, password, callback] { callback(VerifyCredentials(password)); }); + m_queue.EmplaceItem([this, password, callback] { + { + std::lock_guard lg{m_lock}; + callback(VerifyCredentials(password)); + } + if (m_update_callback) + m_update_callback(); + }); } bool AchievementManager::IsLoggedIn() const @@ -141,11 +161,11 @@ void AchievementManager::LoadGameByFilenameAsync(const std::string& iso_path, } const auto fetch_game_data_response = FetchGameData(); - m_is_game_loaded = fetch_game_data_response == ResponseType::SUCCESS; - if (!m_is_game_loaded) + if (fetch_game_data_response != ResponseType::SUCCESS) { OSD::AddMessage("Unable to retrieve data from RetroAchievements server.", OSD::Duration::VERY_LONG, OSD::Color::RED); + return; } // Claim the lock, then queue the fetch unlock data calls, then initialize the unlock map in @@ -154,6 +174,7 @@ void AchievementManager::LoadGameByFilenameAsync(const std::string& iso_path, // it. { std::lock_guard lg{m_lock}; + m_is_game_loaded = true; LoadUnlockData([](ResponseType r_type) {}); ActivateDeactivateAchievements(); PointSpread spread = TallyScore(); @@ -179,10 +200,17 @@ void AchievementManager::LoadGameByFilenameAsync(const std::string& iso_path, // Reset this to zero so that RP immediately triggers on the first frame m_last_ping_time = 0; + if (m_update_callback) + m_update_callback(); callback(fetch_game_data_response); }); } +bool AchievementManager::IsGameLoaded() const +{ + return m_is_game_loaded; +} + void AchievementManager::LoadUnlockData(const ResponseCallback& callback) { m_queue.EmplaceItem([this, callback] { @@ -194,6 +222,8 @@ void AchievementManager::LoadUnlockData(const ResponseCallback& callback) } callback(FetchUnlockData(false)); + if (m_update_callback) + m_update_callback(); }); } @@ -296,39 +326,114 @@ u32 AchievementManager::MemoryPeeker(u32 address, u32 num_bytes, void* ud) void AchievementManager::AchievementEventHandler(const rc_runtime_event_t* runtime_event) { - switch (runtime_event->type) { - case RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED: - HandleAchievementTriggeredEvent(runtime_event); - break; - case RC_RUNTIME_EVENT_LBOARD_STARTED: - HandleLeaderboardStartedEvent(runtime_event); - break; - case RC_RUNTIME_EVENT_LBOARD_CANCELED: - HandleLeaderboardCanceledEvent(runtime_event); - break; - case RC_RUNTIME_EVENT_LBOARD_TRIGGERED: - HandleLeaderboardTriggeredEvent(runtime_event); - break; + std::lock_guard lg{m_lock}; + switch (runtime_event->type) + { + case RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED: + HandleAchievementTriggeredEvent(runtime_event); + break; + case RC_RUNTIME_EVENT_LBOARD_STARTED: + HandleLeaderboardStartedEvent(runtime_event); + break; + case RC_RUNTIME_EVENT_LBOARD_CANCELED: + HandleLeaderboardCanceledEvent(runtime_event); + break; + case RC_RUNTIME_EVENT_LBOARD_TRIGGERED: + HandleLeaderboardTriggeredEvent(runtime_event); + break; + } } + if (m_update_callback) + m_update_callback(); +} + +std::recursive_mutex* AchievementManager::GetLock() +{ + return &m_lock; +} + +std::string AchievementManager::GetPlayerDisplayName() const +{ + return IsLoggedIn() ? m_display_name : ""; +} + +u32 AchievementManager::GetPlayerScore() const +{ + return IsLoggedIn() ? m_player_score : 0; +} + +std::string AchievementManager::GetGameDisplayName() const +{ + return IsGameLoaded() ? m_game_data.title : ""; +} + +AchievementManager::PointSpread AchievementManager::TallyScore() const +{ + PointSpread spread{}; + if (!IsGameLoaded()) + return spread; + for (const auto& entry : m_unlock_map) + { + u32 points = entry.second.points; + spread.total_count++; + spread.total_points += points; + if (entry.second.remote_unlock_status == UnlockStatus::UnlockType::HARDCORE || + (hardcore_mode_enabled && entry.second.session_unlock_count > 0)) + { + spread.hard_unlocks++; + spread.hard_points += points; + } + else if (entry.second.remote_unlock_status == UnlockStatus::UnlockType::SOFTCORE || + entry.second.session_unlock_count > 0) + { + spread.soft_unlocks++; + spread.soft_points += points; + } + } + return spread; +} + +rc_api_fetch_game_data_response_t* AchievementManager::GetGameData() +{ + return &m_game_data; +} + +AchievementManager::UnlockStatus +AchievementManager::GetUnlockStatus(AchievementId achievement_id) const +{ + return m_unlock_map.at(achievement_id); +} + +void AchievementManager::GetAchievementProgress(AchievementId achievement_id, u32* value, + u32* target) +{ + rc_runtime_get_achievement_measured(&m_runtime, achievement_id, value, target); } void AchievementManager::CloseGame() { - m_is_game_loaded = false; - m_game_id = 0; - m_queue.Cancel(); - m_unlock_map.clear(); - m_system = nullptr; - ActivateDeactivateAchievements(); - ActivateDeactivateLeaderboards(); - ActivateDeactivateRichPresence(); + { + std::lock_guard lg{m_lock}; + m_is_game_loaded = false; + m_game_id = 0; + m_queue.Cancel(); + m_unlock_map.clear(); + m_system = nullptr; + ActivateDeactivateAchievements(); + ActivateDeactivateLeaderboards(); + ActivateDeactivateRichPresence(); + } + if (m_update_callback) + m_update_callback(); } void AchievementManager::Logout() { CloseGame(); Config::SetBaseOrCurrent(Config::RA_API_TOKEN, ""); + if (m_update_callback) + m_update_callback(); } void AchievementManager::Shutdown() @@ -353,6 +458,7 @@ AchievementManager::ResponseType AchievementManager::VerifyCredentials(const std { Config::SetBaseOrCurrent(Config::RA_API_TOKEN, login_data.api_token); m_display_name = login_data.display_name; + m_player_score = login_data.score; } rc_api_destroy_login_response(&login_data); return r_type; @@ -618,30 +724,6 @@ void AchievementManager::HandleLeaderboardTriggeredEvent(const rc_runtime_event_ } } -AchievementManager::PointSpread AchievementManager::TallyScore() const -{ - PointSpread spread{}; - for (const auto& entry : m_unlock_map) - { - u32 points = entry.second.points; - spread.total_count++; - spread.total_points += points; - if (entry.second.remote_unlock_status == UnlockStatus::UnlockType::HARDCORE || - (hardcore_mode_enabled && entry.second.session_unlock_count > 0)) - { - spread.hard_unlocks++; - spread.hard_points += points; - } - else if (entry.second.remote_unlock_status == UnlockStatus::UnlockType::SOFTCORE || - entry.second.session_unlock_count > 0) - { - spread.soft_unlocks++; - spread.soft_points += points; - } - } - return spread; -} - // Every RetroAchievements API call, with only a partial exception for fetch_image, follows // the same design pattern (here, X is the name of the call): // Create a specific rc_api_X_request_t struct and populate with the necessary values diff --git a/Source/Core/Core/AchievementManager.h b/Source/Core/Core/AchievementManager.h index f35cd5f659..14b6b0ec77 100644 --- a/Source/Core/Core/AchievementManager.h +++ b/Source/Core/Core/AchievementManager.h @@ -41,6 +41,7 @@ public: UNKNOWN_FAILURE }; using ResponseCallback = std::function; + using UpdateCallback = std::function; struct PointSpread { @@ -52,12 +53,27 @@ public: u32 soft_points; }; + struct UnlockStatus + { + AchievementId game_data_index = 0; + enum class UnlockType + { + LOCKED, + SOFTCORE, + HARDCORE + } remote_unlock_status = UnlockType::LOCKED; + u32 session_unlock_count = 0; + u32 points = 0; + }; + static AchievementManager* GetInstance(); void Init(); + void SetUpdateCallback(UpdateCallback callback); ResponseType Login(const std::string& password); void LoginAsync(const std::string& password, const ResponseCallback& callback); bool IsLoggedIn() const; void LoadGameByFilenameAsync(const std::string& iso_path, const ResponseCallback& callback); + bool IsGameLoaded() const; void LoadUnlockData(const ResponseCallback& callback); void ActivateDeactivateAchievements(); @@ -68,6 +84,15 @@ public: u32 MemoryPeeker(u32 address, u32 num_bytes, void* ud); void AchievementEventHandler(const rc_runtime_event_t* runtime_event); + std::recursive_mutex* GetLock(); + std::string GetPlayerDisplayName() const; + u32 GetPlayerScore() const; + std::string GetGameDisplayName() const; + PointSpread TallyScore() const; + rc_api_fetch_game_data_response_t* GetGameData(); + UnlockStatus GetUnlockStatus(AchievementId achievement_id) const; + void GetAchievementProgress(AchievementId achievement_id, u32* value, u32* target); + void CloseGame(); void Logout(); void Shutdown(); @@ -95,8 +120,6 @@ private: void HandleLeaderboardCanceledEvent(const rc_runtime_event_t* runtime_event); void HandleLeaderboardTriggeredEvent(const rc_runtime_event_t* runtime_event); - PointSpread TallyScore() const; - template ResponseType Request(RcRequest rc_request, RcResponse* rc_response, const std::function& init_request, @@ -105,25 +128,15 @@ private: rc_runtime_t m_runtime{}; Core::System* m_system{}; bool m_is_runtime_initialized = false; + UpdateCallback m_update_callback; std::string m_display_name; + u32 m_player_score = 0; std::array m_game_hash{}; u32 m_game_id = 0; rc_api_fetch_game_data_response_t m_game_data{}; bool m_is_game_loaded = false; time_t m_last_ping_time = 0; - struct UnlockStatus - { - AchievementId game_data_index = 0; - enum class UnlockType - { - LOCKED, - SOFTCORE, - HARDCORE - } remote_unlock_status = UnlockType::LOCKED; - u32 session_unlock_count = 0; - u32 points = 0; - }; std::unordered_map m_unlock_map; Common::WorkQueueThread> m_queue; diff --git a/Source/Core/DolphinQt/Achievements/AchievementHeaderWidget.cpp b/Source/Core/DolphinQt/Achievements/AchievementHeaderWidget.cpp new file mode 100644 index 0000000000..4dc3e66538 --- /dev/null +++ b/Source/Core/DolphinQt/Achievements/AchievementHeaderWidget.cpp @@ -0,0 +1,140 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifdef USE_RETRO_ACHIEVEMENTS +#include "DolphinQt/Achievements/AchievementHeaderWidget.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "Core/AchievementManager.h" +#include "Core/Core.h" + +#include "DolphinQt/QtUtils/ModalMessageBox.h" +#include "DolphinQt/QtUtils/NonDefaultQPushButton.h" +#include "DolphinQt/QtUtils/SignalBlocking.h" +#include "DolphinQt/Settings.h" + +AchievementHeaderWidget::AchievementHeaderWidget(QWidget* parent) : QWidget(parent) +{ + m_user_name = new QLabel(); + m_user_points = new QLabel(); + m_game_name = new QLabel(); + m_game_points = new QLabel(); + m_game_progress_hard = new QProgressBar(); + m_game_progress_soft = new QProgressBar(); + m_rich_presence = new QLabel(); + + QVBoxLayout* m_user_right_col = new QVBoxLayout(); + m_user_right_col->addWidget(m_user_name); + m_user_right_col->addWidget(m_user_points); + QHBoxLayout* m_user_layout = new QHBoxLayout(); + // TODO: player badge goes here + m_user_layout->addLayout(m_user_right_col); + m_user_box = new QGroupBox(); + m_user_box->setLayout(m_user_layout); + + QVBoxLayout* m_game_right_col = new QVBoxLayout(); + m_game_right_col->addWidget(m_game_name); + m_game_right_col->addWidget(m_game_points); + m_game_right_col->addWidget(m_game_progress_hard); + m_game_right_col->addWidget(m_game_progress_soft); + QHBoxLayout* m_game_upper_row = new QHBoxLayout(); + // TODO: player badge and game badge go here + m_game_upper_row->addLayout(m_game_right_col); + QVBoxLayout* m_game_layout = new QVBoxLayout(); + m_game_layout->addLayout(m_game_upper_row); + m_game_layout->addWidget(m_rich_presence); + m_game_box = new QGroupBox(); + m_game_box->setLayout(m_game_layout); + + QVBoxLayout* m_total = new QVBoxLayout(); + m_total->addWidget(m_user_box); + m_total->addWidget(m_game_box); + + UpdateData(); + + m_total->setContentsMargins(0, 0, 0, 0); + m_total->setAlignment(Qt::AlignTop); + setLayout(m_total); +} + +void AchievementHeaderWidget::UpdateData() +{ + if (!AchievementManager::GetInstance()->IsLoggedIn()) + { + m_user_box->setVisible(false); + m_game_box->setVisible(false); + return; + } + + QString user_name = + QString::fromStdString(AchievementManager::GetInstance()->GetPlayerDisplayName()); + m_user_name->setText(user_name); + m_user_points->setText(tr("%1 points").arg(AchievementManager::GetInstance()->GetPlayerScore())); + + if (!AchievementManager::GetInstance()->IsGameLoaded()) + { + m_user_box->setVisible(true); + m_game_box->setVisible(false); + return; + } + + AchievementManager::PointSpread point_spread = AchievementManager::GetInstance()->TallyScore(); + m_game_name->setText( + QString::fromStdString(AchievementManager::GetInstance()->GetGameDisplayName())); + m_game_points->setText(GetPointsString(user_name, point_spread)); + m_game_progress_hard = new QProgressBar(); + m_game_progress_hard->setRange(0, point_spread.total_count); + m_game_progress_soft->setValue(point_spread.hard_unlocks); + m_game_progress_soft->setRange(0, point_spread.total_count); + m_game_progress_soft->setValue(point_spread.hard_unlocks + point_spread.soft_unlocks); + // TODO: RP needs a minor refactor to work here, will be a future PR + // m_rich_presence->setText(QString::fromStdString(AchievementManager::GetInstance()->GenerateRichPresence())); + // m_rich_presence->setVisible(Config::Get(Config::RA_RICH_PRESENCE_ENABLED)); + m_rich_presence->setText(QString{}); + m_rich_presence->setVisible(false); + + m_user_box->setVisible(false); + m_game_box->setVisible(true); +} + +QString +AchievementHeaderWidget::GetPointsString(const QString& user_name, + const AchievementManager::PointSpread& point_spread) const +{ + if (point_spread.soft_points > 0) + { + return tr("%1 has unlocked %2/%3 achievements (%4 hardcore) worth %5/%6 points (%7 hardcore)") + .arg(user_name) + .arg(point_spread.hard_unlocks + point_spread.soft_unlocks) + .arg(point_spread.total_count) + .arg(point_spread.hard_unlocks) + .arg(point_spread.hard_points + point_spread.soft_points) + .arg(point_spread.total_points) + .arg(point_spread.hard_points); + } + else + { + return tr("%1 has unlocked %2/%3 achievements worth %4/%5 points") + .arg(user_name) + .arg(point_spread.hard_unlocks) + .arg(point_spread.total_count) + .arg(point_spread.hard_points) + .arg(point_spread.total_points); + } +} + +#endif // USE_RETRO_ACHIEVEMENTS diff --git a/Source/Core/DolphinQt/Achievements/AchievementHeaderWidget.h b/Source/Core/DolphinQt/Achievements/AchievementHeaderWidget.h new file mode 100644 index 0000000000..b99457f2f1 --- /dev/null +++ b/Source/Core/DolphinQt/Achievements/AchievementHeaderWidget.h @@ -0,0 +1,42 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#ifdef USE_RETRO_ACHIEVEMENTS +#include + +#include "Core/AchievementManager.h" + +class QGroupBox; +class QLabel; +class QProgressBar; +class QVBoxLayout; + +class AchievementHeaderWidget final : public QWidget +{ + Q_OBJECT +public: + explicit AchievementHeaderWidget(QWidget* parent); + void UpdateData(); + +private: + QString GetPointsString(const QString& user_name, + const AchievementManager::PointSpread& point_spread) const; + + QGroupBox* m_common_box; + QVBoxLayout* m_common_layout; + + QLabel* m_user_name; + QLabel* m_user_points; + QLabel* m_game_name; + QLabel* m_game_points; + QProgressBar* m_game_progress_hard; + QProgressBar* m_game_progress_soft; + QLabel* m_rich_presence; + + QGroupBox* m_user_box; + QGroupBox* m_game_box; +}; + +#endif // USE_RETRO_ACHIEVEMENTS diff --git a/Source/Core/DolphinQt/Achievements/AchievementProgressWidget.cpp b/Source/Core/DolphinQt/Achievements/AchievementProgressWidget.cpp new file mode 100644 index 0000000000..9f8c38bfe5 --- /dev/null +++ b/Source/Core/DolphinQt/Achievements/AchievementProgressWidget.cpp @@ -0,0 +1,124 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifdef USE_RETRO_ACHIEVEMENTS +#include "DolphinQt/Achievements/AchievementProgressWidget.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#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/ModalMessageBox.h" +#include "DolphinQt/QtUtils/NonDefaultQPushButton.h" +#include "DolphinQt/QtUtils/SignalBlocking.h" +#include "DolphinQt/Settings.h" + +AchievementProgressWidget::AchievementProgressWidget(QWidget* parent) : QWidget(parent) +{ + m_common_box = new QGroupBox(); + m_common_layout = new QVBoxLayout(); + + 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); +} + +QGroupBox* +AchievementProgressWidget::CreateAchievementBox(const rc_api_achievement_definition_t* achievement) +{ + QLabel* a_title = new QLabel(QString::fromUtf8(achievement->title, strlen(achievement->title))); + QLabel* a_description = + new QLabel(QString::fromUtf8(achievement->description, strlen(achievement->description))); + QLabel* a_points = new QLabel(tr("%1 points").arg(achievement->points)); + QLabel* a_status = new QLabel(GetStatusString(achievement->id)); + QProgressBar* a_progress_bar = new QProgressBar(); + unsigned int value = 0; + unsigned int target = 0; + AchievementManager::GetInstance()->GetAchievementProgress(achievement->id, &value, &target); + if (target > 0) + { + a_progress_bar->setRange(0, target); + a_progress_bar->setValue(value); + } + else + { + a_progress_bar->setVisible(false); + } + + QVBoxLayout* a_col_right = new QVBoxLayout(); + a_col_right->addWidget(a_title); + a_col_right->addWidget(a_description); + a_col_right->addWidget(a_points); + a_col_right->addWidget(a_status); + a_col_right->addWidget(a_progress_bar); + QHBoxLayout* a_total = new QHBoxLayout(); + // TODO: achievement badge goes here + a_total->addLayout(a_col_right); + QGroupBox* a_group_box = new QGroupBox(); + a_group_box->setLayout(a_total); + return a_group_box; +} + +void AchievementProgressWidget::UpdateData() +{ + QLayoutItem* item; + while ((item = m_common_layout->layout()->takeAt(0)) != nullptr) + { + delete item->widget(); + delete item; + } + + const auto* game_data = AchievementManager::GetInstance()->GetGameData(); + for (u32 ix = 0; ix < game_data->num_achievements; ix++) + { + m_common_layout->addWidget(CreateAchievementBox(game_data->achievements + ix)); + } +} + +QString AchievementProgressWidget::GetStatusString(u32 achievement_id) const +{ + const auto unlock_status = AchievementManager::GetInstance()->GetUnlockStatus(achievement_id); + if (unlock_status.session_unlock_count > 0) + { + if (Config::Get(Config::RA_ENCORE_ENABLED)) + { + return tr("Unlocked %1 times this session").arg(unlock_status.session_unlock_count); + } + return tr("Unlocked this session"); + } + switch (unlock_status.remote_unlock_status) + { + case AchievementManager::UnlockStatus::UnlockType::LOCKED: + return tr("Locked"); + case AchievementManager::UnlockStatus::UnlockType::SOFTCORE: + return tr("Unlocked (Casual)"); + case AchievementManager::UnlockStatus::UnlockType::HARDCORE: + return tr("Unlocked"); + } + return {}; +} + +#endif // USE_RETRO_ACHIEVEMENTS diff --git a/Source/Core/DolphinQt/Achievements/AchievementProgressWidget.h b/Source/Core/DolphinQt/Achievements/AchievementProgressWidget.h new file mode 100644 index 0000000000..b1e09e40d8 --- /dev/null +++ b/Source/Core/DolphinQt/Achievements/AchievementProgressWidget.h @@ -0,0 +1,34 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#ifdef USE_RETRO_ACHIEVEMENTS +#include + +#include "Common/CommonTypes.h" + +class QCheckBox; +class QGroupBox; +class QLineEdit; +class QPushButton; +class QVBoxLayout; + +struct rc_api_achievement_definition_t; + +class AchievementProgressWidget final : public QWidget +{ + Q_OBJECT +public: + explicit AchievementProgressWidget(QWidget* parent); + void UpdateData(); + +private: + QGroupBox* CreateAchievementBox(const rc_api_achievement_definition_t* achievement); + QString GetStatusString(u32 achievement_id) const; + + QGroupBox* m_common_box; + QVBoxLayout* m_common_layout; +}; + +#endif // USE_RETRO_ACHIEVEMENTS diff --git a/Source/Core/DolphinQt/Achievements/AchievementsWindow.cpp b/Source/Core/DolphinQt/Achievements/AchievementsWindow.cpp index b23843f75b..d791d1b107 100644 --- a/Source/Core/DolphinQt/Achievements/AchievementsWindow.cpp +++ b/Source/Core/DolphinQt/Achievements/AchievementsWindow.cpp @@ -4,11 +4,16 @@ #ifdef USE_RETRO_ACHIEVEMENTS #include "DolphinQt/Achievements/AchievementsWindow.h" +#include + #include #include #include +#include "DolphinQt/Achievements/AchievementHeaderWidget.h" +#include "DolphinQt/Achievements/AchievementProgressWidget.h" #include "DolphinQt/Achievements/AchievementSettingsWidget.h" +#include "DolphinQt/QtUtils/QueueOnObject.h" #include "DolphinQt/QtUtils/WrapInScrollArea.h" AchievementsWindow::AchievementsWindow(QWidget* parent) : QDialog(parent) @@ -18,6 +23,8 @@ AchievementsWindow::AchievementsWindow(QWidget* parent) : QDialog(parent) CreateMainLayout(); ConnectWidgets(); + AchievementManager::GetInstance()->SetUpdateCallback( + [this] { QueueOnObject(this, &AchievementsWindow::UpdateData); }); } void AchievementsWindow::showEvent(QShowEvent* event) @@ -30,13 +37,18 @@ void AchievementsWindow::CreateMainLayout() { auto* layout = new QVBoxLayout(); + m_header_widget = new AchievementHeaderWidget(this); m_tab_widget = new QTabWidget(); + m_progress_widget = new AchievementProgressWidget(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_button_box = new QDialogButtonBox(QDialogButtonBox::Close); + layout->addWidget(m_header_widget); layout->addWidget(m_tab_widget); layout->addWidget(m_button_box); @@ -50,6 +62,14 @@ void AchievementsWindow::ConnectWidgets() void AchievementsWindow::UpdateData() { + { + std::lock_guard lg{*AchievementManager::GetInstance()->GetLock()}; + m_header_widget->UpdateData(); + m_header_widget->setVisible(AchievementManager::GetInstance()->IsLoggedIn()); + // Settings tab handles its own updates ... indeed, that calls this + m_progress_widget->UpdateData(); + m_tab_widget->setTabVisible(1, AchievementManager::GetInstance()->IsGameLoaded()); + } update(); } diff --git a/Source/Core/DolphinQt/Achievements/AchievementsWindow.h b/Source/Core/DolphinQt/Achievements/AchievementsWindow.h index 9570e8127f..d8407a3a17 100644 --- a/Source/Core/DolphinQt/Achievements/AchievementsWindow.h +++ b/Source/Core/DolphinQt/Achievements/AchievementsWindow.h @@ -6,8 +6,14 @@ #ifdef USE_RETRO_ACHIEVEMENTS #include -class QTabWidget; +#include "Core/AchievementManager.h" +#include "DolphinQt/QtUtils/QueueOnObject.h" + +class AchievementHeaderWidget; +class AchievementProgressWidget; class QDialogButtonBox; +class QTabWidget; +class UpdateCallback; class AchievementsWindow : public QDialog { @@ -21,7 +27,9 @@ private: void showEvent(QShowEvent* event); void ConnectWidgets(); + AchievementHeaderWidget* m_header_widget; QTabWidget* m_tab_widget; + AchievementProgressWidget* m_progress_widget; QDialogButtonBox* m_button_box; }; diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt index e302287677..7087f03429 100644 --- a/Source/Core/DolphinQt/CMakeLists.txt +++ b/Source/Core/DolphinQt/CMakeLists.txt @@ -27,6 +27,10 @@ add_executable(dolphin-emu CheatSearchWidget.h CheatsManager.cpp CheatsManager.h + Achievements/AchievementHeaderWidget.cpp + Achievements/AchievementHeaderWidget.h + Achievements/AchievementProgressWidget.cpp + Achievements/AchievementProgressWidget.h Achievements/AchievementSettingsWidget.cpp Achievements/AchievementSettingsWidget.h Achievements/AchievementsWindow.cpp diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj index 79b05b372b..ac769a10b9 100644 --- a/Source/Core/DolphinQt/DolphinQt.vcxproj +++ b/Source/Core/DolphinQt/DolphinQt.vcxproj @@ -50,6 +50,8 @@ + + @@ -256,6 +258,8 @@ + +