ImGuiManager: Easing for OSD messages

This commit is contained in:
Stenzek 2023-09-17 00:51:46 +10:00
parent 6b34c2a66d
commit d7bccfe9a4

View file

@ -8,6 +8,7 @@
#include "input_manager.h"
#include "common/assert.h"
#include "common/easing.h"
#include "common/file_system.h"
#include "common/image.h"
#include "common/log.h"
@ -47,8 +48,8 @@ static bool AddImGuiFonts(bool fullscreen_fonts);
static ImFont* AddTextFont(float size);
static ImFont* AddFixedFont(float size);
static bool AddIconFonts(float size);
static void AcquirePendingOSDMessages();
static void DrawOSDMessages();
static void AcquirePendingOSDMessages(Common::Timer::Value current_time);
static void DrawOSDMessages(Common::Timer::Value current_time);
static void CreateSoftwareCursorTextures();
static void UpdateSoftwareCursorTexture(u32 index);
static void DestroySoftwareCursorTextures();
@ -79,12 +80,18 @@ static std::atomic_bool s_imgui_wants_mouse{false};
// mapping of host key -> imgui key
static std::unordered_map<u32, ImGuiKey> s_imgui_key_map;
static constexpr float OSD_FADE_IN_TIME = 0.1f;
static constexpr float OSD_FADE_OUT_TIME = 0.4f;
struct OSDMessage
{
std::string key;
std::string text;
std::chrono::steady_clock::time_point time;
Common::Timer::Value start_time;
Common::Timer::Value move_time;
float duration;
float target_y;
float last_y;
};
static std::deque<OSDMessage> s_osd_active_messages;
@ -416,8 +423,8 @@ void ImGuiManager::SetKeyMap()
{ImGuiKey_KeypadDivide, "KeypadDivide", nullptr},
{ImGuiKey_KeypadMultiply, "KeypadMultiply", nullptr},
{ImGuiKey_KeypadSubtract, "KeypadMinus", nullptr},
{ImGuiKey_KeypadAdd, "KeypadPlus", nullptr },
{ImGuiKey_KeypadEnter, "KeypadReturn", nullptr },
{ImGuiKey_KeypadAdd, "KeypadPlus", nullptr},
{ImGuiKey_KeypadEnter, "KeypadReturn", nullptr},
{ImGuiKey_KeypadEqual, "KeypadEqual", nullptr}};
s_imgui_key_map.clear();
@ -608,11 +615,16 @@ void Host::AddKeyedOSDMessage(std::string key, std::string message, float durati
if (!s_show_osd_messages)
return;
const Common::Timer::Value current_time = Common::Timer::GetCurrentValue();
OSDMessage msg;
msg.key = std::move(key);
msg.text = std::move(message);
msg.duration = duration;
msg.time = std::chrono::steady_clock::now();
msg.start_time = current_time;
msg.move_time = current_time;
msg.target_y = -1.0f;
msg.last_y = -1.0f;
std::unique_lock<std::mutex> lock(s_osd_messages_lock);
s_osd_posted_messages.push_back(std::move(msg));
@ -646,10 +658,9 @@ void Host::RemoveKeyedOSDMessage(std::string key)
if (!s_show_osd_messages)
return;
OSDMessage msg;
OSDMessage msg = {};
msg.key = std::move(key);
msg.duration = 0.0f;
msg.time = std::chrono::steady_clock::now();
std::unique_lock<std::mutex> lock(s_osd_messages_lock);
s_osd_posted_messages.push_back(std::move(msg));
@ -665,7 +676,7 @@ void Host::ClearOSDMessages()
s_osd_active_messages.clear();
}
void ImGuiManager::AcquirePendingOSDMessages()
void ImGuiManager::AcquirePendingOSDMessages(Common::Timer::Value current_time)
{
std::atomic_thread_fence(std::memory_order_consume);
if (s_osd_posted_messages.empty())
@ -686,7 +697,12 @@ void ImGuiManager::AcquirePendingOSDMessages()
{
iter->text = std::move(new_msg.text);
iter->duration = new_msg.duration;
iter->time = new_msg.time;
// Don't fade it in again
const float time_passed =
static_cast<float>(Common::Timer::ConvertValueToSeconds(current_time - iter->start_time));
iter->start_time =
current_time - Common::Timer::ConvertSecondsToValue(std::min(time_passed, OSD_FADE_IN_TIME));
}
else
{
@ -701,8 +717,10 @@ void ImGuiManager::AcquirePendingOSDMessages()
}
}
void ImGuiManager::DrawOSDMessages()
void ImGuiManager::DrawOSDMessages(Common::Timer::Value current_time)
{
static constexpr float MOVE_DURATION = 0.5f;
ImFont* const font = ImGui::GetFont();
const float scale = s_global_scale;
const float spacing = std::ceil(5.0f * scale);
@ -713,15 +731,12 @@ void ImGuiManager::DrawOSDMessages()
float position_x = margin;
float position_y = margin;
const auto now = std::chrono::steady_clock::now();
auto iter = s_osd_active_messages.begin();
while (iter != s_osd_active_messages.end())
{
const OSDMessage& msg = *iter;
const double time = std::chrono::duration<double>(now - msg.time).count();
const float time_remaining = static_cast<float>(msg.duration - time);
if (time_remaining <= 0.0f)
OSDMessage& msg = *iter;
const float time_passed = static_cast<float>(Common::Timer::ConvertValueToSeconds(current_time - msg.start_time));
if (time_passed >= msg.duration)
{
iter = s_osd_active_messages.erase(iter);
continue;
@ -729,22 +744,53 @@ void ImGuiManager::DrawOSDMessages()
++iter;
const float opacity = std::min(time_remaining, 1.0f);
const u32 alpha = static_cast<u32>(opacity * 255.0f);
u8 opacity;
if (time_passed < OSD_FADE_IN_TIME)
opacity = static_cast<u8>((time_passed / OSD_FADE_IN_TIME) * 255.0f);
else if (time_passed > (msg.duration - OSD_FADE_OUT_TIME))
opacity = static_cast<u8>(std::min((msg.duration - time_passed) / OSD_FADE_OUT_TIME, 1.0f) * 255.0f);
else
opacity = 255;
if (position_y >= ImGui::GetIO().DisplaySize.y)
const float expected_y = position_y;
float actual_y = msg.last_y;
if (msg.target_y != expected_y)
{
msg.move_time = current_time;
msg.target_y = expected_y;
msg.last_y = (msg.last_y < 0.0f) ? expected_y : msg.last_y;
actual_y = msg.last_y;
}
else if (actual_y != expected_y)
{
const float time_since_move =
static_cast<float>(Common::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 = msg.last_y - ((msg.last_y - msg.target_y) * frac);
}
}
if (actual_y >= ImGui::GetIO().DisplaySize.y)
break;
const ImVec2 pos(position_x, position_y);
const ImVec2 pos(position_x, actual_y);
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 ImVec4 text_rect(pos.x + padding, pos.y + padding, pos.x + size.x - padding, pos.y + size.y - padding);
ImDrawList* dl = ImGui::GetForegroundDrawList();
dl->AddRectFilled(pos, ImVec2(pos.x + size.x, pos.y + size.y), IM_COL32(0x21, 0x21, 0x21, alpha), rounding);
dl->AddRect(pos, ImVec2(pos.x + size.x, pos.y + size.y), IM_COL32(0x48, 0x48, 0x48, alpha), rounding);
dl->AddText(font, font->FontSize, ImVec2(text_rect.x, text_rect.y), IM_COL32(0xff, 0xff, 0xff, alpha),
dl->AddRectFilled(pos, ImVec2(pos.x + size.x, pos.y + size.y), IM_COL32(0x21, 0x21, 0x21, opacity), rounding);
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;
}
@ -752,8 +798,9 @@ void ImGuiManager::DrawOSDMessages()
void ImGuiManager::RenderOSDMessages()
{
AcquirePendingOSDMessages();
DrawOSDMessages();
const Common::Timer::Value current_time = Common::Timer::GetCurrentValue();
AcquirePendingOSDMessages(current_time);
DrawOSDMessages(current_time);
}
float ImGuiManager::GetGlobalScale()