mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +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)
|
||||
GH.events().dispatchMouseDoubleClick(position);
|
||||
else
|
||||
GH.events().dispatchMouseLeftButtonPressed(position);
|
||||
GH.events().dispatchMouseLeftButtonPressed(position, 0);
|
||||
break;
|
||||
case SDL_BUTTON_RIGHT:
|
||||
GH.events().dispatchShowPopup(position);
|
||||
GH.events().dispatchShowPopup(position, 0);
|
||||
break;
|
||||
case SDL_BUTTON_MIDDLE:
|
||||
middleClickPosition = position;
|
||||
@ -74,7 +74,7 @@ void InputSourceMouse::handleEventMouseButtonUp(const SDL_MouseButtonEvent & but
|
||||
switch(button.button)
|
||||
{
|
||||
case SDL_BUTTON_LEFT:
|
||||
GH.events().dispatchMouseLeftButtonReleased(position);
|
||||
GH.events().dispatchMouseLeftButtonReleased(position, 0);
|
||||
break;
|
||||
case SDL_BUTTON_RIGHT:
|
||||
GH.events().dispatchClosePopup(position);
|
||||
|
@ -39,6 +39,7 @@ InputSourceTouch::InputSourceTouch()
|
||||
params.relativeModeSpeedFactor = settings["general"]["relativePointerSpeedMultiplier"].Float();
|
||||
params.longTouchTimeMilliseconds = settings["general"]["longTouchTimeMilliseconds"].Float();
|
||||
params.hapticFeedbackEnabled = settings["general"]["hapticFeedback"].Bool();
|
||||
params.touchToleranceDistance = settings["input"]["touchToleranceDistance"].Bool();
|
||||
|
||||
if (params.useRelativeMode)
|
||||
state = TouchState::RELATIVE_MODE;
|
||||
@ -121,9 +122,9 @@ void InputSourceTouch::handleEventFingerDown(const SDL_TouchFingerEvent & tfinge
|
||||
if(tfinger.x > 0.5)
|
||||
{
|
||||
if (tfinger.y < 0.5)
|
||||
GH.events().dispatchShowPopup(GH.getCursorPosition());
|
||||
GH.events().dispatchShowPopup(GH.getCursorPosition(), params.touchToleranceDistance);
|
||||
else
|
||||
GH.events().dispatchMouseLeftButtonPressed(GH.getCursorPosition());
|
||||
GH.events().dispatchMouseLeftButtonPressed(GH.getCursorPosition(), params.touchToleranceDistance);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -168,7 +169,7 @@ void InputSourceTouch::handleEventFingerUp(const SDL_TouchFingerEvent & tfinger)
|
||||
if (tfinger.y < 0.5)
|
||||
GH.events().dispatchClosePopup(GH.getCursorPosition());
|
||||
else
|
||||
GH.events().dispatchMouseLeftButtonReleased(GH.getCursorPosition());
|
||||
GH.events().dispatchMouseLeftButtonReleased(GH.getCursorPosition(), params.touchToleranceDistance);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -180,8 +181,8 @@ void InputSourceTouch::handleEventFingerUp(const SDL_TouchFingerEvent & tfinger)
|
||||
case TouchState::TAP_DOWN_SHORT:
|
||||
{
|
||||
GH.input().setCursorPosition(convertTouchToMouse(tfinger));
|
||||
GH.events().dispatchMouseLeftButtonPressed(convertTouchToMouse(tfinger));
|
||||
GH.events().dispatchMouseLeftButtonReleased(convertTouchToMouse(tfinger));
|
||||
GH.events().dispatchMouseLeftButtonPressed(convertTouchToMouse(tfinger), params.touchToleranceDistance);
|
||||
GH.events().dispatchMouseLeftButtonReleased(convertTouchToMouse(tfinger), params.touchToleranceDistance);
|
||||
state = TouchState::IDLE;
|
||||
break;
|
||||
}
|
||||
@ -230,7 +231,7 @@ void InputSourceTouch::handleUpdate()
|
||||
uint32_t currentTime = SDL_GetTicks();
|
||||
if (currentTime > lastTapTimeTicks + params.longTouchTimeMilliseconds)
|
||||
{
|
||||
GH.events().dispatchShowPopup(GH.getCursorPosition());
|
||||
GH.events().dispatchShowPopup(GH.getCursorPosition(), params.touchToleranceDistance);
|
||||
|
||||
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
|
||||
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 hapticFeedbackEnabled = false;
|
||||
|
@ -263,6 +263,11 @@ bool CIntObject::receiveEvent(const Point & position, int eventType) const
|
||||
return pos.isInside(position);
|
||||
}
|
||||
|
||||
const Rect & CIntObject::getPosition() const
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
void CIntObject::onScreenResize()
|
||||
{
|
||||
center(pos, true);
|
||||
|
@ -109,6 +109,8 @@ public:
|
||||
/// by default, usedEvents inside UI elements are always handled
|
||||
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 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
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include "MouseButton.h"
|
||||
#include "WindowHandler.h"
|
||||
|
||||
#include "../../lib/Point.h"
|
||||
#include "../../lib/Rect.h"
|
||||
|
||||
template<typename Functor>
|
||||
void EventDispatcher::processLists(ui16 activityFlag, const Functor & cb)
|
||||
@ -134,28 +134,64 @@ void EventDispatcher::dispatchMouseDoubleClick(const Point & position)
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
for(auto & i : hlp)
|
||||
{
|
||||
if(!vstd::contains(rclickable, i))
|
||||
continue;
|
||||
|
||||
if( !i->receiveEvent(position, AEventsReceiver::LCLICK))
|
||||
if( !i->receiveEvent(position, AEventsReceiver::SHOW_POPUP) && i != nearestElement)
|
||||
continue;
|
||||
|
||||
i->showPopupWindow(position);
|
||||
@ -170,7 +206,7 @@ void EventDispatcher::dispatchClosePopup(const Point & position)
|
||||
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
|
||||
// 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(!)
|
||||
// 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
|
||||
AEventsReceiver * nearestElement = findElementInToleranceRange(lclickable, position, AEventsReceiver::LCLICK, tolerance);
|
||||
auto hlp = lclickable;
|
||||
|
||||
for(auto & i : hlp)
|
||||
{
|
||||
if(!vstd::contains(lclickable, i))
|
||||
continue;
|
||||
|
||||
if( i->receiveEvent(position, AEventsReceiver::LCLICK))
|
||||
if( i->receiveEvent(position, AEventsReceiver::LCLICK) || i == nearestElement)
|
||||
{
|
||||
if(isPressed)
|
||||
i->clickPressed(position);
|
||||
|
@ -35,8 +35,8 @@ class EventDispatcher
|
||||
EventReceiversList textInterested;
|
||||
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>
|
||||
void processLists(ui16 activityFlag, const Functor & cb);
|
||||
@ -56,15 +56,15 @@ public:
|
||||
void dispatchShortcutReleased(const std::vector<EShortcut> & shortcuts);
|
||||
|
||||
/// Mouse events
|
||||
void dispatchMouseLeftButtonPressed(const Point & position);
|
||||
void dispatchMouseLeftButtonReleased(const Point & position);
|
||||
void dispatchMouseLeftButtonPressed(const Point & position, int tolerance);
|
||||
void dispatchMouseLeftButtonReleased(const Point & position, int tolerance);
|
||||
void dispatchMouseScrolled(const Point & distance, const Point & position);
|
||||
void dispatchMouseDoubleClick(const Point & position);
|
||||
void dispatchMouseMoved(const Point & distance, const Point & position);
|
||||
|
||||
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 dispatchGesturePanningStarted(const Point & initialPosition);
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
class Point;
|
||||
class Rect;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class EventDispatcher;
|
||||
@ -39,6 +40,8 @@ protected:
|
||||
/// 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 const Rect & getPosition() const= 0;
|
||||
|
||||
public:
|
||||
virtual void clickPressed(const Point & cursorPosition) {}
|
||||
virtual void clickReleased(const Point & cursorPosition) {}
|
||||
|
@ -128,6 +128,13 @@ void CButton::setState(ButtonState newState)
|
||||
{
|
||||
if (state == newState)
|
||||
return;
|
||||
|
||||
if (newState == BLOCKED)
|
||||
removeUsedEvents(LCLICK | SHOW_POPUP | HOVER | KEYBOARD);
|
||||
else
|
||||
addUsedEvents(LCLICK | SHOW_POPUP | HOVER | KEYBOARD);
|
||||
|
||||
|
||||
state = newState;
|
||||
update();
|
||||
}
|
||||
|
@ -214,11 +214,15 @@
|
||||
"type" : "object",
|
||||
"additionalProperties" : false,
|
||||
"default" : {},
|
||||
"required" : [ "radialWheelGarrisonSwipe" ],
|
||||
"required" : [ "radialWheelGarrisonSwipe", "touchToleranceDistance" ],
|
||||
"properties" : {
|
||||
"radialWheelGarrisonSwipe" : {
|
||||
"type" : "boolean",
|
||||
"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
|
||||
|
@ -142,6 +142,9 @@ public:
|
||||
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
|
||||
DLL_LINKAGE bool intersectionTest(const Rect & other) const;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user