ddnet/src/game/client/components/menus_browser.cpp

1674 lines
64 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/log.h>
#include <engine/favorites.h>
2011-03-23 12:06:35 +00:00
#include <engine/friends.h>
#include <engine/keys.h>
2010-05-29 07:25:38 +00:00
#include <engine/serverbrowser.h>
#include <engine/shared/config.h>
2022-12-09 15:15:19 +00:00
#include <engine/shared/localization.h>
2010-05-29 07:25:38 +00:00
#include <engine/textrender.h>
2023-01-07 08:20:25 +00:00
#include <game/client/animstate.h>
#include <game/client/components/countryflags.h>
#include <game/client/gameclient.h>
#include <game/client/render.h>
#include <game/client/ui.h>
#include <game/client/ui_listbox.h>
#include <game/localization.h>
#include "menus.h"
using namespace FontIcons;
static const ColorRGBA gs_HighlightedTextColor = ColorRGBA(0.4f, 0.4f, 1.0f, 1.0f);
template<size_t N>
static void FormatServerbrowserPing(char (&aBuffer)[N], const CServerInfo *pInfo)
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
{
if(!pInfo->m_LatencyIsEstimated)
{
str_from_int(pInfo->m_Latency, aBuffer);
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
return;
}
static const char *LOCATION_NAMES[CServerInfo::NUM_LOCS] = {
"", // LOC_UNKNOWN
2022-12-09 15:15:19 +00:00
Localizable("AFR"), // LOC_AFRICA
Localizable("ASI"), // LOC_ASIA
Localizable("AUS"), // LOC_AUSTRALIA
Localizable("EUR"), // LOC_EUROPE
Localizable("NA"), // LOC_NORTH_AMERICA
Localizable("SA"), // LOC_SOUTH_AMERICA
Localizable("CHN"), // LOC_CHINA
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
};
dbg_assert(0 <= pInfo->m_Location && pInfo->m_Location < CServerInfo::NUM_LOCS, "location out of range");
str_copy(aBuffer, Localize(LOCATION_NAMES[pInfo->m_Location]));
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
}
static ColorRGBA GetPingTextColor(int Latency)
2010-05-29 07:25:38 +00:00
{
return color_cast<ColorRGBA>(ColorHSLA((300.0f - clamp(Latency, 0, 300)) / 1000.0f, 1.0f, 0.5f));
}
static ColorRGBA GetGametypeTextColor(const char *pGametype)
{
ColorHSLA HslaColor;
if(str_comp(pGametype, "DM") == 0 || str_comp(pGametype, "TDM") == 0 || str_comp(pGametype, "CTF") == 0)
HslaColor = ColorHSLA(0.33f, 1.0f, 0.75f);
else if(str_find_nocase(pGametype, "catch"))
HslaColor = ColorHSLA(0.17f, 1.0f, 0.75f);
else if(str_find_nocase(pGametype, "idm") || str_find_nocase(pGametype, "itdm") || str_find_nocase(pGametype, "ictf") || str_find_nocase(pGametype, "f-ddrace"))
HslaColor = ColorHSLA(0.0f, 1.0f, 0.75f);
else if(str_find_nocase(pGametype, "fng"))
HslaColor = ColorHSLA(0.83f, 1.0f, 0.75f);
else if(str_find_nocase(pGametype, "gores"))
HslaColor = ColorHSLA(0.525f, 1.0f, 0.75f);
else if(str_find_nocase(pGametype, "BW"))
HslaColor = ColorHSLA(0.05f, 1.0f, 0.75f);
else if(str_find_nocase(pGametype, "ddracenet") || str_find_nocase(pGametype, "ddnet") || str_find_nocase(pGametype, "0xf"))
HslaColor = ColorHSLA(0.58f, 1.0f, 0.75f);
else if(str_find_nocase(pGametype, "ddrace") || str_find_nocase(pGametype, "mkrace"))
HslaColor = ColorHSLA(0.75f, 1.0f, 0.75f);
else if(str_find_nocase(pGametype, "race") || str_find_nocase(pGametype, "fastcap"))
HslaColor = ColorHSLA(0.46f, 1.0f, 0.75f);
else if(str_find_nocase(pGametype, "s-ddr"))
HslaColor = ColorHSLA(1.0f, 1.0f, 0.7f);
else
HslaColor = ColorHSLA(1.0f, 1.0f, 1.0f);
return color_cast<ColorRGBA>(HslaColor);
}
void CMenus::RenderServerbrowserServerList(CUIRect View, bool &WasListboxItemActivated)
{
static CListBox s_ListBox;
CUIRect Headers;
View.HSplitTop(ms_ListheaderHeight, &Headers, &View);
Headers.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f), IGraphics::CORNER_T, 5.0f);
Headers.VSplitRight(s_ListBox.ScrollbarWidthMax(), &Headers, nullptr);
View.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.15f), IGraphics::CORNER_NONE, 0.0f);
2010-05-29 07:25:38 +00:00
struct SColumn
{
int m_ID;
2010-05-29 07:25:38 +00:00
int m_Sort;
const char *m_pCaption;
2010-05-29 07:25:38 +00:00
int m_Direction;
float m_Width;
CUIRect m_Rect;
};
2010-05-29 07:25:38 +00:00
enum
{
COL_FLAG_LOCK = 0,
COL_FLAG_FAV,
COL_FLAG_OFFICIAL,
COL_NAME,
COL_GAMETYPE,
COL_MAP,
COL_PLAYERS,
COL_PING,
UI_ELEM_LOCK_ICON = 0,
UI_ELEM_FAVORITE_ICON,
UI_ELEM_OFFICIAL_ICON_1,
UI_ELEM_OFFICIAL_ICON_2,
UI_ELEM_NAME_1,
UI_ELEM_NAME_2,
UI_ELEM_NAME_3,
UI_ELEM_GAMETYPE,
UI_ELEM_MAP_1,
UI_ELEM_MAP_2,
UI_ELEM_MAP_3,
UI_ELEM_FINISH_ICON,
UI_ELEM_PLAYERS,
UI_ELEM_FRIEND_ICON,
UI_ELEM_PING,
NUM_UI_ELEMS,
};
2010-05-29 07:25:38 +00:00
static SColumn s_aCols[] = {
{-1, -1, "", -1, 2.0f, {0}},
{COL_FLAG_LOCK, -1, "", -1, 14.0f, {0}},
{COL_FLAG_FAV, -1, "", -1, 14.0f, {0}},
{COL_FLAG_OFFICIAL, -1, "", -1, 14.0f, {0}},
{COL_NAME, IServerBrowser::SORT_NAME, Localizable("Name"), 0, 50.0f, {0}},
{COL_GAMETYPE, IServerBrowser::SORT_GAMETYPE, Localizable("Type"), 1, 50.0f, {0}},
{COL_MAP, IServerBrowser::SORT_MAP, Localizable("Map"), 1, 120.0f + (Headers.w - 480) / 8, {0}},
{COL_PLAYERS, IServerBrowser::SORT_NUMPLAYERS, Localizable("Players"), 1, 85.0f, {0}},
{-1, -1, "", 1, 10.0f, {0}},
{COL_PING, IServerBrowser::SORT_PING, Localizable("Ping"), 1, 40.0f, {0}},
};
2010-05-29 07:25:38 +00:00
const int NumCols = std::size(s_aCols);
2010-05-29 07:25:38 +00:00
// do layout
2010-05-29 07:25:38 +00:00
for(int i = 0; i < NumCols; i++)
{
2010-05-29 07:25:38 +00:00
if(s_aCols[i].m_Direction == -1)
{
2010-05-29 07:25:38 +00:00
Headers.VSplitLeft(s_aCols[i].m_Width, &s_aCols[i].m_Rect, &Headers);
if(i + 1 < NumCols)
{
Headers.VSplitLeft(2.0f, nullptr, &Headers);
}
}
}
2010-05-29 07:25:38 +00:00
for(int i = NumCols - 1; i >= 0; i--)
{
2010-05-29 07:25:38 +00:00
if(s_aCols[i].m_Direction == 1)
{
2010-05-29 07:25:38 +00:00
Headers.VSplitRight(s_aCols[i].m_Width, &Headers, &s_aCols[i].m_Rect);
Headers.VSplitRight(2.0f, &Headers, nullptr);
}
}
2010-05-29 07:25:38 +00:00
for(auto &Col : s_aCols)
{
if(Col.m_Direction == 0)
Col.m_Rect = Headers;
}
2010-05-29 07:25:38 +00:00
const bool PlayersOrPing = (g_Config.m_BrSort == IServerBrowser::SORT_NUMPLAYERS || g_Config.m_BrSort == IServerBrowser::SORT_PING);
// do headers
for(const auto &Col : s_aCols)
{
int Checked = g_Config.m_BrSort == Col.m_Sort;
if(PlayersOrPing && g_Config.m_BrSortOrder == 2 && (Col.m_Sort == IServerBrowser::SORT_NUMPLAYERS || Col.m_Sort == IServerBrowser::SORT_PING))
Checked = 2;
if(DoButton_GridHeader(&Col.m_ID, Localize(Col.m_pCaption), Checked, &Col.m_Rect))
{
if(Col.m_Sort != -1)
{
if(g_Config.m_BrSort == Col.m_Sort)
g_Config.m_BrSortOrder = (g_Config.m_BrSortOrder + 1) % (PlayersOrPing ? 3 : 2);
else
2010-05-29 07:25:38 +00:00
g_Config.m_BrSortOrder = 0;
g_Config.m_BrSort = Col.m_Sort;
}
}
}
2010-05-29 07:25:38 +00:00
const int NumServers = ServerBrowser()->NumSortedServers();
2010-05-29 07:25:38 +00:00
// display important messages in the middle of the screen so no
// users misses it
{
2021-07-08 17:18:01 +00:00
if(!ServerBrowser()->NumServers() && ServerBrowser()->IsGettingServerlist())
UI()->DoLabel(&View, Localize("Getting server list from master server"), 16.0f, TEXTALIGN_MC);
2010-05-29 07:25:38 +00:00
else if(!ServerBrowser()->NumServers())
UI()->DoLabel(&View, Localize("No servers found"), 16.0f, TEXTALIGN_MC);
2010-05-29 07:25:38 +00:00
else if(ServerBrowser()->NumServers() && !NumServers)
UI()->DoLabel(&View, Localize("No servers match your filter criteria"), 16.0f, TEXTALIGN_MC);
}
2008-09-07 21:13:24 +00:00
s_ListBox.SetActive(!UI()->IsPopupOpen());
s_ListBox.DoStart(ms_ListheaderHeight, NumServers, 1, 3, -1, &View, false);
2010-05-29 07:25:38 +00:00
if(m_ServerBrowserShouldRevealSelection)
{
s_ListBox.ScrollToSelected();
m_ServerBrowserShouldRevealSelection = false;
}
2010-05-29 07:25:38 +00:00
m_SelectedIndex = -1;
const auto &&RenderBrowserIcons = [this](CUIElement::SUIElementRect &UIRect, CUIRect *pRect, const ColorRGBA &TextColor, const ColorRGBA &TextOutlineColor, const char *pText, int TextAlign, bool SmallFont = false) {
const float FontSize = SmallFont ? 6.0f : 14.0f;
Add font index, support font family variants depending on language Add `fonts/index.json` which specifies: - List of all font files that should be loaded (filenames). - Default font (specified by family name or by family and style name). - Font variants for different languages, using the name of the language file as key. - Fallback fonts. - Icon font. There are characters (e.g. all in `刃直海角骨入`) that look different depending on the language of the content being Japanese, Simplified Chinese, Traditional Chinese and Hangul, because Unicode uses the same codepoint for characters regardless of the language. To render these characters correctly, the active variant font is switched depending on the selected language. The `ITextRender` interface is changed so the current language variant can be set using `SetFontLanguageVariant` and the default and icon fonts can be toggled using `SetFontPreset`. The class `CFont` is removed entirely. The text render is restructured: The font faces and font atlas are now managed by a separate class `CGlyphMap` like on upstream. As the text fill and outline textures always have the same size, the texture skyline only needs to be stored once and free positions in the atlas only need to be calculated once for each glyph instead of separately for the fill and outline textures. The font files and their licenses are also updated: - Update Source Han Sans to version 2.001. - Update Glow Sans Japanese Compressed to version 0.93. - Update Deja Vu Sans to version 2.37. - Update Font Awesome icons font to March 2023 version. Closes #6881.
2023-07-17 13:49:41 +00:00
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
2022-03-19 10:25:54 +00:00
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
TextRender()->TextColor(TextColor);
TextRender()->TextOutlineColor(TextOutlineColor);
UI()->DoLabelStreamed(UIRect, pRect, pText, FontSize, TextAlign);
2022-03-19 10:25:54 +00:00
TextRender()->TextOutlineColor(TextRender()->DefaultTextOutlineColor());
TextRender()->TextColor(TextRender()->DefaultTextColor());
TextRender()->SetRenderFlags(0);
Add font index, support font family variants depending on language Add `fonts/index.json` which specifies: - List of all font files that should be loaded (filenames). - Default font (specified by family name or by family and style name). - Font variants for different languages, using the name of the language file as key. - Fallback fonts. - Icon font. There are characters (e.g. all in `刃直海角骨入`) that look different depending on the language of the content being Japanese, Simplified Chinese, Traditional Chinese and Hangul, because Unicode uses the same codepoint for characters regardless of the language. To render these characters correctly, the active variant font is switched depending on the selected language. The `ITextRender` interface is changed so the current language variant can be set using `SetFontLanguageVariant` and the default and icon fonts can be toggled using `SetFontPreset`. The class `CFont` is removed entirely. The text render is restructured: The font faces and font atlas are now managed by a separate class `CGlyphMap` like on upstream. As the text fill and outline textures always have the same size, the texture skyline only needs to be stored once and free positions in the atlas only need to be calculated once for each glyph instead of separately for the fill and outline textures. The font files and their licenses are also updated: - Update Source Han Sans to version 2.001. - Update Glow Sans Japanese Compressed to version 0.93. - Update Deja Vu Sans to version 2.37. - Update Font Awesome icons font to March 2023 version. Closes #6881.
2023-07-17 13:49:41 +00:00
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
2022-03-19 10:25:54 +00:00
};
for(int i = 0; i < NumServers; i++)
{
const CServerInfo *pItem = ServerBrowser()->SortedGet(i);
2010-05-29 07:25:38 +00:00
if(pItem->m_pUIElement == nullptr)
2020-10-12 10:29:47 +00:00
{
pItem->m_pUIElement = UI()->GetNewUIElement(NUM_UI_ELEMS);
2020-10-12 10:29:47 +00:00
}
const CListboxItem ListItem = s_ListBox.DoNextItem(pItem, str_comp(pItem->m_aAddress, g_Config.m_UiServerAddress) == 0);
if(ListItem.m_Selected)
2010-05-29 07:25:38 +00:00
m_SelectedIndex = i;
2011-06-26 15:10:13 +00:00
// update friend counter
int FriendsOnServer = 0;
2011-06-26 15:10:13 +00:00
if(pItem->m_FriendState != IFriends::FRIEND_NO)
{
for(int j = 0; j < pItem->m_NumReceivedClients; ++j)
2011-06-26 15:10:13 +00:00
{
if(pItem->m_aClients[j].m_FriendState != IFriends::FRIEND_NO)
{
FriendsOnServer++;
2011-06-26 15:10:13 +00:00
}
}
}
if(!ListItem.m_Visible)
2010-05-29 07:25:38 +00:00
{
// reset active item, if not visible
if(UI()->CheckActiveItem(pItem))
UI()->SetActiveItem(nullptr);
2010-05-29 07:25:38 +00:00
// don't render invisible items
continue;
}
const float FontSize = 12.0f;
char aTemp[64];
for(const auto &Col : s_aCols)
{
2010-05-29 07:25:38 +00:00
CUIRect Button;
Button.x = Col.m_Rect.x;
Button.y = ListItem.m_Rect.y;
Button.h = ListItem.m_Rect.h;
Button.w = Col.m_Rect.w;
2010-05-29 07:25:38 +00:00
const int ID = Col.m_ID;
if(ID == COL_FLAG_LOCK)
{
2010-05-29 07:25:38 +00:00
if(pItem->m_Flags & SERVER_FLAG_PASSWORD)
2022-03-19 10:25:54 +00:00
{
RenderBrowserIcons(*pItem->m_pUIElement->Rect(UI_ELEM_LOCK_ICON), &Button, ColorRGBA(0.75f, 0.75f, 0.75f, 1.0f), TextRender()->DefaultTextOutlineColor(), FONT_ICON_LOCK, TEXTALIGN_MC);
2022-03-19 10:25:54 +00:00
}
}
else if(ID == COL_FLAG_FAV)
{
if(pItem->m_Favorite != TRISTATE::NONE)
2022-03-19 10:25:54 +00:00
{
RenderBrowserIcons(*pItem->m_pUIElement->Rect(UI_ELEM_FAVORITE_ICON), &Button, ColorRGBA(0.94f, 0.4f, 0.4f, 1.0f), TextRender()->DefaultTextOutlineColor(), FONT_ICON_HEART, TEXTALIGN_MC);
2022-03-19 10:25:54 +00:00
}
}
else if(ID == COL_FLAG_OFFICIAL)
{
if(pItem->m_Official && g_Config.m_UiPage != PAGE_DDNET && g_Config.m_UiPage != PAGE_KOG)
2022-03-19 10:25:54 +00:00
{
RenderBrowserIcons(*pItem->m_pUIElement->Rect(UI_ELEM_OFFICIAL_ICON_1), &Button, ColorRGBA(0.4f, 0.7f, 0.94f, 1.0f), ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f), FONT_ICON_CERTIFICATE, TEXTALIGN_MC);
RenderBrowserIcons(*pItem->m_pUIElement->Rect(UI_ELEM_OFFICIAL_ICON_2), &Button, ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f), ColorRGBA(0.0f, 0.0f, 0.0f, 0.0f), FONT_ICON_CHECK, TEXTALIGN_MC, true);
2022-03-19 10:25:54 +00:00
}
}
else if(ID == COL_NAME)
{
SLabelProperties Props;
Props.m_MaxWidth = Button.w;
Props.m_StopAtEnd = true;
Props.m_EnableWidthCheck = false;
bool Printed = false;
if(g_Config.m_BrFilterString[0] && (pItem->m_QuickSearchHit & IServerBrowser::QUICK_SERVERNAME))
Printed = PrintHighlighted(pItem->m_aName, [&](const char *pFilteredStr, const int FilterLen) {
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(UI_ELEM_NAME_1), &Button, pItem->m_aName, FontSize, TEXTALIGN_ML, Props, (int)(pFilteredStr - pItem->m_aName));
TextRender()->TextColor(gs_HighlightedTextColor);
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(UI_ELEM_NAME_2), &Button, pFilteredStr, FontSize, TEXTALIGN_ML, Props, FilterLen, &pItem->m_pUIElement->Rect(UI_ELEM_NAME_1)->m_Cursor);
TextRender()->TextColor(TextRender()->DefaultTextColor());
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(UI_ELEM_NAME_3), &Button, pFilteredStr + FilterLen, FontSize, TEXTALIGN_ML, Props, -1, &pItem->m_pUIElement->Rect(UI_ELEM_NAME_2)->m_Cursor);
});
if(!Printed)
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(UI_ELEM_NAME_1), &Button, pItem->m_aName, FontSize, TEXTALIGN_ML, Props);
}
else if(ID == COL_GAMETYPE)
{
SLabelProperties Props;
Props.m_MaxWidth = Button.w;
Props.m_StopAtEnd = true;
Props.m_EnableWidthCheck = false;
if(g_Config.m_UiColorizeGametype)
{
TextRender()->TextColor(GetGametypeTextColor(pItem->m_aGameType));
}
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(UI_ELEM_GAMETYPE), &Button, pItem->m_aGameType, FontSize, TEXTALIGN_ML, Props);
TextRender()->TextColor(TextRender()->DefaultTextColor());
}
else if(ID == COL_MAP)
{
if(g_Config.m_UiPage == PAGE_DDNET)
{
CUIRect Icon;
Button.VMargin(4.0f, &Button);
Button.VSplitLeft(Button.h, &Icon, &Button);
if(g_Config.m_BrIndicateFinished && pItem->m_HasRank == 1)
2022-03-19 10:25:54 +00:00
{
Icon.Margin(2.0f, &Icon);
RenderBrowserIcons(*pItem->m_pUIElement->Rect(UI_ELEM_FINISH_ICON), &Icon, TextRender()->DefaultTextColor(), TextRender()->DefaultTextOutlineColor(), FONT_ICON_FLAG_CHECKERED, TEXTALIGN_MC);
2022-03-19 10:25:54 +00:00
}
}
SLabelProperties Props;
Props.m_MaxWidth = Button.w;
Props.m_StopAtEnd = true;
Props.m_EnableWidthCheck = false;
bool Printed = false;
if(g_Config.m_BrFilterString[0] && (pItem->m_QuickSearchHit & IServerBrowser::QUICK_MAPNAME))
Printed = PrintHighlighted(pItem->m_aMap, [&](const char *pFilteredStr, const int FilterLen) {
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(UI_ELEM_MAP_1), &Button, pItem->m_aMap, FontSize, TEXTALIGN_ML, Props, (int)(pFilteredStr - pItem->m_aMap));
TextRender()->TextColor(gs_HighlightedTextColor);
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(UI_ELEM_MAP_2), &Button, pFilteredStr, FontSize, TEXTALIGN_ML, Props, FilterLen, &pItem->m_pUIElement->Rect(UI_ELEM_MAP_1)->m_Cursor);
TextRender()->TextColor(TextRender()->DefaultTextColor());
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(UI_ELEM_MAP_3), &Button, pFilteredStr + FilterLen, FontSize, TEXTALIGN_ML, Props, -1, &pItem->m_pUIElement->Rect(UI_ELEM_MAP_2)->m_Cursor);
});
if(!Printed)
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(UI_ELEM_MAP_1), &Button, pItem->m_aMap, FontSize, TEXTALIGN_ML, Props);
}
else if(ID == COL_PLAYERS)
{
Button.VMargin(2.0f, &Button);
2011-06-26 15:10:13 +00:00
if(pItem->m_FriendState != IFriends::FRIEND_NO)
{
CUIRect Icon;
Button.VSplitRight(50.0f, &Icon, &Button);
2011-06-26 15:10:13 +00:00
Icon.Margin(2.0f, &Icon);
RenderBrowserIcons(*pItem->m_pUIElement->Rect(UI_ELEM_FRIEND_ICON), &Icon, ColorRGBA(0.94f, 0.4f, 0.4f, 1.0f), TextRender()->DefaultTextOutlineColor(), FONT_ICON_HEART, TEXTALIGN_MC);
if(FriendsOnServer > 1)
{
str_from_int(FriendsOnServer, aTemp);
TextRender()->TextColor(0.94f, 0.8f, 0.8f, 1.0f);
UI()->DoLabel(&Icon, aTemp, 9.0f, TEXTALIGN_MC);
TextRender()->TextColor(TextRender()->DefaultTextColor());
}
2011-06-26 15:10:13 +00:00
}
2018-10-29 21:03:57 +00:00
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(gs_HighlightedTextColor);
}
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(UI_ELEM_PLAYERS), &Button, aTemp, FontSize, TEXTALIGN_MR);
TextRender()->TextColor(TextRender()->DefaultTextColor());
}
else if(ID == COL_PING)
{
Button.VMargin(4.0f, &Button);
FormatServerbrowserPing(aTemp, pItem);
if(g_Config.m_UiColorizePing)
{
TextRender()->TextColor(GetPingTextColor(pItem->m_Latency));
}
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(UI_ELEM_PING), &Button, aTemp, FontSize, TEXTALIGN_MR);
TextRender()->TextColor(TextRender()->DefaultTextColor());
}
}
}
const int NewSelected = s_ListBox.DoEnd();
if(NewSelected != m_SelectedIndex)
{
m_SelectedIndex = NewSelected;
if(m_SelectedIndex >= 0)
{
// select the new server
const CServerInfo *pItem = ServerBrowser()->SortedGet(NewSelected);
if(pItem)
{
str_copy(g_Config.m_UiServerAddress, pItem->m_aAddress);
m_ServerBrowserShouldRevealSelection = true;
}
}
}
WasListboxItemActivated = s_ListBox.WasItemActivated();
}
void CMenus::RenderServerbrowserStatusBox(CUIRect StatusBox, bool WasListboxItemActivated)
{
// Render bar that shows the loading progression.
// The bar is only shown while loading and fades out when it's done.
CUIRect RefreshBar;
StatusBox.HSplitTop(5.0f, &RefreshBar, &StatusBox);
static float s_LoadingProgressionFadeEnd = 0.0f;
if(ServerBrowser()->IsRefreshing() && ServerBrowser()->LoadingProgression() < 100)
{
s_LoadingProgressionFadeEnd = Client()->GlobalTime() + 2.0f;
}
const float LoadingProgressionTimeDiff = s_LoadingProgressionFadeEnd - Client()->GlobalTime();
if(LoadingProgressionTimeDiff > 0.0f)
{
const float RefreshBarAlpha = minimum(LoadingProgressionTimeDiff, 0.8f);
RefreshBar.h = 2.0f;
RefreshBar.w *= ServerBrowser()->LoadingProgression() / 100.0f;
RefreshBar.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, RefreshBarAlpha), IGraphics::CORNER_NONE, 0.0f);
}
2010-05-29 07:25:38 +00:00
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
const float SearchExcludeAddrStrMax = 130.0f;
const float SearchIconWidth = TextRender()->TextWidth(16.0f, FONT_ICON_MAGNIFYING_GLASS);
const float ExcludeIconWidth = TextRender()->TextWidth(16.0f, FONT_ICON_BAN);
const float ExcludeSearchIconMax = maximum(SearchIconWidth, ExcludeIconWidth);
TextRender()->SetRenderFlags(0);
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
CUIRect SearchInfoAndAddr, ServersAndConnect, ServersPlayersOnline, SearchAndInfo, ServerAddr, ConnectButtons;
StatusBox.VSplitRight(135.0f, &SearchInfoAndAddr, &ServersAndConnect);
if(SearchInfoAndAddr.w > 350.0f)
SearchInfoAndAddr.VSplitLeft(350.0f, &SearchInfoAndAddr, nullptr);
SearchInfoAndAddr.HSplitTop(40.0f, &SearchAndInfo, &ServerAddr);
ServersAndConnect.HSplitTop(35.0f, &ServersPlayersOnline, &ConnectButtons);
ConnectButtons.HSplitTop(5.0f, nullptr, &ConnectButtons);
CUIRect QuickSearch, QuickExclude;
SearchAndInfo.HSplitTop(20.0f, &QuickSearch, &QuickExclude);
QuickSearch.Margin(2.0f, &QuickSearch);
QuickExclude.Margin(2.0f, &QuickExclude);
2009-01-21 18:09:06 +00:00
// render quick search
{
Add font index, support font family variants depending on language Add `fonts/index.json` which specifies: - List of all font files that should be loaded (filenames). - Default font (specified by family name or by family and style name). - Font variants for different languages, using the name of the language file as key. - Fallback fonts. - Icon font. There are characters (e.g. all in `刃直海角骨入`) that look different depending on the language of the content being Japanese, Simplified Chinese, Traditional Chinese and Hangul, because Unicode uses the same codepoint for characters regardless of the language. To render these characters correctly, the active variant font is switched depending on the selected language. The `ITextRender` interface is changed so the current language variant can be set using `SetFontLanguageVariant` and the default and icon fonts can be toggled using `SetFontPreset`. The class `CFont` is removed entirely. The text render is restructured: The font faces and font atlas are now managed by a separate class `CGlyphMap` like on upstream. As the text fill and outline textures always have the same size, the texture skyline only needs to be stored once and free positions in the atlas only need to be calculated once for each glyph instead of separately for the fill and outline textures. The font files and their licenses are also updated: - Update Source Han Sans to version 2.001. - Update Glow Sans Japanese Compressed to version 0.93. - Update Deja Vu Sans to version 2.37. - Update Font Awesome icons font to March 2023 version. Closes #6881.
2023-07-17 13:49:41 +00:00
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
2020-10-06 10:25:10 +00:00
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
UI()->DoLabel(&QuickSearch, FONT_ICON_MAGNIFYING_GLASS, 16.0f, TEXTALIGN_ML);
TextRender()->SetRenderFlags(0);
Add font index, support font family variants depending on language Add `fonts/index.json` which specifies: - List of all font files that should be loaded (filenames). - Default font (specified by family name or by family and style name). - Font variants for different languages, using the name of the language file as key. - Fallback fonts. - Icon font. There are characters (e.g. all in `刃直海角骨入`) that look different depending on the language of the content being Japanese, Simplified Chinese, Traditional Chinese and Hangul, because Unicode uses the same codepoint for characters regardless of the language. To render these characters correctly, the active variant font is switched depending on the selected language. The `ITextRender` interface is changed so the current language variant can be set using `SetFontLanguageVariant` and the default and icon fonts can be toggled using `SetFontPreset`. The class `CFont` is removed entirely. The text render is restructured: The font faces and font atlas are now managed by a separate class `CGlyphMap` like on upstream. As the text fill and outline textures always have the same size, the texture skyline only needs to be stored once and free positions in the atlas only need to be calculated once for each glyph instead of separately for the fill and outline textures. The font files and their licenses are also updated: - Update Source Han Sans to version 2.001. - Update Glow Sans Japanese Compressed to version 0.93. - Update Deja Vu Sans to version 2.37. - Update Font Awesome icons font to March 2023 version. Closes #6881.
2023-07-17 13:49:41 +00:00
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
QuickSearch.VSplitLeft(ExcludeSearchIconMax, nullptr, &QuickSearch);
QuickSearch.VSplitLeft(5.0f, nullptr, &QuickSearch);
char aBufSearch[64];
str_format(aBufSearch, sizeof(aBufSearch), "%s:", Localize("Search"));
UI()->DoLabel(&QuickSearch, aBufSearch, 14.0f, TEXTALIGN_ML);
QuickSearch.VSplitLeft(SearchExcludeAddrStrMax, nullptr, &QuickSearch);
QuickSearch.VSplitLeft(5.0f, nullptr, &QuickSearch);
Port line input and IME support from 0.7 Port the line input (UI edit boxes, chat, console) and Input Method Editor (IME) support from upstream. Closes #4397. General ------------------------------ Fix issues with the text input. Closes #4346. Closes #4524. Word skipping (when holding Ctrl) is overhauled to be consistent with the Windows / Firefox experience that I took as reference. Improve usability by not blinking (i.e. always rendering) the caret shortly after is has been moved. UI text input ------------------------------ Fix inconsistent mouse-based left and right scrolling (closes #4347). Support smooth left and right scrolling. Chat ------------------------------ Support keyboard-based text selection of the chat input. Mouse-based selection could be support in the future when we decide to add something like an ingame UI cursor. Support smooth up and down scrolling of the chat input, removing the old hack that offsets the input string to simulate scrolling. Console ------------------------------ Also support mouse-based text selection of the command input. Only text from either the command input or the console log can be selected at the same time. This ensures that Ctrl+C will always copy the text that is currently visually selected in the console. Check for Ctrl+C input event in event handler instead of in render function, to hopefully fix the issue that copying does not work sometimes (closes #5974 until further notice). When Ctrl+C is used to copy text from the console log, the selection is cleared. This should make it more clear when text was copied from the log. Fix an issue that was preventing the console log selection from being cleared, when all log lines are selected. Remove Ctrl+A/E hotkeys that move cursor to beginning/end respectively. Ctrl+A now selectes all text like for all other inputs. Home and End keys can still be used to go the beginning and end. Remove Ctrl+U/K hotkeys that clear everything before/after the cursor respectively. Hold shift and use Home/End to select everything instead. IME support ------------------------------ Render list of IME candidates in the client on Windows, so the candidate list can also be viewed in fullscreen mode. There is no API available to retrieve a candidate list on the other operating systems. Improve composition rendering by underlining the composition text instead of putting it in square brackets. Track active input globally to properly activate and deactivate IME through the SDL functions. Closes #1030. Closes #1008. Password rendering ------------------------------ Fix rendering of passwords containing unicode. Instead of rendering one star character for each UTF-8 `char`, render on star for every unicode codepoint. Show the composition text also for passwords. Without seeing the composition text it's hard to type a password containing those characters. The candidate window exposes the composition anyway. If you don't want to expose your password this way, e.g. while streaming, you could: 1. Use a latin password and switch off the IME for the password input with the IME hotkey. 2. Blank your screen with an external program while you are streaming and entering passwords. 3. Use binds to authenticate in rcon or to set the server browser password. Refactoring ------------------------------ Move all text input logic and general rendering to `CLineInput`. A `CLineInput` is associated with a particular `char` buffer given as a pointer either in the constructor or with `SetBuffer`. The maximum byte size of the buffer must also be specified. The maximum length in unicode codepoints can also be specified separately (e.g. on upstream, name are limited by the number of unicode codepoints instead). Add `CLineInputBuffered`, which is a `CLineInput` that own a `char` buffer of a fixed size, which is specified as a template argument. As `CLineInput` does not own a buffer anymore, this reduces duplicate code for line inputs that need their own buffer. Add `CLineInputNumber` which has additional convenience functions to consider the text as an `int` or `float`, to reduce duplicate code in those cases. In the future we could also add an input filter function so that only numbers can be entered in the number input. Add `CLineInput::SetClipboardLineCallback` to handle the case that multiple lines of text are pasted into a lineinput. This reduces duplicate code, as this behavior was previously implemented separately for chat and console. The behavior is also fixed to be consistent with the console on Windows, so the first line being pasted edits the current input text and then sends it instead of being sent on its own without the existing input text. Add `CalcFontSizeAndBoundingBox` to UI to reduce duplicate code. Expose `CalcAlignedCursorPos` as static member function to reuse it for line input. Dispatch input events to UI inputs through the event handler instead of storing them in a duplicate buffer. Use `size_t` for line input cursor position, length etc. and for `str_utf8_stats`. Add `IButtonColorFunction` to UI to describe a functions that defines colors for the Default, Active and Hovered states of UI elements. Add some default button color functions. Use button color function to reduce duplicate code in scrollbar rendering. Use `vec2` instead of two `floats` to represent the mouse positions in the text renderer. Remove `CaretPosition` again, as it does not calculate the correct Y position near line breaks due to the wrapping being different when not rendering the entire string. Instead, calculate the exact caret position when rending a text container and store the caret position in the text cursor for later use. IME usage guide (Windows) ------------------------------ 1. Install the respective language and the Microsoft-IME keyboard (e.g. for Chinese, Japanese or Korean). 2. Launch the game (or a text editor to first try out the IME). Note that Windows may track the input language separately for every application. You can change this in the Windows input settings so the input language is changed globally. 2. Switch the input language using the hotkey Windows+Space or another hotkey that you configured in the Windows input settings (Alt+Shift is the default, but you should consider disabling it, to avoid accidentally changing the input language while playing). 3. Switch from Latin/English input mode to the respective asian input mode. - Chinese: Use Ctrl+Space to switch between English and Chinese input mode. You can change this hotkey in the IME's settings. - Japanese: Use Ctrl+Space to switch between Alphanumeric and Hiragana/Katakana input mode. You can change this hotkey in the IME's settings. - Korean: Use Right Alt to switch between English and Hangul input mode. You cannot change this hotkey as of yet. - Note that the input mode is also tracked per application, but there is no setting to change this behavior as far as I know, so you'll need to switch for every application separately. 4. Start typing. The underlined text is the current composition text. While a composition is active, you can only edit the composition text. Confirm the composition with Space or by selecting a candidate from the candidate list with the arrow keys. Cancel the composition with Escape or by using Backspace to delete the composition text. Note that not all languages offer a candidate list. SDL version-specific issues ------------------------------ - 2.26.5, 2.24.2, 2.0.22: IME candidates work. But there are minor bugs when moving the composition cursor. - 2.0.18, 2.0.20: IME candidates work. - 2.0.16 (our current version): IME candidates cannot be determined with Windows API. Windows tries to draw the composition window like before, so this does not work in fullscreen mode. - 2.0.8 (upstream 0.7): IME candidates work. But this SDL version is too old for us.
2023-01-03 21:28:38 +00:00
static CLineInput s_FilterInput(g_Config.m_BrFilterString, sizeof(g_Config.m_BrFilterString));
if(!UI()->IsPopupOpen() && Input()->KeyPress(KEY_F) && Input()->ModifierIsPressed())
2022-03-13 18:09:33 +00:00
{
UI()->SetActiveItem(&s_FilterInput);
Port line input and IME support from 0.7 Port the line input (UI edit boxes, chat, console) and Input Method Editor (IME) support from upstream. Closes #4397. General ------------------------------ Fix issues with the text input. Closes #4346. Closes #4524. Word skipping (when holding Ctrl) is overhauled to be consistent with the Windows / Firefox experience that I took as reference. Improve usability by not blinking (i.e. always rendering) the caret shortly after is has been moved. UI text input ------------------------------ Fix inconsistent mouse-based left and right scrolling (closes #4347). Support smooth left and right scrolling. Chat ------------------------------ Support keyboard-based text selection of the chat input. Mouse-based selection could be support in the future when we decide to add something like an ingame UI cursor. Support smooth up and down scrolling of the chat input, removing the old hack that offsets the input string to simulate scrolling. Console ------------------------------ Also support mouse-based text selection of the command input. Only text from either the command input or the console log can be selected at the same time. This ensures that Ctrl+C will always copy the text that is currently visually selected in the console. Check for Ctrl+C input event in event handler instead of in render function, to hopefully fix the issue that copying does not work sometimes (closes #5974 until further notice). When Ctrl+C is used to copy text from the console log, the selection is cleared. This should make it more clear when text was copied from the log. Fix an issue that was preventing the console log selection from being cleared, when all log lines are selected. Remove Ctrl+A/E hotkeys that move cursor to beginning/end respectively. Ctrl+A now selectes all text like for all other inputs. Home and End keys can still be used to go the beginning and end. Remove Ctrl+U/K hotkeys that clear everything before/after the cursor respectively. Hold shift and use Home/End to select everything instead. IME support ------------------------------ Render list of IME candidates in the client on Windows, so the candidate list can also be viewed in fullscreen mode. There is no API available to retrieve a candidate list on the other operating systems. Improve composition rendering by underlining the composition text instead of putting it in square brackets. Track active input globally to properly activate and deactivate IME through the SDL functions. Closes #1030. Closes #1008. Password rendering ------------------------------ Fix rendering of passwords containing unicode. Instead of rendering one star character for each UTF-8 `char`, render on star for every unicode codepoint. Show the composition text also for passwords. Without seeing the composition text it's hard to type a password containing those characters. The candidate window exposes the composition anyway. If you don't want to expose your password this way, e.g. while streaming, you could: 1. Use a latin password and switch off the IME for the password input with the IME hotkey. 2. Blank your screen with an external program while you are streaming and entering passwords. 3. Use binds to authenticate in rcon or to set the server browser password. Refactoring ------------------------------ Move all text input logic and general rendering to `CLineInput`. A `CLineInput` is associated with a particular `char` buffer given as a pointer either in the constructor or with `SetBuffer`. The maximum byte size of the buffer must also be specified. The maximum length in unicode codepoints can also be specified separately (e.g. on upstream, name are limited by the number of unicode codepoints instead). Add `CLineInputBuffered`, which is a `CLineInput` that own a `char` buffer of a fixed size, which is specified as a template argument. As `CLineInput` does not own a buffer anymore, this reduces duplicate code for line inputs that need their own buffer. Add `CLineInputNumber` which has additional convenience functions to consider the text as an `int` or `float`, to reduce duplicate code in those cases. In the future we could also add an input filter function so that only numbers can be entered in the number input. Add `CLineInput::SetClipboardLineCallback` to handle the case that multiple lines of text are pasted into a lineinput. This reduces duplicate code, as this behavior was previously implemented separately for chat and console. The behavior is also fixed to be consistent with the console on Windows, so the first line being pasted edits the current input text and then sends it instead of being sent on its own without the existing input text. Add `CalcFontSizeAndBoundingBox` to UI to reduce duplicate code. Expose `CalcAlignedCursorPos` as static member function to reuse it for line input. Dispatch input events to UI inputs through the event handler instead of storing them in a duplicate buffer. Use `size_t` for line input cursor position, length etc. and for `str_utf8_stats`. Add `IButtonColorFunction` to UI to describe a functions that defines colors for the Default, Active and Hovered states of UI elements. Add some default button color functions. Use button color function to reduce duplicate code in scrollbar rendering. Use `vec2` instead of two `floats` to represent the mouse positions in the text renderer. Remove `CaretPosition` again, as it does not calculate the correct Y position near line breaks due to the wrapping being different when not rendering the entire string. Instead, calculate the exact caret position when rending a text container and store the caret position in the text cursor for later use. IME usage guide (Windows) ------------------------------ 1. Install the respective language and the Microsoft-IME keyboard (e.g. for Chinese, Japanese or Korean). 2. Launch the game (or a text editor to first try out the IME). Note that Windows may track the input language separately for every application. You can change this in the Windows input settings so the input language is changed globally. 2. Switch the input language using the hotkey Windows+Space or another hotkey that you configured in the Windows input settings (Alt+Shift is the default, but you should consider disabling it, to avoid accidentally changing the input language while playing). 3. Switch from Latin/English input mode to the respective asian input mode. - Chinese: Use Ctrl+Space to switch between English and Chinese input mode. You can change this hotkey in the IME's settings. - Japanese: Use Ctrl+Space to switch between Alphanumeric and Hiragana/Katakana input mode. You can change this hotkey in the IME's settings. - Korean: Use Right Alt to switch between English and Hangul input mode. You cannot change this hotkey as of yet. - Note that the input mode is also tracked per application, but there is no setting to change this behavior as far as I know, so you'll need to switch for every application separately. 4. Start typing. The underlined text is the current composition text. While a composition is active, you can only edit the composition text. Confirm the composition with Space or by selecting a candidate from the candidate list with the arrow keys. Cancel the composition with Escape or by using Backspace to delete the composition text. Note that not all languages offer a candidate list. SDL version-specific issues ------------------------------ - 2.26.5, 2.24.2, 2.0.22: IME candidates work. But there are minor bugs when moving the composition cursor. - 2.0.18, 2.0.20: IME candidates work. - 2.0.16 (our current version): IME candidates cannot be determined with Windows API. Windows tries to draw the composition window like before, so this does not work in fullscreen mode. - 2.0.8 (upstream 0.7): IME candidates work. But this SDL version is too old for us.
2023-01-03 21:28:38 +00:00
s_FilterInput.SelectAll();
2022-03-13 18:09:33 +00:00
}
Port line input and IME support from 0.7 Port the line input (UI edit boxes, chat, console) and Input Method Editor (IME) support from upstream. Closes #4397. General ------------------------------ Fix issues with the text input. Closes #4346. Closes #4524. Word skipping (when holding Ctrl) is overhauled to be consistent with the Windows / Firefox experience that I took as reference. Improve usability by not blinking (i.e. always rendering) the caret shortly after is has been moved. UI text input ------------------------------ Fix inconsistent mouse-based left and right scrolling (closes #4347). Support smooth left and right scrolling. Chat ------------------------------ Support keyboard-based text selection of the chat input. Mouse-based selection could be support in the future when we decide to add something like an ingame UI cursor. Support smooth up and down scrolling of the chat input, removing the old hack that offsets the input string to simulate scrolling. Console ------------------------------ Also support mouse-based text selection of the command input. Only text from either the command input or the console log can be selected at the same time. This ensures that Ctrl+C will always copy the text that is currently visually selected in the console. Check for Ctrl+C input event in event handler instead of in render function, to hopefully fix the issue that copying does not work sometimes (closes #5974 until further notice). When Ctrl+C is used to copy text from the console log, the selection is cleared. This should make it more clear when text was copied from the log. Fix an issue that was preventing the console log selection from being cleared, when all log lines are selected. Remove Ctrl+A/E hotkeys that move cursor to beginning/end respectively. Ctrl+A now selectes all text like for all other inputs. Home and End keys can still be used to go the beginning and end. Remove Ctrl+U/K hotkeys that clear everything before/after the cursor respectively. Hold shift and use Home/End to select everything instead. IME support ------------------------------ Render list of IME candidates in the client on Windows, so the candidate list can also be viewed in fullscreen mode. There is no API available to retrieve a candidate list on the other operating systems. Improve composition rendering by underlining the composition text instead of putting it in square brackets. Track active input globally to properly activate and deactivate IME through the SDL functions. Closes #1030. Closes #1008. Password rendering ------------------------------ Fix rendering of passwords containing unicode. Instead of rendering one star character for each UTF-8 `char`, render on star for every unicode codepoint. Show the composition text also for passwords. Without seeing the composition text it's hard to type a password containing those characters. The candidate window exposes the composition anyway. If you don't want to expose your password this way, e.g. while streaming, you could: 1. Use a latin password and switch off the IME for the password input with the IME hotkey. 2. Blank your screen with an external program while you are streaming and entering passwords. 3. Use binds to authenticate in rcon or to set the server browser password. Refactoring ------------------------------ Move all text input logic and general rendering to `CLineInput`. A `CLineInput` is associated with a particular `char` buffer given as a pointer either in the constructor or with `SetBuffer`. The maximum byte size of the buffer must also be specified. The maximum length in unicode codepoints can also be specified separately (e.g. on upstream, name are limited by the number of unicode codepoints instead). Add `CLineInputBuffered`, which is a `CLineInput` that own a `char` buffer of a fixed size, which is specified as a template argument. As `CLineInput` does not own a buffer anymore, this reduces duplicate code for line inputs that need their own buffer. Add `CLineInputNumber` which has additional convenience functions to consider the text as an `int` or `float`, to reduce duplicate code in those cases. In the future we could also add an input filter function so that only numbers can be entered in the number input. Add `CLineInput::SetClipboardLineCallback` to handle the case that multiple lines of text are pasted into a lineinput. This reduces duplicate code, as this behavior was previously implemented separately for chat and console. The behavior is also fixed to be consistent with the console on Windows, so the first line being pasted edits the current input text and then sends it instead of being sent on its own without the existing input text. Add `CalcFontSizeAndBoundingBox` to UI to reduce duplicate code. Expose `CalcAlignedCursorPos` as static member function to reuse it for line input. Dispatch input events to UI inputs through the event handler instead of storing them in a duplicate buffer. Use `size_t` for line input cursor position, length etc. and for `str_utf8_stats`. Add `IButtonColorFunction` to UI to describe a functions that defines colors for the Default, Active and Hovered states of UI elements. Add some default button color functions. Use button color function to reduce duplicate code in scrollbar rendering. Use `vec2` instead of two `floats` to represent the mouse positions in the text renderer. Remove `CaretPosition` again, as it does not calculate the correct Y position near line breaks due to the wrapping being different when not rendering the entire string. Instead, calculate the exact caret position when rending a text container and store the caret position in the text cursor for later use. IME usage guide (Windows) ------------------------------ 1. Install the respective language and the Microsoft-IME keyboard (e.g. for Chinese, Japanese or Korean). 2. Launch the game (or a text editor to first try out the IME). Note that Windows may track the input language separately for every application. You can change this in the Windows input settings so the input language is changed globally. 2. Switch the input language using the hotkey Windows+Space or another hotkey that you configured in the Windows input settings (Alt+Shift is the default, but you should consider disabling it, to avoid accidentally changing the input language while playing). 3. Switch from Latin/English input mode to the respective asian input mode. - Chinese: Use Ctrl+Space to switch between English and Chinese input mode. You can change this hotkey in the IME's settings. - Japanese: Use Ctrl+Space to switch between Alphanumeric and Hiragana/Katakana input mode. You can change this hotkey in the IME's settings. - Korean: Use Right Alt to switch between English and Hangul input mode. You cannot change this hotkey as of yet. - Note that the input mode is also tracked per application, but there is no setting to change this behavior as far as I know, so you'll need to switch for every application separately. 4. Start typing. The underlined text is the current composition text. While a composition is active, you can only edit the composition text. Confirm the composition with Space or by selecting a candidate from the candidate list with the arrow keys. Cancel the composition with Escape or by using Backspace to delete the composition text. Note that not all languages offer a candidate list. SDL version-specific issues ------------------------------ - 2.26.5, 2.24.2, 2.0.22: IME candidates work. But there are minor bugs when moving the composition cursor. - 2.0.18, 2.0.20: IME candidates work. - 2.0.16 (our current version): IME candidates cannot be determined with Windows API. Windows tries to draw the composition window like before, so this does not work in fullscreen mode. - 2.0.8 (upstream 0.7): IME candidates work. But this SDL version is too old for us.
2023-01-03 21:28:38 +00:00
if(UI()->DoClearableEditBox(&s_FilterInput, &QuickSearch, 12.0f))
Client()->ServerBrowserUpdate();
2010-05-29 07:25:38 +00:00
}
// render quick exclude
{
Add font index, support font family variants depending on language Add `fonts/index.json` which specifies: - List of all font files that should be loaded (filenames). - Default font (specified by family name or by family and style name). - Font variants for different languages, using the name of the language file as key. - Fallback fonts. - Icon font. There are characters (e.g. all in `刃直海角骨入`) that look different depending on the language of the content being Japanese, Simplified Chinese, Traditional Chinese and Hangul, because Unicode uses the same codepoint for characters regardless of the language. To render these characters correctly, the active variant font is switched depending on the selected language. The `ITextRender` interface is changed so the current language variant can be set using `SetFontLanguageVariant` and the default and icon fonts can be toggled using `SetFontPreset`. The class `CFont` is removed entirely. The text render is restructured: The font faces and font atlas are now managed by a separate class `CGlyphMap` like on upstream. As the text fill and outline textures always have the same size, the texture skyline only needs to be stored once and free positions in the atlas only need to be calculated once for each glyph instead of separately for the fill and outline textures. The font files and their licenses are also updated: - Update Source Han Sans to version 2.001. - Update Glow Sans Japanese Compressed to version 0.93. - Update Deja Vu Sans to version 2.37. - Update Font Awesome icons font to March 2023 version. Closes #6881.
2023-07-17 13:49:41 +00:00
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
2020-10-06 10:25:10 +00:00
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
UI()->DoLabel(&QuickExclude, FONT_ICON_BAN, 16.0f, TEXTALIGN_ML);
TextRender()->SetRenderFlags(0);
Add font index, support font family variants depending on language Add `fonts/index.json` which specifies: - List of all font files that should be loaded (filenames). - Default font (specified by family name or by family and style name). - Font variants for different languages, using the name of the language file as key. - Fallback fonts. - Icon font. There are characters (e.g. all in `刃直海角骨入`) that look different depending on the language of the content being Japanese, Simplified Chinese, Traditional Chinese and Hangul, because Unicode uses the same codepoint for characters regardless of the language. To render these characters correctly, the active variant font is switched depending on the selected language. The `ITextRender` interface is changed so the current language variant can be set using `SetFontLanguageVariant` and the default and icon fonts can be toggled using `SetFontPreset`. The class `CFont` is removed entirely. The text render is restructured: The font faces and font atlas are now managed by a separate class `CGlyphMap` like on upstream. As the text fill and outline textures always have the same size, the texture skyline only needs to be stored once and free positions in the atlas only need to be calculated once for each glyph instead of separately for the fill and outline textures. The font files and their licenses are also updated: - Update Source Han Sans to version 2.001. - Update Glow Sans Japanese Compressed to version 0.93. - Update Deja Vu Sans to version 2.37. - Update Font Awesome icons font to March 2023 version. Closes #6881.
2023-07-17 13:49:41 +00:00
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
QuickExclude.VSplitLeft(ExcludeSearchIconMax, nullptr, &QuickExclude);
QuickExclude.VSplitLeft(5.0f, nullptr, &QuickExclude);
char aBufExclude[64];
str_format(aBufExclude, sizeof(aBufExclude), "%s:", Localize("Exclude"));
UI()->DoLabel(&QuickExclude, aBufExclude, 14.0f, TEXTALIGN_ML);
QuickExclude.VSplitLeft(SearchExcludeAddrStrMax, nullptr, &QuickExclude);
QuickExclude.VSplitLeft(5.0f, nullptr, &QuickExclude);
Port line input and IME support from 0.7 Port the line input (UI edit boxes, chat, console) and Input Method Editor (IME) support from upstream. Closes #4397. General ------------------------------ Fix issues with the text input. Closes #4346. Closes #4524. Word skipping (when holding Ctrl) is overhauled to be consistent with the Windows / Firefox experience that I took as reference. Improve usability by not blinking (i.e. always rendering) the caret shortly after is has been moved. UI text input ------------------------------ Fix inconsistent mouse-based left and right scrolling (closes #4347). Support smooth left and right scrolling. Chat ------------------------------ Support keyboard-based text selection of the chat input. Mouse-based selection could be support in the future when we decide to add something like an ingame UI cursor. Support smooth up and down scrolling of the chat input, removing the old hack that offsets the input string to simulate scrolling. Console ------------------------------ Also support mouse-based text selection of the command input. Only text from either the command input or the console log can be selected at the same time. This ensures that Ctrl+C will always copy the text that is currently visually selected in the console. Check for Ctrl+C input event in event handler instead of in render function, to hopefully fix the issue that copying does not work sometimes (closes #5974 until further notice). When Ctrl+C is used to copy text from the console log, the selection is cleared. This should make it more clear when text was copied from the log. Fix an issue that was preventing the console log selection from being cleared, when all log lines are selected. Remove Ctrl+A/E hotkeys that move cursor to beginning/end respectively. Ctrl+A now selectes all text like for all other inputs. Home and End keys can still be used to go the beginning and end. Remove Ctrl+U/K hotkeys that clear everything before/after the cursor respectively. Hold shift and use Home/End to select everything instead. IME support ------------------------------ Render list of IME candidates in the client on Windows, so the candidate list can also be viewed in fullscreen mode. There is no API available to retrieve a candidate list on the other operating systems. Improve composition rendering by underlining the composition text instead of putting it in square brackets. Track active input globally to properly activate and deactivate IME through the SDL functions. Closes #1030. Closes #1008. Password rendering ------------------------------ Fix rendering of passwords containing unicode. Instead of rendering one star character for each UTF-8 `char`, render on star for every unicode codepoint. Show the composition text also for passwords. Without seeing the composition text it's hard to type a password containing those characters. The candidate window exposes the composition anyway. If you don't want to expose your password this way, e.g. while streaming, you could: 1. Use a latin password and switch off the IME for the password input with the IME hotkey. 2. Blank your screen with an external program while you are streaming and entering passwords. 3. Use binds to authenticate in rcon or to set the server browser password. Refactoring ------------------------------ Move all text input logic and general rendering to `CLineInput`. A `CLineInput` is associated with a particular `char` buffer given as a pointer either in the constructor or with `SetBuffer`. The maximum byte size of the buffer must also be specified. The maximum length in unicode codepoints can also be specified separately (e.g. on upstream, name are limited by the number of unicode codepoints instead). Add `CLineInputBuffered`, which is a `CLineInput` that own a `char` buffer of a fixed size, which is specified as a template argument. As `CLineInput` does not own a buffer anymore, this reduces duplicate code for line inputs that need their own buffer. Add `CLineInputNumber` which has additional convenience functions to consider the text as an `int` or `float`, to reduce duplicate code in those cases. In the future we could also add an input filter function so that only numbers can be entered in the number input. Add `CLineInput::SetClipboardLineCallback` to handle the case that multiple lines of text are pasted into a lineinput. This reduces duplicate code, as this behavior was previously implemented separately for chat and console. The behavior is also fixed to be consistent with the console on Windows, so the first line being pasted edits the current input text and then sends it instead of being sent on its own without the existing input text. Add `CalcFontSizeAndBoundingBox` to UI to reduce duplicate code. Expose `CalcAlignedCursorPos` as static member function to reuse it for line input. Dispatch input events to UI inputs through the event handler instead of storing them in a duplicate buffer. Use `size_t` for line input cursor position, length etc. and for `str_utf8_stats`. Add `IButtonColorFunction` to UI to describe a functions that defines colors for the Default, Active and Hovered states of UI elements. Add some default button color functions. Use button color function to reduce duplicate code in scrollbar rendering. Use `vec2` instead of two `floats` to represent the mouse positions in the text renderer. Remove `CaretPosition` again, as it does not calculate the correct Y position near line breaks due to the wrapping being different when not rendering the entire string. Instead, calculate the exact caret position when rending a text container and store the caret position in the text cursor for later use. IME usage guide (Windows) ------------------------------ 1. Install the respective language and the Microsoft-IME keyboard (e.g. for Chinese, Japanese or Korean). 2. Launch the game (or a text editor to first try out the IME). Note that Windows may track the input language separately for every application. You can change this in the Windows input settings so the input language is changed globally. 2. Switch the input language using the hotkey Windows+Space or another hotkey that you configured in the Windows input settings (Alt+Shift is the default, but you should consider disabling it, to avoid accidentally changing the input language while playing). 3. Switch from Latin/English input mode to the respective asian input mode. - Chinese: Use Ctrl+Space to switch between English and Chinese input mode. You can change this hotkey in the IME's settings. - Japanese: Use Ctrl+Space to switch between Alphanumeric and Hiragana/Katakana input mode. You can change this hotkey in the IME's settings. - Korean: Use Right Alt to switch between English and Hangul input mode. You cannot change this hotkey as of yet. - Note that the input mode is also tracked per application, but there is no setting to change this behavior as far as I know, so you'll need to switch for every application separately. 4. Start typing. The underlined text is the current composition text. While a composition is active, you can only edit the composition text. Confirm the composition with Space or by selecting a candidate from the candidate list with the arrow keys. Cancel the composition with Escape or by using Backspace to delete the composition text. Note that not all languages offer a candidate list. SDL version-specific issues ------------------------------ - 2.26.5, 2.24.2, 2.0.22: IME candidates work. But there are minor bugs when moving the composition cursor. - 2.0.18, 2.0.20: IME candidates work. - 2.0.16 (our current version): IME candidates cannot be determined with Windows API. Windows tries to draw the composition window like before, so this does not work in fullscreen mode. - 2.0.8 (upstream 0.7): IME candidates work. But this SDL version is too old for us.
2023-01-03 21:28:38 +00:00
static CLineInput s_ExcludeInput(g_Config.m_BrExcludeString, sizeof(g_Config.m_BrExcludeString));
if(!UI()->IsPopupOpen() && Input()->KeyPress(KEY_X) && Input()->ShiftIsPressed() && Input()->ModifierIsPressed())
{
Port line input and IME support from 0.7 Port the line input (UI edit boxes, chat, console) and Input Method Editor (IME) support from upstream. Closes #4397. General ------------------------------ Fix issues with the text input. Closes #4346. Closes #4524. Word skipping (when holding Ctrl) is overhauled to be consistent with the Windows / Firefox experience that I took as reference. Improve usability by not blinking (i.e. always rendering) the caret shortly after is has been moved. UI text input ------------------------------ Fix inconsistent mouse-based left and right scrolling (closes #4347). Support smooth left and right scrolling. Chat ------------------------------ Support keyboard-based text selection of the chat input. Mouse-based selection could be support in the future when we decide to add something like an ingame UI cursor. Support smooth up and down scrolling of the chat input, removing the old hack that offsets the input string to simulate scrolling. Console ------------------------------ Also support mouse-based text selection of the command input. Only text from either the command input or the console log can be selected at the same time. This ensures that Ctrl+C will always copy the text that is currently visually selected in the console. Check for Ctrl+C input event in event handler instead of in render function, to hopefully fix the issue that copying does not work sometimes (closes #5974 until further notice). When Ctrl+C is used to copy text from the console log, the selection is cleared. This should make it more clear when text was copied from the log. Fix an issue that was preventing the console log selection from being cleared, when all log lines are selected. Remove Ctrl+A/E hotkeys that move cursor to beginning/end respectively. Ctrl+A now selectes all text like for all other inputs. Home and End keys can still be used to go the beginning and end. Remove Ctrl+U/K hotkeys that clear everything before/after the cursor respectively. Hold shift and use Home/End to select everything instead. IME support ------------------------------ Render list of IME candidates in the client on Windows, so the candidate list can also be viewed in fullscreen mode. There is no API available to retrieve a candidate list on the other operating systems. Improve composition rendering by underlining the composition text instead of putting it in square brackets. Track active input globally to properly activate and deactivate IME through the SDL functions. Closes #1030. Closes #1008. Password rendering ------------------------------ Fix rendering of passwords containing unicode. Instead of rendering one star character for each UTF-8 `char`, render on star for every unicode codepoint. Show the composition text also for passwords. Without seeing the composition text it's hard to type a password containing those characters. The candidate window exposes the composition anyway. If you don't want to expose your password this way, e.g. while streaming, you could: 1. Use a latin password and switch off the IME for the password input with the IME hotkey. 2. Blank your screen with an external program while you are streaming and entering passwords. 3. Use binds to authenticate in rcon or to set the server browser password. Refactoring ------------------------------ Move all text input logic and general rendering to `CLineInput`. A `CLineInput` is associated with a particular `char` buffer given as a pointer either in the constructor or with `SetBuffer`. The maximum byte size of the buffer must also be specified. The maximum length in unicode codepoints can also be specified separately (e.g. on upstream, name are limited by the number of unicode codepoints instead). Add `CLineInputBuffered`, which is a `CLineInput` that own a `char` buffer of a fixed size, which is specified as a template argument. As `CLineInput` does not own a buffer anymore, this reduces duplicate code for line inputs that need their own buffer. Add `CLineInputNumber` which has additional convenience functions to consider the text as an `int` or `float`, to reduce duplicate code in those cases. In the future we could also add an input filter function so that only numbers can be entered in the number input. Add `CLineInput::SetClipboardLineCallback` to handle the case that multiple lines of text are pasted into a lineinput. This reduces duplicate code, as this behavior was previously implemented separately for chat and console. The behavior is also fixed to be consistent with the console on Windows, so the first line being pasted edits the current input text and then sends it instead of being sent on its own without the existing input text. Add `CalcFontSizeAndBoundingBox` to UI to reduce duplicate code. Expose `CalcAlignedCursorPos` as static member function to reuse it for line input. Dispatch input events to UI inputs through the event handler instead of storing them in a duplicate buffer. Use `size_t` for line input cursor position, length etc. and for `str_utf8_stats`. Add `IButtonColorFunction` to UI to describe a functions that defines colors for the Default, Active and Hovered states of UI elements. Add some default button color functions. Use button color function to reduce duplicate code in scrollbar rendering. Use `vec2` instead of two `floats` to represent the mouse positions in the text renderer. Remove `CaretPosition` again, as it does not calculate the correct Y position near line breaks due to the wrapping being different when not rendering the entire string. Instead, calculate the exact caret position when rending a text container and store the caret position in the text cursor for later use. IME usage guide (Windows) ------------------------------ 1. Install the respective language and the Microsoft-IME keyboard (e.g. for Chinese, Japanese or Korean). 2. Launch the game (or a text editor to first try out the IME). Note that Windows may track the input language separately for every application. You can change this in the Windows input settings so the input language is changed globally. 2. Switch the input language using the hotkey Windows+Space or another hotkey that you configured in the Windows input settings (Alt+Shift is the default, but you should consider disabling it, to avoid accidentally changing the input language while playing). 3. Switch from Latin/English input mode to the respective asian input mode. - Chinese: Use Ctrl+Space to switch between English and Chinese input mode. You can change this hotkey in the IME's settings. - Japanese: Use Ctrl+Space to switch between Alphanumeric and Hiragana/Katakana input mode. You can change this hotkey in the IME's settings. - Korean: Use Right Alt to switch between English and Hangul input mode. You cannot change this hotkey as of yet. - Note that the input mode is also tracked per application, but there is no setting to change this behavior as far as I know, so you'll need to switch for every application separately. 4. Start typing. The underlined text is the current composition text. While a composition is active, you can only edit the composition text. Confirm the composition with Space or by selecting a candidate from the candidate list with the arrow keys. Cancel the composition with Escape or by using Backspace to delete the composition text. Note that not all languages offer a candidate list. SDL version-specific issues ------------------------------ - 2.26.5, 2.24.2, 2.0.22: IME candidates work. But there are minor bugs when moving the composition cursor. - 2.0.18, 2.0.20: IME candidates work. - 2.0.16 (our current version): IME candidates cannot be determined with Windows API. Windows tries to draw the composition window like before, so this does not work in fullscreen mode. - 2.0.8 (upstream 0.7): IME candidates work. But this SDL version is too old for us.
2023-01-03 21:28:38 +00:00
UI()->SetActiveItem(&s_ExcludeInput);
s_ExcludeInput.SelectAll();
}
Port line input and IME support from 0.7 Port the line input (UI edit boxes, chat, console) and Input Method Editor (IME) support from upstream. Closes #4397. General ------------------------------ Fix issues with the text input. Closes #4346. Closes #4524. Word skipping (when holding Ctrl) is overhauled to be consistent with the Windows / Firefox experience that I took as reference. Improve usability by not blinking (i.e. always rendering) the caret shortly after is has been moved. UI text input ------------------------------ Fix inconsistent mouse-based left and right scrolling (closes #4347). Support smooth left and right scrolling. Chat ------------------------------ Support keyboard-based text selection of the chat input. Mouse-based selection could be support in the future when we decide to add something like an ingame UI cursor. Support smooth up and down scrolling of the chat input, removing the old hack that offsets the input string to simulate scrolling. Console ------------------------------ Also support mouse-based text selection of the command input. Only text from either the command input or the console log can be selected at the same time. This ensures that Ctrl+C will always copy the text that is currently visually selected in the console. Check for Ctrl+C input event in event handler instead of in render function, to hopefully fix the issue that copying does not work sometimes (closes #5974 until further notice). When Ctrl+C is used to copy text from the console log, the selection is cleared. This should make it more clear when text was copied from the log. Fix an issue that was preventing the console log selection from being cleared, when all log lines are selected. Remove Ctrl+A/E hotkeys that move cursor to beginning/end respectively. Ctrl+A now selectes all text like for all other inputs. Home and End keys can still be used to go the beginning and end. Remove Ctrl+U/K hotkeys that clear everything before/after the cursor respectively. Hold shift and use Home/End to select everything instead. IME support ------------------------------ Render list of IME candidates in the client on Windows, so the candidate list can also be viewed in fullscreen mode. There is no API available to retrieve a candidate list on the other operating systems. Improve composition rendering by underlining the composition text instead of putting it in square brackets. Track active input globally to properly activate and deactivate IME through the SDL functions. Closes #1030. Closes #1008. Password rendering ------------------------------ Fix rendering of passwords containing unicode. Instead of rendering one star character for each UTF-8 `char`, render on star for every unicode codepoint. Show the composition text also for passwords. Without seeing the composition text it's hard to type a password containing those characters. The candidate window exposes the composition anyway. If you don't want to expose your password this way, e.g. while streaming, you could: 1. Use a latin password and switch off the IME for the password input with the IME hotkey. 2. Blank your screen with an external program while you are streaming and entering passwords. 3. Use binds to authenticate in rcon or to set the server browser password. Refactoring ------------------------------ Move all text input logic and general rendering to `CLineInput`. A `CLineInput` is associated with a particular `char` buffer given as a pointer either in the constructor or with `SetBuffer`. The maximum byte size of the buffer must also be specified. The maximum length in unicode codepoints can also be specified separately (e.g. on upstream, name are limited by the number of unicode codepoints instead). Add `CLineInputBuffered`, which is a `CLineInput` that own a `char` buffer of a fixed size, which is specified as a template argument. As `CLineInput` does not own a buffer anymore, this reduces duplicate code for line inputs that need their own buffer. Add `CLineInputNumber` which has additional convenience functions to consider the text as an `int` or `float`, to reduce duplicate code in those cases. In the future we could also add an input filter function so that only numbers can be entered in the number input. Add `CLineInput::SetClipboardLineCallback` to handle the case that multiple lines of text are pasted into a lineinput. This reduces duplicate code, as this behavior was previously implemented separately for chat and console. The behavior is also fixed to be consistent with the console on Windows, so the first line being pasted edits the current input text and then sends it instead of being sent on its own without the existing input text. Add `CalcFontSizeAndBoundingBox` to UI to reduce duplicate code. Expose `CalcAlignedCursorPos` as static member function to reuse it for line input. Dispatch input events to UI inputs through the event handler instead of storing them in a duplicate buffer. Use `size_t` for line input cursor position, length etc. and for `str_utf8_stats`. Add `IButtonColorFunction` to UI to describe a functions that defines colors for the Default, Active and Hovered states of UI elements. Add some default button color functions. Use button color function to reduce duplicate code in scrollbar rendering. Use `vec2` instead of two `floats` to represent the mouse positions in the text renderer. Remove `CaretPosition` again, as it does not calculate the correct Y position near line breaks due to the wrapping being different when not rendering the entire string. Instead, calculate the exact caret position when rending a text container and store the caret position in the text cursor for later use. IME usage guide (Windows) ------------------------------ 1. Install the respective language and the Microsoft-IME keyboard (e.g. for Chinese, Japanese or Korean). 2. Launch the game (or a text editor to first try out the IME). Note that Windows may track the input language separately for every application. You can change this in the Windows input settings so the input language is changed globally. 2. Switch the input language using the hotkey Windows+Space or another hotkey that you configured in the Windows input settings (Alt+Shift is the default, but you should consider disabling it, to avoid accidentally changing the input language while playing). 3. Switch from Latin/English input mode to the respective asian input mode. - Chinese: Use Ctrl+Space to switch between English and Chinese input mode. You can change this hotkey in the IME's settings. - Japanese: Use Ctrl+Space to switch between Alphanumeric and Hiragana/Katakana input mode. You can change this hotkey in the IME's settings. - Korean: Use Right Alt to switch between English and Hangul input mode. You cannot change this hotkey as of yet. - Note that the input mode is also tracked per application, but there is no setting to change this behavior as far as I know, so you'll need to switch for every application separately. 4. Start typing. The underlined text is the current composition text. While a composition is active, you can only edit the composition text. Confirm the composition with Space or by selecting a candidate from the candidate list with the arrow keys. Cancel the composition with Escape or by using Backspace to delete the composition text. Note that not all languages offer a candidate list. SDL version-specific issues ------------------------------ - 2.26.5, 2.24.2, 2.0.22: IME candidates work. But there are minor bugs when moving the composition cursor. - 2.0.18, 2.0.20: IME candidates work. - 2.0.16 (our current version): IME candidates cannot be determined with Windows API. Windows tries to draw the composition window like before, so this does not work in fullscreen mode. - 2.0.8 (upstream 0.7): IME candidates work. But this SDL version is too old for us.
2023-01-03 21:28:38 +00:00
if(UI()->DoClearableEditBox(&s_ExcludeInput, &QuickExclude, 12.0f))
Client()->ServerBrowserUpdate();
}
// render status
{
CUIRect ServersOnline, PlayersOnline;
ServersPlayersOnline.HSplitMid(&PlayersOnline, &ServersOnline);
char aBuf[128];
if(ServerBrowser()->NumServers() != 1)
str_format(aBuf, sizeof(aBuf), Localize("%d of %d servers"), ServerBrowser()->NumSortedServers(), ServerBrowser()->NumServers());
else
str_format(aBuf, sizeof(aBuf), Localize("%d of %d server"), ServerBrowser()->NumSortedServers(), ServerBrowser()->NumServers());
UI()->DoLabel(&ServersOnline, aBuf, 12.0f, TEXTALIGN_MR);
int NumPlayers = 0;
for(int i = 0; i < ServerBrowser()->NumSortedServers(); i++)
NumPlayers += ServerBrowser()->SortedGet(i)->m_NumFilteredPlayers;
if(NumPlayers != 1)
str_format(aBuf, sizeof(aBuf), Localize("%d players"), NumPlayers);
else
str_format(aBuf, sizeof(aBuf), Localize("%d player"), NumPlayers);
UI()->DoLabel(&PlayersOnline, aBuf, 12.0f, TEXTALIGN_MR);
}
// address info
{
CUIRect ServerAddrLabel, ServerAddrEditBox;
ServerAddr.Margin(2.0f, &ServerAddr);
ServerAddr.VSplitLeft(SearchExcludeAddrStrMax + 5.0f + ExcludeSearchIconMax + 5.0f, &ServerAddrLabel, &ServerAddrEditBox);
UI()->DoLabel(&ServerAddrLabel, Localize("Server address:"), 14.0f, TEXTALIGN_ML);
Port line input and IME support from 0.7 Port the line input (UI edit boxes, chat, console) and Input Method Editor (IME) support from upstream. Closes #4397. General ------------------------------ Fix issues with the text input. Closes #4346. Closes #4524. Word skipping (when holding Ctrl) is overhauled to be consistent with the Windows / Firefox experience that I took as reference. Improve usability by not blinking (i.e. always rendering) the caret shortly after is has been moved. UI text input ------------------------------ Fix inconsistent mouse-based left and right scrolling (closes #4347). Support smooth left and right scrolling. Chat ------------------------------ Support keyboard-based text selection of the chat input. Mouse-based selection could be support in the future when we decide to add something like an ingame UI cursor. Support smooth up and down scrolling of the chat input, removing the old hack that offsets the input string to simulate scrolling. Console ------------------------------ Also support mouse-based text selection of the command input. Only text from either the command input or the console log can be selected at the same time. This ensures that Ctrl+C will always copy the text that is currently visually selected in the console. Check for Ctrl+C input event in event handler instead of in render function, to hopefully fix the issue that copying does not work sometimes (closes #5974 until further notice). When Ctrl+C is used to copy text from the console log, the selection is cleared. This should make it more clear when text was copied from the log. Fix an issue that was preventing the console log selection from being cleared, when all log lines are selected. Remove Ctrl+A/E hotkeys that move cursor to beginning/end respectively. Ctrl+A now selectes all text like for all other inputs. Home and End keys can still be used to go the beginning and end. Remove Ctrl+U/K hotkeys that clear everything before/after the cursor respectively. Hold shift and use Home/End to select everything instead. IME support ------------------------------ Render list of IME candidates in the client on Windows, so the candidate list can also be viewed in fullscreen mode. There is no API available to retrieve a candidate list on the other operating systems. Improve composition rendering by underlining the composition text instead of putting it in square brackets. Track active input globally to properly activate and deactivate IME through the SDL functions. Closes #1030. Closes #1008. Password rendering ------------------------------ Fix rendering of passwords containing unicode. Instead of rendering one star character for each UTF-8 `char`, render on star for every unicode codepoint. Show the composition text also for passwords. Without seeing the composition text it's hard to type a password containing those characters. The candidate window exposes the composition anyway. If you don't want to expose your password this way, e.g. while streaming, you could: 1. Use a latin password and switch off the IME for the password input with the IME hotkey. 2. Blank your screen with an external program while you are streaming and entering passwords. 3. Use binds to authenticate in rcon or to set the server browser password. Refactoring ------------------------------ Move all text input logic and general rendering to `CLineInput`. A `CLineInput` is associated with a particular `char` buffer given as a pointer either in the constructor or with `SetBuffer`. The maximum byte size of the buffer must also be specified. The maximum length in unicode codepoints can also be specified separately (e.g. on upstream, name are limited by the number of unicode codepoints instead). Add `CLineInputBuffered`, which is a `CLineInput` that own a `char` buffer of a fixed size, which is specified as a template argument. As `CLineInput` does not own a buffer anymore, this reduces duplicate code for line inputs that need their own buffer. Add `CLineInputNumber` which has additional convenience functions to consider the text as an `int` or `float`, to reduce duplicate code in those cases. In the future we could also add an input filter function so that only numbers can be entered in the number input. Add `CLineInput::SetClipboardLineCallback` to handle the case that multiple lines of text are pasted into a lineinput. This reduces duplicate code, as this behavior was previously implemented separately for chat and console. The behavior is also fixed to be consistent with the console on Windows, so the first line being pasted edits the current input text and then sends it instead of being sent on its own without the existing input text. Add `CalcFontSizeAndBoundingBox` to UI to reduce duplicate code. Expose `CalcAlignedCursorPos` as static member function to reuse it for line input. Dispatch input events to UI inputs through the event handler instead of storing them in a duplicate buffer. Use `size_t` for line input cursor position, length etc. and for `str_utf8_stats`. Add `IButtonColorFunction` to UI to describe a functions that defines colors for the Default, Active and Hovered states of UI elements. Add some default button color functions. Use button color function to reduce duplicate code in scrollbar rendering. Use `vec2` instead of two `floats` to represent the mouse positions in the text renderer. Remove `CaretPosition` again, as it does not calculate the correct Y position near line breaks due to the wrapping being different when not rendering the entire string. Instead, calculate the exact caret position when rending a text container and store the caret position in the text cursor for later use. IME usage guide (Windows) ------------------------------ 1. Install the respective language and the Microsoft-IME keyboard (e.g. for Chinese, Japanese or Korean). 2. Launch the game (or a text editor to first try out the IME). Note that Windows may track the input language separately for every application. You can change this in the Windows input settings so the input language is changed globally. 2. Switch the input language using the hotkey Windows+Space or another hotkey that you configured in the Windows input settings (Alt+Shift is the default, but you should consider disabling it, to avoid accidentally changing the input language while playing). 3. Switch from Latin/English input mode to the respective asian input mode. - Chinese: Use Ctrl+Space to switch between English and Chinese input mode. You can change this hotkey in the IME's settings. - Japanese: Use Ctrl+Space to switch between Alphanumeric and Hiragana/Katakana input mode. You can change this hotkey in the IME's settings. - Korean: Use Right Alt to switch between English and Hangul input mode. You cannot change this hotkey as of yet. - Note that the input mode is also tracked per application, but there is no setting to change this behavior as far as I know, so you'll need to switch for every application separately. 4. Start typing. The underlined text is the current composition text. While a composition is active, you can only edit the composition text. Confirm the composition with Space or by selecting a candidate from the candidate list with the arrow keys. Cancel the composition with Escape or by using Backspace to delete the composition text. Note that not all languages offer a candidate list. SDL version-specific issues ------------------------------ - 2.26.5, 2.24.2, 2.0.22: IME candidates work. But there are minor bugs when moving the composition cursor. - 2.0.18, 2.0.20: IME candidates work. - 2.0.16 (our current version): IME candidates cannot be determined with Windows API. Windows tries to draw the composition window like before, so this does not work in fullscreen mode. - 2.0.8 (upstream 0.7): IME candidates work. But this SDL version is too old for us.
2023-01-03 21:28:38 +00:00
static CLineInput s_ServerAddressInput(g_Config.m_UiServerAddress, sizeof(g_Config.m_UiServerAddress));
if(UI()->DoClearableEditBox(&s_ServerAddressInput, &ServerAddrEditBox, 12.0f))
m_ServerBrowserShouldRevealSelection = true;
}
// buttons
{
CUIRect ButtonRefresh, ButtonConnect;
ConnectButtons.VSplitMid(&ButtonRefresh, &ButtonConnect, 5.0f);
// refresh button
{
char aLabelBuf[32] = {0};
const auto &&RefreshLabelFunc = [this, aLabelBuf]() mutable {
if(ServerBrowser()->IsRefreshing() || ServerBrowser()->IsGettingServerlist())
str_format(aLabelBuf, sizeof(aLabelBuf), "%s%s", FONT_ICON_ARROW_ROTATE_RIGHT, FONT_ICON_ELLIPSIS);
else
str_copy(aLabelBuf, FONT_ICON_ARROW_ROTATE_RIGHT);
return aLabelBuf;
};
SMenuButtonProperties Props;
Props.m_HintRequiresStringCheck = true;
Props.m_UseIconFont = true;
static CButtonContainer s_RefreshButton;
if(UI()->DoButton_Menu(m_RefreshButton, &s_RefreshButton, RefreshLabelFunc, &ButtonRefresh, Props) || (!UI()->IsPopupOpen() && (Input()->KeyPress(KEY_F5) || (Input()->KeyPress(KEY_R) && Input()->ModifierIsPressed()))))
{
RefreshBrowserTab(g_Config.m_UiPage);
}
}
// connect button
{
const auto &&ConnectLabelFunc = []() { return FONT_ICON_RIGHT_TO_BRACKET; };
SMenuButtonProperties Props;
Props.m_UseIconFont = true;
Props.m_Color = ColorRGBA(0.5f, 1.0f, 0.5f, 0.5f);
static CButtonContainer s_ConnectButton;
if(UI()->DoButton_Menu(m_ConnectButton, &s_ConnectButton, ConnectLabelFunc, &ButtonConnect, Props) || WasListboxItemActivated || (!UI()->IsPopupOpen() && UI()->ConsumeHotkey(CUI::HOTKEY_ENTER)))
{
Connect(g_Config.m_UiServerAddress);
}
}
}
2008-09-04 18:42:26 +00:00
}
void CMenus::Connect(const char *pAddress)
{
if(Client()->State() == IClient::STATE_ONLINE && Client()->GetCurrentRaceTime() / 60 >= g_Config.m_ClConfirmDisconnectTime && g_Config.m_ClConfirmDisconnectTime >= 0)
{
str_copy(m_aNextServer, pAddress);
PopupConfirm(Localize("Disconnect"), Localize("Are you sure that you want to disconnect and switch to a different server?"), Localize("Yes"), Localize("No"), &CMenus::PopupConfirmSwitchServer);
}
else
Client()->Connect(pAddress);
}
void CMenus::PopupConfirmSwitchServer()
{
Client()->Connect(m_aNextServer);
}
2010-05-29 07:25:38 +00:00
void CMenus::RenderServerbrowserFilters(CUIRect View)
2008-09-04 18:42:26 +00:00
{
const float RowHeight = 18.0f;
const float FontSize = (RowHeight - 4.0f) * CUI::ms_FontmodHeight; // based on DoButton_CheckBox
View.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.15f), IGraphics::CORNER_B, 4.0f);
View.Margin(5.0f, &View);
2008-09-04 18:42:26 +00:00
CUIRect Button, ResetButton;
View.HSplitBottom(RowHeight, &View, &ResetButton);
View.HSplitBottom(3.0f, &View, nullptr);
2008-09-04 18:42:26 +00:00
View.HSplitTop(RowHeight, &Button, &View);
if(DoButton_CheckBox(&g_Config.m_BrFilterEmpty, Localize("Has people playing"), g_Config.m_BrFilterEmpty, &Button))
2010-05-29 07:25:38 +00:00
g_Config.m_BrFilterEmpty ^= 1;
2008-09-04 18:42:26 +00:00
View.HSplitTop(RowHeight, &Button, &View);
if(DoButton_CheckBox(&g_Config.m_BrFilterSpectators, Localize("Count players only"), g_Config.m_BrFilterSpectators, &Button))
g_Config.m_BrFilterSpectators ^= 1;
View.HSplitTop(RowHeight, &Button, &View);
if(DoButton_CheckBox(&g_Config.m_BrFilterFull, Localize("Server not full"), g_Config.m_BrFilterFull, &Button))
2010-05-29 07:25:38 +00:00
g_Config.m_BrFilterFull ^= 1;
2008-09-04 18:42:26 +00:00
View.HSplitTop(RowHeight, &Button, &View);
if(DoButton_CheckBox(&g_Config.m_BrFilterFriends, Localize("Show friends only"), g_Config.m_BrFilterFriends, &Button))
2011-03-23 12:06:35 +00:00
g_Config.m_BrFilterFriends ^= 1;
View.HSplitTop(RowHeight, &Button, &View);
if(DoButton_CheckBox(&g_Config.m_BrFilterPw, Localize("No password"), g_Config.m_BrFilterPw, &Button))
2010-05-29 07:25:38 +00:00
g_Config.m_BrFilterPw ^= 1;
2008-09-04 18:42:26 +00:00
View.HSplitTop(RowHeight, &Button, &View);
if(DoButton_CheckBox(&g_Config.m_BrFilterGametypeStrict, Localize("Strict gametype filter"), g_Config.m_BrFilterGametypeStrict, &Button))
2011-04-17 09:57:33 +00:00
g_Config.m_BrFilterGametypeStrict ^= 1;
View.HSplitTop(3.0f, nullptr, &View);
View.HSplitTop(RowHeight, &Button, &View);
UI()->DoLabel(&Button, Localize("Game types:"), FontSize, TEXTALIGN_ML);
Button.VSplitRight(60.0f, nullptr, &Button);
Port line input and IME support from 0.7 Port the line input (UI edit boxes, chat, console) and Input Method Editor (IME) support from upstream. Closes #4397. General ------------------------------ Fix issues with the text input. Closes #4346. Closes #4524. Word skipping (when holding Ctrl) is overhauled to be consistent with the Windows / Firefox experience that I took as reference. Improve usability by not blinking (i.e. always rendering) the caret shortly after is has been moved. UI text input ------------------------------ Fix inconsistent mouse-based left and right scrolling (closes #4347). Support smooth left and right scrolling. Chat ------------------------------ Support keyboard-based text selection of the chat input. Mouse-based selection could be support in the future when we decide to add something like an ingame UI cursor. Support smooth up and down scrolling of the chat input, removing the old hack that offsets the input string to simulate scrolling. Console ------------------------------ Also support mouse-based text selection of the command input. Only text from either the command input or the console log can be selected at the same time. This ensures that Ctrl+C will always copy the text that is currently visually selected in the console. Check for Ctrl+C input event in event handler instead of in render function, to hopefully fix the issue that copying does not work sometimes (closes #5974 until further notice). When Ctrl+C is used to copy text from the console log, the selection is cleared. This should make it more clear when text was copied from the log. Fix an issue that was preventing the console log selection from being cleared, when all log lines are selected. Remove Ctrl+A/E hotkeys that move cursor to beginning/end respectively. Ctrl+A now selectes all text like for all other inputs. Home and End keys can still be used to go the beginning and end. Remove Ctrl+U/K hotkeys that clear everything before/after the cursor respectively. Hold shift and use Home/End to select everything instead. IME support ------------------------------ Render list of IME candidates in the client on Windows, so the candidate list can also be viewed in fullscreen mode. There is no API available to retrieve a candidate list on the other operating systems. Improve composition rendering by underlining the composition text instead of putting it in square brackets. Track active input globally to properly activate and deactivate IME through the SDL functions. Closes #1030. Closes #1008. Password rendering ------------------------------ Fix rendering of passwords containing unicode. Instead of rendering one star character for each UTF-8 `char`, render on star for every unicode codepoint. Show the composition text also for passwords. Without seeing the composition text it's hard to type a password containing those characters. The candidate window exposes the composition anyway. If you don't want to expose your password this way, e.g. while streaming, you could: 1. Use a latin password and switch off the IME for the password input with the IME hotkey. 2. Blank your screen with an external program while you are streaming and entering passwords. 3. Use binds to authenticate in rcon or to set the server browser password. Refactoring ------------------------------ Move all text input logic and general rendering to `CLineInput`. A `CLineInput` is associated with a particular `char` buffer given as a pointer either in the constructor or with `SetBuffer`. The maximum byte size of the buffer must also be specified. The maximum length in unicode codepoints can also be specified separately (e.g. on upstream, name are limited by the number of unicode codepoints instead). Add `CLineInputBuffered`, which is a `CLineInput` that own a `char` buffer of a fixed size, which is specified as a template argument. As `CLineInput` does not own a buffer anymore, this reduces duplicate code for line inputs that need their own buffer. Add `CLineInputNumber` which has additional convenience functions to consider the text as an `int` or `float`, to reduce duplicate code in those cases. In the future we could also add an input filter function so that only numbers can be entered in the number input. Add `CLineInput::SetClipboardLineCallback` to handle the case that multiple lines of text are pasted into a lineinput. This reduces duplicate code, as this behavior was previously implemented separately for chat and console. The behavior is also fixed to be consistent with the console on Windows, so the first line being pasted edits the current input text and then sends it instead of being sent on its own without the existing input text. Add `CalcFontSizeAndBoundingBox` to UI to reduce duplicate code. Expose `CalcAlignedCursorPos` as static member function to reuse it for line input. Dispatch input events to UI inputs through the event handler instead of storing them in a duplicate buffer. Use `size_t` for line input cursor position, length etc. and for `str_utf8_stats`. Add `IButtonColorFunction` to UI to describe a functions that defines colors for the Default, Active and Hovered states of UI elements. Add some default button color functions. Use button color function to reduce duplicate code in scrollbar rendering. Use `vec2` instead of two `floats` to represent the mouse positions in the text renderer. Remove `CaretPosition` again, as it does not calculate the correct Y position near line breaks due to the wrapping being different when not rendering the entire string. Instead, calculate the exact caret position when rending a text container and store the caret position in the text cursor for later use. IME usage guide (Windows) ------------------------------ 1. Install the respective language and the Microsoft-IME keyboard (e.g. for Chinese, Japanese or Korean). 2. Launch the game (or a text editor to first try out the IME). Note that Windows may track the input language separately for every application. You can change this in the Windows input settings so the input language is changed globally. 2. Switch the input language using the hotkey Windows+Space or another hotkey that you configured in the Windows input settings (Alt+Shift is the default, but you should consider disabling it, to avoid accidentally changing the input language while playing). 3. Switch from Latin/English input mode to the respective asian input mode. - Chinese: Use Ctrl+Space to switch between English and Chinese input mode. You can change this hotkey in the IME's settings. - Japanese: Use Ctrl+Space to switch between Alphanumeric and Hiragana/Katakana input mode. You can change this hotkey in the IME's settings. - Korean: Use Right Alt to switch between English and Hangul input mode. You cannot change this hotkey as of yet. - Note that the input mode is also tracked per application, but there is no setting to change this behavior as far as I know, so you'll need to switch for every application separately. 4. Start typing. The underlined text is the current composition text. While a composition is active, you can only edit the composition text. Confirm the composition with Space or by selecting a candidate from the candidate list with the arrow keys. Cancel the composition with Escape or by using Backspace to delete the composition text. Note that not all languages offer a candidate list. SDL version-specific issues ------------------------------ - 2.26.5, 2.24.2, 2.0.22: IME candidates work. But there are minor bugs when moving the composition cursor. - 2.0.18, 2.0.20: IME candidates work. - 2.0.16 (our current version): IME candidates cannot be determined with Windows API. Windows tries to draw the composition window like before, so this does not work in fullscreen mode. - 2.0.8 (upstream 0.7): IME candidates work. But this SDL version is too old for us.
2023-01-03 21:28:38 +00:00
static CLineInput s_GametypeInput(g_Config.m_BrFilterGametype, sizeof(g_Config.m_BrFilterGametype));
if(UI()->DoEditBox(&s_GametypeInput, &Button, FontSize))
Client()->ServerBrowserUpdate();
2008-09-04 18:42:26 +00:00
// server address
View.HSplitTop(6.0f, nullptr, &View);
View.HSplitTop(RowHeight, &Button, &View);
View.HSplitTop(6.0f, nullptr, &View);
UI()->DoLabel(&Button, Localize("Server address:"), FontSize, TEXTALIGN_ML);
Button.VSplitRight(60.0f, nullptr, &Button);
Port line input and IME support from 0.7 Port the line input (UI edit boxes, chat, console) and Input Method Editor (IME) support from upstream. Closes #4397. General ------------------------------ Fix issues with the text input. Closes #4346. Closes #4524. Word skipping (when holding Ctrl) is overhauled to be consistent with the Windows / Firefox experience that I took as reference. Improve usability by not blinking (i.e. always rendering) the caret shortly after is has been moved. UI text input ------------------------------ Fix inconsistent mouse-based left and right scrolling (closes #4347). Support smooth left and right scrolling. Chat ------------------------------ Support keyboard-based text selection of the chat input. Mouse-based selection could be support in the future when we decide to add something like an ingame UI cursor. Support smooth up and down scrolling of the chat input, removing the old hack that offsets the input string to simulate scrolling. Console ------------------------------ Also support mouse-based text selection of the command input. Only text from either the command input or the console log can be selected at the same time. This ensures that Ctrl+C will always copy the text that is currently visually selected in the console. Check for Ctrl+C input event in event handler instead of in render function, to hopefully fix the issue that copying does not work sometimes (closes #5974 until further notice). When Ctrl+C is used to copy text from the console log, the selection is cleared. This should make it more clear when text was copied from the log. Fix an issue that was preventing the console log selection from being cleared, when all log lines are selected. Remove Ctrl+A/E hotkeys that move cursor to beginning/end respectively. Ctrl+A now selectes all text like for all other inputs. Home and End keys can still be used to go the beginning and end. Remove Ctrl+U/K hotkeys that clear everything before/after the cursor respectively. Hold shift and use Home/End to select everything instead. IME support ------------------------------ Render list of IME candidates in the client on Windows, so the candidate list can also be viewed in fullscreen mode. There is no API available to retrieve a candidate list on the other operating systems. Improve composition rendering by underlining the composition text instead of putting it in square brackets. Track active input globally to properly activate and deactivate IME through the SDL functions. Closes #1030. Closes #1008. Password rendering ------------------------------ Fix rendering of passwords containing unicode. Instead of rendering one star character for each UTF-8 `char`, render on star for every unicode codepoint. Show the composition text also for passwords. Without seeing the composition text it's hard to type a password containing those characters. The candidate window exposes the composition anyway. If you don't want to expose your password this way, e.g. while streaming, you could: 1. Use a latin password and switch off the IME for the password input with the IME hotkey. 2. Blank your screen with an external program while you are streaming and entering passwords. 3. Use binds to authenticate in rcon or to set the server browser password. Refactoring ------------------------------ Move all text input logic and general rendering to `CLineInput`. A `CLineInput` is associated with a particular `char` buffer given as a pointer either in the constructor or with `SetBuffer`. The maximum byte size of the buffer must also be specified. The maximum length in unicode codepoints can also be specified separately (e.g. on upstream, name are limited by the number of unicode codepoints instead). Add `CLineInputBuffered`, which is a `CLineInput` that own a `char` buffer of a fixed size, which is specified as a template argument. As `CLineInput` does not own a buffer anymore, this reduces duplicate code for line inputs that need their own buffer. Add `CLineInputNumber` which has additional convenience functions to consider the text as an `int` or `float`, to reduce duplicate code in those cases. In the future we could also add an input filter function so that only numbers can be entered in the number input. Add `CLineInput::SetClipboardLineCallback` to handle the case that multiple lines of text are pasted into a lineinput. This reduces duplicate code, as this behavior was previously implemented separately for chat and console. The behavior is also fixed to be consistent with the console on Windows, so the first line being pasted edits the current input text and then sends it instead of being sent on its own without the existing input text. Add `CalcFontSizeAndBoundingBox` to UI to reduce duplicate code. Expose `CalcAlignedCursorPos` as static member function to reuse it for line input. Dispatch input events to UI inputs through the event handler instead of storing them in a duplicate buffer. Use `size_t` for line input cursor position, length etc. and for `str_utf8_stats`. Add `IButtonColorFunction` to UI to describe a functions that defines colors for the Default, Active and Hovered states of UI elements. Add some default button color functions. Use button color function to reduce duplicate code in scrollbar rendering. Use `vec2` instead of two `floats` to represent the mouse positions in the text renderer. Remove `CaretPosition` again, as it does not calculate the correct Y position near line breaks due to the wrapping being different when not rendering the entire string. Instead, calculate the exact caret position when rending a text container and store the caret position in the text cursor for later use. IME usage guide (Windows) ------------------------------ 1. Install the respective language and the Microsoft-IME keyboard (e.g. for Chinese, Japanese or Korean). 2. Launch the game (or a text editor to first try out the IME). Note that Windows may track the input language separately for every application. You can change this in the Windows input settings so the input language is changed globally. 2. Switch the input language using the hotkey Windows+Space or another hotkey that you configured in the Windows input settings (Alt+Shift is the default, but you should consider disabling it, to avoid accidentally changing the input language while playing). 3. Switch from Latin/English input mode to the respective asian input mode. - Chinese: Use Ctrl+Space to switch between English and Chinese input mode. You can change this hotkey in the IME's settings. - Japanese: Use Ctrl+Space to switch between Alphanumeric and Hiragana/Katakana input mode. You can change this hotkey in the IME's settings. - Korean: Use Right Alt to switch between English and Hangul input mode. You cannot change this hotkey as of yet. - Note that the input mode is also tracked per application, but there is no setting to change this behavior as far as I know, so you'll need to switch for every application separately. 4. Start typing. The underlined text is the current composition text. While a composition is active, you can only edit the composition text. Confirm the composition with Space or by selecting a candidate from the candidate list with the arrow keys. Cancel the composition with Escape or by using Backspace to delete the composition text. Note that not all languages offer a candidate list. SDL version-specific issues ------------------------------ - 2.26.5, 2.24.2, 2.0.22: IME candidates work. But there are minor bugs when moving the composition cursor. - 2.0.18, 2.0.20: IME candidates work. - 2.0.16 (our current version): IME candidates cannot be determined with Windows API. Windows tries to draw the composition window like before, so this does not work in fullscreen mode. - 2.0.8 (upstream 0.7): IME candidates work. But this SDL version is too old for us.
2023-01-03 21:28:38 +00:00
static CLineInput s_FilterServerAddressInput(g_Config.m_BrFilterServerAddress, sizeof(g_Config.m_BrFilterServerAddress));
if(UI()->DoEditBox(&s_FilterServerAddressInput, &Button, FontSize))
Client()->ServerBrowserUpdate();
// player country
{
CUIRect Flag;
View.HSplitTop(RowHeight, &Button, &View);
Button.VSplitRight(60.0f, &Button, &Flag);
if(DoButton_CheckBox(&g_Config.m_BrFilterCountry, Localize("Player country:"), g_Config.m_BrFilterCountry, &Button))
g_Config.m_BrFilterCountry ^= 1;
2011-08-11 08:59:14 +00:00
const float OldWidth = Flag.w;
Flag.w = Flag.h * 2.0f;
Flag.x += (OldWidth - Flag.w) / 2.0f;
m_pClient->m_CountryFlags.Render(g_Config.m_BrFilterCountryIndex, ColorRGBA(1.0f, 1.0f, 1.0f, UI()->HotItem() == &g_Config.m_BrFilterCountryIndex ? 1.0f : g_Config.m_BrFilterCountry ? 0.9f : 0.5f), Flag.x, Flag.y, Flag.w, Flag.h);
if(UI()->DoButtonLogic(&g_Config.m_BrFilterCountryIndex, 0, &Flag))
{
static SPopupMenuId s_PopupCountryId;
static SPopupCountrySelectionContext s_PopupCountryContext;
s_PopupCountryContext.m_pMenus = this;
s_PopupCountryContext.m_Selection = g_Config.m_BrFilterCountryIndex;
s_PopupCountryContext.m_New = true;
UI()->DoPopupMenu(&s_PopupCountryId, Flag.x, Flag.y + Flag.h, 490, 210, &s_PopupCountryContext, PopupCountrySelection);
}
}
View.HSplitTop(RowHeight, &Button, &View);
if(DoButton_CheckBox(&g_Config.m_BrFilterConnectingPlayers, Localize("Filter connecting players"), g_Config.m_BrFilterConnectingPlayers, &Button))
g_Config.m_BrFilterConnectingPlayers ^= 1;
2014-09-19 21:52:09 +00:00
// ddnet country filters
if(g_Config.m_UiPage == PAGE_DDNET)
{
View.HSplitTop(RowHeight, &Button, &View);
if(DoButton_CheckBox(&g_Config.m_BrIndicateFinished, Localize("Indicate map finish"), g_Config.m_BrIndicateFinished, &Button))
{
g_Config.m_BrIndicateFinished ^= 1;
if(g_Config.m_BrIndicateFinished)
ServerBrowser()->Refresh(ServerBrowser()->GetCurrentType());
}
if(g_Config.m_BrIndicateFinished)
{
View.HSplitTop(RowHeight, &Button, &View);
if(DoButton_CheckBox(&g_Config.m_BrFilterUnfinishedMap, Localize("Unfinished map"), g_Config.m_BrFilterUnfinishedMap, &Button))
g_Config.m_BrFilterUnfinishedMap ^= 1;
}
else
{
g_Config.m_BrFilterUnfinishedMap = 0;
}
}
if(g_Config.m_UiPage == PAGE_DDNET || g_Config.m_UiPage == PAGE_KOG)
{
const ColorRGBA ColorActive = ColorRGBA(0.0f, 0.0f, 0.0f, 0.3f);
const ColorRGBA ColorInactive = ColorRGBA(0.0f, 0.0f, 0.0f, 0.15f);
2014-09-19 21:52:09 +00:00
const int Network = g_Config.m_UiPage == PAGE_DDNET ? IServerBrowser::NETWORK_DDNET : IServerBrowser::NETWORK_KOG;
2014-09-19 21:52:09 +00:00
CUIRect TabContents, CountriesTab, TypesTab;
View.HSplitTop(6.0f, nullptr, &View);
View.HSplitTop(19.0f, &Button, &View);
View.HSplitTop(minimum(120.0f + CScrollRegion::HEIGHT_MAGIC_FIX, View.h), &TabContents, &View);
Button.VSplitMid(&CountriesTab, &TypesTab);
TabContents.Draw(ms_ColorTabbarInactive, IGraphics::CORNER_B, 4.0f);
2014-09-19 21:52:09 +00:00
enum EFilterTab
{
FILTERTAB_COUNTRIES = 0,
FILTERTAB_TYPES,
};
static EFilterTab s_ActiveTab = FILTERTAB_COUNTRIES;
2014-09-19 21:52:09 +00:00
2022-07-16 13:32:06 +00:00
static CButtonContainer s_CountriesButton;
if(DoButton_MenuTab(&s_CountriesButton, Localize("Countries"), s_ActiveTab == FILTERTAB_COUNTRIES, &CountriesTab, IGraphics::CORNER_TL, nullptr, &ColorInactive, &ColorActive, nullptr, 4.0f))
2014-12-14 15:45:18 +00:00
{
s_ActiveTab = FILTERTAB_COUNTRIES;
2014-12-14 15:45:18 +00:00
}
2014-09-19 21:52:09 +00:00
2022-07-16 13:32:06 +00:00
static CButtonContainer s_TypesButton;
if(DoButton_MenuTab(&s_TypesButton, Localize("Types"), s_ActiveTab == FILTERTAB_TYPES, &TypesTab, IGraphics::CORNER_TR, nullptr, &ColorInactive, &ColorActive, nullptr, 4.0f))
2014-09-19 21:52:09 +00:00
{
s_ActiveTab = FILTERTAB_TYPES;
2014-12-14 15:45:18 +00:00
}
2014-11-21 13:11:04 +00:00
if(s_ActiveTab == FILTERTAB_COUNTRIES)
2014-12-14 15:45:18 +00:00
{
RenderServerbrowserCountriesFilter(TabContents, Network);
}
else if(s_ActiveTab == FILTERTAB_TYPES)
{
RenderServerbrowserTypesFilter(TabContents, Network);
}
}
2014-09-19 21:52:09 +00:00
static CButtonContainer s_ResetButton;
if(DoButton_Menu(&s_ResetButton, Localize("Reset filter"), 0, &ResetButton))
{
g_Config.m_BrFilterString[0] = '\0';
g_Config.m_BrExcludeString[0] = '\0';
g_Config.m_BrFilterFull = 0;
g_Config.m_BrFilterEmpty = 0;
g_Config.m_BrFilterSpectators = 0;
g_Config.m_BrFilterFriends = 0;
g_Config.m_BrFilterCountry = 0;
g_Config.m_BrFilterCountryIndex = -1;
g_Config.m_BrFilterPw = 0;
g_Config.m_BrFilterGametype[0] = '\0';
g_Config.m_BrFilterGametypeStrict = 0;
g_Config.m_BrFilterConnectingPlayers = 1;
g_Config.m_BrFilterUnfinishedMap = 0;
g_Config.m_BrFilterServerAddress[0] = '\0';
g_Config.m_BrFilterExcludeCountries[0] = '\0';
g_Config.m_BrFilterExcludeTypes[0] = '\0';
if(g_Config.m_UiPage == PAGE_DDNET || g_Config.m_UiPage == PAGE_KOG)
ServerBrowser()->Refresh(ServerBrowser()->GetCurrentType());
else
Client()->ServerBrowserUpdate();
}
}
2014-09-19 21:52:09 +00:00
void CMenus::RenderServerbrowserDDNetFilter(CUIRect View,
char *pFilterExclude, int FilterExcludeSize,
float ItemHeight, int MaxItems, int ItemsPerRow,
CScrollRegion &ScrollRegion, std::vector<unsigned char> &vItemIds,
const std::function<const char *(int ItemIndex)> &GetItemName,
const std::function<void(int ItemIndex, CUIRect Item, const void *pItemId, bool Active)> &RenderItem)
{
vItemIds.resize(MaxItems);
2014-09-19 21:52:09 +00:00
vec2 ScrollOffset(0.0f, 0.0f);
CScrollRegionParams ScrollParams;
ScrollParams.m_ScrollbarWidth = 10.0f;
ScrollParams.m_ScrollbarMargin = 3.0f;
ScrollParams.m_ScrollUnit = 2.0f * ItemHeight;
ScrollRegion.Begin(&View, &ScrollOffset, &ScrollParams);
View.y += ScrollOffset.y;
CUIRect Row;
int ColumnIndex = 0;
for(int ItemIndex = 0; ItemIndex < MaxItems; ++ItemIndex)
{
CUIRect Item;
if(ColumnIndex == 0)
View.HSplitTop(ItemHeight, &Row, &View);
Row.VSplitLeft(View.w / ItemsPerRow, &Item, &Row);
ColumnIndex = (ColumnIndex + 1) % ItemsPerRow;
if(!ScrollRegion.AddRect(Item))
continue;
2014-09-19 21:52:09 +00:00
const void *pItemId = &vItemIds[ItemIndex];
const char *pName = GetItemName(ItemIndex);
const bool Active = !ServerBrowser()->DDNetFiltered(pFilterExclude, pName);
2014-09-19 21:52:09 +00:00
const int Click = UI()->DoButtonLogic(pItemId, 0, &Item);
if(Click == 1 || Click == 2)
{
// left/right click to toggle filter
if(pFilterExclude[0] == '\0')
2014-12-14 15:45:18 +00:00
{
if(Click == 1)
2014-09-19 21:52:09 +00:00
{
// Left click: when all are active, only activate one
for(int j = 0; j < MaxItems; ++j)
2014-12-14 15:45:18 +00:00
{
if(j != ItemIndex)
ServerBrowser()->DDNetFilterAdd(pFilterExclude, FilterExcludeSize, GetItemName(j));
}
}
else if(Click == 2)
{
// Right click: when all are active, only deactivate one
ServerBrowser()->DDNetFilterAdd(pFilterExclude, FilterExcludeSize, GetItemName(ItemIndex));
}
}
else
{
bool AllFilteredExceptUs = true;
for(int j = 0; j < MaxItems; ++j)
{
if(j != ItemIndex && !ServerBrowser()->DDNetFiltered(pFilterExclude, GetItemName(j)))
{
AllFilteredExceptUs = false;
break;
}
}
// when last one is removed, reset (re-enable all)
if(AllFilteredExceptUs)
{
pFilterExclude[0] = '\0';
}
else if(Active)
{
ServerBrowser()->DDNetFilterAdd(pFilterExclude, FilterExcludeSize, pName);
}
else
{
ServerBrowser()->DDNetFilterRem(pFilterExclude, FilterExcludeSize, pName);
2014-09-19 21:52:09 +00:00
}
2014-12-14 15:45:18 +00:00
}
ServerBrowser()->Refresh(ServerBrowser()->GetCurrentType());
2014-12-14 15:45:18 +00:00
}
else if(Click == 3)
2014-12-14 15:45:18 +00:00
{
// middle click to reset (re-enable all)
pFilterExclude[0] = '\0';
ServerBrowser()->Refresh(ServerBrowser()->GetCurrentType());
}
2014-12-14 15:45:18 +00:00
if(UI()->HotItem() == pItemId && !ScrollRegion.Animating())
Item.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, 0.33f), IGraphics::CORNER_ALL, 2.0f);
RenderItem(ItemIndex, Item, pItemId, Active);
}
2014-12-14 15:45:18 +00:00
ScrollRegion.End();
}
2014-12-14 15:45:18 +00:00
void CMenus::RenderServerbrowserCountriesFilter(CUIRect View, int Network)
{
char *pFilterExcludeCountries = Network == IServerBrowser::NETWORK_DDNET ? g_Config.m_BrFilterExcludeCountries : g_Config.m_BrFilterExcludeCountriesKoG;
const int FilterExcludeCountriesSize = Network == IServerBrowser::NETWORK_DDNET ? sizeof(g_Config.m_BrFilterExcludeCountries) : sizeof(g_Config.m_BrFilterExcludeCountriesKoG);
const int MaxEntries = ServerBrowser()->NumCountries(Network);
const int EntriesPerRow = MaxEntries > 8 ? 5 : 4;
2014-12-14 15:45:18 +00:00
static CScrollRegion s_ScrollRegion;
static std::vector<unsigned char> s_vItemIds;
2014-09-19 21:52:09 +00:00
const float ItemHeight = 20.0f;
const float Spacing = 2.0f;
2014-12-14 15:45:18 +00:00
const auto &&GetItemName = [&](int ItemIndex) {
return ServerBrowser()->GetCountryName(Network, ItemIndex);
};
const auto &&RenderItem = [&](int ItemIndex, CUIRect Item, const void *pItemId, bool Active) {
Item.Margin(Spacing, &Item);
const float OldWidth = Item.w;
Item.w = Item.h * 2.0f;
Item.x += (OldWidth - Item.w) / 2.0f;
const int FlagID = ServerBrowser()->GetCountryFlag(Network, ItemIndex);
m_pClient->m_CountryFlags.Render(FlagID, ColorRGBA(1.0f, 1.0f, 1.0f, (Active ? 0.9f : 0.2f) + (UI()->HotItem() == pItemId ? 0.1f : 0.0f)), Item.x, Item.y, Item.w, Item.h);
};
2014-12-14 15:45:18 +00:00
RenderServerbrowserDDNetFilter(View, pFilterExcludeCountries, FilterExcludeCountriesSize, ItemHeight + 2.0f * Spacing, MaxEntries, EntriesPerRow, s_ScrollRegion, s_vItemIds, GetItemName, RenderItem);
}
2014-12-14 15:45:18 +00:00
void CMenus::RenderServerbrowserTypesFilter(CUIRect View, int Network)
{
char *pFilterExcludeTypes = Network == IServerBrowser::NETWORK_DDNET ? g_Config.m_BrFilterExcludeTypes : g_Config.m_BrFilterExcludeTypesKoG;
const int FilterExcludeTypesSize = Network == IServerBrowser::NETWORK_DDNET ? sizeof(g_Config.m_BrFilterExcludeTypes) : sizeof(g_Config.m_BrFilterExcludeTypesKoG);
const int MaxEntries = ServerBrowser()->NumTypes(Network);
const int EntriesPerRow = 3;
2014-12-14 15:45:18 +00:00
static CScrollRegion s_ScrollRegion;
static std::vector<unsigned char> s_vItemIds;
2014-09-19 21:52:09 +00:00
const float ItemHeight = 13.0f;
const float Spacing = 2.0f;
2014-09-19 21:52:09 +00:00
const auto &&GetItemName = [&](int ItemIndex) {
return ServerBrowser()->GetType(Network, ItemIndex);
};
const auto &&RenderItem = [&](int ItemIndex, CUIRect Item, const void *pItemId, bool Active) {
Item.Margin(Spacing, &Item);
TextRender()->TextColor(1.0f, 1.0f, 1.0f, (Active ? 0.9f : 0.2f) + (UI()->HotItem() == pItemId ? 0.1f : 0.0f));
UI()->DoLabel(&Item, GetItemName(ItemIndex), Item.h * CUI::ms_FontmodHeight, TEXTALIGN_MC);
TextRender()->TextColor(TextRender()->DefaultTextColor());
};
RenderServerbrowserDDNetFilter(View, pFilterExcludeTypes, FilterExcludeTypesSize, ItemHeight + 2.0f * Spacing, MaxEntries, EntriesPerRow, s_ScrollRegion, s_vItemIds, GetItemName, RenderItem);
2008-09-04 18:42:26 +00:00
}
CUI::EPopupMenuFunctionResult CMenus::PopupCountrySelection(void *pContext, CUIRect View, bool Active)
{
SPopupCountrySelectionContext *pPopupContext = static_cast<SPopupCountrySelectionContext *>(pContext);
CMenus *pMenus = pPopupContext->m_pMenus;
static CListBox s_ListBox;
s_ListBox.SetActive(Active);
s_ListBox.DoStart(50.0f, pMenus->m_pClient->m_CountryFlags.Num(), 8, 1, -1, &View, false);
if(pPopupContext->m_New)
{
pPopupContext->m_New = false;
s_ListBox.ScrollToSelected();
}
for(size_t i = 0; i < pMenus->m_pClient->m_CountryFlags.Num(); ++i)
{
const CCountryFlags::CCountryFlag *pEntry = pMenus->m_pClient->m_CountryFlags.GetByIndex(i);
const CListboxItem Item = s_ListBox.DoNextItem(pEntry, pEntry->m_CountryCode == pPopupContext->m_Selection);
if(!Item.m_Visible)
continue;
CUIRect FlagRect, Label;
Item.m_Rect.Margin(5.0f, &FlagRect);
FlagRect.HSplitBottom(12.0f, &FlagRect, &Label);
Label.HSplitTop(2.0f, nullptr, &Label);
const float OldWidth = FlagRect.w;
FlagRect.w = FlagRect.h * 2.0f;
FlagRect.x += (OldWidth - FlagRect.w) / 2.0f;
pMenus->m_pClient->m_CountryFlags.Render(pEntry->m_CountryCode, ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f), FlagRect.x, FlagRect.y, FlagRect.w, FlagRect.h);
pMenus->UI()->DoLabel(&Label, pEntry->m_aCountryCodeString, 10.0f, TEXTALIGN_MC);
}
const int NewSelected = s_ListBox.DoEnd();
pPopupContext->m_Selection = NewSelected >= 0 ? pMenus->m_pClient->m_CountryFlags.GetByIndex(NewSelected)->m_CountryCode : -1;
if(s_ListBox.WasItemSelected() || s_ListBox.WasItemActivated())
{
g_Config.m_BrFilterCountry = 1;
g_Config.m_BrFilterCountryIndex = pPopupContext->m_Selection;
pMenus->Client()->ServerBrowserUpdate();
return CUI::POPUP_CLOSE_CURRENT;
}
return CUI::POPUP_KEEP_OPEN;
}
void CMenus::RenderServerbrowserInfo(CUIRect View)
2008-09-04 18:42:26 +00:00
{
2010-05-29 07:25:38 +00:00
const CServerInfo *pSelectedServer = ServerBrowser()->SortedGet(m_SelectedIndex);
const float RowHeight = 18.0f;
const float FontSize = (RowHeight - 4.0f) * CUI::ms_FontmodHeight; // based on DoButton_CheckBox
CUIRect ServerDetails, Scoreboard;
View.HSplitTop(4.0f * 15.0f + RowHeight + 2.0f * 5.0f + 2.0f * 2.0f, &ServerDetails, &Scoreboard);
ServerDetails.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.15f), IGraphics::CORNER_B, 4.0f);
if(pSelectedServer)
2008-09-04 18:42:26 +00:00
{
ServerDetails.Margin(5.0f, &ServerDetails);
// copy info button
{
CUIRect Button;
ServerDetails.HSplitBottom(15.0f, &ServerDetails, &Button);
static CButtonContainer s_CopyButton;
if(DoButton_Menu(&s_CopyButton, Localize("Copy info"), 0, &Button))
{
char aInfo[256];
pSelectedServer->InfoToString(aInfo, sizeof(aInfo));
Input()->SetClipboardText(aInfo);
}
}
// favorite checkbox
{
CUIRect ButtonAddFav, ButtonLeakIp;
ServerDetails.HSplitBottom(2.0f, &ServerDetails, nullptr);
ServerDetails.HSplitBottom(RowHeight, &ServerDetails, &ButtonAddFav);
ServerDetails.HSplitBottom(2.0f, &ServerDetails, nullptr);
ButtonAddFav.VSplitMid(&ButtonAddFav, &ButtonLeakIp);
2010-05-29 07:25:38 +00:00
static int s_AddFavButton = 0;
if(DoButton_CheckBox_Tristate(&s_AddFavButton, Localize("Favorite"), pSelectedServer->m_Favorite, &ButtonAddFav))
{
if(pSelectedServer->m_Favorite != TRISTATE::NONE)
{
Favorites()->Remove(pSelectedServer->m_aAddresses, pSelectedServer->m_NumAddresses);
}
2008-09-04 18:42:26 +00:00
else
{
Favorites()->Add(pSelectedServer->m_aAddresses, pSelectedServer->m_NumAddresses);
if(g_Config.m_UiPage == PAGE_LAN)
{
Favorites()->AllowPing(pSelectedServer->m_aAddresses, pSelectedServer->m_NumAddresses, true);
}
}
Client()->ServerBrowserUpdate();
}
if(pSelectedServer->m_Favorite != TRISTATE::NONE)
{
static int s_LeakIpButton = 0;
if(DoButton_CheckBox_Tristate(&s_LeakIpButton, Localize("Leak IP"), pSelectedServer->m_FavoriteAllowPing, &ButtonLeakIp))
{
Favorites()->AllowPing(pSelectedServer->m_aAddresses, pSelectedServer->m_NumAddresses, pSelectedServer->m_FavoriteAllowPing == TRISTATE::NONE);
}
Client()->ServerBrowserUpdate();
}
2008-09-04 18:42:26 +00:00
}
CUIRect LeftColumn, RightColumn, Row;
2010-05-29 07:25:38 +00:00
ServerDetails.VSplitLeft(80.0f, &LeftColumn, &RightColumn);
LeftColumn.HSplitTop(15.0f, &Row, &LeftColumn);
UI()->DoLabel(&Row, Localize("Version"), FontSize, TEXTALIGN_ML);
2010-05-29 07:25:38 +00:00
RightColumn.HSplitTop(15.0f, &Row, &RightColumn);
UI()->DoLabel(&Row, pSelectedServer->m_aVersion, FontSize, TEXTALIGN_ML);
LeftColumn.HSplitTop(15.0f, &Row, &LeftColumn);
UI()->DoLabel(&Row, Localize("Game type"), FontSize, TEXTALIGN_ML);
2010-05-29 07:25:38 +00:00
RightColumn.HSplitTop(15.0f, &Row, &RightColumn);
UI()->DoLabel(&Row, pSelectedServer->m_aGameType, FontSize, TEXTALIGN_ML);
LeftColumn.HSplitTop(15.0f, &Row, &LeftColumn);
UI()->DoLabel(&Row, Localize("Ping"), FontSize, TEXTALIGN_ML);
2010-05-29 07:25:38 +00:00
char aTemp[16];
FormatServerbrowserPing(aTemp, pSelectedServer);
2010-05-29 07:25:38 +00:00
RightColumn.HSplitTop(15.0f, &Row, &RightColumn);
UI()->DoLabel(&Row, aTemp, FontSize, TEXTALIGN_ML);
RenderServerbrowserInfoScoreboard(Scoreboard, pSelectedServer);
2008-09-04 18:42:26 +00:00
}
else
{
UI()->DoLabel(&ServerDetails, Localize("No server selected"), FontSize, TEXTALIGN_MC);
}
}
2010-05-29 07:25:38 +00:00
void CMenus::RenderServerbrowserInfoScoreboard(CUIRect View, const CServerInfo *pSelectedServer)
{
const float FontSize = 10.0f;
2010-05-29 07:25:38 +00:00
static CListBox s_ListBox;
View.VSplitLeft(5.0f, nullptr, &View);
if(!s_ListBox.ScrollbarShown())
View.VSplitRight(5.0f, &View, nullptr);
s_ListBox.DoAutoSpacing(1.0f);
s_ListBox.SetScrollbarWidth(16.0f);
s_ListBox.SetScrollbarMargin(5.0f);
s_ListBox.DoStart(25.0f, pSelectedServer->m_NumReceivedClients, 1, 3, -1, &View);
for(int i = 0; i < pSelectedServer->m_NumReceivedClients; i++)
2008-09-04 18:42:26 +00:00
{
const CServerInfo::CClient &CurrentClient = pSelectedServer->m_aClients[i];
const CListboxItem Item = s_ListBox.DoNextItem(&CurrentClient);
if(!Item.m_Visible)
continue;
CUIRect Skin, Name, Clan, Score, Flag;
Name = Item.m_Rect;
ColorRGBA Color = CurrentClient.m_FriendState == IFriends::FRIEND_NO ?
ColorRGBA(1.0f, 1.0f, 1.0f, (i % 2 + 1) * 0.05f) :
ColorRGBA(0.5f, 1.0f, 0.5f, 0.15f + (i % 2 + 1) * 0.05f);
Name.Draw(Color, IGraphics::CORNER_ALL, 4.0f);
Name.VSplitLeft(1.0f, nullptr, &Name);
Name.VSplitLeft(34.0f, &Score, &Name);
Name.VSplitLeft(18.0f, &Skin, &Name);
Name.VSplitRight(26.0f, &Name, &Flag);
Flag.HMargin(6.0f, &Flag);
Name.HSplitTop(12.0f, &Name, &Clan);
// score
char aTemp[16];
if(!CurrentClient.m_Player)
{
str_copy(aTemp, "SPEC");
}
else if(pSelectedServer->m_ClientScoreKind == CServerInfo::CLIENT_SCORE_KIND_POINTS)
{
str_from_int(CurrentClient.m_Score, aTemp);
}
else
{
std::optional<int> Time = {};
if(pSelectedServer->m_ClientScoreKind == CServerInfo::CLIENT_SCORE_KIND_TIME_BACKCOMPAT)
2014-02-19 13:24:26 +00:00
{
const int TempTime = absolute(CurrentClient.m_Score);
if(TempTime != 0 && TempTime != 9999)
Time = TempTime;
}
2014-02-19 13:24:26 +00:00
else
{
// CServerInfo::CLIENT_SCORE_KIND_POINTS
if(CurrentClient.m_Score >= 0)
Time = CurrentClient.m_Score;
}
2014-02-19 13:24:26 +00:00
if(Time.has_value())
2023-01-07 08:20:25 +00:00
{
str_time((int64_t)Time.value() * 100, TIME_HOURS, aTemp, sizeof(aTemp));
}
else
{
aTemp[0] = '\0';
2023-01-07 08:20:25 +00:00
}
}
2014-01-10 11:22:48 +00:00
UI()->DoLabel(&Score, aTemp, FontSize, TEXTALIGN_ML);
// render tee if available
if(CurrentClient.m_aSkin[0] != '\0')
{
const CTeeRenderInfo TeeInfo = GetTeeRenderInfo(vec2(Skin.w, Skin.h), CurrentClient.m_aSkin, CurrentClient.m_CustomSkinColors, CurrentClient.m_CustomSkinColorBody, CurrentClient.m_CustomSkinColorFeet);
const CAnimState *pIdleState = CAnimState::GetIdle();
vec2 OffsetToMid;
RenderTools()->GetRenderTeeOffsetToRenderedTee(pIdleState, &TeeInfo, OffsetToMid);
const vec2 TeeRenderPos = vec2(Skin.x + TeeInfo.m_Size / 2.0f, Skin.y + Skin.h / 2.0f + OffsetToMid.y);
RenderTools()->RenderTee(pIdleState, &TeeInfo, EMOTE_NORMAL, vec2(1.0f, 0.0f), TeeRenderPos);
}
// name
CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, Name.x, Name.y + (Name.h - (FontSize - 1.0f)) / 2.0f, FontSize - 1.0f, TEXTFLAG_RENDER | TEXTFLAG_STOP_AT_END);
Cursor.m_LineWidth = Name.w;
const char *pName = CurrentClient.m_aName;
bool Printed = false;
if(g_Config.m_BrFilterString[0])
Printed = PrintHighlighted(pName, [&](const char *pFilteredStr, const int FilterLen) {
TextRender()->TextEx(&Cursor, pName, (int)(pFilteredStr - pName));
TextRender()->TextColor(gs_HighlightedTextColor);
TextRender()->TextEx(&Cursor, pFilteredStr, FilterLen);
TextRender()->TextColor(TextRender()->DefaultTextColor());
TextRender()->TextEx(&Cursor, pFilteredStr + FilterLen, -1);
});
if(!Printed)
TextRender()->TextEx(&Cursor, pName, -1);
// clan
TextRender()->SetCursor(&Cursor, Clan.x, Clan.y + (Clan.h - (FontSize - 2.0f)) / 2.0f, FontSize - 2.0f, TEXTFLAG_RENDER | TEXTFLAG_STOP_AT_END);
Cursor.m_LineWidth = Clan.w;
const char *pClan = CurrentClient.m_aClan;
Printed = false;
if(g_Config.m_BrFilterString[0])
Printed = PrintHighlighted(pClan, [&](const char *pFilteredStr, const int FilterLen) {
TextRender()->TextEx(&Cursor, pClan, (int)(pFilteredStr - pClan));
TextRender()->TextColor(0.4f, 0.4f, 1.0f, 1.0f);
TextRender()->TextEx(&Cursor, pFilteredStr, FilterLen);
TextRender()->TextColor(TextRender()->DefaultTextColor());
TextRender()->TextEx(&Cursor, pFilteredStr + FilterLen, -1);
});
if(!Printed)
TextRender()->TextEx(&Cursor, pClan, -1);
// flag
m_pClient->m_CountryFlags.Render(CurrentClient.m_Country, ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f), Flag.x, Flag.y, Flag.w, Flag.h);
}
const int NewSelected = s_ListBox.DoEnd();
if(s_ListBox.WasItemSelected())
{
const CServerInfo::CClient &SelectedClient = pSelectedServer->m_aClients[NewSelected];
if(SelectedClient.m_FriendState == IFriends::FRIEND_PLAYER)
m_pClient->Friends()->RemoveFriend(SelectedClient.m_aName, SelectedClient.m_aClan);
else
m_pClient->Friends()->AddFriend(SelectedClient.m_aName, SelectedClient.m_aClan);
FriendlistOnUpdate();
Client()->ServerBrowserUpdate();
}
2011-06-26 15:10:13 +00:00
}
2011-03-23 12:06:35 +00:00
void CMenus::RenderServerbrowserFriends(CUIRect View)
2008-09-04 18:42:26 +00:00
{
2011-06-26 15:10:13 +00:00
const float FontSize = 10.0f;
static bool s_aListExtended[NUM_FRIEND_TYPES] = {true, true, false};
static const ColorRGBA s_aListColors[NUM_FRIEND_TYPES] = {ColorRGBA(0.5f, 1.0f, 0.5f, 1.0f), ColorRGBA(0.4f, 0.4f, 1.0f, 1.0f), ColorRGBA(1.0f, 0.5f, 0.5f, 1.0f)};
const ColorRGBA OfflineClanColor = ColorRGBA(0.7f, 0.45f, 0.75f, 1.0f);
const float SpacingH = 2.0f;
2011-03-23 12:06:35 +00:00
CUIRect List, ServerFriends;
View.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.15f), IGraphics::CORNER_NONE, 0.0f);
View.HSplitBottom(70.0f, &List, &ServerFriends);
List.HSplitTop(5.0f, nullptr, &List);
List.VSplitLeft(5.0f, nullptr, &List);
2010-05-29 07:25:38 +00:00
// calculate friends
// TODO: optimize this
m_pRemoveFriend = nullptr;
for(auto &vFriends : m_avFriends)
vFriends.clear();
for(int FriendIndex = 0; FriendIndex < m_pClient->Friends()->NumFriends(); ++FriendIndex)
2011-03-23 12:06:35 +00:00
{
m_avFriends[FRIEND_OFF].emplace_back(m_pClient->Friends()->GetFriend(FriendIndex));
2011-03-23 12:06:35 +00:00
}
for(int ServerIndex = 0; ServerIndex < ServerBrowser()->NumSortedServers(); ++ServerIndex)
{
const CServerInfo *pEntry = ServerBrowser()->SortedGet(ServerIndex);
if(pEntry->m_FriendState == IFriends::FRIEND_NO)
continue;
2011-06-26 15:10:13 +00:00
for(int ClientIndex = 0; ClientIndex < pEntry->m_NumClients; ++ClientIndex)
{
const CServerInfo::CClient &CurrentClient = pEntry->m_aClients[ClientIndex];
if(CurrentClient.m_FriendState == IFriends::FRIEND_NO)
continue;
const int FriendIndex = CurrentClient.m_FriendState == IFriends::FRIEND_PLAYER ? FRIEND_PLAYER_ON : FRIEND_CLAN_ON;
m_avFriends[FriendIndex].emplace_back(CurrentClient, pEntry);
const auto &&RemovalPredicate = [CurrentClient](const CFriendItem &Friend) {
return (Friend.Name()[0] == '\0' || str_comp(Friend.Name(), CurrentClient.m_aName) == 0) && ((Friend.Name()[0] != '\0' && g_Config.m_ClFriendsIgnoreClan) || str_comp(Friend.Clan(), CurrentClient.m_aClan) == 0);
};
m_avFriends[FRIEND_OFF].erase(std::remove_if(m_avFriends[FRIEND_OFF].begin(), m_avFriends[FRIEND_OFF].end(), RemovalPredicate), m_avFriends[FRIEND_OFF].end());
}
}
for(auto &vFriends : m_avFriends)
std::sort(vFriends.begin(), vFriends.end());
// friends list
static CScrollRegion s_ScrollRegion;
if(!s_ScrollRegion.ScrollbarShown())
List.VSplitRight(5.0f, &List, nullptr);
vec2 ScrollOffset(0.0f, 0.0f);
CScrollRegionParams ScrollParams;
ScrollParams.m_ScrollbarWidth = 16.0f;
ScrollParams.m_ScrollbarMargin = 5.0f;
ScrollParams.m_ScrollUnit = 80.0f;
s_ScrollRegion.Begin(&List, &ScrollOffset, &ScrollParams);
List.y += ScrollOffset.y;
char aBuf[256];
for(size_t FriendType = 0; FriendType < NUM_FRIEND_TYPES; ++FriendType)
2011-06-26 15:10:13 +00:00
{
// header
CUIRect Header, GroupIcon, GroupLabel;
List.HSplitTop(ms_ListheaderHeight, &Header, &List);
s_ScrollRegion.AddRect(Header);
Header.Draw(ColorRGBA(1.0f, 1.0f, 1.0f, UI()->HotItem() == &s_aListExtended[FriendType] ? 0.4f : 0.25f), IGraphics::CORNER_ALL, 5.0f);
Header.VSplitLeft(Header.h, &GroupIcon, &GroupLabel);
GroupIcon.Margin(2.0f, &GroupIcon);
Add font index, support font family variants depending on language Add `fonts/index.json` which specifies: - List of all font files that should be loaded (filenames). - Default font (specified by family name or by family and style name). - Font variants for different languages, using the name of the language file as key. - Fallback fonts. - Icon font. There are characters (e.g. all in `刃直海角骨入`) that look different depending on the language of the content being Japanese, Simplified Chinese, Traditional Chinese and Hangul, because Unicode uses the same codepoint for characters regardless of the language. To render these characters correctly, the active variant font is switched depending on the selected language. The `ITextRender` interface is changed so the current language variant can be set using `SetFontLanguageVariant` and the default and icon fonts can be toggled using `SetFontPreset`. The class `CFont` is removed entirely. The text render is restructured: The font faces and font atlas are now managed by a separate class `CGlyphMap` like on upstream. As the text fill and outline textures always have the same size, the texture skyline only needs to be stored once and free positions in the atlas only need to be calculated once for each glyph instead of separately for the fill and outline textures. The font files and their licenses are also updated: - Update Source Han Sans to version 2.001. - Update Glow Sans Japanese Compressed to version 0.93. - Update Deja Vu Sans to version 2.37. - Update Font Awesome icons font to March 2023 version. Closes #6881.
2023-07-17 13:49:41 +00:00
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
TextRender()->TextColor(UI()->HotItem() == &s_aListExtended[FriendType] ? TextRender()->DefaultTextColor() : ColorRGBA(0.6f, 0.6f, 0.6f, 1.0f));
UI()->DoLabel(&GroupIcon, s_aListExtended[FriendType] ? FONT_ICON_SQUARE_MINUS : FONT_ICON_SQUARE_PLUS, GroupIcon.h * CUI::ms_FontmodHeight, TEXTALIGN_MC);
TextRender()->TextColor(TextRender()->DefaultTextColor());
Add font index, support font family variants depending on language Add `fonts/index.json` which specifies: - List of all font files that should be loaded (filenames). - Default font (specified by family name or by family and style name). - Font variants for different languages, using the name of the language file as key. - Fallback fonts. - Icon font. There are characters (e.g. all in `刃直海角骨入`) that look different depending on the language of the content being Japanese, Simplified Chinese, Traditional Chinese and Hangul, because Unicode uses the same codepoint for characters regardless of the language. To render these characters correctly, the active variant font is switched depending on the selected language. The `ITextRender` interface is changed so the current language variant can be set using `SetFontLanguageVariant` and the default and icon fonts can be toggled using `SetFontPreset`. The class `CFont` is removed entirely. The text render is restructured: The font faces and font atlas are now managed by a separate class `CGlyphMap` like on upstream. As the text fill and outline textures always have the same size, the texture skyline only needs to be stored once and free positions in the atlas only need to be calculated once for each glyph instead of separately for the fill and outline textures. The font files and their licenses are also updated: - Update Source Han Sans to version 2.001. - Update Glow Sans Japanese Compressed to version 0.93. - Update Deja Vu Sans to version 2.37. - Update Font Awesome icons font to March 2023 version. Closes #6881.
2023-07-17 13:49:41 +00:00
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
switch(FriendType)
2011-06-26 15:10:13 +00:00
{
case FRIEND_PLAYER_ON:
str_format(aBuf, sizeof(aBuf), Localize("Online players (%d)"), (int)m_avFriends[FriendType].size());
break;
case FRIEND_CLAN_ON:
str_format(aBuf, sizeof(aBuf), Localize("Online clanmates (%d)"), (int)m_avFriends[FriendType].size());
break;
case FRIEND_OFF:
str_format(aBuf, sizeof(aBuf), Localize("Offline (%d)", "friends (server browser)"), (int)m_avFriends[FriendType].size());
break;
default:
dbg_assert(false, "FriendType invalid");
break;
}
UI()->DoLabel(&GroupLabel, aBuf, FontSize, TEXTALIGN_ML);
if(UI()->DoButtonLogic(&s_aListExtended[FriendType], 0, &Header))
{
s_aListExtended[FriendType] = !s_aListExtended[FriendType];
}
// entries
if(s_aListExtended[FriendType])
{
for(size_t FriendIndex = 0; FriendIndex < m_avFriends[FriendType].size(); ++FriendIndex)
2011-06-26 15:10:13 +00:00
{
2023-05-19 13:14:46 +00:00
// space
{
CUIRect Space;
List.HSplitTop(SpacingH, &Space, &List);
s_ScrollRegion.AddRect(Space);
}
2023-05-19 13:14:46 +00:00
CUIRect Rect;
const auto &Friend = m_avFriends[FriendType][FriendIndex];
List.HSplitTop(11.0f + 10.0f + 2 * 2.0f + 1.0f + (Friend.ServerInfo() == nullptr ? 0.0f : 10.0f), &Rect, &List);
2023-05-19 13:14:46 +00:00
s_ScrollRegion.AddRect(Rect);
if(s_ScrollRegion.RectClipped(Rect))
continue;
const bool Inside = UI()->HotItem() == Friend.ListItemId() || UI()->HotItem() == Friend.RemoveButtonId();
bool ButtonResult = UI()->DoButtonLogic(Friend.ListItemId(), 0, &Rect);
if(Friend.ServerInfo())
{
GameClient()->m_Tooltips.DoToolTip(Friend.ListItemId(), &Rect, Localize("Click to select server. Double click to join your friend."));
}
const bool OfflineClan = Friend.FriendState() == IFriends::FRIEND_CLAN && FriendType == FRIEND_OFF;
Rect.Draw((OfflineClan ? OfflineClanColor : s_aListColors[FriendType]).WithAlpha(Inside ? 0.5f : 0.3f), IGraphics::CORNER_ALL, 5.0f);
Rect.Margin(2.0f, &Rect);
CUIRect RemoveButton, NameLabel, ClanLabel, InfoLabel;
Rect.HSplitTop(16.0f, &RemoveButton, nullptr);
RemoveButton.VSplitRight(13.0f, nullptr, &RemoveButton);
RemoveButton.HMargin((RemoveButton.h - RemoveButton.w) / 2.0f, &RemoveButton);
Rect.VSplitLeft(2.0f, nullptr, &Rect);
if(Friend.ServerInfo())
Rect.HSplitBottom(10.0f, &Rect, &InfoLabel);
Rect.HSplitTop(11.0f + 10.0f, &Rect, nullptr);
// tee
if(Friend.Skin()[0] != '\0')
{
CUIRect Skin;
Rect.VSplitLeft(Rect.h, &Skin, &Rect);
Rect.VSplitLeft(2.0f, nullptr, &Rect);
const CTeeRenderInfo TeeInfo = GetTeeRenderInfo(vec2(Skin.w, Skin.h), Friend.Skin(), Friend.CustomSkinColors(), Friend.CustomSkinColorBody(), Friend.CustomSkinColorFeet());
const CAnimState *pIdleState = CAnimState::GetIdle();
vec2 OffsetToMid;
RenderTools()->GetRenderTeeOffsetToRenderedTee(pIdleState, &TeeInfo, OffsetToMid);
const vec2 TeeRenderPos = vec2(Skin.x + Skin.w / 2.0f, Skin.y + Skin.h * 0.55f + OffsetToMid.y);
RenderTools()->RenderTee(pIdleState, &TeeInfo, EMOTE_NORMAL, vec2(1.0f, 0.0f), TeeRenderPos);
}
Rect.HSplitTop(11.0f, &NameLabel, &ClanLabel);
// name
UI()->DoLabel(&NameLabel, Friend.Name(), FontSize - 1.0f, TEXTALIGN_ML);
// clan
UI()->DoLabel(&ClanLabel, Friend.Clan(), FontSize - 2.0f, TEXTALIGN_ML);
// server info
if(Friend.ServerInfo())
2011-06-26 15:10:13 +00:00
{
// official server icon
if(Friend.ServerInfo()->m_Official)
{
CUIRect OfficialIcon;
InfoLabel.VSplitLeft(InfoLabel.h, &OfficialIcon, &InfoLabel);
InfoLabel.VSplitLeft(1.0f, nullptr, &InfoLabel); // spacing
OfficialIcon.HSplitTop(1.0f, nullptr, &OfficialIcon); // alignment
SLabelProperties Props;
Props.m_EnableWidthCheck = false;
Add font index, support font family variants depending on language Add `fonts/index.json` which specifies: - List of all font files that should be loaded (filenames). - Default font (specified by family name or by family and style name). - Font variants for different languages, using the name of the language file as key. - Fallback fonts. - Icon font. There are characters (e.g. all in `刃直海角骨入`) that look different depending on the language of the content being Japanese, Simplified Chinese, Traditional Chinese and Hangul, because Unicode uses the same codepoint for characters regardless of the language. To render these characters correctly, the active variant font is switched depending on the selected language. The `ITextRender` interface is changed so the current language variant can be set using `SetFontLanguageVariant` and the default and icon fonts can be toggled using `SetFontPreset`. The class `CFont` is removed entirely. The text render is restructured: The font faces and font atlas are now managed by a separate class `CGlyphMap` like on upstream. As the text fill and outline textures always have the same size, the texture skyline only needs to be stored once and free positions in the atlas only need to be calculated once for each glyph instead of separately for the fill and outline textures. The font files and their licenses are also updated: - Update Source Han Sans to version 2.001. - Update Glow Sans Japanese Compressed to version 0.93. - Update Deja Vu Sans to version 2.37. - Update Font Awesome icons font to March 2023 version. Closes #6881.
2023-07-17 13:49:41 +00:00
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
TextRender()->TextColor(0.4f, 0.7f, 0.94f, 1.0f);
TextRender()->TextOutlineColor(0.0f, 0.0f, 0.0f, 1.0f);
UI()->DoLabel(&OfficialIcon, FONT_ICON_CERTIFICATE, OfficialIcon.h, TEXTALIGN_MC, Props);
TextRender()->TextColor(0.0f, 0.0f, 0.0f, 1.0f);
TextRender()->TextOutlineColor(0.0f, 0.0f, 0.0f, 0.0f);
UI()->DoLabel(&OfficialIcon, FONT_ICON_CHECK, OfficialIcon.h * 0.5f, TEXTALIGN_MC, Props);
TextRender()->TextColor(TextRender()->DefaultTextColor());
TextRender()->TextOutlineColor(TextRender()->DefaultTextOutlineColor());
TextRender()->SetRenderFlags(0);
Add font index, support font family variants depending on language Add `fonts/index.json` which specifies: - List of all font files that should be loaded (filenames). - Default font (specified by family name or by family and style name). - Font variants for different languages, using the name of the language file as key. - Fallback fonts. - Icon font. There are characters (e.g. all in `刃直海角骨入`) that look different depending on the language of the content being Japanese, Simplified Chinese, Traditional Chinese and Hangul, because Unicode uses the same codepoint for characters regardless of the language. To render these characters correctly, the active variant font is switched depending on the selected language. The `ITextRender` interface is changed so the current language variant can be set using `SetFontLanguageVariant` and the default and icon fonts can be toggled using `SetFontPreset`. The class `CFont` is removed entirely. The text render is restructured: The font faces and font atlas are now managed by a separate class `CGlyphMap` like on upstream. As the text fill and outline textures always have the same size, the texture skyline only needs to be stored once and free positions in the atlas only need to be calculated once for each glyph instead of separately for the fill and outline textures. The font files and their licenses are also updated: - Update Source Han Sans to version 2.001. - Update Glow Sans Japanese Compressed to version 0.93. - Update Deja Vu Sans to version 2.37. - Update Font Awesome icons font to March 2023 version. Closes #6881.
2023-07-17 13:49:41 +00:00
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
}
// server info text
char aLatency[16];
FormatServerbrowserPing(aLatency, Friend.ServerInfo());
if(aLatency[0] != '\0')
str_format(aBuf, sizeof(aBuf), "%s | %s | %s", Friend.ServerInfo()->m_aMap, Friend.ServerInfo()->m_aGameType, aLatency);
else
str_format(aBuf, sizeof(aBuf), "%s | %s", Friend.ServerInfo()->m_aMap, Friend.ServerInfo()->m_aGameType);
UI()->DoLabel(&InfoLabel, aBuf, FontSize - 2.0f, TEXTALIGN_ML);
}
// remove button
if(Inside)
{
TextRender()->TextColor(UI()->HotItem() == Friend.RemoveButtonId() ? TextRender()->DefaultTextColor() : ColorRGBA(0.4f, 0.4f, 0.4f, 1.0f));
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
UI()->DoLabel(&RemoveButton, FONT_ICON_TRASH, RemoveButton.h * CUI::ms_FontmodHeight, TEXTALIGN_MC);
TextRender()->SetRenderFlags(0);
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
TextRender()->TextColor(TextRender()->DefaultTextColor());
if(UI()->DoButtonLogic(Friend.RemoveButtonId(), 0, &RemoveButton))
{
m_pRemoveFriend = &Friend;
ButtonResult = false;
}
GameClient()->m_Tooltips.DoToolTip(Friend.RemoveButtonId(), &RemoveButton, Friend.FriendState() == IFriends::FRIEND_PLAYER ? Localize("Click to remove this player from your friends list.") : Localize("Click to remove this clan from your friends list."));
}
// handle click and double click on item
if(ButtonResult && Friend.ServerInfo())
{
str_copy(g_Config.m_UiServerAddress, Friend.ServerInfo()->m_aAddress);
m_ServerBrowserShouldRevealSelection = true;
if(Input()->MouseDoubleClick())
2011-06-26 15:10:13 +00:00
{
Connect(g_Config.m_UiServerAddress);
2011-06-26 15:10:13 +00:00
}
}
}
if(m_avFriends[FriendType].empty())
{
CUIRect Label;
List.HSplitTop(12.0f, &Label, &List);
s_ScrollRegion.AddRect(Label);
UI()->DoLabel(&Label, Localize("None"), Label.h * CUI::ms_FontmodHeight, TEXTALIGN_ML);
}
2011-06-26 15:10:13 +00:00
}
2011-03-23 12:06:35 +00:00
// space
{
CUIRect Space;
List.HSplitTop(SpacingH, &Space, &List);
s_ScrollRegion.AddRect(Space);
}
2011-03-23 12:06:35 +00:00
}
s_ScrollRegion.End();
if(m_pRemoveFriend != nullptr)
{
char aMessage[256];
str_format(aMessage, sizeof(aMessage),
m_pRemoveFriend->FriendState() == IFriends::FRIEND_PLAYER ? Localize("Are you sure that you want to remove the player '%s' from your friends list?") : Localize("Are you sure that you want to remove the clan '%s' from your friends list?"),
m_pRemoveFriend->FriendState() == IFriends::FRIEND_PLAYER ? m_pRemoveFriend->Name() : m_pRemoveFriend->Clan());
PopupConfirm(Localize("Remove friend"), aMessage, Localize("Yes"), Localize("No"), &CMenus::PopupConfirmRemoveFriend);
}
2011-03-23 12:06:35 +00:00
// add friend
if(m_pClient->Friends()->NumFriends() < IFriends::MAX_FRIENDS)
{
CUIRect Button;
ServerFriends.Margin(5.0f, &ServerFriends);
ServerFriends.HSplitTop(18.0f, &Button, &ServerFriends);
2011-03-23 12:06:35 +00:00
str_format(aBuf, sizeof(aBuf), "%s:", Localize("Name"));
UI()->DoLabel(&Button, aBuf, FontSize + 2.0f, TEXTALIGN_ML);
Button.VSplitLeft(80.0f, nullptr, &Button);
Port line input and IME support from 0.7 Port the line input (UI edit boxes, chat, console) and Input Method Editor (IME) support from upstream. Closes #4397. General ------------------------------ Fix issues with the text input. Closes #4346. Closes #4524. Word skipping (when holding Ctrl) is overhauled to be consistent with the Windows / Firefox experience that I took as reference. Improve usability by not blinking (i.e. always rendering) the caret shortly after is has been moved. UI text input ------------------------------ Fix inconsistent mouse-based left and right scrolling (closes #4347). Support smooth left and right scrolling. Chat ------------------------------ Support keyboard-based text selection of the chat input. Mouse-based selection could be support in the future when we decide to add something like an ingame UI cursor. Support smooth up and down scrolling of the chat input, removing the old hack that offsets the input string to simulate scrolling. Console ------------------------------ Also support mouse-based text selection of the command input. Only text from either the command input or the console log can be selected at the same time. This ensures that Ctrl+C will always copy the text that is currently visually selected in the console. Check for Ctrl+C input event in event handler instead of in render function, to hopefully fix the issue that copying does not work sometimes (closes #5974 until further notice). When Ctrl+C is used to copy text from the console log, the selection is cleared. This should make it more clear when text was copied from the log. Fix an issue that was preventing the console log selection from being cleared, when all log lines are selected. Remove Ctrl+A/E hotkeys that move cursor to beginning/end respectively. Ctrl+A now selectes all text like for all other inputs. Home and End keys can still be used to go the beginning and end. Remove Ctrl+U/K hotkeys that clear everything before/after the cursor respectively. Hold shift and use Home/End to select everything instead. IME support ------------------------------ Render list of IME candidates in the client on Windows, so the candidate list can also be viewed in fullscreen mode. There is no API available to retrieve a candidate list on the other operating systems. Improve composition rendering by underlining the composition text instead of putting it in square brackets. Track active input globally to properly activate and deactivate IME through the SDL functions. Closes #1030. Closes #1008. Password rendering ------------------------------ Fix rendering of passwords containing unicode. Instead of rendering one star character for each UTF-8 `char`, render on star for every unicode codepoint. Show the composition text also for passwords. Without seeing the composition text it's hard to type a password containing those characters. The candidate window exposes the composition anyway. If you don't want to expose your password this way, e.g. while streaming, you could: 1. Use a latin password and switch off the IME for the password input with the IME hotkey. 2. Blank your screen with an external program while you are streaming and entering passwords. 3. Use binds to authenticate in rcon or to set the server browser password. Refactoring ------------------------------ Move all text input logic and general rendering to `CLineInput`. A `CLineInput` is associated with a particular `char` buffer given as a pointer either in the constructor or with `SetBuffer`. The maximum byte size of the buffer must also be specified. The maximum length in unicode codepoints can also be specified separately (e.g. on upstream, name are limited by the number of unicode codepoints instead). Add `CLineInputBuffered`, which is a `CLineInput` that own a `char` buffer of a fixed size, which is specified as a template argument. As `CLineInput` does not own a buffer anymore, this reduces duplicate code for line inputs that need their own buffer. Add `CLineInputNumber` which has additional convenience functions to consider the text as an `int` or `float`, to reduce duplicate code in those cases. In the future we could also add an input filter function so that only numbers can be entered in the number input. Add `CLineInput::SetClipboardLineCallback` to handle the case that multiple lines of text are pasted into a lineinput. This reduces duplicate code, as this behavior was previously implemented separately for chat and console. The behavior is also fixed to be consistent with the console on Windows, so the first line being pasted edits the current input text and then sends it instead of being sent on its own without the existing input text. Add `CalcFontSizeAndBoundingBox` to UI to reduce duplicate code. Expose `CalcAlignedCursorPos` as static member function to reuse it for line input. Dispatch input events to UI inputs through the event handler instead of storing them in a duplicate buffer. Use `size_t` for line input cursor position, length etc. and for `str_utf8_stats`. Add `IButtonColorFunction` to UI to describe a functions that defines colors for the Default, Active and Hovered states of UI elements. Add some default button color functions. Use button color function to reduce duplicate code in scrollbar rendering. Use `vec2` instead of two `floats` to represent the mouse positions in the text renderer. Remove `CaretPosition` again, as it does not calculate the correct Y position near line breaks due to the wrapping being different when not rendering the entire string. Instead, calculate the exact caret position when rending a text container and store the caret position in the text cursor for later use. IME usage guide (Windows) ------------------------------ 1. Install the respective language and the Microsoft-IME keyboard (e.g. for Chinese, Japanese or Korean). 2. Launch the game (or a text editor to first try out the IME). Note that Windows may track the input language separately for every application. You can change this in the Windows input settings so the input language is changed globally. 2. Switch the input language using the hotkey Windows+Space or another hotkey that you configured in the Windows input settings (Alt+Shift is the default, but you should consider disabling it, to avoid accidentally changing the input language while playing). 3. Switch from Latin/English input mode to the respective asian input mode. - Chinese: Use Ctrl+Space to switch between English and Chinese input mode. You can change this hotkey in the IME's settings. - Japanese: Use Ctrl+Space to switch between Alphanumeric and Hiragana/Katakana input mode. You can change this hotkey in the IME's settings. - Korean: Use Right Alt to switch between English and Hangul input mode. You cannot change this hotkey as of yet. - Note that the input mode is also tracked per application, but there is no setting to change this behavior as far as I know, so you'll need to switch for every application separately. 4. Start typing. The underlined text is the current composition text. While a composition is active, you can only edit the composition text. Confirm the composition with Space or by selecting a candidate from the candidate list with the arrow keys. Cancel the composition with Escape or by using Backspace to delete the composition text. Note that not all languages offer a candidate list. SDL version-specific issues ------------------------------ - 2.26.5, 2.24.2, 2.0.22: IME candidates work. But there are minor bugs when moving the composition cursor. - 2.0.18, 2.0.20: IME candidates work. - 2.0.16 (our current version): IME candidates cannot be determined with Windows API. Windows tries to draw the composition window like before, so this does not work in fullscreen mode. - 2.0.8 (upstream 0.7): IME candidates work. But this SDL version is too old for us.
2023-01-03 21:28:38 +00:00
static CLineInputBuffered<MAX_NAME_LENGTH> s_NameInput;
UI()->DoEditBox(&s_NameInput, &Button, FontSize + 2.0f);
2011-03-23 12:06:35 +00:00
ServerFriends.HSplitTop(3.0f, nullptr, &ServerFriends);
ServerFriends.HSplitTop(18.0f, &Button, &ServerFriends);
2011-03-23 12:06:35 +00:00
str_format(aBuf, sizeof(aBuf), "%s:", Localize("Clan"));
UI()->DoLabel(&Button, aBuf, FontSize + 2.0f, TEXTALIGN_ML);
Button.VSplitLeft(80.0f, nullptr, &Button);
Port line input and IME support from 0.7 Port the line input (UI edit boxes, chat, console) and Input Method Editor (IME) support from upstream. Closes #4397. General ------------------------------ Fix issues with the text input. Closes #4346. Closes #4524. Word skipping (when holding Ctrl) is overhauled to be consistent with the Windows / Firefox experience that I took as reference. Improve usability by not blinking (i.e. always rendering) the caret shortly after is has been moved. UI text input ------------------------------ Fix inconsistent mouse-based left and right scrolling (closes #4347). Support smooth left and right scrolling. Chat ------------------------------ Support keyboard-based text selection of the chat input. Mouse-based selection could be support in the future when we decide to add something like an ingame UI cursor. Support smooth up and down scrolling of the chat input, removing the old hack that offsets the input string to simulate scrolling. Console ------------------------------ Also support mouse-based text selection of the command input. Only text from either the command input or the console log can be selected at the same time. This ensures that Ctrl+C will always copy the text that is currently visually selected in the console. Check for Ctrl+C input event in event handler instead of in render function, to hopefully fix the issue that copying does not work sometimes (closes #5974 until further notice). When Ctrl+C is used to copy text from the console log, the selection is cleared. This should make it more clear when text was copied from the log. Fix an issue that was preventing the console log selection from being cleared, when all log lines are selected. Remove Ctrl+A/E hotkeys that move cursor to beginning/end respectively. Ctrl+A now selectes all text like for all other inputs. Home and End keys can still be used to go the beginning and end. Remove Ctrl+U/K hotkeys that clear everything before/after the cursor respectively. Hold shift and use Home/End to select everything instead. IME support ------------------------------ Render list of IME candidates in the client on Windows, so the candidate list can also be viewed in fullscreen mode. There is no API available to retrieve a candidate list on the other operating systems. Improve composition rendering by underlining the composition text instead of putting it in square brackets. Track active input globally to properly activate and deactivate IME through the SDL functions. Closes #1030. Closes #1008. Password rendering ------------------------------ Fix rendering of passwords containing unicode. Instead of rendering one star character for each UTF-8 `char`, render on star for every unicode codepoint. Show the composition text also for passwords. Without seeing the composition text it's hard to type a password containing those characters. The candidate window exposes the composition anyway. If you don't want to expose your password this way, e.g. while streaming, you could: 1. Use a latin password and switch off the IME for the password input with the IME hotkey. 2. Blank your screen with an external program while you are streaming and entering passwords. 3. Use binds to authenticate in rcon or to set the server browser password. Refactoring ------------------------------ Move all text input logic and general rendering to `CLineInput`. A `CLineInput` is associated with a particular `char` buffer given as a pointer either in the constructor or with `SetBuffer`. The maximum byte size of the buffer must also be specified. The maximum length in unicode codepoints can also be specified separately (e.g. on upstream, name are limited by the number of unicode codepoints instead). Add `CLineInputBuffered`, which is a `CLineInput` that own a `char` buffer of a fixed size, which is specified as a template argument. As `CLineInput` does not own a buffer anymore, this reduces duplicate code for line inputs that need their own buffer. Add `CLineInputNumber` which has additional convenience functions to consider the text as an `int` or `float`, to reduce duplicate code in those cases. In the future we could also add an input filter function so that only numbers can be entered in the number input. Add `CLineInput::SetClipboardLineCallback` to handle the case that multiple lines of text are pasted into a lineinput. This reduces duplicate code, as this behavior was previously implemented separately for chat and console. The behavior is also fixed to be consistent with the console on Windows, so the first line being pasted edits the current input text and then sends it instead of being sent on its own without the existing input text. Add `CalcFontSizeAndBoundingBox` to UI to reduce duplicate code. Expose `CalcAlignedCursorPos` as static member function to reuse it for line input. Dispatch input events to UI inputs through the event handler instead of storing them in a duplicate buffer. Use `size_t` for line input cursor position, length etc. and for `str_utf8_stats`. Add `IButtonColorFunction` to UI to describe a functions that defines colors for the Default, Active and Hovered states of UI elements. Add some default button color functions. Use button color function to reduce duplicate code in scrollbar rendering. Use `vec2` instead of two `floats` to represent the mouse positions in the text renderer. Remove `CaretPosition` again, as it does not calculate the correct Y position near line breaks due to the wrapping being different when not rendering the entire string. Instead, calculate the exact caret position when rending a text container and store the caret position in the text cursor for later use. IME usage guide (Windows) ------------------------------ 1. Install the respective language and the Microsoft-IME keyboard (e.g. for Chinese, Japanese or Korean). 2. Launch the game (or a text editor to first try out the IME). Note that Windows may track the input language separately for every application. You can change this in the Windows input settings so the input language is changed globally. 2. Switch the input language using the hotkey Windows+Space or another hotkey that you configured in the Windows input settings (Alt+Shift is the default, but you should consider disabling it, to avoid accidentally changing the input language while playing). 3. Switch from Latin/English input mode to the respective asian input mode. - Chinese: Use Ctrl+Space to switch between English and Chinese input mode. You can change this hotkey in the IME's settings. - Japanese: Use Ctrl+Space to switch between Alphanumeric and Hiragana/Katakana input mode. You can change this hotkey in the IME's settings. - Korean: Use Right Alt to switch between English and Hangul input mode. You cannot change this hotkey as of yet. - Note that the input mode is also tracked per application, but there is no setting to change this behavior as far as I know, so you'll need to switch for every application separately. 4. Start typing. The underlined text is the current composition text. While a composition is active, you can only edit the composition text. Confirm the composition with Space or by selecting a candidate from the candidate list with the arrow keys. Cancel the composition with Escape or by using Backspace to delete the composition text. Note that not all languages offer a candidate list. SDL version-specific issues ------------------------------ - 2.26.5, 2.24.2, 2.0.22: IME candidates work. But there are minor bugs when moving the composition cursor. - 2.0.18, 2.0.20: IME candidates work. - 2.0.16 (our current version): IME candidates cannot be determined with Windows API. Windows tries to draw the composition window like before, so this does not work in fullscreen mode. - 2.0.8 (upstream 0.7): IME candidates work. But this SDL version is too old for us.
2023-01-03 21:28:38 +00:00
static CLineInputBuffered<MAX_CLAN_LENGTH> s_ClanInput;
UI()->DoEditBox(&s_ClanInput, &Button, FontSize + 2.0f);
2011-03-23 12:06:35 +00:00
ServerFriends.HSplitTop(3.0f, nullptr, &ServerFriends);
ServerFriends.HSplitTop(18.0f, &Button, &ServerFriends);
2022-07-16 13:32:06 +00:00
static CButtonContainer s_AddButton;
if(DoButton_Menu(&s_AddButton, s_NameInput.IsEmpty() && !s_ClanInput.IsEmpty() ? Localize("Add Clan") : Localize("Add Friend"), 0, &Button))
2011-03-23 12:06:35 +00:00
{
Port line input and IME support from 0.7 Port the line input (UI edit boxes, chat, console) and Input Method Editor (IME) support from upstream. Closes #4397. General ------------------------------ Fix issues with the text input. Closes #4346. Closes #4524. Word skipping (when holding Ctrl) is overhauled to be consistent with the Windows / Firefox experience that I took as reference. Improve usability by not blinking (i.e. always rendering) the caret shortly after is has been moved. UI text input ------------------------------ Fix inconsistent mouse-based left and right scrolling (closes #4347). Support smooth left and right scrolling. Chat ------------------------------ Support keyboard-based text selection of the chat input. Mouse-based selection could be support in the future when we decide to add something like an ingame UI cursor. Support smooth up and down scrolling of the chat input, removing the old hack that offsets the input string to simulate scrolling. Console ------------------------------ Also support mouse-based text selection of the command input. Only text from either the command input or the console log can be selected at the same time. This ensures that Ctrl+C will always copy the text that is currently visually selected in the console. Check for Ctrl+C input event in event handler instead of in render function, to hopefully fix the issue that copying does not work sometimes (closes #5974 until further notice). When Ctrl+C is used to copy text from the console log, the selection is cleared. This should make it more clear when text was copied from the log. Fix an issue that was preventing the console log selection from being cleared, when all log lines are selected. Remove Ctrl+A/E hotkeys that move cursor to beginning/end respectively. Ctrl+A now selectes all text like for all other inputs. Home and End keys can still be used to go the beginning and end. Remove Ctrl+U/K hotkeys that clear everything before/after the cursor respectively. Hold shift and use Home/End to select everything instead. IME support ------------------------------ Render list of IME candidates in the client on Windows, so the candidate list can also be viewed in fullscreen mode. There is no API available to retrieve a candidate list on the other operating systems. Improve composition rendering by underlining the composition text instead of putting it in square brackets. Track active input globally to properly activate and deactivate IME through the SDL functions. Closes #1030. Closes #1008. Password rendering ------------------------------ Fix rendering of passwords containing unicode. Instead of rendering one star character for each UTF-8 `char`, render on star for every unicode codepoint. Show the composition text also for passwords. Without seeing the composition text it's hard to type a password containing those characters. The candidate window exposes the composition anyway. If you don't want to expose your password this way, e.g. while streaming, you could: 1. Use a latin password and switch off the IME for the password input with the IME hotkey. 2. Blank your screen with an external program while you are streaming and entering passwords. 3. Use binds to authenticate in rcon or to set the server browser password. Refactoring ------------------------------ Move all text input logic and general rendering to `CLineInput`. A `CLineInput` is associated with a particular `char` buffer given as a pointer either in the constructor or with `SetBuffer`. The maximum byte size of the buffer must also be specified. The maximum length in unicode codepoints can also be specified separately (e.g. on upstream, name are limited by the number of unicode codepoints instead). Add `CLineInputBuffered`, which is a `CLineInput` that own a `char` buffer of a fixed size, which is specified as a template argument. As `CLineInput` does not own a buffer anymore, this reduces duplicate code for line inputs that need their own buffer. Add `CLineInputNumber` which has additional convenience functions to consider the text as an `int` or `float`, to reduce duplicate code in those cases. In the future we could also add an input filter function so that only numbers can be entered in the number input. Add `CLineInput::SetClipboardLineCallback` to handle the case that multiple lines of text are pasted into a lineinput. This reduces duplicate code, as this behavior was previously implemented separately for chat and console. The behavior is also fixed to be consistent with the console on Windows, so the first line being pasted edits the current input text and then sends it instead of being sent on its own without the existing input text. Add `CalcFontSizeAndBoundingBox` to UI to reduce duplicate code. Expose `CalcAlignedCursorPos` as static member function to reuse it for line input. Dispatch input events to UI inputs through the event handler instead of storing them in a duplicate buffer. Use `size_t` for line input cursor position, length etc. and for `str_utf8_stats`. Add `IButtonColorFunction` to UI to describe a functions that defines colors for the Default, Active and Hovered states of UI elements. Add some default button color functions. Use button color function to reduce duplicate code in scrollbar rendering. Use `vec2` instead of two `floats` to represent the mouse positions in the text renderer. Remove `CaretPosition` again, as it does not calculate the correct Y position near line breaks due to the wrapping being different when not rendering the entire string. Instead, calculate the exact caret position when rending a text container and store the caret position in the text cursor for later use. IME usage guide (Windows) ------------------------------ 1. Install the respective language and the Microsoft-IME keyboard (e.g. for Chinese, Japanese or Korean). 2. Launch the game (or a text editor to first try out the IME). Note that Windows may track the input language separately for every application. You can change this in the Windows input settings so the input language is changed globally. 2. Switch the input language using the hotkey Windows+Space or another hotkey that you configured in the Windows input settings (Alt+Shift is the default, but you should consider disabling it, to avoid accidentally changing the input language while playing). 3. Switch from Latin/English input mode to the respective asian input mode. - Chinese: Use Ctrl+Space to switch between English and Chinese input mode. You can change this hotkey in the IME's settings. - Japanese: Use Ctrl+Space to switch between Alphanumeric and Hiragana/Katakana input mode. You can change this hotkey in the IME's settings. - Korean: Use Right Alt to switch between English and Hangul input mode. You cannot change this hotkey as of yet. - Note that the input mode is also tracked per application, but there is no setting to change this behavior as far as I know, so you'll need to switch for every application separately. 4. Start typing. The underlined text is the current composition text. While a composition is active, you can only edit the composition text. Confirm the composition with Space or by selecting a candidate from the candidate list with the arrow keys. Cancel the composition with Escape or by using Backspace to delete the composition text. Note that not all languages offer a candidate list. SDL version-specific issues ------------------------------ - 2.26.5, 2.24.2, 2.0.22: IME candidates work. But there are minor bugs when moving the composition cursor. - 2.0.18, 2.0.20: IME candidates work. - 2.0.16 (our current version): IME candidates cannot be determined with Windows API. Windows tries to draw the composition window like before, so this does not work in fullscreen mode. - 2.0.8 (upstream 0.7): IME candidates work. But this SDL version is too old for us.
2023-01-03 21:28:38 +00:00
m_pClient->Friends()->AddFriend(s_NameInput.GetString(), s_ClanInput.GetString());
s_NameInput.Clear();
s_ClanInput.Clear();
2011-06-26 15:10:13 +00:00
FriendlistOnUpdate();
2011-03-23 12:06:35 +00:00
Client()->ServerBrowserUpdate();
}
}
}
void CMenus::FriendlistOnUpdate()
{
// TODO: friends are currently updated every frame; optimize and only update friends when necessary
}
void CMenus::PopupConfirmRemoveFriend()
{
m_pClient->Friends()->RemoveFriend(m_pRemoveFriend->FriendState() == IFriends::FRIEND_PLAYER ? m_pRemoveFriend->Name() : "", m_pRemoveFriend->Clan());
FriendlistOnUpdate();
Client()->ServerBrowserUpdate();
m_pRemoveFriend = nullptr;
}
enum
{
UI_TOOLBOX_PAGE_FILTERS = 0,
UI_TOOLBOX_PAGE_INFO,
UI_TOOLBOX_PAGE_FRIENDS,
NUM_UI_TOOLBOX_PAGES,
};
void CMenus::RenderServerbrowserTabBar(CUIRect TabBar)
{
CUIRect FilterTabButton, InfoTabButton, FriendsTabButton;
TabBar.VSplitLeft(TabBar.w / 3.0f, &FilterTabButton, &TabBar);
TabBar.VSplitMid(&InfoTabButton, &FriendsTabButton);
const ColorRGBA ColorActive = ColorRGBA(0.0f, 0.0f, 0.0f, 0.3f);
const ColorRGBA ColorInactive = ColorRGBA(0.0f, 0.0f, 0.0f, 0.15f);
if(!UI()->IsPopupOpen() && UI()->ConsumeHotkey(CUI::HOTKEY_TAB))
{
const int Direction = Input()->ShiftIsPressed() ? -1 : 1;
g_Config.m_UiToolboxPage = (g_Config.m_UiToolboxPage + NUM_UI_TOOLBOX_PAGES + Direction) % NUM_UI_TOOLBOX_PAGES;
}
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
static CButtonContainer s_FilterTabButton;
if(DoButton_MenuTab(&s_FilterTabButton, FONT_ICON_LIST_UL, g_Config.m_UiToolboxPage == UI_TOOLBOX_PAGE_FILTERS, &FilterTabButton, IGraphics::CORNER_T, &m_aAnimatorsSmallPage[SMALL_TAB_BROWSER_FILTER], &ColorInactive, &ColorActive))
g_Config.m_UiToolboxPage = UI_TOOLBOX_PAGE_FILTERS;
static CButtonContainer s_InfoTabButton;
if(DoButton_MenuTab(&s_InfoTabButton, FONT_ICON_INFO, g_Config.m_UiToolboxPage == UI_TOOLBOX_PAGE_INFO, &InfoTabButton, IGraphics::CORNER_T, &m_aAnimatorsSmallPage[SMALL_TAB_BROWSER_INFO], &ColorInactive, &ColorActive))
g_Config.m_UiToolboxPage = UI_TOOLBOX_PAGE_INFO;
static CButtonContainer s_FriendsTabButton;
if(DoButton_MenuTab(&s_FriendsTabButton, FONT_ICON_HEART, g_Config.m_UiToolboxPage == UI_TOOLBOX_PAGE_FRIENDS, &FriendsTabButton, IGraphics::CORNER_T, &m_aAnimatorsSmallPage[SMALL_TAB_BROWSER_FRIENDS], &ColorInactive, &ColorActive))
g_Config.m_UiToolboxPage = UI_TOOLBOX_PAGE_FRIENDS;
TextRender()->SetRenderFlags(0);
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
}
void CMenus::RenderServerbrowserToolBox(CUIRect ToolBox)
{
ToolBox.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.15f), IGraphics::CORNER_B, 4.0f);
switch(g_Config.m_UiToolboxPage)
{
case UI_TOOLBOX_PAGE_FILTERS:
RenderServerbrowserFilters(ToolBox);
return;
case UI_TOOLBOX_PAGE_INFO:
RenderServerbrowserInfo(ToolBox);
return;
case UI_TOOLBOX_PAGE_FRIENDS:
RenderServerbrowserFriends(ToolBox);
return;
default:
dbg_assert(false, "ui_toolbox_page invalid");
return;
}
}
2010-05-29 07:25:38 +00:00
void CMenus::RenderServerbrowser(CUIRect MainView)
2008-09-04 18:42:26 +00:00
{
/*
+-----------------+ +--tabs--+
| | | |
| | | |
| server list | | tool |
| | | box |
| | | |
+-----------------+ | |
status box +--------+
2008-09-04 18:42:26 +00:00
*/
2010-05-29 07:25:38 +00:00
CUIRect ServerList, StatusBox, ToolBox, TabBar;
MainView.Draw(ms_ColorTabbarActive, IGraphics::CORNER_B, 10.0f);
MainView.Margin(10.0f, &MainView);
MainView.VSplitRight(205.0f, &ServerList, &ToolBox);
ServerList.VSplitRight(5.0f, &ServerList, nullptr);
ToolBox.HSplitTop(24.0f, &TabBar, &ToolBox);
ServerList.HSplitBottom(65.0f, &ServerList, &StatusBox);
2008-09-04 18:42:26 +00:00
bool WasListboxItemActivated;
RenderServerbrowserServerList(ServerList, WasListboxItemActivated);
RenderServerbrowserStatusBox(StatusBox, WasListboxItemActivated);
2010-05-29 07:25:38 +00:00
RenderServerbrowserTabBar(TabBar);
RenderServerbrowserToolBox(ToolBox);
}
2010-05-29 07:25:38 +00:00
template<typename F>
bool CMenus::PrintHighlighted(const char *pName, F &&PrintFn)
{
const char *pStr = g_Config.m_BrFilterString;
char aFilterStr[sizeof(g_Config.m_BrFilterString)];
while((pStr = str_next_token(pStr, IServerBrowser::SEARCH_EXCLUDE_TOKEN, aFilterStr, sizeof(aFilterStr))))
{
// highlight the parts that matches
const char *pFilteredStr;
int FilterLen = str_length(aFilterStr);
if(aFilterStr[0] == '"' && aFilterStr[FilterLen - 1] == '"')
{
aFilterStr[FilterLen - 1] = '\0';
pFilteredStr = str_comp(pName, &aFilterStr[1]) == 0 ? pName : nullptr;
FilterLen -= 2;
}
else
{
const char *pFilteredStrEnd;
pFilteredStr = str_utf8_find_nocase(pName, aFilterStr, &pFilteredStrEnd);
if(pFilteredStr != nullptr && pFilteredStrEnd != nullptr)
FilterLen = pFilteredStrEnd - pFilteredStr;
}
if(pFilteredStr)
{
PrintFn(pFilteredStr, FilterLen);
return true;
}
}
return false;
}
CTeeRenderInfo CMenus::GetTeeRenderInfo(vec2 Size, const char *pSkinName, bool CustomSkinColors, int CustomSkinColorBody, int CustomSkinColorFeet) const
{
const CSkin *pSkin = m_pClient->m_Skins.Find(pSkinName);
CTeeRenderInfo TeeInfo;
TeeInfo.m_OriginalRenderSkin = pSkin->m_OriginalSkin;
TeeInfo.m_ColorableRenderSkin = pSkin->m_ColorableSkin;
TeeInfo.m_SkinMetrics = pSkin->m_Metrics;
TeeInfo.m_CustomColoredSkin = CustomSkinColors;
if(CustomSkinColors)
{
TeeInfo.m_ColorBody = color_cast<ColorRGBA>(ColorHSLA(CustomSkinColorBody).UnclampLighting());
TeeInfo.m_ColorFeet = color_cast<ColorRGBA>(ColorHSLA(CustomSkinColorFeet).UnclampLighting());
}
else
2008-09-04 18:42:26 +00:00
{
TeeInfo.m_ColorBody = ColorRGBA(1.0f, 1.0f, 1.0f);
TeeInfo.m_ColorFeet = ColorRGBA(1.0f, 1.0f, 1.0f);
}
TeeInfo.m_Size = minimum(Size.x, Size.y);
return TeeInfo;
}
2011-06-26 15:10:13 +00:00
void CMenus::ConchainFriendlistUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
{
pfnCallback(pResult, pCallbackUserData);
if(pResult->NumArguments() >= 1 && ((CMenus *)pUserData)->Client()->State() == IClient::STATE_OFFLINE)
2011-06-26 15:10:13 +00:00
{
((CMenus *)pUserData)->FriendlistOnUpdate();
((CMenus *)pUserData)->Client()->ServerBrowserUpdate();
}
}
void CMenus::ConchainServerbrowserUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
{
pfnCallback(pResult, pCallbackUserData);
if(pResult->NumArguments() >= 1 && g_Config.m_UiPage == PAGE_FAVORITES)
((CMenus *)pUserData)->ServerBrowser()->Refresh(IServerBrowser::TYPE_FAVORITES);
}