ImGuiManager: Easing for OSD messages
This commit is contained in:
parent
6b34c2a66d
commit
d7bccfe9a4
|
@ -8,6 +8,7 @@
|
||||||
#include "input_manager.h"
|
#include "input_manager.h"
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/easing.h"
|
||||||
#include "common/file_system.h"
|
#include "common/file_system.h"
|
||||||
#include "common/image.h"
|
#include "common/image.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
|
@ -47,8 +48,8 @@ static bool AddImGuiFonts(bool fullscreen_fonts);
|
||||||
static ImFont* AddTextFont(float size);
|
static ImFont* AddTextFont(float size);
|
||||||
static ImFont* AddFixedFont(float size);
|
static ImFont* AddFixedFont(float size);
|
||||||
static bool AddIconFonts(float size);
|
static bool AddIconFonts(float size);
|
||||||
static void AcquirePendingOSDMessages();
|
static void AcquirePendingOSDMessages(Common::Timer::Value current_time);
|
||||||
static void DrawOSDMessages();
|
static void DrawOSDMessages(Common::Timer::Value current_time);
|
||||||
static void CreateSoftwareCursorTextures();
|
static void CreateSoftwareCursorTextures();
|
||||||
static void UpdateSoftwareCursorTexture(u32 index);
|
static void UpdateSoftwareCursorTexture(u32 index);
|
||||||
static void DestroySoftwareCursorTextures();
|
static void DestroySoftwareCursorTextures();
|
||||||
|
@ -79,12 +80,18 @@ static std::atomic_bool s_imgui_wants_mouse{false};
|
||||||
// mapping of host key -> imgui key
|
// mapping of host key -> imgui key
|
||||||
static std::unordered_map<u32, ImGuiKey> s_imgui_key_map;
|
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
|
struct OSDMessage
|
||||||
{
|
{
|
||||||
std::string key;
|
std::string key;
|
||||||
std::string text;
|
std::string text;
|
||||||
std::chrono::steady_clock::time_point time;
|
Common::Timer::Value start_time;
|
||||||
|
Common::Timer::Value move_time;
|
||||||
float duration;
|
float duration;
|
||||||
|
float target_y;
|
||||||
|
float last_y;
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::deque<OSDMessage> s_osd_active_messages;
|
static std::deque<OSDMessage> s_osd_active_messages;
|
||||||
|
@ -416,8 +423,8 @@ void ImGuiManager::SetKeyMap()
|
||||||
{ImGuiKey_KeypadDivide, "KeypadDivide", nullptr},
|
{ImGuiKey_KeypadDivide, "KeypadDivide", nullptr},
|
||||||
{ImGuiKey_KeypadMultiply, "KeypadMultiply", nullptr},
|
{ImGuiKey_KeypadMultiply, "KeypadMultiply", nullptr},
|
||||||
{ImGuiKey_KeypadSubtract, "KeypadMinus", nullptr},
|
{ImGuiKey_KeypadSubtract, "KeypadMinus", nullptr},
|
||||||
{ImGuiKey_KeypadAdd, "KeypadPlus", nullptr },
|
{ImGuiKey_KeypadAdd, "KeypadPlus", nullptr},
|
||||||
{ImGuiKey_KeypadEnter, "KeypadReturn", nullptr },
|
{ImGuiKey_KeypadEnter, "KeypadReturn", nullptr},
|
||||||
{ImGuiKey_KeypadEqual, "KeypadEqual", nullptr}};
|
{ImGuiKey_KeypadEqual, "KeypadEqual", nullptr}};
|
||||||
|
|
||||||
s_imgui_key_map.clear();
|
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)
|
if (!s_show_osd_messages)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const Common::Timer::Value current_time = Common::Timer::GetCurrentValue();
|
||||||
|
|
||||||
OSDMessage msg;
|
OSDMessage msg;
|
||||||
msg.key = std::move(key);
|
msg.key = std::move(key);
|
||||||
msg.text = std::move(message);
|
msg.text = std::move(message);
|
||||||
msg.duration = duration;
|
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);
|
std::unique_lock<std::mutex> lock(s_osd_messages_lock);
|
||||||
s_osd_posted_messages.push_back(std::move(msg));
|
s_osd_posted_messages.push_back(std::move(msg));
|
||||||
|
@ -646,10 +658,9 @@ void Host::RemoveKeyedOSDMessage(std::string key)
|
||||||
if (!s_show_osd_messages)
|
if (!s_show_osd_messages)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
OSDMessage msg;
|
OSDMessage msg = {};
|
||||||
msg.key = std::move(key);
|
msg.key = std::move(key);
|
||||||
msg.duration = 0.0f;
|
msg.duration = 0.0f;
|
||||||
msg.time = std::chrono::steady_clock::now();
|
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lock(s_osd_messages_lock);
|
std::unique_lock<std::mutex> lock(s_osd_messages_lock);
|
||||||
s_osd_posted_messages.push_back(std::move(msg));
|
s_osd_posted_messages.push_back(std::move(msg));
|
||||||
|
@ -665,7 +676,7 @@ void Host::ClearOSDMessages()
|
||||||
s_osd_active_messages.clear();
|
s_osd_active_messages.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGuiManager::AcquirePendingOSDMessages()
|
void ImGuiManager::AcquirePendingOSDMessages(Common::Timer::Value current_time)
|
||||||
{
|
{
|
||||||
std::atomic_thread_fence(std::memory_order_consume);
|
std::atomic_thread_fence(std::memory_order_consume);
|
||||||
if (s_osd_posted_messages.empty())
|
if (s_osd_posted_messages.empty())
|
||||||
|
@ -686,7 +697,12 @@ void ImGuiManager::AcquirePendingOSDMessages()
|
||||||
{
|
{
|
||||||
iter->text = std::move(new_msg.text);
|
iter->text = std::move(new_msg.text);
|
||||||
iter->duration = new_msg.duration;
|
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
|
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();
|
ImFont* const font = ImGui::GetFont();
|
||||||
const float scale = s_global_scale;
|
const float scale = s_global_scale;
|
||||||
const float spacing = std::ceil(5.0f * scale);
|
const float spacing = std::ceil(5.0f * scale);
|
||||||
|
@ -713,15 +731,12 @@ void ImGuiManager::DrawOSDMessages()
|
||||||
float position_x = margin;
|
float position_x = margin;
|
||||||
float position_y = margin;
|
float position_y = margin;
|
||||||
|
|
||||||
const auto now = std::chrono::steady_clock::now();
|
|
||||||
|
|
||||||
auto iter = s_osd_active_messages.begin();
|
auto iter = s_osd_active_messages.begin();
|
||||||
while (iter != s_osd_active_messages.end())
|
while (iter != s_osd_active_messages.end())
|
||||||
{
|
{
|
||||||
const OSDMessage& msg = *iter;
|
OSDMessage& msg = *iter;
|
||||||
const double time = std::chrono::duration<double>(now - msg.time).count();
|
const float time_passed = static_cast<float>(Common::Timer::ConvertValueToSeconds(current_time - msg.start_time));
|
||||||
const float time_remaining = static_cast<float>(msg.duration - time);
|
if (time_passed >= msg.duration)
|
||||||
if (time_remaining <= 0.0f)
|
|
||||||
{
|
{
|
||||||
iter = s_osd_active_messages.erase(iter);
|
iter = s_osd_active_messages.erase(iter);
|
||||||
continue;
|
continue;
|
||||||
|
@ -729,22 +744,53 @@ void ImGuiManager::DrawOSDMessages()
|
||||||
|
|
||||||
++iter;
|
++iter;
|
||||||
|
|
||||||
const float opacity = std::min(time_remaining, 1.0f);
|
u8 opacity;
|
||||||
const u32 alpha = static_cast<u32>(opacity * 255.0f);
|
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;
|
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(),
|
const ImVec2 text_size(font->CalcTextSizeA(font->FontSize, max_width, max_width, msg.text.c_str(),
|
||||||
msg.text.c_str() + msg.text.length()));
|
msg.text.c_str() + msg.text.length()));
|
||||||
const ImVec2 size(text_size.x + padding * 2.0f, text_size.y + padding * 2.0f);
|
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);
|
const ImVec4 text_rect(pos.x + padding, pos.y + padding, pos.x + size.x - padding, pos.y + size.y - padding);
|
||||||
|
|
||||||
ImDrawList* dl = ImGui::GetForegroundDrawList();
|
ImDrawList* dl = ImGui::GetForegroundDrawList();
|
||||||
dl->AddRectFilled(pos, ImVec2(pos.x + size.x, pos.y + size.y), IM_COL32(0x21, 0x21, 0x21, alpha), rounding);
|
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, alpha), 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, alpha),
|
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);
|
msg.text.c_str(), msg.text.c_str() + msg.text.length(), max_width, &text_rect);
|
||||||
position_y += size.y + spacing;
|
position_y += size.y + spacing;
|
||||||
}
|
}
|
||||||
|
@ -752,8 +798,9 @@ void ImGuiManager::DrawOSDMessages()
|
||||||
|
|
||||||
void ImGuiManager::RenderOSDMessages()
|
void ImGuiManager::RenderOSDMessages()
|
||||||
{
|
{
|
||||||
AcquirePendingOSDMessages();
|
const Common::Timer::Value current_time = Common::Timer::GetCurrentValue();
|
||||||
DrawOSDMessages();
|
AcquirePendingOSDMessages(current_time);
|
||||||
|
DrawOSDMessages(current_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
float ImGuiManager::GetGlobalScale()
|
float ImGuiManager::GetGlobalScale()
|
||||||
|
|
Loading…
Reference in New Issue