mirror of
https://github.com/PCSX2/pcsx2.git
synced 2025-12-16 04:08:48 +00:00
OSD/Achievements: Add 9-position alignment options for Achievement notifications/popups, and OSD
This commit is contained in:
parent
ab19b109ce
commit
76b758dbd2
@ -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);
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user