diff --git a/src/engine/client/text.cpp b/src/engine/client/text.cpp index b2a60d6c5..f0734b131 100644 --- a/src/engine/client/text.cpp +++ b/src/engine/client/text.cpp @@ -1091,7 +1091,7 @@ public: pCursor->m_Y = DrawY; } - virtual int CreateTextContainer(CTextCursor *pCursor, const char *pText) + virtual int CreateTextContainer(CTextCursor *pCursor, const char *pText, int Length = -1) { CFont *pFont = pCursor->m_pFont; @@ -1144,7 +1144,7 @@ public: TextContainer.m_FontSize = pSizeData->m_FontSize; - AppendTextContainer(pCursor, ContainerIndex, pText); + AppendTextContainer(pCursor, ContainerIndex, pText, Length); if(TextContainer.m_StringInfo.m_CharacterQuads.size() == 0) { @@ -1181,7 +1181,7 @@ public: return ContainerIndex; } - virtual void AppendTextContainer(CTextCursor *pCursor, int TextContainerIndex, const char *pText) + virtual void AppendTextContainer(CTextCursor *pCursor, int TextContainerIndex, const char *pText, int Length = -1) { STextContainer &TextContainer = GetTextContainer(TextContainerIndex); @@ -1218,7 +1218,8 @@ public: pSizeData = TextContainer.m_pFont->GetFontSize(TextContainer.m_FontSize); // string length - int Length = str_length(pText); + if(Length < 0) + Length = str_length(pText); float Scale = 1.0f / pSizeData->m_FontSize; @@ -1443,19 +1444,19 @@ public: } // just deletes and creates text container - virtual void RecreateTextContainer(CTextCursor *pCursor, int TextContainerIndex, const char *pText) + virtual void RecreateTextContainer(CTextCursor *pCursor, int TextContainerIndex, const char *pText, int Length = -1) { DeleteTextContainer(TextContainerIndex); - CreateTextContainer(pCursor, pText); + CreateTextContainer(pCursor, pText, Length); } - virtual void RecreateTextContainerSoft(CTextCursor *pCursor, int TextContainerIndex, const char *pText) + virtual void RecreateTextContainerSoft(CTextCursor *pCursor, int TextContainerIndex, const char *pText, int Length = -1) { STextContainer &TextContainer = GetTextContainer(TextContainerIndex); TextContainer.m_StringInfo.m_CharacterQuads.clear(); TextContainer.m_StringInfo.m_QuadNum = 0; // the text buffer gets then recreated by the appended quads - AppendTextContainer(pCursor, TextContainerIndex, pText); + AppendTextContainer(pCursor, TextContainerIndex, pText, Length); } virtual void SetTextContainerSelection(int TextContainerIndex, const char *pText, int CursorPos, int SelectionStart, int SelectionEnd) diff --git a/src/engine/serverbrowser.h b/src/engine/serverbrowser.h index 014c044d2..8e8dd736d 100644 --- a/src/engine/serverbrowser.h +++ b/src/engine/serverbrowser.h @@ -5,6 +5,7 @@ #include #include +#include #include "kernel.h" @@ -61,6 +62,8 @@ public: char m_aAddress[NETADDR_MAXSTRSIZE]; CClient m_aClients[MAX_CLIENTS]; mutable int m_NumFilteredPlayers; + + mutable CUIElement *m_pUIElement; }; bool IsVanilla(const CServerInfo *pInfo); diff --git a/src/engine/textrender.h b/src/engine/textrender.h index cb9274870..04bb9a9ed 100644 --- a/src/engine/textrender.h +++ b/src/engine/textrender.h @@ -61,6 +61,10 @@ struct STextRenderColor { Set(r, g, b, a); } + STextRenderColor(const ColorRGBA &TextColorRGBA) + { + Set(TextColorRGBA.r, TextColorRGBA.g, TextColorRGBA.b, TextColorRGBA.a); + } void Set(float r, float g, float b, float a) { @@ -89,13 +93,16 @@ public: virtual void SetRenderFlags(unsigned int Flags) = 0; + ColorRGBA DefaultTextColor() { return ColorRGBA(1, 1, 1, 1); } + ColorRGBA DefaultTextOutlineColor() { return ColorRGBA(0, 0, 0, 0.3f); } + // virtual void TextEx(CTextCursor *pCursor, const char *pText, int Length) = 0; - virtual int CreateTextContainer(CTextCursor *pCursor, const char *pText) = 0; - virtual void AppendTextContainer(CTextCursor *pCursor, int TextContainerIndex, const char *pText) = 0; + virtual int CreateTextContainer(CTextCursor *pCursor, const char *pText, int Length = -1) = 0; + virtual void AppendTextContainer(CTextCursor *pCursor, int TextContainerIndex, const char *pText, int Length = -1) = 0; // just deletes and creates text container - virtual void RecreateTextContainer(CTextCursor *pCursor, int TextContainerIndex, const char *pText) = 0; - virtual void RecreateTextContainerSoft(CTextCursor *pCursor, int TextContainerIndex, const char *pText) = 0; + virtual void RecreateTextContainer(CTextCursor *pCursor, int TextContainerIndex, const char *pText, int Length = -1) = 0; + virtual void RecreateTextContainerSoft(CTextCursor *pCursor, int TextContainerIndex, const char *pText, int Length = -1) = 0; virtual void SetTextContainerSelection(int TextContainerIndex, const char *pText, int CursorPos, int SelectionStart, int SelectionEnd) = 0; virtual void DeleteTextContainer(int TextContainerIndex) = 0; @@ -110,6 +117,7 @@ public: virtual void TextColor(float r, float g, float b, float a) = 0; virtual void TextColor(ColorRGBA rgb) = 0; virtual void TextOutlineColor(float r, float g, float b, float a) = 0; + virtual void TextOutlineColor(ColorRGBA rgb) = 0; virtual void Text(void *pFontSetV, float x, float y, float Size, const char *pText, float LineWidth) = 0; virtual float TextWidth(void *pFontSetV, float Size, const char *pText, int StrLength, float LineWidth, float *pAlignedHeight = NULL, float *pMaxCharacterHeightInLine = NULL) = 0; virtual int TextLineCount(void *pFontSetV, float Size, const char *pText, float LineWidth) = 0; diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index 52380d56c..09eda1ce7 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -99,10 +99,10 @@ CMenus::CMenus() float CMenus::ButtonColorMul(const void *pID) { if(UI()->ActiveItem() == pID) - return 0.5f; + return ButtonColorMulActive(); else if(UI()->HotItem() == pID) - return 1.5f; - return 1; + return ButtonColorMulHot(); + return ButtonColorMulDefault(); } int CMenus::DoButton_Icon(int ImageId, int SpriteId, const CUIRect *pRect) @@ -1107,6 +1107,9 @@ void CMenus::OnInit() if(g_Config.m_ClSkipStartMenu) m_ShowStart = false; + m_RefreshButton.Init(UI()); + m_ConnectButton.Init(UI()); + Console()->Chain("add_favorite", ConchainServerbrowserUpdate, this); Console()->Chain("remove_favorite", ConchainServerbrowserUpdate, this); Console()->Chain("add_friend", ConchainFriendlistUpdate, this); diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index 79df6207b..797c4f8b7 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -48,6 +49,11 @@ class CMenus : public CComponent static ColorRGBA ms_ColorTabbarActive; static ColorRGBA ms_ColorTabbarHover; + char m_aLocalStringHelper[1024]; + + float ButtonColorMulActive() { return 0.5f; } + float ButtonColorMulHot() { return 1.5f; } + float ButtonColorMulDefault() { return 1.0f; } float ButtonColorMul(const void *pID); int DoButton_DemoPlayer(const void *pID, const char *pText, int Checked, const CUIRect *pRect); @@ -88,6 +94,95 @@ class CMenus : public CComponent //static int ui_do_key_reader(void *id, const CUIRect *rect, int key); void UiDoGetButtons(int Start, int Stop, CUIRect View, CUIRect ScopeView); + // new gui with gui elements + template + int DoButtonMenu(CUIElement &UIElement, const void *pID, T &&GetTextLambda, int Checked, const CUIRect *pRect, bool HintRequiresStringCheck, bool HintCanChangePositionOrSize = false, int Corners = CUI::CORNER_ALL, float r = 5.0f, float FontFactor = 0.0f, vec4 ColorHot = vec4(1.0f, 1.0f, 1.0f, 0.75f), vec4 Color = vec4(1, 1, 1, 0.5f), int AlignVertically = 1) + { + CUIRect Text = *pRect; + Text.HMargin(pRect->h >= 20.0f ? 2.0f : 1.0f, &Text); + Text.HMargin((Text.h * FontFactor) / 2.0f, &Text); + + if(UIElement.Size() != 3 || HintRequiresStringCheck || HintCanChangePositionOrSize) + { + bool NeedsRecalc = UIElement.Size() != 3; + if(HintCanChangePositionOrSize) + { + if(UIElement.Size() == 3) + { + if(UIElement.Get(0)->m_X != pRect->x || UIElement.Get(0)->m_Y != pRect->y || UIElement.Get(0)->m_Width != pRect->w || UIElement.Get(0)->m_Y != pRect->h) + { + NeedsRecalc = true; + } + } + } + const char *pText = NULL; + if(HintRequiresStringCheck) + { + if(UIElement.Size() == 3) + { + pText = GetTextLambda(); + if(str_comp(UIElement.Get(0)->m_Text.c_str(), pText) != 0) + { + NeedsRecalc = true; + } + } + } + if(NeedsRecalc) + { + if(UIElement.Size() > 0) + { + UI()->ResetUIElement(UIElement); + } + + vec4 RealColor = Color; + for(int i = 0; i < 3; ++i) + { + Color.a = RealColor.a; + if(i == 0) + Color.a *= ButtonColorMulActive(); + else if(i == 1) + Color.a *= ButtonColorMulHot(); + else if(i == 2) + Color.a *= ButtonColorMulDefault(); + Graphics()->SetColor(Color); + + CUIElement::SUIElementRect NewRect; + NewRect.m_UIRectQuadContainer = RenderTools()->CreateRoundRectQuadContainer(pRect->x, pRect->y, pRect->w, pRect->h, r, Corners); + + NewRect.m_X = pRect->x; + NewRect.m_Y = pRect->y; + NewRect.m_Width = pRect->w; + NewRect.m_Height = pRect->h; + + if(i == 0) + { + if(pText == NULL) + pText = GetTextLambda(); + NewRect.m_Text = pText; + UI()->DoLabel(NewRect, &Text, pText, Text.h * ms_FontmodHeight, 0, -1, AlignVertically); + } + UIElement.Add(NewRect); + } + Graphics()->SetColor(1, 1, 1, 1); + } + } + + // render + size_t Index = 2; + if(UI()->ActiveItem() == pID) + Index = 0; + else if(UI()->HotItem() == pID) + Index = 1; + Graphics()->TextureClear(); + Graphics()->RenderQuadContainer(UIElement.Get(Index)->m_UIRectQuadContainer, -1); + STextRenderColor ColorText(TextRender()->DefaultTextColor()); + STextRenderColor ColorTextOutline(TextRender()->DefaultTextOutlineColor()); + if(UIElement.Get(0)->m_UITextContainer != -1) + TextRender()->RenderTextContainer(UIElement.Get(0)->m_UITextContainer, &ColorText, &ColorTextOutline); + + return UI()->DoButtonLogic(pID, Checked, pRect); + } + struct CListboxItem { int m_Visible; @@ -186,6 +281,9 @@ protected: char m_aMessageBody[512]; char m_aMessageButton[512]; + CUIElement m_RefreshButton; + CUIElement m_ConnectButton; + void PopupMessage(const char *pTopic, const char *pBody, const char *pButton); // TODO: this is a bit ugly but.. well.. yeah diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index d7ebf876a..5920de562 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -21,6 +21,16 @@ #include "menus.h" +static const int g_OffsetColFlagLock = 2; +static const int g_OffsetColFav = g_OffsetColFlagLock + 3; +static const int g_OffsetColOff = g_OffsetColFav + 3; +static const int g_OffsetColName = g_OffsetColOff + 3; +static const int g_OffsetColGameType = g_OffsetColName + 3; +static const int g_OffsetColMap = g_OffsetColGameType + 3; +static const int g_OffsetColPlayers = g_OffsetColMap + 3; +static const int g_OffsetColPing = g_OffsetColPlayers + 3; +static const int g_OffsetColVersion = g_OffsetColPing + 3; + void CMenus::RenderServerbrowserServerList(CUIRect View) { CUIRect Headers; @@ -261,6 +271,25 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) CUIRect Row; CUIRect SelectHitBox; + //initialize + if(pItem->m_pUIElement == NULL) + { + pItem->m_pUIElement = UI()->GetNewUIElement(); + } + + const int UIRectCount = 2 + (COL_VERSION + 1) * 3; + + if(pItem->m_pUIElement->Size() != UIRectCount) + { + UI()->ResetUIElement(*pItem->m_pUIElement); + + for(int UIElIndex = 0; UIElIndex < UIRectCount; ++UIElIndex) + { + CUIElement::SUIElementRect AddRect; + pItem->m_pUIElement->Add(AddRect); + } + } + int Selected = str_comp(pItem->m_aAddress, g_Config.m_UiServerAddress) == 0; //selected_index==ItemIndex; View.HSplitTop(ms_ListheaderHeight, &Row, &View); @@ -299,7 +328,7 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) { CUIRect r = Row; r.Margin(0.5f, &r); - RenderTools()->DrawUIRect(&r, ColorRGBA(1, 1, 1, 0.5f), CUI::CORNER_ALL, 4.0f); + RenderTools()->DrawUIElRect(*pItem->m_pUIElement->Get(0), &r, ColorRGBA(1, 1, 1, 0.5f), CUI::CORNER_ALL, 4.0f); } // clip the selection @@ -315,7 +344,7 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) { CUIRect r = Row; r.Margin(0.5f, &r); - RenderTools()->DrawUIRect(&r, ColorRGBA(1, 1, 1, 0.25f), CUI::CORNER_ALL, 4.0f); + RenderTools()->DrawUIElRect(*pItem->m_pUIElement->Get(1), &r, ColorRGBA(1, 1, 1, 0.25f), CUI::CORNER_ALL, 4.0f); } if(UI()->DoButtonLogic(pItem, "", Selected, &SelectHitBox)) @@ -364,10 +393,7 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) } else if(ID == COL_NAME) { - CTextCursor Cursor; float FontSize = 12.0f * UI()->Scale(); - TextRender()->SetCursor(&Cursor, Button.x, Button.y + (Button.h - FontSize) / 2.f, FontSize, TEXTFLAG_RENDER | TEXTFLAG_STOP_AT_END); - Cursor.m_LineWidth = Button.w; if(g_Config.m_BrFilterString[0] && (pItem->m_QuickSearchHit & IServerBrowser::QUICK_SERVERNAME)) { @@ -375,17 +401,17 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) const char *pStr = str_find_nocase(pItem->m_aName, g_Config.m_BrFilterString); if(pStr) { - TextRender()->TextEx(&Cursor, pItem->m_aName, (int)(pStr - pItem->m_aName)); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(g_OffsetColName + 0), &Button, pItem->m_aName, FontSize, -1, Button.w, 1, true, (int)(pStr - pItem->m_aName)); TextRender()->TextColor(0.4f, 0.4f, 1.0f, 1); - TextRender()->TextEx(&Cursor, pStr, str_length(g_Config.m_BrFilterString)); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(g_OffsetColName + 1), &Button, pStr, FontSize, -1, Button.w, 1, true, (int)str_length(g_Config.m_BrFilterString), &pItem->m_pUIElement->Get(g_OffsetColName + 0)->m_Cursor); TextRender()->TextColor(1, 1, 1, 1); - TextRender()->TextEx(&Cursor, pStr + str_length(g_Config.m_BrFilterString), -1); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(g_OffsetColName + 2), &Button, pStr + str_length(g_Config.m_BrFilterString), FontSize, -1, Button.w, 1, true, -1, &pItem->m_pUIElement->Get(g_OffsetColName + 1)->m_Cursor); } else - TextRender()->TextEx(&Cursor, pItem->m_aName, -1); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(g_OffsetColName), &Button, pItem->m_aName, FontSize, -1, Button.w, 1, true); } else - TextRender()->TextEx(&Cursor, pItem->m_aName, -1); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(g_OffsetColName), &Button, pItem->m_aName, FontSize, -1, Button.w, 1, true); } else if(ID == COL_MAP) { @@ -400,10 +426,7 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) DoButton_Icon(IMAGE_BROWSEICONS, SPRITE_BROWSE_RANK, &Icon); } - CTextCursor Cursor; float FontSize = 12.0f * UI()->Scale(); - TextRender()->SetCursor(&Cursor, Button.x, Button.y + (Button.h - FontSize) / 2.f, 12.0f * UI()->Scale(), TEXTFLAG_RENDER | TEXTFLAG_STOP_AT_END); - Cursor.m_LineWidth = Button.w; if(g_Config.m_BrFilterString[0] && (pItem->m_QuickSearchHit & IServerBrowser::QUICK_MAPNAME)) { @@ -411,17 +434,17 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) const char *pStr = str_find_nocase(pItem->m_aMap, g_Config.m_BrFilterString); if(pStr) { - TextRender()->TextEx(&Cursor, pItem->m_aMap, (int)(pStr - pItem->m_aMap)); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(g_OffsetColMap + 0), &Button, pItem->m_aMap, FontSize, -1, Button.w, 1, true, (int)(pStr - pItem->m_aMap)); TextRender()->TextColor(0.4f, 0.4f, 1.0f, 1); - TextRender()->TextEx(&Cursor, pStr, str_length(g_Config.m_BrFilterString)); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(g_OffsetColMap + 1), &Button, pStr, FontSize, -1, Button.w, 1, true, (int)str_length(g_Config.m_BrFilterString), &pItem->m_pUIElement->Get(g_OffsetColMap + 0)->m_Cursor); TextRender()->TextColor(1, 1, 1, 1); - TextRender()->TextEx(&Cursor, pStr + str_length(g_Config.m_BrFilterString), -1); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(g_OffsetColMap + 2), &Button, pStr + str_length(g_Config.m_BrFilterString), FontSize, -1, Button.w, 1, true, -1, &pItem->m_pUIElement->Get(g_OffsetColMap + 1)->m_Cursor); } else - TextRender()->TextEx(&Cursor, pItem->m_aMap, -1); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(g_OffsetColMap), &Button, pItem->m_aMap, FontSize, -1, Button.w, 1, true); } else - TextRender()->TextEx(&Cursor, pItem->m_aMap, -1); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(g_OffsetColMap), &Button, pItem->m_aMap, FontSize, -1, Button.w, 1, true); } else if(ID == COL_PLAYERS) { @@ -437,7 +460,8 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) str_format(aTemp, sizeof(aTemp), "%i/%i", pItem->m_NumFilteredPlayers, ServerBrowser()->Max(*pItem)); if(g_Config.m_BrFilterString[0] && (pItem->m_QuickSearchHit & IServerBrowser::QUICK_PLAYER)) TextRender()->TextColor(0.4f, 0.4f, 1.0f, 1); - UI()->DoLabelScaled(&Button, aTemp, 12.0f, 1); + float FontSize = 12.0f * UI()->Scale(); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(g_OffsetColPlayers), &Button, aTemp, FontSize, -1, Button.w, 1, true); TextRender()->TextColor(1, 1, 1, 1); } else if(ID == COL_PING) @@ -449,20 +473,19 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) TextRender()->TextColor(rgb); } - UI()->DoLabelScaled(&Button, aTemp, 12.0f, 1); + float FontSize = 12.0f * UI()->Scale(); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(g_OffsetColPing), &Button, aTemp, FontSize, -1, Button.w, 1, true); TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); } else if(ID == COL_VERSION) { const char *pVersion = pItem->m_aVersion; - UI()->DoLabelScaled(&Button, pVersion, 12.0f, 1); + float FontSize = 12.0f * UI()->Scale(); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(g_OffsetColVersion), &Button, pVersion, FontSize, -1, Button.w, 1, true); } else if(ID == COL_GAMETYPE) { - CTextCursor Cursor; float FontSize = 12.0f * UI()->Scale(); - TextRender()->SetCursor(&Cursor, Button.x, Button.y + (Button.h - FontSize) / 2.f, 12.0f * UI()->Scale(), TEXTFLAG_RENDER | TEXTFLAG_STOP_AT_END); - Cursor.m_LineWidth = Button.w; if(g_Config.m_UiColorizeGametype) { @@ -485,11 +508,11 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) ColorRGBA rgb = color_cast(hsl); TextRender()->TextColor(rgb); - TextRender()->TextEx(&Cursor, pItem->m_aGameType, -1); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(g_OffsetColGameType), &Button, pItem->m_aGameType, FontSize, -1, Button.w, 1, true); TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); } else - TextRender()->TextEx(&Cursor, pItem->m_aGameType, -1); + UI()->DoLabelStreamed(*pItem->m_pUIElement->Get(g_OffsetColGameType), &Button, pItem->m_aGameType, FontSize, -1, Button.w, 1, true); } } } @@ -620,13 +643,16 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) ButtonConnect.VSplitLeft(5.0f, NULL, &ButtonConnect); static int s_RefreshButton = 0; - char aBuf[64]; - if(ServerBrowser()->IsRefreshing()) - str_format(aBuf, sizeof(aBuf), "%s (%d%%)", Localize("Refresh"), ServerBrowser()->LoadingProgression()); - else - str_copy(aBuf, Localize("Refresh"), sizeof(aBuf)); + auto Func = [this]() mutable -> const char * { + if(ServerBrowser()->IsRefreshing()) + str_format(m_aLocalStringHelper, sizeof(m_aLocalStringHelper), "%s (%d%%)", Localize("Refresh"), ServerBrowser()->LoadingProgression()); + else + str_copy(m_aLocalStringHelper, Localize("Refresh"), sizeof(m_aLocalStringHelper)); - if(DoButton_Menu(&s_RefreshButton, aBuf, 0, &ButtonRefresh, NULL, CUI::CORNER_ALL) || Input()->KeyPress(KEY_F5) || (Input()->KeyPress(KEY_R) && (Input()->KeyIsPressed(KEY_LCTRL) || Input()->KeyIsPressed(KEY_RCTRL)))) + return m_aLocalStringHelper; + }; + + if(DoButtonMenu(m_RefreshButton, &s_RefreshButton, Func, 0, &ButtonRefresh, true, false, CUI::CORNER_ALL) || Input()->KeyPress(KEY_F5) || (Input()->KeyPress(KEY_R) && (Input()->KeyIsPressed(KEY_LCTRL) || Input()->KeyIsPressed(KEY_RCTRL)))) { if(g_Config.m_UiPage == PAGE_INTERNET) ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET); @@ -650,7 +676,10 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) } static int s_JoinButton = 0; - if(DoButton_Menu(&s_JoinButton, Localize("Connect"), 0, &ButtonConnect, NULL, CUI::CORNER_ALL, 5, 0, vec4(0.7f, 1, 0.7f, 0.1f), vec4(0.7f, 1, 0.7f, 0.2f)) || m_EnterPressed) + + if(DoButtonMenu( + m_ConnectButton, &s_JoinButton, []() -> const char * { return Localize("Connect"); }, 0, &ButtonConnect, false, false, CUI::CORNER_ALL, 5, 0, vec4(0.7f, 1, 0.7f, 0.1f), vec4(0.7f, 1, 0.7f, 0.2f)) || + m_EnterPressed) { Client()->Connect(g_Config.m_UiServerAddress); m_EnterPressed = false; diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index cd5a8e438..2d40a0621 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -1443,6 +1443,7 @@ void CMenus::RenderLanguageSelection(CUIRect MainView) { str_copy(g_Config.m_ClLanguagefile, s_Languages[s_SelectedLanguage].m_FileName, sizeof(g_Config.m_ClLanguagefile)); g_Localization.Load(s_Languages[s_SelectedLanguage].m_FileName, Storage(), Console()); + GameClient()->OnLanguageChange(); } } diff --git a/src/game/client/components/nameplates.cpp b/src/game/client/components/nameplates.cpp index f588065d1..d37fd72c0 100644 --- a/src/game/client/components/nameplates.cpp +++ b/src/game/client/components/nameplates.cpp @@ -202,8 +202,8 @@ void CNamePlates::RenderNameplatePos(vec2 Position, const CNetObj_PlayerInfo *pP } } - TextRender()->TextColor(1, 1, 1, 1); - TextRender()->TextOutlineColor(0.0f, 0.0f, 0.0f, 0.3f); + TextRender()->TextColor(TextRender()->DefaultTextColor()); + TextRender()->TextOutlineColor(TextRender()->DefaultTextOutlineColor()); TextRender()->SetRenderFlags(0); } diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 5c78d9f7e..d37cc7e5b 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -948,6 +948,7 @@ void CGameClient::OnWindowResize() for(int i = 0; i < m_All.m_Num; i++) m_All.m_paComponents[i]->OnWindowResize(); + UI()->OnWindowResize(); TextRender()->OnWindowResize(); } @@ -957,6 +958,11 @@ void CGameClient::OnWindowResizeCB(void *pUser) pClient->OnWindowResize(); } +void CGameClient::OnLanguageChange() +{ + UI()->OnLanguageChange(); +} + void CGameClient::OnRconType(bool UsernameReq) { m_pGameConsole->RequireUsername(UsernameReq); diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index a5d1d0fa9..8cb9df0e3 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -378,6 +378,8 @@ public: void OnWindowResize(); static void OnWindowResizeCB(void *pUser); + void OnLanguageChange(); + virtual const char *GetItemName(int Type); virtual const char *Version(); virtual const char *NetVersion(); diff --git a/src/game/client/render.cpp b/src/game/client/render.cpp index 48974478f..0858d74e6 100644 --- a/src/game/client/render.cpp +++ b/src/game/client/render.cpp @@ -455,6 +455,31 @@ int CRenderTools::CreateRoundRectQuadContainer(float x, float y, float w, float return ContainerIndex; } +void CRenderTools::DrawUIElRect(CUIElement::SUIElementRect &ElUIRect, const CUIRect *pRect, ColorRGBA Color, int Corners, float Rounding) +{ + bool NeedsRecreate = false; + if(ElUIRect.m_UIRectQuadContainer == -1 || ElUIRect.m_X != pRect->x || ElUIRect.m_Y != pRect->y || ElUIRect.m_Width != pRect->w || ElUIRect.m_Height != pRect->h) + { + if(ElUIRect.m_UIRectQuadContainer != -1) + Graphics()->DeleteQuadContainer(ElUIRect.m_UIRectQuadContainer); + NeedsRecreate = true; + } + if(NeedsRecreate) + { + ElUIRect.m_X = pRect->x; + ElUIRect.m_Y = pRect->y; + ElUIRect.m_Width = pRect->w; + ElUIRect.m_Height = pRect->h; + + Graphics()->SetColor(Color); + ElUIRect.m_UIRectQuadContainer = CreateRoundRectQuadContainer(pRect->x, pRect->y, pRect->w, pRect->h, Rounding, Corners); + Graphics()->SetColor(1, 1, 1, 1); + } + + Graphics()->TextureClear(); + Graphics()->RenderQuadContainer(ElUIRect.m_UIRectQuadContainer, -1); +} + void CRenderTools::DrawRoundRect(float x, float y, float w, float h, float r) { DrawRoundRectExt(x, y, w, h, r, 0xf); diff --git a/src/game/client/render.h b/src/game/client/render.h index cc419a6a6..9c78e89d1 100644 --- a/src/game/client/render.h +++ b/src/game/client/render.h @@ -84,6 +84,8 @@ public: int CreateRoundRectQuadContainer(float x, float y, float w, float h, float r, int Corners); + void DrawUIElRect(CUIElement::SUIElementRect &ElUIRect, const CUIRect *pRect, ColorRGBA Color, int Corners, float Rounding); + void DrawUIRect(const CUIRect *pRect, ColorRGBA Color, int Corners, float Rounding); void DrawUIRect4(const CUIRect *pRect, vec4 ColorTopLeft, vec4 ColorTopRight, vec4 ColorBottomLeft, vec4 ColorBottomRight, int Corners, float Rounding); diff --git a/src/game/client/ui.cpp b/src/game/client/ui.cpp index ab55d4648..316b4de88 100644 --- a/src/game/client/ui.cpp +++ b/src/game/client/ui.cpp @@ -8,6 +8,11 @@ #include #include +void CUIElement::Init(CUI *pUI) +{ + pUI->AddUIElement(this); +} + /******************************************************** UI *********************************************************/ @@ -32,6 +37,62 @@ CUI::CUI() m_Screen.h = 480.0f; } +CUI::~CUI() +{ + for(CUIElement *&pEl : m_OwnUIElements) + { + delete pEl; + } + m_OwnUIElements.clear(); +} + +CUIElement *CUI::GetNewUIElement() +{ + CUIElement *pNewEl = new CUIElement(this); + + 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; + } + + UIElement.Clear(); +} + +void CUI::OnElementsReset() +{ + for(CUIElement *pEl : m_UIElements) + { + ResetUIElement(*pEl); + } +} + +void CUI::OnWindowResize() +{ + OnElementsReset(); +} + +void CUI::OnLanguageChange() +{ + OnElementsReset(); +} + int CUI::Update(float Mx, float My, float Mwx, float Mwy, int Buttons) { m_MouseX = Mx; @@ -274,6 +335,11 @@ void CUIRect::HMargin(float Cut, CUIRect *pOtherRect) const } int CUI::DoButtonLogic(const void *pID, const char *pText, int Checked, const CUIRect *pRect) +{ + return DoButtonLogic(pID, Checked, pRect); +} + +int CUI::DoButtonLogic(const void *pID, int Checked, const CUIRect *pRect) { // logic int ReturnValue = 0; @@ -403,3 +469,94 @@ void CUI::DoLabelScaled(const CUIRect *r, const char *pText, float Size, int Ali { DoLabel(r, pText, Size * Scale(), Align, MaxWidth, AlignVertically); } + +void CUI::DoLabel(CUIElement::SUIElementRect &RectEl, const CUIRect *pRect, const char *pText, float Size, int Align, int MaxWidth, int AlignVertically, bool StopAtEnd, int StrLen, CTextCursor *pReadCursor) +{ + 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; + + RectEl.m_UITextContainer = TextRender()->CreateTextContainer(&Cursor, pText, StrLen); + RectEl.m_Cursor = Cursor; +} + +void CUI::DoLabelStreamed(CUIElement::SUIElementRect &RectEl, const CUIRect *pRect, const char *pText, float Size, int Align, int MaxWidth, int AlignVertically, bool StopAtEnd, int StrLen, CTextCursor *pReadCursor) +{ + bool NeedsRecreate = false; + if(RectEl.m_UITextContainer == -1 || RectEl.m_X != pRect->x || RectEl.m_Y != pRect->y || RectEl.m_Width != pRect->w || RectEl.m_Height != pRect->h) + { + 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; + + RectEl.m_X = pRect->x; + RectEl.m_Y = pRect->y; + RectEl.m_Width = pRect->w; + RectEl.m_Height = pRect->h; + + if(StrLen > 0) + RectEl.m_Text = std::string(pText, StrLen); + else if(StrLen < 0) + RectEl.m_Text = pText; + else + RectEl.m_Text.clear(); + + DoLabel(RectEl, pRect, pText, Size, Align, MaxWidth, AlignVertically, StopAtEnd, StrLen, pReadCursor); + } + + STextRenderColor ColorText(TextRender()->DefaultTextColor()); + STextRenderColor ColorTextOutline(TextRender()->DefaultTextOutlineColor()); + if(RectEl.m_UITextContainer != -1) + TextRender()->RenderTextContainer(RectEl.m_UITextContainer, &ColorText, &ColorTextOutline); +} diff --git a/src/game/client/ui.h b/src/game/client/ui.h index 22afe4480..f98843d78 100644 --- a/src/game/client/ui.h +++ b/src/game/client/ui.h @@ -3,6 +3,11 @@ #ifndef GAME_CLIENT_UI_H #define GAME_CLIENT_UI_H +#include +#include +#include +#include + class CUIRect { // TODO: Refactor: Redo UI scaling @@ -97,6 +102,65 @@ public: void HMargin(float Cut, CUIRect *pOtherRect) const; }; +class CUI; + +class CUIElement +{ + friend class CUI; + + CUIElement(CUI *pUI) { Init(pUI); } + +public: + struct SUIElementRect + { + public: + int m_UIRectQuadContainer; + int m_UITextContainer; + + float m_X; + float m_Y; + float m_Width; + float m_Height; + + std::string m_Text; + + CTextCursor m_Cursor; + + SUIElementRect() : + m_UIRectQuadContainer(-1), m_UITextContainer(-1), m_X(-1), m_Y(-1), m_Width(-1), m_Height(-1) + { + } + }; + +protected: + std::vector m_UIRects; + + // used for marquees or other user implemented things + int64 m_ElementTime; + +public: + CUIElement() = default; + + void Init(CUI *pUI); + + SUIElementRect *Get(size_t Index) + { + return &m_UIRects[Index]; + } + + size_t Size() + { + return m_UIRects.size(); + } + + void Clear() { m_UIRects.clear(); } + + void Add(SUIElementRect &ElRect) + { + m_UIRects.push_back(ElRect); + } +}; + class CUI { const void *m_pHotItem; @@ -112,6 +176,9 @@ class CUI class IGraphics *m_pGraphics; class ITextRender *m_pTextRender; + std::vector m_OwnUIElements; // ui elements maintained by CUI class + std::vector m_UIElements; + public: // TODO: Refactor: Fill this in void SetGraphics(class IGraphics *pGraphics, class ITextRender *pTextRender) @@ -123,6 +190,16 @@ public: class ITextRender *TextRender() { return m_pTextRender; } CUI(); + ~CUI(); + + void ResetUIElement(CUIElement &UIElement); + + CUIElement *GetNewUIElement(); + + void AddUIElement(CUIElement *pElement); + void OnElementsReset(); + void OnWindowResize(); + void OnLanguageChange(); enum { @@ -173,12 +250,16 @@ public: void SetScale(float s); float Scale(); + int DoButtonLogic(const void *pID, int Checked, const CUIRect *pRect); int DoButtonLogic(const void *pID, const char *pText /* TODO: Refactor: Remove */, int Checked, const CUIRect *pRect); int DoPickerLogic(const void *pID, const CUIRect *pRect, float *pX, float *pY); // TODO: Refactor: Remove this? void DoLabel(const CUIRect *pRect, const char *pText, float Size, int Align, int MaxWidth = -1, int AlignVertically = 1); void DoLabelScaled(const CUIRect *pRect, const char *pText, float Size, int Align, int MaxWidth = -1, int AlignVertically = 1); + + void DoLabel(CUIElement::SUIElementRect &RectEl, const CUIRect *pRect, const char *pText, float Size, int Align, int MaxWidth = -1, int AlignVertically = 1, bool StopAtEnd = false, int StrLen = -1, class CTextCursor *pReadCursor = NULL); + void DoLabelStreamed(CUIElement::SUIElementRect &RectEl, const CUIRect *pRect, const char *pText, float Size, int Align, int MaxWidth = -1, int AlignVertically = 1, bool StopAtEnd = false, int StrLen = -1, class CTextCursor *pReadCursor = NULL); }; #endif