diff --git a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp index 852fbb74ca..8ec6278aae 100644 --- a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp +++ b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp @@ -44,6 +44,11 @@ bool IsSettingSaveable(const Config::Location& config_location) &Config::RA_ENABLED.GetLocation(), &Config::RA_USERNAME.GetLocation(), &Config::RA_API_TOKEN.GetLocation(), + &Config::RA_ACHIEVEMENTS_ENABLED.GetLocation(), + &Config::RA_LEADERBOARDS_ENABLED.GetLocation(), + &Config::RA_RICH_PRESENCE_ENABLED.GetLocation(), + &Config::RA_UNOFFICIAL_ENABLED.GetLocation(), + &Config::RA_ENCORE_ENABLED.GetLocation(), }; return std::any_of(begin(s_setting_saveable), end(s_setting_saveable), diff --git a/Source/Core/DolphinQt/Achievements/AchievementSettingsWidget.cpp b/Source/Core/DolphinQt/Achievements/AchievementSettingsWidget.cpp new file mode 100644 index 0000000000..d2b8f63b6f --- /dev/null +++ b/Source/Core/DolphinQt/Achievements/AchievementSettingsWidget.cpp @@ -0,0 +1,239 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifdef USE_RETRO_ACHIEVEMENTS +#include "DolphinQt/Achievements/AchievementSettingsWidget.h" + +#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/Achievements/AchievementsWindow.h" +#include "DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h" +#include "DolphinQt/Config/ToolTipControls/ToolTipCheckBox.h" +#include "DolphinQt/QtUtils/ModalMessageBox.h" +#include "DolphinQt/QtUtils/NonDefaultQPushButton.h" +#include "DolphinQt/QtUtils/SignalBlocking.h" +#include "DolphinQt/Settings.h" + +static constexpr bool hardcore_mode_enabled = false; + +AchievementSettingsWidget::AchievementSettingsWidget(QWidget* parent, + AchievementsWindow* parent_window) + : QWidget(parent), parent_window(parent_window) +{ + CreateLayout(); + LoadSettings(); + ConnectWidgets(); + + connect(&Settings::Instance(), &Settings::ConfigChanged, this, + &AchievementSettingsWidget::LoadSettings); +} + +void AchievementSettingsWidget::CreateLayout() +{ + m_common_layout = new QVBoxLayout(); + m_common_integration_enabled_input = + new ToolTipCheckBox(tr("Enable RetroAchievements.org Integration")); + m_common_integration_enabled_input->SetDescription( + tr("Enable integration with RetroAchievements for earning achievements and competing in " + "leaderboards.

Must log in with a RetroAchievements account to use. Dolphin does " + "not save your password locally and uses an API token to maintain login.")); + m_common_username_label = new QLabel(tr("Username")); + m_common_username_input = new QLineEdit(QStringLiteral("")); + m_common_password_label = new QLabel(tr("Password")); + m_common_password_input = new QLineEdit(QStringLiteral("")); + m_common_password_input->setEchoMode(QLineEdit::Password); + m_common_login_button = new QPushButton(tr("Login")); + m_common_logout_button = new QPushButton(tr("Logout")); + m_common_login_failed = new QLabel(tr("Login Failed")); + m_common_login_failed->setStyleSheet(QStringLiteral("QLabel { color : red; }")); + m_common_login_failed->setVisible(false); + m_common_achievements_enabled_input = new ToolTipCheckBox(tr("Enable Achievements")); + m_common_achievements_enabled_input->SetDescription(tr("Enable unlocking achievements.
")); + m_common_leaderboards_enabled_input = new ToolTipCheckBox(tr("Enable Leaderboards")); + m_common_leaderboards_enabled_input->SetDescription( + tr("Enable competing in RetroAchievements leaderboards.

Hardcore Mode must be enabled " + "to use.")); + m_common_rich_presence_enabled_input = new ToolTipCheckBox(tr("Enable Rich Presence")); + m_common_rich_presence_enabled_input->SetDescription( + tr("Enable detailed rich presence on the RetroAchievements website.

This provides a " + "detailed description of what the player is doing in game to the website. If this is " + "disabled, the website will only report what game is being played.

This has no " + "bearing on Discord rich presence.")); + m_common_unofficial_enabled_input = new ToolTipCheckBox(tr("Enable Unofficial Achievements")); + m_common_unofficial_enabled_input->SetDescription( + tr("Enable unlocking unofficial achievements as well as official " + "achievements.

Unofficial achievements may be optional or unfinished achievements " + "that have not been deemed official by RetroAchievements and may be useful for testing or " + "simply for fun.")); + m_common_encore_enabled_input = new ToolTipCheckBox(tr("Enable Encore Achievements")); + m_common_encore_enabled_input->SetDescription(tr( + "Enable unlocking achievements in Encore Mode.

Encore Mode re-enables achievements " + "the player has already unlocked on the site so that the player will be notified if they " + "meet the unlock conditions again, useful for custom speedrun criteria or simply for fun.")); + + m_common_layout->addWidget(m_common_integration_enabled_input); + m_common_layout->addWidget(m_common_username_label); + m_common_layout->addWidget(m_common_username_input); + m_common_layout->addWidget(m_common_password_label); + m_common_layout->addWidget(m_common_password_input); + m_common_layout->addWidget(m_common_login_button); + m_common_layout->addWidget(m_common_logout_button); + m_common_layout->addWidget(m_common_login_failed); + m_common_layout->addWidget(m_common_achievements_enabled_input); + m_common_layout->addWidget(m_common_leaderboards_enabled_input); + m_common_layout->addWidget(m_common_rich_presence_enabled_input); + m_common_layout->addWidget(m_common_unofficial_enabled_input); + m_common_layout->addWidget(m_common_encore_enabled_input); + + m_common_layout->setAlignment(Qt::AlignTop); + setLayout(m_common_layout); +} + +void AchievementSettingsWidget::ConnectWidgets() +{ + connect(m_common_integration_enabled_input, &QCheckBox::toggled, this, + &AchievementSettingsWidget::ToggleRAIntegration); + connect(m_common_login_button, &QPushButton::pressed, this, &AchievementSettingsWidget::Login); + connect(m_common_logout_button, &QPushButton::pressed, this, &AchievementSettingsWidget::Logout); + connect(m_common_achievements_enabled_input, &QCheckBox::toggled, this, + &AchievementSettingsWidget::ToggleAchievements); + connect(m_common_leaderboards_enabled_input, &QCheckBox::toggled, this, + &AchievementSettingsWidget::ToggleLeaderboards); + connect(m_common_rich_presence_enabled_input, &QCheckBox::toggled, this, + &AchievementSettingsWidget::ToggleRichPresence); + connect(m_common_unofficial_enabled_input, &QCheckBox::toggled, this, + &AchievementSettingsWidget::ToggleUnofficial); + connect(m_common_encore_enabled_input, &QCheckBox::toggled, this, + &AchievementSettingsWidget::ToggleEncore); +} + +void AchievementSettingsWidget::OnControllerInterfaceConfigure() +{ + ControllerInterfaceWindow* window = new ControllerInterfaceWindow(this); + window->setAttribute(Qt::WA_DeleteOnClose, true); + window->setWindowModality(Qt::WindowModality::WindowModal); + window->show(); +} + +void AchievementSettingsWidget::LoadSettings() +{ + bool enabled = Config::Get(Config::RA_ENABLED); + bool achievements_enabled = Config::Get(Config::RA_ACHIEVEMENTS_ENABLED); + bool logged_out = Config::Get(Config::RA_API_TOKEN).empty(); + std::string username = Config::Get(Config::RA_USERNAME); + + SignalBlocking(m_common_integration_enabled_input)->setChecked(enabled); + SignalBlocking(m_common_username_label)->setEnabled(enabled); + if (!username.empty()) + SignalBlocking(m_common_username_input)->setText(QString::fromStdString(username)); + SignalBlocking(m_common_username_input)->setEnabled(enabled && logged_out); + SignalBlocking(m_common_password_label)->setVisible(logged_out); + SignalBlocking(m_common_password_label)->setEnabled(enabled); + SignalBlocking(m_common_password_input)->setVisible(logged_out); + SignalBlocking(m_common_password_input)->setEnabled(enabled); + SignalBlocking(m_common_login_button)->setVisible(logged_out); + SignalBlocking(m_common_login_button)->setEnabled(enabled && !Core::IsRunning()); + SignalBlocking(m_common_logout_button)->setVisible(!logged_out); + SignalBlocking(m_common_logout_button)->setEnabled(enabled); + + SignalBlocking(m_common_achievements_enabled_input)->setChecked(achievements_enabled); + SignalBlocking(m_common_achievements_enabled_input)->setEnabled(enabled); + + SignalBlocking(m_common_leaderboards_enabled_input) + ->setChecked(Config::Get(Config::RA_LEADERBOARDS_ENABLED)); + SignalBlocking(m_common_leaderboards_enabled_input)->setEnabled(enabled && hardcore_mode_enabled); + + SignalBlocking(m_common_rich_presence_enabled_input) + ->setChecked(Config::Get(Config::RA_RICH_PRESENCE_ENABLED)); + SignalBlocking(m_common_rich_presence_enabled_input)->setEnabled(enabled); + + SignalBlocking(m_common_unofficial_enabled_input) + ->setChecked(Config::Get(Config::RA_UNOFFICIAL_ENABLED)); + SignalBlocking(m_common_unofficial_enabled_input)->setEnabled(enabled && achievements_enabled); + + SignalBlocking(m_common_encore_enabled_input)->setChecked(Config::Get(Config::RA_ENCORE_ENABLED)); + SignalBlocking(m_common_encore_enabled_input)->setEnabled(enabled && achievements_enabled); +} + +void AchievementSettingsWidget::SaveSettings() +{ + Config::ConfigChangeCallbackGuard config_guard; + + Config::SetBaseOrCurrent(Config::RA_ENABLED, m_common_integration_enabled_input->isChecked()); + Config::SetBaseOrCurrent(Config::RA_ACHIEVEMENTS_ENABLED, + m_common_achievements_enabled_input->isChecked()); + Config::SetBaseOrCurrent(Config::RA_LEADERBOARDS_ENABLED, + m_common_leaderboards_enabled_input->isChecked()); + Config::SetBaseOrCurrent(Config::RA_RICH_PRESENCE_ENABLED, + m_common_rich_presence_enabled_input->isChecked()); + Config::SetBaseOrCurrent(Config::RA_UNOFFICIAL_ENABLED, + m_common_unofficial_enabled_input->isChecked()); + Config::SetBaseOrCurrent(Config::RA_ENCORE_ENABLED, m_common_encore_enabled_input->isChecked()); + Config::Save(); +} + +void AchievementSettingsWidget::ToggleRAIntegration() +{ + SaveSettings(); + if (Config::Get(Config::RA_ENABLED)) + AchievementManager::GetInstance()->Init(); + else + AchievementManager::GetInstance()->Shutdown(); +} + +void AchievementSettingsWidget::Login() +{ + Config::SetBaseOrCurrent(Config::RA_USERNAME, m_common_username_input->text().toStdString()); + AchievementManager::GetInstance()->Login(m_common_password_input->text().toStdString()); + m_common_password_input->setText(QString()); + m_common_login_failed->setVisible(Config::Get(Config::RA_API_TOKEN).empty()); + SaveSettings(); +} + +void AchievementSettingsWidget::Logout() +{ + AchievementManager::GetInstance()->Logout(); + SaveSettings(); +} + +void AchievementSettingsWidget::ToggleAchievements() +{ + SaveSettings(); + AchievementManager::GetInstance()->ActivateDeactivateAchievements(); +} + +void AchievementSettingsWidget::ToggleLeaderboards() +{ + SaveSettings(); + AchievementManager::GetInstance()->ActivateDeactivateLeaderboards(); +} + +void AchievementSettingsWidget::ToggleRichPresence() +{ + SaveSettings(); + AchievementManager::GetInstance()->ActivateDeactivateRichPresence(); +} + +void AchievementSettingsWidget::ToggleUnofficial() +{ + SaveSettings(); + AchievementManager::GetInstance()->ActivateDeactivateAchievements(); +} + +void AchievementSettingsWidget::ToggleEncore() +{ + SaveSettings(); + AchievementManager::GetInstance()->ActivateDeactivateAchievements(); +} + +#endif // USE_RETRO_ACHIEVEMENTS diff --git a/Source/Core/DolphinQt/Achievements/AchievementSettingsWidget.h b/Source/Core/DolphinQt/Achievements/AchievementSettingsWidget.h new file mode 100644 index 0000000000..ed23c53e7a --- /dev/null +++ b/Source/Core/DolphinQt/Achievements/AchievementSettingsWidget.h @@ -0,0 +1,62 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#ifdef USE_RETRO_ACHIEVEMENTS +#include + +class AchievementsWindow; +class QGroupBox; +class QVBoxLayout; +class QLabel; +class QLineEdit; +class QPushButton; +class ToolTipCheckBox; + +class AchievementSettingsWidget final : public QWidget +{ + Q_OBJECT +public: + explicit AchievementSettingsWidget(QWidget* parent, AchievementsWindow* parent_window); + +private: + void OnControllerInterfaceConfigure(); + + void CreateLayout(); + void ConnectWidgets(); + + void LoadSettings(); + void SaveSettings(); + + void ToggleRAIntegration(); + void Login(); + void Logout(); + void ToggleAchievements(); + void ToggleLeaderboards(); + void ToggleRichPresence(); + void ToggleHardcore(); + void ToggleBadgeIcons(); + void ToggleUnofficial(); + void ToggleEncore(); + + AchievementsWindow* parent_window; + + QGroupBox* m_common_box; + QVBoxLayout* m_common_layout; + ToolTipCheckBox* m_common_integration_enabled_input; + QLabel* m_common_login_failed; + QLabel* m_common_username_label; + QLineEdit* m_common_username_input; + QLabel* m_common_password_label; + QLineEdit* m_common_password_input; + QPushButton* m_common_login_button; + QPushButton* m_common_logout_button; + ToolTipCheckBox* m_common_achievements_enabled_input; + ToolTipCheckBox* m_common_leaderboards_enabled_input; + ToolTipCheckBox* m_common_rich_presence_enabled_input; + ToolTipCheckBox* m_common_unofficial_enabled_input; + ToolTipCheckBox* m_common_encore_enabled_input; +}; + +#endif // USE_RETRO_ACHIEVEMENTS diff --git a/Source/Core/DolphinQt/Achievements/AchievementsWindow.cpp b/Source/Core/DolphinQt/Achievements/AchievementsWindow.cpp new file mode 100644 index 0000000000..b23843f75b --- /dev/null +++ b/Source/Core/DolphinQt/Achievements/AchievementsWindow.cpp @@ -0,0 +1,56 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifdef USE_RETRO_ACHIEVEMENTS +#include "DolphinQt/Achievements/AchievementsWindow.h" + +#include +#include +#include + +#include "DolphinQt/Achievements/AchievementSettingsWidget.h" +#include "DolphinQt/QtUtils/WrapInScrollArea.h" + +AchievementsWindow::AchievementsWindow(QWidget* parent) : QDialog(parent) +{ + setWindowTitle(tr("Achievements")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + CreateMainLayout(); + ConnectWidgets(); +} + +void AchievementsWindow::showEvent(QShowEvent* event) +{ + QDialog::showEvent(event); + update(); +} + +void AchievementsWindow::CreateMainLayout() +{ + auto* layout = new QVBoxLayout(); + + m_tab_widget = new QTabWidget(); + m_tab_widget->addTab( + GetWrappedWidget(new AchievementSettingsWidget(m_tab_widget, this), this, 125, 100), + tr("Settings")); + + m_button_box = new QDialogButtonBox(QDialogButtonBox::Close); + + layout->addWidget(m_tab_widget); + layout->addWidget(m_button_box); + + WrapInScrollArea(this, layout); +} + +void AchievementsWindow::ConnectWidgets() +{ + connect(m_button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); +} + +void AchievementsWindow::UpdateData() +{ + update(); +} + +#endif // USE_RETRO_ACHIEVEMENTS diff --git a/Source/Core/DolphinQt/Achievements/AchievementsWindow.h b/Source/Core/DolphinQt/Achievements/AchievementsWindow.h new file mode 100644 index 0000000000..9570e8127f --- /dev/null +++ b/Source/Core/DolphinQt/Achievements/AchievementsWindow.h @@ -0,0 +1,28 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#ifdef USE_RETRO_ACHIEVEMENTS +#include + +class QTabWidget; +class QDialogButtonBox; + +class AchievementsWindow : public QDialog +{ + Q_OBJECT +public: + explicit AchievementsWindow(QWidget* parent); + void UpdateData(); + +private: + void CreateMainLayout(); + void showEvent(QShowEvent* event); + void ConnectWidgets(); + + QTabWidget* m_tab_widget; + QDialogButtonBox* m_button_box; +}; + +#endif // USE_RETRO_ACHIEVEMENTS diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt index 4b576c1776..573a3236a0 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/AchievementSettingsWidget.cpp + Achievements/AchievementSettingsWidget.h + Achievements/AchievementsWindow.cpp + Achievements/AchievementsWindow.h Config/ARCodeWidget.cpp Config/ARCodeWidget.h Config/CheatCodeEditor.cpp diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj index 55eedc5424..896bde5458 100644 --- a/Source/Core/DolphinQt/DolphinQt.vcxproj +++ b/Source/Core/DolphinQt/DolphinQt.vcxproj @@ -50,6 +50,8 @@ + + @@ -252,6 +254,8 @@ + + diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp index e0342a4f8e..8262c8f01e 100644 --- a/Source/Core/DolphinQt/MainWindow.cpp +++ b/Source/Core/DolphinQt/MainWindow.cpp @@ -72,6 +72,7 @@ #include "DiscIO/RiivolutionPatcher.h" #include "DolphinQt/AboutDialog.h" +#include "DolphinQt/Achievements/AchievementsWindow.h" #include "DolphinQt/CheatsManager.h" #include "DolphinQt/Config/ControllersWindow.h" #include "DolphinQt/Config/FreeLookWindow.h" @@ -544,6 +545,10 @@ void MainWindow::ConnectMenuBar() connect(m_menu_bar, &MenuBar::ShowInfinityBase, this, &MainWindow::ShowInfinityBase); connect(m_menu_bar, &MenuBar::ConnectWiiRemote, this, &MainWindow::OnConnectWiiRemote); +#ifdef USE_RETRO_ACHIEVEMENTS + connect(m_menu_bar, &MenuBar::ShowAchievementsWindow, this, &MainWindow::ShowAchievementsWindow); +#endif // USE_RETRO_ACHIEVEMENTS + // Movie connect(m_menu_bar, &MenuBar::PlayRecording, this, &MainWindow::OnPlayRecording); connect(m_menu_bar, &MenuBar::StartRecording, this, &MainWindow::OnStartRecording); @@ -1892,6 +1897,20 @@ void MainWindow::OnConnectWiiRemote(int id) }); } +#ifdef USE_RETRO_ACHIEVEMENTS +void MainWindow::ShowAchievementsWindow() +{ + if (!m_achievements_window) + { + m_achievements_window = new AchievementsWindow(this); + } + + m_achievements_window->show(); + m_achievements_window->raise(); + m_achievements_window->activateWindow(); +} +#endif // USE_RETRO_ACHIEVEMENTS + void MainWindow::ShowMemcardManager() { GCMemcardManager manager(this); diff --git a/Source/Core/DolphinQt/MainWindow.h b/Source/Core/DolphinQt/MainWindow.h index 869d77cc07..fa3a274bfc 100644 --- a/Source/Core/DolphinQt/MainWindow.h +++ b/Source/Core/DolphinQt/MainWindow.h @@ -16,6 +16,7 @@ class QStackedWidget; class QString; +class AchievementsWindow; class BreakpointWidget; struct BootParameters; class CheatsManager; @@ -168,6 +169,10 @@ private: void ShowCheatsManager(); void ShowRiivolutionBootWidget(const UICommon::GameFile& game); +#ifdef USE_RETRO_ACHIEVEMENTS + void ShowAchievementsWindow(); +#endif // USE_RETRO_ACHIEVEMENTS + void NetPlayInit(); bool NetPlayJoin(); bool NetPlayHost(const UICommon::GameFile& game); @@ -241,6 +246,10 @@ private: static constexpr int num_wii_controllers = 4; std::array m_wii_tas_input_windows{}; +#ifdef USE_RETRO_ACHIEVEMENTS + AchievementsWindow* m_achievements_window = nullptr; +#endif // USE_RETRO_ACHIEVEMENTS + BreakpointWidget* m_breakpoint_widget; CodeWidget* m_code_widget; JITWidget* m_jit_widget; diff --git a/Source/Core/DolphinQt/MenuBar.cpp b/Source/Core/DolphinQt/MenuBar.cpp index 663aadd341..b0989fae66 100644 --- a/Source/Core/DolphinQt/MenuBar.cpp +++ b/Source/Core/DolphinQt/MenuBar.cpp @@ -21,6 +21,7 @@ #include "Core/Boot/Boot.h" #include "Core/CommonTitles.h" +#include "Core/Config/AchievementSettings.h" #include "Core/Config/MainSettings.h" #include "Core/ConfigManager.h" #include "Core/Core.h" @@ -236,6 +237,15 @@ void MenuBar::AddToolsMenu() tools_menu->addSeparator(); +#ifdef USE_RETRO_ACHIEVEMENTS + if (Config::Get(Config::RA_ENABLED)) + { + tools_menu->addAction(tr("Achievements"), this, [this] { emit ShowAchievementsWindow(); }); + + tools_menu->addSeparator(); + } +#endif // USE_RETRO_ACHIEVEMENTS + QMenu* gc_ipl = tools_menu->addMenu(tr("Load GameCube Main Menu")); m_ntscj_ipl = gc_ipl->addAction(tr("NTSC-J"), this, diff --git a/Source/Core/DolphinQt/MenuBar.h b/Source/Core/DolphinQt/MenuBar.h index ba6490e1d9..a5c5730b6b 100644 --- a/Source/Core/DolphinQt/MenuBar.h +++ b/Source/Core/DolphinQt/MenuBar.h @@ -93,6 +93,10 @@ signals: void ShowInfinityBase(); void ConnectWiiRemote(int id); +#ifdef USE_RETRO_ACHIEVEMENTS + void ShowAchievementsWindow(); +#endif // USE_RETRO_ACHIEVEMENTS + // Options void Configure(); void ConfigureGraphics();