ddnet/src/game/client/components/menus.h

763 lines
24 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. */
2010-05-29 07:25:38 +00:00
#ifndef GAME_CLIENT_COMPONENTS_MENUS_H
#define GAME_CLIENT_COMPONENTS_MENUS_H
#include <base/types.h>
#include <base/vmath.h>
2010-05-29 07:25:38 +00:00
2022-05-18 16:00:05 +00:00
#include <chrono>
2022-10-04 16:40:24 +00:00
#include <unordered_set>
#include <vector>
2022-05-29 16:33:38 +00:00
#include <engine/console.h>
#include <engine/demo.h>
2011-06-26 15:10:13 +00:00
#include <engine/friends.h>
#include <engine/shared/config.h>
#include <engine/shared/linereader.h>
2020-10-12 10:29:47 +00:00
#include <engine/textrender.h>
2020-09-26 07:37:35 +00:00
#include <game/client/components/mapimages.h>
2010-05-29 07:25:38 +00:00
#include <game/client/component.h>
#include <game/client/ui.h>
#include <game/voting.h>
2010-05-29 07:25:38 +00:00
2021-07-12 09:29:59 +00:00
#include <game/client/render.h>
struct CServerProcess
{
PROCESS m_Process;
};
2010-05-29 07:25:38 +00:00
2020-10-28 02:59:50 +00:00
struct SColorPicker
{
public:
const float ms_Width = 160.0f;
2020-12-14 00:51:31 +00:00
const float ms_Height = 186.0f;
2020-10-28 02:59:50 +00:00
float m_X;
float m_Y;
bool m_Active;
CUIRect m_AttachedRect;
unsigned int *m_pColor;
2020-12-14 00:51:31 +00:00
unsigned int m_HSVColor;
2020-10-28 02:59:50 +00:00
};
2022-10-25 16:51:56 +00:00
// component to fetch keypresses, override all other input
2010-05-29 07:25:38 +00:00
class CMenusKeyBinder : public CComponent
{
public:
bool m_TakeKey;
bool m_GotKey;
IInput::CEvent m_Key;
2022-02-06 22:14:26 +00:00
int m_ModifierCombination;
2010-05-29 07:25:38 +00:00
CMenusKeyBinder();
virtual int Sizeof() const override { return sizeof(*this); }
virtual bool OnInput(const IInput::CEvent &Event) override;
2010-05-29 07:25:38 +00:00
};
class CMenus : public CComponent
{
static ColorRGBA ms_GuiColor;
static ColorRGBA ms_ColorTabbarInactiveOutgame;
static ColorRGBA ms_ColorTabbarActiveOutgame;
static ColorRGBA ms_ColorTabbarHoverOutgame;
static ColorRGBA ms_ColorTabbarInactiveIngame;
static ColorRGBA ms_ColorTabbarActiveIngame;
static ColorRGBA ms_ColorTabbarHoverIngame;
static ColorRGBA ms_ColorTabbarInactive;
static ColorRGBA ms_ColorTabbarActive;
static ColorRGBA ms_ColorTabbarHover;
2020-10-28 02:59:50 +00:00
static SColorPicker ms_ColorPicker;
2020-12-14 00:51:31 +00:00
static bool ms_ValueSelectorTextMode;
2020-10-28 02:59:50 +00:00
2020-10-12 10:29:47 +00:00
char m_aLocalStringHelper[1024];
2010-05-29 07:25:38 +00:00
int DoButton_DemoPlayer(const void *pID, const char *pText, int Checked, const CUIRect *pRect);
2022-08-19 05:05:02 +00:00
int DoButton_FontIcon(CButtonContainer *pButtonContainer, const char *pText, int Checked, const CUIRect *pRect, int Corners, bool Enabled = true);
int DoButton_Toggle(const void *pID, int Checked, const CUIRect *pRect, bool Active);
int DoButton_Menu(CButtonContainer *pButtonContainer, const char *pText, int Checked, const CUIRect *pRect, const char *pImageName = nullptr, int Corners = IGraphics::CORNER_ALL, float r = 5.0f, float FontFactor = 0.0f, vec4 ColorHot = vec4(1.0f, 1.0f, 1.0f, 0.75f), vec4 Color = vec4(1, 1, 1, 0.5f), bool CheckForActiveColorPicker = false);
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);
2010-05-29 07:25:38 +00:00
int DoButton_CheckBox_Common(const void *pID, const char *pText, const char *pBoxText, const CUIRect *pRect);
int DoButton_CheckBox(const void *pID, const char *pText, int Checked, const CUIRect *pRect);
int DoButton_CheckBoxAutoVMarginAndSet(const void *pID, const char *pText, int *pValue, CUIRect *pRect, float VMargin);
2010-05-29 07:25:38 +00:00
int DoButton_CheckBox_Number(const void *pID, const char *pText, int Checked, const CUIRect *pRect);
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);
void DoLaserPreview(const CUIRect *pRect, ColorHSLA OutlineColor, ColorHSLA InnerColor, const int LaserType);
int DoValueSelector(void *pID, CUIRect *pRect, const char *pLabel, bool UseScroll, int Current, int Min, int Max, int Step, float Scale, bool IsHex, float Round, ColorRGBA *pColor);
2010-05-29 07:25:38 +00:00
int DoButton_GridHeader(const void *pID, const char *pText, int Checked, const CUIRect *pRect);
void DoButton_KeySelect(const void *pID, const char *pText, const CUIRect *pRect);
int DoKeyReader(void *pID, const CUIRect *pRect, int Key, int ModifierCombination, int *pNewModifierCombination);
2010-05-29 07:25:38 +00:00
void DoSettingsControlsButtons(int Start, int Stop, CUIRect View);
2010-05-29 07:25:38 +00:00
float RenderSettingsControlsJoystick(CUIRect View);
void DoJoystickAxisPicker(CUIRect View);
void DoJoystickBar(const CUIRect *pRect, float Current, float Tolerance, bool Active);
2020-10-28 02:59:50 +00:00
void RenderColorPicker();
void RefreshSkins();
2023-03-24 22:14:17 +00:00
void RandomSkin();
2020-10-12 10:29:47 +00:00
// new gui with gui elements
template<typename T>
int DoButtonMenu(CUIElement &UIElement, const void *pID, T &&GetTextLambda, int Checked, const CUIRect *pRect, bool HintRequiresStringCheck, bool HintCanChangePositionOrSize = false, int Corners = IGraphics::CORNER_ALL, float r = 5.0f, float FontFactor = 0.0f, vec4 ColorHot = vec4(1.0f, 1.0f, 1.0f, 0.75f), vec4 Color = vec4(1, 1, 1, 0.5f))
2020-10-12 10:29:47 +00:00
{
CUIRect Text = *pRect;
Text.HMargin(pRect->h >= 20.0f ? 2.0f : 1.0f, &Text);
Text.HMargin((Text.h * FontFactor) / 2.0f, &Text);
if(!UIElement.AreRectsInit() || HintRequiresStringCheck || HintCanChangePositionOrSize || !UIElement.Rect(0)->m_UITextContainer.Valid())
2020-10-12 10:29:47 +00:00
{
bool NeedsRecalc = !UIElement.AreRectsInit() || !UIElement.Rect(0)->m_UITextContainer.Valid();
2020-10-12 10:29:47 +00:00
if(HintCanChangePositionOrSize)
{
2021-09-13 21:48:10 +00:00
if(UIElement.AreRectsInit())
2020-10-12 10:29:47 +00:00
{
if(UIElement.Rect(0)->m_X != pRect->x || UIElement.Rect(0)->m_Y != pRect->y || UIElement.Rect(0)->m_Width != pRect->w || UIElement.Rect(0)->m_Y != pRect->h)
2020-10-12 10:29:47 +00:00
{
NeedsRecalc = true;
}
}
}
const char *pText = nullptr;
2020-10-12 10:29:47 +00:00
if(HintRequiresStringCheck)
{
2021-09-13 21:48:10 +00:00
if(UIElement.AreRectsInit())
2020-10-12 10:29:47 +00:00
{
pText = GetTextLambda();
if(str_comp(UIElement.Rect(0)->m_Text.c_str(), pText) != 0)
2020-10-12 10:29:47 +00:00
{
NeedsRecalc = true;
}
}
}
if(NeedsRecalc)
{
2021-09-13 21:48:10 +00:00
if(!UIElement.AreRectsInit())
2020-10-12 10:29:47 +00:00
{
2021-09-13 21:48:10 +00:00
UIElement.InitRects(3);
2020-10-12 10:29:47 +00:00
}
2021-09-13 21:48:10 +00:00
UI()->ResetUIElement(UIElement);
2020-10-12 10:29:47 +00:00
vec4 RealColor = Color;
for(int i = 0; i < 3; ++i)
{
Color.a = RealColor.a;
if(i == 0)
2021-11-26 20:55:31 +00:00
Color.a *= UI()->ButtonColorMulActive();
2020-10-12 10:29:47 +00:00
else if(i == 1)
2021-11-26 20:55:31 +00:00
Color.a *= UI()->ButtonColorMulHot();
2020-10-12 10:29:47 +00:00
else if(i == 2)
2021-11-26 20:55:31 +00:00
Color.a *= UI()->ButtonColorMulDefault();
2020-10-12 10:29:47 +00:00
Graphics()->SetColor(Color);
CUIElement::SUIElementRect &NewRect = *UIElement.Rect(i);
NewRect.m_UIRectQuadContainer = Graphics()->CreateRectQuadContainer(pRect->x, pRect->y, pRect->w, pRect->h, r, Corners);
2020-10-12 10:29:47 +00:00
NewRect.m_X = pRect->x;
NewRect.m_Y = pRect->y;
NewRect.m_Width = pRect->w;
NewRect.m_Height = pRect->h;
if(i == 0)
{
if(pText == nullptr)
2020-10-12 10:29:47 +00:00
pText = GetTextLambda();
NewRect.m_Text = pText;
UI()->DoLabel(NewRect, &Text, pText, Text.h * CUI::ms_FontmodHeight, TEXTALIGN_MC);
2020-10-12 10:29:47 +00:00
}
}
Graphics()->SetColor(1, 1, 1, 1);
}
}
// render
size_t Index = 2;
if(UI()->CheckActiveItem(pID))
2020-10-12 10:29:47 +00:00
Index = 0;
else if(UI()->HotItem() == pID)
Index = 1;
Graphics()->TextureClear();
Graphics()->RenderQuadContainer(UIElement.Rect(Index)->m_UIRectQuadContainer, -1);
ColorRGBA ColorText(TextRender()->DefaultTextColor());
ColorRGBA ColorTextOutline(TextRender()->DefaultTextOutlineColor());
if(UIElement.Rect(0)->m_UITextContainer.Valid())
TextRender()->RenderTextContainer(UIElement.Rect(0)->m_UITextContainer, ColorText, ColorTextOutline);
2020-10-12 10:29:47 +00:00
return UI()->DoButtonLogic(pID, Checked, pRect);
}
2022-02-14 08:57:14 +00:00
/**
* Places and renders a tooltip near pNearRect.
* For now only works correctly with single line tooltips, since Text width calculation gets broken when there are multiple lines.
*
2022-04-07 07:46:02 +00:00
* @param pID The ID of the tooltip. Usually a reference to some g_Config value.
2022-02-14 08:57:14 +00:00
* @param pNearTo Place the tooltip near this rect.
* @param pText The text to display in the tooltip
*/
2022-04-07 07:46:02 +00:00
void DoToolTip(const void *pID, const CUIRect *pNearRect, const char *pText, float WidthHint = -1.0f);
2020-09-26 07:37:35 +00:00
// menus_settings_assets.cpp
public:
struct SCustomItem
{
IGraphics::CTextureHandle m_RenderTexture;
char m_aName[50];
bool operator<(const SCustomItem &Other) const { return str_comp(m_aName, Other.m_aName) < 0; }
};
struct SCustomEntities : public SCustomItem
{
struct SEntitiesImage
{
IGraphics::CTextureHandle m_Texture;
};
SEntitiesImage m_aImages[MAP_IMAGE_MOD_TYPE_COUNT];
};
struct SCustomGame : public SCustomItem
{
};
struct SCustomEmoticon : public SCustomItem
{
};
struct SCustomParticle : public SCustomItem
{
};
struct SCustomHud : public SCustomItem
{
};
2022-06-14 17:28:51 +00:00
struct SCustomExtras : public SCustomItem
{
};
2020-09-26 07:37:35 +00:00
protected:
std::vector<SCustomEntities> m_vEntitiesList;
std::vector<SCustomGame> m_vGameList;
std::vector<SCustomEmoticon> m_vEmoticonList;
std::vector<SCustomParticle> m_vParticlesList;
std::vector<SCustomHud> m_vHudList;
2022-06-14 17:28:51 +00:00
std::vector<SCustomExtras> m_vExtrasList;
2020-09-26 07:37:35 +00:00
bool m_IsInit = false;
2020-09-26 07:37:35 +00:00
static void LoadEntities(struct SCustomEntities *pEntitiesItem, void *pUser);
static int EntitiesScan(const char *pName, int IsDir, int DirType, void *pUser);
static int GameScan(const char *pName, int IsDir, int DirType, void *pUser);
static int EmoticonsScan(const char *pName, int IsDir, int DirType, void *pUser);
static int ParticlesScan(const char *pName, int IsDir, int DirType, void *pUser);
static int HudScan(const char *pName, int IsDir, int DirType, void *pUser);
2022-06-14 17:28:51 +00:00
static int ExtrasScan(const char *pName, int IsDir, int DirType, void *pUser);
2020-09-26 07:37:35 +00:00
static void ConchainAssetsEntities(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainAssetGame(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainAssetParticles(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainAssetEmoticons(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainAssetHud(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
2022-06-14 17:28:51 +00:00
static void ConchainAssetExtras(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
2020-09-26 07:37:35 +00:00
void ClearCustomItems(int CurTab);
int m_MenuPage;
2010-05-29 07:25:38 +00:00
int m_GamePage;
int m_Popup;
int m_ActivePage;
bool m_ShowStart;
2010-05-29 07:25:38 +00:00
bool m_MenuActive;
vec2 m_MousePos;
bool m_JoinTutorial;
char m_aNextServer[256];
// images
struct CMenuImage
{
char m_aName[64];
IGraphics::CTextureHandle m_OrgTexture;
IGraphics::CTextureHandle m_GreyTexture;
};
std::vector<CMenuImage> m_vMenuImages;
static int MenuImageScan(const char *pName, int IsDir, int DirType, void *pUser);
2020-09-10 18:14:47 +00:00
const CMenuImage *FindMenuImage(const char *pName);
2011-02-27 16:56:03 +00:00
// loading
int m_LoadCurrent;
int m_LoadTotal;
2010-05-29 07:25:38 +00:00
//
char m_aMessageTopic[512];
char m_aMessageBody[512];
char m_aMessageButton[512];
2020-10-12 10:29:47 +00:00
CUIElement m_RefreshButton;
CUIElement m_ConnectButton;
// generic popups
typedef void (CMenus::*FPopupButtonCallback)();
void DefaultButtonCallback()
{
// do nothing
}
enum
{
BUTTON_CONFIRM = 0, // confirm / yes / close / ok
BUTTON_CANCEL, // cancel / no
NUM_BUTTONS
};
char m_aPopupTitle[128];
char m_aPopupMessage[256];
struct
{
char m_aLabel[64];
int m_NextPopup;
FPopupButtonCallback m_pfnCallback;
} m_aPopupButtons[NUM_BUTTONS];
void PopupMessage(const char *pTitle, const char *pMessage,
const char *pButtonLabel, int NextPopup = POPUP_NONE, FPopupButtonCallback pfnButtonCallback = &CMenus::DefaultButtonCallback);
void PopupConfirm(const char *pTitle, const char *pMessage,
const char *pConfirmButtonLabel, const char *pCancelButtonLabel,
FPopupButtonCallback pfnConfirmButtonCallback = &CMenus::DefaultButtonCallback, int ConfirmNextPopup = POPUP_NONE,
FPopupButtonCallback pfnCancelButtonCallback = &CMenus::DefaultButtonCallback, int CancelNextPopup = POPUP_NONE);
2010-05-29 07:25:38 +00:00
// some settings
static float ms_ButtonHeight;
static float ms_ListheaderHeight;
2014-06-16 11:29:18 +00:00
static float ms_ListitemAdditionalHeight;
// for settings
bool m_NeedRestartGeneral;
bool m_NeedRestartSkins;
bool m_NeedRestartGraphics;
bool m_NeedRestartSound;
bool m_NeedRestartUpdate;
bool m_NeedRestartDDNet;
2010-05-29 07:25:38 +00:00
bool m_NeedSendinfo;
2014-04-28 13:19:57 +00:00
bool m_NeedSendDummyinfo;
int m_SettingPlayerPage;
2010-12-16 00:52:29 +00:00
// for map download popup
2021-06-23 05:05:49 +00:00
int64_t m_DownloadLastCheckTime;
2010-12-16 00:52:29 +00:00
int m_DownloadLastCheckSize;
float m_DownloadSpeed;
// for password popup
CLineInput m_PasswordInput;
2010-05-29 07:25:38 +00:00
// for call vote
int m_CallvoteSelectedOption;
int m_CallvoteSelectedPlayer;
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
CLineInputBuffered<VOTE_REASON_LENGTH> m_CallvoteReasonInput;
CLineInputBuffered<64> m_FilterInput;
bool m_ControlPageOpening;
2010-05-29 07:25:38 +00:00
// demo
enum
{
SORT_DEMONAME = 0,
SORT_MARKERS,
SORT_LENGTH,
SORT_DATE,
};
2010-05-29 07:25:38 +00:00
struct CDemoItem
{
char m_aFilename[IO_MAX_PATH_LENGTH];
char m_aName[IO_MAX_PATH_LENGTH];
bool m_IsDir;
2010-10-06 21:07:35 +00:00
int m_StorageType;
2015-08-27 12:57:56 +00:00
time_t m_Date;
bool m_InfosLoaded;
bool m_Valid;
CDemoHeader m_Info;
CTimelineMarkers m_TimelineMarkers;
2019-10-14 00:27:08 +00:00
CMapInfo m_MapInfo;
int NumMarkers() const
2015-08-27 12:57:56 +00:00
{
return clamp<int>(bytes_be_to_uint(m_TimelineMarkers.m_aNumTimelineMarkers), 0, MAX_TIMELINE_MARKERS);
}
int Length() const
{
return bytes_be_to_uint(m_Info.m_aLength);
}
unsigned Size() const
{
return bytes_be_to_uint(m_Info.m_aMapSize);
}
bool operator<(const CDemoItem &Other) const
{
if(!str_comp(m_aFilename, ".."))
return true;
if(!str_comp(Other.m_aFilename, ".."))
return false;
if(m_IsDir && !Other.m_IsDir)
return true;
if(!m_IsDir && Other.m_IsDir)
return false;
const CDemoItem &Left = g_Config.m_BrDemoSortOrder ? Other : *this;
const CDemoItem &Right = g_Config.m_BrDemoSortOrder ? *this : Other;
if(g_Config.m_BrDemoSort == SORT_DEMONAME)
return str_comp_filenames(Left.m_aFilename, Right.m_aFilename) < 0;
if(g_Config.m_BrDemoSort == SORT_DATE)
return Left.m_Date < Right.m_Date;
if(!Other.m_InfosLoaded)
return m_InfosLoaded;
if(!m_InfosLoaded)
return !Other.m_InfosLoaded;
if(g_Config.m_BrDemoSort == SORT_MARKERS)
return Left.NumMarkers() < Right.NumMarkers();
if(g_Config.m_BrDemoSort == SORT_LENGTH)
return Left.Length() < Right.Length();
// Unknown sort
return true;
2015-08-27 12:57:56 +00:00
}
2010-05-29 07:25:38 +00:00
};
char m_aCurrentDemoFolder[IO_MAX_PATH_LENGTH];
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
CLineInputBuffered<IO_MAX_PATH_LENGTH> m_DemoRenameInput;
CLineInputBuffered<IO_MAX_PATH_LENGTH> m_DemoSliceInput;
CLineInputBuffered<IO_MAX_PATH_LENGTH> m_DemoRenderInput;
int m_DemolistSelectedIndex;
bool m_DemolistSelectedIsDir;
int m_DemolistStorageType;
2019-09-28 04:18:38 +00:00
int m_Speed = 4;
2022-05-18 16:00:05 +00:00
std::chrono::nanoseconds m_DemoPopulateStartTime{0};
void DemolistOnUpdate(bool Reset);
static int DemolistFetchCallback(const CFsFileInfo *pInfo, int IsDir, int StorageType, void *pUser);
2011-03-23 12:06:35 +00:00
2011-06-26 15:10:13 +00:00
// friends
class CFriendItem
2011-06-26 15:10:13 +00:00
{
char m_aName[MAX_NAME_LENGTH];
char m_aClan[MAX_CLAN_LENGTH];
const CServerInfo *m_pServerInfo;
int m_FriendState;
bool m_IsPlayer;
2011-06-26 15:10:13 +00:00
public:
2022-05-24 19:20:04 +00:00
CFriendItem() {}
CFriendItem(const CFriendInfo *pFriendInfo) :
m_pServerInfo(nullptr), m_IsPlayer(false)
2022-05-24 19:20:04 +00:00
{
str_copy(m_aName, pFriendInfo->m_aName);
str_copy(m_aClan, pFriendInfo->m_aClan);
m_FriendState = m_aName[0] == '\0' ? IFriends::FRIEND_CLAN : IFriends::FRIEND_PLAYER;
2022-05-24 19:20:04 +00:00
}
CFriendItem(const char *pName, const char *pClan, const CServerInfo *pServerInfo, int FriendState, bool IsPlayer) :
m_pServerInfo(pServerInfo), m_FriendState(FriendState), m_IsPlayer(IsPlayer)
{
str_copy(m_aName, pName);
str_copy(m_aClan, pClan);
}
const char *Name() const { return m_aName; }
const char *Clan() const { return m_aClan; }
const CServerInfo *ServerInfo() const { return m_pServerInfo; }
int FriendState() const { return m_FriendState; }
bool IsPlayer() const { return m_IsPlayer; }
const void *ListItemId() const { return &m_aName; }
const void *RemoveButtonId() const { return &m_FriendState; }
2022-05-24 19:20:04 +00:00
2020-10-08 05:28:53 +00:00
bool operator<(const CFriendItem &Other) const
2011-06-26 15:10:13 +00:00
{
const int Result = str_comp(m_aName, Other.m_aName);
return Result < 0 || (Result == 0 && str_comp(m_aClan, Other.m_aClan) < 0);
2011-06-26 15:10:13 +00:00
}
};
enum
{
FRIEND_PLAYER_ON = 0,
FRIEND_CLAN_ON,
FRIEND_OFF,
NUM_FRIEND_TYPES
};
std::vector<CFriendItem> m_avFriends[NUM_FRIEND_TYPES];
const CFriendItem *m_pRemoveFriend = nullptr;
2011-06-26 15:10:13 +00:00
void FriendlistOnUpdate();
2010-05-29 07:25:38 +00:00
// found in menus.cpp
int Render();
#if defined(CONF_VIDEORECORDER)
void PopupConfirmDemoReplaceVideo();
#endif
2010-05-29 07:25:38 +00:00
int RenderMenubar(CUIRect r);
void RenderNews(CUIRect MainView);
static void ConchainUpdateMusicState(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
void UpdateMusicState();
2010-05-29 07:25:38 +00:00
// found in menus_demo.cpp
static bool DemoFilterChat(const void *pData, int Size, void *pUser);
bool FetchHeader(CDemoItem &Item);
void FetchAllHeaders();
void HandleDemoSeeking(float PositionToSeek, float TimeToSeek);
2010-05-29 07:25:38 +00:00
void RenderDemoPlayer(CUIRect MainView);
void RenderDemoList(CUIRect MainView);
void PopupConfirmDeleteDemo();
// found in menus_start.cpp
void RenderStartMenu(CUIRect MainView);
2010-05-29 07:25:38 +00:00
// found in menus_ingame.cpp
STextContainerIndex m_MotdTextContainerIndex;
2010-05-29 07:25:38 +00:00
void RenderGame(CUIRect MainView);
void PopupConfirmDisconnect();
void PopupConfirmDisconnectDummy();
void RenderPlayers(CUIRect MainView);
2010-05-29 07:25:38 +00:00
void RenderServerInfo(CUIRect MainView);
void RenderServerInfoMotd(CUIRect Motd);
2010-05-29 07:25:38 +00:00
void RenderServerControl(CUIRect MainView);
bool RenderServerControlKick(CUIRect MainView, bool FilterSpectators);
bool RenderServerControlServer(CUIRect MainView);
void RenderIngameHint();
2010-05-29 07:25:38 +00:00
// found in menus_browser.cpp
int m_SelectedIndex;
void RenderServerbrowserServerList(CUIRect View);
void Connect(const char *pAddress);
void PopupConfirmSwitchServer();
2010-05-29 07:25:38 +00:00
void RenderServerbrowserServerDetail(CUIRect View);
void RenderServerbrowserFilters(CUIRect View);
2011-03-23 12:06:35 +00:00
void RenderServerbrowserFriends(CUIRect View);
void PopupConfirmRemoveFriend();
2010-05-29 07:25:38 +00:00
void RenderServerbrowser(CUIRect MainView);
template<typename F>
bool PrintHighlighted(const char *pName, F &&PrintFn);
2011-06-26 15:10:13 +00:00
static void ConchainFriendlistUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainServerbrowserUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
2022-10-04 16:40:24 +00:00
// skin favorite list
bool m_SkinFavoritesChanged = false;
std::unordered_set<std::string> m_SkinFavorites;
static void Con_AddFavoriteSkin(IConsole::IResult *pResult, void *pUserData);
static void Con_RemFavoriteSkin(IConsole::IResult *pResult, void *pUserData);
static void ConfigSaveCallback(IConfigManager *pConfigManager, void *pUserData);
void OnConfigSave(IConfigManager *pConfigManager);
2010-05-29 07:25:38 +00:00
// found in menus_settings.cpp
bool RenderLanguageSelection(CUIRect MainView);
void RenderThemeSelection(CUIRect MainView);
2010-05-29 07:25:38 +00:00
void RenderSettingsGeneral(CUIRect MainView);
void RenderSettingsPlayer(CUIRect MainView);
void RenderSettingsDummyPlayer(CUIRect MainView);
2011-04-01 17:36:44 +00:00
void RenderSettingsTee(CUIRect MainView);
2010-05-29 07:25:38 +00:00
void RenderSettingsControls(CUIRect MainView);
void ResetSettingsControls();
2010-05-29 07:25:38 +00:00
void RenderSettingsGraphics(CUIRect MainView);
void RenderSettingsSound(CUIRect MainView);
void RenderSettings(CUIRect MainView);
2020-09-26 07:37:35 +00:00
void RenderSettingsCustom(CUIRect MainView);
void SetNeedSendInfo();
2010-05-29 07:25:38 +00:00
void SetActive(bool Active);
IGraphics::CTextureHandle m_TextureBlob;
bool CheckHotKey(int Key) const;
2020-09-18 16:45:42 +00:00
class CMenuBackground *m_pBackground;
2010-05-29 07:25:38 +00:00
public:
void RenderBackground();
2020-09-18 16:45:42 +00:00
void SetMenuBackground(class CMenuBackground *pBackground) { m_pBackground = pBackground; }
2010-05-29 07:25:38 +00:00
static CMenusKeyBinder m_Binder;
2010-05-29 07:25:38 +00:00
CMenus();
virtual int Sizeof() const override { return sizeof(*this); }
2010-05-29 07:25:38 +00:00
void RenderLoading(const char *pCaption, const char *pContent, int IncreaseCounter, bool RenderLoadingBar = true, bool RenderMenuBackgroundMap = true);
bool IsInit() { return m_IsInit; }
2010-05-29 07:25:38 +00:00
bool IsActive() const { return m_MenuActive; }
void KillServer();
2010-05-29 07:25:38 +00:00
virtual void OnInit() override;
2022-10-04 16:40:24 +00:00
void OnConsoleInit() override;
2010-05-29 07:25:38 +00:00
virtual void OnStateChange(int NewState, int OldState) override;
virtual void OnWindowResize() override;
virtual void OnReset() override;
virtual void OnRender() override;
virtual bool OnInput(const IInput::CEvent &Event) override;
virtual bool OnCursorMove(float x, float y, IInput::ECursorType CursorType) override;
virtual void OnShutdown() override;
2014-06-05 10:11:41 +00:00
enum
{
PAGE_NEWS = 1,
2014-06-05 10:11:41 +00:00
PAGE_GAME,
PAGE_PLAYERS,
PAGE_SERVER_INFO,
PAGE_CALLVOTE,
PAGE_INTERNET,
PAGE_LAN,
PAGE_FAVORITES,
2014-09-13 14:36:25 +00:00
PAGE_DDNET,
PAGE_KOG,
2014-06-05 10:11:41 +00:00
PAGE_DEMOS,
PAGE_SETTINGS,
PAGE_SYSTEM,
PAGE_NETWORK,
PAGE_GHOST,
PAGE_LENGTH,
SETTINGS_LANGUAGE = 0,
SETTINGS_GENERAL,
SETTINGS_PLAYER,
SETTINGS_TEE,
2022-06-26 22:38:49 +00:00
SETTINGS_APPEARANCE,
SETTINGS_CONTROLS,
SETTINGS_GRAPHICS,
SETTINGS_SOUND,
SETTINGS_DDNET,
2020-09-26 07:37:35 +00:00
SETTINGS_ASSETS,
SETTINGS_LENGTH,
BIG_TAB_NEWS = 0,
BIG_TAB_INTERNET,
BIG_TAB_LAN,
BIG_TAB_FAVORITES,
BIG_TAB_DDNET,
BIG_TAB_KOG,
BIG_TAB_DEMOS,
BIG_TAB_LENGTH,
SMALL_TAB_HOME = 0,
SMALL_TAB_QUIT,
SMALL_TAB_SETTINGS,
SMALL_TAB_EDITOR,
SMALL_TAB_DEMOBUTTON,
SMALL_TAB_SERVER,
SMALL_TAB_LENGTH,
2014-06-05 10:11:41 +00:00
};
SUIAnimator m_aAnimatorsBigPage[BIG_TAB_LENGTH];
SUIAnimator m_aAnimatorsSmallPage[SMALL_TAB_LENGTH];
SUIAnimator m_aAnimatorsSettingsTab[SETTINGS_LENGTH];
// DDRace
int DoButton_CheckBox_Tristate(const void *pID, const char *pText, TRISTATE Checked, const CUIRect *pRect);
std::vector<CDemoItem> m_vDemos;
void DemolistPopulate();
void DemoSeekTick(IDemoPlayer::ETickOffset TickOffset);
2015-08-28 18:44:07 +00:00
bool m_Dummy;
2017-09-09 21:10:42 +00:00
const char *GetCurrentDemoFolder() const { return m_aCurrentDemoFolder; }
// Ghost
struct CGhostItem
{
char m_aFilename[IO_MAX_PATH_LENGTH];
char m_aPlayer[MAX_NAME_LENGTH];
int m_Time;
int m_Slot;
bool m_Own;
CGhostItem() :
m_Slot(-1), m_Own(false) { m_aFilename[0] = 0; }
2020-10-08 05:28:53 +00:00
bool operator<(const CGhostItem &Other) const { return m_Time < Other.m_Time; }
bool Active() const { return m_Slot != -1; }
bool HasFile() const { return m_aFilename[0]; }
};
std::vector<CGhostItem> m_vGhosts;
2022-05-18 16:00:05 +00:00
std::chrono::nanoseconds m_GhostPopulateStartTime{0};
void GhostlistPopulate();
CGhostItem *GetOwnGhost();
2017-09-10 01:48:22 +00:00
void UpdateOwnGhost(CGhostItem Item);
void DeleteGhostItem(int Index);
int GetCurPopup() { return m_Popup; }
bool CanDisplayWarning();
2022-05-18 16:00:05 +00:00
void PopupWarning(const char *pTopic, const char *pBody, const char *pButton, std::chrono::nanoseconds Duration);
2022-05-18 16:00:05 +00:00
std::chrono::nanoseconds m_PopupWarningLastTime;
std::chrono::nanoseconds m_PopupWarningDuration;
2014-08-23 15:48:04 +00:00
int m_DemoPlayerState;
2014-08-25 09:19:00 +00:00
char m_aDemoPlayerPopupHint[256];
2014-08-23 15:48:04 +00:00
enum
{
POPUP_NONE = 0,
POPUP_MESSAGE, // generic message popup (one button)
POPUP_CONFIRM, // generic confirmation popup (two buttons)
POPUP_FIRST_LAUNCH,
POPUP_POINTS,
POPUP_CONNECTING,
POPUP_DISCONNECTED,
POPUP_LANGUAGE,
POPUP_COUNTRY,
POPUP_RENAME_DEMO,
2019-09-26 15:28:29 +00:00
POPUP_RENDER_DEMO,
POPUP_PASSWORD,
POPUP_QUIT,
POPUP_WARNING,
2014-08-23 15:48:04 +00:00
// demo player states
DEMOPLAYER_NONE = 0,
2014-08-23 15:48:04 +00:00
DEMOPLAYER_SLICE_SAVE,
};
private:
static int GhostlistFetchCallback(const char *pName, int IsDir, int StorageType, void *pUser);
void SetMenuPage(int NewPage);
void RefreshBrowserTab(int UiPage);
// found in menus_ingame.cpp
void RenderInGameNetwork(CUIRect MainView);
void RenderGhost(CUIRect MainView);
// found in menus_settings.cpp
void RenderSettingsDDNet(CUIRect MainView);
2022-06-26 22:38:49 +00:00
void RenderSettingsAppearance(CUIRect MainView);
ColorHSLA RenderHSLColorPicker(const CUIRect *pRect, unsigned int *pColor, bool Alpha);
2021-05-03 03:59:50 +00:00
ColorHSLA RenderHSLScrollbars(CUIRect *pRect, unsigned int *pColor, bool Alpha = false, bool ClampedLight = false);
2022-07-16 13:32:06 +00:00
int RenderDropDown(int &CurDropDownState, CUIRect *pRect, int CurSelection, const void **pIDs, const char **pStr, int PickNum, CButtonContainer *pButtonContainer, float &ScrollVal);
CServerProcess m_ServerProcess;
2010-05-29 07:25:38 +00:00
};
#endif