1#ifndef GAME_CLIENT_COMPONENTS_TOUCH_CONTROLS_H
2#define GAME_CLIENT_COMPONENTS_TOUCH_CONTROLS_H
3
4#include <base/color.h>
5#include <base/vmath.h>
6
7#include <engine/input.h>
8
9#include <game/client/component.h>
10#include <game/client/ui_rect.h>
11
12#include <array>
13#include <chrono>
14#include <functional>
15#include <memory>
16#include <optional>
17#include <string>
18#include <vector>
19
20class CJsonWriter;
21typedef struct _json_value json_value;
22
23class CTouchControls : public CComponent
24{
25public:
26 static constexpr const int BUTTON_SIZE_SCALE = 1000000;
27 static constexpr const int BUTTON_SIZE_MINIMUM = 50000;
28 static constexpr const int BUTTON_SIZE_MAXIMUM = 500000;
29 enum class EDirectTouchIngameMode
30 {
31 DISABLED,
32 ACTION,
33 AIM,
34 AIM_RELATIVE,
35 FIRE,
36 HOOK,
37 NUM_STATES
38 };
39 enum class EDirectTouchSpectateMode
40 {
41 DISABLED,
42 AIM,
43 NUM_STATES
44 };
45
46 int Sizeof() const override { return sizeof(*this); }
47 void OnInit() override;
48 void OnReset() override;
49 void OnWindowResize() override;
50 bool OnTouchState(const std::vector<IInput::CTouchFingerState> &vTouchFingerStates) override;
51 void OnRender() override;
52
53 bool LoadConfigurationFromFile(int StorageType);
54 bool LoadConfigurationFromClipboard();
55 bool SaveConfigurationToFile();
56 void SaveConfigurationToClipboard();
57
58 EDirectTouchIngameMode DirectTouchIngame() const { return m_DirectTouchIngame; }
59 void SetDirectTouchIngame(EDirectTouchIngameMode DirectTouchIngame)
60 {
61 m_DirectTouchIngame = DirectTouchIngame;
62 m_EditingChanges = true;
63 }
64 EDirectTouchSpectateMode DirectTouchSpectate() const { return m_DirectTouchSpectate; }
65 void SetDirectTouchSpectate(EDirectTouchSpectateMode DirectTouchSpectate)
66 {
67 m_DirectTouchSpectate = DirectTouchSpectate;
68 m_EditingChanges = true;
69 }
70 bool IsEditingActive() const { return m_EditingActive; }
71 void SetEditingActive(bool EditingActive) { m_EditingActive = EditingActive; }
72 bool HasEditingChanges() const { return m_EditingChanges; }
73 void SetEditingChanges(bool EditingChanges) { m_EditingChanges = EditingChanges; }
74
75 class CUnitRect
76 {
77 public:
78 int m_X;
79 int m_Y;
80 int m_W;
81 int m_H;
82 float Distance(const CUnitRect &Other) const;
83 bool IsOverlap(const CUnitRect &Other) const
84 {
85 return (m_X < Other.m_X + Other.m_W) && (m_X + m_W > Other.m_X) && (m_Y < Other.m_Y + Other.m_H) && (m_Y + m_H > Other.m_Y);
86 }
87 bool operator==(const CUnitRect &Other) const
88 {
89 return m_X == Other.m_X && m_Y == Other.m_Y && m_W == Other.m_W && m_H == Other.m_H;
90 }
91 };
92
93 enum class EButtonVisibility
94 {
95 INGAME,
96 ZOOM_ALLOWED,
97 VOTE_ACTIVE,
98 DUMMY_ALLOWED,
99 DUMMY_CONNECTED,
100 RCON_AUTHED,
101 DEMO_PLAYER,
102 EXTRA_MENU_1,
103 EXTRA_MENU_2,
104 EXTRA_MENU_3,
105 EXTRA_MENU_4,
106 EXTRA_MENU_5,
107 NUM_VISIBILITIES
108 };
109 static const constexpr int MAX_EXTRA_MENU_NUMBER = (int)EButtonVisibility::EXTRA_MENU_5 - (int)EButtonVisibility::EXTRA_MENU_1 + 1;
110
111 enum class EButtonShape
112 {
113 RECT,
114 CIRCLE,
115 NUM_SHAPES
116 };
117
118 class CButtonLabel
119 {
120 public:
121 enum class EType
122 {
123 /**
124 * Label is used as is.
125 */
126 PLAIN,
127 /**
128 * Label is localized. Only usable for default button labels for which there must be
129 * corresponding `Localizable`-calls in code and string in the translation files.
130 */
131 LOCALIZED,
132 /**
133 * Icon font is used for the label.
134 */
135 ICON,
136 /**
137 * Number of label types.
138 */
139 NUM_TYPES
140 };
141
142 EType m_Type;
143 const char *m_pLabel;
144 };
145
146private:
147 static constexpr const char *const DIRECT_TOUCH_INGAME_MODE_NAMES[(int)EDirectTouchIngameMode::NUM_STATES] = {"disabled", "action", "aim", "aim-relative", "fire", "hook"};
148 static constexpr const char *const DIRECT_TOUCH_SPECTATE_MODE_NAMES[(int)EDirectTouchSpectateMode::NUM_STATES] = {"disabled", "aim"};
149 static constexpr const char *const SHAPE_NAMES[(int)EButtonShape::NUM_SHAPES] = {"rect", "circle"};
150
151 class CButtonVisibility
152 {
153 public:
154 EButtonVisibility m_Type;
155 bool m_Parity;
156
157 CButtonVisibility(EButtonVisibility Type, bool Parity) :
158 m_Type(Type), m_Parity(Parity) {}
159 };
160
161 class CButtonVisibilityData
162 {
163 public:
164 const char *m_pId;
165 std::function<bool()> m_Function;
166 };
167
168 CButtonVisibilityData m_aVisibilityFunctions[(int)EButtonVisibility::NUM_VISIBILITIES];
169
170 enum
171 {
172 ACTION_AIM,
173 ACTION_FIRE,
174 ACTION_HOOK,
175 NUM_ACTIONS
176 };
177
178 static constexpr const char *const LABEL_TYPE_NAMES[(int)CButtonLabel::EType::NUM_TYPES] = {"plain", "localized", "icon"};
179
180public:
181 class CTouchButtonBehavior;
182
183 class CTouchButton
184 {
185 public:
186 CTouchButton(CTouchControls *pTouchControls);
187 CTouchButton(CTouchButton &&Other) noexcept;
188 CTouchButton(const CTouchButton &Other) = delete;
189
190 CTouchButton &operator=(const CTouchButton &Other) = delete;
191 CTouchButton &operator=(CTouchButton &&Other) noexcept;
192
193 CTouchControls *m_pTouchControls;
194
195 CUnitRect m_UnitRect; // {0,0,BUTTON_SIZE_MINIMUM,BUTTON_SIZE_MINIMUM} = default
196 CUIRect m_ScreenRect;
197
198 EButtonShape m_Shape; // Rect = default
199 int m_BackgroundCorners; // only used with EButtonShape::RECT
200
201 std::vector<CButtonVisibility> m_vVisibilities;
202 std::unique_ptr<CTouchButtonBehavior> m_pBehavior; // nullptr = default. In button editor the default is bind behavior with nothing.
203
204 bool m_VisibilityCached;
205 std::chrono::nanoseconds m_VisibilityStartTime;
206
207 void UpdatePointers();
208 void UpdateScreenFromUnitRect();
209 void UpdateBackgroundCorners();
210
211 vec2 ClampTouchPosition(vec2 TouchPosition) const;
212 bool IsInside(vec2 TouchPosition) const;
213 void UpdateVisibilityGame();
214 void UpdateVisibilityEditor();
215 bool IsVisible() const;
216 // Force using Selected for button colors, Rect for rendering rects.
217 void Render(std::optional<bool> Selected = std::nullopt, std::optional<CUnitRect> Rect = std::nullopt) const;
218 void WriteToConfiguration(CJsonWriter *pWriter);
219 };
220
221 class CTouchButtonBehavior
222 {
223 public:
224 CTouchButton *m_pTouchButton;
225 CTouchControls *m_pTouchControls;
226
227 bool m_Active; // variables below must only be used when active
228 IInput::CTouchFinger m_Finger;
229 vec2 m_ActivePosition;
230 vec2 m_AccumulatedDelta;
231 std::chrono::nanoseconds m_ActivationStartTime;
232
233 virtual ~CTouchButtonBehavior() = default;
234 virtual void Init(CTouchButton *pTouchButton);
235
236 void Reset();
237 void SetActive(const IInput::CTouchFingerState &FingerState);
238 void SetInactive(bool ByFinger);
239 bool IsActive() const;
240 bool IsActive(const IInput::CTouchFinger &Finger) const;
241
242 virtual CButtonLabel GetLabel() const = 0;
243 virtual void OnActivate() {}
244 virtual void OnDeactivate(bool ByFinger) {}
245 virtual void OnUpdate() {}
246 virtual void WriteToConfiguration(CJsonWriter *pWriter) = 0;
247 virtual const char *GetBehaviorType() const = 0;
248 };
249
250 /**
251 * Abstract class for predefined behaviors.
252 *
253 * Subclasses must implemented the concrete behavior and provide the label.
254 */
255 class CPredefinedTouchButtonBehavior : public CTouchButtonBehavior
256 {
257 public:
258 static constexpr const char *const BEHAVIOR_TYPE = "predefined";
259
260 CPredefinedTouchButtonBehavior(const char *pId) :
261 m_pId(pId) {}
262
263 /**
264 * Implements the serialization for predefined behaviors. Subclasses
265 * may override this, but they should call the parent function first.
266 */
267 void WriteToConfiguration(CJsonWriter *pWriter) override;
268 const char *GetBehaviorType() const override { return BEHAVIOR_TYPE; }
269 const char *GetPredefinedType() { return m_pId; }
270
271 private:
272 const char *m_pId;
273 };
274
275 class CIngameMenuTouchButtonBehavior : public CPredefinedTouchButtonBehavior
276 {
277 public:
278 static constexpr const char *const BEHAVIOR_ID = "ingame-menu";
279
280 CIngameMenuTouchButtonBehavior() :
281 CPredefinedTouchButtonBehavior(BEHAVIOR_ID) {}
282
283 CButtonLabel GetLabel() const override;
284 void OnDeactivate(bool ByFinger) override;
285 };
286
287 class CExtraMenuTouchButtonBehavior : public CPredefinedTouchButtonBehavior
288 {
289 public:
290 static constexpr const char *const BEHAVIOR_ID = "extra-menu";
291
292 CExtraMenuTouchButtonBehavior(int Number);
293
294 CButtonLabel GetLabel() const override;
295 void OnDeactivate(bool ByFinger) override;
296 int GetNumber() const { return m_Number; }
297 void WriteToConfiguration(CJsonWriter *pWriter) override;
298
299 private:
300 int m_Number;
301 char m_aLabel[16];
302 };
303
304 class CEmoticonTouchButtonBehavior : public CPredefinedTouchButtonBehavior
305 {
306 public:
307 static constexpr const char *const BEHAVIOR_ID = "emoticon";
308
309 CEmoticonTouchButtonBehavior() :
310 CPredefinedTouchButtonBehavior(BEHAVIOR_ID) {}
311
312 CButtonLabel GetLabel() const override;
313 void OnDeactivate(bool ByFinger) override;
314 };
315
316 class CSpectateTouchButtonBehavior : public CPredefinedTouchButtonBehavior
317 {
318 public:
319 static constexpr const char *const BEHAVIOR_ID = "spectate";
320
321 CSpectateTouchButtonBehavior() :
322 CPredefinedTouchButtonBehavior(BEHAVIOR_ID) {}
323
324 CButtonLabel GetLabel() const override;
325 void OnDeactivate(bool ByFinger) override;
326 };
327
328 class CSwapActionTouchButtonBehavior : public CPredefinedTouchButtonBehavior
329 {
330 public:
331 static constexpr const char *const BEHAVIOR_ID = "swap-action";
332
333 CSwapActionTouchButtonBehavior() :
334 CPredefinedTouchButtonBehavior(BEHAVIOR_ID) {}
335
336 CButtonLabel GetLabel() const override;
337 void OnActivate() override;
338 void OnDeactivate(bool ByFinger) override;
339
340 private:
341 int m_ActiveAction = NUM_ACTIONS;
342 };
343
344 class CUseActionTouchButtonBehavior : public CPredefinedTouchButtonBehavior
345 {
346 public:
347 static constexpr const char *const BEHAVIOR_ID = "use-action";
348
349 CUseActionTouchButtonBehavior() :
350 CPredefinedTouchButtonBehavior(BEHAVIOR_ID) {}
351
352 CButtonLabel GetLabel() const override;
353 void OnActivate() override;
354 void OnDeactivate(bool ByFinger) override;
355
356 private:
357 int m_ActiveAction = NUM_ACTIONS;
358 };
359
360 class CJoystickTouchButtonBehavior : public CPredefinedTouchButtonBehavior
361 {
362 public:
363 CJoystickTouchButtonBehavior(const char *pId) :
364 CPredefinedTouchButtonBehavior(pId) {}
365
366 CButtonLabel GetLabel() const override;
367 void OnActivate() override;
368 void OnDeactivate(bool ByFinger) override;
369 void OnUpdate() override;
370 int ActiveAction() const { return m_ActiveAction; }
371 virtual int SelectedAction() const = 0;
372 virtual bool IsRelative() const { return false; }
373
374 private:
375 int m_ActiveAction = NUM_ACTIONS;
376 };
377
378 class CJoystickActionTouchButtonBehavior : public CJoystickTouchButtonBehavior
379 {
380 public:
381 static constexpr const char *const BEHAVIOR_ID = "joystick-action";
382
383 CJoystickActionTouchButtonBehavior() :
384 CJoystickTouchButtonBehavior(BEHAVIOR_ID) {}
385
386 int SelectedAction() const override;
387 };
388
389 class CJoystickAimTouchButtonBehavior : public CJoystickTouchButtonBehavior
390 {
391 public:
392 static constexpr const char *const BEHAVIOR_ID = "joystick-aim";
393
394 CJoystickAimTouchButtonBehavior() :
395 CJoystickTouchButtonBehavior(BEHAVIOR_ID) {}
396
397 int SelectedAction() const override;
398 };
399
400 class CJoystickAimRelativeTouchButtonBehavior : public CJoystickTouchButtonBehavior
401 {
402 public:
403 static constexpr const char *const BEHAVIOR_ID = "joystick-aim-relative";
404
405 CJoystickAimRelativeTouchButtonBehavior() :
406 CJoystickTouchButtonBehavior(BEHAVIOR_ID) {}
407
408 int SelectedAction() const override;
409 bool IsRelative() const override { return true; }
410 };
411
412 class CJoystickFireTouchButtonBehavior : public CJoystickTouchButtonBehavior
413 {
414 public:
415 static constexpr const char *const BEHAVIOR_ID = "joystick-fire";
416
417 CJoystickFireTouchButtonBehavior() :
418 CJoystickTouchButtonBehavior(BEHAVIOR_ID) {}
419
420 int SelectedAction() const override;
421 };
422
423 class CJoystickHookTouchButtonBehavior : public CJoystickTouchButtonBehavior
424 {
425 public:
426 static constexpr const char *const BEHAVIOR_ID = "joystick-hook";
427
428 CJoystickHookTouchButtonBehavior() :
429 CJoystickTouchButtonBehavior(BEHAVIOR_ID) {}
430
431 int SelectedAction() const override;
432 };
433
434 /**
435 * Generic behavior implementation that executes a console command like a bind.
436 */
437 class CBindTouchButtonBehavior : public CTouchButtonBehavior
438 {
439 public:
440 static constexpr const char *const BEHAVIOR_TYPE = "bind";
441
442 CBindTouchButtonBehavior(const char *pLabel, CButtonLabel::EType LabelType, const char *pCommand) :
443 m_Label(pLabel),
444 m_LabelType(LabelType),
445 m_Command(pCommand) {}
446
447 CButtonLabel GetLabel() const override;
448 const char *GetCommand() const { return m_Command.c_str(); }
449 void OnActivate() override;
450 void OnDeactivate(bool ByFinger) override;
451 void OnUpdate() override;
452 void WriteToConfiguration(CJsonWriter *pWriter) override;
453 const char *GetBehaviorType() const override { return BEHAVIOR_TYPE; }
454
455 private:
456 std::string m_Label;
457 CButtonLabel::EType m_LabelType;
458 std::string m_Command;
459
460 bool m_Repeating = false;
461 std::chrono::nanoseconds m_LastUpdateTime;
462 std::chrono::nanoseconds m_AccumulatedRepeatingTime;
463 };
464
465 /**
466 * Generic behavior implementation that switches between executing one of two or more console commands.
467 */
468 class CBindToggleTouchButtonBehavior : public CTouchButtonBehavior
469 {
470 public:
471 static constexpr const char *const BEHAVIOR_TYPE = "bind-toggle";
472
473 class CCommand
474 {
475 public:
476 std::string m_Label;
477 CButtonLabel::EType m_LabelType;
478 std::string m_Command;
479
480 CCommand(const char *pLabel, CButtonLabel::EType LabelType, const char *pCommand) :
481 m_Label(pLabel),
482 m_LabelType(LabelType),
483 m_Command(pCommand) {}
484 CCommand() :
485 m_LabelType(CButtonLabel::EType::PLAIN) {}
486 };
487
488 CBindToggleTouchButtonBehavior(std::vector<CCommand> &&vCommands) :
489 m_vCommands(std::move(vCommands)) {}
490
491 CButtonLabel GetLabel() const override;
492 std::vector<CCommand> GetCommand() const { return m_vCommands; }
493 size_t GetActiveCommandIndex() const { return m_ActiveCommandIndex; }
494 void OnActivate() override;
495 void WriteToConfiguration(CJsonWriter *pWriter) override;
496 const char *GetBehaviorType() const override { return BEHAVIOR_TYPE; }
497
498 private:
499 std::vector<CCommand> m_vCommands;
500 size_t m_ActiveCommandIndex = 0;
501 };
502
503private:
504 /**
505 * Mode of direct touch input while ingame.
506 *
507 * Saved to the touch controls configuration.
508 */
509 EDirectTouchIngameMode m_DirectTouchIngame = EDirectTouchIngameMode::ACTION;
510
511 /**
512 * Mode of direct touch input while spectating.
513 *
514 * Saved to the touch controls configuration.
515 */
516 EDirectTouchSpectateMode m_DirectTouchSpectate = EDirectTouchSpectateMode::AIM;
517
518 /**
519 * Background color of inactive touch buttons.
520 *
521 * Saved to the touch controls configuration.
522 */
523 ColorRGBA m_BackgroundColorInactive = ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f);
524
525 /**
526 * Background color of active touch buttons.
527 *
528 * Saved to the touch controls configuration.
529 */
530 ColorRGBA m_BackgroundColorActive = ColorRGBA(0.2f, 0.2f, 0.2f, 0.25f);
531
532 /**
533 * All touch buttons.
534 *
535 * Saved to the touch controls configuration.
536 */
537 std::vector<CTouchButton> m_vTouchButtons;
538
539 /**
540 * The activation states of the different extra menus which are toggle by the extra menu button behavior.
541 */
542 bool m_aExtraMenuActive[(int)EButtonVisibility::EXTRA_MENU_5 - (int)EButtonVisibility::EXTRA_MENU_1 + 1] = {false};
543
544 /**
545 * The currently selected action which is used for direct touch and is changed and used by some button behaviors.
546 */
547 int m_ActionSelected = ACTION_FIRE;
548
549 /**
550 * Counts how many joysticks are pressed.
551 */
552 int m_JoystickPressCount = 0;
553
554 /**
555 * The action that was last activated with direct touch input, which will determine the finger that will
556 * be used to update the mouse position from direct touch input.
557 */
558 int m_DirectTouchLastAction = ACTION_FIRE;
559
560 class CActionState
561 {
562 public:
563 bool m_Active = false;
564 IInput::CTouchFinger m_Finger;
565 };
566
567 /**
568 * The states of the different actions for direct touch input.
569 */
570 CActionState m_aDirectTouchActionStates[NUM_ACTIONS];
571
572 /**
573 * These fingers were activating buttons that became invisible and were therefore deactivated. The fingers
574 * are stored until they are released so they do not activate direct touch input or touch buttons anymore.
575 */
576 std::vector<IInput::CTouchFinger> m_vStaleFingers;
577
578 /**
579 * Whether editing mode is currently active.
580 */
581 bool m_EditingActive = false;
582
583 /**
584 * Whether there are changes to the current configuration in editing mode.
585 */
586 bool m_EditingChanges = false;
587
588 void InitVisibilityFunctions();
589 int NextActiveAction(int Action) const;
590 int NextDirectTouchAction() const;
591 void UpdateButtonsGame(const std::vector<IInput::CTouchFingerState> &vTouchFingerStates);
592 void ResetButtons();
593 void RenderButtonsGame();
594 vec2 CalculateScreenSize() const;
595
596 bool ParseConfiguration(const void *pFileData, unsigned FileLength);
597 std::optional<EDirectTouchIngameMode> ParseDirectTouchIngameMode(const json_value *pModeValue);
598 std::optional<EDirectTouchSpectateMode> ParseDirectTouchSpectateMode(const json_value *pModeValue);
599 std::optional<ColorRGBA> ParseColor(const json_value *pColorValue, const char *pAttributeName, std::optional<ColorRGBA> DefaultColor) const;
600 std::optional<CTouchButton> ParseButton(const json_value *pButtonObject);
601 std::unique_ptr<CTouchButtonBehavior> ParseBehavior(const json_value *pBehaviorObject);
602 std::unique_ptr<CPredefinedTouchButtonBehavior> ParsePredefinedBehavior(const json_value *pBehaviorObject);
603 std::unique_ptr<CExtraMenuTouchButtonBehavior> ParseExtraMenuBehavior(const json_value *pBehaviorObject);
604 std::unique_ptr<CBindTouchButtonBehavior> ParseBindBehavior(const json_value *pBehaviorObject);
605 std::unique_ptr<CBindToggleTouchButtonBehavior> ParseBindToggleBehavior(const json_value *pBehaviorObject);
606 void WriteConfiguration(CJsonWriter *pWriter);
607
608 std::vector<ivec2> m_vTargets;
609 std::vector<CUnitRect> m_vLastUpdateRects;
610 std::vector<CUnitRect> m_vXSortedRects;
611 std::vector<CUnitRect> m_vYSortedRects;
612 int m_LastWidth = -10;
613 int m_LastHeight = -10;
614 void BuildPositionXY(std::vector<CUnitRect> vVisibleButtonRects, CUnitRect MyRect);
615 std::optional<CUnitRect> FindPositionXY(std::vector<CUnitRect> &vVisibleButtonRects, CUnitRect MyRect);
616 CUnitRect FindSizeWH(std::vector<CUnitRect> vVisibleButtonRects, CUnitRect MyRect);
617
618 // This is how editor render buttons.
619 void RenderButtonsEditor();
620 // This is how editor deal with touch inputs.
621 void UpdateButtonsEditor(const std::vector<IInput::CTouchFingerState> &vTouchFingerStates);
622 void UpdateSampleButton(const CTouchButton &SrcButton);
623
624 // For process fingerstates in button editor.
625 bool m_PreventSaving = false;
626 std::optional<IInput::CTouchFingerState> m_ActiveFingerState;
627 std::optional<IInput::CTouchFingerState> m_ZoomFingerState;
628 std::optional<IInput::CTouchFingerState> m_LongPressFingerState;
629 vec2 m_ZoomStartPos = vec2(0.0f, 0.0f);
630 vec2 m_AccumulatedDelta = vec2(0.0f, 0.0f);
631 std::vector<IInput::CTouchFingerState> m_vDeletedFingerState;
632 std::array<bool, (size_t)EButtonVisibility::NUM_VISIBILITIES> m_aVirtualVisibilities;
633
634 // Partially copied so it looks the same as m_pSelectedButton. Follows along the fingers while sliding.
635 std::unique_ptr<CTouchButton> m_pSampleButton = nullptr;
636 // For rendering. Calculated from m_pSampleButton's m_UnitRect, will not overlapping with any rect.
637 std::optional<CUnitRect> m_ShownRect;
638 CTouchButton *m_pSelectedButton = nullptr;
639
640 bool m_UnsavedChanges = false;
641 bool m_PreviewAllButtons = false;
642
643public:
644 CTouchButton *NewButton();
645 void DeleteSelectedButton();
646 bool IsRectOverlapping(CUnitRect MyRect, EButtonShape Shape) const;
647 std::optional<CUnitRect> UpdatePosition(CUnitRect MyRect, EButtonShape Shape, bool Ignore = false); // If Ignore == true, then the function will also try to avoid m_pSelectedButton.
648 void ResetButtonPointers();
649 void ResetVirtualVisibilities();
650 CUIRect CalculateScreenFromUnitRect(CUnitRect Unit, EButtonShape Shape = EButtonShape::RECT) const;
651 CUnitRect CalculateHitbox(const CUnitRect &Rect, EButtonShape Shape) const;
652
653 // Getters and setters.
654 bool HasUnsavedChanges() const { return m_UnsavedChanges; }
655 void SetUnsavedChanges(bool UnsavedChanges) { m_UnsavedChanges = UnsavedChanges; }
656 std::array<bool, (size_t)EButtonVisibility::NUM_VISIBILITIES> VirtualVisibilities() const { return m_aVirtualVisibilities; }
657 void ReverseVirtualVisibilities(int Number) { m_aVirtualVisibilities[Number] = !m_aVirtualVisibilities[Number]; }
658 std::optional<CUnitRect> ShownRect() const { return m_ShownRect; }
659 void SetShownRect(std::optional<CUnitRect> Rect) { m_ShownRect = Rect; }
660 CTouchButton *SelectedButton() const { return m_pSelectedButton; }
661 void SetSelectedButton(CTouchButton *TargetButton) { m_pSelectedButton = TargetButton; }
662 void RemakeSampleButton() { m_pSampleButton = std::make_unique<CTouchButton>(args: this); }
663 CTouchButton *SampleButton() const { return m_pSampleButton.get(); }
664 bool IsButtonEditing() const { return m_pSelectedButton != nullptr || m_pSampleButton != nullptr; }
665 ColorRGBA DefaultBackgroundColorInactive() const { return ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f); }
666 ColorRGBA DefaultBackgroundColorActive() const { return ColorRGBA(0.2f, 0.2f, 0.2f, 0.25f); }
667 ColorRGBA BackgroundColorInactive() const { return m_BackgroundColorInactive; }
668 ColorRGBA BackgroundColorActive() const { return m_BackgroundColorActive; }
669 void SetBackgroundColorInactive(ColorRGBA Color) { m_BackgroundColorInactive = Color; }
670 void SetBackgroundColorActive(ColorRGBA Color) { m_BackgroundColorActive = Color; }
671 std::vector<CTouchButton *> GetButtonsEditor();
672 bool PreviewAllButtons() const { return m_PreviewAllButtons; }
673 void SetPreviewAllButtons(bool Preview) { m_PreviewAllButtons = Preview; }
674
675 // Set the EPopupType and call
676 enum class EPopupType
677 {
678 // Unsaved settings when changing selected button.
679 BUTTON_CHANGED,
680 // FindPositionXY can't find an empty space for the selected button(Currently it's overlapping).
681 NO_SPACE,
682 // Selected button is not visible.
683 BUTTON_INVISIBLE,
684 NUM_POPUPS
685 };
686
687 // These things must be set before opening the menu for calling the popup.
688 // After setting these, use GameClient()->m_Menus.SetActive(true), then the popup could be called automatically if EPopupType is not NUM_POPUPS.
689 class CPopupParam
690 {
691 public:
692 EPopupType m_PopupType = EPopupType::NUM_POPUPS;
693 CTouchButton *m_pOldSelectedButton = nullptr;
694 CTouchButton *m_pNewSelectedButton = nullptr;
695 bool m_KeepMenuOpen = false;
696 };
697
698 CPopupParam RequiredPopup();
699
700 // The issues won't be resolved until the Button Editor is rendered. If you want to solve issues right now don't use this.
701 // This is usually for update cached settings in button editor.
702 enum class EIssueType
703 {
704 CACHE_SETTINGS, // Update Cached settings from m_pTargetButton.
705 SAVE_SETTINGS, // Save Cached settings to m_pTargetButton.
706 CACHE_POSITION, // Update position from m_pTargetButton.
707 NUM_ISSUES
708 };
709
710 class CIssueParam
711 {
712 public:
713 bool m_Resolved = true;
714 CTouchButton *m_pTargetButton = nullptr;
715 };
716
717 bool AnyIssueNotResolved() const;
718 std::array<CTouchControls::CIssueParam, (unsigned)CTouchControls::EIssueType::NUM_ISSUES> Issues();
719
720private:
721 CPopupParam m_PopupParam;
722 std::array<CIssueParam, (int)EIssueType::NUM_ISSUES> m_aIssueParam;
723};
724
725#endif
726