mirror of
https://github.com/stenzek/duckstation.git
synced 2026-02-04 05:04:33 +00:00
ImGuiManager: Allow repositioning OSD messages
This commit is contained in:
@@ -2287,6 +2287,12 @@ void FullscreenUI::DrawInterfaceSettingsPage()
|
||||
FSUI_VSTR("Determines the margin between the edge of the screen and on-screen messages."),
|
||||
"Display", "OSDMargin", ImGuiManager::DEFAULT_SCREEN_MARGIN, 0.0f, 100.0f, 1.0f, 1.0f,
|
||||
"%.0fpx");
|
||||
DrawEnumSetting(bsi, FSUI_ICONVSTR(ICON_FA_LOCATION_DOT, "Message Location"),
|
||||
FSUI_VSTR("Selects which location on the screen messages are displayed."), "Display",
|
||||
"OSDMessageLocation", Settings::DEFAULT_OSD_MESSAGE_LOCATION, &Settings::ParseNotificationLocation,
|
||||
&Settings::GetNotificationLocationName, &Settings::GetNotificationLocationDisplayName,
|
||||
NotificationLocation::MaxCount);
|
||||
|
||||
DrawToggleSetting(bsi, FSUI_ICONVSTR(ICON_FA_CIRCLE_EXCLAMATION, "Show Messages"),
|
||||
FSUI_VSTR("Shows on-screen-display messages when events occur. Errors and warnings are still "
|
||||
"displayed regardless of this setting."),
|
||||
|
||||
@@ -484,6 +484,7 @@ TRANSLATE_NOOP("FullscreenUI", "Menu Background");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Menu Borders");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Merge Multi-Disc Games");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Merges multi-disc games into one item in the game list.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Message Location");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Minimal Output Latency");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Move Down");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Move Up");
|
||||
@@ -671,6 +672,7 @@ TRANSLATE_NOOP("FullscreenUI", "Selects the screen location for achievement and
|
||||
TRANSLATE_NOOP("FullscreenUI", "Selects the screen location for challenge/progress indicators, and leaderboard trackers.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Selects the type of emulated controller for this port.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Selects the view that the game list will open to.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Selects which location on the screen messages are displayed.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Serial");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Session: {}");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Set Cover Image");
|
||||
|
||||
@@ -4978,16 +4978,25 @@ FullscreenUI::NotificationLayout::NotificationLayout(NotificationLocation locati
|
||||
static constexpr float top_start_pct = 0.15f;
|
||||
#endif
|
||||
|
||||
const float top_margin = ImFloor(top_start_pct * ImGui::GetIO().DisplaySize.y);
|
||||
CalcStartPosition(screen_margin, top_margin);
|
||||
}
|
||||
|
||||
FullscreenUI::NotificationLayout::NotificationLayout(NotificationLocation location, float spacing, float screen_margin)
|
||||
: m_spacing(spacing), m_location(location)
|
||||
{
|
||||
CalcStartPosition(screen_margin, screen_margin);
|
||||
}
|
||||
|
||||
void FullscreenUI::NotificationLayout::CalcStartPosition(float screen_margin, float top_margin)
|
||||
{
|
||||
const ImGuiIO& io = ImGui::GetIO();
|
||||
switch (m_location)
|
||||
{
|
||||
case NotificationLocation::TopLeft:
|
||||
{
|
||||
m_current_position.x = screen_margin;
|
||||
|
||||
// need to consider osd message size
|
||||
m_current_position.y = std::max(std::max(screen_margin, top_start_pct * io.DisplaySize.y),
|
||||
ImGuiManager::GetOSDMessageEndPosition() + m_spacing);
|
||||
m_current_position.y = std::max(screen_margin, top_margin);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -5001,7 +5010,7 @@ FullscreenUI::NotificationLayout::NotificationLayout(NotificationLocation locati
|
||||
case NotificationLocation::TopRight:
|
||||
{
|
||||
m_current_position.x = io.DisplaySize.x - screen_margin;
|
||||
m_current_position.y = std::max(screen_margin, top_start_pct * io.DisplaySize.y);
|
||||
m_current_position.y = std::max(screen_margin, top_margin);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -5028,6 +5037,16 @@ FullscreenUI::NotificationLayout::NotificationLayout(NotificationLocation locati
|
||||
|
||||
DefaultCaseIsUnreachable();
|
||||
}
|
||||
|
||||
// don't draw over osd messages
|
||||
if (m_location == g_gpu_settings.display_osd_message_location)
|
||||
{
|
||||
const float osd_end = ImGuiManager::GetOSDMessageEndPosition();
|
||||
if (m_location >= NotificationLocation::BottomLeft)
|
||||
m_current_position.y = std::min(m_current_position.y, osd_end);
|
||||
else
|
||||
m_current_position.y = std::max(m_current_position.y, osd_end);
|
||||
}
|
||||
}
|
||||
|
||||
bool FullscreenUI::NotificationLayout::IsVerticalAnimation() const
|
||||
|
||||
@@ -519,8 +519,10 @@ class NotificationLayout
|
||||
{
|
||||
public:
|
||||
NotificationLayout(NotificationLocation location);
|
||||
NotificationLayout(NotificationLocation location, float spacing, float screen_margin);
|
||||
|
||||
ALWAYS_INLINE NotificationLocation GetLocation() const { return m_location; }
|
||||
ALWAYS_INLINE const ImVec2& GetCurrentPosition() const { return m_current_position; }
|
||||
bool IsVerticalAnimation() const;
|
||||
|
||||
ImVec2 GetFixedPosition(float width, float height);
|
||||
@@ -533,6 +535,8 @@ public:
|
||||
float in_duration, float out_duration, float width_coeff);
|
||||
|
||||
private:
|
||||
void CalcStartPosition(float screen_margin, float top_margin);
|
||||
|
||||
ImVec2 m_current_position;
|
||||
float m_spacing;
|
||||
NotificationLocation m_location;
|
||||
|
||||
@@ -400,6 +400,8 @@ void Settings::Load(const SettingsInterface& si, const SettingsInterface& contro
|
||||
DEFAULT_DISPLAY_OSD_MESSAGE_DURATIONS[i]);
|
||||
}
|
||||
display_osd_message_duration[static_cast<size_t>(OSDMessageType::Persistent)] = std::numeric_limits<float>::max();
|
||||
display_osd_message_location = ParseNotificationLocation(si.GetStringValue("Display", "OSDMessageLocation").c_str())
|
||||
.value_or(DEFAULT_OSD_MESSAGE_LOCATION);
|
||||
|
||||
save_state_compression = ParseSaveStateCompressionModeName(
|
||||
si.GetStringValue("Main", "SaveStateCompression",
|
||||
@@ -755,6 +757,8 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const
|
||||
TinyString::from_format("OSD{}Duration", GetDisplayOSDMessageTypeName(static_cast<OSDMessageType>(i))),
|
||||
display_osd_message_duration[i]);
|
||||
}
|
||||
|
||||
si.SetStringValue("Display", "OSDMessageLocation", GetNotificationLocationName(display_osd_message_location));
|
||||
}
|
||||
|
||||
si.SetBoolValue("Display", "AutoResizeWindow", display_auto_resize_window);
|
||||
|
||||
@@ -128,6 +128,7 @@ struct GPUSettings
|
||||
float display_osd_margin = 0.0f;
|
||||
|
||||
std::array<float, 5> display_osd_message_duration = DEFAULT_DISPLAY_OSD_MESSAGE_DURATIONS;
|
||||
NotificationLocation display_osd_message_location = DEFAULT_OSD_MESSAGE_LOCATION;
|
||||
|
||||
// texture replacements
|
||||
struct TextureReplacementSettings
|
||||
@@ -234,6 +235,7 @@ struct GPUSettings
|
||||
static constexpr u8 DEFAULT_DISPLAY_SCREENSHOT_QUALITY = 85;
|
||||
static constexpr float DEFAULT_DISPLAY_PRE_FRAME_SLEEP_BUFFER = 2.0f;
|
||||
static constexpr float DEFAULT_OSD_SCALE = 100.0f;
|
||||
static constexpr NotificationLocation DEFAULT_OSD_MESSAGE_LOCATION = NotificationLocation::TopLeft;
|
||||
|
||||
static const std::array<float, 5> DEFAULT_DISPLAY_OSD_MESSAGE_DURATIONS;
|
||||
|
||||
|
||||
@@ -4629,6 +4629,7 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
|
||||
g_settings.display_osd_scale != old_settings.display_osd_scale ||
|
||||
g_settings.display_osd_margin != old_settings.display_osd_margin ||
|
||||
g_settings.display_osd_message_duration != old_settings.display_osd_message_duration ||
|
||||
g_settings.display_osd_message_location != old_settings.display_osd_message_location ||
|
||||
g_settings.gpu_pgxp_enable != old_settings.gpu_pgxp_enable ||
|
||||
g_settings.gpu_pgxp_texture_correction != old_settings.gpu_pgxp_texture_correction ||
|
||||
g_settings.gpu_pgxp_color_correction != old_settings.gpu_pgxp_color_correction ||
|
||||
|
||||
@@ -235,6 +235,10 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
|
||||
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.osdMargin, "Display", "OSDMargin",
|
||||
ImGuiManager::DEFAULT_SCREEN_MARGIN);
|
||||
SettingWidgetBinder::BindWidgetToStringSetting(sif, m_ui.fullscreenUITheme, "UI", "FullscreenUITheme");
|
||||
SettingWidgetBinder::BindWidgetToEnumSetting(
|
||||
sif, m_ui.osdMessageLocation, "Display", "OSDMessageLocation", &Settings::ParseNotificationLocation,
|
||||
&Settings::GetNotificationLocationName, &Settings::GetNotificationLocationDisplayName,
|
||||
Settings::DEFAULT_OSD_MESSAGE_LOCATION, NotificationLocation::MaxCount);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showMessages, "Display", "ShowOSDMessages", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showFPS, "Display", "ShowFPS", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showSpeed, "Display", "ShowSpeed", false);
|
||||
@@ -582,10 +586,15 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
|
||||
// OSD Tab
|
||||
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.osdScale, tr("OSD Scale"), tr("100%"),
|
||||
m_ui.osdScale, tr("Display Scale"), tr("100%"),
|
||||
tr("Changes the size at which on-screen elements, including status and messages are displayed."));
|
||||
dialog->registerWidgetHelp(m_ui.fullscreenUITheme, tr("Theme"), tr("Automatic"),
|
||||
tr("Determines the theme to use for on-screen display elements and the Big Picture UI."));
|
||||
dialog->registerWidgetHelp(m_ui.osdMargin, tr("Display Margins"), tr("0px"),
|
||||
tr("Determines the margin between the edge of the screen and on-screen messages."));
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.osdMessageLocation, tr("Message Location"), tr("Selects which location on the screen messages are displayed."),
|
||||
QString::fromStdString(Settings::GetNotificationLocationDisplayName(Settings::DEFAULT_OSD_MESSAGE_LOCATION)));
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.showMessages, tr("Show Messages"), tr("Checked"),
|
||||
tr("Shows on-screen-display messages when events occur such as save states being created/loaded, screenshots being "
|
||||
|
||||
@@ -293,7 +293,7 @@
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
@@ -763,7 +763,7 @@
|
||||
<property name="title">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_14" columnstretch="0,1" columnminimumwidth="100,0">
|
||||
<layout class="QGridLayout" name="gridLayout_14" columnstretch="0,1" columnminimumwidth="120,0">
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
@@ -814,7 +814,17 @@
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="fullscreenUITheme"/>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="osdMessageLocation"/>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Message Location:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="showMessages">
|
||||
@@ -910,7 +920,7 @@
|
||||
<property name="title">
|
||||
<string>Message Durations</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_21" columnstretch="0,1,0,1" columnminimumwidth="100,0,100,0">
|
||||
<layout class="QGridLayout" name="gridLayout_21" columnstretch="0,1,0,1" columnminimumwidth="120,0,120,0">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="osdErrorDurationLabel">
|
||||
<property name="text">
|
||||
@@ -1025,7 +1035,7 @@
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
|
||||
@@ -978,11 +978,11 @@ void ImGuiManager::DrawOSDMessages(Timer::Value current_time)
|
||||
|
||||
static constexpr float MOVE_DURATION = 0.5f;
|
||||
|
||||
s_state.osd_messages_end_y =
|
||||
(g_gpu_settings.display_osd_message_location >= NotificationLocation::BottomLeft) ? s_state.window_height : 0.0f;
|
||||
|
||||
if (s_state.osd_active_messages.empty())
|
||||
{
|
||||
s_state.osd_messages_end_y = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
ImFont* const font = s_state.text_font;
|
||||
const float font_size = GetOSDFontSize();
|
||||
@@ -1002,12 +1002,12 @@ void ImGuiManager::DrawOSDMessages(Timer::Value current_time)
|
||||
const float max_width_for_color = std::ceil(400.0f * scale);
|
||||
const float min_rounded_width = rounding * 2.0f;
|
||||
const bool show_messages = g_gpu_settings.display_show_messages;
|
||||
float position_x = margin;
|
||||
float position_y = margin;
|
||||
|
||||
const ImVec4 left_background_color = DarkerColor(UIStyle.ToastBackgroundColor, 1.3f);
|
||||
const ImVec4 right_background_color = DarkerColor(UIStyle.ToastBackgroundColor, 0.8f);
|
||||
|
||||
FullscreenUI::NotificationLayout layout(g_gpu_settings.display_osd_message_location, spacing, margin);
|
||||
|
||||
auto iter = s_state.osd_active_messages.begin();
|
||||
while (iter != s_state.osd_active_messages.end())
|
||||
{
|
||||
@@ -1051,69 +1051,56 @@ void ImGuiManager::DrawOSDMessages(Timer::Value current_time)
|
||||
const float box_width = icon_size_with_margin + std::max(text_size.x, title_size.x) + padding + padding;
|
||||
const float box_height = std::max(icon_size.y, title_size.y + text_size.y) + padding + padding;
|
||||
|
||||
float opacity;
|
||||
float actual_x;
|
||||
if (time_passed < OSD_FADE_IN_TIME)
|
||||
const auto& [layout_pos, opacity] = layout.GetNextPosition(box_width, box_height, time_passed, msg.duration,
|
||||
OSD_FADE_IN_TIME, OSD_FADE_OUT_TIME, 0.2f);
|
||||
float actual_y;
|
||||
if (!layout.IsVerticalAnimation() || opacity == 1.0f)
|
||||
{
|
||||
const float pct = time_passed / OSD_FADE_IN_TIME;
|
||||
const float eased_pct = std::clamp(Easing::OutExpo(pct), 0.0f, 1.0f);
|
||||
actual_x = ImFloor(position_x - (box_width * 0.2f * (1.0f - eased_pct)));
|
||||
opacity = pct;
|
||||
}
|
||||
else if (time_passed > (msg.duration - OSD_FADE_OUT_TIME))
|
||||
{
|
||||
const float pct = (msg.duration - time_passed) / OSD_FADE_OUT_TIME;
|
||||
const float eased_pct = std::clamp(Easing::InExpo(pct), 0.0f, 1.0f);
|
||||
actual_x = ImFloor(position_x - (box_width * 0.2f * (1.0f - eased_pct)));
|
||||
opacity = eased_pct;
|
||||
actual_y = msg.last_y;
|
||||
if (msg.target_y != layout_pos.y)
|
||||
{
|
||||
if (msg.last_y < 0.0f)
|
||||
{
|
||||
// First showing.
|
||||
msg.last_y = layout_pos.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We got repositioned, probably due to another message above getting removed.
|
||||
const float time_since_move = static_cast<float>(Timer::ConvertValueToSeconds(current_time - msg.move_time));
|
||||
const float frac = Easing::OutExpo(time_since_move / MOVE_DURATION);
|
||||
msg.last_y = std::floor(msg.last_y - ((msg.last_y - msg.target_y) * frac));
|
||||
}
|
||||
|
||||
msg.move_time = current_time;
|
||||
msg.target_y = layout_pos.y;
|
||||
actual_y = msg.last_y;
|
||||
}
|
||||
else if (actual_y != layout_pos.y)
|
||||
{
|
||||
const float time_since_move = static_cast<float>(Timer::ConvertValueToSeconds(current_time - msg.move_time));
|
||||
if (time_since_move >= MOVE_DURATION)
|
||||
{
|
||||
msg.move_time = current_time;
|
||||
msg.last_y = msg.target_y;
|
||||
actual_y = msg.last_y;
|
||||
}
|
||||
else
|
||||
{
|
||||
const float frac = Easing::OutExpo(time_since_move / MOVE_DURATION);
|
||||
actual_y = std::floor(msg.last_y - ((msg.last_y - msg.target_y) * frac));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
opacity = 1.0f;
|
||||
actual_x = position_x;
|
||||
}
|
||||
|
||||
const float expected_y = position_y;
|
||||
float actual_y = msg.last_y;
|
||||
if (msg.target_y != expected_y)
|
||||
{
|
||||
if (msg.last_y < 0.0f)
|
||||
{
|
||||
// First showing.
|
||||
msg.last_y = expected_y;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We got repositioned, probably due to another message above getting removed.
|
||||
const float time_since_move = static_cast<float>(Timer::ConvertValueToSeconds(current_time - msg.move_time));
|
||||
const float frac = Easing::OutExpo(time_since_move / MOVE_DURATION);
|
||||
msg.last_y = std::floor(msg.last_y - ((msg.last_y - msg.target_y) * frac));
|
||||
}
|
||||
|
||||
msg.move_time = current_time;
|
||||
msg.target_y = expected_y;
|
||||
actual_y = msg.last_y;
|
||||
}
|
||||
else if (actual_y != expected_y)
|
||||
{
|
||||
const float time_since_move = static_cast<float>(Timer::ConvertValueToSeconds(current_time - msg.move_time));
|
||||
if (time_since_move >= MOVE_DURATION)
|
||||
{
|
||||
msg.move_time = current_time;
|
||||
msg.last_y = msg.target_y;
|
||||
actual_y = msg.last_y;
|
||||
}
|
||||
else
|
||||
{
|
||||
const float frac = Easing::OutExpo(time_since_move / MOVE_DURATION);
|
||||
actual_y = std::floor(msg.last_y - ((msg.last_y - msg.target_y) * frac));
|
||||
}
|
||||
actual_y = layout_pos.y;
|
||||
}
|
||||
|
||||
if (actual_y >= ImGui::GetIO().DisplaySize.y || (msg.type >= OSDMessageType::Info && !show_messages))
|
||||
break;
|
||||
|
||||
const ImVec2 pos = ImVec2(actual_x, actual_y);
|
||||
const ImVec2 pos = ImVec2(layout_pos.x, actual_y);
|
||||
const ImVec2 pos_max = ImVec2(pos.x + box_width, pos.y + box_height);
|
||||
|
||||
ImDrawList* const dl = ImGui::GetForegroundDrawList();
|
||||
@@ -1157,11 +1144,9 @@ void ImGuiManager::DrawOSDMessages(Timer::Value current_time)
|
||||
RenderShadowedTextClipped(dl, font, font_size, text_font_weight, text_rect.Min, text_rect.Max, actual_text_color,
|
||||
msg.text, &text_size, ImVec2(0.0f, 0.0f), max_text_width, &text_rect, scale);
|
||||
}
|
||||
|
||||
position_y += box_height + spacing;
|
||||
}
|
||||
|
||||
s_state.osd_messages_end_y = position_y;
|
||||
s_state.osd_messages_end_y = layout.GetCurrentPosition().y;
|
||||
}
|
||||
|
||||
void ImGuiManager::RenderOSDMessages()
|
||||
|
||||
Reference in New Issue
Block a user