Browse Source

Merge pull request #2864 from Laserlicht/double_tap

support double tap
Nordsoft91 2 years ago
parent
commit
461aa97d7e

+ 1 - 1
client/eventsSDL/InputSourceMouse.cpp

@@ -48,7 +48,7 @@ void InputSourceMouse::handleEventMouseButtonDown(const SDL_MouseButtonEvent & b
 	{
 	{
 		case SDL_BUTTON_LEFT:
 		case SDL_BUTTON_LEFT:
 			if(button.clicks > 1)
 			if(button.clicks > 1)
-				GH.events().dispatchMouseDoubleClick(position);
+				GH.events().dispatchMouseDoubleClick(position, 0);
 			else
 			else
 				GH.events().dispatchMouseLeftButtonPressed(position, 0);
 				GH.events().dispatchMouseLeftButtonPressed(position, 0);
 			break;
 			break;

+ 13 - 3
client/eventsSDL/InputSourceTouch.cpp

@@ -33,7 +33,7 @@
 #include <SDL_timer.h>
 #include <SDL_timer.h>
 
 
 InputSourceTouch::InputSourceTouch()
 InputSourceTouch::InputSourceTouch()
-	: lastTapTimeTicks(0)
+	: lastTapTimeTicks(0), lastLeftClickTimeTicks(0)
 {
 {
 	params.useRelativeMode = settings["general"]["userRelativePointer"].Bool();
 	params.useRelativeMode = settings["general"]["userRelativePointer"].Bool();
 	params.relativeModeSpeedFactor = settings["general"]["relativePointerSpeedMultiplier"].Float();
 	params.relativeModeSpeedFactor = settings["general"]["relativePointerSpeedMultiplier"].Float();
@@ -181,8 +181,18 @@ 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), params.touchToleranceDistance);
-			GH.events().dispatchMouseLeftButtonReleased(convertTouchToMouse(tfinger), params.touchToleranceDistance);
+			if(tfinger.timestamp - lastLeftClickTimeTicks < params.doubleTouchTimeMilliseconds && (convertTouchToMouse(tfinger) - lastLeftClickPosition).length() < params.doubleTouchToleranceDistance)
+			{
+				GH.events().dispatchMouseDoubleClick(convertTouchToMouse(tfinger), params.touchToleranceDistance);
+				GH.events().dispatchMouseLeftButtonReleased(convertTouchToMouse(tfinger), params.touchToleranceDistance);
+			}
+			else
+			{
+				GH.events().dispatchMouseLeftButtonPressed(convertTouchToMouse(tfinger), params.touchToleranceDistance);
+				GH.events().dispatchMouseLeftButtonReleased(convertTouchToMouse(tfinger), params.touchToleranceDistance);
+				lastLeftClickTimeTicks = tfinger.timestamp;
+				lastLeftClickPosition = convertTouchToMouse(tfinger);
+			}
 			state = TouchState::IDLE;
 			state = TouchState::IDLE;
 			break;
 			break;
 		}
 		}

+ 9 - 0
client/eventsSDL/InputSourceTouch.h

@@ -72,6 +72,12 @@ struct TouchInputParameters
 	/// tap for period longer than specified here will be qualified as "long tap", triggering corresponding gesture
 	/// tap for period longer than specified here will be qualified as "long tap", triggering corresponding gesture
 	uint32_t longTouchTimeMilliseconds = 750;
 	uint32_t longTouchTimeMilliseconds = 750;
 
 
+	/// time span in where the second tap has to happen for qualifing as "double click"
+	uint32_t doubleTouchTimeMilliseconds = 500;
+
+	/// max distance in where the second tap has to happen for qualifing as "double click"
+	uint32_t doubleTouchToleranceDistance = 50;
+
 	/// moving finger for distance larger than specified will be qualified as panning gesture instead of long press
 	/// moving finger for distance larger than specified will be qualified as panning gesture instead of long press
 	uint32_t panningSensitivityThreshold = 10;
 	uint32_t panningSensitivityThreshold = 10;
 
 
@@ -94,6 +100,9 @@ class InputSourceTouch
 	uint32_t lastTapTimeTicks;
 	uint32_t lastTapTimeTicks;
 	Point lastTapPosition;
 	Point lastTapPosition;
 
 
+	uint32_t lastLeftClickTimeTicks;
+	Point lastLeftClickPosition;
+
 	Point convertTouchToMouse(const SDL_TouchFingerEvent & current);
 	Point convertTouchToMouse(const SDL_TouchFingerEvent & current);
 	Point convertTouchToMouse(float x, float y);
 	Point convertTouchToMouse(float x, float y);
 
 

+ 34 - 18
client/gui/EventDispatcher.cpp

@@ -116,25 +116,9 @@ void EventDispatcher::dispatchShortcutReleased(const std::vector<EShortcut> & sh
 	}
 	}
 }
 }
 
 
-void EventDispatcher::dispatchMouseDoubleClick(const Point & position)
+void EventDispatcher::dispatchMouseDoubleClick(const Point & position, int tolerance)
 {
 {
-	bool doubleClicked = false;
-	auto hlp = doubleClickInterested;
-
-	for(auto & i : hlp)
-	{
-		if(!vstd::contains(doubleClickInterested, i))
-			continue;
-
-		if(i->receiveEvent(position, AEventsReceiver::DOUBLECLICK))
-		{
-			i->clickDouble(position);
-			doubleClicked = true;
-		}
-	}
-
-	if(!doubleClicked)
-		handleLeftButtonClick(position, 0, true);
+	handleDoubleButtonClick(position, tolerance);
 }
 }
 
 
 void EventDispatcher::dispatchMouseLeftButtonPressed(const Point & position, int tolerance)
 void EventDispatcher::dispatchMouseLeftButtonPressed(const Point & position, int tolerance)
@@ -246,6 +230,38 @@ void EventDispatcher::handleLeftButtonClick(const Point & position, int toleranc
 	}
 	}
 }
 }
 
 
+void EventDispatcher::handleDoubleButtonClick(const Point & position, int tolerance)
+{
+	// WARNING: this approach is NOT SAFE
+	// 1) We allow (un)registering elements when list itself is being processed/iterated
+	// 2) To avoid iterator invalidation we create a copy of this list for processing
+	// HOWEVER it is completely possible (as in, actually happen, no just theory) to:
+	// 1) element gets unregistered and deleted from lclickable
+	// 2) element is completely deleted, as in - destructor called, memory freed
+	// 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(doubleClickInterested, position, AEventsReceiver::DOUBLECLICK, tolerance);
+	bool doubleClicked = false;
+	auto hlp = doubleClickInterested;
+
+	for(auto & i : hlp)
+	{
+		if(!vstd::contains(doubleClickInterested, i))
+			continue;
+
+		if(i->receiveEvent(position, AEventsReceiver::DOUBLECLICK) || i == nearestElement)
+		{
+			i->clickDouble(position);
+			doubleClicked = true;
+		}
+	}
+
+	if(!doubleClicked)
+		handleLeftButtonClick(position, tolerance, true);
+}
+
 void EventDispatcher::dispatchMouseScrolled(const Point & distance, const Point & position)
 void EventDispatcher::dispatchMouseScrolled(const Point & distance, const Point & position)
 {
 {
 	EventReceiversList hlp = wheelInterested;
 	EventReceiversList hlp = wheelInterested;

+ 2 - 1
client/gui/EventDispatcher.h

@@ -36,6 +36,7 @@ class EventDispatcher
 	EventReceiversList panningInterested;
 	EventReceiversList panningInterested;
 
 
 	void handleLeftButtonClick(const Point & position, int tolerance, bool isPressed);
 	void handleLeftButtonClick(const Point & position, int tolerance, bool isPressed);
+	void handleDoubleButtonClick(const Point & position, int tolerance);
 	AEventsReceiver * findElementInToleranceRange(const EventReceiversList & list, const Point & position, int eventToTest, int tolerance);
 	AEventsReceiver * findElementInToleranceRange(const EventReceiversList & list, const Point & position, int eventToTest, int tolerance);
 
 
 	template<typename Functor>
 	template<typename Functor>
@@ -59,7 +60,7 @@ public:
 	void dispatchMouseLeftButtonPressed(const Point & position, int tolerance);
 	void dispatchMouseLeftButtonPressed(const Point & position, int tolerance);
 	void dispatchMouseLeftButtonReleased(const Point & position, int tolerance);
 	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, int tolerance);
 	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);