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_UI_H
4#define GAME_CLIENT_UI_H
5
6#include "lineinput.h"
7#include "ui_rect.h"
8
9#include <engine/input.h>
10#include <engine/textrender.h>
11
12#include <chrono>
13#include <string>
14#include <vector>
15
16class CScrollRegion;
17class IClient;
18class IGraphics;
19class IKernel;
20
21enum class EEditState
22{
23 NONE,
24 START,
25 EDITING,
26 END,
27 ONE_GO
28};
29
30template<typename T>
31struct SEditResult
32{
33 EEditState m_State;
34 T m_Value;
35};
36
37struct SUIAnimator
38{
39 bool m_Active;
40 bool m_ScaleLabel;
41 bool m_RepositionLabel;
42
43 std::chrono::nanoseconds m_Time;
44 float m_Value;
45
46 float m_XOffset;
47 float m_YOffset;
48 float m_WOffset;
49 float m_HOffset;
50};
51
52class IScrollbarScale
53{
54public:
55 virtual ~IScrollbarScale() = default;
56 virtual float ToRelative(int AbsoluteValue, int Min, int Max) const = 0;
57 virtual int ToAbsolute(float RelativeValue, int Min, int Max) const = 0;
58};
59class CLinearScrollbarScale : public IScrollbarScale
60{
61public:
62 float ToRelative(int AbsoluteValue, int Min, int Max) const override
63 {
64 return (AbsoluteValue - Min) / (float)(Max - Min);
65 }
66 int ToAbsolute(float RelativeValue, int Min, int Max) const override
67 {
68 return round_to_int(f: RelativeValue * (Max - Min) + Min + 0.1f);
69 }
70};
71class CLogarithmicScrollbarScale : public IScrollbarScale
72{
73private:
74 int m_MinAdjustment;
75
76public:
77 CLogarithmicScrollbarScale(int MinAdjustment)
78 {
79 m_MinAdjustment = std::max(a: MinAdjustment, b: 1); // must be at least 1 to support Min == 0 with logarithm
80 }
81 float ToRelative(int AbsoluteValue, int Min, int Max) const override
82 {
83 if(Min < m_MinAdjustment)
84 {
85 AbsoluteValue += m_MinAdjustment;
86 Min += m_MinAdjustment;
87 Max += m_MinAdjustment;
88 }
89 return (std::log(x: AbsoluteValue) - std::log(x: Min)) / (float)(std::log(x: Max) - std::log(x: Min));
90 }
91 int ToAbsolute(float RelativeValue, int Min, int Max) const override
92 {
93 int ResultAdjustment = 0;
94 if(Min < m_MinAdjustment)
95 {
96 Min += m_MinAdjustment;
97 Max += m_MinAdjustment;
98 ResultAdjustment = -m_MinAdjustment;
99 }
100 return round_to_int(f: std::exp(x: RelativeValue * (std::log(x: Max) - std::log(x: Min)) + std::log(x: Min))) + ResultAdjustment;
101 }
102};
103
104class IButtonColorFunction
105{
106public:
107 virtual ~IButtonColorFunction() = default;
108 virtual ColorRGBA GetColor(bool Active, bool Hovered) const = 0;
109};
110class CDarkButtonColorFunction : public IButtonColorFunction
111{
112public:
113 ColorRGBA GetColor(bool Active, bool Hovered) const override
114 {
115 if(Active)
116 return ColorRGBA(0.15f, 0.15f, 0.15f, 0.25f);
117 else if(Hovered)
118 return ColorRGBA(0.5f, 0.5f, 0.5f, 0.25f);
119 return ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f);
120 }
121};
122class CLightButtonColorFunction : public IButtonColorFunction
123{
124public:
125 ColorRGBA GetColor(bool Active, bool Hovered) const override
126 {
127 if(Active)
128 return ColorRGBA(1.0f, 1.0f, 1.0f, 0.4f);
129 else if(Hovered)
130 return ColorRGBA(1.0f, 1.0f, 1.0f, 0.6f);
131 return ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f);
132 }
133};
134class CScrollBarColorFunction : public IButtonColorFunction
135{
136public:
137 ColorRGBA GetColor(bool Active, bool Hovered) const override
138 {
139 if(Active)
140 return ColorRGBA(0.9f, 0.9f, 0.9f, 1.0f);
141 else if(Hovered)
142 return ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f);
143 return ColorRGBA(0.8f, 0.8f, 0.8f, 1.0f);
144 }
145};
146
147class CUi;
148
149class CUIElement
150{
151 friend class CUi;
152
153 CUi *m_pUI;
154
155 CUIElement(CUi *pUI, int RequestedRectCount) { Init(pUI, RequestedRectCount); }
156
157public:
158 struct SUIElementRect
159 {
160 CUIElement *m_pParent;
161
162 public:
163 int m_UIRectQuadContainer;
164 STextContainerIndex m_UITextContainer;
165
166 float m_X;
167 float m_Y;
168 float m_Width;
169 float m_Height;
170 float m_Rounding;
171 int m_Corners;
172
173 std::string m_Text;
174 int m_ReadCursorGlyphCount;
175
176 CTextCursor m_Cursor;
177
178 ColorRGBA m_TextColor;
179 ColorRGBA m_TextOutlineColor;
180
181 SUIElementRect();
182
183 ColorRGBA m_QuadColor;
184
185 void Reset();
186 void Draw(const CUIRect *pRect, ColorRGBA Color, int Corners, float Rounding);
187 };
188
189protected:
190 CUi *Ui() const { return m_pUI; }
191 std::vector<SUIElementRect> m_vUIRects;
192
193public:
194 CUIElement() = default;
195
196 void Init(CUi *pUI, int RequestedRectCount);
197
198 SUIElementRect *Rect(size_t Index)
199 {
200 return &m_vUIRects[Index];
201 }
202
203 bool AreRectsInit()
204 {
205 return !m_vUIRects.empty();
206 }
207
208 void InitRects(int RequestedRectCount);
209};
210
211struct SLabelProperties
212{
213 float m_MaxWidth = -1;
214 bool m_StopAtEnd = false;
215 bool m_EllipsisAtEnd = false;
216 bool m_EnableWidthCheck = true;
217 float m_MinimumFontSize = 5.0f;
218 std::vector<STextColorSplit> m_vColorSplits;
219
220 void SetColor(const ColorRGBA &Color);
221};
222
223class CLabelResult
224{
225public:
226 bool m_Truncated;
227};
228
229enum EButtonFlags : unsigned
230{
231 BUTTONFLAG_NONE = 0,
232 BUTTONFLAG_LEFT = 1 << 0,
233 BUTTONFLAG_RIGHT = 1 << 1,
234 BUTTONFLAG_MIDDLE = 1 << 2,
235
236 BUTTONFLAG_ALL = BUTTONFLAG_LEFT | BUTTONFLAG_RIGHT | BUTTONFLAG_MIDDLE,
237};
238
239struct SMenuButtonProperties
240{
241 int m_Checked = 0;
242 bool m_HintRequiresStringCheck = false;
243 bool m_HintCanChangePositionOrSize = false;
244 bool m_UseIconFont = false;
245 bool m_ShowDropDownIcon = false;
246 int m_Corners = IGraphics::CORNER_ALL;
247 float m_Rounding = 5.0f;
248 float m_FontFactor = 0.0f;
249 ColorRGBA m_Color = ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f);
250 unsigned m_Flags = BUTTONFLAG_LEFT;
251};
252
253class CUIElementBase
254{
255private:
256 static CUi *ms_pUi;
257
258public:
259 static void Init(CUi *pUI) { ms_pUi = pUI; }
260
261 IClient *Client() const;
262 IGraphics *Graphics() const;
263 IInput *Input() const;
264 ITextRender *TextRender() const;
265 CUi *Ui() const { return ms_pUi; }
266};
267
268class CButtonContainer
269{
270};
271
272struct SValueSelectorProperties
273{
274 bool m_UseScroll = true;
275 int64_t m_Step = 1;
276 float m_Scale = 1.0f;
277 bool m_IsHex = false;
278 int m_HexPrefix = 6;
279 ColorRGBA m_Color = ColorRGBA(0.0f, 0.0f, 0.0f, 0.4f);
280};
281
282struct SProgressSpinnerProperties
283{
284 float m_Progress = -1.0f; // between 0.0f and 1.0f, or negative for indeterminate progress
285 ColorRGBA m_Color = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f);
286 int m_Segments = 64;
287};
288
289/**
290 * Type safe UI ID for popup menus.
291 */
292struct SPopupMenuId
293{
294};
295
296struct SPopupMenuProperties
297{
298 int m_Corners = IGraphics::CORNER_ALL;
299 ColorRGBA m_BorderColor = ColorRGBA(0.5f, 0.5f, 0.5f, 0.75f);
300 ColorRGBA m_BackgroundColor = ColorRGBA(0.0f, 0.0f, 0.0f, 0.75f);
301};
302
303/**
304 * Text that keeps its text container across frames and is only rebuilt when the text or
305 * its layout changes, for text that is rendered every frame. The color is applied when
306 * rendering, so changing it does not rebuild the container.
307 *
308 * The container must be released with @link Reset @endlink before the text render drops
309 * its containers, which happens on window resize and language change.
310 */
311class CCachedText
312{
313 STextContainerIndex m_TextContainerIndex;
314 std::string m_Text;
315 float m_FontSize = -1.0f;
316 float m_LineWidth = -1.0f;
317 int m_CursorFlags = 0;
318 STextBoundingBox m_BoundingBox = {.m_X: 0.0f, .m_Y: 0.0f, .m_W: 0.0f, .m_H: 0.0f};
319 float m_MaxCharacterHeight = 0.0f;
320
321public:
322 CCachedText() = default;
323 // Copying would leave two owners for the same text container.
324 CCachedText(const CCachedText &) = delete;
325 CCachedText &operator=(const CCachedText &) = delete;
326
327 void Update(ITextRender *pTextRender, const char *pText, float FontSize, float LineWidth = -1.0f, int CursorFlags = TEXTFLAG_RENDER);
328 void Render(ITextRender *pTextRender, vec2 Pos, ColorRGBA Color) const;
329 void Reset(ITextRender *pTextRender);
330
331 float Width() const { return m_BoundingBox.m_W; }
332 float MaxCharacterHeight() const { return m_MaxCharacterHeight; }
333};
334
335class CUi
336{
337public:
338 /**
339 * These enum values are returned by popup menu functions to specify the behavior.
340 */
341 enum EPopupMenuFunctionResult
342 {
343 /**
344 * The current popup menu will be kept open.
345 */
346 POPUP_KEEP_OPEN = 0,
347
348 /**
349 * The current popup menu will be closed.
350 */
351 POPUP_CLOSE_CURRENT = 1,
352
353 /**
354 * The current popup menu and all popup menus above it will be closed.
355 */
356 POPUP_CLOSE_CURRENT_AND_DESCENDANTS = 2,
357 };
358
359 /**
360 * Callback that draws a popup menu.
361 *
362 * @param pContext The context object of the popup menu.
363 * @param View The UI rect where the popup menu's contents should be drawn.
364 * @param Active Whether this popup is active (the top-most popup).
365 * Only the active popup should handle key and mouse events.
366 *
367 * @return Value from the @link EPopupMenuFunctionResult @endlink enum.
368 */
369 typedef EPopupMenuFunctionResult (*FPopupMenuFunction)(void *pContext, CUIRect View, bool Active);
370
371 /**
372 * Callback that is called when one or more popups are closed.
373 */
374 typedef std::function<void()> FPopupMenuClosedCallback;
375
376 /**
377 * Represents the aggregated state of current touch events to control a user interface.
378 */
379 class CTouchState
380 {
381 friend class CUi;
382
383 bool m_SecondaryPressedNext = false;
384 float m_SecondaryActivationTime = 0.0f;
385 vec2 m_SecondaryActivationDelta = vec2(0.0f, 0.0f);
386
387 public:
388 bool m_AnyPressed = false;
389 bool m_PrimaryPressed = false;
390 bool m_SecondaryPressed = false;
391 vec2 m_PrimaryPosition = vec2(-1.0f, -1.0f);
392 vec2 m_PrimaryDelta = vec2(0.0f, 0.0f);
393 vec2 m_ScrollAmount = vec2(0.0f, 0.0f);
394 };
395
396private:
397 bool m_Enabled;
398
399 const void *m_pHotItem = nullptr;
400 const void *m_pActiveItem = nullptr;
401 const void *m_pLastActiveItem = nullptr; // only used internally to track active CLineInput
402 const void *m_pBecomingHotItem = nullptr;
403 CScrollRegion *m_pHotScrollRegion = nullptr;
404 CScrollRegion *m_pBecomingHotScrollRegion = nullptr;
405 bool m_ActiveItemValid = false;
406
407 int m_ActiveButtonLogicButton = -1;
408 int m_ActiveDraggableButtonLogicButton = -1;
409 class CDoubleClickState
410 {
411 public:
412 const void *m_pLastClickedId = nullptr;
413 float m_LastClickTime = -1.0f;
414 vec2 m_LastClickPos = vec2(-1.0f, -1.0f);
415 };
416 CDoubleClickState m_DoubleClickState;
417 const void *m_pLastEditingItem = nullptr;
418 const void *m_pLastActiveScrollbar = nullptr;
419 int m_ScrollbarValue = 0;
420 float m_ActiveScrollbarOffset = 0.0f;
421 float m_ProgressSpinnerOffset = 0.0f;
422 class CValueSelectorState
423 {
424 public:
425 int m_Button = -1;
426 bool m_DidScroll = false;
427 float m_ScrollValue = 0.0f;
428 CLineInputNumber m_NumberInput;
429 const void *m_pLastTextId = nullptr;
430 };
431 CValueSelectorState m_ActiveValueSelectorState;
432
433 vec2 m_UpdatedMousePos = vec2(0.0f, 0.0f); // in window screen space
434 vec2 m_UpdatedMouseDelta = vec2(0.0f, 0.0f); // in window screen space
435 vec2 m_MousePos = vec2(0.0f, 0.0f); // in gui space
436 vec2 m_MouseDelta = vec2(0.0f, 0.0f); // in gui space
437 unsigned m_UpdatedMouseButtons = 0;
438 unsigned m_MouseButtons = 0;
439 unsigned m_LastMouseButtons = 0;
440 CTouchState m_TouchState;
441 bool m_MouseSlow = false;
442 bool m_MouseLock = false;
443 const void *m_pMouseLockId = nullptr;
444
445 unsigned m_HotkeysPressed = 0;
446
447 enum class EBackButtonOp
448 {
449 NONE,
450 CLICKED,
451 DRAGGING,
452 };
453 EBackButtonOp m_BackButtonOp = EBackButtonOp::NONE;
454 vec2 m_BackButtonDragOffset = vec2(0.0f, 0.0f);
455 vec2 m_BackButtonInitialMouse = vec2(0.0f, 0.0f);
456 CUIRect m_BackButtonRect = {.x: 0.0f, .y: 0.0f, .w: 0.0f, .h: 0.0f};
457 const char m_BackButtonId = 0;
458
459 std::function<void(const IInput::CEvent &Event)> m_DispatchInputFunction;
460 std::function<void()> m_OnBackButtonPressedFunction;
461
462 CUIRect m_Screen;
463
464 std::vector<CUIRect> m_vClips;
465 void UpdateClipping();
466
467 struct SPopupMenu
468 {
469 static constexpr float POPUP_BORDER = 1.0f;
470 static constexpr float POPUP_MARGIN = 4.0f;
471
472 const SPopupMenuId *m_pId;
473 SPopupMenuProperties m_Props;
474 CUIRect m_Rect;
475 void *m_pContext;
476 FPopupMenuFunction m_pfnFunc;
477 };
478 std::vector<SPopupMenu> m_vPopupMenus;
479 FPopupMenuClosedCallback m_pfnPopupMenuClosedCallback = nullptr;
480
481 static CUi::EPopupMenuFunctionResult PopupMessage(void *pContext, CUIRect View, bool Active);
482 static CUi::EPopupMenuFunctionResult PopupConfirm(void *pContext, CUIRect View, bool Active);
483 static CUi::EPopupMenuFunctionResult PopupSelection(void *pContext, CUIRect View, bool Active);
484 static CUi::EPopupMenuFunctionResult PopupColorPicker(void *pContext, CUIRect View, bool Active);
485
486 IClient *m_pClient;
487 IGraphics *m_pGraphics;
488 IInput *m_pInput;
489 ITextRender *m_pTextRender;
490
491 std::vector<CUIElement *> m_vpOwnUIElements; // ui elements maintained by CUi class
492 std::vector<CUIElement *> m_vpUIElements;
493
494public:
495 static const CLinearScrollbarScale ms_LinearScrollbarScale;
496 static const CLogarithmicScrollbarScale ms_LogarithmicScrollbarScale;
497 static const CDarkButtonColorFunction ms_DarkButtonColorFunction;
498 static const CLightButtonColorFunction ms_LightButtonColorFunction;
499 static const CScrollBarColorFunction ms_ScrollBarColorFunction;
500
501 static const float ms_FontmodHeight;
502
503 void Init(IKernel *pKernel);
504 IClient *Client() const { return m_pClient; }
505 IGraphics *Graphics() const { return m_pGraphics; }
506 IInput *Input() const { return m_pInput; }
507 ITextRender *TextRender() const { return m_pTextRender; }
508
509 CUi();
510 ~CUi();
511
512 enum EHotkey : unsigned
513 {
514 HOTKEY_ENTER = 1 << 0,
515 HOTKEY_ESCAPE = 1 << 1,
516 HOTKEY_UP = 1 << 2,
517 HOTKEY_DOWN = 1 << 3,
518 HOTKEY_LEFT = 1 << 4,
519 HOTKEY_RIGHT = 1 << 5,
520 HOTKEY_DELETE = 1 << 6,
521 HOTKEY_TAB = 1 << 7,
522 HOTKEY_SCROLL_UP = 1 << 8,
523 HOTKEY_SCROLL_DOWN = 1 << 9,
524 HOTKEY_PAGE_UP = 1 << 10,
525 HOTKEY_PAGE_DOWN = 1 << 11,
526 HOTKEY_HOME = 1 << 12,
527 HOTKEY_END = 1 << 13,
528 };
529
530 void ResetUIElement(CUIElement &UIElement) const;
531
532 CUIElement *GetNewUIElement(int RequestedRectCount);
533
534 void AddUIElement(CUIElement *pElement);
535 void OnElementsReset();
536 void OnWindowResize();
537 void OnCursorMove(float X, float Y);
538
539 void SetEnabled(bool Enabled) { m_Enabled = Enabled; }
540 bool Enabled() const { return m_Enabled; }
541 void Update();
542 void DebugRender(float X, float Y);
543
544 vec2 MousePos() const { return m_MousePos; }
545 float MouseX() const { return m_MousePos.x; }
546 float MouseY() const { return m_MousePos.y; }
547 vec2 MouseDelta() const { return m_MouseDelta; }
548 float MouseDeltaX() const { return m_MouseDelta.x; }
549 float MouseDeltaY() const { return m_MouseDelta.y; }
550 vec2 UpdatedMousePos() const { return m_UpdatedMousePos; }
551 vec2 UpdatedMouseDelta() const { return m_UpdatedMouseDelta; }
552 int MouseButton(int Index) const { return (m_MouseButtons >> Index) & 1; }
553 int MouseButtonClicked(int Index) const { return MouseButton(Index) && !((m_LastMouseButtons >> Index) & 1); }
554 bool CheckMouseLock()
555 {
556 if(m_MouseLock && ActiveItem() != m_pMouseLockId)
557 DisableMouseLock();
558 return m_MouseLock;
559 }
560 void EnableMouseLock(const void *pId)
561 {
562 m_MouseLock = true;
563 m_pMouseLockId = pId;
564 }
565 void DisableMouseLock() { m_MouseLock = false; }
566
567 void SetHotItem(const void *pId) { m_pBecomingHotItem = pId; }
568 void SetActiveItem(const void *pId)
569 {
570 m_ActiveItemValid = true;
571 m_pActiveItem = pId;
572 if(pId)
573 m_pLastActiveItem = pId;
574 }
575 bool CheckActiveItem(const void *pId)
576 {
577 if(m_pActiveItem == pId)
578 {
579 m_ActiveItemValid = true;
580 return true;
581 }
582 return false;
583 }
584 void SetHotScrollRegion(CScrollRegion *pId) { m_pBecomingHotScrollRegion = pId; }
585 const void *HotItem() const { return m_pHotItem; }
586 const void *NextHotItem() const { return m_pBecomingHotItem; }
587 const void *ActiveItem() const { return m_pActiveItem; }
588 const CScrollRegion *HotScrollRegion() const { return m_pHotScrollRegion; }
589
590 void StartCheck() { m_ActiveItemValid = false; }
591 void FinishCheck()
592 {
593 if(!m_ActiveItemValid && m_pActiveItem != nullptr)
594 {
595 SetActiveItem(nullptr);
596 m_pHotItem = nullptr;
597 m_pBecomingHotItem = nullptr;
598 }
599 }
600
601 bool MouseInside(const CUIRect *pRect) const;
602 bool MouseInsideClip() const { return !IsClipped() || MouseInside(pRect: ClipArea()); }
603 bool MouseHovered(const CUIRect *pRect) const { return MouseInside(pRect) && MouseInsideClip(); }
604 void ConvertMouseMove(float *pX, float *pY, IInput::ECursorType CursorType) const;
605 void UpdateTouchState(CTouchState &State) const;
606 void SetMouseSlow(bool MouseSlow) { m_MouseSlow = MouseSlow; }
607
608 bool ConsumeHotkey(EHotkey Hotkey);
609 void ClearHotkeys() { m_HotkeysPressed = 0; }
610 bool OnInput(const IInput::CEvent &Event);
611
612 constexpr float ButtonColorMulActive() const { return 0.5f; }
613 constexpr float ButtonColorMulHot() const { return 1.5f; }
614 constexpr float ButtonColorMulDefault() const { return 1.0f; }
615 float ButtonColorMul(const void *pId);
616
617 const CUIRect *Screen();
618 void MapScreen();
619 float PixelSize();
620
621 void ClipEnable(const CUIRect *pRect);
622 void ClipDisable();
623 const CUIRect *ClipArea() const;
624 bool IsClipped() const { return !m_vClips.empty(); }
625
626 int DoButtonLogic(const void *pId, int Checked, const CUIRect *pRect, unsigned Flags);
627 int DoDraggableButtonLogic(const void *pId, int Checked, const CUIRect *pRect, bool *pClicked, bool *pAbrupted);
628 bool DoDoubleClickLogic(const void *pId);
629 EEditState DoPickerLogic(const void *pId, const CUIRect *pRect, float *pX, float *pY);
630 void DoSmoothScrollLogic(float *pScrollOffset, float *pScrollOffsetChange, float ViewPortSize, float TotalSize, bool SmoothClamp = false, float ScrollSpeed = 10.0f) const;
631 static vec2 CalcAlignedCursorPos(const CUIRect *pRect, vec2 TextSize, int Align, const float *pBiggestCharHeight = nullptr);
632
633 CLabelResult DoLabel(const CUIRect *pRect, const char *pText, float Size, int Align, const SLabelProperties &LabelProps = {}) const;
634 CLabelResult DoLabel_AutoLineSize(const char *pText, float FontSize, int Align, CUIRect *pRect, float LineSize, const SLabelProperties &LabelProps = {}) const;
635
636 void DoLabel(CUIElement::SUIElementRect &RectEl, const CUIRect *pRect, const char *pText, float Size, int Align, const SLabelProperties &LabelProps = {}, int StrLen = -1, const CTextCursor *pReadCursor = nullptr) const;
637 void DoLabelStreamed(CUIElement::SUIElementRect &RectEl, const CUIRect *pRect, const char *pText, float Size, int Align, const SLabelProperties &LabelProps = {}, int StrLen = -1, const CTextCursor *pReadCursor = nullptr) const;
638
639 /**
640 * Creates an input field.
641 *
642 * @see DoClearableEditBox
643 *
644 * @param pLineInput This pointer will be stored and written to on next user input.
645 * So you can not pass in a pointer that goes out of scope such as a local variable.
646 * Pass in either a member variable of the current class or a static variable.
647 * For example ```static CLineInputBuffered<IO_MAX_PATH_LENGTH> s_MyInput;```
648 * @param pRect the UI rect it will attach to with a 2.0f margin
649 * @param FontSize Size of the font (`10.0f`, `12.0f` and `14.0f` are commonly used here)
650 * @param Corners Number of corners (default: `IGraphics::CORNER_ALL`)
651 * @param vColorSplits Sets color splits of the `CTextCursor` to allow multicolored text
652 *
653 * @return true if the value of the input field changed since the last call.
654 */
655 bool DoEditBox(CLineInput *pLineInput, const CUIRect *pRect, float FontSize, int Corners = IGraphics::CORNER_ALL, const std::vector<STextColorSplit> &vColorSplits = {});
656
657 /**
658 * Creates an input field with a clear [x] button attached to it.
659 *
660 * @see DoEditBox
661 *
662 * @param pLineInput This pointer will be stored and written to on next user input.
663 * So you can not pass in a pointer that goes out of scope such as a local variable.
664 * Pass in either a member variable of the current class or a static variable.
665 * For example ```static CLineInputBuffered<IO_MAX_PATH_LENGTH> s_MyInput;```
666 * @param pRect the UI rect it will attach to
667 * @param FontSize Size of the font (`10.0f`, `12.0f` and `14.0f` are commonly used here)
668 * @param Corners Number of corners (default: `IGraphics::CORNER_ALL`)
669 * @param vColorSplits Sets color splits of the `CTextCursor` to allow multicolored text
670 *
671 * @return true if the value of the input field changed since the last call.
672 */
673 bool DoClearableEditBox(CLineInput *pLineInput, const CUIRect *pRect, float FontSize, int Corners = IGraphics::CORNER_ALL, const std::vector<STextColorSplit> &vColorSplits = {});
674
675 /**
676 * Creates an input field with a search icon and a clear [x] button attached to it.
677 * The input will have default text "Search" and the hotkey Ctrl+F can be used to activate the input.
678 *
679 * @see DoEditBox
680 *
681 * @param pLineInput This pointer will be stored and written to on next user input.
682 * So you can not pass in a pointer that goes out of scope such as a local variable.
683 * Pass in either a member variable of the current class or a static variable.
684 * For example ```static CLineInputBuffered<IO_MAX_PATH_LENGTH> s_MyInput;```
685 * @param pRect the UI rect it will attach to
686 * @param FontSize Size of the font (`10.0f`, `12.0f` and `14.0f` are commonly used here)
687 * @param HotkeyEnabled Whether the hotkey to enable this editbox is currently enabled.
688 *
689 * @return true if the value of the input field changed since the last call.
690 */
691 bool DoEditBox_Search(CLineInput *pLineInput, const CUIRect *pRect, float FontSize, bool HotkeyEnabled);
692
693 int DoButton_Menu(CUIElement &UIElement, const CButtonContainer *pId, const std::function<const char *()> &GetTextLambda, const CUIRect *pRect, const SMenuButtonProperties &Props = {});
694 int DoButton_FontIcon(CButtonContainer *pButtonContainer, const char *pText, int Checked, const CUIRect *pRect, unsigned Flags, int Corners = IGraphics::CORNER_ALL, bool Enabled = true, std::optional<ColorRGBA> ButtonColor = std::nullopt);
695 // only used for popup menus
696 int DoButton_PopupMenu(CButtonContainer *pButtonContainer, const char *pText, const CUIRect *pRect, float Size, int Align, float Padding = 0.0f, bool TransparentInactive = false, bool Enabled = true, std::optional<ColorRGBA> ButtonColor = std::nullopt);
697
698 // value selector
699 SEditResult<int64_t> DoValueSelectorWithState(const void *pId, const CUIRect *pRect, const char *pLabel, int64_t Current, int64_t Min, int64_t Max, const SValueSelectorProperties &Props = {});
700 int64_t DoValueSelector(const void *pId, const CUIRect *pRect, const char *pLabel, int64_t Current, int64_t Min, int64_t Max, const SValueSelectorProperties &Props = {});
701
702 // scrollbars
703 enum
704 {
705 SCROLLBAR_OPTION_INFINITE = 1 << 0,
706 SCROLLBAR_OPTION_NOCLAMPVALUE = 1 << 1,
707 SCROLLBAR_OPTION_MULTILINE = 1 << 2,
708 SCROLLBAR_OPTION_DELAYUPDATE = 1 << 3,
709 };
710 float DoScrollbarV(const void *pId, const CUIRect *pRect, float Current);
711 float DoScrollbarH(const void *pId, const CUIRect *pRect, float Current, const ColorRGBA *pColorInner = nullptr);
712 bool DoScrollbarOption(const void *pId, int *pOption, const CUIRect *pRect, const char *pStr, int Min, int Max, const IScrollbarScale *pScale = &ms_LinearScrollbarScale, unsigned Flags = 0u, const char *pSuffix = "");
713
714 // progress bar
715 void RenderProgressBar(CUIRect ProgressBar, float Progress);
716
717 // render time with hundredths or thousands aligned to the right of the UIRect
718 void RenderTime(CUIRect TimeRect, float FontSize, int Seconds, bool NotFinished, int Millis, bool TrueMilliseconds, CCachedText &SecondsText, CCachedText &MillisText, ColorRGBA Color) const;
719
720 // progress spinner
721 void RenderProgressSpinner(vec2 Center, float OuterRadius, const SProgressSpinnerProperties &Props = {}) const;
722
723 // virtual back button
724 void DoBackButton();
725 void RenderBackButton();
726 void SetDispatchInputCallback(std::function<void(const IInput::CEvent &Event)> pfnCallback) { m_DispatchInputFunction = std::move(pfnCallback); }
727 // Fired the moment the back button transitions to active (mouse-down inside it).
728 void SetOnBackButtonPressedCallback(std::function<void()> pfnCallback) { m_OnBackButtonPressedFunction = std::move(pfnCallback); }
729
730 // popup menu
731 void DoPopupMenu(const SPopupMenuId *pId, float X, float Y, float Width, float Height, void *pContext, FPopupMenuFunction pfnFunc, const SPopupMenuProperties &Props = {});
732 void RenderPopupMenus();
733 void ClosePopupMenu(const SPopupMenuId *pId, bool IncludeDescendants = false);
734 void ClosePopupMenus();
735 bool IsPopupOpen() const;
736 bool IsPopupOpen(const SPopupMenuId *pId) const;
737 bool IsPopupHovered() const;
738 void SetPopupMenuClosedCallback(FPopupMenuClosedCallback pfnCallback);
739
740 struct SMessagePopupContext : public SPopupMenuId
741 {
742 static constexpr float POPUP_MAX_WIDTH = 200.0f;
743 static constexpr float POPUP_FONT_SIZE = 10.0f;
744
745 CUi *m_pUI; // set by CUi when popup is shown
746 char m_aMessage[1024];
747 ColorRGBA m_TextColor;
748
749 void DefaultColor(class ITextRender *pTextRender);
750 void ErrorColor();
751 };
752 void ShowPopupMessage(float X, float Y, SMessagePopupContext *pContext);
753
754 struct SConfirmPopupContext : public SPopupMenuId
755 {
756 enum EConfirmationResult
757 {
758 UNSET = 0,
759 CONFIRMED,
760 CANCELED,
761 };
762 static constexpr float POPUP_MAX_WIDTH = 200.0f;
763 static constexpr float POPUP_FONT_SIZE = 10.0f;
764 static constexpr float POPUP_BUTTON_HEIGHT = 12.0f;
765 static constexpr float POPUP_BUTTON_SPACING = 5.0f;
766
767 CUi *m_pUI; // set by CUi when popup is shown
768 char m_aPositiveButtonLabel[128];
769 char m_aNegativeButtonLabel[128];
770 char m_aMessage[1024];
771 EConfirmationResult m_Result;
772
773 CButtonContainer m_CancelButton;
774 CButtonContainer m_ConfirmButton;
775
776 SConfirmPopupContext();
777 void Reset();
778 void YesNoButtons();
779 };
780 void ShowPopupConfirm(float X, float Y, SConfirmPopupContext *pContext);
781
782 struct SSelectionPopupContext : public SPopupMenuId
783 {
784 CUi *m_pUI; // set by CUi when popup is shown
785 CScrollRegion *m_pScrollRegion;
786 SPopupMenuProperties m_Props;
787 char m_aMessage[256];
788 std::vector<std::string> m_vEntries;
789 std::vector<CButtonContainer> m_vButtonContainers;
790 const std::string *m_pSelection;
791 int m_SelectionIndex;
792 float m_EntryHeight;
793 float m_EntryPadding;
794 float m_EntrySpacing;
795 float m_FontSize;
796 float m_Width;
797 float m_AlignmentHeight;
798 bool m_TransparentButtons;
799
800 SSelectionPopupContext();
801 void Reset();
802 };
803 void ShowPopupSelection(float X, float Y, SSelectionPopupContext *pContext);
804
805 struct SColorPickerPopupContext : public SPopupMenuId
806 {
807 enum EColorPickerMode
808 {
809 MODE_UNSET = -1,
810 MODE_HSVA,
811 MODE_RGBA,
812 MODE_HSLA,
813 };
814
815 CUi *m_pUI; // set by CUi when popup is shown
816 EColorPickerMode m_ColorMode = MODE_UNSET;
817 bool m_Alpha = false;
818 unsigned int *m_pHslaColor = nullptr; // may be nullptr
819 ColorHSVA m_HsvaColor;
820 ColorRGBA m_RgbaColor;
821 ColorHSLA m_HslaColor;
822 // UI element IDs
823 const char m_HuePickerId = 0;
824 const char m_ColorPickerId = 0;
825 const char m_aValueSelectorIds[5] = {0};
826 CButtonContainer m_aModeButtons[(int)MODE_HSLA + 1];
827 EEditState m_State = EEditState::NONE;
828 };
829 void ShowPopupColorPicker(float X, float Y, SColorPickerPopupContext *pContext);
830
831 // dropdown menu
832 struct SDropDownState
833 {
834 SSelectionPopupContext m_SelectionPopupContext;
835 CUIElement m_UiElement;
836 CButtonContainer m_ButtonContainer;
837 bool m_Init = false;
838 };
839 int DoDropDown(CUIRect *pRect, int CurSelection, const char **pStrs, int Num, SDropDownState &State);
840};
841
842#endif
843