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