Skip to content

Commit

Permalink
RetroAchievements/Qt: Add configurable achievement notification duration
Browse files Browse the repository at this point in the history
  • Loading branch information
TwosomesUP authored and stenzek committed Aug 8, 2023
1 parent 2ef1589 commit f2c032b
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 29 deletions.
15 changes: 11 additions & 4 deletions pcsx2-qt/SettingWidgetBinder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1078,15 +1078,20 @@ namespace SettingWidgetBinder
static inline void BindSliderToIntSetting(SettingsInterface* sif, QSlider* slider, QLabel* label, const QString& label_suffix,
std::string section, std::string key, s32 default_value)
{
const s32 global_value = Host::GetBaseIntSettingValue(section.c_str(), key.c_str(), default_value);
s32 global_value = Host::GetBaseIntSettingValue(section.c_str(), key.c_str(), default_value);

//Clamp in case setting was updated manually using INI
global_value = std::clamp(global_value, slider->minimum(), slider->maximum());

if (sif)
{
const QFont orig_font(label->font());
QFont bold_font(orig_font);
bold_font.setBold(true);

const s32 current_value = sif->GetOptionalIntValue(section.c_str(), key.c_str()).value_or(global_value);
s32 current_value = sif->GetOptionalIntValue(section.c_str(), key.c_str()).value_or(global_value);
current_value = std::clamp(current_value, slider->minimum(), slider->maximum());

slider->setValue(current_value);

label->setText(QStringLiteral("%1%2").arg(current_value).arg(label_suffix));
Expand All @@ -1100,8 +1105,10 @@ namespace SettingWidgetBinder
[sif, slider, label, label_suffix, orig_font = std::move(orig_font), section, key, default_value](const QPoint& pt) {
QMenu menu(slider);
slider->connect(menu.addAction(qApp->translate("SettingWidgetBinder", "Reset")), &QAction::triggered, slider,
[sif, label, label_suffix, orig_font, section, key, default_value]() {
const s32 global_value = Host::GetBaseIntSettingValue(section.c_str(), key.c_str(), default_value);
[sif, slider, label, label_suffix, orig_font, section, key, default_value]() {
s32 global_value = Host::GetBaseIntSettingValue(section.c_str(), key.c_str(), default_value);
global_value = std::clamp(global_value, slider->minimum(), slider->maximum());

label->setText(QStringLiteral("%1%2").arg(global_value).arg(label_suffix));
label->setFont(orig_font);

Expand Down
25 changes: 25 additions & 0 deletions pcsx2-qt/Settings/AchievementSettingsWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
#include <QtCore/QDateTime>
#include <QtWidgets/QMessageBox>

static constexpr s32 DEFAULT_NOTIFICATIONS_DURATION = 5;

AchievementSettingsWidget::AchievementSettingsWidget(SettingsDialog* dialog, QWidget* parent)
: QWidget(parent)
, m_dialog(dialog)
Expand All @@ -48,6 +50,9 @@ AchievementSettingsWidget::AchievementSettingsWidget(SettingsDialog* dialog, QWi
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.soundEffects, "Achievements", "SoundEffects", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.primedIndicators, "Achievements", "PrimedIndicators", true);

SettingWidgetBinder::BindSliderToIntSetting(sif, m_ui.notifications_duration, m_ui.notifications_duration_seconds,
tr(" seconds"), "Achievements", "NotificationsDuration", DEFAULT_NOTIFICATIONS_DURATION);

dialog->registerWidgetHelp(m_ui.enable, tr("Enable Achievements"), tr("Unchecked"),
tr("When enabled and logged in, PCSX2 will scan for achievements on game load."));
dialog->registerWidgetHelp(m_ui.testMode, tr("Enable Test Mode"), tr("Unchecked"),
Expand All @@ -70,10 +75,18 @@ AchievementSettingsWidget::AchievementSettingsWidget(SettingsDialog* dialog, QWi
dialog->registerWidgetHelp(m_ui.primedIndicators, tr("Show Challenge Indicators"), tr("Checked"),
tr("Shows icons in the lower-right corner of the screen when a challenge/primed achievement is active."));

dialog->registerWidgetHelp(m_ui.notifications_duration, tr("Notification Duration"),
tr("5 seconds"), tr("The duration, in seconds, an achievement popup notification will remain on screen."));
dialog->registerWidgetHelp(m_ui.notifications_duration_label, tr("Notification Duration"),
tr("5 seconds"), tr("The duration, in seconds, an achievement popup notification will remain on screen."));
dialog->registerWidgetHelp(m_ui.notifications_duration_seconds, tr("Notification Duration"),
tr("5 seconds"), tr("The duration, in seconds, an achievement popup notification will remain on screen."));

connect(m_ui.enable, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::updateEnableState);
connect(m_ui.notifications, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::updateEnableState);
connect(m_ui.challengeMode, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::updateEnableState);
connect(m_ui.challengeMode, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::onChallengeModeStateChanged);
connect(m_ui.notifications_duration, &QSlider::valueChanged, this, &AchievementSettingsWidget::onNotificationsDurationChanged);

if (!m_dialog->isPerGameSettings())
{
Expand Down Expand Up @@ -105,6 +118,8 @@ void AchievementSettingsWidget::updateEnableState()
{
const bool enabled = m_dialog->getEffectiveBoolValue("Achievements", "Enabled", false);
const bool challenge = m_dialog->getEffectiveBoolValue("Achievements", "ChallengeMode", false);
const bool notifications = m_dialog->getEffectiveBoolValue("Achievements", "Notifications", true);

m_ui.testMode->setEnabled(enabled);
m_ui.unofficialTestMode->setEnabled(enabled);
m_ui.richPresence->setEnabled(enabled);
Expand All @@ -113,6 +128,10 @@ void AchievementSettingsWidget::updateEnableState()
m_ui.notifications->setEnabled(enabled);
m_ui.soundEffects->setEnabled(enabled);
m_ui.primedIndicators->setEnabled(enabled);

m_ui.notifications_duration->setEnabled(enabled && notifications);
m_ui.notifications_duration_label->setEnabled(enabled && notifications);
m_ui.notifications_duration_seconds->setEnabled(enabled && notifications);
}

void AchievementSettingsWidget::onChallengeModeStateChanged()
Expand Down Expand Up @@ -196,3 +215,9 @@ void AchievementSettingsWidget::onAchievementsRefreshed(quint32 id, const QStrin
{
m_ui.gameInfo->setText(game_info_string);
}

void AchievementSettingsWidget::onNotificationsDurationChanged()
{
m_ui.notifications_duration_seconds->setText(tr("%1 seconds")
.arg(m_ui.notifications_duration->value()));
}
1 change: 1 addition & 0 deletions pcsx2-qt/Settings/AchievementSettingsWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ private Q_SLOTS:
void onLoginLogoutPressed();
void onViewProfilePressed();
void onAchievementsRefreshed(quint32 id, const QString& game_info_string, quint32 total, quint32 points);
void onNotificationsDurationChanged();

private:
void updateLoginState();
Expand Down
99 changes: 83 additions & 16 deletions pcsx2-qt/Settings/AchievementSettingsWidget.ui
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>658</width>
<height>496</height>
<width>829</width>
<height>641</height>
</rect>
</property>
<property name="windowTitle">
Expand Down Expand Up @@ -39,13 +39,6 @@
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="primedIndicators">
<property name="text">
<string>Show Challenge Indicators</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="richPresence">
<property name="text">
Expand Down Expand Up @@ -74,13 +67,6 @@
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="testMode">
<property name="text">
<string>Enable Test Mode</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="soundEffects">
<property name="text">
Expand All @@ -89,12 +75,93 @@
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="primedIndicators">
<property name="text">
<string>Show Challenge Indicators</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="testMode">
<property name="text">
<string>Enable Test Mode</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="notificationBox">
<property name="title">
<string>Notifications</string>
</property>
<layout class="QVBoxLayout" name="notifications_box_layout" stretch="0,0">
<item>
<widget class="QCheckBox" name="notifications">
<property name="text">
<string>Show Notifications</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="notifications_duration_layout" stretch="0,0,0">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="notifications_duration_label">
<property name="text">
<string>Duration</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="notifications_duration">
<property name="minimum">
<number>3</number>
</property>
<property name="maximum">
<number>10</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>5</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="invertedAppearance">
<bool>false</bool>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>1</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="notifications_duration_seconds">
<property name="text">
<string>5 seconds</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
Expand Down
24 changes: 16 additions & 8 deletions pcsx2/Achievements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ namespace Achievements
static void UnlockAchievementCallback(s32 status_code, const std::string& content_type, Common::HTTPDownloader::Request::Data data);
static void SubmitLeaderboardCallback(s32 status_code, const std::string& content_type, Common::HTTPDownloader::Request::Data data, u32 lboard_id);

static s32 GetNotificationsDuration();

static bool s_active = false;
static bool s_logged_in = false;
static bool s_challenge_mode = false;
Expand Down Expand Up @@ -1117,9 +1119,9 @@ void Achievements::DisplayAchievementSummary()
summary.append(TRANSLATE_SV("Achievements", "Leaderboard submission is enabled."));
}

MTGS::RunOnGSThread([title = std::move(title), summary = std::move(summary), icon = s_game_icon]() {
MTGS::RunOnGSThread([duration = GetNotificationsDuration(), title = std::move(title), summary = std::move(summary), icon = s_game_icon]() {
if (FullscreenUI::IsInitialized())
ImGuiFullscreen::AddNotification(10.0f, std::move(title), std::move(summary), std::move(icon));
ImGuiFullscreen::AddNotification(duration, std::move(title), std::move(summary), std::move(icon));
});
}

Expand All @@ -1137,9 +1139,9 @@ void Achievements::DisplayMasteredNotification()
std::string message(fmt::format(
"{} achievements, {} points{}", GetAchievementCount(), GetCurrentPointsForGame(), s_challenge_mode ? " (Hardcore Mode)" : ""));

MTGS::RunOnGSThread([title = std::move(title), message = std::move(message), icon = s_game_icon]() {
MTGS::RunOnGSThread([duration = GetNotificationsDuration(), title = std::move(title), message = std::move(message), icon = s_game_icon]() {
if (FullscreenUI::IsInitialized())
ImGuiFullscreen::AddNotification(20.0f, std::move(title), std::move(message), std::move(icon));
ImGuiFullscreen::AddNotification(duration, std::move(title), std::move(message), std::move(icon));
});
}

Expand Down Expand Up @@ -1850,9 +1852,9 @@ void Achievements::SubmitLeaderboardCallback(s32 status_code, const std::string&
std::string summary = fmt::format(
"Your Score: {} (Best: {})\nLeaderboard Position: {} of {}", submitted_score, best_score, response.new_rank, response.num_entries);

MTGS::RunOnGSThread([title = lb->title, summary = std::move(summary), icon = s_game_icon]() {
MTGS::RunOnGSThread([duration = GetNotificationsDuration(), title = lb->title, summary = std::move(summary), icon = s_game_icon]() {
if (FullscreenUI::IsInitialized())
ImGuiFullscreen::AddNotification(10.0f, std::move(title), std::move(summary), std::move(icon));
ImGuiFullscreen::AddNotification(duration, std::move(title), std::move(summary), std::move(icon));
});
}

Expand Down Expand Up @@ -1894,8 +1896,8 @@ void Achievements::UnlockAchievement(u32 achievement_id, bool add_notification /
}

MTGS::RunOnGSThread(
[title = std::move(title), description = achievement->description, icon = GetAchievementBadgePath(*achievement)]() {
ImGuiFullscreen::AddNotification(15.0f, std::move(title), std::move(description), std::move(icon));
[duration = GetNotificationsDuration(), title = std::move(title), description = achievement->description, icon = GetAchievementBadgePath(*achievement)]() {
ImGuiFullscreen::AddNotification(duration, std::move(title), std::move(description), std::move(icon));
});
}

Expand Down Expand Up @@ -2153,6 +2155,12 @@ void Achievements::PokeMemory(unsigned address, unsigned num_bytes, void* ud, un
}
}


s32 Achievements::GetNotificationsDuration()
{
return EmuConfig.Achievements.NotificationsDuration;
}

#ifdef ENABLE_RAINTEGRATION

#include "RA_Consoles.h"
Expand Down
4 changes: 3 additions & 1 deletion pcsx2/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -1262,12 +1262,14 @@ struct Pcsx2Config
PrimedIndicators : 1;
BITFIELD_END

s32 NotificationsDuration = 5;

AchievementsOptions();
void LoadSave(SettingsWrapper& wrap);

bool operator==(const AchievementsOptions& right) const
{
return OpEqu(bitset);
return OpEqu(bitset) && OpEqu(NotificationsDuration);
}

bool operator!=(const AchievementsOptions& right) const
Expand Down
8 changes: 8 additions & 0 deletions pcsx2/Pcsx2Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1429,6 +1429,7 @@ Pcsx2Config::AchievementsOptions::AchievementsOptions()
Notifications = true;
SoundEffects = true;
PrimedIndicators = true;
NotificationsDuration = 5;
}

void Pcsx2Config::AchievementsOptions::LoadSave(SettingsWrapper& wrap)
Expand All @@ -1444,6 +1445,13 @@ void Pcsx2Config::AchievementsOptions::LoadSave(SettingsWrapper& wrap)
SettingsWrapBitBool(Notifications);
SettingsWrapBitBool(SoundEffects);
SettingsWrapBitBool(PrimedIndicators);
SettingsWrapBitfield(NotificationsDuration);

if (wrap.IsLoading())
{
//Clamp in case setting was updated manually using the INI
NotificationsDuration = std::clamp(NotificationsDuration, 3, 10);
}
}

#endif
Expand Down

0 comments on commit f2c032b

Please sign in to comment.