2022-04-18 07:34:05 +00:00
|
|
|
#include "tooltips.h"
|
|
|
|
|
|
|
|
#include <game/client/render.h>
|
2022-07-08 16:28:30 +00:00
|
|
|
#include <game/client/ui.h>
|
2022-04-18 07:34:05 +00:00
|
|
|
|
|
|
|
CTooltips::CTooltips()
|
|
|
|
{
|
2022-04-18 07:34:21 +00:00
|
|
|
OnReset();
|
2022-04-18 07:34:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CTooltips::OnReset()
|
|
|
|
{
|
2022-07-10 13:33:23 +00:00
|
|
|
m_HoverTime = -1;
|
2022-04-18 07:34:21 +00:00
|
|
|
m_Tooltips.clear();
|
2023-05-18 09:46:54 +00:00
|
|
|
ClearActiveTooltip();
|
2022-04-18 07:34:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CTooltips::SetActiveTooltip(CTooltip &Tooltip)
|
|
|
|
{
|
2022-04-29 23:00:50 +00:00
|
|
|
m_ActiveTooltip.emplace(Tooltip);
|
2022-04-18 07:34:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void CTooltips::ClearActiveTooltip()
|
|
|
|
{
|
2022-04-29 23:00:50 +00:00
|
|
|
m_ActiveTooltip.reset();
|
2023-05-18 09:46:54 +00:00
|
|
|
m_PreviousTooltip.reset();
|
2022-04-18 07:34:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CTooltips::DoToolTip(const void *pID, const CUIRect *pNearRect, const char *pText, float WidthHint)
|
|
|
|
{
|
2022-04-18 07:34:21 +00:00
|
|
|
uintptr_t ID = reinterpret_cast<uintptr_t>(pID);
|
2022-04-26 22:51:02 +00:00
|
|
|
const auto result = m_Tooltips.emplace(ID, CTooltip{
|
|
|
|
*pNearRect,
|
|
|
|
pText,
|
2022-04-30 08:05:28 +00:00
|
|
|
WidthHint,
|
|
|
|
false});
|
2022-04-26 22:51:02 +00:00
|
|
|
CTooltip &Tooltip = result.first->second;
|
2022-04-18 07:34:21 +00:00
|
|
|
|
2022-04-26 22:51:02 +00:00
|
|
|
if(!result.second)
|
2022-04-18 07:34:21 +00:00
|
|
|
{
|
2022-04-26 22:51:02 +00:00
|
|
|
Tooltip.m_Rect = *pNearRect; // update in case of window resize
|
2022-05-14 10:49:12 +00:00
|
|
|
Tooltip.m_pText = pText; // update in case of language change
|
2022-04-18 07:34:21 +00:00
|
|
|
}
|
2022-04-30 08:05:28 +00:00
|
|
|
|
|
|
|
Tooltip.m_OnScreen = true;
|
|
|
|
|
2022-04-26 22:51:02 +00:00
|
|
|
if(UI()->MouseInside(&Tooltip.m_Rect))
|
2022-04-18 07:34:21 +00:00
|
|
|
{
|
2022-04-26 22:51:02 +00:00
|
|
|
SetActiveTooltip(Tooltip);
|
2022-04-18 07:34:21 +00:00
|
|
|
}
|
2022-04-18 07:34:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CTooltips::OnRender()
|
|
|
|
{
|
2022-04-29 23:00:50 +00:00
|
|
|
if(m_ActiveTooltip.has_value())
|
2022-04-18 07:34:21 +00:00
|
|
|
{
|
2022-04-29 23:00:50 +00:00
|
|
|
CTooltip &Tooltip = m_ActiveTooltip.value();
|
2022-04-18 07:34:21 +00:00
|
|
|
|
|
|
|
if(!UI()->MouseInside(&Tooltip.m_Rect))
|
|
|
|
{
|
2022-04-30 08:05:28 +00:00
|
|
|
Tooltip.m_OnScreen = false;
|
2022-04-18 07:34:21 +00:00
|
|
|
ClearActiveTooltip();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-04-30 08:05:28 +00:00
|
|
|
if(!Tooltip.m_OnScreen)
|
|
|
|
return;
|
|
|
|
|
2023-05-18 09:46:54 +00:00
|
|
|
// Reset hover time if a different tooltip is active.
|
2023-05-18 10:04:00 +00:00
|
|
|
// Only reset hover time when rendering, because multiple tooltips can be
|
2023-05-18 09:46:54 +00:00
|
|
|
// activated in the same frame, but only the last one should be rendered.
|
|
|
|
if(!m_PreviousTooltip.has_value() || m_PreviousTooltip.value().get().m_pText != Tooltip.m_pText)
|
|
|
|
m_HoverTime = time_get();
|
|
|
|
m_PreviousTooltip.emplace(Tooltip);
|
|
|
|
|
2023-05-18 10:04:00 +00:00
|
|
|
// Delay tooltip until 1 second passed. Start fade-in in the last 0.25 seconds.
|
|
|
|
constexpr float SecondsBeforeFadeIn = 0.75f;
|
|
|
|
const float SecondsSinceActivation = (time_get() - m_HoverTime) / (float)time_freq();
|
|
|
|
if(SecondsSinceActivation < SecondsBeforeFadeIn)
|
2022-04-18 07:34:21 +00:00
|
|
|
return;
|
2023-05-18 10:04:00 +00:00
|
|
|
constexpr float SecondsFadeIn = 0.25f;
|
|
|
|
const float AlphaFactor = SecondsSinceActivation < SecondsBeforeFadeIn + SecondsFadeIn ? (SecondsSinceActivation - SecondsBeforeFadeIn) / SecondsFadeIn : 1.0f;
|
2022-04-18 07:34:21 +00:00
|
|
|
|
2023-04-10 14:36:13 +00:00
|
|
|
constexpr float FontSize = 14.0f;
|
|
|
|
constexpr float Margin = 5.0f;
|
|
|
|
constexpr float Padding = 5.0f;
|
2022-04-18 07:34:21 +00:00
|
|
|
|
2023-05-18 09:18:02 +00:00
|
|
|
const STextBoundingBox BoundingBox = TextRender()->TextBoundingBox(FontSize, Tooltip.m_pText, -1, Tooltip.m_WidthHint);
|
2022-04-18 07:34:21 +00:00
|
|
|
CUIRect Rect;
|
2023-05-18 09:18:02 +00:00
|
|
|
Rect.w = BoundingBox.m_W + 2 * Padding;
|
|
|
|
Rect.h = BoundingBox.m_H + 2 * Padding;
|
2022-04-18 07:34:21 +00:00
|
|
|
|
2023-04-07 09:50:30 +00:00
|
|
|
const CUIRect *pScreen = UI()->Screen();
|
2023-04-10 14:36:13 +00:00
|
|
|
Rect.w = minimum(Rect.w, pScreen->w - 2 * Margin);
|
|
|
|
Rect.h = minimum(Rect.h, pScreen->h - 2 * Margin);
|
2022-04-18 07:34:21 +00:00
|
|
|
|
|
|
|
// Try the top side.
|
2023-04-10 14:36:13 +00:00
|
|
|
if(Tooltip.m_Rect.y - Rect.h - Margin > pScreen->y)
|
2022-04-18 07:34:21 +00:00
|
|
|
{
|
2023-04-10 14:36:13 +00:00
|
|
|
Rect.x = clamp(UI()->MouseX() - Rect.w / 2.0f, Margin, pScreen->w - Rect.w - Margin);
|
|
|
|
Rect.y = Tooltip.m_Rect.y - Rect.h - Margin;
|
2022-04-18 07:34:21 +00:00
|
|
|
}
|
|
|
|
// Try the bottom side.
|
2023-04-10 14:36:13 +00:00
|
|
|
else if(Tooltip.m_Rect.y + Tooltip.m_Rect.h + Margin < pScreen->h)
|
2022-04-18 07:34:21 +00:00
|
|
|
{
|
2023-04-10 14:36:13 +00:00
|
|
|
Rect.x = clamp(UI()->MouseX() - Rect.w / 2.0f, Margin, pScreen->w - Rect.w - Margin);
|
|
|
|
Rect.y = Tooltip.m_Rect.y + Tooltip.m_Rect.h + Margin;
|
2022-04-18 07:34:21 +00:00
|
|
|
}
|
|
|
|
// Try the right side.
|
2023-04-10 14:36:13 +00:00
|
|
|
else if(Tooltip.m_Rect.x + Tooltip.m_Rect.w + Margin + Rect.w < pScreen->w)
|
2022-04-18 07:34:21 +00:00
|
|
|
{
|
2023-04-10 14:36:13 +00:00
|
|
|
Rect.x = Tooltip.m_Rect.x + Tooltip.m_Rect.w + Margin;
|
|
|
|
Rect.y = clamp(UI()->MouseY() - Rect.h / 2.0f, Margin, pScreen->h - Rect.h - Margin);
|
2022-04-18 07:34:21 +00:00
|
|
|
}
|
|
|
|
// Try the left side.
|
2023-04-10 14:36:13 +00:00
|
|
|
else if(Tooltip.m_Rect.x - Rect.w - Margin > pScreen->x)
|
2022-04-18 07:34:21 +00:00
|
|
|
{
|
2023-04-10 14:36:13 +00:00
|
|
|
Rect.x = Tooltip.m_Rect.x - Rect.w - Margin;
|
|
|
|
Rect.y = clamp(UI()->MouseY() - Rect.h / 2.0f, Margin, pScreen->h - Rect.h - Margin);
|
2022-04-18 07:34:21 +00:00
|
|
|
}
|
|
|
|
|
2023-05-18 10:04:00 +00:00
|
|
|
Rect.Draw(ColorRGBA(0.2f, 0.2f, 0.2f, 0.8f * AlphaFactor), IGraphics::CORNER_ALL, Padding);
|
2023-04-10 14:36:13 +00:00
|
|
|
Rect.Margin(Padding, &Rect);
|
2023-05-18 10:04:00 +00:00
|
|
|
|
|
|
|
CTextCursor Cursor;
|
|
|
|
TextRender()->SetCursor(&Cursor, Rect.x, Rect.y, FontSize, TEXTFLAG_RENDER);
|
|
|
|
Cursor.m_LineWidth = Tooltip.m_WidthHint;
|
|
|
|
|
|
|
|
STextContainerIndex TextContainerIndex;
|
|
|
|
const unsigned OldRenderFlags = TextRender()->GetRenderFlags();
|
|
|
|
TextRender()->SetRenderFlags(OldRenderFlags | TEXT_RENDER_FLAG_ONE_TIME_USE);
|
|
|
|
TextRender()->CreateTextContainer(TextContainerIndex, &Cursor, Tooltip.m_pText);
|
|
|
|
TextRender()->SetRenderFlags(OldRenderFlags);
|
|
|
|
|
|
|
|
if(TextContainerIndex.Valid())
|
|
|
|
{
|
|
|
|
ColorRGBA TextColor = TextRender()->DefaultTextColor();
|
|
|
|
TextColor.a *= AlphaFactor;
|
|
|
|
ColorRGBA OutlineColor = TextRender()->DefaultTextOutlineColor();
|
|
|
|
OutlineColor.a *= AlphaFactor;
|
|
|
|
TextRender()->RenderTextContainer(TextContainerIndex, TextColor, OutlineColor);
|
|
|
|
}
|
|
|
|
|
|
|
|
TextRender()->DeleteTextContainer(TextContainerIndex);
|
|
|
|
|
2022-04-30 08:05:28 +00:00
|
|
|
Tooltip.m_OnScreen = false;
|
2022-04-18 07:34:21 +00:00
|
|
|
}
|
2022-04-18 07:34:05 +00:00
|
|
|
}
|