OSD/Achievements: Add 9-position alignment options for Achievement notifications/popups, and OSD

This commit is contained in:
SternXD 2025-07-02 14:21:17 -04:00 committed by lightningterror
parent ab19b109ce
commit 76b758dbd2
13 changed files with 769 additions and 42 deletions

View File

@ -9,6 +9,7 @@
#include "QtUtils.h"
#include "pcsx2/Achievements.h"
#include "pcsx2/Config.h"
#include "pcsx2/Host.h"
#include "common/StringUtil.h"
@ -35,6 +36,8 @@ AchievementSettingsWidget::AchievementSettingsWidget(SettingsWindow* dialog, QWi
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.unlockSound, "Achievements", "UnlockSound", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.lbSound, "Achievements", "LBSubmitSound", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.overlays, "Achievements", "Overlays", true);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.overlayPosition, "Achievements", "OverlayPosition", static_cast<int>(AchievementOverlayPosition::BottomRight));
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.notificationPosition, "Achievements", "NotificationPosition", static_cast<int>(OsdOverlayPos::TopLeft));
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.encoreMode, "Achievements", "EncoreMode", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.spectatorMode, "Achievements", "SpectatorMode", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.unofficialAchievements, "Achievements", "UnofficialTestMode",false);
@ -52,6 +55,8 @@ AchievementSettingsWidget::AchievementSettingsWidget(SettingsWindow* dialog, QWi
dialog->registerWidgetHelp(m_ui.soundEffects, tr("Enable Sound Effects"), tr("Checked"), tr("Plays sound effects for events such as achievement unlocks and leaderboard submissions."));
dialog->registerWidgetHelp(m_ui.soundEffectsBox, tr("Custom Sound Effect"), tr("Any"), tr("Customize the sound effect that are played whenever you received a notification, earned an achievement or submitted an entry to the leaderboard."));
dialog->registerWidgetHelp(m_ui.overlays, tr("Enable In-Game Overlays"), tr("Checked"), tr("Shows icons in the lower-right corner of the screen when a challenge/primed achievement is active."));
dialog->registerWidgetHelp(m_ui.overlayPosition, tr("Overlay Position"), tr("Bottom Right"), tr("Determines where achievement overlays are positioned on the screen."));
dialog->registerWidgetHelp(m_ui.notificationPosition, tr("Notification Position"), tr("Top Left"), tr("Determines where achievement notification popups are positioned on the screen."));
dialog->registerWidgetHelp(m_ui.encoreMode, tr("Enable Encore Mode"), tr("Unchecked"),tr("When enabled, each session will behave as if no achievements have been unlocked."));
dialog->registerWidgetHelp(m_ui.spectatorMode, tr("Enable Spectator Mode"), tr("Unchecked"), tr("When enabled, PCSX2 will assume all achievements are locked and not send any unlock notifications to the server."));
dialog->registerWidgetHelp(m_ui.unofficialAchievements, tr("Test Unofficial Achievements"), tr("Unchecked"), tr("When enabled, PCSX2 will list achievements from unofficial sets. Please note that these achievements are not tracked by RetroAchievements, so they unlock every time."));
@ -65,6 +70,7 @@ AchievementSettingsWidget::AchievementSettingsWidget(SettingsWindow* dialog, QWi
connect(m_ui.notificationSound, &QCheckBox::checkStateChanged, this, &AchievementSettingsWidget::updateEnableState);
connect(m_ui.unlockSound, &QCheckBox::checkStateChanged, this, &AchievementSettingsWidget::updateEnableState);
connect(m_ui.lbSound, &QCheckBox::checkStateChanged, this, &AchievementSettingsWidget::updateEnableState);
connect(m_ui.overlays, &QCheckBox::checkStateChanged, this, &AchievementSettingsWidget::updateEnableState);
connect(m_ui.achievementNotificationsDuration, &QSlider::valueChanged, this, &AchievementSettingsWidget::onAchievementsNotificationDurationSliderChanged);
connect(m_ui.leaderboardNotificationsDuration, &QSlider::valueChanged, this, &AchievementSettingsWidget::onLeaderboardsNotificationDurationSliderChanged);
@ -139,6 +145,16 @@ void AchievementSettingsWidget::updateEnableState()
m_ui.soundEffects->setEnabled(enabled);
m_ui.overlays->setEnabled(enabled);
const bool overlays_enabled = enabled && m_dialog->getEffectiveBoolValue("Achievements", "Overlays", true);
const bool notifications_enabled = enabled && (m_dialog->getEffectiveBoolValue("Achievements", "Notifications", true) ||
m_dialog->getEffectiveBoolValue("Achievements", "LeaderboardNotifications", true));
m_ui.overlaySettingsBox->setEnabled(overlays_enabled || notifications_enabled);
m_ui.overlayPosition->setEnabled(overlays_enabled);
m_ui.overlayPositionLabel->setEnabled(overlays_enabled);
m_ui.notificationPosition->setEnabled(notifications_enabled);
m_ui.notificationPositionLabel->setEnabled(notifications_enabled);
m_ui.encoreMode->setEnabled(enabled);
m_ui.spectatorMode->setEnabled(enabled);
m_ui.unofficialAchievements->setEnabled(enabled);

View File

@ -197,6 +197,133 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="overlaySettingsBox">
<property name="title">
<string>Overlay Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_overlay">
<item row="0" column="0">
<widget class="QLabel" name="overlayPositionLabel">
<property name="text">
<string>Overlay Position:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="overlayPosition">
<item>
<property name="text">
<string>Top Left</string>
</property>
</item>
<item>
<property name="text">
<string>Top Center</string>
</property>
</item>
<item>
<property name="text">
<string>Top Right</string>
</property>
</item>
<item>
<property name="text">
<string>Center Left</string>
</property>
</item>
<item>
<property name="text">
<string>Center</string>
</property>
</item>
<item>
<property name="text">
<string>Center Right</string>
</property>
</item>
<item>
<property name="text">
<string>Bottom Left</string>
</property>
</item>
<item>
<property name="text">
<string>Bottom Center</string>
</property>
</item>
<item>
<property name="text">
<string>Bottom Right (Default)</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="notificationPositionLabel">
<property name="text">
<string>Notification Position:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="notificationPosition">
<item>
<property name="text">
<string>None</string>
</property>
</item>
<item>
<property name="text">
<string>Top Left (Default)</string>
</property>
</item>
<item>
<property name="text">
<string>Top Center</string>
</property>
</item>
<item>
<property name="text">
<string>Top Right</string>
</property>
</item>
<item>
<property name="text">
<string>Center Left</string>
</property>
</item>
<item>
<property name="text">
<string>Center</string>
</property>
</item>
<item>
<property name="text">
<string>Center Right</string>
</property>
</item>
<item>
<property name="text">
<string>Bottom Left</string>
</property>
</item>
<item>
<property name="text">
<string>Bottom Center</string>
</property>
</item>
<item>
<property name="text">
<string>Bottom Right</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="soundEffectsBox">
<property name="title">

View File

@ -1612,12 +1612,47 @@
</item>
<item>
<property name="text">
<string>Left (Default)</string>
<string>Top Left (Default)</string>
</property>
</item>
<item>
<property name="text">
<string>Right</string>
<string>Top Center</string>
</property>
</item>
<item>
<property name="text">
<string>Top Right</string>
</property>
</item>
<item>
<property name="text">
<string>Center Left</string>
</property>
</item>
<item>
<property name="text">
<string>Center</string>
</property>
</item>
<item>
<property name="text">
<string>Center Right</string>
</property>
</item>
<item>
<property name="text">
<string>Bottom Left</string>
</property>
</item>
<item>
<property name="text">
<string>Bottom Center</string>
</property>
</item>
<item>
<property name="text">
<string>Bottom Right</string>
</property>
</item>
</widget>
@ -1631,12 +1666,47 @@
</item>
<item>
<property name="text">
<string>Left</string>
<string>Top Left</string>
</property>
</item>
<item>
<property name="text">
<string>Right (Default)</string>
<string>Top Center</string>
</property>
</item>
<item>
<property name="text">
<string>Top Right (Default)</string>
</property>
</item>
<item>
<property name="text">
<string>Center Left</string>
</property>
</item>
<item>
<property name="text">
<string>Center</string>
</property>
</item>
<item>
<property name="text">
<string>Center Right</string>
</property>
</item>
<item>
<property name="text">
<string>Bottom Left</string>
</property>
</item>
<item>
<property name="text">
<string>Bottom Center</string>
</property>
</item>
<item>
<property name="text">
<string>Bottom Right</string>
</property>
</item>
</widget>

View File

@ -164,6 +164,7 @@ namespace Achievements
static void DisplayHardcoreDeferredMessage();
static void DisplayAchievementSummary();
static void UpdateRichPresence(std::unique_lock<std::recursive_mutex>& lock);
static void UpdateNotificationPosition();
static std::string GetAchievementBadgePath(const rc_client_achievement_t* achievement, int state);
static std::string GetUserBadgePath(const std::string_view username);
@ -444,6 +445,9 @@ bool Achievements::Initialize()
if (VMManager::HasValidVM() && IsLoggedInOrLoggingIn() && EmuConfig.Achievements.HardcoreMode)
DisplayHardcoreDeferredMessage();
// Set initial notification position
UpdateNotificationPosition();
return true;
}
@ -494,6 +498,63 @@ void Achievements::DestroyClient(rc_client_t** client, std::unique_ptr<HTTPDownl
http->reset();
}
void Achievements::UpdateNotificationPosition()
{
// Set notification position based on achievement settings
float horizontal_position, vertical_position, direction;
// Determine horizontal alignment
switch (EmuConfig.Achievements.NotificationPosition)
{
case OsdOverlayPos::TopLeft:
case OsdOverlayPos::CenterLeft:
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:
default:
horizontal_position = 1.0f; // Right
break;
}
// Determine vertical alignment and stacking direction
switch (EmuConfig.Achievements.NotificationPosition)
{
case OsdOverlayPos::TopLeft:
case OsdOverlayPos::TopCenter:
case OsdOverlayPos::TopRight:
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:
default:
vertical_position = 0.85f; // Bottom area
direction = -1.0f; // Stack upward
break;
}
ImGuiFullscreen::SetNotificationPosition(horizontal_position, vertical_position, direction);
}
void Achievements::UpdateSettings(const Pcsx2Config::AchievementsOptions& old_config)
{
if (IsUsingRAIntegration())
@ -549,6 +610,10 @@ void Achievements::UpdateSettings(const Pcsx2Config::AchievementsOptions& old_co
rc_client_set_unofficial_enabled(s_client, EmuConfig.Achievements.UnofficialTestMode);
}
// Update notification position if it changed
if (EmuConfig.Achievements.NotificationPosition != old_config.NotificationPosition)
UpdateNotificationPosition();
// in case cache directory changed
EnsureCacheDirectoriesExist();
}
@ -1915,6 +1980,115 @@ static float IndicatorOpacity(const T& i)
return (i.active) ? opacity : (1.0f - opacity);
}
static ImVec2 CalculateOverlayPosition(const ImGuiIO& io, float padding, AchievementOverlayPosition alignment)
{
switch (alignment)
{
case AchievementOverlayPosition::TopLeft:
return ImVec2(padding, padding);
case AchievementOverlayPosition::TopCenter:
return ImVec2(io.DisplaySize.x * 0.5f, padding);
case AchievementOverlayPosition::TopRight:
return ImVec2(io.DisplaySize.x - padding, padding);
case AchievementOverlayPosition::CenterLeft:
return ImVec2(padding, io.DisplaySize.y * 0.5f);
case AchievementOverlayPosition::Center:
return ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f);
case AchievementOverlayPosition::CenterRight:
return ImVec2(io.DisplaySize.x - padding, io.DisplaySize.y * 0.5f);
case AchievementOverlayPosition::BottomLeft:
return ImVec2(padding, io.DisplaySize.y - padding);
case AchievementOverlayPosition::BottomCenter:
return ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y - padding);
case AchievementOverlayPosition::BottomRight:
default:
return ImVec2(io.DisplaySize.x - padding, io.DisplaySize.y - padding);
}
}
static ImVec2 AdjustPositionForAlignment(const ImVec2& base_position, const ImVec2& element_size, AchievementOverlayPosition alignment)
{
ImVec2 adjusted = base_position;
// Adjust for horizontal alignment
switch (alignment)
{
case AchievementOverlayPosition::TopLeft:
case AchievementOverlayPosition::CenterLeft:
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:
default:
// Right aligned offset by full element width
adjusted.x -= element_size.x;
break;
}
// Adjust for vertical alignment
switch (alignment)
{
case AchievementOverlayPosition::TopLeft:
case AchievementOverlayPosition::TopCenter:
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:
default:
// Bottom aligned offset by full element height
adjusted.y -= element_size.y;
break;
}
return adjusted;
}
static ImVec2 GetStackingDirection(AchievementOverlayPosition alignment)
{
switch (alignment)
{
case AchievementOverlayPosition::TopLeft:
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
}
}
void Achievements::DrawGameOverlays()
{
@ -1930,13 +2104,14 @@ void Achievements::DrawGameOverlays()
const float padding = LayoutScale(10.0f);
const ImVec2 image_size = LayoutScale(ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT);
const ImGuiIO& io = ImGui::GetIO();
ImVec2 position = ImVec2(io.DisplaySize.x - padding, io.DisplaySize.y - padding);
ImVec2 position = CalculateOverlayPosition(io, padding, EmuConfig.Achievements.OverlayPosition);
ImDrawList* dl = ImGui::GetBackgroundDrawList();
if (!s_active_challenge_indicators.empty())
{
const ImVec2 stack_direction = GetStackingDirection(EmuConfig.Achievements.OverlayPosition);
ImVec2 current_position = AdjustPositionForAlignment(position, image_size, EmuConfig.Achievements.OverlayPosition);
const float x_advance = image_size.x + spacing;
ImVec2 current_position = ImVec2(position.x - image_size.x, position.y - image_size.y);
for (auto it = s_active_challenge_indicators.begin(); it != s_active_challenge_indicators.end();)
{
@ -1949,7 +2124,26 @@ void Achievements::DrawGameOverlays()
{
dl->AddImage(reinterpret_cast<ImTextureID>(badge->GetNativeHandle()),
current_position, current_position + image_size, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), col);
current_position.x -= x_advance;
// For horizontal layouts, go horizontally for vertical layouts, stay in the same row
if (std::abs(stack_direction.x) > 0.0f)
{
current_position.x += stack_direction.x * x_advance;
}
else
{
// For right-aligned vertical layouts, move left for next indicator
switch (EmuConfig.Achievements.OverlayPosition)
{
case AchievementOverlayPosition::TopRight:
case AchievementOverlayPosition::BottomRight:
current_position.x -= x_advance;
break;
default:
current_position.x += x_advance;
break;
}
}
}
if (!indicator.active && opacity <= 0.01f)
@ -1963,7 +2157,9 @@ void Achievements::DrawGameOverlays()
}
}
position.y -= image_size.y + padding;
// Go to the next row/column for anymore overlays
position.x += stack_direction.x * (image_size.x + padding);
position.y += stack_direction.y * (image_size.y + padding);
}
if (s_active_progress_indicator.has_value())
@ -1976,9 +2172,12 @@ void Achievements::DrawGameOverlays()
const char* text_end = text_start + std::strlen(text_start);
const ImVec2 text_size = g_medium_font->CalcTextSizeA(g_medium_font->FontSize, FLT_MAX, 0.0f, text_start, text_end);
const ImVec2 box_min =
ImVec2(position.x - image_size.x - text_size.x - spacing - padding * 2.0f, position.y - image_size.y - padding * 2.0f);
const ImVec2 box_max = position;
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);
dl->AddRectFilled(box_min, box_max, ImGui::GetColorU32(ImVec4(0.13f, 0.13f, 0.13f, opacity * 0.5f)), box_rounding);
@ -2002,11 +2201,15 @@ void Achievements::DrawGameOverlays()
s_active_progress_indicator.reset();
}
position.y -= image_size.y + padding * 3.0f;
// Go to the next row/column for anymore overlays
position.x += stack_direction.x * (progress_box_size.x + padding);
position.y += stack_direction.y * (progress_box_size.y + padding);
}
if (!s_active_leaderboard_trackers.empty())
{
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;
@ -2019,8 +2222,11 @@ void Achievements::DrawGameOverlays()
const ImVec2 size = ImGuiFullscreen::g_medium_font->CalcTextSizeA(
ImGuiFullscreen::g_medium_font->FontSize, FLT_MAX, 0.0f, width_string.c_str(), width_string.end_ptr());
const ImVec2 box_min = ImVec2(position.x - size.x - padding * 2.0f, position.y - size.y - padding * 2.0f);
const ImVec2 box_max = position;
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);
dl->AddRectFilled(box_min, box_max, ImGui::GetColorU32(ImVec4(0.13f, 0.13f, 0.13f, opacity * 0.5f)), box_rounding);
dl->AddRect(box_min, box_max, ImGui::GetColorU32(ImVec4(0.8f, 0.8f, 0.8f, opacity)), box_rounding);
@ -2048,7 +2254,9 @@ void Achievements::DrawGameOverlays()
++it;
}
position.x = box_min.x - padding;
// Go to the next position for anymore trackers
position.x += stack_direction.x * (tracker_box_size.x + padding);
position.y += stack_direction.y * (tracker_box_size.y + padding);
}
// Uncomment if there are any other overlays above this one.

View File

@ -341,7 +341,14 @@ enum class OsdOverlayPos : u8
{
None,
TopLeft,
TopCenter,
TopRight,
CenterLeft,
Center,
CenterRight,
BottomLeft,
BottomCenter,
BottomRight,
};
enum class TexturePreloadingLevel : u8
@ -452,6 +459,20 @@ enum class GSNativeScaling : u8
MaxCount
};
enum class AchievementOverlayPosition : u8
{
TopLeft,
TopCenter,
TopRight,
CenterLeft,
Center,
CenterRight,
BottomLeft,
BottomCenter,
BottomRight,
MaxCount
};
// --------------------------------------------------------------------------------------
// TraceLogsEE
// --------------------------------------------------------------------------------------
@ -1213,6 +1234,8 @@ struct Pcsx2Config
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];
BITFIELD32()
bool
Enabled : 1,
@ -1231,6 +1254,8 @@ struct Pcsx2Config
u32 NotificationsDuration = DEFAULT_NOTIFICATION_DURATION;
u32 LeaderboardsDuration = DEFAULT_LEADERBOARD_DURATION;
AchievementOverlayPosition OverlayPosition = AchievementOverlayPosition::BottomRight;
OsdOverlayPos NotificationPosition = OsdOverlayPos::TopLeft;
std::string InfoSoundName;
std::string UnlockSoundName;

View File

@ -9,6 +9,7 @@
#include "Gif_Unit.h"
#include "Host.h"
#include "ImGui/ImGuiManager.h"
#include "ImGui/ImGuiOverlays.h"
#include "R3000A.h"
#include "R5900.h"
#include "VMManager.h"
@ -379,8 +380,9 @@ void GSDumpReplayer::RenderUI()
do \
{ \
text_size = font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), -1.0f, (text), nullptr, nullptr); \
dl->AddText(font, font->FontSize, ImVec2(GSConfig.OsdPerformancePos == OsdOverlayPos::TopLeft ? ImGuiManager::GetWindowWidth() - margin - text_size.x + shadow_offset : margin + shadow_offset, position_y + shadow_offset), IM_COL32(0, 0, 0, 100), (text)); \
dl->AddText(font, font->FontSize, ImVec2(GSConfig.OsdPerformancePos == OsdOverlayPos::TopLeft ? ImGuiManager::GetWindowWidth() - margin - text_size.x : margin, position_y), color, (text)); \
const ImVec2 text_pos = CalculatePerformanceOverlayTextPosition(GSConfig.OsdPerformancePos, margin, text_size, ImGuiManager::GetWindowWidth(), position_y); \
dl->AddText(font, font->FontSize, ImVec2(text_pos.x + shadow_offset, text_pos.y + shadow_offset), IM_COL32(0, 0, 0, 100), (text)); \
dl->AddText(font, font->FontSize, text_pos, color, (text)); \
position_y += text_size.y + spacing; \
} while (0)

View File

@ -3653,6 +3653,30 @@ void FullscreenUI::DrawInterfaceSettingsPage()
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_TRIANGLE_EXCLAMATION, "Warn About Unsafe Settings"),
FSUI_CSTR("Displays warnings when settings are enabled which may break games."), "EmuCore", "WarnAboutUnsafeSettings", true);
// OSD Positioning Options
static constexpr const char* s_osd_position_options[] = {
FSUI_NSTR("None"),
FSUI_NSTR("Top Left"),
FSUI_NSTR("Top Center"),
FSUI_NSTR("Top Right"),
FSUI_NSTR("Center Left"),
FSUI_NSTR("Center"),
FSUI_NSTR("Center Right"),
FSUI_NSTR("Bottom Left"),
FSUI_NSTR("Bottom Center"),
FSUI_NSTR("Bottom Right"),
};
static constexpr const char* s_osd_position_values[] = {
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
};
DrawStringListSetting(bsi, FSUI_ICONSTR(ICON_FA_COMMENT, "OSD Messages Position"),
FSUI_CSTR("Determines where on-screen display messages are positioned."), "EmuCore/GS", "OsdMessagesPos", "1",
s_osd_position_options, s_osd_position_values, std::size(s_osd_position_options), true);
DrawStringListSetting(bsi, FSUI_ICONSTR(ICON_FA_CHART_BAR, "OSD Performance Position"),
FSUI_CSTR("Determines where performance statistics are positioned."), "EmuCore/GS", "OsdPerformancePos", "3",
s_osd_position_options, s_osd_position_values, std::size(s_osd_position_options), true);
MenuHeading(FSUI_CSTR("Operations"));
if (MenuButton(FSUI_ICONSTR(ICON_FA_DUMPSTER_FIRE, "Reset Settings"),
FSUI_CSTR("Resets configuration to defaults (excluding controller settings)."), !IsEditingGameSettings(bsi)))
@ -7598,6 +7622,34 @@ void FullscreenUI::DrawAchievementsSettingsPage(std::unique_lock<std::mutex>& se
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_PF_HEARTBEAT_ALT, "Enable In-Game Overlays"),
FSUI_CSTR("Shows icons in the lower-right corner of the screen when a challenge/primed achievement is active."), "Achievements",
"Overlays", true, enabled);
if (enabled)
{
const char* alignment_options[] = {
TRANSLATE_NOOP("FullscreenUI", "Top Left"),
TRANSLATE_NOOP("FullscreenUI", "Top Center"),
TRANSLATE_NOOP("FullscreenUI", "Top Right"),
TRANSLATE_NOOP("FullscreenUI", "Center Left"),
TRANSLATE_NOOP("FullscreenUI", "Center"),
TRANSLATE_NOOP("FullscreenUI", "Center Right"),
TRANSLATE_NOOP("FullscreenUI", "Bottom Left"),
TRANSLATE_NOOP("FullscreenUI", "Bottom Center"),
TRANSLATE_NOOP("FullscreenUI", "Bottom Right")
};
DrawIntListSetting(bsi, FSUI_ICONSTR(ICON_FA_ALIGN_CENTER, "Overlay Position"),
FSUI_CSTR("Determines where achievement overlays are positioned on the screen."), "Achievements", "OverlayPosition",
8, alignment_options, std::size(alignment_options), true, 0, enabled);
const bool notifications_enabled = Host::GetBaseBoolSettingValue("Achievements", "Notifications", true) ||
Host::GetBaseBoolSettingValue("Achievements", "LeaderboardNotifications", true);
if (notifications_enabled)
{
DrawIntListSetting(bsi, FSUI_ICONSTR(ICON_FA_BELL, "Notification Position"),
FSUI_CSTR("Determines where achievement notification popups are positioned on the screen."), "Achievements", "NotificationPosition",
2, alignment_options, std::size(alignment_options), true, 0, enabled);
}
}
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_LOCK, "Encore Mode"),
FSUI_CSTR("When enabled, each session will behave as if no achievements have been unlocked."), "Achievements", "EncoreMode", false,
enabled);

View File

@ -181,6 +181,9 @@ namespace ImGuiFullscreen
};
static std::vector<Notification> s_notifications;
static float s_notification_vertical_position = 0.15f;
static float s_notification_vertical_direction = 1.0f;
static float s_notification_horizontal_position = 0.0f; // 0.0 = left, 0.5 = center, 1.0 = right
static std::string s_toast_title;
static std::string s_toast_message;
@ -697,7 +700,17 @@ void ImGuiFullscreen::EndLayout()
const float notification_margin = LayoutScale(10.0f);
const float spacing = LayoutScale(10.0f);
const float notification_vertical_pos = GetNotificationVerticalPosition();
ImVec2 position(notification_margin, notification_vertical_pos * ImGui::GetIO().DisplaySize.y +
// Get the horizonal position based on alignment
float horizontal_pos;
if (s_notification_horizontal_position <= 0.0f)
horizontal_pos = notification_margin; // Left
else if (s_notification_horizontal_position >= 1.0f)
horizontal_pos = ImGui::GetIO().DisplaySize.x - notification_margin; // Right
else
horizontal_pos = ImGui::GetIO().DisplaySize.x * s_notification_horizontal_position; // Center
ImVec2 position(horizontal_pos, notification_vertical_pos * ImGui::GetIO().DisplaySize.y +
((notification_vertical_pos >= 0.5f) ? -notification_margin : notification_margin));
DrawBackgroundProgressDialogs(position, spacing);
DrawNotifications(position, spacing);
@ -2672,9 +2685,6 @@ void ImGuiFullscreen::DrawMessageDialog()
}
}
static float s_notification_vertical_position = 0.15f;
static float s_notification_vertical_direction = 1.0f;
float ImGuiFullscreen::GetNotificationVerticalPosition()
{
return s_notification_vertical_position;
@ -2691,6 +2701,13 @@ void ImGuiFullscreen::SetNotificationVerticalPosition(float position, float dire
s_notification_vertical_direction = direction;
}
void ImGuiFullscreen::SetNotificationPosition(float horizontal_position, float vertical_position, float direction)
{
s_notification_horizontal_position = horizontal_position;
s_notification_vertical_position = vertical_position;
s_notification_vertical_direction = direction;
}
ImGuiID ImGuiFullscreen::GetBackgroundProgressID(const char* str_id)
{
return ImHashStr(str_id);
@ -2952,7 +2969,14 @@ void ImGuiFullscreen::DrawNotifications(ImVec2& position, float spacing)
}
}
const ImVec2 box_min(position.x, actual_y);
// Adjust horizontal position based on alignment
float final_x = position.x;
if (s_notification_horizontal_position >= 1.0f)
final_x = position.x - box_width;
else if (s_notification_horizontal_position > 0.0f && s_notification_horizontal_position < 1.0f)
final_x = position.x - (box_width * 0.5f);
const ImVec2 box_min(final_x, actual_y);
const ImVec2 box_max(box_min.x + box_width, box_min.y + box_height);
const u32 background_color = (toast_background_color & ~IM_COL32_A_MASK) | (opacity << IM_COL32_A_SHIFT);
const u32 border_color = (toast_border_color & ~IM_COL32_A_MASK) | (opacity << IM_COL32_A_SHIFT);

View File

@ -273,6 +273,7 @@ namespace ImGuiFullscreen
float GetNotificationVerticalPosition();
float GetNotificationVerticalDirection();
void SetNotificationVerticalPosition(float position, float direction);
void SetNotificationPosition(float horizontal_position, float vertical_position, float direction);
void OpenBackgroundProgressDialog(const char* str_id, std::string message, s32 min, s32 max, s32 value);
void UpdateBackgroundProgressDialog(const char* str_id, std::string message, s32 min, s32 max, s32 value);

View File

@ -730,8 +730,34 @@ void ImGuiManager::DrawOSDMessages(Common::Timer::Value current_time)
const float padding = std::ceil(8.0f * scale);
const float rounding = std::ceil(5.0f * scale);
const float max_width = s_window_width - (margin + padding) * 2.0f;
float position_x = GSConfig.OsdMessagesPos == OsdOverlayPos::TopRight ? GetWindowWidth() - margin : margin;
float position_y = margin;
switch (GSConfig.OsdMessagesPos)
{
case OsdOverlayPos::TopLeft:
case OsdOverlayPos::TopCenter:
case OsdOverlayPos::TopRight:
position_y = margin;
break;
case OsdOverlayPos::CenterLeft:
case OsdOverlayPos::Center:
case OsdOverlayPos::CenterRight:
position_y = s_window_height * 0.5f;
break;
case OsdOverlayPos::BottomLeft:
case OsdOverlayPos::BottomCenter:
case OsdOverlayPos::BottomRight:
// For bottom positions, start from the bottom and let messages stack upward
position_y = s_window_height - margin;
break;
case OsdOverlayPos::None:
default:
position_y = margin;
break;
}
auto iter = s_osd_active_messages.begin();
while (iter != s_osd_active_messages.end())
@ -798,7 +824,18 @@ void ImGuiManager::DrawOSDMessages(Common::Timer::Value current_time)
const ImVec2 text_size(
font->CalcTextSizeA(font->FontSize, max_width, max_width, msg.text.c_str(), msg.text.c_str() + msg.text.length()));
const ImVec2 size(text_size.x + padding * 2.0f, text_size.y + padding * 2.0f);
const ImVec2 pos(position_x - (GSConfig.OsdMessagesPos == OsdOverlayPos::TopRight ? size.x : 0), actual_y);
// For bottom positions, adjust actual_y to try to account for message height
float final_y = actual_y;
if (GSConfig.OsdMessagesPos == OsdOverlayPos::BottomLeft ||
GSConfig.OsdMessagesPos == OsdOverlayPos::BottomCenter ||
GSConfig.OsdMessagesPos == OsdOverlayPos::BottomRight)
{
final_y = actual_y - size.y;
}
const ImVec2 base_pos = CalculateOSDPosition(GSConfig.OsdMessagesPos, margin, size, s_window_width, s_window_height);
const ImVec2 pos(base_pos.x, final_y);
const ImVec4 text_rect(pos.x + padding, pos.y + padding, pos.x + size.x - padding, pos.y + size.y - padding);
ImDrawList* dl = ImGui::GetBackgroundDrawList();
@ -806,7 +843,18 @@ void ImGuiManager::DrawOSDMessages(Common::Timer::Value current_time)
dl->AddRect(pos, ImVec2(pos.x + size.x, pos.y + size.y), IM_COL32(0x48, 0x48, 0x48, opacity), rounding);
dl->AddText(font, font->FontSize, ImVec2(text_rect.x, text_rect.y), IM_COL32(0xff, 0xff, 0xff, opacity), msg.text.c_str(),
msg.text.c_str() + msg.text.length(), max_width, &text_rect);
position_y += size.y + spacing;
// Stack direction depends on the position upward for bottom positions, downward for others
if (GSConfig.OsdMessagesPos == OsdOverlayPos::BottomLeft ||
GSConfig.OsdMessagesPos == OsdOverlayPos::BottomCenter ||
GSConfig.OsdMessagesPos == OsdOverlayPos::BottomRight)
{
position_y -= (size.y + spacing); // Stack upward for bottom positions
}
else
{
position_y += size.y + spacing; // Stack downward for top/center positions
}
}
}

View File

@ -49,6 +49,71 @@
InputRecordingUI::InputRecordingData g_InputRecordingData;
// OSD positioning funcs
ImVec2 CalculateOSDPosition(OsdOverlayPos position, float margin, const ImVec2& text_size, float window_width, float window_height)
{
switch (position)
{
case OsdOverlayPos::TopLeft:
return ImVec2(margin, margin);
case OsdOverlayPos::TopCenter:
return ImVec2((window_width - text_size.x) * 0.5f, margin);
case OsdOverlayPos::TopRight:
return ImVec2(window_width - margin - text_size.x, margin);
case OsdOverlayPos::CenterLeft:
return ImVec2(margin, (window_height - text_size.y) * 0.5f);
case OsdOverlayPos::Center:
return ImVec2((window_width - text_size.x) * 0.5f, (window_height - text_size.y) * 0.5f);
case OsdOverlayPos::CenterRight:
return ImVec2(window_width - margin - text_size.x, (window_height - text_size.y) * 0.5f);
case OsdOverlayPos::BottomLeft:
return ImVec2(margin, window_height - margin - text_size.y);
case OsdOverlayPos::BottomCenter:
return ImVec2((window_width - text_size.x) * 0.5f, window_height - margin - text_size.y);
case OsdOverlayPos::BottomRight:
return ImVec2(window_width - margin - text_size.x, window_height - margin - text_size.y);
case OsdOverlayPos::None:
default:
return ImVec2(0.0f, 0.0f);
}
}
ImVec2 CalculatePerformanceOverlayTextPosition(OsdOverlayPos position, float margin, const ImVec2& text_size, float window_width, float position_y)
{
const float abs_margin = std::abs(margin);
// Get the X position based on horizontal alignment
float x_pos;
switch (position)
{
case OsdOverlayPos::TopLeft:
case OsdOverlayPos::CenterLeft:
case OsdOverlayPos::BottomLeft:
x_pos = abs_margin; // Left alignment
break;
case OsdOverlayPos::TopCenter:
case OsdOverlayPos::Center:
case OsdOverlayPos::BottomCenter:
x_pos = (window_width - text_size.x) * 0.5f; // Center alignment
break;
case OsdOverlayPos::TopRight:
case OsdOverlayPos::CenterRight:
case OsdOverlayPos::BottomRight:
default:
x_pos = window_width - text_size.x - abs_margin; // Right alignment
break;
}
return ImVec2(x_pos, position_y);
}
bool ShouldUseLeftAlignment(OsdOverlayPos position)
{
return (position == OsdOverlayPos::TopLeft || position == OsdOverlayPos::CenterLeft || position == OsdOverlayPos::BottomLeft);
}
namespace ImGuiManager
{
static void FormatProcessorStat(SmallStringBase& text, double usage, double time);
@ -107,17 +172,38 @@ __ri void ImGuiManager::DrawPerformanceOverlay(float& position_y, float scale, f
SmallString text;
ImVec2 text_size;
if (GSConfig.OsdPerformancePos == OsdOverlayPos::TopLeft)
margin = -margin;
// Adjust initial Y position based on vertical alignment
switch (GSConfig.OsdPerformancePos)
{
case OsdOverlayPos::CenterLeft:
case OsdOverlayPos::Center:
case OsdOverlayPos::CenterRight:
position_y = (GetWindowHeight() - (fixed_font->FontSize * 8.0f)) * 0.5f;
break;
case OsdOverlayPos::BottomLeft:
case OsdOverlayPos::BottomCenter:
case OsdOverlayPos::BottomRight:
position_y = GetWindowHeight() - margin - (fixed_font->FontSize * 15.0f + spacing * 14.0f);
break;
case OsdOverlayPos::TopLeft:
case OsdOverlayPos::TopCenter:
case OsdOverlayPos::TopRight:
default:
// Top alignment keeps the passed position_y
break;
}
#define DRAW_LINE(font, text, color) \
do \
{ \
text_size = font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), -1.0f, (text), nullptr, nullptr); \
dl->AddText(font, font->FontSize, \
ImVec2((GSConfig.OsdPerformancePos == OsdOverlayPos::TopLeft ? 0 : GetWindowWidth() - text_size.x) - margin + shadow_offset, position_y + shadow_offset), \
IM_COL32(0, 0, 0, 100), (text)); \
dl->AddText(font, font->FontSize, ImVec2((GSConfig.OsdPerformancePos == OsdOverlayPos::TopLeft ? 0 : GetWindowWidth() - text_size.x) - margin, position_y), color, (text)); \
const ImVec2 text_pos = CalculatePerformanceOverlayTextPosition(GSConfig.OsdPerformancePos, margin, text_size, GetWindowWidth(), position_y); \
dl->AddText(font, font->FontSize, ImVec2(text_pos.x + shadow_offset, text_pos.y + shadow_offset), IM_COL32(0, 0, 0, 100), (text)); \
dl->AddText(font, font->FontSize, text_pos, color, (text)); \
position_y += text_size.y + spacing; \
} while (0)
@ -299,7 +385,9 @@ __ri void ImGuiManager::DrawPerformanceOverlay(float& position_y, float scale, f
{
const ImVec2 history_size(200.0f * scale, 50.0f * scale);
ImGui::SetNextWindowSize(ImVec2(history_size.x, history_size.y));
ImGui::SetNextWindowPos(ImVec2((GSConfig.OsdPerformancePos == OsdOverlayPos::TopLeft ? 0 : GetWindowWidth() - history_size.x) - margin, position_y));
const ImVec2 window_pos = CalculatePerformanceOverlayTextPosition(GSConfig.OsdPerformancePos, margin, history_size, GetWindowWidth(), position_y);
ImGui::SetNextWindowPos(window_pos);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.25f));
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
ImGui::PushStyleColor(ImGuiCol_PlotLines, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
@ -336,20 +424,59 @@ __ri void ImGuiManager::DrawPerformanceOverlay(float& position_y, float scale, f
text.clear();
text.append_format("Max: {:.1f} ms", max);
text_size = fixed_font->CalcTextSizeA(fixed_font->FontSize, FLT_MAX, 0.0f, text.c_str(), text.c_str() + text.length());
win_dl->AddText(ImVec2((GSConfig.OsdPerformancePos == OsdOverlayPos::TopLeft ? 2.0f * spacing : wpos.x + history_size.x - text_size.x - spacing) + shadow_offset,
wpos.y + shadow_offset),
float text_x;
switch (GSConfig.OsdPerformancePos)
{
case OsdOverlayPos::TopLeft:
case OsdOverlayPos::CenterLeft:
case OsdOverlayPos::BottomLeft:
text_x = wpos.x + 2.0f * spacing; // Left alignment within window
break;
case OsdOverlayPos::TopCenter:
case OsdOverlayPos::Center:
case OsdOverlayPos::BottomCenter:
text_x = wpos.x + (history_size.x - text_size.x) * 0.5f; // Center alignment within window
break;
case OsdOverlayPos::TopRight:
case OsdOverlayPos::CenterRight:
case OsdOverlayPos::BottomRight:
default:
text_x = wpos.x + history_size.x - text_size.x - spacing; // Right alignment within window
break;
}
win_dl->AddText(ImVec2(text_x + shadow_offset, wpos.y + shadow_offset),
IM_COL32(0, 0, 0, 100), text.c_str(), text.c_str() + text.length());
win_dl->AddText(ImVec2((GSConfig.OsdPerformancePos == OsdOverlayPos::TopLeft ? 2.0f * spacing : wpos.x + history_size.x - text_size.x - spacing), wpos.y),
win_dl->AddText(ImVec2(text_x, wpos.y),
IM_COL32(255, 255, 255, 255), text.c_str(), text.c_str() + text.length());
text.clear();
text.append_format("Min: {:.1f} ms", min);
text_size = fixed_font->CalcTextSizeA(fixed_font->FontSize, FLT_MAX, 0.0f, text.c_str(), text.c_str() + text.length());
win_dl->AddText(ImVec2((GSConfig.OsdPerformancePos == OsdOverlayPos::TopLeft ? 2.0f * spacing : wpos.x + history_size.x - text_size.x - spacing) + shadow_offset,
wpos.y + history_size.y - fixed_font->FontSize + shadow_offset),
float min_text_x;
switch (GSConfig.OsdPerformancePos)
{
case OsdOverlayPos::TopLeft:
case OsdOverlayPos::CenterLeft:
case OsdOverlayPos::BottomLeft:
min_text_x = wpos.x + 2.0f * spacing; // Left alignment within window
break;
case OsdOverlayPos::TopCenter:
case OsdOverlayPos::Center:
case OsdOverlayPos::BottomCenter:
min_text_x = wpos.x + (history_size.x - text_size.x) * 0.5f; // Center alignment within window
break;
case OsdOverlayPos::TopRight:
case OsdOverlayPos::CenterRight:
case OsdOverlayPos::BottomRight:
default:
min_text_x = wpos.x + history_size.x - text_size.x - spacing; // Right alignment within window
break;
}
win_dl->AddText(ImVec2(min_text_x + shadow_offset, wpos.y + history_size.y - fixed_font->FontSize + shadow_offset),
IM_COL32(0, 0, 0, 100), text.c_str(), text.c_str() + text.length());
win_dl->AddText(ImVec2((GSConfig.OsdPerformancePos == OsdOverlayPos::TopLeft ? 2.0f * spacing : wpos.x + history_size.x - text_size.x - spacing),
wpos.y + history_size.y - fixed_font->FontSize),
win_dl->AddText(ImVec2(min_text_x, wpos.y + history_size.y - fixed_font->FontSize),
IM_COL32(255, 255, 255, 255), text.c_str(), text.c_str() + text.length());
}
ImGui::End();
@ -362,7 +489,12 @@ __ri void ImGuiManager::DrawPerformanceOverlay(float& position_y, float scale, f
{
if (GSConfig.OsdShowIndicators)
{
DRAW_LINE(standard_font, ICON_FA_PAUSE, IM_COL32(255, 255, 255, 255));
// We should put the Pause icon in the top right regardless of performance overlay position
text = ICON_FA_PAUSE;
text_size = standard_font->CalcTextSizeA(standard_font->FontSize, std::numeric_limits<float>::max(), -1.0f, text.c_str(), nullptr, nullptr);
const ImVec2 pause_pos(GetWindowWidth() - margin - text_size.x, margin);
dl->AddText(standard_font, standard_font->FontSize, ImVec2(pause_pos.x + shadow_offset, pause_pos.y + shadow_offset), IM_COL32(0, 0, 0, 100), text.c_str());
dl->AddText(standard_font, standard_font->FontSize, pause_pos, IM_COL32(255, 255, 255, 255), text.c_str());
}
}
@ -1147,7 +1279,7 @@ void ImGuiManager::RenderOverlays()
{
const float scale = ImGuiManager::GetGlobalScale();
const float margin = std::ceil(10.0f * scale);
const float spacing = std::ceil(5.0f * scale);
const float spacing = std::ceil(5.0f * scale);
float position_y = margin;
DrawVideoCaptureOverlay(position_y, scale, margin, spacing);

View File

@ -4,12 +4,19 @@
#pragma once
#include "ImGuiManager.h"
#include "Config.h"
struct ImVec2;
namespace ImGuiManager
{
void RenderOverlays();
}
ImVec2 CalculateOSDPosition(OsdOverlayPos position, float margin, const ImVec2& text_size, float window_width, float window_height);
ImVec2 CalculatePerformanceOverlayTextPosition(OsdOverlayPos position, float margin, const ImVec2& text_size, float window_width, float position_y);
bool ShouldUseLeftAlignment(OsdOverlayPos position);
namespace SaveStateSelectorUI
{
static constexpr float DEFAULT_OPEN_TIME = 5.0f;

View File

@ -671,6 +671,18 @@ const char* Pcsx2Config::GSOptions::CaptureContainers[] = {
nullptr};
const char* Pcsx2Config::GSOptions::DEFAULT_CAPTURE_CONTAINER = "mp4";
const char* Pcsx2Config::AchievementsOptions::OverlayPositionNames[(size_t)AchievementOverlayPosition::MaxCount + 1] = {
"TopLeft",
"TopCenter",
"TopRight",
"CenterLeft",
"Center",
"CenterRight",
"BottomLeft",
"BottomCenter",
"BottomRight",
nullptr};
const char* Pcsx2Config::GSOptions::GetRendererName(GSRendererType type)
{
switch (type)
@ -1863,6 +1875,8 @@ void Pcsx2Config::AchievementsOptions::LoadSave(SettingsWrapper& wrap)
SettingsWrapBitBool(Overlays);
SettingsWrapEntry(NotificationsDuration);
SettingsWrapEntry(LeaderboardsDuration);
SettingsWrapIntEnumEx(OverlayPosition, "OverlayPosition");
SettingsWrapIntEnumEx(NotificationPosition, "NotificationPosition");
SettingsWrapEntry(InfoSoundName);
SettingsWrapEntry(UnlockSoundName);
SettingsWrapEntry(LBSubmitSoundName);
@ -1877,7 +1891,8 @@ void Pcsx2Config::AchievementsOptions::LoadSave(SettingsWrapper& wrap)
bool Pcsx2Config::AchievementsOptions::operator==(const AchievementsOptions& right) const
{
return OpEqu(bitset) && OpEqu(NotificationsDuration) && OpEqu(LeaderboardsDuration);
return OpEqu(bitset) && OpEqu(NotificationsDuration) && OpEqu(LeaderboardsDuration) &&
OpEqu(OverlayPosition) && OpEqu(NotificationPosition);
}
bool Pcsx2Config::AchievementsOptions::operator!=(const AchievementsOptions& right) const