ddnet/src/game/client/ui.cpp

623 lines
14 KiB
C++
Raw Normal View History

2010-11-20 10:37:14 +00:00
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <base/math.h>
#include <base/system.h>
#include "ui.h"
2010-05-29 07:25:38 +00:00
#include <engine/graphics.h>
#include <engine/shared/config.h>
2010-05-29 07:25:38 +00:00
#include <engine/textrender.h>
2007-05-22 15:03:32 +00:00
2021-09-13 21:48:10 +00:00
void CUIElement::Init(CUI *pUI, int RequestedRectCount)
2020-10-12 10:29:47 +00:00
{
pUI->AddUIElement(this);
2021-09-13 21:48:10 +00:00
if(RequestedRectCount > 0)
m_UIRects.resize(RequestedRectCount);
2020-10-12 10:29:47 +00:00
}
2021-09-13 21:48:10 +00:00
void CUIElement::InitRects(int RequestedRectCount)
2020-11-25 12:05:53 +00:00
{
2021-09-13 21:48:10 +00:00
dbg_assert(m_UIRects.size() == 0, "UI rects can only be initialized once, create another ui element instead.");
m_UIRects.resize(RequestedRectCount);
}
CUIElement::SUIElementRect::SUIElementRect() { Reset(); }
void CUIElement::SUIElementRect::Reset()
{
m_UIRectQuadContainer = -1;
m_UITextContainer = -1;
m_X = -1;
m_Y = -1;
m_Width = -1;
m_Height = -1;
m_Text.clear();
2020-11-25 12:05:53 +00:00
mem_zero(&m_Cursor, sizeof(m_Cursor));
2021-09-13 21:48:10 +00:00
m_TextColor.Set(-1, -1, -1, -1);
m_TextOutlineColor.Set(-1, -1, -1, -1);
m_QuadColor = ColorRGBA(-1, -1, -1, -1);
2020-11-25 12:05:53 +00:00
}
2007-05-22 15:03:32 +00:00
/********************************************************
UI
2007-05-22 15:03:32 +00:00
*********************************************************/
2009-10-27 14:38:53 +00:00
2021-12-03 18:49:22 +00:00
float CUI::ms_FontmodHeight = 0.8f;
void CUI::Init(class IGraphics *pGraphics, class ITextRender *pTextRender)
{
m_pGraphics = pGraphics;
m_pTextRender = pTextRender;
}
2009-10-27 14:38:53 +00:00
CUI::CUI()
{
m_pHotItem = 0;
m_pActiveItem = 0;
m_pLastActiveItem = 0;
m_pBecomingHotItem = 0;
2009-10-27 14:38:53 +00:00
m_MouseX = 0;
m_MouseY = 0;
m_MouseWorldX = 0;
m_MouseWorldY = 0;
m_MouseButtons = 0;
m_LastMouseButtons = 0;
2009-10-27 14:38:53 +00:00
m_Screen.x = 0;
m_Screen.y = 0;
m_Screen.w = 848.0f;
m_Screen.h = 480.0f;
}
2020-10-12 10:29:47 +00:00
CUI::~CUI()
{
for(CUIElement *&pEl : m_OwnUIElements)
{
delete pEl;
}
m_OwnUIElements.clear();
}
2021-09-13 21:48:10 +00:00
CUIElement *CUI::GetNewUIElement(int RequestedRectCount)
2020-10-12 10:29:47 +00:00
{
2021-09-13 21:48:10 +00:00
CUIElement *pNewEl = new CUIElement(this, RequestedRectCount);
2020-10-12 10:29:47 +00:00
m_OwnUIElements.push_back(pNewEl);
return pNewEl;
}
void CUI::AddUIElement(CUIElement *pElement)
{
m_UIElements.push_back(pElement);
}
void CUI::ResetUIElement(CUIElement &UIElement)
{
for(CUIElement::SUIElementRect &Rect : UIElement.m_UIRects)
{
if(Rect.m_UIRectQuadContainer != -1)
Graphics()->DeleteQuadContainer(Rect.m_UIRectQuadContainer);
Rect.m_UIRectQuadContainer = -1;
if(Rect.m_UITextContainer != -1)
TextRender()->DeleteTextContainer(Rect.m_UITextContainer);
Rect.m_UITextContainer = -1;
2021-09-13 21:48:10 +00:00
Rect.Reset();
}
2020-10-12 10:29:47 +00:00
}
void CUI::OnElementsReset()
{
for(CUIElement *pEl : m_UIElements)
{
ResetUIElement(*pEl);
}
}
void CUI::OnWindowResize()
{
OnElementsReset();
}
void CUI::OnLanguageChange()
{
OnElementsReset();
}
2010-05-29 07:25:38 +00:00
int CUI::Update(float Mx, float My, float Mwx, float Mwy, int Buttons)
2007-05-22 15:03:32 +00:00
{
2020-12-14 00:51:31 +00:00
m_MouseDeltaX = Mx - m_MouseX;
m_MouseDeltaY = My - m_MouseY;
m_MouseX = Mx;
m_MouseY = My;
m_MouseWorldX = Mwx;
m_MouseWorldY = Mwy;
m_LastMouseButtons = m_MouseButtons;
m_MouseButtons = Buttons;
m_pHotItem = m_pBecomingHotItem;
if(m_pActiveItem)
m_pHotItem = m_pActiveItem;
m_pBecomingHotItem = 0;
return 0;
2007-05-22 15:03:32 +00:00
}
2009-10-27 14:38:53 +00:00
2021-11-26 20:55:31 +00:00
bool CUI::MouseInside(const CUIRect *pRect) const
2007-05-22 15:03:32 +00:00
{
2021-11-26 20:55:31 +00:00
return pRect->Inside(m_MouseX, m_MouseY);
2007-05-22 15:03:32 +00:00
}
void CUI::ConvertMouseMove(float *x, float *y) const
{
float Fac = (float)(g_Config.m_UiMousesens) / g_Config.m_InpMousesens;
*x = *x * Fac;
*y = *y * Fac;
}
2021-11-26 20:55:31 +00:00
float CUI::ButtonColorMul(const void *pID)
{
if(ActiveItem() == pID)
return ButtonColorMulActive();
else if(HotItem() == pID)
return ButtonColorMulHot();
return ButtonColorMulDefault();
}
2009-10-27 14:38:53 +00:00
CUIRect *CUI::Screen()
2007-05-22 15:03:32 +00:00
{
float Aspect = Graphics()->ScreenAspect();
float w, h;
2007-05-22 15:03:32 +00:00
h = 600;
w = Aspect * h;
2007-05-22 15:03:32 +00:00
m_Screen.w = w;
m_Screen.h = h;
2007-05-27 18:14:24 +00:00
return &m_Screen;
2007-05-22 15:03:32 +00:00
}
2021-09-22 19:48:55 +00:00
void CUI::MapScreen()
{
const CUIRect *pScreen = Screen();
Graphics()->MapScreen(pScreen->x, pScreen->y, pScreen->w, pScreen->h);
}
float CUI::PixelSize()
{
return Screen()->w / Graphics()->ScreenWidth();
}
2009-10-27 14:38:53 +00:00
void CUI::SetScale(float s)
2007-10-29 18:40:04 +00:00
{
g_Config.m_UiScale = (int)(s * 100.0f);
2007-10-29 18:40:04 +00:00
}
float CUI::Scale() const
2007-10-29 18:40:04 +00:00
{
return g_Config.m_UiScale / 100.0f;
}
float CUIRect::Scale() const
{
return g_Config.m_UiScale / 100.0f;
}
2007-10-29 18:40:04 +00:00
2009-10-27 14:38:53 +00:00
void CUI::ClipEnable(const CUIRect *r)
2007-10-29 18:40:04 +00:00
{
float XScale = Graphics()->ScreenWidth() / Screen()->w;
float YScale = Graphics()->ScreenHeight() / Screen()->h;
Graphics()->ClipEnable((int)(r->x * XScale), (int)(r->y * YScale), (int)(r->w * XScale), (int)(r->h * YScale));
2007-10-29 18:40:04 +00:00
}
2009-10-27 14:38:53 +00:00
void CUI::ClipDisable()
2007-10-29 18:40:04 +00:00
{
2009-10-27 14:38:53 +00:00
Graphics()->ClipDisable();
2007-10-29 18:40:04 +00:00
}
2011-03-26 15:19:37 +00:00
void CUIRect::HSplitMid(CUIRect *pTop, CUIRect *pBottom) const
{
CUIRect r = *this;
float Cut = r.h / 2;
if(pTop)
{
pTop->x = r.x;
pTop->y = r.y;
pTop->w = r.w;
pTop->h = Cut;
}
if(pBottom)
{
pBottom->x = r.x;
pBottom->y = r.y + Cut;
pBottom->w = r.w;
pBottom->h = r.h - Cut;
}
2011-03-26 15:19:37 +00:00
}
2010-05-29 07:25:38 +00:00
void CUIRect::HSplitTop(float Cut, CUIRect *pTop, CUIRect *pBottom) const
2007-10-29 18:40:04 +00:00
{
CUIRect r = *this;
Cut *= Scale();
if(pTop)
{
pTop->x = r.x;
pTop->y = r.y;
pTop->w = r.w;
pTop->h = Cut;
}
if(pBottom)
{
pBottom->x = r.x;
pBottom->y = r.y + Cut;
pBottom->w = r.w;
pBottom->h = r.h - Cut;
}
2007-10-29 18:40:04 +00:00
}
2010-05-29 07:25:38 +00:00
void CUIRect::HSplitBottom(float Cut, CUIRect *pTop, CUIRect *pBottom) const
2007-10-29 18:40:04 +00:00
{
CUIRect r = *this;
Cut *= Scale();
if(pTop)
{
pTop->x = r.x;
pTop->y = r.y;
pTop->w = r.w;
pTop->h = r.h - Cut;
}
if(pBottom)
{
pBottom->x = r.x;
pBottom->y = r.y + r.h - Cut;
pBottom->w = r.w;
pBottom->h = Cut;
}
2007-10-29 18:40:04 +00:00
}
2010-05-29 07:25:38 +00:00
void CUIRect::VSplitMid(CUIRect *pLeft, CUIRect *pRight) const
2007-10-29 18:40:04 +00:00
{
CUIRect r = *this;
float Cut = r.w / 2;
// Cut *= Scale();
if(pLeft)
{
pLeft->x = r.x;
pLeft->y = r.y;
pLeft->w = Cut;
pLeft->h = r.h;
}
if(pRight)
{
pRight->x = r.x + Cut;
pRight->y = r.y;
pRight->w = r.w - Cut;
pRight->h = r.h;
}
2008-01-12 12:27:55 +00:00
}
2010-05-29 07:25:38 +00:00
void CUIRect::VSplitLeft(float Cut, CUIRect *pLeft, CUIRect *pRight) const
2008-01-12 12:27:55 +00:00
{
CUIRect r = *this;
Cut *= Scale();
if(pLeft)
{
pLeft->x = r.x;
pLeft->y = r.y;
pLeft->w = Cut;
pLeft->h = r.h;
}
if(pRight)
{
pRight->x = r.x + Cut;
pRight->y = r.y;
pRight->w = r.w - Cut;
pRight->h = r.h;
}
2007-10-29 18:40:04 +00:00
}
2010-05-29 07:25:38 +00:00
void CUIRect::VSplitRight(float Cut, CUIRect *pLeft, CUIRect *pRight) const
2007-10-29 18:40:04 +00:00
{
CUIRect r = *this;
Cut *= Scale();
if(pLeft)
{
pLeft->x = r.x;
pLeft->y = r.y;
pLeft->w = r.w - Cut;
pLeft->h = r.h;
}
if(pRight)
{
pRight->x = r.x + r.w - Cut;
pRight->y = r.y;
pRight->w = Cut;
pRight->h = r.h;
}
2008-01-12 12:27:55 +00:00
}
2007-10-29 18:40:04 +00:00
2010-05-29 07:25:38 +00:00
void CUIRect::Margin(float Cut, CUIRect *pOtherRect) const
2008-01-12 12:27:55 +00:00
{
CUIRect r = *this;
2010-05-29 07:25:38 +00:00
Cut *= Scale();
2008-01-12 12:27:55 +00:00
pOtherRect->x = r.x + Cut;
pOtherRect->y = r.y + Cut;
pOtherRect->w = r.w - 2 * Cut;
pOtherRect->h = r.h - 2 * Cut;
2008-01-12 12:27:55 +00:00
}
2010-05-29 07:25:38 +00:00
void CUIRect::VMargin(float Cut, CUIRect *pOtherRect) const
2008-01-12 12:27:55 +00:00
{
CUIRect r = *this;
2010-05-29 07:25:38 +00:00
Cut *= Scale();
2008-01-12 12:27:55 +00:00
pOtherRect->x = r.x + Cut;
pOtherRect->y = r.y;
pOtherRect->w = r.w - 2 * Cut;
pOtherRect->h = r.h;
2007-10-29 18:40:04 +00:00
}
2010-05-29 07:25:38 +00:00
void CUIRect::HMargin(float Cut, CUIRect *pOtherRect) const
2007-10-29 18:40:04 +00:00
{
CUIRect r = *this;
2010-05-29 07:25:38 +00:00
Cut *= Scale();
2007-10-29 18:40:04 +00:00
pOtherRect->x = r.x;
pOtherRect->y = r.y + Cut;
pOtherRect->w = r.w;
pOtherRect->h = r.h - 2 * Cut;
2008-01-12 12:27:55 +00:00
}
2021-11-26 20:55:31 +00:00
bool CUIRect::Inside(float x, float y) const
{
return x >= this->x && x < this->x + this->w && y >= this->y && y < this->y + this->h;
}
2009-10-27 14:38:53 +00:00
int CUI::DoButtonLogic(const void *pID, const char *pText, int Checked, const CUIRect *pRect)
2020-10-12 10:29:47 +00:00
{
return DoButtonLogic(pID, Checked, pRect);
}
int CUI::DoButtonLogic(const void *pID, int Checked, const CUIRect *pRect)
2008-01-12 12:27:55 +00:00
{
// logic
int ReturnValue = 0;
int Inside = MouseInside(pRect);
2009-10-27 14:38:53 +00:00
static int ButtonUsed = 0;
if(ActiveItem() == pID)
{
if(!MouseButton(ButtonUsed))
{
if(Inside && Checked >= 0)
ReturnValue = 1 + ButtonUsed;
2009-10-27 14:38:53 +00:00
SetActiveItem(0);
}
}
else if(HotItem() == pID)
{
for(int i = 0; i < 3; ++i)
2009-10-27 14:38:53 +00:00
{
if(MouseButton(i))
{
SetActiveItem(pID);
ButtonUsed = i;
}
2009-10-27 14:38:53 +00:00
}
}
2009-10-27 14:38:53 +00:00
if(Inside)
SetHotItem(pID);
return ReturnValue;
2009-10-27 14:38:53 +00:00
}
int CUI::DoPickerLogic(const void *pID, const CUIRect *pRect, float *pX, float *pY)
{
int Inside = MouseInside(pRect);
2020-12-14 00:51:31 +00:00
if(Inside)
SetHotItem(pID);
2020-12-14 00:51:31 +00:00
if(HotItem() == pID && MouseButtonClicked(0))
SetActiveItem(pID);
if(ActiveItem() == pID && !MouseButton(0))
SetActiveItem(0);
if(ActiveItem() != pID)
return 0;
if(pX)
*pX = clamp(m_MouseX - pRect->x, 0.0f, pRect->w) / Scale();
if(pY)
*pY = clamp(m_MouseY - pRect->y, 0.0f, pRect->h) / Scale();
return 1;
}
float CUI::DoTextLabel(float x, float y, float w, float h, const char *pText, float Size, int Align, float MaxWidth, int AlignVertically, bool StopAtEnd, class CTextCursor *pSelCursor)
2008-01-12 12:27:55 +00:00
{
2020-10-06 10:25:10 +00:00
float AlignedSize = 0;
float MaxCharacterHeightInLine = 0;
float tw = TextRender()->TextWidth(0, Size, pText, -1, MaxWidth, &AlignedSize, &MaxCharacterHeightInLine);
2021-09-13 21:48:10 +00:00
int Flags = TEXTFLAG_RENDER | (StopAtEnd ? TEXTFLAG_STOP_AT_END : 0);
float AlignmentVert = y + (h - AlignedSize) / 2.f;
float AlignmentHori = 0;
2020-10-06 10:25:10 +00:00
if(AlignVertically == 0)
{
2021-09-13 21:48:10 +00:00
AlignmentVert = y + (h - AlignedSize) / 2.f - (AlignedSize - MaxCharacterHeightInLine) / 2.f;
2020-10-06 10:25:10 +00:00
}
if(Align == 0)
{
2021-09-13 21:48:10 +00:00
AlignmentHori = x + (w - tw) / 2.f;
2008-01-12 12:27:55 +00:00
}
2010-05-29 07:25:38 +00:00
else if(Align < 0)
{
2021-09-13 21:48:10 +00:00
AlignmentHori = x;
}
2010-05-29 07:25:38 +00:00
else if(Align > 0)
2008-01-12 12:27:55 +00:00
{
2021-09-13 21:48:10 +00:00
AlignmentHori = x + w - tw;
2008-01-12 12:27:55 +00:00
}
2021-09-13 21:48:10 +00:00
CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, AlignmentHori, AlignmentVert, Size, Flags);
Cursor.m_LineWidth = (float)MaxWidth;
if(pSelCursor)
{
Cursor.m_CursorMode = pSelCursor->m_CursorMode;
Cursor.m_CursorCharacter = pSelCursor->m_CursorCharacter;
Cursor.m_CalculateSelectionMode = pSelCursor->m_CalculateSelectionMode;
Cursor.m_PressMouseX = pSelCursor->m_PressMouseX;
Cursor.m_PressMouseY = pSelCursor->m_PressMouseY;
Cursor.m_ReleaseMouseX = pSelCursor->m_ReleaseMouseX;
Cursor.m_ReleaseMouseY = pSelCursor->m_ReleaseMouseY;
Cursor.m_SelectionStart = pSelCursor->m_SelectionStart;
Cursor.m_SelectionEnd = pSelCursor->m_SelectionEnd;
}
2021-09-13 21:48:10 +00:00
TextRender()->TextEx(&Cursor, pText, -1);
if(pSelCursor)
{
*pSelCursor = Cursor;
}
2021-09-13 21:48:10 +00:00
return tw;
}
void CUI::DoLabel(const CUIRect *r, const char *pText, float Size, int Align, float MaxWidth, int AlignVertically, CTextCursor *pSelCursor)
2021-09-13 21:48:10 +00:00
{
DoTextLabel(r->x, r->y, r->w, r->h, pText, Size, Align, MaxWidth, AlignVertically, false, pSelCursor);
2007-10-29 18:40:04 +00:00
}
void CUI::DoLabelScaled(const CUIRect *r, const char *pText, float Size, int Align, float MaxWidth, int AlignVertically)
{
2020-10-06 10:25:10 +00:00
DoLabel(r, pText, Size * Scale(), Align, MaxWidth, AlignVertically);
}
2020-10-12 10:29:47 +00:00
void CUI::DoLabel(CUIElement::SUIElementRect &RectEl, const CUIRect *pRect, const char *pText, float Size, int Align, float MaxWidth, int AlignVertically, bool StopAtEnd, int StrLen, CTextCursor *pReadCursor)
2020-10-12 10:29:47 +00:00
{
float AlignedSize = 0;
float MaxCharacterHeightInLine = 0;
float tw = TextRender()->TextWidth(0, Size, pText, -1, MaxWidth, &AlignedSize, &MaxCharacterHeightInLine);
float AlignmentVert = pRect->y + (pRect->h - AlignedSize) / 2.f;
float AlignmentHori = 0;
CTextCursor Cursor;
int Flags = TEXTFLAG_RENDER | (StopAtEnd ? TEXTFLAG_STOP_AT_END : 0);
if(AlignVertically == 0)
{
AlignmentVert = pRect->y + (pRect->h - AlignedSize) / 2.f - (AlignedSize - MaxCharacterHeightInLine) / 2.f;
}
if(Align == 0)
{
AlignmentHori = pRect->x + (pRect->w - tw) / 2.f;
}
else if(Align < 0)
{
AlignmentHori = pRect->x;
}
else if(Align > 0)
{
AlignmentHori = pRect->x + pRect->w - tw;
}
if(pReadCursor)
{
Cursor = *pReadCursor;
}
else
{
TextRender()->SetCursor(&Cursor, AlignmentHori, AlignmentVert, Size, Flags);
}
Cursor.m_LineWidth = MaxWidth;
2020-11-08 18:41:16 +00:00
RectEl.m_TextColor = TextRender()->GetTextColor();
RectEl.m_TextOutlineColor = TextRender()->GetTextOutlineColor();
2021-09-13 21:48:10 +00:00
TextRender()->TextColor(TextRender()->DefaultTextColor());
TextRender()->TextOutlineColor(TextRender()->DefaultTextOutlineColor());
RectEl.m_UITextContainer = TextRender()->CreateTextContainer(&Cursor, pText, StrLen);
TextRender()->TextColor(RectEl.m_TextColor);
TextRender()->TextOutlineColor(RectEl.m_TextOutlineColor);
RectEl.m_Cursor = Cursor;
2020-10-12 10:29:47 +00:00
}
2021-09-13 21:48:10 +00:00
void CUI::DoLabelStreamed(CUIElement::SUIElementRect &RectEl, float x, float y, float w, float h, const char *pText, float Size, int Align, float MaxWidth, int AlignVertically, bool StopAtEnd, int StrLen, CTextCursor *pReadCursor)
2020-10-12 10:29:47 +00:00
{
bool NeedsRecreate = false;
2020-11-08 18:41:16 +00:00
bool ColorChanged = RectEl.m_TextColor != TextRender()->GetTextColor() || RectEl.m_TextOutlineColor != TextRender()->GetTextOutlineColor();
2021-09-13 21:48:10 +00:00
if(RectEl.m_UITextContainer == -1 || RectEl.m_X != x || RectEl.m_Y != y || RectEl.m_Width != w || RectEl.m_Height != h || ColorChanged)
2020-10-12 10:29:47 +00:00
{
NeedsRecreate = true;
}
else
{
if(StrLen <= -1)
{
if(str_comp(RectEl.m_Text.c_str(), pText) != 0)
NeedsRecreate = true;
}
else
{
if(StrLen != (int)RectEl.m_Text.size() || str_comp_num(RectEl.m_Text.c_str(), pText, StrLen) != 0)
NeedsRecreate = true;
}
}
if(NeedsRecreate)
{
if(RectEl.m_UITextContainer != -1)
TextRender()->DeleteTextContainer(RectEl.m_UITextContainer);
RectEl.m_UITextContainer = -1;
2021-09-13 21:48:10 +00:00
RectEl.m_X = x;
RectEl.m_Y = y;
RectEl.m_Width = w;
RectEl.m_Height = h;
2020-10-12 10:29:47 +00:00
if(StrLen > 0)
RectEl.m_Text = std::string(pText, StrLen);
else if(StrLen < 0)
RectEl.m_Text = pText;
else
RectEl.m_Text.clear();
2021-09-13 21:48:10 +00:00
CUIRect TmpRect;
TmpRect.x = x;
TmpRect.y = y;
TmpRect.w = w;
TmpRect.h = h;
DoLabel(RectEl, &TmpRect, pText, Size, Align, MaxWidth, AlignVertically, StopAtEnd, StrLen, pReadCursor);
2020-10-12 10:29:47 +00:00
}
2021-09-13 21:48:10 +00:00
STextRenderColor ColorText(RectEl.m_TextColor);
STextRenderColor ColorTextOutline(RectEl.m_TextOutlineColor);
2020-10-12 10:29:47 +00:00
if(RectEl.m_UITextContainer != -1)
TextRender()->RenderTextContainer(RectEl.m_UITextContainer, &ColorText, &ColorTextOutline);
}
2021-09-13 21:48:10 +00:00
void CUI::DoLabelStreamed(CUIElement::SUIElementRect &RectEl, const CUIRect *pRect, const char *pText, float Size, int Align, float MaxWidth, int AlignVertically, bool StopAtEnd, int StrLen, CTextCursor *pReadCursor)
{
DoLabelStreamed(RectEl, pRect->x, pRect->y, pRect->w, pRect->h, pText, Size, Align, MaxWidth, AlignVertically, StopAtEnd, StrLen, pReadCursor);
}