diff --git a/pcsx2-qt/SettingWidgetBinder.h b/pcsx2-qt/SettingWidgetBinder.h index 8bc4d1925ca03..f9b345f54b4be 100644 --- a/pcsx2-qt/SettingWidgetBinder.h +++ b/pcsx2-qt/SettingWidgetBinder.h @@ -23,6 +23,7 @@ #include #include "common/FileSystem.h" +#include "common/HostSys.h" #include "common/Path.h" #include "pcsx2/Config.h" @@ -1261,8 +1262,8 @@ namespace SettingWidgetBinder widget->connect(widget, &QLineEdit::editingFinished, widget, std::move(value_changed)); } - static inline void BindWidgetToFileSetting(SettingsInterface* sif, QLineEdit* widget, QAbstractButton* browse_button, - QAbstractButton* open_button, QAbstractButton* reset_button, std::string section, std::string key, std::string default_value, + static inline void BindWidgetToAudioFileSetting(SettingsInterface* sif, QLineEdit* widget, QAbstractButton* browse_button, + QAbstractButton* preview_button, QAbstractButton* reset_button, std::string section, std::string key, std::string default_value, const QString& filter, bool allow_pergame = false, bool use_relative = true) { using Accessor = SettingAccessor; @@ -1296,40 +1297,25 @@ namespace SettingWidgetBinder Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), relative_path.c_str()); } else - { Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), new_value.c_str()); - } if (!FileSystem::FileExists(new_value.c_str())) - { QMessageBox::critical(QtUtils::GetRootWidget(widget), qApp->translate("SettingWidgetBinder", "Error"), qApp->translate("SettingWidgetBinder", "File cannot be found.")); - } Host::CommitBaseSettingChanges(); return; } else - { - QMessageBox::critical(QtUtils::GetRootWidget(widget), qApp->translate("SettingWidgetBinder", "Error"), - qApp->translate("SettingWidgetBinder", "File path cannot be empty.")); - } + Host::RemoveBaseSettingValue(section.c_str(), key.c_str()); - // reset to old value - std::string current_path(Host::GetBaseStringSettingValue(section.c_str(), key.c_str(), default_value.c_str())); - if (current_path.empty()) - current_path = default_value; - else if (use_relative && !Path::IsAbsolute(current_path)) - current_path = Path::Canonicalize(Path::Combine(EmuFolders::DataRoot, current_path)); - - widget->setText(QString::fromStdString(current_path)); }; if (browse_button) { QObject::connect(browse_button, &QAbstractButton::clicked, browse_button, [widget, key, value_changed, filter]() { const QString path(QDir::toNativeSeparators(QFileDialog::getOpenFileName(QtUtils::GetRootWidget(widget), - qApp->translate("SettingWidgetBinder", "Select File"), QString(), filter))); + qApp->translate("SettingWidgetBinder", "Select Audio File"), QString(), filter))); if (path.isEmpty()) return; @@ -1337,19 +1323,22 @@ namespace SettingWidgetBinder value_changed(); }); } - if (open_button) + if (preview_button) { - QObject::connect(open_button, &QAbstractButton::clicked, open_button, [widget]() { - QString path(Accessor::getStringValue(widget)); - if (!path.isEmpty()) - QtUtils::OpenURL(QtUtils::GetRootWidget(widget), QUrl::fromLocalFile(path)); + QObject::connect(preview_button, &QAbstractButton::clicked, preview_button, [widget, default_value = std::move(default_value)]() { + const QByteArray path = widget->text().toUtf8(); + Common::PlaySoundAsync( + (path.isEmpty() + ? default_value + : path.constData()).c_str() + ); }); } if (reset_button) { QObject::connect( - reset_button, &QAbstractButton::clicked, reset_button, [widget, default_value = std::move(default_value), value_changed]() { - widget->setText(QString::fromStdString(default_value)); + reset_button, &QAbstractButton::clicked, reset_button, [widget, value_changed]() { + widget->clear(); value_changed(); }); } diff --git a/pcsx2-qt/Settings/AchievementSettingsWidget.cpp b/pcsx2-qt/Settings/AchievementSettingsWidget.cpp index a78ba2aca28fc..1d9de3632ca3f 100644 --- a/pcsx2-qt/Settings/AchievementSettingsWidget.cpp +++ b/pcsx2-qt/Settings/AchievementSettingsWidget.cpp @@ -17,7 +17,10 @@ #include #include -const char* AchievementSettingsWidget::AUDIO_FILE_FILTER = QT_TRANSLATE_NOOP("AchievementSettingsWidget", "WAV Audio Files (*.wav)"); +static constexpr const char* AUDIO_FILE_FILTER = QT_TRANSLATE_NOOP("AchievementSettingsWidget", "WAV Audio Files (*.wav)"); +static constexpr const char* DEFAULT_INFO_SOUND_NAME = "sounds/achievements/message.wav"; +static constexpr const char* DEFAULT_UNLOCK_SOUND_NAME = "sounds/achievements/unlock.wav"; +static constexpr const char* DEFAULT_LBSUBMIT_SOUND_NAME = "sounds/achievements/lbsubmit.wav"; AchievementSettingsWidget::AchievementSettingsWidget(SettingsWindow* settings_dialog, QWidget* parent) : SettingsWidget(settings_dialog, parent) @@ -44,9 +47,9 @@ AchievementSettingsWidget::AchievementSettingsWidget(SettingsWindow* settings_di SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.achievementNotificationsDuration, "Achievements", "NotificationsDuration", Pcsx2Config::AchievementsOptions::DEFAULT_NOTIFICATION_DURATION); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.leaderboardNotificationsDuration, "Achievements", "LeaderboardsDuration", Pcsx2Config::AchievementsOptions::DEFAULT_LEADERBOARD_DURATION); - SettingWidgetBinder::BindWidgetToFileSetting(sif, m_ui.notificationSoundPath, m_ui.notificationSoundBrowse, m_ui.notificationSoundOpen, m_ui.notificationSoundReset, "Achievements", "InfoSoundName", Path::Combine(EmuFolders::Resources, EmuConfig.Achievements.DEFAULT_INFO_SOUND_NAME), qApp->translate("AchievementSettingsWidget", AUDIO_FILE_FILTER), true, false); - SettingWidgetBinder::BindWidgetToFileSetting(sif, m_ui.unlockSoundPath, m_ui.unlockSoundBrowse, m_ui.unlockSoundOpen, m_ui.unlockSoundReset, "Achievements", "UnlockSoundName", Path::Combine(EmuFolders::Resources, EmuConfig.Achievements.DEFAULT_UNLOCK_SOUND_NAME), qApp->translate("AchievementSettingsWidget", AUDIO_FILE_FILTER), true, false); - SettingWidgetBinder::BindWidgetToFileSetting(sif, m_ui.lbSoundPath, m_ui.lbSoundBrowse, m_ui.lbSoundOpen, m_ui.lbSoundReset, "Achievements", "LBSubmitSoundName", Path::Combine(EmuFolders::Resources, EmuConfig.Achievements.DEFAULT_LBSUBMIT_SOUND_NAME), qApp->translate("AchievementSettingsWidget", AUDIO_FILE_FILTER), true, false); + SettingWidgetBinder::BindWidgetToAudioFileSetting(sif, m_ui.notificationSoundPath, m_ui.notificationSoundBrowse, m_ui.notificationSoundOpen, m_ui.notificationSoundReset, "Achievements", "InfoSoundName", Path::Combine(EmuFolders::Resources, DEFAULT_INFO_SOUND_NAME), qApp->translate("AchievementSettingsWidget", AUDIO_FILE_FILTER), true, false); + SettingWidgetBinder::BindWidgetToAudioFileSetting(sif, m_ui.unlockSoundPath, m_ui.unlockSoundBrowse, m_ui.unlockSoundOpen, m_ui.unlockSoundReset, "Achievements", "UnlockSoundName", Path::Combine(EmuFolders::Resources, DEFAULT_UNLOCK_SOUND_NAME), qApp->translate("AchievementSettingsWidget", AUDIO_FILE_FILTER), true, false); + SettingWidgetBinder::BindWidgetToAudioFileSetting(sif, m_ui.lbSoundPath, m_ui.lbSoundBrowse, m_ui.lbSoundOpen, m_ui.lbSoundReset, "Achievements", "LBSubmitSoundName", Path::Combine(EmuFolders::Resources, DEFAULT_LBSUBMIT_SOUND_NAME), qApp->translate("AchievementSettingsWidget", AUDIO_FILE_FILTER), true, false); dialog()->registerWidgetHelp(m_ui.enable, tr("Enable Achievements"), tr("Unchecked"), tr("When enabled and logged in, PCSX2 will scan for achievements on startup.")); dialog()->registerWidgetHelp(m_ui.hardcoreMode, tr("Enable Hardcore Mode"), tr("Unchecked"), tr("\"Challenge\" mode for achievements, including leaderboard tracking. Disables save state, cheats, and slowdown functions.")); diff --git a/pcsx2-qt/Settings/AchievementSettingsWidget.h b/pcsx2-qt/Settings/AchievementSettingsWidget.h index 6f65881d55cf8..d00a6475ad180 100644 --- a/pcsx2-qt/Settings/AchievementSettingsWidget.h +++ b/pcsx2-qt/Settings/AchievementSettingsWidget.h @@ -26,7 +26,6 @@ private Q_SLOTS: private: void updateLoginState(); - static const char* AUDIO_FILE_FILTER; Ui::AchievementSettingsWidget m_ui; }; diff --git a/pcsx2-qt/Settings/AchievementSettingsWidget.ui b/pcsx2-qt/Settings/AchievementSettingsWidget.ui index 0bcb83685f492..95589bf4a6ad7 100644 --- a/pcsx2-qt/Settings/AchievementSettingsWidget.ui +++ b/pcsx2-qt/Settings/AchievementSettingsWidget.ui @@ -316,10 +316,18 @@ - + + + Default Sound Effect + + - + + + Default Sound Effect + + @@ -350,7 +358,11 @@ - + + + Default Sound Effect + + @@ -426,12 +438,12 @@ Login token generated at: Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop + + Qt::TextInteractionFlag::TextBrowserInteraction + viewProfile - - Qt::TextBrowserInteraction - @@ -473,7 +485,7 @@ Login token generated at: Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - Qt::TextBrowserInteraction + Qt::TextInteractionFlag::TextBrowserInteraction diff --git a/pcsx2/Achievements.cpp b/pcsx2/Achievements.cpp index 777476c4defe7..d2a59a0d6313b 100644 --- a/pcsx2/Achievements.cpp +++ b/pcsx2/Achievements.cpp @@ -63,6 +63,10 @@ namespace Achievements static constexpr float LEADERBOARD_STARTED_NOTIFICATION_TIME = 3.0f; static constexpr float LEADERBOARD_FAILED_NOTIFICATION_TIME = 3.0f; + static constexpr const char* DEFAULT_INFO_SOUND_NAME = "sounds/achievements/message.wav"; + static constexpr const char* DEFAULT_UNLOCK_SOUND_NAME = "sounds/achievements/unlock.wav"; + static constexpr const char* DEFAULT_LBSUBMIT_SOUND_NAME = "sounds/achievements/lbsubmit.wav"; + static constexpr float INDICATOR_FADE_IN_TIME = 0.1f; static constexpr float INDICATOR_FADE_OUT_TIME = 0.5f; @@ -455,9 +459,9 @@ bool Achievements::Initialize() SettingsInterface* secretsInterface = Host::Internal::GetSecretsSettingsLayer(); secretsInterface->SetStringValue("Achievements", "Token", oldToken.c_str()); secretsInterface->Save(); - + oldToken.clear(); - + auto baseLock = Host::GetSettingsLock(); SettingsInterface* baseInterface = Host::Internal::GetBaseSettingsLayer(); baseInterface->DeleteValue("Achievements", "Token"); @@ -540,7 +544,7 @@ void Achievements::UpdateNotificationPosition() { // Set notification position based on achievement settings float horizontal_position, vertical_position, direction; - + // Determine horizontal alignment switch (EmuConfig.Achievements.NotificationPosition) { @@ -549,13 +553,13 @@ void Achievements::UpdateNotificationPosition() case OsdOverlayPos::BottomLeft: horizontal_position = 0.0f; // Left break; - + case OsdOverlayPos::TopCenter: case OsdOverlayPos::Center: case OsdOverlayPos::BottomCenter: horizontal_position = 0.5f; // Center break; - + case OsdOverlayPos::TopRight: case OsdOverlayPos::CenterRight: case OsdOverlayPos::BottomRight: @@ -563,7 +567,7 @@ void Achievements::UpdateNotificationPosition() horizontal_position = 1.0f; // Right break; } - + // Determine vertical alignment and stacking direction switch (EmuConfig.Achievements.NotificationPosition) { @@ -573,14 +577,14 @@ void Achievements::UpdateNotificationPosition() vertical_position = 0.15f; // Top area direction = 1.0f; // Stack downward break; - + case OsdOverlayPos::CenterLeft: case OsdOverlayPos::Center: case OsdOverlayPos::CenterRight: vertical_position = 0.5f; // Center direction = 1.0f; // Stack downward break; - + case OsdOverlayPos::BottomLeft: case OsdOverlayPos::BottomCenter: case OsdOverlayPos::BottomRight: @@ -589,7 +593,7 @@ void Achievements::UpdateNotificationPosition() direction = -1.0f; // Stack upward break; } - + ImGuiFullscreen::SetNotificationPosition(horizontal_position, vertical_position, direction); } @@ -1142,7 +1146,11 @@ void Achievements::DisplayAchievementSummary() } if (EmuConfig.Achievements.SoundEffects && EmuConfig.Achievements.InfoSound) - Common::PlaySoundAsync(EmuConfig.Achievements.InfoSoundName.c_str()); + Common::PlaySoundAsync( + (EmuConfig.Achievements.InfoSoundName.empty() + ? Path::Combine(EmuFolders::Resources, DEFAULT_INFO_SOUND_NAME) + : EmuConfig.Achievements.InfoSoundName).c_str() + ); } void Achievements::DisplayHardcoreDeferredMessage() @@ -1194,7 +1202,11 @@ void Achievements::HandleUnlockEvent(const rc_client_event_t* event) } if (EmuConfig.Achievements.SoundEffects && EmuConfig.Achievements.UnlockSound) - Common::PlaySoundAsync(EmuConfig.Achievements.UnlockSoundName.c_str()); + Common::PlaySoundAsync( + (EmuConfig.Achievements.UnlockSoundName.empty() + ? Path::Combine(EmuFolders::Resources, DEFAULT_UNLOCK_SOUND_NAME) + : EmuConfig.Achievements.UnlockSoundName).c_str() + ); } void Achievements::HandleGameCompleteEvent(const rc_client_event_t* event) @@ -1315,7 +1327,11 @@ void Achievements::HandleLeaderboardSubmittedEvent(const rc_client_event_t* even } if (EmuConfig.Achievements.SoundEffects && EmuConfig.Achievements.LBSubmitSound) - Common::PlaySoundAsync(EmuConfig.Achievements.LBSubmitSoundName.c_str()); + Common::PlaySoundAsync( + (EmuConfig.Achievements.LBSubmitSoundName.empty() + ? Path::Combine(EmuFolders::Resources, DEFAULT_LBSUBMIT_SOUND_NAME) + : EmuConfig.Achievements.LBSubmitSoundName).c_str() + ); } void Achievements::HandleLeaderboardScoreboardEvent(const rc_client_event_t* event) @@ -1833,7 +1849,7 @@ void Achievements::ClientLoginWithPasswordCallback(int result, const char* error Host::SetBaseStringSettingValue("Achievements", "Username", params->username); Host::SetBaseStringSettingValue("Achievements", "LoginTimestamp", fmt::format("{}", std::time(nullptr)).c_str()); Host::CommitBaseSettingChanges(); - + SettingsInterface* secretsInterface = Host::Internal::GetSecretsSettingsLayer(); secretsInterface->SetStringValue("Achievements", "Token", user->token); secretsInterface->Save(); @@ -2032,7 +2048,7 @@ static ImVec2 CalculateOverlayPosition(const ImGuiIO& io, float padding, Achieve static ImVec2 AdjustPositionForAlignment(const ImVec2& base_position, const ImVec2& element_size, AchievementOverlayPosition alignment) { ImVec2 adjusted = base_position; - + // Adjust for horizontal alignment switch (alignment) { @@ -2041,14 +2057,14 @@ static ImVec2 AdjustPositionForAlignment(const ImVec2& base_position, const ImVe case AchievementOverlayPosition::BottomLeft: // Left aligned no adjustment needed for x break; - + case AchievementOverlayPosition::TopCenter: case AchievementOverlayPosition::Center: case AchievementOverlayPosition::BottomCenter: // Center aligned offset by half element width adjusted.x -= element_size.x * 0.5f; break; - + case AchievementOverlayPosition::TopRight: case AchievementOverlayPosition::CenterRight: case AchievementOverlayPosition::BottomRight: @@ -2057,7 +2073,7 @@ static ImVec2 AdjustPositionForAlignment(const ImVec2& base_position, const ImVe adjusted.x -= element_size.x; break; } - + // Adjust for vertical alignment switch (alignment) { @@ -2066,14 +2082,14 @@ static ImVec2 AdjustPositionForAlignment(const ImVec2& base_position, const ImVe case AchievementOverlayPosition::TopRight: // Top aligned no adjustment needed for y break; - + case AchievementOverlayPosition::CenterLeft: case AchievementOverlayPosition::Center: case AchievementOverlayPosition::CenterRight: // Center aligned offset by half element height adjusted.y -= element_size.y * 0.5f; break; - + case AchievementOverlayPosition::BottomLeft: case AchievementOverlayPosition::BottomCenter: case AchievementOverlayPosition::BottomRight: @@ -2082,7 +2098,7 @@ static ImVec2 AdjustPositionForAlignment(const ImVec2& base_position, const ImVe adjusted.y -= element_size.y; break; } - + return adjusted; } @@ -2094,19 +2110,19 @@ static ImVec2 GetStackingDirection(AchievementOverlayPosition alignment) case AchievementOverlayPosition::TopCenter: case AchievementOverlayPosition::TopRight: return ImVec2(0.0f, 1.0f); // Stack downward - + case AchievementOverlayPosition::BottomLeft: case AchievementOverlayPosition::BottomCenter: case AchievementOverlayPosition::BottomRight: default: return ImVec2(0.0f, -1.0f); // Stack upward - + case AchievementOverlayPosition::CenterLeft: return ImVec2(1.0f, 0.0f); // Stack rightward - + case AchievementOverlayPosition::CenterRight: return ImVec2(-1.0f, 0.0f); // Stack leftward - + case AchievementOverlayPosition::Center: return ImVec2(0.0f, -1.0f); // Stack upward for center } @@ -2147,7 +2163,7 @@ void Achievements::DrawGameOverlays() { dl->AddImage(reinterpret_cast(badge->GetNativeHandle()), current_position, current_position + image_size, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), col); - + // For horizontal layouts, go horizontally for vertical layouts, stay in the same row if (std::abs(stack_direction.x) > 0.0f) { @@ -2198,7 +2214,7 @@ void Achievements::DrawGameOverlays() const ImVec2 progress_box_size = ImVec2(image_size.x + text_size.x + spacing + padding * 2.0f, image_size.y + padding * 2.0f); const ImVec2 stack_direction = GetStackingDirection(EmuConfig.Achievements.OverlayPosition); const ImVec2 box_position = AdjustPositionForAlignment(position, progress_box_size, EmuConfig.Achievements.OverlayPosition); - + const ImVec2 box_min = box_position; const ImVec2 box_max = box_position + progress_box_size; const float box_rounding = LayoutScale(1.0f); @@ -2232,7 +2248,7 @@ void Achievements::DrawGameOverlays() if (!s_active_leaderboard_trackers.empty() && EmuConfig.Achievements.LBOverlays) { const ImVec2 stack_direction = GetStackingDirection(EmuConfig.Achievements.OverlayPosition); - + for (auto it = s_active_leaderboard_trackers.begin(); it != s_active_leaderboard_trackers.end();) { const LeaderboardTrackerIndicator& indicator = *it; @@ -2247,7 +2263,7 @@ void Achievements::DrawGameOverlays() const ImVec2 tracker_box_size = ImVec2(size.x + padding * 2.0f, size.y + padding * 2.0f); const ImVec2 box_position = AdjustPositionForAlignment(position, tracker_box_size, EmuConfig.Achievements.OverlayPosition); - + const ImVec2 box_min = box_position; const ImVec2 box_max = box_position + tracker_box_size; const float box_rounding = LayoutScale(1.0f); diff --git a/pcsx2/Config.h b/pcsx2/Config.h index b7a84a8472fdc..3c2dbeab893de 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -1285,9 +1285,6 @@ struct Pcsx2Config static constexpr u32 MAXIMUM_NOTIFICATION_DURATION = 30; static constexpr u32 DEFAULT_NOTIFICATION_DURATION = 5; static constexpr u32 DEFAULT_LEADERBOARD_DURATION = 10; - static constexpr const char* DEFAULT_INFO_SOUND_NAME = "sounds/achievements/message.wav"; - static constexpr const char* DEFAULT_UNLOCK_SOUND_NAME = "sounds/achievements/unlock.wav"; - static constexpr const char* DEFAULT_LBSUBMIT_SOUND_NAME = "sounds/achievements/lbsubmit.wav"; static const char* OverlayPositionNames[(size_t)AchievementOverlayPosition::MaxCount + 1]; diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp index 325771142f146..207d925cc10aa 100644 --- a/pcsx2/Pcsx2Config.cpp +++ b/pcsx2/Pcsx2Config.cpp @@ -1884,15 +1884,6 @@ void Pcsx2Config::AchievementsOptions::LoadSave(SettingsWrapper& wrap) { SettingsWrapSection("Achievements"); - if (InfoSoundName.empty()) - InfoSoundName = Path::Combine(EmuFolders::Resources, DEFAULT_INFO_SOUND_NAME); - - if (UnlockSoundName.empty()) - UnlockSoundName = Path::Combine(EmuFolders::Resources, DEFAULT_UNLOCK_SOUND_NAME); - - if (LBSubmitSoundName.empty()) - LBSubmitSoundName = Path::Combine(EmuFolders::Resources, DEFAULT_LBSUBMIT_SOUND_NAME); - SettingsWrapBitBool(Enabled); SettingsWrapBitBoolEx(HardcoreMode, "ChallengeMode"); SettingsWrapBitBool(EncoreMode);