1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#ifndef GAME_CLIENT_COMPONENTS_MENUS_H
4#define GAME_CLIENT_COMPONENTS_MENUS_H
5
6#include <base/types.h>
7#include <base/vmath.h>
8
9#include <engine/console.h>
10#include <engine/demo.h>
11#include <engine/friends.h>
12#include <engine/serverbrowser.h>
13#include <engine/shared/config.h>
14#include <engine/textrender.h>
15
16#include <game/client/component.h>
17#include <game/client/components/community_icons.h>
18#include <game/client/components/mapimages.h>
19#include <game/client/components/menus_ingame_touch_controls.h>
20#include <game/client/components/menus_settings_controls.h>
21#include <game/client/components/menus_start.h>
22#include <game/client/components/skins7.h>
23#include <game/client/lineinput.h>
24#include <game/client/ui.h>
25#include <game/voting.h>
26
27#include <chrono>
28#include <deque>
29#include <optional>
30#include <vector>
31
32class CMenus : public CComponent
33{
34 static ColorRGBA ms_GuiColor;
35 static ColorRGBA ms_ColorTabbarInactiveOutgame;
36 static ColorRGBA ms_ColorTabbarActiveOutgame;
37 static ColorRGBA ms_ColorTabbarHoverOutgame;
38 static ColorRGBA ms_ColorTabbarInactiveIngame;
39 static ColorRGBA ms_ColorTabbarActiveIngame;
40 static ColorRGBA ms_ColorTabbarHoverIngame;
41 static ColorRGBA ms_ColorTabbarInactive;
42 static ColorRGBA ms_ColorTabbarActive;
43 static ColorRGBA ms_ColorTabbarHover;
44
45public:
46 int DoButton_Toggle(const void *pId, int Checked, const CUIRect *pRect, bool Active, unsigned Flags = BUTTONFLAG_LEFT);
47 int DoButton_Menu(CButtonContainer *pButtonContainer, const char *pText, int Checked, const CUIRect *pRect, unsigned Flags = BUTTONFLAG_LEFT, const char *pImageName = nullptr, int Corners = IGraphics::CORNER_ALL, float Rounding = 5.0f, float FontFactor = 0.0f, ColorRGBA Color = ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f));
48 int DoButton_MenuTab(CButtonContainer *pButtonContainer, const char *pText, int Checked, const CUIRect *pRect, int Corners, SUIAnimator *pAnimator = nullptr, const ColorRGBA *pDefaultColor = nullptr, const ColorRGBA *pActiveColor = nullptr, const ColorRGBA *pHoverColor = nullptr, float EdgeRounding = 10.0f, const CCommunityIcon *pCommunityIcon = nullptr);
49
50 int DoButton_CheckBox_Common(const void *pId, const char *pText, const char *pBoxText, const CUIRect *pRect, unsigned Flags);
51 int DoButton_CheckBox(const void *pId, const char *pText, int Checked, const CUIRect *pRect);
52 int DoButton_CheckBoxAutoVMarginAndSet(const void *pId, const char *pText, int *pValue, CUIRect *pRect, float VMargin);
53 int DoButton_CheckBox_Number(const void *pId, const char *pText, int Checked, const CUIRect *pRect);
54
55 bool DoLine_RadioMenu(CUIRect &View, const char *pLabel, std::vector<CButtonContainer> &vButtonContainers, const std::vector<const char *> &vLabels, const std::vector<int> &vValues, int &Value);
56
57private:
58 CUi::SColorPickerPopupContext m_ColorPickerPopupContext;
59 ColorHSLA DoLine_ColorPicker(CButtonContainer *pResetId, float LineSize, float LabelSize, float BottomMargin, CUIRect *pMainRect, const char *pText, unsigned int *pColorValue, ColorRGBA DefaultColor, bool CheckBoxSpacing = true, int *pCheckBoxValue = nullptr, bool Alpha = false);
60 ColorHSLA DoButton_ColorPicker(const CUIRect *pRect, unsigned int *pHslaColor, bool Alpha);
61
62 void DoLaserPreview(const CUIRect *pRect, ColorHSLA OutlineColor, ColorHSLA InnerColor, int LaserType);
63 int DoButton_GridHeader(const void *pId, const char *pText, int Checked, const CUIRect *pRect, int Align = TEXTALIGN_ML);
64 int DoButton_Favorite(const void *pButtonId, const void *pParentId, bool Checked, const CUIRect *pRect);
65
66 bool m_SkinListScrollToSelected = false;
67 std::optional<std::chrono::nanoseconds> m_SkinList7LastRefreshTime;
68 std::optional<std::chrono::nanoseconds> m_SkinPartsList7LastRefreshTime;
69
70 int m_DirectionQuadContainerIndex;
71
72 // menus_settings_assets.cpp
73public:
74 struct SCustomItem
75 {
76 IGraphics::CTextureHandle m_RenderTexture;
77
78 char m_aName[50];
79
80 bool operator<(const SCustomItem &Other) const { return str_comp(a: m_aName, b: Other.m_aName) < 0; }
81 };
82
83 struct SCustomEntities : public SCustomItem
84 {
85 struct SEntitiesImage
86 {
87 IGraphics::CTextureHandle m_Texture;
88 };
89 SEntitiesImage m_aImages[MAP_IMAGE_MOD_TYPE_COUNT];
90 };
91
92 struct SCustomGame : public SCustomItem
93 {
94 };
95
96 struct SCustomEmoticon : public SCustomItem
97 {
98 };
99
100 struct SCustomParticle : public SCustomItem
101 {
102 };
103
104 struct SCustomHud : public SCustomItem
105 {
106 };
107
108 struct SCustomExtras : public SCustomItem
109 {
110 };
111
112protected:
113 std::vector<SCustomEntities> m_vEntitiesList;
114 std::vector<SCustomGame> m_vGameList;
115 std::vector<SCustomEmoticon> m_vEmoticonList;
116 std::vector<SCustomParticle> m_vParticlesList;
117 std::vector<SCustomHud> m_vHudList;
118 std::vector<SCustomExtras> m_vExtrasList;
119
120 bool m_IsInit = false;
121
122 static void LoadEntities(struct SCustomEntities *pEntitiesItem, void *pUser);
123 static int EntitiesScan(const char *pName, int IsDir, int DirType, void *pUser);
124
125 static int GameScan(const char *pName, int IsDir, int DirType, void *pUser);
126 static int EmoticonsScan(const char *pName, int IsDir, int DirType, void *pUser);
127 static int ParticlesScan(const char *pName, int IsDir, int DirType, void *pUser);
128 static int HudScan(const char *pName, int IsDir, int DirType, void *pUser);
129 static int ExtrasScan(const char *pName, int IsDir, int DirType, void *pUser);
130
131 static void ConchainAssetsEntities(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
132 static void ConchainAssetGame(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
133 static void ConchainAssetParticles(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
134 static void ConchainAssetEmoticons(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
135 static void ConchainAssetHud(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
136 static void ConchainAssetExtras(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
137
138 void ClearCustomItems(int CurTab);
139
140 int m_MenuPage;
141 int m_GamePage;
142 int m_Popup;
143 bool m_ShowStart;
144 bool m_MenuActive;
145
146 bool m_DummyNamePlatePreview = false;
147
148 bool m_JoinTutorial = false;
149 bool m_CreateDefaultFavoriteCommunities = false;
150 bool m_ForceRefreshLanPage = false;
151
152 char m_aNextServer[256];
153
154 // images
155 struct CMenuImage
156 {
157 char m_aName[64];
158 IGraphics::CTextureHandle m_OrgTexture;
159 IGraphics::CTextureHandle m_GreyTexture;
160 };
161 std::vector<CMenuImage> m_vMenuImages;
162 static int MenuImageScan(const char *pName, int IsDir, int DirType, void *pUser);
163 const CMenuImage *FindMenuImage(const char *pName);
164
165 // loading
166 class CLoadingState
167 {
168 public:
169 std::chrono::nanoseconds m_LastRender{0};
170 int m_Current;
171 int m_Total;
172 };
173 CLoadingState m_LoadingState;
174
175 //
176 char m_aMessageTopic[512];
177 char m_aMessageBody[512];
178 char m_aMessageButton[512];
179
180 CUIElement m_RefreshButton;
181 CUIElement m_ConnectButton;
182
183 // generic popups
184 typedef void (CMenus::*FPopupButtonCallback)();
185 void DefaultButtonCallback()
186 {
187 // do nothing
188 }
189 enum
190 {
191 BUTTON_CONFIRM = 0, // confirm / yes / close / ok
192 BUTTON_CANCEL, // cancel / no
193 NUM_BUTTONS
194 };
195 char m_aPopupTitle[128];
196 char m_aPopupMessage[IO_MAX_PATH_LENGTH + 256];
197 struct
198 {
199 char m_aLabel[64];
200 int m_NextPopup;
201 FPopupButtonCallback m_pfnCallback;
202 } m_aPopupButtons[NUM_BUTTONS];
203
204 void PopupMessage(const char *pTitle, const char *pMessage,
205 const char *pButtonLabel, int NextPopup = POPUP_NONE, FPopupButtonCallback pfnButtonCallback = &CMenus::DefaultButtonCallback);
206 void PopupConfirm(const char *pTitle, const char *pMessage,
207 const char *pConfirmButtonLabel, const char *pCancelButtonLabel,
208 FPopupButtonCallback pfnConfirmButtonCallback = &CMenus::DefaultButtonCallback, int ConfirmNextPopup = POPUP_NONE,
209 FPopupButtonCallback pfnCancelButtonCallback = &CMenus::DefaultButtonCallback, int CancelNextPopup = POPUP_NONE);
210
211 // some settings
212 static float ms_ButtonHeight;
213 static float ms_ListheaderHeight;
214 static float ms_ListitemAdditionalHeight;
215
216 // for settings
217 bool m_NeedRestartGraphics;
218 bool m_NeedRestartSound;
219 bool m_NeedRestartUpdate;
220 bool m_NeedSendinfo;
221 bool m_NeedSendDummyinfo;
222 int m_SettingPlayerPage;
223
224 // 0.7 skins
225 bool m_CustomSkinMenu = false;
226 int m_TeePartSelected = protocol7::SKINPART_BODY;
227 const CSkins7::CSkin *m_pSelectedSkin = nullptr;
228 CLineInputBuffered<protocol7::MAX_SKIN_ARRAY_SIZE, protocol7::MAX_SKIN_LENGTH> m_SkinNameInput;
229 bool m_SkinPartListNeedsUpdate = false;
230 void PopupConfirmDeleteSkin7();
231
232 // for map download popup
233 int64_t m_DownloadLastCheckTime;
234 int m_DownloadLastCheckSize;
235 float m_DownloadSpeed;
236
237 // for password popup
238 CLineInput m_PasswordInput;
239
240 // for call vote
241 int m_CallvoteSelectedOption;
242 int m_CallvoteSelectedPlayer;
243 CLineInputBuffered<VOTE_REASON_LENGTH> m_CallvoteReasonInput;
244 CLineInputBuffered<64> m_FilterInput;
245 bool m_ControlPageOpening;
246
247 // demo
248 enum
249 {
250 SORT_DEMONAME = 0,
251 SORT_MARKERS,
252 SORT_LENGTH,
253 SORT_DATE,
254 };
255
256 class CDemoItem
257 {
258 public:
259 char m_aFilename[IO_MAX_PATH_LENGTH];
260 char m_aName[IO_MAX_PATH_LENGTH];
261 bool m_IsDir;
262 bool m_IsLink;
263 int m_StorageType;
264 time_t m_Date;
265 int64_t m_Size;
266
267 bool m_InfosLoaded;
268 bool m_Valid;
269 CDemoHeader m_Info;
270 CTimelineMarkers m_TimelineMarkers;
271 CMapInfo m_MapInfo;
272
273 int NumMarkers() const
274 {
275 return std::clamp<int>(val: bytes_be_to_uint(bytes: m_TimelineMarkers.m_aNumTimelineMarkers), lo: 0, hi: MAX_TIMELINE_MARKERS);
276 }
277
278 int Length() const
279 {
280 return bytes_be_to_uint(bytes: m_Info.m_aLength);
281 }
282
283 unsigned MapSize() const
284 {
285 return bytes_be_to_uint(bytes: m_Info.m_aMapSize);
286 }
287
288 bool operator<(const CDemoItem &Other) const
289 {
290 if(!str_comp(a: m_aFilename, b: ".."))
291 return true;
292 if(!str_comp(a: Other.m_aFilename, b: ".."))
293 return false;
294 if(m_IsDir && !Other.m_IsDir)
295 return true;
296 if(!m_IsDir && Other.m_IsDir)
297 return false;
298
299 const CDemoItem &Left = g_Config.m_BrDemoSortOrder ? Other : *this;
300 const CDemoItem &Right = g_Config.m_BrDemoSortOrder ? *this : Other;
301
302 if(g_Config.m_BrDemoSort == SORT_DEMONAME)
303 return str_comp_filenames(a: Left.m_aFilename, b: Right.m_aFilename) < 0;
304 if(g_Config.m_BrDemoSort == SORT_DATE)
305 return Left.m_Date < Right.m_Date;
306
307 if(!Other.m_InfosLoaded)
308 return m_InfosLoaded;
309 if(!m_InfosLoaded)
310 return !Other.m_InfosLoaded;
311
312 if(g_Config.m_BrDemoSort == SORT_MARKERS)
313 return Left.NumMarkers() < Right.NumMarkers();
314 if(g_Config.m_BrDemoSort == SORT_LENGTH)
315 return Left.Length() < Right.Length();
316
317 // Unknown sort
318 return true;
319 }
320 };
321
322 char m_aCurrentDemoFolder[IO_MAX_PATH_LENGTH];
323 char m_aCurrentDemoSelectionName[IO_MAX_PATH_LENGTH];
324 CLineInputBuffered<IO_MAX_PATH_LENGTH> m_DemoRenameInput;
325 CLineInputBuffered<IO_MAX_PATH_LENGTH> m_DemoSliceInput;
326 CLineInputBuffered<IO_MAX_PATH_LENGTH> m_DemoSearchInput;
327#if defined(CONF_VIDEORECORDER)
328 CLineInputBuffered<IO_MAX_PATH_LENGTH> m_DemoRenderInput;
329#endif
330 int m_DemolistSelectedIndex;
331 bool m_DemolistSelectedReveal = false;
332 int m_DemolistStorageType;
333 bool m_DemolistMultipleStorages = false;
334 int m_Speed = 4;
335 bool m_StartPaused = false;
336
337 std::chrono::nanoseconds m_DemoPopulateStartTime{0};
338
339 void DemolistOnUpdate(bool Reset);
340 static int DemolistFetchCallback(const CFsFileInfo *pInfo, int IsDir, int StorageType, void *pUser);
341
342 // friends
343 class CFriendItem
344 {
345 char m_aName[MAX_NAME_LENGTH];
346 char m_aClan[MAX_CLAN_LENGTH];
347 const CServerInfo *m_pServerInfo;
348 int m_FriendState;
349 bool m_IsPlayer;
350 bool m_IsAfk;
351 // skin info 0.6
352 char m_aSkin[MAX_SKIN_LENGTH];
353 bool m_CustomSkinColors;
354 int m_CustomSkinColorBody;
355 int m_CustomSkinColorFeet;
356 // skin info 0.7
357 char m_aaSkin7[protocol7::NUM_SKINPARTS][protocol7::MAX_SKIN_LENGTH];
358 bool m_aUseCustomSkinColor7[protocol7::NUM_SKINPARTS];
359 int m_aCustomSkinColor7[protocol7::NUM_SKINPARTS];
360
361 public:
362 CFriendItem(const CFriendInfo *pFriendInfo) :
363 m_pServerInfo(nullptr),
364 m_IsPlayer(false),
365 m_IsAfk(false),
366 m_CustomSkinColors(false),
367 m_CustomSkinColorBody(0),
368 m_CustomSkinColorFeet(0)
369 {
370 str_copy(dst&: m_aName, src: pFriendInfo->m_aName);
371 str_copy(dst&: m_aClan, src: pFriendInfo->m_aClan);
372 m_FriendState = m_aName[0] == '\0' ? IFriends::FRIEND_CLAN : IFriends::FRIEND_PLAYER;
373 m_aSkin[0] = '\0';
374 for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
375 {
376 m_aaSkin7[Part][0] = '\0';
377 m_aUseCustomSkinColor7[Part] = false;
378 m_aCustomSkinColor7[Part] = 0;
379 }
380 }
381 CFriendItem(const CServerInfo::CClient &CurrentClient, const CServerInfo *pServerInfo) :
382 m_pServerInfo(pServerInfo),
383 m_FriendState(CurrentClient.m_FriendState),
384 m_IsPlayer(CurrentClient.m_Player),
385 m_IsAfk(CurrentClient.m_Afk),
386 m_CustomSkinColors(CurrentClient.m_CustomSkinColors),
387 m_CustomSkinColorBody(CurrentClient.m_CustomSkinColorBody),
388 m_CustomSkinColorFeet(CurrentClient.m_CustomSkinColorFeet)
389 {
390 str_copy(dst&: m_aName, src: CurrentClient.m_aName);
391 str_copy(dst&: m_aClan, src: CurrentClient.m_aClan);
392 str_copy(dst&: m_aSkin, src: CurrentClient.m_aSkin);
393 for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++)
394 {
395 str_copy(dst&: m_aaSkin7[Part], src: CurrentClient.m_aaSkin7[Part]);
396 m_aUseCustomSkinColor7[Part] = CurrentClient.m_aUseCustomSkinColor7[Part];
397 m_aCustomSkinColor7[Part] = CurrentClient.m_aCustomSkinColor7[Part];
398 }
399 }
400
401 const char *Name() const { return m_aName; }
402 const char *Clan() const { return m_aClan; }
403 const CServerInfo *ServerInfo() const { return m_pServerInfo; }
404 int FriendState() const { return m_FriendState; }
405 bool IsPlayer() const { return m_IsPlayer; }
406 bool IsAfk() const { return m_IsAfk; }
407 // 0.6 skin
408 const char *Skin() const { return m_aSkin; }
409 bool CustomSkinColors() const { return m_CustomSkinColors; }
410 int CustomSkinColorBody() const { return m_CustomSkinColorBody; }
411 int CustomSkinColorFeet() const { return m_CustomSkinColorFeet; }
412 // 0.7 skin
413 const char *Skin7(int Part) const { return m_aaSkin7[Part]; }
414 bool UseCustomSkinColor7(int Part) const { return m_aUseCustomSkinColor7[Part]; }
415 int CustomSkinColor7(int Part) const { return m_aCustomSkinColor7[Part]; }
416
417 const void *ListItemId() const { return &m_aName; }
418 const void *RemoveButtonId() const { return &m_FriendState; }
419 const void *CommunityTooltipId() const { return &m_IsPlayer; }
420 const void *SkinTooltipId() const { return &m_aSkin; }
421
422 bool operator<(const CFriendItem &Other) const
423 {
424 const int Result = str_comp_nocase(a: m_aName, b: Other.m_aName);
425 return Result < 0 || (Result == 0 && str_comp_nocase(a: m_aClan, b: Other.m_aClan) < 0);
426 }
427 };
428
429 enum
430 {
431 FRIEND_PLAYER_ON = 0,
432 FRIEND_CLAN_ON,
433 FRIEND_OFF,
434 NUM_FRIEND_TYPES
435 };
436 std::vector<CFriendItem> m_avFriends[NUM_FRIEND_TYPES];
437 const CFriendItem *m_pRemoveFriend = nullptr;
438
439 // found in menus.cpp
440 void Render();
441 void RenderPopupFullscreen(CUIRect Screen);
442 void RenderPopupConnecting(CUIRect Screen);
443 void RenderPopupLoading(CUIRect Screen);
444#if defined(CONF_VIDEORECORDER)
445 void PopupConfirmDemoReplaceVideo();
446#endif
447 void RenderMenubar(CUIRect Box, IClient::EClientState ClientState);
448 void RenderNews(CUIRect MainView);
449 static void ConchainBackgroundEntities(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
450 static void ConchainUpdateMusicState(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
451 void UpdateMusicState();
452
453 // found in menus_demo.cpp
454 vec2 m_DemoControlsPositionOffset = vec2(0.0f, 0.0f);
455 float m_LastPauseChange = -1.0f;
456 float m_LastSpeedChange = -1.0f;
457 static constexpr int DEFAULT_SKIP_DURATION_INDEX = 3;
458 int m_SkipDurationIndex = DEFAULT_SKIP_DURATION_INDEX;
459 static bool DemoFilterChat(const void *pData, int Size, void *pUser);
460 bool FetchHeader(CDemoItem &Item);
461 void FetchAllHeaders();
462 void HandleDemoSeeking(float PositionToSeek, float TimeToSeek);
463 void RenderDemoPlayer(CUIRect MainView);
464 void RenderDemoPlayerSliceSavePopup(CUIRect MainView);
465 bool m_DemoBrowserListInitialized = false;
466 void RenderDemoBrowser(CUIRect MainView);
467 void RenderDemoBrowserList(CUIRect ListView, bool &WasListboxItemActivated);
468 void RenderDemoBrowserDetails(CUIRect DetailsView);
469 void RenderDemoBrowserButtons(CUIRect ButtonsView, bool WasListboxItemActivated);
470 void PopupConfirmPlayDemo();
471 void PopupConfirmDeleteDemo();
472 void PopupConfirmDeleteFolder();
473 static void ConchainDemoPlay(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
474 static void ConchainDemoSpeed(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
475
476 // found in menus_ingame.cpp
477 STextContainerIndex m_MotdTextContainerIndex;
478 void RenderGame(CUIRect MainView);
479 void PopupConfirmDisconnect();
480 void PopupConfirmDisconnectDummy();
481 void PopupConfirmDiscardTouchControlsChanges();
482 void PopupConfirmResetTouchControls();
483 void PopupConfirmImportTouchControlsClipboard();
484 void PopupConfirmDeleteButton();
485 void PopupCancelDeselectButton();
486 void PopupConfirmSelectedNotVisible();
487 void PopupConfirmChangeSelectedButton();
488 void PopupCancelChangeSelectedButton();
489 void PopupConfirmTurnOffEditor();
490 void RenderPlayers(CUIRect MainView);
491 void RenderServerInfo(CUIRect MainView);
492 void RenderServerInfoMotd(CUIRect Motd);
493 void RenderServerControl(CUIRect MainView);
494 bool RenderServerControlKick(CUIRect MainView, bool FilterSpectators, bool UpdateScroll);
495 bool RenderServerControlServer(CUIRect MainView, bool UpdateScroll);
496 void RenderIngameHint();
497
498 // found in menus_browser.cpp
499 int m_SelectedIndex;
500 bool m_ServerBrowserShouldRevealSelection;
501 std::vector<CUIElement *> m_avpServerBrowserUiElements[IServerBrowser::NUM_TYPES];
502 void RenderServerbrowserServerList(CUIRect View, bool &WasListboxItemActivated);
503 void RenderServerbrowserStatusBox(CUIRect StatusBox, bool WasListboxItemActivated);
504 void Connect(const char *pAddress);
505 void PopupConfirmSwitchServer();
506 void RenderServerbrowserFilters(CUIRect View);
507 void ResetServerbrowserFilters();
508 void RenderServerbrowserDDNetFilter(CUIRect View,
509 IFilterList &Filter,
510 float ItemHeight, int MaxItems, int ItemsPerRow,
511 CScrollRegion &ScrollRegion, std::vector<unsigned char> &vItemIds,
512 bool UpdateCommunityCacheOnChange,
513 const std::function<const char *(int ItemIndex)> &GetItemName,
514 const std::function<void(int ItemIndex, CUIRect Item, const void *pItemId, bool Active)> &RenderItem);
515 void RenderServerbrowserCommunitiesFilter(CUIRect View);
516 void RenderServerbrowserCountriesFilter(CUIRect View);
517 void RenderServerbrowserTypesFilter(CUIRect View);
518 struct SPopupCountrySelectionContext
519 {
520 CMenus *m_pMenus;
521 int m_Selection;
522 bool m_New;
523 };
524 static CUi::EPopupMenuFunctionResult PopupCountrySelection(void *pContext, CUIRect View, bool Active);
525 void RenderServerbrowserInfo(CUIRect View);
526 void RenderServerbrowserInfoScoreboard(CUIRect View, const CServerInfo *pSelectedServer);
527 void RenderServerbrowserFriends(CUIRect View);
528 void FriendlistOnUpdate();
529 void PopupConfirmRemoveFriend();
530 void RenderServerbrowserTabBar(CUIRect TabBar);
531 void RenderServerbrowserToolBox(CUIRect ToolBox);
532 void RenderServerbrowser(CUIRect MainView);
533 template<typename F>
534 bool PrintHighlighted(const char *pName, F &&PrintFn);
535 CTeeRenderInfo GetTeeRenderInfo(vec2 Size, const char *pSkinName, bool CustomSkinColors, int CustomSkinColorBody, int CustomSkinColorFeet) const;
536 static void ConchainFriendlistUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
537 static void ConchainFavoritesUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
538 static void ConchainCommunitiesUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
539 static void ConchainUiPageUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
540 void UpdateCommunityCache(bool Force);
541
542 // found in menus_settings.cpp
543 void RenderLanguageSettings(CUIRect MainView);
544 bool RenderLanguageSelection(CUIRect MainView);
545 void RenderThemeSelection(CUIRect MainView);
546 void RenderSettingsGeneral(CUIRect MainView);
547 void RenderSettingsPlayer(CUIRect MainView);
548 void RenderSettingsTee(CUIRect MainView);
549 void RenderSettingsTee7(CUIRect MainView);
550 void RenderSettingsTeeCustom7(CUIRect MainView);
551 void RenderSkinSelection7(CUIRect MainView);
552 void RenderSkinPartSelection7(CUIRect MainView);
553 void RenderSettingsGraphics(CUIRect MainView);
554 void RenderSettingsSound(CUIRect MainView);
555 void RenderSettings(CUIRect MainView);
556 void RenderSettingsCustom(CUIRect MainView);
557
558 // found in menus_settings_controls.cpp
559 // TODO: Change PopupConfirm to avoid using a function pointer to a CMenus
560 // member function, to move this function to CMenusSettingsControls
561 void ResetSettingsControls();
562
563 std::vector<CButtonContainer> m_vButtonContainersNamePlateShow = {{}, {}, {}, {}};
564 std::vector<CButtonContainer> m_vButtonContainersNamePlateKeyPresses = {{}, {}, {}, {}};
565
566 class CMapListItem
567 {
568 public:
569 char m_aFilename[IO_MAX_PATH_LENGTH];
570 bool m_IsDirectory;
571 };
572 class CPopupMapPickerContext
573 {
574 public:
575 std::vector<CMapListItem> m_vMaps;
576 char m_aCurrentMapFolder[IO_MAX_PATH_LENGTH] = "";
577 static int MapListFetchCallback(const CFsFileInfo *pInfo, int IsDir, int StorageType, void *pUser);
578 void MapListPopulate();
579 CMenus *m_pMenus;
580 int m_Selection;
581 };
582
583 static bool CompareFilenameAscending(const CMapListItem Lhs, const CMapListItem Rhs)
584 {
585 if(str_comp(a: Lhs.m_aFilename, b: "..") == 0)
586 return true;
587 if(str_comp(a: Rhs.m_aFilename, b: "..") == 0)
588 return false;
589 if(Lhs.m_IsDirectory != Rhs.m_IsDirectory)
590 return Lhs.m_IsDirectory;
591 return str_comp_filenames(a: Lhs.m_aFilename, b: Rhs.m_aFilename) < 0;
592 }
593
594 static CUi::EPopupMenuFunctionResult PopupMapPicker(void *pContext, CUIRect View, bool Active);
595
596 void SetNeedSendInfo();
597 void UpdateColors();
598
599 IGraphics::CTextureHandle m_TextureBlob;
600
601public:
602 void RenderBackground();
603
604 CMenus();
605 int Sizeof() const override { return sizeof(*this); }
606
607 void RenderLoading(const char *pCaption, const char *pContent, int IncreaseCounter);
608 void FinishLoading();
609
610 bool IsInit() const { return m_IsInit; }
611
612 bool IsActive() const { return m_MenuActive; }
613 void SetActive(bool Active);
614
615 void OnInterfacesInit(CGameClient *pClient) override;
616 void OnInit() override;
617
618 void OnStateChange(int NewState, int OldState) override;
619 void OnWindowResize() override;
620 void OnReset() override;
621 void OnRender() override;
622 bool OnInput(const IInput::CEvent &Event) override;
623 bool OnCursorMove(float x, float y, IInput::ECursorType CursorType) override;
624 void OnShutdown() override;
625
626 enum
627 {
628 PAGE_NEWS = 1,
629 PAGE_GAME,
630 PAGE_PLAYERS,
631 PAGE_SERVER_INFO,
632 PAGE_CALLVOTE,
633 PAGE_INTERNET,
634 PAGE_LAN,
635 PAGE_FAVORITES,
636 PAGE_FAVORITE_COMMUNITY_1,
637 PAGE_FAVORITE_COMMUNITY_2,
638 PAGE_FAVORITE_COMMUNITY_3,
639 PAGE_FAVORITE_COMMUNITY_4,
640 PAGE_FAVORITE_COMMUNITY_5,
641 PAGE_DEMOS,
642 PAGE_SETTINGS,
643 PAGE_NETWORK,
644 PAGE_GHOST,
645
646 PAGE_LENGTH,
647 };
648
649 enum
650 {
651 SETTINGS_LANGUAGE = 0,
652 SETTINGS_GENERAL,
653 SETTINGS_PLAYER,
654 SETTINGS_TEE,
655 SETTINGS_APPEARANCE,
656 SETTINGS_CONTROLS,
657 SETTINGS_GRAPHICS,
658 SETTINGS_SOUND,
659 SETTINGS_DDNET,
660 SETTINGS_ASSETS,
661
662 SETTINGS_LENGTH,
663 };
664
665 enum
666 {
667 BIG_TAB_NEWS = 0,
668 BIG_TAB_INTERNET,
669 BIG_TAB_LAN,
670 BIG_TAB_FAVORITES,
671 BIT_TAB_FAVORITE_COMMUNITY_1,
672 BIT_TAB_FAVORITE_COMMUNITY_2,
673 BIT_TAB_FAVORITE_COMMUNITY_3,
674 BIT_TAB_FAVORITE_COMMUNITY_4,
675 BIT_TAB_FAVORITE_COMMUNITY_5,
676 BIG_TAB_DEMOS,
677
678 BIG_TAB_LENGTH,
679 };
680
681 enum
682 {
683 SMALL_TAB_HOME = 0,
684 SMALL_TAB_QUIT,
685 SMALL_TAB_SETTINGS,
686 SMALL_TAB_EDITOR,
687 SMALL_TAB_DEMOBUTTON,
688 SMALL_TAB_SERVER,
689 SMALL_TAB_BROWSER_FILTER,
690 SMALL_TAB_BROWSER_INFO,
691 SMALL_TAB_BROWSER_FRIENDS,
692
693 SMALL_TAB_LENGTH,
694 };
695
696 SUIAnimator m_aAnimatorsBigPage[BIG_TAB_LENGTH];
697 SUIAnimator m_aAnimatorsSmallPage[SMALL_TAB_LENGTH];
698 SUIAnimator m_aAnimatorsSettingsTab[SETTINGS_LENGTH];
699
700 // DDRace
701 int DoButton_CheckBox_Tristate(const void *pId, const char *pText, TRISTATE Checked, const CUIRect *pRect);
702 std::vector<CDemoItem> m_vDemos;
703 std::vector<CDemoItem *> m_vpFilteredDemos;
704 void DemolistPopulate();
705 void RefreshFilteredDemos();
706 void DemoSeekTick(IDemoPlayer::ETickOffset TickOffset);
707 bool m_Dummy;
708
709 const char *GetCurrentDemoFolder() const { return m_aCurrentDemoFolder; }
710
711 // Ghost
712 struct CGhostItem
713 {
714 char m_aFilename[IO_MAX_PATH_LENGTH];
715 char m_aPlayer[MAX_NAME_LENGTH];
716
717 bool m_Failed;
718 int m_Time;
719 int m_Slot;
720 bool m_Own;
721 time_t m_Date;
722
723 CGhostItem() :
724 m_Slot(-1), m_Own(false) { m_aFilename[0] = 0; }
725
726 bool operator<(const CGhostItem &Other) const { return m_Time < Other.m_Time; }
727
728 bool Active() const { return m_Slot != -1; }
729 bool HasFile() const { return m_aFilename[0]; }
730 };
731
732 enum
733 {
734 GHOST_SORT_NONE = -1,
735 GHOST_SORT_NAME,
736 GHOST_SORT_TIME,
737 GHOST_SORT_DATE,
738 };
739
740 std::vector<CGhostItem> m_vGhosts;
741
742 std::chrono::nanoseconds m_GhostPopulateStartTime{0};
743
744 void GhostlistPopulate();
745 CGhostItem *GetOwnGhost();
746 void UpdateOwnGhost(CGhostItem Item);
747 void DeleteGhostItem(int Index);
748 void SortGhostlist();
749
750 bool CanDisplayWarning() const;
751
752 void PopupWarning(const char *pTopic, const char *pBody, const char *pButton, std::chrono::nanoseconds Duration);
753
754 std::chrono::nanoseconds m_PopupWarningLastTime;
755 std::chrono::nanoseconds m_PopupWarningDuration;
756
757 int m_DemoPlayerState;
758
759 enum
760 {
761 POPUP_NONE = 0,
762 POPUP_MESSAGE, // generic message popup (one button)
763 POPUP_CONFIRM, // generic confirmation popup (two buttons)
764 POPUP_FIRST_LAUNCH,
765 POPUP_POINTS,
766 POPUP_DISCONNECTED,
767 POPUP_LANGUAGE,
768 POPUP_RENAME_DEMO,
769 POPUP_RENDER_DEMO,
770 POPUP_RENDER_DONE,
771 POPUP_PASSWORD,
772 POPUP_QUIT,
773 POPUP_RESTART,
774 POPUP_WARNING,
775 POPUP_SAVE_SKIN,
776 };
777
778 enum
779 {
780 // demo player states
781 DEMOPLAYER_NONE = 0,
782 DEMOPLAYER_SLICE_SAVE,
783 };
784
785 void SetMenuPage(int NewPage);
786 void RefreshBrowserTab(bool Force);
787 void ForceRefreshLanPage();
788 void SetShowStart(bool ShowStart);
789 void ShowQuitPopup();
790
791private:
792 CCommunityIcons m_CommunityIcons;
793 CMenusIngameTouchControls m_MenusIngameTouchControls;
794 friend CMenusIngameTouchControls;
795 CMenusSettingsControls m_MenusSettingsControls;
796 friend CMenusSettingsControls;
797 CMenusStart m_MenusStart;
798
799 static int GhostlistFetchCallback(const CFsFileInfo *pInfo, int IsDir, int StorageType, void *pUser);
800
801 // found in menus_ingame.cpp
802 void RenderInGameNetwork(CUIRect MainView);
803 void RenderGhost(CUIRect MainView);
804
805 // found in menus_settings.cpp
806 void RenderSettingsDDNet(CUIRect MainView);
807 void RenderSettingsAppearance(CUIRect MainView);
808 bool RenderHslaScrollbars(CUIRect *pRect, unsigned int *pColor, bool Alpha, float DarkestLight);
809};
810#endif
811