mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-12 02:28:11 +02:00
Merge pull request #2769 from IvanSavenko/touch_tolerance
(1.3.2) Implemented tolerance for touch events
This commit is contained in:
commit
fccd564d8d
@ -50,10 +50,10 @@ void InputSourceMouse::handleEventMouseButtonDown(const SDL_MouseButtonEvent & b
|
|||||||
if(button.clicks > 1)
|
if(button.clicks > 1)
|
||||||
GH.events().dispatchMouseDoubleClick(position);
|
GH.events().dispatchMouseDoubleClick(position);
|
||||||
else
|
else
|
||||||
GH.events().dispatchMouseLeftButtonPressed(position);
|
GH.events().dispatchMouseLeftButtonPressed(position, 0);
|
||||||
break;
|
break;
|
||||||
case SDL_BUTTON_RIGHT:
|
case SDL_BUTTON_RIGHT:
|
||||||
GH.events().dispatchShowPopup(position);
|
GH.events().dispatchShowPopup(position, 0);
|
||||||
break;
|
break;
|
||||||
case SDL_BUTTON_MIDDLE:
|
case SDL_BUTTON_MIDDLE:
|
||||||
middleClickPosition = position;
|
middleClickPosition = position;
|
||||||
@ -74,7 +74,7 @@ void InputSourceMouse::handleEventMouseButtonUp(const SDL_MouseButtonEvent & but
|
|||||||
switch(button.button)
|
switch(button.button)
|
||||||
{
|
{
|
||||||
case SDL_BUTTON_LEFT:
|
case SDL_BUTTON_LEFT:
|
||||||
GH.events().dispatchMouseLeftButtonReleased(position);
|
GH.events().dispatchMouseLeftButtonReleased(position, 0);
|
||||||
break;
|
break;
|
||||||
case SDL_BUTTON_RIGHT:
|
case SDL_BUTTON_RIGHT:
|
||||||
GH.events().dispatchClosePopup(position);
|
GH.events().dispatchClosePopup(position);
|
||||||
|
@ -39,6 +39,7 @@ InputSourceTouch::InputSourceTouch()
|
|||||||
params.relativeModeSpeedFactor = settings["general"]["relativePointerSpeedMultiplier"].Float();
|
params.relativeModeSpeedFactor = settings["general"]["relativePointerSpeedMultiplier"].Float();
|
||||||
params.longTouchTimeMilliseconds = settings["general"]["longTouchTimeMilliseconds"].Float();
|
params.longTouchTimeMilliseconds = settings["general"]["longTouchTimeMilliseconds"].Float();
|
||||||
params.hapticFeedbackEnabled = settings["general"]["hapticFeedback"].Bool();
|
params.hapticFeedbackEnabled = settings["general"]["hapticFeedback"].Bool();
|
||||||
|
params.touchToleranceDistance = settings["input"]["touchToleranceDistance"].Bool();
|
||||||
|
|
||||||
if (params.useRelativeMode)
|
if (params.useRelativeMode)
|
||||||
state = TouchState::RELATIVE_MODE;
|
state = TouchState::RELATIVE_MODE;
|
||||||
@ -121,9 +122,9 @@ void InputSourceTouch::handleEventFingerDown(const SDL_TouchFingerEvent & tfinge
|
|||||||
if(tfinger.x > 0.5)
|
if(tfinger.x > 0.5)
|
||||||
{
|
{
|
||||||
if (tfinger.y < 0.5)
|
if (tfinger.y < 0.5)
|
||||||
GH.events().dispatchShowPopup(GH.getCursorPosition());
|
GH.events().dispatchShowPopup(GH.getCursorPosition(), params.touchToleranceDistance);
|
||||||
else
|
else
|
||||||
GH.events().dispatchMouseLeftButtonPressed(GH.getCursorPosition());
|
GH.events().dispatchMouseLeftButtonPressed(GH.getCursorPosition(), params.touchToleranceDistance);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -168,7 +169,7 @@ void InputSourceTouch::handleEventFingerUp(const SDL_TouchFingerEvent & tfinger)
|
|||||||
if (tfinger.y < 0.5)
|
if (tfinger.y < 0.5)
|
||||||
GH.events().dispatchClosePopup(GH.getCursorPosition());
|
GH.events().dispatchClosePopup(GH.getCursorPosition());
|
||||||
else
|
else
|
||||||
GH.events().dispatchMouseLeftButtonReleased(GH.getCursorPosition());
|
GH.events().dispatchMouseLeftButtonReleased(GH.getCursorPosition(), params.touchToleranceDistance);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -180,8 +181,8 @@ void InputSourceTouch::handleEventFingerUp(const SDL_TouchFingerEvent & tfinger)
|
|||||||
case TouchState::TAP_DOWN_SHORT:
|
case TouchState::TAP_DOWN_SHORT:
|
||||||
{
|
{
|
||||||
GH.input().setCursorPosition(convertTouchToMouse(tfinger));
|
GH.input().setCursorPosition(convertTouchToMouse(tfinger));
|
||||||
GH.events().dispatchMouseLeftButtonPressed(convertTouchToMouse(tfinger));
|
GH.events().dispatchMouseLeftButtonPressed(convertTouchToMouse(tfinger), params.touchToleranceDistance);
|
||||||
GH.events().dispatchMouseLeftButtonReleased(convertTouchToMouse(tfinger));
|
GH.events().dispatchMouseLeftButtonReleased(convertTouchToMouse(tfinger), params.touchToleranceDistance);
|
||||||
state = TouchState::IDLE;
|
state = TouchState::IDLE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -230,7 +231,7 @@ void InputSourceTouch::handleUpdate()
|
|||||||
uint32_t currentTime = SDL_GetTicks();
|
uint32_t currentTime = SDL_GetTicks();
|
||||||
if (currentTime > lastTapTimeTicks + params.longTouchTimeMilliseconds)
|
if (currentTime > lastTapTimeTicks + params.longTouchTimeMilliseconds)
|
||||||
{
|
{
|
||||||
GH.events().dispatchShowPopup(GH.getCursorPosition());
|
GH.events().dispatchShowPopup(GH.getCursorPosition(), params.touchToleranceDistance);
|
||||||
|
|
||||||
if (GH.windows().isTopWindowPopup())
|
if (GH.windows().isTopWindowPopup())
|
||||||
{
|
{
|
||||||
|
@ -78,6 +78,9 @@ struct TouchInputParameters
|
|||||||
/// gesture will be qualified as pinch if distance between fingers is at least specified here
|
/// gesture will be qualified as pinch if distance between fingers is at least specified here
|
||||||
uint32_t pinchSensitivityThreshold = 10;
|
uint32_t pinchSensitivityThreshold = 10;
|
||||||
|
|
||||||
|
/// touch event will trigger clicking of elements up to X pixels away from actual touch position
|
||||||
|
uint32_t touchToleranceDistance = 20;
|
||||||
|
|
||||||
bool useRelativeMode = false;
|
bool useRelativeMode = false;
|
||||||
|
|
||||||
bool hapticFeedbackEnabled = false;
|
bool hapticFeedbackEnabled = false;
|
||||||
|
@ -263,6 +263,11 @@ bool CIntObject::receiveEvent(const Point & position, int eventType) const
|
|||||||
return pos.isInside(position);
|
return pos.isInside(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Rect & CIntObject::getPosition() const
|
||||||
|
{
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
void CIntObject::onScreenResize()
|
void CIntObject::onScreenResize()
|
||||||
{
|
{
|
||||||
center(pos, true);
|
center(pos, true);
|
||||||
|
@ -109,6 +109,8 @@ public:
|
|||||||
/// by default, usedEvents inside UI elements are always handled
|
/// by default, usedEvents inside UI elements are always handled
|
||||||
bool receiveEvent(const Point & position, int eventType) const override;
|
bool receiveEvent(const Point & position, int eventType) const override;
|
||||||
|
|
||||||
|
const Rect & getPosition() const override;
|
||||||
|
|
||||||
const Rect & center(const Rect &r, bool propagate = true); //sets pos so that r will be in the center of screen, assigns sizes of r to pos, returns new position
|
const Rect & center(const Rect &r, bool propagate = true); //sets pos so that r will be in the center of screen, assigns sizes of r to pos, returns new position
|
||||||
const Rect & center(const Point &p, bool propagate = true); //moves object so that point p will be in its center
|
const Rect & center(const Point &p, bool propagate = true); //moves object so that point p will be in its center
|
||||||
const Rect & center(bool propagate = true); //centers when pos.w and pos.h are set, returns new position
|
const Rect & center(bool propagate = true); //centers when pos.w and pos.h are set, returns new position
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
#include "MouseButton.h"
|
#include "MouseButton.h"
|
||||||
#include "WindowHandler.h"
|
#include "WindowHandler.h"
|
||||||
|
|
||||||
#include "../../lib/Point.h"
|
#include "../../lib/Rect.h"
|
||||||
|
|
||||||
template<typename Functor>
|
template<typename Functor>
|
||||||
void EventDispatcher::processLists(ui16 activityFlag, const Functor & cb)
|
void EventDispatcher::processLists(ui16 activityFlag, const Functor & cb)
|
||||||
@ -134,28 +134,64 @@ void EventDispatcher::dispatchMouseDoubleClick(const Point & position)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!doubleClicked)
|
if(!doubleClicked)
|
||||||
handleLeftButtonClick(position, true);
|
handleLeftButtonClick(position, 0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventDispatcher::dispatchMouseLeftButtonPressed(const Point & position)
|
void EventDispatcher::dispatchMouseLeftButtonPressed(const Point & position, int tolerance)
|
||||||
{
|
{
|
||||||
handleLeftButtonClick(position, true);
|
handleLeftButtonClick(position, tolerance, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventDispatcher::dispatchMouseLeftButtonReleased(const Point & position)
|
void EventDispatcher::dispatchMouseLeftButtonReleased(const Point & position, int tolerance)
|
||||||
{
|
{
|
||||||
handleLeftButtonClick(position, false);
|
handleLeftButtonClick(position, tolerance, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventDispatcher::dispatchShowPopup(const Point & position)
|
AEventsReceiver * EventDispatcher::findElementInToleranceRange(const EventReceiversList & list, const Point & position, int eventToTest, int tolerance)
|
||||||
{
|
{
|
||||||
|
AEventsReceiver * bestElement = nullptr;
|
||||||
|
int bestDistance = std::numeric_limits<int>::max();
|
||||||
|
|
||||||
|
for(auto & i : list)
|
||||||
|
{
|
||||||
|
// if there is element that can actually receive event then tolerance clicking is disabled
|
||||||
|
if( i->receiveEvent(position, eventToTest))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (i->getPosition().distanceTo(position) > bestDistance)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Point center = i->getPosition().center();
|
||||||
|
Point distance = center - position;
|
||||||
|
|
||||||
|
if (distance.lengthSquared() == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Point moveDelta = distance * tolerance / distance.length();
|
||||||
|
Point testPosition = position + moveDelta;
|
||||||
|
|
||||||
|
if( !i->receiveEvent(testPosition, eventToTest))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bestElement = i;
|
||||||
|
bestDistance = i->getPosition().distanceTo(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventDispatcher::dispatchShowPopup(const Point & position, int tolerance)
|
||||||
|
{
|
||||||
|
AEventsReceiver * nearestElement = findElementInToleranceRange(rclickable, position, AEventsReceiver::LCLICK, tolerance);
|
||||||
|
|
||||||
auto hlp = rclickable;
|
auto hlp = rclickable;
|
||||||
|
|
||||||
for(auto & i : hlp)
|
for(auto & i : hlp)
|
||||||
{
|
{
|
||||||
if(!vstd::contains(rclickable, i))
|
if(!vstd::contains(rclickable, i))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if( !i->receiveEvent(position, AEventsReceiver::LCLICK))
|
if( !i->receiveEvent(position, AEventsReceiver::SHOW_POPUP) && i != nearestElement)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
i->showPopupWindow(position);
|
i->showPopupWindow(position);
|
||||||
@ -170,7 +206,7 @@ void EventDispatcher::dispatchClosePopup(const Point & position)
|
|||||||
assert(!GH.windows().isTopWindowPopup());
|
assert(!GH.windows().isTopWindowPopup());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventDispatcher::handleLeftButtonClick(const Point & position, bool isPressed)
|
void EventDispatcher::handleLeftButtonClick(const Point & position, int tolerance, bool isPressed)
|
||||||
{
|
{
|
||||||
// WARNING: this approach is NOT SAFE
|
// WARNING: this approach is NOT SAFE
|
||||||
// 1) We allow (un)registering elements when list itself is being processed/iterated
|
// 1) We allow (un)registering elements when list itself is being processed/iterated
|
||||||
@ -181,13 +217,15 @@ void EventDispatcher::handleLeftButtonClick(const Point & position, bool isPress
|
|||||||
// 3) new element is created *with exactly same address(!)
|
// 3) new element is created *with exactly same address(!)
|
||||||
// 4) new element is registered and code will incorrectly assume that this element is still registered
|
// 4) new element is registered and code will incorrectly assume that this element is still registered
|
||||||
// POSSIBLE SOLUTION: make EventReceivers inherit from create_shared_from this and store weak_ptr's in lists
|
// POSSIBLE SOLUTION: make EventReceivers inherit from create_shared_from this and store weak_ptr's in lists
|
||||||
|
AEventsReceiver * nearestElement = findElementInToleranceRange(lclickable, position, AEventsReceiver::LCLICK, tolerance);
|
||||||
auto hlp = lclickable;
|
auto hlp = lclickable;
|
||||||
|
|
||||||
for(auto & i : hlp)
|
for(auto & i : hlp)
|
||||||
{
|
{
|
||||||
if(!vstd::contains(lclickable, i))
|
if(!vstd::contains(lclickable, i))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if( i->receiveEvent(position, AEventsReceiver::LCLICK))
|
if( i->receiveEvent(position, AEventsReceiver::LCLICK) || i == nearestElement)
|
||||||
{
|
{
|
||||||
if(isPressed)
|
if(isPressed)
|
||||||
i->clickPressed(position);
|
i->clickPressed(position);
|
||||||
|
@ -35,8 +35,8 @@ class EventDispatcher
|
|||||||
EventReceiversList textInterested;
|
EventReceiversList textInterested;
|
||||||
EventReceiversList panningInterested;
|
EventReceiversList panningInterested;
|
||||||
|
|
||||||
void handleLeftButtonClick(const Point & position, bool isPressed);
|
void handleLeftButtonClick(const Point & position, int tolerance, bool isPressed);
|
||||||
|
AEventsReceiver * findElementInToleranceRange(const EventReceiversList & list, const Point & position, int eventToTest, int tolerance);
|
||||||
|
|
||||||
template<typename Functor>
|
template<typename Functor>
|
||||||
void processLists(ui16 activityFlag, const Functor & cb);
|
void processLists(ui16 activityFlag, const Functor & cb);
|
||||||
@ -56,15 +56,15 @@ public:
|
|||||||
void dispatchShortcutReleased(const std::vector<EShortcut> & shortcuts);
|
void dispatchShortcutReleased(const std::vector<EShortcut> & shortcuts);
|
||||||
|
|
||||||
/// Mouse events
|
/// Mouse events
|
||||||
void dispatchMouseLeftButtonPressed(const Point & position);
|
void dispatchMouseLeftButtonPressed(const Point & position, int tolerance);
|
||||||
void dispatchMouseLeftButtonReleased(const Point & position);
|
void dispatchMouseLeftButtonReleased(const Point & position, int tolerance);
|
||||||
void dispatchMouseScrolled(const Point & distance, const Point & position);
|
void dispatchMouseScrolled(const Point & distance, const Point & position);
|
||||||
void dispatchMouseDoubleClick(const Point & position);
|
void dispatchMouseDoubleClick(const Point & position);
|
||||||
void dispatchMouseMoved(const Point & distance, const Point & position);
|
void dispatchMouseMoved(const Point & distance, const Point & position);
|
||||||
|
|
||||||
void dispatchMouseDragged(const Point & currentPosition, const Point & lastUpdateDistance);
|
void dispatchMouseDragged(const Point & currentPosition, const Point & lastUpdateDistance);
|
||||||
|
|
||||||
void dispatchShowPopup(const Point & position);
|
void dispatchShowPopup(const Point & position, int tolerance);
|
||||||
void dispatchClosePopup(const Point & position);
|
void dispatchClosePopup(const Point & position);
|
||||||
|
|
||||||
void dispatchGesturePanningStarted(const Point & initialPosition);
|
void dispatchGesturePanningStarted(const Point & initialPosition);
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
class Point;
|
class Point;
|
||||||
|
class Rect;
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
|
||||||
class EventDispatcher;
|
class EventDispatcher;
|
||||||
@ -39,6 +40,8 @@ protected:
|
|||||||
/// If true, event of selected type in selected position will be processed by this element
|
/// If true, event of selected type in selected position will be processed by this element
|
||||||
virtual bool receiveEvent(const Point & position, int eventType) const= 0;
|
virtual bool receiveEvent(const Point & position, int eventType) const= 0;
|
||||||
|
|
||||||
|
virtual const Rect & getPosition() const= 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual void clickPressed(const Point & cursorPosition) {}
|
virtual void clickPressed(const Point & cursorPosition) {}
|
||||||
virtual void clickReleased(const Point & cursorPosition) {}
|
virtual void clickReleased(const Point & cursorPosition) {}
|
||||||
|
@ -128,6 +128,13 @@ void CButton::setState(ButtonState newState)
|
|||||||
{
|
{
|
||||||
if (state == newState)
|
if (state == newState)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (newState == BLOCKED)
|
||||||
|
removeUsedEvents(LCLICK | SHOW_POPUP | HOVER | KEYBOARD);
|
||||||
|
else
|
||||||
|
addUsedEvents(LCLICK | SHOW_POPUP | HOVER | KEYBOARD);
|
||||||
|
|
||||||
|
|
||||||
state = newState;
|
state = newState;
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
@ -214,11 +214,15 @@
|
|||||||
"type" : "object",
|
"type" : "object",
|
||||||
"additionalProperties" : false,
|
"additionalProperties" : false,
|
||||||
"default" : {},
|
"default" : {},
|
||||||
"required" : [ "radialWheelGarrisonSwipe" ],
|
"required" : [ "radialWheelGarrisonSwipe", "touchToleranceDistance" ],
|
||||||
"properties" : {
|
"properties" : {
|
||||||
"radialWheelGarrisonSwipe" : {
|
"radialWheelGarrisonSwipe" : {
|
||||||
"type" : "boolean",
|
"type" : "boolean",
|
||||||
"default" : true
|
"default" : true
|
||||||
|
},
|
||||||
|
"touchToleranceDistance" : {
|
||||||
|
"type" : "number",
|
||||||
|
"default" : 20
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -136,4 +136,12 @@ Rect Rect::intersect(const Rect & other) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Rect::distanceTo(const Point & target) const
|
||||||
|
{
|
||||||
|
int distanceX = std::max({left() - target.x, 0, target.x - right()});
|
||||||
|
int distanceY = std::max({top() - target.y, 0, target.y - bottom()});
|
||||||
|
|
||||||
|
return Point(distanceX, distanceY).length();
|
||||||
|
}
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
@ -142,6 +142,9 @@ public:
|
|||||||
return x == other.x && y == other.y && w == other.w && h == other.h;
|
return x == other.x && y == other.y && w == other.w && h == other.h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// returns distance from this rect to point, or 0 if inside
|
||||||
|
DLL_LINKAGE int distanceTo(const Point & target) const;
|
||||||
|
|
||||||
/// returns true if this rect intersects with another rect
|
/// returns true if this rect intersects with another rect
|
||||||
DLL_LINKAGE bool intersectionTest(const Rect & other) const;
|
DLL_LINKAGE bool intersectionTest(const Rect & other) const;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user