mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-09 09:38:19 +00:00
Merge pull request #8621 from Robyt3/Client-Engine-UI-Touch-Input
Support touch input in engine, UI and console
This commit is contained in:
commit
cb9521d29f
|
@ -4558,6 +4558,10 @@ int main(int argc, const char **argv)
|
|||
pClient->ShellRegister();
|
||||
#endif
|
||||
|
||||
// Do not automatically translate touch events to mouse events and vice versa.
|
||||
SDL_SetHint("SDL_TOUCH_MOUSE_EVENTS", "0");
|
||||
SDL_SetHint("SDL_MOUSE_TOUCH_EVENTS", "0");
|
||||
|
||||
#if defined(CONF_PLATFORM_MACOS)
|
||||
// Hints will not be set if there is an existing override hint or environment variable that takes precedence.
|
||||
// So this respects cli environment overrides.
|
||||
|
|
|
@ -263,14 +263,7 @@ bool CInput::MouseRelative(float *pX, float *pY)
|
|||
return false;
|
||||
|
||||
ivec2 Relative;
|
||||
#if defined(CONF_PLATFORM_ANDROID) // No relative mouse on Android
|
||||
ivec2 CurrentPos;
|
||||
SDL_GetMouseState(&CurrentPos.x, &CurrentPos.y);
|
||||
Relative = CurrentPos - m_LastMousePos;
|
||||
m_LastMousePos = CurrentPos;
|
||||
#else
|
||||
SDL_GetRelativeMouseState(&Relative.x, &Relative.y);
|
||||
#endif
|
||||
|
||||
*pX = Relative.x;
|
||||
*pY = Relative.y;
|
||||
|
@ -287,25 +280,30 @@ void CInput::MouseModeAbsolute()
|
|||
void CInput::MouseModeRelative()
|
||||
{
|
||||
m_InputGrabbed = true;
|
||||
#if !defined(CONF_PLATFORM_ANDROID) // No relative mouse on Android
|
||||
SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||
#endif
|
||||
Graphics()->SetWindowGrab(true);
|
||||
// Clear pending relative mouse motion
|
||||
SDL_GetRelativeMouseState(nullptr, nullptr);
|
||||
}
|
||||
|
||||
void CInput::NativeMousePos(int *pX, int *pY) const
|
||||
vec2 CInput::NativeMousePos() const
|
||||
{
|
||||
SDL_GetMouseState(pX, pY);
|
||||
ivec2 Position;
|
||||
SDL_GetMouseState(&Position.x, &Position.y);
|
||||
return vec2(Position.x, Position.y);
|
||||
}
|
||||
|
||||
bool CInput::NativeMousePressed(int Index)
|
||||
bool CInput::NativeMousePressed(int Index) const
|
||||
{
|
||||
int i = SDL_GetMouseState(nullptr, nullptr);
|
||||
return (i & SDL_BUTTON(Index)) != 0;
|
||||
}
|
||||
|
||||
const std::vector<IInput::CTouchFingerState> &CInput::TouchFingerStates() const
|
||||
{
|
||||
return m_vTouchFingerStates;
|
||||
}
|
||||
|
||||
const char *CInput::GetClipboardText()
|
||||
{
|
||||
SDL_free(m_pClipboardText);
|
||||
|
@ -353,6 +351,10 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
float CInput::GetUpdateTime() const
|
||||
|
@ -539,6 +541,39 @@ void CInput::HandleJoystickRemovedEvent(const SDL_JoyDeviceEvent &Event)
|
|||
}
|
||||
}
|
||||
|
||||
void CInput::HandleTouchDownEvent(const SDL_TouchFingerEvent &Event)
|
||||
{
|
||||
CTouchFingerState TouchFingerState;
|
||||
TouchFingerState.m_Finger.m_DeviceId = Event.touchId;
|
||||
TouchFingerState.m_Finger.m_FingerId = Event.fingerId;
|
||||
TouchFingerState.m_Position = vec2(Event.x, Event.y);
|
||||
TouchFingerState.m_Delta = vec2(Event.dx, Event.dy);
|
||||
m_vTouchFingerStates.emplace_back(TouchFingerState);
|
||||
}
|
||||
|
||||
void CInput::HandleTouchUpEvent(const SDL_TouchFingerEvent &Event)
|
||||
{
|
||||
auto FoundState = std::find_if(m_vTouchFingerStates.begin(), m_vTouchFingerStates.end(), [Event](const CTouchFingerState &State) {
|
||||
return State.m_Finger.m_DeviceId == Event.touchId && State.m_Finger.m_FingerId == Event.fingerId;
|
||||
});
|
||||
if(FoundState != m_vTouchFingerStates.end())
|
||||
{
|
||||
m_vTouchFingerStates.erase(FoundState);
|
||||
}
|
||||
}
|
||||
|
||||
void CInput::HandleTouchMotionEvent(const SDL_TouchFingerEvent &Event)
|
||||
{
|
||||
auto FoundState = std::find_if(m_vTouchFingerStates.begin(), m_vTouchFingerStates.end(), [Event](const CTouchFingerState &State) {
|
||||
return State.m_Finger.m_DeviceId == Event.touchId && State.m_Finger.m_FingerId == Event.fingerId;
|
||||
});
|
||||
if(FoundState != m_vTouchFingerStates.end())
|
||||
{
|
||||
FoundState->m_Position = vec2(Event.x, Event.y);
|
||||
FoundState->m_Delta += vec2(Event.dx, Event.dy);
|
||||
}
|
||||
}
|
||||
|
||||
void CInput::SetCompositionWindowPosition(float X, float Y, float H)
|
||||
{
|
||||
SDL_Rect Rect;
|
||||
|
@ -745,6 +780,18 @@ int CInput::Update()
|
|||
Action |= IInput::FLAG_RELEASE;
|
||||
break;
|
||||
|
||||
case SDL_FINGERDOWN:
|
||||
HandleTouchDownEvent(Event.tfinger);
|
||||
break;
|
||||
|
||||
case SDL_FINGERUP:
|
||||
HandleTouchUpEvent(Event.tfinger);
|
||||
break;
|
||||
|
||||
case SDL_FINGERMOTION:
|
||||
HandleTouchMotionEvent(Event.tfinger);
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
// Ignore keys following a focus gain as they may be part of global
|
||||
// shortcuts
|
||||
|
|
|
@ -60,8 +60,8 @@ private:
|
|||
IEngineGraphics *m_pGraphics;
|
||||
IConsole *m_pConsole;
|
||||
|
||||
IEngineGraphics *Graphics() { return m_pGraphics; }
|
||||
IConsole *Console() { return m_pConsole; }
|
||||
IEngineGraphics *Graphics() const { return m_pGraphics; }
|
||||
IConsole *Console() const { return m_pConsole; }
|
||||
|
||||
// joystick
|
||||
std::vector<CJoystick> m_vJoysticks;
|
||||
|
@ -78,7 +78,6 @@ private:
|
|||
|
||||
bool m_MouseFocus;
|
||||
#if defined(CONF_PLATFORM_ANDROID)
|
||||
ivec2 m_LastMousePos = ivec2(0, 0); // No relative mouse on Android
|
||||
int m_NumBackPresses = 0;
|
||||
bool m_BackButtonReleased = true;
|
||||
int64_t m_LastBackPress = -1;
|
||||
|
@ -102,6 +101,7 @@ private:
|
|||
uint32_t m_aInputCount[g_MaxKeys];
|
||||
unsigned char m_aInputState[g_MaxKeys];
|
||||
uint32_t m_InputCounter;
|
||||
std::vector<CTouchFingerState> m_vTouchFingerStates;
|
||||
|
||||
void UpdateMouseState();
|
||||
void UpdateJoystickState();
|
||||
|
@ -110,6 +110,9 @@ private:
|
|||
void HandleJoystickHatMotionEvent(const SDL_JoyHatEvent &Event);
|
||||
void HandleJoystickAddedEvent(const SDL_JoyDeviceEvent &Event);
|
||||
void HandleJoystickRemovedEvent(const SDL_JoyDeviceEvent &Event);
|
||||
void HandleTouchDownEvent(const SDL_TouchFingerEvent &Event);
|
||||
void HandleTouchUpEvent(const SDL_TouchFingerEvent &Event);
|
||||
void HandleTouchMotionEvent(const SDL_TouchFingerEvent &Event);
|
||||
|
||||
char m_aDropFile[IO_MAX_PATH_LENGTH];
|
||||
|
||||
|
@ -142,8 +145,10 @@ public:
|
|||
bool MouseRelative(float *pX, float *pY) override;
|
||||
void MouseModeAbsolute() override;
|
||||
void MouseModeRelative() override;
|
||||
void NativeMousePos(int *pX, int *pY) const override;
|
||||
bool NativeMousePressed(int Index) override;
|
||||
vec2 NativeMousePos() const override;
|
||||
bool NativeMousePressed(int Index) const override;
|
||||
|
||||
const std::vector<CTouchFingerState> &TouchFingerStates() const override;
|
||||
|
||||
const char *GetClipboardText() override;
|
||||
void SetClipboardText(const char *pText) override;
|
||||
|
|
|
@ -6,9 +6,11 @@
|
|||
#include "kernel.h"
|
||||
|
||||
#include <base/types.h>
|
||||
#include <base/vmath.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
const int g_MaxKeys = 512;
|
||||
extern const char g_aaKeyStrings[g_MaxKeys][20];
|
||||
|
@ -89,12 +91,62 @@ public:
|
|||
virtual void SetActiveJoystick(size_t Index) = 0;
|
||||
|
||||
// mouse
|
||||
virtual void NativeMousePos(int *pX, int *pY) const = 0;
|
||||
virtual bool NativeMousePressed(int Index) = 0;
|
||||
virtual vec2 NativeMousePos() const = 0;
|
||||
virtual bool NativeMousePressed(int Index) const = 0;
|
||||
virtual void MouseModeRelative() = 0;
|
||||
virtual void MouseModeAbsolute() = 0;
|
||||
virtual bool MouseRelative(float *pX, float *pY) = 0;
|
||||
|
||||
// touch
|
||||
/**
|
||||
* Represents a unique finger for a current touch event. If there are multiple touch input devices, they
|
||||
* are handled transparently like different fingers. The concrete values of the member variables of this
|
||||
* class are arbitrary based on the touch device driver and should only be used to uniquely identify touch
|
||||
* fingers. Note that once a finger has been released, the same finger value may also be reused again.
|
||||
*/
|
||||
class CTouchFinger
|
||||
{
|
||||
friend class CInput;
|
||||
|
||||
int64_t m_DeviceId;
|
||||
int64_t m_FingerId;
|
||||
|
||||
public:
|
||||
bool operator==(const CTouchFinger &Other) const { return m_DeviceId == Other.m_DeviceId && m_FingerId == Other.m_FingerId; }
|
||||
bool operator!=(const CTouchFinger &Other) const { return !(*this == Other); }
|
||||
};
|
||||
/**
|
||||
* Represents the state of a particular touch finger currently being pressed down on a touch device.
|
||||
*/
|
||||
class CTouchFingerState
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* The unique finger which this state is associated with.
|
||||
*/
|
||||
CTouchFinger m_Finger;
|
||||
/**
|
||||
* The current position of the finger. The x- and y-components of the position are normalized to the
|
||||
* range `0.0f`-`1.0f` representing the absolute position of the finger on the current touch device.
|
||||
*/
|
||||
vec2 m_Position;
|
||||
/**
|
||||
* The current delta of the finger. The x- and y-components of the delta are normalized to the
|
||||
* range `0.0f`-`1.0f` representing the absolute delta of the finger on the current touch device.
|
||||
*
|
||||
* @remark This is reset to zero at the end of each frame.
|
||||
*/
|
||||
vec2 m_Delta;
|
||||
};
|
||||
/**
|
||||
* Returns a vector of the states of all touch fingers currently being pressed down on touch devices.
|
||||
* Note that this only contains fingers which are pressed down, i.e. released fingers are never stored.
|
||||
* The order of the fingers in this vector is based on the order in which the fingers where pressed.
|
||||
*
|
||||
* @return vector of all touch finger states
|
||||
*/
|
||||
virtual const std::vector<CTouchFingerState> &TouchFingerStates() const = 0;
|
||||
|
||||
// clipboard
|
||||
virtual const char *GetClipboardText() = 0;
|
||||
virtual void SetClipboardText(const char *pText) = 0;
|
||||
|
|
|
@ -1072,24 +1072,48 @@ void CGameConsole::OnRender()
|
|||
TextRender()->TextEx(&Cursor, aPrompt);
|
||||
|
||||
// check if mouse is pressed
|
||||
if(!pConsole->m_MouseIsPress && Input()->NativeMousePressed(1))
|
||||
const vec2 WindowSize = vec2(Graphics()->WindowWidth(), Graphics()->WindowHeight());
|
||||
const vec2 ScreenSize = vec2(Screen.w, Screen.h);
|
||||
Ui()->UpdateTouchState(m_TouchState);
|
||||
const auto &&GetMousePosition = [&]() -> vec2 {
|
||||
if(m_TouchState.m_PrimaryPressed)
|
||||
{
|
||||
return m_TouchState.m_PrimaryPosition * ScreenSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Input()->NativeMousePos() / WindowSize * ScreenSize;
|
||||
}
|
||||
};
|
||||
if(!pConsole->m_MouseIsPress && (m_TouchState.m_PrimaryPressed || Input()->NativeMousePressed(1)))
|
||||
{
|
||||
pConsole->m_MouseIsPress = true;
|
||||
ivec2 MousePress;
|
||||
Input()->NativeMousePos(&MousePress.x, &MousePress.y);
|
||||
pConsole->m_MousePress.x = (MousePress.x / (float)Graphics()->WindowWidth()) * Screen.w;
|
||||
pConsole->m_MousePress.y = (MousePress.y / (float)Graphics()->WindowHeight()) * Screen.h;
|
||||
pConsole->m_MousePress = GetMousePosition();
|
||||
}
|
||||
if(pConsole->m_MouseIsPress && !m_TouchState.m_PrimaryPressed && !Input()->NativeMousePressed(1))
|
||||
{
|
||||
pConsole->m_MouseIsPress = false;
|
||||
}
|
||||
if(pConsole->m_MouseIsPress)
|
||||
{
|
||||
ivec2 MouseRelease;
|
||||
Input()->NativeMousePos(&MouseRelease.x, &MouseRelease.y);
|
||||
pConsole->m_MouseRelease.x = (MouseRelease.x / (float)Graphics()->WindowWidth()) * Screen.w;
|
||||
pConsole->m_MouseRelease.y = (MouseRelease.y / (float)Graphics()->WindowHeight()) * Screen.h;
|
||||
pConsole->m_MouseRelease = GetMousePosition();
|
||||
}
|
||||
if(pConsole->m_MouseIsPress && !Input()->NativeMousePressed(1))
|
||||
const float ScaledRowHeight = RowHeight / ScreenSize.y;
|
||||
if(absolute(m_TouchState.m_ScrollAmount.y) >= ScaledRowHeight)
|
||||
{
|
||||
pConsole->m_MouseIsPress = false;
|
||||
if(m_TouchState.m_ScrollAmount.y > 0.0f)
|
||||
{
|
||||
pConsole->m_BacklogCurLine += pConsole->GetLinesToScroll(-1, 1);
|
||||
m_TouchState.m_ScrollAmount.y -= ScaledRowHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
--pConsole->m_BacklogCurLine;
|
||||
if(pConsole->m_BacklogCurLine < 0)
|
||||
pConsole->m_BacklogCurLine = 0;
|
||||
m_TouchState.m_ScrollAmount.y += ScaledRowHeight;
|
||||
}
|
||||
pConsole->m_HasSelection = false;
|
||||
}
|
||||
|
||||
x = Cursor.m_X;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include <game/client/component.h>
|
||||
#include <game/client/lineinput.h>
|
||||
#include <game/client/ui.h>
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -153,6 +154,7 @@ class CGameConsole : public CComponent
|
|||
float m_StateChangeDuration;
|
||||
|
||||
bool m_WantsSelectionCopy = false;
|
||||
CUi::CTouchState m_TouchState;
|
||||
|
||||
static const ColorRGBA ms_SearchHighlightColor;
|
||||
static const ColorRGBA ms_SearchSelectedColor;
|
||||
|
|
|
@ -176,24 +176,63 @@ void CUi::OnCursorMove(float X, float Y)
|
|||
|
||||
void CUi::Update(vec2 MouseWorldPos)
|
||||
{
|
||||
unsigned MouseButtons = 0;
|
||||
const vec2 WindowSize = vec2(Graphics()->WindowWidth(), Graphics()->WindowHeight());
|
||||
const CUIRect *pScreen = Screen();
|
||||
|
||||
unsigned UpdatedMouseButtonsNext = 0;
|
||||
if(Enabled())
|
||||
{
|
||||
if(Input()->KeyIsPressed(KEY_MOUSE_1))
|
||||
MouseButtons |= 1;
|
||||
if(Input()->KeyIsPressed(KEY_MOUSE_2))
|
||||
MouseButtons |= 2;
|
||||
if(Input()->KeyIsPressed(KEY_MOUSE_3))
|
||||
MouseButtons |= 4;
|
||||
// Update mouse buttons based on mouse keys
|
||||
for(int MouseKey = KEY_MOUSE_1; MouseKey <= KEY_MOUSE_3; ++MouseKey)
|
||||
{
|
||||
if(Input()->KeyIsPressed(MouseKey))
|
||||
{
|
||||
m_UpdatedMouseButtons |= 1 << (MouseKey - KEY_MOUSE_1);
|
||||
}
|
||||
}
|
||||
|
||||
// Update mouse position and buttons based on touch finger state
|
||||
UpdateTouchState(m_TouchState);
|
||||
if(m_TouchState.m_AnyPressed)
|
||||
{
|
||||
if(!CheckMouseLock())
|
||||
{
|
||||
m_UpdatedMousePos = m_TouchState.m_PrimaryPosition * WindowSize;
|
||||
m_UpdatedMousePos.x = clamp(m_UpdatedMousePos.x, 0.0f, WindowSize.x - 1.0f);
|
||||
m_UpdatedMousePos.y = clamp(m_UpdatedMousePos.y, 0.0f, WindowSize.y - 1.0f);
|
||||
}
|
||||
m_UpdatedMouseDelta += m_TouchState.m_PrimaryDelta * WindowSize;
|
||||
|
||||
// Scroll currently hovered scroll region with touch scroll gesture.
|
||||
if(m_TouchState.m_ScrollAmount != vec2(0.0f, 0.0f))
|
||||
{
|
||||
if(m_pHotScrollRegion != nullptr)
|
||||
{
|
||||
m_pHotScrollRegion->ScrollRelativeDirect(-m_TouchState.m_ScrollAmount.y * pScreen->h);
|
||||
}
|
||||
m_TouchState.m_ScrollAmount = vec2(0.0f, 0.0f);
|
||||
}
|
||||
|
||||
// We need to delay the click until the next update or it's not possible to use UI
|
||||
// elements because click and hover would happen at the same time for touch events.
|
||||
if(m_TouchState.m_PrimaryPressed)
|
||||
{
|
||||
UpdatedMouseButtonsNext |= 1;
|
||||
}
|
||||
if(m_TouchState.m_SecondaryPressed)
|
||||
{
|
||||
UpdatedMouseButtonsNext |= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const CUIRect *pScreen = Screen();
|
||||
m_MousePos = m_UpdatedMousePos * vec2(pScreen->w / Graphics()->WindowWidth(), pScreen->h / Graphics()->WindowHeight());
|
||||
m_MousePos = m_UpdatedMousePos * vec2(pScreen->w, pScreen->h) / WindowSize;
|
||||
m_MouseDelta = m_UpdatedMouseDelta;
|
||||
m_UpdatedMouseDelta = vec2(0.0f, 0.0f);
|
||||
m_MouseWorldPos = MouseWorldPos;
|
||||
m_LastMouseButtons = m_MouseButtons;
|
||||
m_MouseButtons = MouseButtons;
|
||||
m_MouseButtons = m_UpdatedMouseButtons;
|
||||
m_UpdatedMouseButtons = UpdatedMouseButtonsNext;
|
||||
|
||||
m_pHotItem = m_pBecomingHotItem;
|
||||
if(m_pActiveItem)
|
||||
|
@ -257,6 +296,87 @@ void CUi::ConvertMouseMove(float *pX, float *pY, IInput::ECursorType CursorType)
|
|||
*pY *= Factor;
|
||||
}
|
||||
|
||||
void CUi::UpdateTouchState(CTouchState &State) const
|
||||
{
|
||||
const std::vector<IInput::CTouchFingerState> &vTouchFingerStates = Input()->TouchFingerStates();
|
||||
|
||||
// Updated touch position as long as any finger is beinged pressed.
|
||||
const bool WasAnyPressed = State.m_AnyPressed;
|
||||
State.m_AnyPressed = !vTouchFingerStates.empty();
|
||||
if(State.m_AnyPressed)
|
||||
{
|
||||
// We always use the position of first finger being pressed down. Multi-touch UI is
|
||||
// not possible and always choosing the last finger would cause the cursor to briefly
|
||||
// warp without having any effect if multiple fingers are used.
|
||||
const IInput::CTouchFingerState &PrimaryTouchFingerState = vTouchFingerStates.front();
|
||||
State.m_PrimaryPosition = PrimaryTouchFingerState.m_Position;
|
||||
State.m_PrimaryDelta = PrimaryTouchFingerState.m_Delta;
|
||||
}
|
||||
|
||||
// Update primary (left click) and secondary (right click) action.
|
||||
if(State.m_SecondaryPressedNext)
|
||||
{
|
||||
// The secondary action is delayed by one frame until the primary has been released,
|
||||
// otherwise most UI elements cannot be activated by the secondary action because they
|
||||
// never become the hot-item unless all mouse buttons are released for one frame.
|
||||
State.m_SecondaryPressedNext = false;
|
||||
State.m_SecondaryPressed = true;
|
||||
}
|
||||
else if(vTouchFingerStates.size() != 1)
|
||||
{
|
||||
// Consider primary and secondary to be pressed only when exactly one finger is pressed,
|
||||
// to avoid UI elements and console text selection being activated while scrolling.
|
||||
State.m_PrimaryPressed = false;
|
||||
State.m_SecondaryPressed = false;
|
||||
}
|
||||
else if(!WasAnyPressed)
|
||||
{
|
||||
State.m_PrimaryPressed = true;
|
||||
State.m_SecondaryActivationTime = Client()->GlobalTime();
|
||||
State.m_SecondaryActivationDelta = vec2(0.0f, 0.0f);
|
||||
}
|
||||
else if(State.m_PrimaryPressed)
|
||||
{
|
||||
// Activate secondary by pressing and holding roughly on the same position for some time.
|
||||
const float SecondaryActivationDelay = 0.5f;
|
||||
const float SecondaryActivationMaxDistance = 0.001f;
|
||||
State.m_SecondaryActivationDelta += State.m_PrimaryDelta;
|
||||
if(Client()->GlobalTime() - State.m_SecondaryActivationTime >= SecondaryActivationDelay &&
|
||||
length(State.m_SecondaryActivationDelta) <= SecondaryActivationMaxDistance)
|
||||
{
|
||||
State.m_PrimaryPressed = false;
|
||||
State.m_SecondaryPressedNext = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle two fingers being moved roughly in same direction as a scrolling gesture.
|
||||
if(vTouchFingerStates.size() == 2)
|
||||
{
|
||||
const vec2 Delta0 = vTouchFingerStates[0].m_Delta;
|
||||
const vec2 Delta1 = vTouchFingerStates[1].m_Delta;
|
||||
const float Similarity = dot(normalize(Delta0), normalize(Delta1));
|
||||
const float SimilarityThreshold = 0.8f; // How parallel the deltas have to be (1.0f being completely parallel)
|
||||
if(Similarity > SimilarityThreshold)
|
||||
{
|
||||
const float DirectionThreshold = 3.0f; // How much longer the delta of one axis has to be compared to other axis
|
||||
|
||||
// Vertical scrolling (y-delta must be larger than x-delta)
|
||||
if(absolute(Delta0.y) > DirectionThreshold * absolute(Delta0.x) &&
|
||||
absolute(Delta1.y) > DirectionThreshold * absolute(Delta1.x) &&
|
||||
Delta0.y * Delta1.y > 0.0f) // Same y direction required
|
||||
{
|
||||
// Accumulate average delta of the two fingers
|
||||
State.m_ScrollAmount.y += (Delta0.y + Delta1.y) / 2.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Scrolling gesture should start from zero again if released.
|
||||
State.m_ScrollAmount = vec2(0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
bool CUi::ConsumeHotkey(EHotkey Hotkey)
|
||||
{
|
||||
const bool Pressed = m_HotkeysPressed & Hotkey;
|
||||
|
|
|
@ -320,6 +320,26 @@ public:
|
|||
*/
|
||||
typedef std::function<void()> FPopupMenuClosedCallback;
|
||||
|
||||
/**
|
||||
* Represents the aggregated state of current touch events to control a user interface.
|
||||
*/
|
||||
class CTouchState
|
||||
{
|
||||
friend class CUi;
|
||||
|
||||
bool m_SecondaryPressedNext = false;
|
||||
float m_SecondaryActivationTime = 0.0f;
|
||||
vec2 m_SecondaryActivationDelta = vec2(0.0f, 0.0f);
|
||||
|
||||
public:
|
||||
bool m_AnyPressed = false;
|
||||
bool m_PrimaryPressed = false;
|
||||
bool m_SecondaryPressed = false;
|
||||
vec2 m_PrimaryPosition = vec2(-1.0f, -1.0f);
|
||||
vec2 m_PrimaryDelta = vec2(0.0f, 0.0f);
|
||||
vec2 m_ScrollAmount = vec2(0.0f, 0.0f);
|
||||
};
|
||||
|
||||
private:
|
||||
bool m_Enabled;
|
||||
|
||||
|
@ -327,8 +347,8 @@ private:
|
|||
const void *m_pActiveItem = nullptr;
|
||||
const void *m_pLastActiveItem = nullptr; // only used internally to track active CLineInput
|
||||
const void *m_pBecomingHotItem = nullptr;
|
||||
const CScrollRegion *m_pHotScrollRegion = nullptr;
|
||||
const CScrollRegion *m_pBecomingHotScrollRegion = nullptr;
|
||||
CScrollRegion *m_pHotScrollRegion = nullptr;
|
||||
CScrollRegion *m_pBecomingHotScrollRegion = nullptr;
|
||||
bool m_ActiveItemValid = false;
|
||||
|
||||
int m_ActiveButtonLogicButton = -1;
|
||||
|
@ -360,8 +380,10 @@ private:
|
|||
vec2 m_MousePos = vec2(0.0f, 0.0f); // in gui space
|
||||
vec2 m_MouseDelta = vec2(0.0f, 0.0f); // in gui space
|
||||
vec2 m_MouseWorldPos = vec2(-1.0f, -1.0f); // in world space
|
||||
unsigned m_UpdatedMouseButtons = 0;
|
||||
unsigned m_MouseButtons = 0;
|
||||
unsigned m_LastMouseButtons = 0;
|
||||
CTouchState m_TouchState;
|
||||
bool m_MouseSlow = false;
|
||||
bool m_MouseLock = false;
|
||||
const void *m_pMouseLockId = nullptr;
|
||||
|
@ -491,7 +513,7 @@ public:
|
|||
}
|
||||
return false;
|
||||
}
|
||||
void SetHotScrollRegion(const CScrollRegion *pId) { m_pBecomingHotScrollRegion = pId; }
|
||||
void SetHotScrollRegion(CScrollRegion *pId) { m_pBecomingHotScrollRegion = pId; }
|
||||
const void *HotItem() const { return m_pHotItem; }
|
||||
const void *NextHotItem() const { return m_pBecomingHotItem; }
|
||||
const void *ActiveItem() const { return m_pActiveItem; }
|
||||
|
@ -512,6 +534,7 @@ public:
|
|||
bool MouseInsideClip() const { return !IsClipped() || MouseInside(ClipArea()); }
|
||||
bool MouseHovered(const CUIRect *pRect) const { return MouseInside(pRect) && MouseInsideClip(); }
|
||||
void ConvertMouseMove(float *pX, float *pY, IInput::ECursorType CursorType) const;
|
||||
void UpdateTouchState(CTouchState &State) const;
|
||||
void ResetMouseSlow() { m_MouseSlow = false; }
|
||||
|
||||
bool ConsumeHotkey(EHotkey Hotkey);
|
||||
|
|
|
@ -234,6 +234,11 @@ void CScrollRegion::ScrollRelative(EScrollRelative Direction, float SpeedMultipl
|
|||
m_ScrollSpeedMultiplier = SpeedMultiplier;
|
||||
}
|
||||
|
||||
void CScrollRegion::ScrollRelativeDirect(float ScrollAmount)
|
||||
{
|
||||
m_RequestScrollY = clamp(m_ScrollY + ScrollAmount, 0.0f, m_ContentH - m_ClipRect.h);
|
||||
}
|
||||
|
||||
void CScrollRegion::DoEdgeScrolling()
|
||||
{
|
||||
if(!ScrollbarShown())
|
||||
|
|
|
@ -133,6 +133,7 @@ public:
|
|||
bool AddRect(const CUIRect &Rect, bool ShouldScrollHere = false); // returns true if the added rect is visible (not clipped)
|
||||
void ScrollHere(EScrollOption Option = SCROLLHERE_KEEP_IN_VIEW);
|
||||
void ScrollRelative(EScrollRelative Direction, float SpeedMultiplier = 1.0f);
|
||||
void ScrollRelativeDirect(float ScrollAmount);
|
||||
const CUIRect *ClipRect() const { return &m_ClipRect; }
|
||||
void DoEdgeScrolling();
|
||||
bool RectClipped(const CUIRect &Rect) const;
|
||||
|
|
Loading…
Reference in a new issue