mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 01:58:19 +00:00
Refactor tooltips to render last, add some tooltips
This commit is contained in:
parent
d4dcaa2471
commit
faab2ded74
|
@ -1958,6 +1958,8 @@ if(CLIENT)
|
|||
components/spectator.h
|
||||
components/statboard.cpp
|
||||
components/statboard.h
|
||||
components/tooltips.cpp
|
||||
components/tooltips.h
|
||||
components/voting.cpp
|
||||
components/voting.h
|
||||
gameclient.cpp
|
||||
|
|
|
@ -2832,64 +2832,4 @@ bool CMenus::HandleListInputs(const CUIRect &View, float &ScrollValue, const flo
|
|||
}
|
||||
|
||||
return NewIndex != -1;
|
||||
}
|
||||
|
||||
void CMenus::DoToolTip(const void *pID, const CUIRect *pNearRect, const char *pText, float WidthHint)
|
||||
{
|
||||
static int64_t HoverTime = -1;
|
||||
|
||||
if(!UI()->MouseInside(pNearRect))
|
||||
{
|
||||
if(pID == UI()->ActiveTooltipItem())
|
||||
HoverTime = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
UI()->SetActiveTooltipItem(pID);
|
||||
|
||||
if(HoverTime == -1)
|
||||
HoverTime = time_get();
|
||||
|
||||
// Delay tooltip until 1 second passed.
|
||||
if(HoverTime > time_get() - time_freq())
|
||||
return;
|
||||
|
||||
const float MARGIN = 5.0f;
|
||||
|
||||
CUIRect Rect;
|
||||
Rect.w = WidthHint;
|
||||
if(WidthHint < 0.0f)
|
||||
Rect.w = TextRender()->TextWidth(0, 14.0f, pText, -1, -1.0f) + 4.0f;
|
||||
Rect.h = 30.0f;
|
||||
|
||||
CUIRect *pScreen = UI()->Screen();
|
||||
|
||||
// Try the top side.
|
||||
if(pNearRect->y - Rect.h - MARGIN > pScreen->y)
|
||||
{
|
||||
Rect.x = clamp(UI()->MouseX() - Rect.w / 2.0f, MARGIN, pScreen->w - Rect.w - MARGIN);
|
||||
Rect.y = pNearRect->y - Rect.h - MARGIN;
|
||||
}
|
||||
// Try the bottom side.
|
||||
else if(pNearRect->y + pNearRect->h + MARGIN < pScreen->h)
|
||||
{
|
||||
Rect.x = clamp(UI()->MouseX() - Rect.w / 2.0f, MARGIN, pScreen->w - Rect.w - MARGIN);
|
||||
Rect.y = pNearRect->y + pNearRect->h + MARGIN;
|
||||
}
|
||||
// Try the right side.
|
||||
else if(pNearRect->x + pNearRect->w + MARGIN + Rect.w < pScreen->w)
|
||||
{
|
||||
Rect.x = pNearRect->x + pNearRect->w + MARGIN;
|
||||
Rect.y = clamp(UI()->MouseY() - Rect.h / 2.0f, MARGIN, pScreen->h - Rect.h - MARGIN);
|
||||
}
|
||||
// Try the left side.
|
||||
else if(pNearRect->x - Rect.w - MARGIN > pScreen->x)
|
||||
{
|
||||
Rect.x = pNearRect->x - Rect.w - MARGIN;
|
||||
Rect.y = clamp(UI()->MouseY() - Rect.h / 2.0f, MARGIN, pScreen->h - Rect.h - MARGIN);
|
||||
}
|
||||
|
||||
RenderTools()->DrawUIRect(&Rect, ColorRGBA(0.2, 0.2, 0.2, 0.80f), CUI::CORNER_ALL, 5.0f);
|
||||
Rect.Margin(2.0f, &Rect);
|
||||
UI()->DoLabel(&Rect, pText, 14.0f, TEXTALIGN_LEFT);
|
||||
}
|
||||
}
|
|
@ -530,7 +530,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
|
|||
{
|
||||
m_Dummy ^= 1;
|
||||
}
|
||||
DoToolTip(&m_Dummy, &DummyLabel, Localize("Toggle to edit your dummy settings."));
|
||||
GameClient()->m_Tooltips.DoToolTip(&m_Dummy, &DummyLabel, Localize("Toggle to edit your dummy settings."));
|
||||
|
||||
Dummy.HSplitTop(20.0f, &DummyLabel, &Dummy);
|
||||
|
||||
|
@ -1265,7 +1265,7 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
|
|||
MainView.HSplitTop(20.0f, &Button, &MainView);
|
||||
if(DoButton_CheckBox(&g_Config.m_GfxHighDetail, Localize("High Detail"), g_Config.m_GfxHighDetail, &Button))
|
||||
g_Config.m_GfxHighDetail ^= 1;
|
||||
DoToolTip(&g_Config.m_GfxHighDetail, &Button, Localize("Allows maps to render with more detail."));
|
||||
GameClient()->m_Tooltips.DoToolTip(&g_Config.m_GfxHighDetail, &Button, Localize("Allows maps to render with more detail."));
|
||||
|
||||
MainView.HSplitTop(20.0f, &Button, &MainView);
|
||||
if(DoButton_CheckBox(&g_Config.m_GfxHighdpi, Localize("Use high DPI"), g_Config.m_GfxHighdpi, &Button))
|
||||
|
@ -2635,6 +2635,7 @@ void CMenus::RenderSettingsDDNet(CUIRect MainView)
|
|||
{
|
||||
g_Config.m_ClRaceGhost ^= 1;
|
||||
}
|
||||
GameClient()->m_Tooltips.DoToolTip(&g_Config.m_ClRaceGhost, &Button, Localize("When you cross the start line, show a ghost tee replicating the movements of your best time."));
|
||||
|
||||
if(g_Config.m_ClRaceGhost)
|
||||
{
|
||||
|
@ -2686,13 +2687,15 @@ void CMenus::RenderSettingsDDNet(CUIRect MainView)
|
|||
Button.VSplitMid(&LeftLeft, &Button);
|
||||
|
||||
Button.VSplitLeft(50.0f, &Label, &Button);
|
||||
UI()->DoLabelScaled(&Label, Localize("Alpha"), 14.0f, TEXTALIGN_LEFT);
|
||||
UI()->DoLabelScaled(&Label, Localize("Opacity"), 14.0f, TEXTALIGN_LEFT);
|
||||
g_Config.m_ClShowOthersAlpha = (int)(UIEx()->DoScrollbarH(&g_Config.m_ClShowOthersAlpha, &Button, g_Config.m_ClShowOthersAlpha / 100.0f) * 100.0f);
|
||||
|
||||
if(DoButton_CheckBox(&g_Config.m_ClShowOthers, Localize("Show others"), g_Config.m_ClShowOthers == SHOW_OTHERS_ON, &LeftLeft))
|
||||
{
|
||||
g_Config.m_ClShowOthers = g_Config.m_ClShowOthers != SHOW_OTHERS_ON ? SHOW_OTHERS_ON : SHOW_OTHERS_OFF;
|
||||
}
|
||||
|
||||
GameClient()->m_Tooltips.DoToolTip(&g_Config.m_ClShowOthersAlpha, &Button, "Adjust the opacity of entities belonging to other teams, such as tees and nameplates");
|
||||
}
|
||||
|
||||
Left.HSplitTop(20.0f, &Button, &Left);
|
||||
|
@ -2707,6 +2710,7 @@ void CMenus::RenderSettingsDDNet(CUIRect MainView)
|
|||
{
|
||||
g_Config.m_ClShowQuads ^= 1;
|
||||
}
|
||||
GameClient()->m_Tooltips.DoToolTip(&g_Config.m_ClShowQuads, &Button, Localize("Quads are used for background decoration"));
|
||||
|
||||
Right.HSplitTop(20.0f, &Label, &Right);
|
||||
Label.VSplitLeft(130.0f, &Label, &Button);
|
||||
|
@ -2720,6 +2724,7 @@ void CMenus::RenderSettingsDDNet(CUIRect MainView)
|
|||
{
|
||||
g_Config.m_ClAntiPing ^= 1;
|
||||
}
|
||||
GameClient()->m_Tooltips.DoToolTip(&g_Config.m_ClAntiPing, &Button, Localize("Tries to predict other entities to give a feel of low latency."));
|
||||
|
||||
if(g_Config.m_ClAntiPing)
|
||||
{
|
||||
|
|
117
src/game/client/components/tooltips.cpp
Normal file
117
src/game/client/components/tooltips.cpp
Normal file
|
@ -0,0 +1,117 @@
|
|||
#include "tooltips.h"
|
||||
|
||||
#include <game/client/render.h>
|
||||
|
||||
CTooltips::CTooltips()
|
||||
{
|
||||
OnReset();
|
||||
}
|
||||
|
||||
void CTooltips::OnReset()
|
||||
{
|
||||
HoverTime = -1;
|
||||
m_Tooltips.clear();
|
||||
}
|
||||
|
||||
void CTooltips::SetActiveTooltip(CTooltip &Tooltip)
|
||||
{
|
||||
if(m_ActiveTooltip.has_value())
|
||||
return;
|
||||
|
||||
m_ActiveTooltip.emplace(Tooltip);
|
||||
HoverTime = time_get();
|
||||
}
|
||||
|
||||
inline void CTooltips::ClearActiveTooltip()
|
||||
{
|
||||
m_ActiveTooltip.reset();
|
||||
}
|
||||
|
||||
void CTooltips::DoToolTip(const void *pID, const CUIRect *pNearRect, const char *pText, float WidthHint)
|
||||
{
|
||||
uintptr_t ID = reinterpret_cast<uintptr_t>(pID);
|
||||
|
||||
const auto &it = m_Tooltips.find(ID);
|
||||
|
||||
if(it == m_Tooltips.end())
|
||||
{
|
||||
CTooltip NewTooltip = {
|
||||
.m_Rect = *pNearRect,
|
||||
.m_pText = pText,
|
||||
.m_WidthHint = WidthHint,
|
||||
};
|
||||
|
||||
m_Tooltips[ID] = NewTooltip;
|
||||
|
||||
CTooltip &Tooltip = m_Tooltips[ID];
|
||||
|
||||
if(UI()->MouseInside(&Tooltip.m_Rect))
|
||||
{
|
||||
SetActiveTooltip(Tooltip);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(UI()->MouseInside(&it->second.m_Rect))
|
||||
{
|
||||
SetActiveTooltip(it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CTooltips::OnRender()
|
||||
{
|
||||
if(m_ActiveTooltip.has_value())
|
||||
{
|
||||
CTooltip &Tooltip = m_ActiveTooltip.value();
|
||||
|
||||
if(!UI()->MouseInside(&Tooltip.m_Rect))
|
||||
{
|
||||
ClearActiveTooltip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Delay tooltip until 1 second passed.
|
||||
if(HoverTime > time_get() - time_freq())
|
||||
return;
|
||||
|
||||
const float MARGIN = 5.0f;
|
||||
|
||||
CUIRect Rect;
|
||||
Rect.w = Tooltip.m_WidthHint;
|
||||
if(Tooltip.m_WidthHint < 0.0f)
|
||||
Rect.w = TextRender()->TextWidth(0, 14.0f, Tooltip.m_pText, -1, -1.0f) + 4.0f;
|
||||
Rect.h = 30.0f;
|
||||
|
||||
CUIRect *pScreen = UI()->Screen();
|
||||
|
||||
// Try the top side.
|
||||
if(Tooltip.m_Rect.y - Rect.h - MARGIN > pScreen->y)
|
||||
{
|
||||
Rect.x = clamp(UI()->MouseX() - Rect.w / 2.0f, MARGIN, pScreen->w - Rect.w - MARGIN);
|
||||
Rect.y = Tooltip.m_Rect.y - Rect.h - MARGIN;
|
||||
}
|
||||
// Try the bottom side.
|
||||
else if(Tooltip.m_Rect.y + Tooltip.m_Rect.h + MARGIN < pScreen->h)
|
||||
{
|
||||
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;
|
||||
}
|
||||
// Try the right side.
|
||||
else if(Tooltip.m_Rect.x + Tooltip.m_Rect.w + MARGIN + Rect.w < pScreen->w)
|
||||
{
|
||||
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);
|
||||
}
|
||||
// Try the left side.
|
||||
else if(Tooltip.m_Rect.x - Rect.w - MARGIN > pScreen->x)
|
||||
{
|
||||
Rect.x = Tooltip.m_Rect.x - Rect.w - MARGIN;
|
||||
Rect.y = clamp(UI()->MouseY() - Rect.h / 2.0f, MARGIN, pScreen->h - Rect.h - MARGIN);
|
||||
}
|
||||
|
||||
RenderTools()->DrawUIRect(&Rect, ColorRGBA(0.2, 0.2, 0.2, 0.80f), CUI::CORNER_ALL, 5.0f);
|
||||
Rect.Margin(2.0f, &Rect);
|
||||
UI()->DoLabel(&Rect, Tooltip.m_pText, 14.0f, TEXTALIGN_LEFT);
|
||||
}
|
||||
}
|
59
src/game/client/components/tooltips.h
Normal file
59
src/game/client/components/tooltips.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
#ifndef GAME_CLIENT_COMPONENTS_TOOLTIPS_H
|
||||
#define GAME_CLIENT_COMPONENTS_TOOLTIPS_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <game/client/component.h>
|
||||
#include <game/client/ui.h>
|
||||
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
|
||||
struct CTooltip
|
||||
{
|
||||
CUIRect m_Rect;
|
||||
const char *m_pText;
|
||||
float m_WidthHint;
|
||||
};
|
||||
|
||||
/**
|
||||
* A component that manages and renders UI tooltips.
|
||||
*
|
||||
* Should be among the last components to render.
|
||||
*/
|
||||
class CTooltips : public CComponent
|
||||
{
|
||||
std::unordered_map<uintptr_t, CTooltip> m_Tooltips;
|
||||
std::optional<std::reference_wrapper<CTooltip>> m_ActiveTooltip;
|
||||
int64_t HoverTime;
|
||||
|
||||
/**
|
||||
* The passed tooltip is only actually set if there is no currently active tooltip.
|
||||
*
|
||||
* @param Tooltip A reference to the tooltip that should be active.
|
||||
*/
|
||||
void SetActiveTooltip(CTooltip &Tooltip);
|
||||
|
||||
inline void ClearActiveTooltip();
|
||||
|
||||
public:
|
||||
CTooltips();
|
||||
virtual int Sizeof() const override { return sizeof(*this); }
|
||||
|
||||
/**
|
||||
* Adds the tooltip to a cache and renders it when active.
|
||||
*
|
||||
* On the first call to this function, the data passed is cached, afterwards the calls are used to detect if the tooltip should be activated.
|
||||
*
|
||||
* For now only works correctly with single line tooltips, since Text width calculation gets broken when there are multiple lines.
|
||||
*
|
||||
* @param pID The ID of the tooltip. Usually a reference to some g_Config value.
|
||||
* @param pNearTo Place the tooltip near this rect.
|
||||
* @param pText The text to display in the tooltip
|
||||
*/
|
||||
void DoToolTip(const void *pID, const CUIRect *pNearRect, const char *pText, float WidthHint = -1.0f);
|
||||
|
||||
virtual void OnReset() override;
|
||||
virtual void OnRender() override;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -137,6 +137,7 @@ void CGameClient::OnConsoleInit()
|
|||
m_All.Add(&m_Statboard);
|
||||
m_All.Add(&m_Motd);
|
||||
m_All.Add(&m_Menus);
|
||||
m_All.Add(&m_Tooltips);
|
||||
m_All.Add(&CMenus::m_Binder);
|
||||
m_All.Add(&m_GameConsole);
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include "components/spectator.h"
|
||||
#include "components/statboard.h"
|
||||
#include "components/voting.h"
|
||||
#include "components/tooltips.h"
|
||||
|
||||
class CGameInfo
|
||||
{
|
||||
|
@ -144,6 +145,8 @@ public:
|
|||
CRaceDemo m_RaceDemo;
|
||||
CGhost m_Ghost;
|
||||
|
||||
CTooltips m_Tooltips;
|
||||
|
||||
private:
|
||||
class CStack
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue