This commit is contained in:
Robert Müller 2024-09-07 13:09:06 +02:00 committed by GitHub
commit 8ef01fcbaf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 2207 additions and 18 deletions

View file

@ -1845,6 +1845,7 @@ set(EXPECTED_DATA
themes/winter.png
themes/winter_day.map
themes/winter_night.map
touch_controls.json
wordlist.txt
)
@ -2399,6 +2400,8 @@ if(CLIENT)
components/statboard.h
components/tooltips.cpp
components/tooltips.h
components/touch_controls.cpp
components/touch_controls.h
components/voting.cpp
components/voting.h
gameclient.cpp

305
data/touch_controls.json Normal file
View file

@ -0,0 +1,305 @@
{
"direct-touch-ingame": true,
"direct-touch-spectate": true,
"touch-buttons": [
{
"x": 0,
"y": 833333,
"w": 200000,
"h": 166667,
"shape": "rect",
"visibilities": [
"ingame"
],
"behavior": {
"type": "bind",
"label": "Move left",
"label-type": "localized",
"command": "+left"
}
},
{
"x": 200000,
"y": 833333,
"w": 200000,
"h": 166667,
"shape": "rect",
"visibilities": [
"ingame"
],
"behavior": {
"type": "bind",
"label": "Move right",
"label-type": "localized",
"command": "+right"
}
},
{
"x": 100000,
"y": 666667,
"w": 200000,
"h": 166667,
"shape": "rect",
"visibilities": [
"ingame"
],
"behavior": {
"type": "bind",
"label": "Jump",
"label-type": "localized",
"command": "+jump"
}
},
{
"x": 116667,
"y": 16667,
"w": 83333,
"h": 83333,
"shape": "rect",
"visibilities": [
"ingame"
],
"behavior": {
"type": "bind",
"label": "Prev. weapon",
"label-type": "localized",
"command": "+prevweapon"
}
},
{
"x": 200000,
"y": 16667,
"w": 83333,
"h": 83333,
"shape": "rect",
"visibilities": [
"ingame"
],
"behavior": {
"type": "bind",
"label": "Next weapon",
"label-type": "localized",
"command": "+nextweapon"
}
},
{
"x": 16667,
"y": 16667,
"w": 83333,
"h": 83333,
"shape": "rect",
"visibilities": [
],
"behavior": {
"type": "predefined",
"id": "extra-menu"
}
},
{
"x": 300000,
"y": 16667,
"w": 83333,
"h": 83333,
"shape": "rect",
"visibilities": [
"extra-menu",
"zoom-allowed"
],
"behavior": {
"type": "bind",
"label": "Zoom out",
"label-type": "localized",
"command": "zoom-"
}
},
{
"x": 383333,
"y": 16667,
"w": 83333,
"h": 83333,
"shape": "rect",
"visibilities": [
"extra-menu",
"zoom-allowed"
],
"behavior": {
"type": "bind",
"label": "Default zoom",
"label-type": "localized",
"command": "zoom"
}
},
{
"x": 466666,
"y": 16667,
"w": 83333,
"h": 83333,
"shape": "rect",
"visibilities": [
"extra-menu",
"zoom-allowed"
],
"behavior": {
"type": "bind",
"label": "Zoom in",
"label-type": "localized",
"command": "zoom+"
}
},
{
"x": 16667,
"y": 133333,
"w": 83333,
"h": 66667,
"shape": "rect",
"visibilities": [
"extra-menu"
],
"behavior": {
"type": "bind",
"label": "Scoreboard",
"label-type": "localized",
"command": "+scoreboard"
}
},
{
"x": 116667,
"y": 133333,
"w": 83333,
"h": 66667,
"shape": "rect",
"visibilities": [
"ingame",
"extra-menu"
],
"behavior": {
"type": "predefined",
"id": "emoticon"
}
},
{
"x": 116667,
"y": 133333,
"w": 83333,
"h": 66667,
"shape": "rect",
"visibilities": [
"-ingame",
"extra-menu"
],
"behavior": {
"type": "predefined",
"id": "spectate"
}
},
{
"x": 216667,
"y": 133333,
"w": 83333,
"h": 66667,
"shape": "rect",
"visibilities": [
"extra-menu"
],
"behavior": {
"type": "bind",
"label": "Chat",
"label-type": "localized",
"command": "chat all"
}
},
{
"x": 316667,
"y": 133333,
"w": 83333,
"h": 66667,
"shape": "rect",
"visibilities": [
"extra-menu"
],
"behavior": {
"type": "bind",
"label": "Team chat",
"label-type": "localized",
"command": "chat team"
}
},
{
"x": 16667,
"y": 333333,
"w": 83333,
"h": 66667,
"shape": "rect",
"visibilities": [
"extra-menu",
"vote-active"
],
"behavior": {
"type": "bind",
"label": "Vote yes",
"label-type": "localized",
"command": "vote yes"
}
},
{
"x": 116667,
"y": 333333,
"w": 83333,
"h": 66667,
"shape": "rect",
"visibilities": [
"extra-menu",
"vote-active"
],
"behavior": {
"type": "bind",
"label": "Vote no",
"label-type": "localized",
"command": "vote no"
}
},
{
"x": 766667,
"y": 16667,
"w": 100000,
"h": 100000,
"shape": "rect",
"visibilities": [
"dummy-connected"
],
"behavior": {
"type": "bind",
"label": "Toggle dummy",
"label-type": "localized",
"command": "toggle cl_dummy 0 1"
}
},
{
"x": 883333,
"y": 16667,
"w": 100000,
"h": 100000,
"shape": "rect",
"visibilities": [
"ingame"
],
"behavior": {
"type": "predefined",
"id": "swap-action"
}
},
{
"x": 755000,
"y": 580000,
"w": 225000,
"h": 400000,
"shape": "circle",
"visibilities": [
],
"behavior": {
"type": "predefined",
"id": "joystick-action"
}
}
]
}

View file

@ -300,6 +300,14 @@ const std::vector<IInput::CTouchFingerState> &CInput::TouchFingerStates() const
return m_vTouchFingerStates;
}
void CInput::ClearTouchDeltas()
{
for(CTouchFingerState &TouchFingerState : m_vTouchFingerStates)
{
TouchFingerState.m_Delta = vec2(0.0f, 0.0f);
}
}
std::string CInput::GetClipboardText()
{
char *pClipboardText = SDL_GetClipboardText();
@ -347,10 +355,7 @@ void CInput::Clear()
mem_zero(m_aInputState, sizeof(m_aInputState));
mem_zero(m_aInputCount, sizeof(m_aInputCount));
m_vInputEvents.clear();
for(CTouchFingerState &TouchFingerState : m_vTouchFingerStates)
{
TouchFingerState.m_Delta = vec2(0.0f, 0.0f);
}
ClearTouchDeltas();
}
float CInput::GetUpdateTime() const

View file

@ -144,6 +144,7 @@ public:
bool NativeMousePressed(int Index) const override;
const std::vector<CTouchFingerState> &TouchFingerStates() const override;
void ClearTouchDeltas() override;
std::string GetClipboardText() override;
void SetClipboardText(const char *pText) override;

View file

@ -137,6 +137,12 @@ public:
* @return vector of all touch finger states
*/
virtual const std::vector<CTouchFingerState> &TouchFingerStates() const = 0;
/**
* Must be called after the touch finger states have been used during the client update to ensure that
* touch deltas are only accumulated until the next update. If the touch states are only used during
* rendering, i.e. for user interfaces, then this is called automatically by calling @link Clear @endlink.
*/
virtual void ClearTouchDeltas() = 0;
// clipboard
virtual std::string GetClipboardText() = 0;

View file

@ -22,6 +22,11 @@ MACRO_CONFIG_INT(ClAntiPingSmooth, cl_antiping_smooth, 0, 0, 1, CFGFLAG_CLIENT |
MACRO_CONFIG_INT(ClAntiPingGunfire, cl_antiping_gunfire, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Predict gunfire and show predicted weapon physics (with cl_antiping_grenade 1 and cl_antiping_weapons 1)")
MACRO_CONFIG_INT(ClPredictionMargin, cl_prediction_margin, 10, 1, 300, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Prediction margin in ms (adds latency, can reduce lag from ping jumps)")
MACRO_CONFIG_INT(ClSubTickAiming, cl_sub_tick_aiming, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Send aiming data at sub-tick accuracy")
#if defined(CONF_PLATFORM_ANDROID)
MACRO_CONFIG_INT(ClTouchControls, cl_touch_controls, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Enable ingame touch controls")
#else
MACRO_CONFIG_INT(ClTouchControls, cl_touch_controls, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Enable ingame touch controls")
#endif
MACRO_CONFIG_INT(ClNameplates, cl_nameplates, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show name plates")
MACRO_CONFIG_INT(ClAfkEmote, cl_afk_emote, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show zzz emote next to afk players")

View file

@ -212,6 +212,14 @@ public:
* @param Event The input event.
*/
virtual bool OnInput(const IInput::CEvent &Event) { return false; }
/**
* Called with all current touch finger states.
*
* @param vTouchFingerStates The touch finger states to be handled.
*
* @return `true` if the component used the touch events, `false` otherwise
*/
virtual bool OnTouchState(const std::vector<IInput::CTouchFingerState> &vTouchFingerStates) { return false; }
};
#endif

View file

@ -12,10 +12,10 @@
class CControls : public CComponent
{
public:
float GetMinMouseDistance() const;
float GetMaxMouseDistance() const;
public:
vec2 m_aMousePos[NUM_DUMMIES];
vec2 m_aMousePosOnAction[NUM_DUMMIES];
vec2 m_aTargetPos[NUM_DUMMIES];

View file

@ -474,8 +474,12 @@ protected:
// found in menus_ingame.cpp
STextContainerIndex m_MotdTextContainerIndex;
void RenderGame(CUIRect MainView);
void RenderTouchControlsEditor(CUIRect MainView);
void PopupConfirmDisconnect();
void PopupConfirmDisconnectDummy();
void PopupConfirmDiscardTouchControlsChanged();
void PopupConfirmResetTouchControls();
void PopupConfirmImportTouchControlsClipboard();
void RenderPlayers(CUIRect MainView);
void RenderServerInfo(CUIRect MainView);
void RenderServerInfoMotd(CUIRect Motd);
@ -637,7 +641,6 @@ protected:
static CUi::EPopupMenuFunctionResult PopupMapPicker(void *pContext, CUIRect View, bool Active);
void SetNeedSendInfo();
void SetActive(bool Active);
void UpdateColors();
IGraphics::CTextureHandle m_TextureBlob;
@ -657,6 +660,8 @@ public:
bool IsInit() { return m_IsInit; }
bool IsActive() const { return m_MenuActive; }
void SetActive(bool Active);
void KillServer();
virtual void OnInit() override;

View file

@ -18,6 +18,7 @@
#include <game/client/animstate.h>
#include <game/client/components/countryflags.h>
#include <game/client/components/touch_controls.h>
#include <game/client/gameclient.h>
#include <game/client/render.h>
#include <game/client/ui.h>
@ -40,21 +41,19 @@ using namespace std::chrono_literals;
void CMenus::RenderGame(CUIRect MainView)
{
CUIRect Button, ButtonBar, ButtonBar2;
CUIRect Button, ButtonBars, ButtonBar, ButtonBar2;
bool ShowDDRaceButtons = MainView.w > 855.0f;
MainView.HSplitTop(45.0f, &ButtonBar, &MainView);
ButtonBar.Draw(ms_ColorTabbarActive, IGraphics::CORNER_B, 10.0f);
// button bar
ButtonBar.HSplitTop(10.0f, 0, &ButtonBar);
ButtonBar.HSplitTop(25.0f, &ButtonBar, 0);
ButtonBar.VMargin(10.0f, &ButtonBar);
ButtonBar.HSplitTop(30.0f, 0, &ButtonBar2);
ButtonBar2.HSplitTop(25.0f, &ButtonBar2, 0);
MainView.HSplitTop(45.0f + (g_Config.m_ClTouchControls ? 35.0f : 0.0f), &ButtonBars, &MainView);
ButtonBars.Draw(ms_ColorTabbarActive, IGraphics::CORNER_B, 10.0f);
ButtonBars.Margin(10.0f, &ButtonBars);
ButtonBars.HSplitTop(25.0f, &ButtonBar, &ButtonBars);
if(g_Config.m_ClTouchControls)
{
ButtonBars.HSplitTop(10.0f, nullptr, &ButtonBars);
ButtonBars.HSplitTop(25.0f, &ButtonBar2, &ButtonBars);
}
ButtonBar.VSplitRight(120.0f, &ButtonBar, &Button);
static CButtonContainer s_DisconnectButton;
if(DoButton_Menu(&s_DisconnectButton, Localize("Disconnect"), 0, &Button))
{
@ -214,6 +213,147 @@ void CMenus::RenderGame(CUIRect MainView)
}
}
}
if(g_Config.m_ClTouchControls)
{
ButtonBar2.VSplitLeft(200.0f, &Button, &ButtonBar2);
static char s_TouchControlsEditCheckbox;
if(DoButton_CheckBox(&s_TouchControlsEditCheckbox, Localize("Edit touch controls"), GameClient()->m_TouchControls.IsEditingActive(), &Button))
{
GameClient()->m_TouchControls.SetEditingActive(!GameClient()->m_TouchControls.IsEditingActive());
}
ButtonBar2.VSplitRight(80.0f, &ButtonBar2, &Button);
static CButtonContainer s_CloseButton;
if(DoButton_Menu(&s_CloseButton, Localize("Close"), 0, &Button))
{
SetActive(false);
}
ButtonBar2.VSplitRight(5.0f, &ButtonBar2, nullptr);
ButtonBar2.VSplitRight(160.0f, &ButtonBar2, &Button);
static CButtonContainer s_RemoveConsoleButton;
if(DoButton_Menu(&s_RemoveConsoleButton, Localize("Remote console"), 0, &Button))
{
Console()->ExecuteLine("toggle_remote_console");
}
ButtonBar2.VSplitRight(5.0f, &ButtonBar2, nullptr);
ButtonBar2.VSplitRight(120.0f, &ButtonBar2, &Button);
static CButtonContainer s_LocalConsoleButton;
if(DoButton_Menu(&s_LocalConsoleButton, Localize("Console"), 0, &Button))
{
Console()->ExecuteLine("toggle_local_console");
}
if(GameClient()->m_TouchControls.IsEditingActive())
{
CUIRect TouchControlsEditor;
MainView.VMargin((MainView.w - 505.0f) / 2.0f, &TouchControlsEditor);
TouchControlsEditor.HMargin((TouchControlsEditor.h - 195.0f) / 2.0f, &TouchControlsEditor);
RenderTouchControlsEditor(TouchControlsEditor);
}
}
}
void CMenus::RenderTouchControlsEditor(CUIRect MainView)
{
CUIRect Button, Row;
MainView.Draw(ms_ColorTabbarActive, IGraphics::CORNER_ALL, 10.0f);
MainView.Margin(10.0f, &MainView);
MainView.HSplitTop(25.0f, &Button, &MainView);
MainView.HSplitTop(5.0f, nullptr, &MainView);
Ui()->DoLabel(&Button, Localize("Edit touch controls"), 20.0f, TEXTALIGN_MC);
MainView.HSplitTop(25.0f, &Row, &MainView);
MainView.HSplitTop(5.0f, nullptr, &MainView);
Row.VSplitLeft(240.0f, &Button, &Row);
static CButtonContainer s_SaveConfigurationButton;
if(DoButton_Menu(&s_SaveConfigurationButton, Localize("Save changes"), GameClient()->m_TouchControls.HasEditingChanges() ? 0 : 1, &Button))
{
if(GameClient()->m_TouchControls.SaveConfigurationToFile())
{
GameClient()->m_TouchControls.SetEditingChanges(false);
}
else
{
SWarning Warning(Localize("Error saving touch controls"), Localize("Could not save touch controls to file. See local console for details."));
Warning.m_AutoHide = false;
Client()->AddWarning(Warning);
}
}
Row.VSplitLeft(5.0f, nullptr, &Row);
Row.VSplitLeft(240.0f, &Button, &Row);
if(GameClient()->m_TouchControls.HasEditingChanges())
{
TextRender()->TextColor(ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f));
Ui()->DoLabel(&Button, Localize("Unsaved changes"), 14.0f, TEXTALIGN_MC);
TextRender()->TextColor(TextRender()->DefaultTextColor());
}
MainView.HSplitTop(25.0f, &Row, &MainView);
MainView.HSplitTop(5.0f, nullptr, &MainView);
Row.VSplitLeft(240.0f, &Button, &Row);
static CButtonContainer s_DiscardChangesButton;
if(DoButton_Menu(&s_DiscardChangesButton, Localize("Discard changes"), GameClient()->m_TouchControls.HasEditingChanges() ? 0 : 1, &Button))
{
PopupConfirm(Localize("Discard changes"),
Localize("Are you sure that you want to discard the current changes to the touch controls?"),
Localize("Yes"), Localize("No"),
&CMenus::PopupConfirmDiscardTouchControlsChanged);
}
Row.VSplitLeft(5.0f, nullptr, &Row);
Row.VSplitLeft(240.0f, &Button, &Row);
static CButtonContainer s_ResetButton;
if(DoButton_Menu(&s_ResetButton, Localize("Reset to default"), 0, &Button))
{
PopupConfirm(Localize("Reset to default"),
Localize("Are you sure that you want to reset the touch controls to default?"),
Localize("Yes"), Localize("No"),
&CMenus::PopupConfirmResetTouchControls);
}
MainView.HSplitTop(25.0f, &Row, &MainView);
MainView.HSplitTop(5.0f, nullptr, &MainView);
Row.VSplitLeft(240.0f, &Button, &Row);
static CButtonContainer s_ClipboardImportButton;
if(DoButton_Menu(&s_ClipboardImportButton, Localize("Import from clipboard"), 0, &Button))
{
PopupConfirm(Localize("Import from clipboard"),
Localize("Are you sure that you want to import the touch controls from the clipboard? The will overwrite your current touch controls."),
Localize("Yes"), Localize("No"),
&CMenus::PopupConfirmImportTouchControlsClipboard);
}
Row.VSplitLeft(5.0f, nullptr, &Row);
Row.VSplitLeft(240.0f, &Button, &Row);
static CButtonContainer s_ClipboardExportButton;
if(DoButton_Menu(&s_ClipboardExportButton, Localize("Export to clipboard"), 0, &Button))
{
GameClient()->m_TouchControls.SaveConfigurationToClipboard();
}
MainView.HSplitTop(25.0f, &Button, &MainView);
MainView.HSplitTop(5.0f, nullptr, &MainView);
static char s_DirectTouchIngameButton;
if(DoButton_CheckBox(&s_DirectTouchIngameButton, Localize("Direct touch input while ingame"), GameClient()->m_TouchControls.IsDirectTouchIngame(), &Button))
{
GameClient()->m_TouchControls.SetDirectTouchIngame(!GameClient()->m_TouchControls.IsDirectTouchIngame());
}
MainView.HSplitTop(25.0f, &Button, &MainView);
MainView.HSplitTop(5.0f, nullptr, &MainView);
static char s_DirectTouchSpectateButton;
if(DoButton_CheckBox(&s_DirectTouchSpectateButton, Localize("Direct touch input while spectate"), GameClient()->m_TouchControls.IsDirectTouchSpectate(), &Button))
{
GameClient()->m_TouchControls.SetDirectTouchSpectate(!GameClient()->m_TouchControls.IsDirectTouchSpectate());
}
}
void CMenus::PopupConfirmDisconnect()
@ -227,6 +367,57 @@ void CMenus::PopupConfirmDisconnectDummy()
SetActive(false);
}
void CMenus::PopupConfirmDiscardTouchControlsChanged()
{
if(GameClient()->m_TouchControls.LoadConfigurationFromFile(IStorage::TYPE_ALL))
{
GameClient()->m_TouchControls.SetEditingChanges(false);
}
else
{
SWarning Warning(Localize("Error loading touch controls"), Localize("Could not load touch controls from file. See local console for details."));
Warning.m_AutoHide = false;
Client()->AddWarning(Warning);
}
}
void CMenus::PopupConfirmResetTouchControls()
{
bool Success = false;
for(int StorageType = 1; StorageType < Storage()->NumPaths(); ++StorageType)
{
if(GameClient()->m_TouchControls.LoadConfigurationFromFile(StorageType))
{
Success = true;
break;
}
}
if(Success)
{
GameClient()->m_TouchControls.SetEditingChanges(true);
}
else
{
SWarning Warning(Localize("Error loading touch controls"), Localize("Could not load default touch controls from file. See local console for details."));
Warning.m_AutoHide = false;
Client()->AddWarning(Warning);
}
}
void CMenus::PopupConfirmImportTouchControlsClipboard()
{
if(GameClient()->m_TouchControls.LoadConfigurationFromClipboard())
{
GameClient()->m_TouchControls.SetEditingChanges(true);
}
else
{
SWarning Warning(Localize("Error loading touch controls"), Localize("Could not load touch controls from clipboard. See local console for details."));
Warning.m_AutoHide = false;
Client()->AddWarning(Warning);
}
}
void CMenus::RenderPlayers(CUIRect MainView)
{
CUIRect Button, Button2, ButtonBar, PlayerList, Player;
@ -1292,6 +1483,9 @@ void CMenus::RenderGhost(CUIRect MainView)
void CMenus::RenderIngameHint()
{
if(g_Config.m_ClTouchControls)
return;
float Width = 300 * Graphics()->ScreenAspect();
Graphics()->MapScreen(0, 0, Width, 300);
TextRender()->TextColor(1, 1, 1, 1);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,402 @@
#ifndef GAME_CLIENT_COMPONENTS_TOUCH_CONTROLS_H
#define GAME_CLIENT_COMPONENTS_TOUCH_CONTROLS_H
#include <base/vmath.h>
#include <engine/input.h>
#include <game/client/component.h>
#include <game/client/ui_rect.h>
#include <chrono>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <vector>
class CJsonWriter;
typedef struct _json_value json_value;
class CTouchControls : public CComponent
{
enum class EButtonShape
{
RECT,
CIRCLE,
NUM_SHAPES
};
static constexpr const char *SHAPE_NAMES[(int)EButtonShape::NUM_SHAPES] = {"rect", "circle"};
enum class EButtonVisibility
{
INGAME,
EXTRA_MENU,
ZOOM_ALLOWED,
VOTE_ACTIVE,
DUMMY_ALLOWED,
DUMMY_CONNECTED,
RCON_AUTHED,
NUM_VISIBILITIES
};
class CButtonVisibility
{
public:
EButtonVisibility m_Type;
bool m_Parity;
CButtonVisibility(EButtonVisibility Type, bool Parity) :
m_Type(Type), m_Parity(Parity) {}
};
class CButtonVisibilityData
{
public:
const char *m_pId;
std::function<bool()> m_Function;
};
CButtonVisibilityData m_aVisibilityFunctions[(int)EButtonVisibility::NUM_VISIBILITIES];
enum
{
ACTION_FIRE,
ACTION_HOOK,
NUM_ACTIONS
};
class CButtonLabel
{
public:
enum class EType
{
/**
* Label is used as is.
*/
PLAIN,
/**
* Label is localized. Only usable for default button labels for which there must be
* corresponding `Localizable`-calls in code and string in the translation files.
*/
LOCALIZED,
/**
* Icon font is used for the label.
*/
ICON,
/**
* Number of label types.
*/
NUM_TYPES
};
EType m_Type;
const char *m_pLabel;
};
static constexpr const char *LABEL_TYPE_NAMES[(int)CButtonLabel::EType::NUM_TYPES] = {"plain", "localized", "icon"};
class CUnitRect
{
public:
int m_X;
int m_Y;
int m_W;
int m_H;
};
class CTouchButtonBehavior;
class CTouchButton
{
public:
CTouchButton(CTouchControls *pTouchControls);
CTouchButton(CTouchButton &&Other) noexcept;
CTouchButton(const CTouchButton &Other) = delete;
CTouchButton &operator=(const CTouchButton &Other) = delete;
CTouchButton &operator=(CTouchButton &&Other) noexcept;
CTouchControls *m_pTouchControls;
CUnitRect m_UnitRect;
CUIRect m_ScreenRect;
EButtonShape m_Shape;
int m_BackgroundCorners; // only used with EButtonShape::RECT
std::vector<CButtonVisibility> m_vVisibilities;
std::unique_ptr<CTouchButtonBehavior> m_pBehavior;
void UpdatePointers();
void UpdateScreenFromUnitRect();
void UpdateUnitFromScreenRect();
void UpdateBackgroundCorners();
vec2 ClampTouchPosition(vec2 TouchPosition) const;
bool IsInside(vec2 TouchPosition) const;
bool IsVisible() const;
void Render() const;
void WriteToConfiguration(CJsonWriter *pWriter);
};
class CTouchButtonBehavior
{
public:
CTouchButton *m_pTouchButton;
CTouchControls *m_pTouchControls;
bool m_Active; // variables below must only be used when active
IInput::CTouchFinger m_Finger;
vec2 m_ActivePosition;
vec2 m_AccumulatedDelta;
std::chrono::nanoseconds m_ActivationStartTime;
virtual ~CTouchButtonBehavior() = default;
virtual void Init(CTouchButton *pTouchButton);
void Reset();
void SetActive(const IInput::CTouchFingerState &FingerState);
void SetInactive();
bool IsActive() const;
bool IsActive(const IInput::CTouchFinger &Finger) const;
virtual CButtonLabel GetLabel() const = 0;
virtual void OnActivate() {}
virtual void OnDeactivate() {}
virtual void OnUpdate() {}
virtual void WriteToConfiguration(CJsonWriter *pWriter) = 0;
};
/**
* Abstract class for predefined behaviors.
*
* This implements the serialization for predefined behaviors.
*
* Subclasses must implemented the actual behavior and provide the label.
*/
class CPredefinedTouchButtonBehavior : public CTouchButtonBehavior
{
const char *m_pId;
public:
static constexpr const char *BEHAVIOR_TYPE = "predefined";
CPredefinedTouchButtonBehavior(const char *pId) :
m_pId(pId) {}
void WriteToConfiguration(CJsonWriter *pWriter) override;
};
class CExtraMenuTouchButtonBehavior : public CPredefinedTouchButtonBehavior
{
public:
static constexpr const char *BEHAVIOR_ID = "extra-menu";
CExtraMenuTouchButtonBehavior() :
CPredefinedTouchButtonBehavior(BEHAVIOR_ID) {}
CButtonLabel GetLabel() const override;
void OnDeactivate() override;
};
class CEmoticonTouchButtonBehavior : public CPredefinedTouchButtonBehavior
{
public:
static constexpr const char *BEHAVIOR_ID = "emoticon";
CEmoticonTouchButtonBehavior() :
CPredefinedTouchButtonBehavior(BEHAVIOR_ID) {}
CButtonLabel GetLabel() const override;
void OnDeactivate() override;
};
class CSpectateTouchButtonBehavior : public CPredefinedTouchButtonBehavior
{
public:
static constexpr const char *BEHAVIOR_ID = "spectate";
CSpectateTouchButtonBehavior() :
CPredefinedTouchButtonBehavior(BEHAVIOR_ID) {}
CButtonLabel GetLabel() const override;
void OnDeactivate() override;
};
class CSwapActionTouchButtonBehavior : public CPredefinedTouchButtonBehavior
{
int m_ActiveAction = NUM_ACTIONS;
public:
static constexpr const char *BEHAVIOR_ID = "swap-action";
CSwapActionTouchButtonBehavior() :
CPredefinedTouchButtonBehavior(BEHAVIOR_ID) {}
CButtonLabel GetLabel() const override;
void OnActivate() override;
void OnDeactivate() override;
};
class CUseActionTouchButtonBehavior : public CPredefinedTouchButtonBehavior
{
int m_ActiveAction = NUM_ACTIONS;
public:
static constexpr const char *BEHAVIOR_ID = "use-action";
CUseActionTouchButtonBehavior() :
CPredefinedTouchButtonBehavior(BEHAVIOR_ID) {}
CButtonLabel GetLabel() const override;
void OnActivate() override;
void OnDeactivate() override;
};
class CJoystickTouchButtonBehavior : public CPredefinedTouchButtonBehavior
{
int m_ActiveAction = NUM_ACTIONS;
public:
CJoystickTouchButtonBehavior(const char *pId) :
CPredefinedTouchButtonBehavior(pId) {}
CButtonLabel GetLabel() const override;
void OnActivate() override;
void OnDeactivate() override;
void OnUpdate() override;
int ActiveAction() const { return m_ActiveAction; }
virtual int SelectedAction() const = 0;
};
class CJoystickActionTouchButtonBehavior : public CJoystickTouchButtonBehavior
{
public:
static constexpr const char *BEHAVIOR_ID = "joystick-action";
CJoystickActionTouchButtonBehavior() :
CJoystickTouchButtonBehavior(BEHAVIOR_ID) {}
~CJoystickActionTouchButtonBehavior();
void Init(CTouchButton *pTouchButton) override;
int SelectedAction() const override;
};
class CJoystickFireTouchButtonBehavior : public CJoystickTouchButtonBehavior
{
public:
static constexpr const char *BEHAVIOR_ID = "joystick-fire";
CJoystickFireTouchButtonBehavior() :
CJoystickTouchButtonBehavior(BEHAVIOR_ID) {}
int SelectedAction() const override;
};
class CJoystickHookTouchButtonBehavior : public CJoystickTouchButtonBehavior
{
public:
static constexpr const char *BEHAVIOR_ID = "joystick-hook";
CJoystickHookTouchButtonBehavior() :
CJoystickTouchButtonBehavior(BEHAVIOR_ID) {}
int SelectedAction() const override;
};
/**
* Generic behavior implementation that executes a console command like a bind.
*/
class CBindTouchButtonBehavior : public CTouchButtonBehavior
{
std::string m_Label;
CButtonLabel::EType m_LabelType;
std::string m_Command;
bool m_Repeating = false;
std::chrono::nanoseconds m_LastUpdateTime;
std::chrono::nanoseconds m_AccumulatedRepeatingTime;
public:
static constexpr const char *BEHAVIOR_TYPE = "bind";
CBindTouchButtonBehavior(const char *pLabel, CButtonLabel::EType LabelType, const char *pCommand) :
m_Label(pLabel),
m_LabelType(LabelType),
m_Command(pCommand) {}
CButtonLabel GetLabel() const override;
void OnActivate() override;
void OnDeactivate() override;
void OnUpdate() override;
void WriteToConfiguration(CJsonWriter *pWriter) override;
};
bool m_DirectTouchIngame = true;
bool m_DirectTouchSpectate = true;
std::vector<CTouchButton> m_vTouchButtons;
bool m_ExtraMenuActive = false;
class CActionState
{
public:
bool m_Active = false;
IInput::CTouchFinger m_Finger;
};
int m_ActionSelected = ACTION_FIRE;
int m_ActionLastActivated = ACTION_FIRE;
CActionState m_aActionStates[NUM_ACTIONS];
CJoystickActionTouchButtonBehavior *m_pPrimaryJoystickTouchButtonBehavior;
bool m_EditingActive = false;
bool m_EditingChanges = false;
void InitVisibilityFunctions();
void UpdateButtons(const std::vector<IInput::CTouchFingerState> &vTouchFingerStates);
void RenderButtons();
vec2 CalculateScreenSize() const;
std::unique_ptr<CPredefinedTouchButtonBehavior> ParsePredefinedBehavior(const json_value *pBehaviorObject);
std::unique_ptr<CBindTouchButtonBehavior> ParseBindBehavior(const json_value *pBehaviorObject);
std::unique_ptr<CTouchButtonBehavior> ParseBehavior(const json_value *pBehaviorObject);
std::optional<CTouchButton> ParseButton(const json_value *pButtonObject);
bool ParseConfiguration(const void *pFileData, unsigned FileLength);
void WriteConfiguration(CJsonWriter *pWriter);
public:
int Sizeof() const override { return sizeof(*this); }
void OnInit() override;
void OnReset() override;
void OnWindowResize() override;
bool OnTouchState(const std::vector<IInput::CTouchFingerState> &vTouchFingerStates) override;
void OnRender() override;
bool LoadConfigurationFromFile(int StorageType);
bool LoadConfigurationFromClipboard();
bool SaveConfigurationToFile();
void SaveConfigurationToClipboard();
bool IsDirectTouchIngame() const { return m_DirectTouchIngame; }
void SetDirectTouchIngame(bool DirectTouchIngame)
{
m_DirectTouchIngame = DirectTouchIngame;
m_EditingChanges = true;
}
bool IsDirectTouchSpectate() const { return m_DirectTouchSpectate; }
void SetDirectTouchSpectate(bool DirectTouchSpectate)
{
m_DirectTouchSpectate = DirectTouchSpectate;
m_EditingChanges = true;
}
bool IsEditingActive() const { return m_EditingActive; }
void SetEditingActive(bool EditingActive) { m_EditingActive = EditingActive; }
bool HasEditingChanges() const { return m_EditingChanges; }
void SetEditingChanges(bool EditingChanges) { m_EditingChanges = EditingChanges; }
};
#endif

View file

@ -145,6 +145,7 @@ void CGameClient::OnConsoleInit()
&m_Chat,
&m_Broadcast,
&m_DebugHud,
&m_TouchControls,
&m_Scoreboard,
&m_Statboard,
&m_Motd,
@ -164,6 +165,7 @@ void CGameClient::OnConsoleInit()
&m_Emoticon,
&m_Menus,
&m_Controls,
&m_TouchControls,
&m_Binds});
// add basic console commands
@ -443,6 +445,23 @@ void CGameClient::OnUpdate()
}
}
// handle touch events
const std::vector<IInput::CTouchFingerState> &vTouchFingerStates = Input()->TouchFingerStates();
bool TouchHandled = false;
for(auto &pComponent : m_vpInput)
{
if(TouchHandled)
{
// Also update inactive components so they can handle touch fingers being released.
pComponent->OnTouchState({});
}
else if(pComponent->OnTouchState(vTouchFingerStates))
{
Input()->ClearTouchDeltas();
TouchHandled = true;
}
}
// handle key presses
Input()->ConsumeEvents([&](const IInput::CEvent &Event) {
for(auto &pComponent : m_vpInput)

View file

@ -58,6 +58,7 @@
#include "components/spectator.h"
#include "components/statboard.h"
#include "components/tooltips.h"
#include "components/touch_controls.h"
#include "components/voting.h"
class CGameInfo
@ -146,6 +147,7 @@ public:
CSounds m_Sounds;
CEmoticon m_Emoticon;
CDamageInd m_DamageInd;
CTouchControls m_TouchControls;
CVoting m_Voting;
CSpectator m_Spectator;