Add touch support to emoticon selector and spectator menu

Support using emoticon selector and spectator menu with touch inputs. Touching in the emoticon selector activates the selected (eye) emote and closes the emoticon selector. The spectator menu is kept open when using it for more convenience. The emoticon selector and spectator menu can be closed by touching anywhere outside of them.

Additionally, the emoticon selector and specator menu can now be closed by pressing the Escape key (i.e. the back button on Android). This made it necessary to change the order of the components for input handling, so the ingame menu will handle the Escape key after the emoticon selector and spectator menu. This also means that the Escape key can now be used to close the selector/menu when they are stuck open, which was previously delayed until ingame menu or console were opened and closed.
This commit is contained in:
Robert Müller 2024-08-21 20:43:53 +02:00
parent 3cf3e2339d
commit 3bda76ff1f
5 changed files with 96 additions and 7 deletions

View file

@ -41,6 +41,7 @@ void CEmoticon::OnReset()
m_Active = false; m_Active = false;
m_SelectedEmote = -1; m_SelectedEmote = -1;
m_SelectedEyeEmote = -1; m_SelectedEyeEmote = -1;
m_TouchPressedOutside = false;
} }
void CEmoticon::OnRelease() void CEmoticon::OnRelease()
@ -58,6 +59,16 @@ bool CEmoticon::OnCursorMove(float x, float y, IInput::ECursorType CursorType)
return true; return true;
} }
bool CEmoticon::OnInput(const IInput::CEvent &Event)
{
if(IsActive() && Event.m_Flags & IInput::FLAG_PRESS && Event.m_Key == KEY_ESCAPE)
{
OnRelease();
return true;
}
return false;
}
void CEmoticon::OnRender() void CEmoticon::OnRender()
{ {
if(Client()->State() != IClient::STATE_ONLINE && Client()->State() != IClient::STATE_DEMOPLAYBACK) if(Client()->State() != IClient::STATE_ONLINE && Client()->State() != IClient::STATE_DEMOPLAYBACK)
@ -65,6 +76,13 @@ void CEmoticon::OnRender()
if(!m_Active) if(!m_Active)
{ {
if(m_TouchPressedOutside)
{
m_SelectedEmote = -1;
m_SelectedEyeEmote = -1;
m_TouchPressedOutside = false;
}
if(m_WasActive && m_SelectedEmote != -1) if(m_WasActive && m_SelectedEmote != -1)
Emote(m_SelectedEmote); Emote(m_SelectedEmote);
if(m_WasActive && m_SelectedEyeEmote != -1) if(m_WasActive && m_SelectedEyeEmote != -1)
@ -82,6 +100,29 @@ void CEmoticon::OnRender()
m_WasActive = true; m_WasActive = true;
const CUIRect Screen = *Ui()->Screen();
const bool WasTouchPressed = m_TouchState.m_AnyPressed;
Ui()->UpdateTouchState(m_TouchState);
if(m_TouchState.m_AnyPressed)
{
const vec2 TouchPos = (m_TouchState.m_PrimaryPosition - vec2(0.5f, 0.5f)) * Screen.Size();
const float TouchCenterDistance = length(TouchPos);
if(TouchCenterDistance <= 170.0f)
{
m_SelectorMouse = TouchPos;
}
else if(TouchCenterDistance > 190.0f)
{
m_TouchPressedOutside = true;
}
}
else if(WasTouchPressed)
{
m_Active = false;
return;
}
if(length(m_SelectorMouse) > 170.0f) if(length(m_SelectorMouse) > 170.0f)
m_SelectorMouse = normalize(m_SelectorMouse) * 170.0f; m_SelectorMouse = normalize(m_SelectorMouse) * 170.0f;
@ -96,8 +137,6 @@ void CEmoticon::OnRender()
else if(length(m_SelectorMouse) > 40.0f) else if(length(m_SelectorMouse) > 40.0f)
m_SelectedEyeEmote = (int)(SelectedAngle / (2 * pi) * NUM_EMOTES); m_SelectedEyeEmote = (int)(SelectedAngle / (2 * pi) * NUM_EMOTES);
CUIRect Screen = *Ui()->Screen();
Ui()->MapScreen(); Ui()->MapScreen();
Graphics()->BlendNormal(); Graphics()->BlendNormal();

View file

@ -4,7 +4,9 @@
#define GAME_CLIENT_COMPONENTS_EMOTICON_H #define GAME_CLIENT_COMPONENTS_EMOTICON_H
#include <base/vmath.h> #include <base/vmath.h>
#include <engine/console.h> #include <engine/console.h>
#include <game/client/component.h> #include <game/client/component.h>
#include <game/client/ui.h>
class CEmoticon : public CComponent class CEmoticon : public CComponent
{ {
@ -15,6 +17,9 @@ class CEmoticon : public CComponent
int m_SelectedEmote; int m_SelectedEmote;
int m_SelectedEyeEmote; int m_SelectedEyeEmote;
CUi::CTouchState m_TouchState;
bool m_TouchPressedOutside;
static void ConKeyEmoticon(IConsole::IResult *pResult, void *pUserData); static void ConKeyEmoticon(IConsole::IResult *pResult, void *pUserData);
static void ConEmote(IConsole::IResult *pResult, void *pUserData); static void ConEmote(IConsole::IResult *pResult, void *pUserData);
@ -27,9 +32,12 @@ public:
virtual void OnRender() override; virtual void OnRender() override;
virtual void OnRelease() override; virtual void OnRelease() override;
virtual bool OnCursorMove(float x, float y, IInput::ECursorType CursorType) override; virtual bool OnCursorMove(float x, float y, IInput::ECursorType CursorType) override;
virtual bool OnInput(const IInput::CEvent &Event) override;
void Emote(int Emoticon); void Emote(int Emoticon);
void EyeEmote(int EyeEmote); void EyeEmote(int EyeEmote);
bool IsActive() const { return m_Active; }
}; };
#endif #endif

View file

@ -186,6 +186,16 @@ bool CSpectator::OnCursorMove(float x, float y, IInput::ECursorType CursorType)
return true; return true;
} }
bool CSpectator::OnInput(const IInput::CEvent &Event)
{
if(IsActive() && Event.m_Flags & IInput::FLAG_PRESS && Event.m_Key == KEY_ESCAPE)
{
OnRelease();
return true;
}
return false;
}
void CSpectator::OnRelease() void CSpectator::OnRelease()
{ {
OnReset(); OnReset();
@ -258,8 +268,6 @@ void CSpectator::OnRender()
float BoxMove = -10.0f; float BoxMove = -10.0f;
float BoxOffset = 0.0f; float BoxOffset = 0.0f;
const bool MousePressed = Input()->KeyPress(KEY_MOUSE_1);
for(const auto &pInfo : m_pClient->m_Snap.m_apInfoByDDTeamName) for(const auto &pInfo : m_pClient->m_Snap.m_apInfoByDDTeamName)
{ {
if(!pInfo || pInfo->m_Team == TEAM_SPECTATORS) if(!pInfo || pInfo->m_Team == TEAM_SPECTATORS)
@ -293,14 +301,42 @@ void CSpectator::OnRender()
ObjWidth = 600.0f; ObjWidth = 600.0f;
} }
const vec2 ScreenSize = vec2(Width, Height);
const vec2 ScreenCenter = ScreenSize / 2.0f;
CUIRect SpectatorRect = {Width / 2.0f - ObjWidth, Height / 2.0f - 300.0f, ObjWidth * 2.0f, 600.0f};
CUIRect SpectatorMouseRect;
SpectatorRect.Margin(20.0f, &SpectatorMouseRect);
const bool WasTouchPressed = m_TouchState.m_AnyPressed;
Ui()->UpdateTouchState(m_TouchState);
if(m_TouchState.m_AnyPressed)
{
const vec2 TouchPos = (m_TouchState.m_PrimaryPosition - vec2(0.5f, 0.5f)) * ScreenSize;
if(SpectatorMouseRect.Inside(ScreenCenter + TouchPos))
{
m_SelectorMouse = TouchPos;
}
}
else if(WasTouchPressed)
{
const vec2 TouchPos = (m_TouchState.m_PrimaryPosition - vec2(0.5f, 0.5f)) * ScreenSize;
if(!SpectatorRect.Inside(ScreenCenter + TouchPos))
{
OnRelease();
return;
}
}
Graphics()->MapScreen(0, 0, Width, Height); Graphics()->MapScreen(0, 0, Width, Height);
Graphics()->DrawRect(Width / 2.0f - ObjWidth, Height / 2.0f - 300.0f, ObjWidth * 2, 600.0f, ColorRGBA(0.0f, 0.0f, 0.0f, 0.3f), IGraphics::CORNER_ALL, 20.0f); SpectatorRect.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.3f), IGraphics::CORNER_ALL, 20.0f);
// clamp mouse position to selector area // clamp mouse position to selector area
m_SelectorMouse.x = clamp(m_SelectorMouse.x, -(ObjWidth - 20.0f), ObjWidth - 20.0f); m_SelectorMouse.x = clamp(m_SelectorMouse.x, -(ObjWidth - 20.0f), ObjWidth - 20.0f);
m_SelectorMouse.y = clamp(m_SelectorMouse.y, -280.0f, 280.0f); m_SelectorMouse.y = clamp(m_SelectorMouse.y, -280.0f, 280.0f);
const bool MousePressed = Input()->KeyPress(KEY_MOUSE_1) || m_TouchState.m_PrimaryPressed;
// draw selections // draw selections
if((Client()->State() == IClient::STATE_DEMOPLAYBACK && m_pClient->m_DemoSpecId == SPEC_FREEVIEW) || if((Client()->State() == IClient::STATE_DEMOPLAYBACK && m_pClient->m_DemoSpecId == SPEC_FREEVIEW) ||
(Client()->State() != IClient::STATE_DEMOPLAYBACK && m_pClient->m_Snap.m_SpecInfo.m_SpectatorId == SPEC_FREEVIEW)) (Client()->State() != IClient::STATE_DEMOPLAYBACK && m_pClient->m_Snap.m_SpecInfo.m_SpectatorId == SPEC_FREEVIEW))
@ -543,7 +579,7 @@ void CSpectator::OnRender()
} }
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);
RenderTools()->RenderCursor(m_SelectorMouse + vec2(Width, Height) / 2, 48.0f); RenderTools()->RenderCursor(ScreenCenter + m_SelectorMouse, 48.0f);
} }
void CSpectator::OnReset() void CSpectator::OnReset()

View file

@ -6,6 +6,7 @@
#include <engine/console.h> #include <engine/console.h>
#include <game/client/component.h> #include <game/client/component.h>
#include <game/client/ui.h>
class CSpectator : public CComponent class CSpectator : public CComponent
{ {
@ -21,6 +22,8 @@ class CSpectator : public CComponent
int m_SelectedSpectatorId; int m_SelectedSpectatorId;
vec2 m_SelectorMouse; vec2 m_SelectorMouse;
CUi::CTouchState m_TouchState;
float m_MultiViewActivateDelay; float m_MultiViewActivateDelay;
bool CanChangeSpectator(); bool CanChangeSpectator();
@ -39,12 +42,15 @@ public:
virtual void OnConsoleInit() override; virtual void OnConsoleInit() override;
virtual bool OnCursorMove(float x, float y, IInput::ECursorType CursorType) override; virtual bool OnCursorMove(float x, float y, IInput::ECursorType CursorType) override;
virtual bool OnInput(const IInput::CEvent &Event) override;
virtual void OnRender() override; virtual void OnRender() override;
virtual void OnRelease() override; virtual void OnRelease() override;
virtual void OnReset() override; virtual void OnReset() override;
void Spectate(int SpectatorId); void Spectate(int SpectatorId);
void SpectateClosest(); void SpectateClosest();
bool IsActive() const { return m_Active; }
}; };
#endif #endif

View file

@ -160,9 +160,9 @@ void CGameClient::OnConsoleInit()
&m_GameConsole, &m_GameConsole,
&m_Chat, // chat has higher prio, due to that you can quit it by pressing esc &m_Chat, // chat has higher prio, due to that you can quit it by pressing esc
&m_Motd, // for pressing esc to remove it &m_Motd, // for pressing esc to remove it
&m_Menus,
&m_Spectator, &m_Spectator,
&m_Emoticon, &m_Emoticon,
&m_Menus,
&m_Controls, &m_Controls,
&m_Binds}); &m_Binds});