From d7bccfe9a44e726f7c99bfc66dcd85a96d32cf56 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 17 Sep 2023 00:51:46 +1000 Subject: [PATCH] ImGuiManager: Easing for OSD messages --- src/util/imgui_manager.cpp | 99 ++++++++++++++++++++++++++++---------- 1 file changed, 73 insertions(+), 26 deletions(-) diff --git a/src/util/imgui_manager.cpp b/src/util/imgui_manager.cpp index 0ab37479b..086c2f1f9 100644 --- a/src/util/imgui_manager.cpp +++ b/src/util/imgui_manager.cpp @@ -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 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 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 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 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(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(now - msg.time).count(); - const float time_remaining = static_cast(msg.duration - time); - if (time_remaining <= 0.0f) + OSDMessage& msg = *iter; + const float time_passed = static_cast(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(opacity * 255.0f); + u8 opacity; + if (time_passed < OSD_FADE_IN_TIME) + opacity = static_cast((time_passed / OSD_FADE_IN_TIME) * 255.0f); + else if (time_passed > (msg.duration - OSD_FADE_OUT_TIME)) + opacity = static_cast(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(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()