mirror of
https://github.com/vcmi/vcmi.git
synced 2025-02-03 13:01:33 +02:00
Implemented left & right click support for touch input
This commit is contained in:
parent
c9d42d59c3
commit
1a5c69a424
@ -78,6 +78,7 @@ void InputHandler::processEvents()
|
||||
handleCurrentEvent(currentEvent);
|
||||
|
||||
eventsQueue.clear();
|
||||
fingerHandler->handleUpdate();
|
||||
}
|
||||
|
||||
bool InputHandler::ignoreEventsUntilInput()
|
||||
|
@ -20,94 +20,175 @@
|
||||
#include "../gui/MouseButton.h"
|
||||
|
||||
#include <SDL_events.h>
|
||||
#include <SDL_render.h>
|
||||
#include <SDL_hints.h>
|
||||
#include <SDL_timer.h>
|
||||
|
||||
InputSourceTouch::InputSourceTouch()
|
||||
: multifinger(false)
|
||||
, isPointerRelativeMode(settings["general"]["userRelativePointer"].Bool())
|
||||
, pointerSpeedMultiplier(settings["general"]["relativePointerSpeedMultiplier"].Float())
|
||||
: lastTapTimeTicks(0)
|
||||
{
|
||||
if(isPointerRelativeMode)
|
||||
{
|
||||
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
|
||||
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
|
||||
}
|
||||
params.useRelativeMode = settings["general"]["userRelativePointer"].Bool();
|
||||
params.relativeModeSpeedFactor = settings["general"]["relativePointerSpeedMultiplier"].Float();
|
||||
|
||||
if (params.useRelativeMode)
|
||||
state = TouchState::RELATIVE_MODE;
|
||||
else
|
||||
state = TouchState::IDLE;
|
||||
|
||||
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
|
||||
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
|
||||
}
|
||||
|
||||
void InputSourceTouch::handleEventFingerMotion(const SDL_TouchFingerEvent & tfinger)
|
||||
{
|
||||
if(isPointerRelativeMode)
|
||||
switch(state)
|
||||
{
|
||||
Point screenSize = GH.screenDimensions();
|
||||
case TouchState::RELATIVE_MODE:
|
||||
{
|
||||
Point screenSize = GH.screenDimensions();
|
||||
|
||||
Point moveDistance {
|
||||
static_cast<int>(screenSize.x * pointerSpeedMultiplier * tfinger.dx),
|
||||
static_cast<int>(screenSize.y * pointerSpeedMultiplier * tfinger.dy)
|
||||
};
|
||||
Point moveDistance {
|
||||
static_cast<int>(screenSize.x * params.relativeModeSpeedFactor * tfinger.dx),
|
||||
static_cast<int>(screenSize.y * params.relativeModeSpeedFactor * tfinger.dy)
|
||||
};
|
||||
|
||||
GH.input().moveCursorPosition(moveDistance);
|
||||
GH.input().moveCursorPosition(moveDistance);
|
||||
break;
|
||||
}
|
||||
case TouchState::IDLE:
|
||||
{
|
||||
// no-op, might happen in some edge cases, e.g. when fingerdown event was ignored
|
||||
break;
|
||||
}
|
||||
case TouchState::TAP_DOWN_SHORT:
|
||||
{
|
||||
state = TouchState::TAP_DOWN_PANNING;
|
||||
break;
|
||||
}
|
||||
case TouchState::TAP_DOWN_PANNING:
|
||||
{
|
||||
emitPanningEvent();
|
||||
break;
|
||||
}
|
||||
case TouchState::TAP_DOWN_DOUBLE:
|
||||
{
|
||||
emitPinchEvent();
|
||||
break;
|
||||
}
|
||||
case TouchState::TAP_DOWN_LONG:
|
||||
{
|
||||
// no-op
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputSourceTouch::handleEventFingerDown(const SDL_TouchFingerEvent & tfinger)
|
||||
{
|
||||
if(isPointerRelativeMode)
|
||||
{
|
||||
if(tfinger.x > 0.5)
|
||||
{
|
||||
bool isRightClick = tfinger.y < 0.5;
|
||||
lastTapTimeTicks = tfinger.timestamp;
|
||||
|
||||
if (isRightClick)
|
||||
GH.events().dispatchMouseButtonPressed(MouseButton::RIGHT, GH.getCursorPosition());
|
||||
else
|
||||
GH.events().dispatchMouseButtonPressed(MouseButton::LEFT, GH.getCursorPosition());
|
||||
switch(state)
|
||||
{
|
||||
case TouchState::RELATIVE_MODE:
|
||||
{
|
||||
if(tfinger.x > 0.5)
|
||||
{
|
||||
MouseButton button = tfinger.y < 0.5 ? MouseButton::RIGHT : MouseButton::LEFT;
|
||||
GH.events().dispatchMouseButtonPressed(button, GH.getCursorPosition());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TouchState::IDLE:
|
||||
{
|
||||
GH.input().setCursorPosition(convertTouchToMouse(tfinger));
|
||||
state = TouchState::TAP_DOWN_SHORT;
|
||||
break;
|
||||
}
|
||||
case TouchState::TAP_DOWN_SHORT:
|
||||
{
|
||||
GH.input().setCursorPosition(convertTouchToMouse(tfinger));
|
||||
state = TouchState::TAP_DOWN_DOUBLE;
|
||||
break;
|
||||
}
|
||||
case TouchState::TAP_DOWN_PANNING:
|
||||
{
|
||||
GH.input().setCursorPosition(convertTouchToMouse(tfinger));
|
||||
state = TouchState::TAP_DOWN_DOUBLE;
|
||||
break;
|
||||
}
|
||||
case TouchState::TAP_DOWN_DOUBLE:
|
||||
{
|
||||
// TODO? ignore?
|
||||
break;
|
||||
}
|
||||
case TouchState::TAP_DOWN_LONG:
|
||||
{
|
||||
// no-op
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifndef VCMI_IOS
|
||||
else
|
||||
{
|
||||
auto fingerCount = SDL_GetNumTouchFingers(tfinger.touchId);
|
||||
multifinger = fingerCount > 1;
|
||||
|
||||
if(fingerCount == 2)
|
||||
{
|
||||
Point position = convertTouchToMouse(tfinger);
|
||||
|
||||
GH.input().setCursorPosition(position);
|
||||
GH.events().dispatchMouseButtonPressed(MouseButton::RIGHT, position);
|
||||
}
|
||||
}
|
||||
#endif //VCMI_IOS
|
||||
}
|
||||
|
||||
void InputSourceTouch::handleEventFingerUp(const SDL_TouchFingerEvent & tfinger)
|
||||
{
|
||||
if(isPointerRelativeMode)
|
||||
switch(state)
|
||||
{
|
||||
if(tfinger.x > 0.5)
|
||||
case TouchState::RELATIVE_MODE:
|
||||
{
|
||||
bool isRightClick = tfinger.y < 0.5;
|
||||
if(tfinger.x > 0.5)
|
||||
{
|
||||
MouseButton button = tfinger.y < 0.5 ? MouseButton::RIGHT : MouseButton::LEFT;
|
||||
GH.events().dispatchMouseButtonReleased(button, GH.getCursorPosition());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TouchState::IDLE:
|
||||
{
|
||||
// no-op, might happen in some edge cases, e.g. when fingerdown event was ignored
|
||||
break;
|
||||
}
|
||||
case TouchState::TAP_DOWN_SHORT:
|
||||
{
|
||||
GH.input().setCursorPosition(convertTouchToMouse(tfinger));
|
||||
GH.events().dispatchMouseButtonPressed(MouseButton::LEFT, convertTouchToMouse(tfinger));
|
||||
GH.events().dispatchMouseButtonReleased(MouseButton::LEFT, convertTouchToMouse(tfinger));
|
||||
state = TouchState::IDLE;
|
||||
break;
|
||||
}
|
||||
case TouchState::TAP_DOWN_PANNING:
|
||||
{
|
||||
state = TouchState::IDLE;
|
||||
break;
|
||||
}
|
||||
case TouchState::TAP_DOWN_DOUBLE:
|
||||
{
|
||||
if (SDL_GetNumTouchFingers(tfinger.touchId) == 1)
|
||||
state = TouchState::TAP_DOWN_PANNING;
|
||||
break;
|
||||
}
|
||||
case TouchState::TAP_DOWN_LONG:
|
||||
{
|
||||
if (SDL_GetNumTouchFingers(tfinger.touchId) == 0)
|
||||
{
|
||||
GH.input().setCursorPosition(convertTouchToMouse(tfinger));
|
||||
GH.events().dispatchMouseButtonReleased(MouseButton::RIGHT, convertTouchToMouse(tfinger));
|
||||
state = TouchState::IDLE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isRightClick)
|
||||
GH.events().dispatchMouseButtonReleased(MouseButton::RIGHT, GH.getCursorPosition());
|
||||
else
|
||||
GH.events().dispatchMouseButtonReleased(MouseButton::LEFT, GH.getCursorPosition());
|
||||
}
|
||||
}
|
||||
#ifndef VCMI_IOS
|
||||
else
|
||||
void InputSourceTouch::handleUpdate()
|
||||
{
|
||||
if ( state == TouchState::TAP_DOWN_SHORT)
|
||||
{
|
||||
if(multifinger)
|
||||
uint32_t currentTime = SDL_GetTicks();
|
||||
if (currentTime > lastTapTimeTicks + params.longPressTimeMilliseconds)
|
||||
{
|
||||
auto fingerCount = SDL_GetNumTouchFingers(tfinger.touchId);
|
||||
Point position = convertTouchToMouse(tfinger);
|
||||
GH.input().setCursorPosition(position);
|
||||
GH.events().dispatchMouseButtonReleased(MouseButton::RIGHT, position);
|
||||
multifinger = fingerCount != 0;
|
||||
state = TouchState::TAP_DOWN_LONG;
|
||||
GH.events().dispatchMouseButtonPressed(MouseButton::RIGHT, GH.getCursorPosition());
|
||||
}
|
||||
}
|
||||
#endif //VCMI_IOS
|
||||
}
|
||||
|
||||
Point InputSourceTouch::convertTouchToMouse(const SDL_TouchFingerEvent & tfinger)
|
||||
@ -117,5 +198,21 @@ Point InputSourceTouch::convertTouchToMouse(const SDL_TouchFingerEvent & tfinger
|
||||
|
||||
bool InputSourceTouch::isMouseButtonPressed(MouseButton button) const
|
||||
{
|
||||
if (state == TouchState::TAP_DOWN_LONG)
|
||||
{
|
||||
if (button == MouseButton::RIGHT)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void InputSourceTouch::emitPanningEvent()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
void InputSourceTouch::emitPinchEvent()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
@ -17,16 +17,77 @@ VCMI_LIB_NAMESPACE_END
|
||||
enum class MouseButton;
|
||||
struct SDL_TouchFingerEvent;
|
||||
|
||||
/// Enumeration that describes current state of gesture recognition
|
||||
enum class TouchState
|
||||
{
|
||||
// special state that allows no transitions
|
||||
// used when player selects "relative mode" in Launcher
|
||||
// in this mode touchscreen acts like touchpad, moving cursor at certains speed
|
||||
// and generates events for positions below cursor instead of positions below touch events
|
||||
RELATIVE_MODE,
|
||||
|
||||
// no active touch events
|
||||
// DOWN -> transition to TAP_DOWN_SHORT
|
||||
// MOTION / UP -> not expected
|
||||
IDLE,
|
||||
|
||||
// single finger is touching the screen for a short time
|
||||
// DOWN -> transition to TAP_DOWN_DOUBLE
|
||||
// MOTION -> transition to TAP_DOWN_PANNING
|
||||
// UP -> transition to IDLE, emit onLeftClickDown and onLeftClickUp
|
||||
// on timer -> transition to TAP_DOWN_LONG, emit onRightClickDown event
|
||||
TAP_DOWN_SHORT,
|
||||
|
||||
// single finger is moving across screen
|
||||
// DOWN -> transition to TAP_DOWN_DOUBLE
|
||||
// MOTION -> emit panning event
|
||||
// UP -> transition to IDLE
|
||||
TAP_DOWN_PANNING,
|
||||
|
||||
// two fingers are touching the screen
|
||||
// DOWN -> ??? how to handle 3rd finger? Ignore?
|
||||
// MOTION -> emit pinch event
|
||||
// UP -> transition to TAP_DOWN
|
||||
TAP_DOWN_DOUBLE,
|
||||
|
||||
// single finger is down for long period of time
|
||||
// DOWN -> ignored
|
||||
// MOTION -> ignored
|
||||
// UP -> transition to IDLE, generate onRightClickUp() event
|
||||
TAP_DOWN_LONG,
|
||||
|
||||
|
||||
// Possible transitions:
|
||||
// -> DOUBLE
|
||||
// -> PANNING -> IDLE
|
||||
// IDLE -> DOWN_SHORT -> IDLE
|
||||
// -> LONG -> IDLE
|
||||
// -> DOUBLE -> PANNING
|
||||
// -> IDLE
|
||||
};
|
||||
|
||||
struct TouchInputParameters
|
||||
{
|
||||
double relativeModeSpeedFactor = 1.0;
|
||||
uint32_t longPressTimeMilliseconds = 500;
|
||||
|
||||
bool useHoldGesture = true;
|
||||
bool usePanGesture = true;
|
||||
bool usePinchGesture = true;
|
||||
bool useRelativeMode = false;
|
||||
};
|
||||
|
||||
/// Class that handles touchscreen input from SDL events
|
||||
class InputSourceTouch
|
||||
{
|
||||
double pointerSpeedMultiplier;
|
||||
bool multifinger;
|
||||
bool isPointerRelativeMode;
|
||||
TouchInputParameters params;
|
||||
TouchState state;
|
||||
uint32_t lastTapTimeTicks;
|
||||
|
||||
Point convertTouchToMouse(const SDL_TouchFingerEvent & current);
|
||||
|
||||
void fakeMouseButtonEventRelativeMode(bool down, bool right);
|
||||
void emitPanningEvent();
|
||||
void emitPinchEvent();
|
||||
|
||||
public:
|
||||
InputSourceTouch();
|
||||
@ -35,5 +96,7 @@ public:
|
||||
void handleEventFingerDown(const SDL_TouchFingerEvent & current);
|
||||
void handleEventFingerUp(const SDL_TouchFingerEvent & current);
|
||||
|
||||
void handleUpdate();
|
||||
|
||||
bool isMouseButtonPressed(MouseButton button) const;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user