diff --git a/CustomInputEvents/InputEventMultiScreenLongPress.gd b/CustomInputEvents/InputEventMultiScreenLongPress.gd new file mode 100644 index 0000000..9a0444b --- /dev/null +++ b/CustomInputEvents/InputEventMultiScreenLongPress.gd @@ -0,0 +1,15 @@ +class_name InputEventMultiScreenLongPress +extends InputEventAction + +var position : Vector2 +var fingers : int +var raw_gesture : RawGesture + +func _init(_raw_gesture : RawGesture = null) -> void: + raw_gesture = _raw_gesture + if raw_gesture: + fingers = raw_gesture.size() + position = raw_gesture.centroid("presses", "position") + +func as_text() -> String: + return "position=" + str(position) + "|fingers=" + str(fingers) diff --git a/CustomInputEvents/InputEventSingleScreenLongPress.gd b/CustomInputEvents/InputEventSingleScreenLongPress.gd new file mode 100644 index 0000000..1e87253 --- /dev/null +++ b/CustomInputEvents/InputEventSingleScreenLongPress.gd @@ -0,0 +1,14 @@ +class_name InputEventSingleScreenLongPress +extends InputEventAction + +var position : Vector2 +var raw_gesture : RawGesture + +func _init(_raw_gesture : RawGesture = null) -> void: + raw_gesture = _raw_gesture + if raw_gesture: + position = raw_gesture.presses[0].position + + +func as_text() -> String: + return "position=" + str(position) diff --git a/InputManager.gd b/InputManager.gd index 7e7a356..635d023 100644 --- a/InputManager.gd +++ b/InputManager.gd @@ -20,6 +20,9 @@ const MULTI_FINGER_RELEASE_THRESHOLD : float = 0.1 const TAP_TIME_LIMIT : float = 0.2 const TAP_DISTANCE_LIMIT : float = 25.0 +const LONG_PRESS_TIME_THRESHOLD : float = 0.75 +const LONG_PRESS_DISTANCE_LIMIT : float = 25.0 + const SWIPE_TIME_LIMIT : float = 0.5 const SWIPE_DISTANCE_THRESHOLD : float = 200.0 @@ -52,9 +55,11 @@ signal single_tap signal single_touch signal single_drag signal single_swipe +signal single_long_press signal multi_drag signal multi_tap signal multi_swipe +signal multi_long_press signal pinch signal twist signal raw_gesture @@ -77,6 +82,7 @@ var _mouse_event : int = Gesture.NONE var _drag_startup_timer : Timer = Timer.new() +var _long_press_timer : Timer = Timer.new() var _single_touch_cancelled : bool = false var _single_drag_enabled : bool = false @@ -86,7 +92,8 @@ var _single_drag_enabled : bool = false ############# func _ready() -> void: - _add_timer(_drag_startup_timer, "_on_drag_startup_timeout") + _add_timer(_drag_startup_timer, "_on_drag_startup_timer_timeout") + _add_timer(_long_press_timer, "_on_long_press_timer_timeout") if DEFAULT_BINDIGS: _set_default_action("multi_swipe_up" , _native_key_event(KEY_I)) @@ -130,11 +137,13 @@ func _handle_mouse_motion(event : InputEventMouseMotion) -> void: if _mouse_event == Gesture.SINGLE_DRAG: _emit("drag", _native_drag_event(0, event.position, event.relative, event.speed)) elif _mouse_event == Gesture.MULTI_DRAG: - var multi_drag_event = InputEventMultiScreenDrag.new() - multi_drag_event.position = event.position - multi_drag_event.relative = event.relative - multi_drag_event.fingers = 2 - _emit("multi_drag", multi_drag_event) + var offset = Vector2(5,5) + var e0 = _native_drag_event(0, event.position-offset, event.relative, event.speed) + raw_gesture._update_screen_drag(e0) + _emit("multi_drag", InputEventMultiScreenDrag.new(raw_gesture,e0)) + var e1 = _native_drag_event(1, event.position+offset, event.relative, event.speed) + raw_gesture._update_screen_drag(e1) + _emit("multi_drag", InputEventMultiScreenDrag.new(raw_gesture,e1)) elif _mouse_event == Gesture.TWIST: var rel1 = event.position - _mouse_event_press_position var rel2 = rel1 + event.relative @@ -155,6 +164,7 @@ func _handle_screen_touch(event : InputEventScreenTouch) -> void: var index : int = event.index if event.pressed: if raw_gesture.size() == 1: # First and only touch + _long_press_timer.start(LONG_PRESS_TIME_THRESHOLD) _single_touch_cancelled = false _emit("single_touch", InputEventSingleScreenTouch.new(raw_gesture)) elif !_single_touch_cancelled : @@ -297,11 +307,25 @@ func _identify_gesture(_raw_gesture : RawGesture) -> int: else: # sector == 1 or sector == 3: return Gesture.TWIST -func _on_drag_startup_timeout() -> void: +func _on_drag_startup_timer_timeout() -> void: _single_drag_enabled = raw_gesture.drags.size() == 1 +func _on_long_press_timer_timeout() -> void: + var ends_centroid : Vector2 = Util.centroid(raw_gesture.get_ends().values()) + var starts_centroid : Vector2 = raw_gesture.centroid("presses", "position") + var distance : float = (ends_centroid - starts_centroid).length() + + if raw_gesture.releases.empty() and distance <= LONG_PRESS_DISTANCE_LIMIT and\ + raw_gesture.is_consistent(LONG_PRESS_DISTANCE_LIMIT, FINGER_SIZE*raw_gesture.size()): + if _single_touch_cancelled: + _emit("multi_long_press", InputEventMultiScreenLongPress.new(raw_gesture)) + else: + _emit("single_long_press", InputEventSingleScreenLongPress.new(raw_gesture)) + + func _end_gesture() -> void: _single_drag_enabled = false + _long_press_timer.stop() raw_gesture = RawGesture.new() # create a native touch event diff --git a/README.md b/README.md index 38787e5..9059573 100644 --- a/README.md +++ b/README.md @@ -25,18 +25,20 @@ This asset was ported to be added to Godot and is now a milestone for version 4. ### Supported gestures -| Gesture name | Signal | Custom input event / Signal arg | Description | -|-----------------------|--------------|---------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------| -| Single finger tap | single_tap | [InputEventSingleScreenTap](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventSingleScreenTap) | Fast press and release with a single finger | -| Single finger touch | single_touch | [InputEventSingleScreenTouch](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventSingleScreenTouch) | Touch with a single finger | -| Single finger drag | single_drag | [InputEventSingleScreenDrag](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventSingleScreenDrag) | Drag with a single finger | -| Single finger swipe | single_swipe | [InputEventSingleScreenSwipe](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventSingleScreenSwipe) | Fast drag and release with a single finger | -| Multiple finger tap | multi_tap | [InputEventMultiScreenTap](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventMultiScreenTap) | Fast press and release with multiple fingers | -| Multiple finger drag | multi_drag | [InputEventMultiScreenDrag](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventMultiScreenDrag) | Drag with multiple fingers (same direction) | -| Multiple finger swipe | multi_swipe | [InputEventMultiScreenTap](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventMultiScreenTap) | Fast drag and release with multiple fingers | -| Pinch | pinch | [InputEventScreenPinch](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventScreenPinch) | Drag with multiple fingers (inward/outward) | -| Twist | twist | [InputEventScreenTwist](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventScreenTwist) | Drag with multiple fingers (rotate) | -| Raw gesture | raw_gesture | [RawGesture](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/RawGesture) | Raw gesture state +| Gesture name | Signal | Custom input event / Signal arg | Description | +|----------------------------|-------------------|---------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------| +| Single finger touch | single_touch | [InputEventSingleScreenTouch](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventSingleScreenTouch) | Touch with a single finger | +| Single finger tap | single_tap | [InputEventSingleScreenTap](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventSingleScreenTap) | Fast press and release with a single finger | +| Single finger long press | single_long_press | [InputEventSingleScreenLongPress](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventSingleScreenLongPress) | Press and hold with a single finger | +| Single finger drag | single_drag | [InputEventSingleScreenDrag](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventSingleScreenDrag) | Drag with a single finger | +| Single finger swipe | single_swipe | [InputEventSingleScreenSwipe](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventSingleScreenSwipe) | Fast drag and release with a single finger | +| Multiple finger tap | multi_tap | [InputEventMultiScreenTap](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventMultiScreenTap) | Fast press and release with multiple fingers | +| Multiple finger long press | multi_long_press | [InputEventMultiScreenLongPress](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventMultiScreenLongPress) | Press and hold with multiple fingers | +| Multiple finger drag | multi_drag | [InputEventMultiScreenDrag](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventMultiScreenDrag) | Drag with multiple fingers (same direction) | +| Multiple finger swipe | multi_swipe | [InputEventMultiScreenTap](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventMultiScreenTap) | Fast drag and release with multiple fingers | +| Pinch | pinch | [InputEventScreenPinch](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventScreenPinch) | Drag with multiple fingers (inward/outward) | +| Twist | twist | [InputEventScreenTwist](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/InputEventScreenTwist) | Drag with multiple fingers (rotate) | +| Raw gesture | raw_gesture | [RawGesture](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/wiki/RawGesture) | Raw gesture state When one of these gestures is detected a Custom Input Event corresponding to the detected gesture will be created and [fed up](https://docs.godotengine.org/en/stable/classes/class_input.html#class-input-method-parse-input-event) to the Godot built in Input Event system so it triggers functions like [`_input(InputEvent event)`](https://docs.godotengine.org/en/stable/classes/class_node.html#class-node-method-input). @@ -85,17 +87,19 @@ that will trigger each of the gestures that can be emulated. These are located in the first lines of [InputManager.gd](https://github.com/Federico-Ciuffardi/Godot-Touch-Input-Manager/blob/master/InputManager.gd), to change them modify the values on the script. -| Name | Default value | Description | -|--------------------------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| DEFAULT_BINDIGS | true | Enable or disable default events for [gesture emulation](#gesture-emulation) | -| DEBUG | false | Enable or disable debug information | -| DRAG_STARTUP_TIME | 0.2 | Time from first native drag event to first [single finger drag](#gestures-supported) custom event | -| FINGER_SIZE | 100.0 | The distance between the fingers must be less than `fingers*FINGER_SIZE` for the [multiple finger tap](#supported-gestures) and [multiple finger swipe](#supported-gestures) gestures to be recognized. Setting it to `INF` removes this restriction. | -| MULTI_FINGER_RELEASE_THRESHOLD | 0.1 | All fingers must be released within `MULTI_FINGER_REALEASE_THRESHOLD` seconds before the gesture ends for the [multiple finger tap](#gestures-supported) and [multiple finger swipe](#gestures-supported) gestures to be recognized | -| TAP_TIME_LIMIT | 0.2 | The time between the first press and the last release must be less than `TAP_TIME_LIMIT` for the [single finger tap](#supported-gestures) and [multiple finger tap](#supported-gestures) gestures to be recognized | -| TAP_DISTANCE_LIMIT | 25.0 | The centroid of the finger presses must differ less than `TAP_DISTANCE_LIMIT` from the centroid of the finger releases for the [single finger tap](#supported-gestures) and [multiple finger tap](#supported-gestures) gestures to be recognized. | -| SWIPE_TIME_LIMIT | 0.5 | The time between the first press and the last release must be less than `SWIPE_TIME_LIMIT` for the [single finger swipe](#supported-gestures) and [multiple finger swipe](#supported-gestures) gestures to be recognized. | -| SWIPE_DISTANCE_THRESHOLD | 200.0 | The centroid of the finger presses must differ by more than `SWIPE_DISTANCE_THRESHOLD` from the centroid of the finger releases for the [single finger swipe](#supported-gestures) and [multiple finger swipe](#supported-gestures) gestures to be recognized. | +| Name | Default value | Description | +|--------------------------------|---------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| DEFAULT_BINDIGS | true | Enable or disable default events for [gesture emulation](#gesture-emulation) | +| DEBUG | false | Enable or disable debug information | +| DRAG_STARTUP_TIME | 0.2 | Seconds from the first native drag event to the first [single finger drag](#gestures-supported) custom event | +| FINGER_SIZE | 100.0 | The distance between the fingers must be less than `fingers*FINGER_SIZE` pixels for the [multiple finger tap](#supported-gestures) and [multiple finger swipe](#supported-gestures) gestures to be recognized. Setting it to `INF` removes this restriction. | +| MULTI_FINGER_RELEASE_THRESHOLD | 0.1 | All fingers must be released within `MULTI_FINGER_REALEASE_THRESHOLD` seconds before the gesture ends for the [multiple finger tap](#gestures-supported) and [multiple finger swipe](#gestures-supported) gestures to be recognized | +| TAP_TIME_LIMIT | 0.2 | The time between the first press and the last release must be less than `TAP_TIME_LIMIT` seconds for the [single finger tap](#supported-gestures) and [multiple finger tap](#supported-gestures) gestures to be recognized | +| TAP_DISTANCE_LIMIT | 25.0 | The centroid of the finger presses must differ less than `TAP_DISTANCE_LIMIT` pixels from the centroid of the finger releases for the [single finger tap](#supported-gestures) and [multiple finger tap](#supported-gestures) gestures to be recognized. | +| SWIPE_TIME_LIMIT | 0.5 | The time between the first press and the last release must be less than `SWIPE_TIME_LIMIT` seconds for the [single finger swipe](#supported-gestures) and [multiple finger swipe](#supported-gestures) gestures to be recognized. | +| SWIPE_DISTANCE_THRESHOLD | 200.0 | The centroid of the finger presses must differ by more than `SWIPE_DISTANCE_THRESHOLD` pixels from the centroid of the finger releases for the [single finger swipe](#supported-gestures) and [multiple finger swipe](#supported-gestures) gestures to be recognized. | +| LONG_PRESS_TIME_THRESHOLD | 0.75 | The fingers must press for `LONG_PRESS_TIME_THRESHOLD` seconds for [single-finger long press](#gestures-supported) and [multi-finger long press](#gestures-supported) gestures to be recognized. | +| LONG_PRESS_DISTANCE_LIMIT | 25.0 | The centroid of the finger presses must differ less than `LONG_PRESS_DISTANCE_LIMIT` pixels from the centroid of the fingers last positions for the [single finger long press](#supported-gestures) and [multiple finger long press](#supported-gestures) gestures to be recognized. | ## Versioning diff --git a/RawGesture.gd b/RawGesture.gd index 9020eb8..9e092fc 100644 --- a/RawGesture.gd +++ b/RawGesture.gd @@ -55,19 +55,37 @@ func centroid(events_name : String , property_name : String): arr = Util.map_callv(arr , "get", [property_name]) return Util.centroid(arr) +func get_ends() -> Dictionary: + var ends : Dictionary = {} + + for i in presses: + ends[i] = presses[i].position + + for i in drags: + ends[i] = drags[i].position + + for i in releases: + ends[i] = releases[i].position + + return ends + # Check for gesture consistency func is_consistent(diff_limit : float, length_limit : float = -1) -> bool: if length_limit == -1: length_limit = length_limit - var valid : bool - var presses_centroid : Vector2 = centroid("presses", "position") - var releases_centroid : Vector2 = centroid("releases", "position") - for i in releases.keys(): - var press_relative_position : Vector2 = presses[i].position - presses_centroid - var release_relative_position : Vector2 = releases[i].position - releases_centroid + + var ends : Dictionary = get_ends() + + var ends_centroid : Vector2 = Util.centroid(ends.values()) + var starts_centroid : Vector2 = centroid("presses", "position") + + var valid : bool = true + for i in ends: + var start_relative_position : Vector2 = presses[i].position - starts_centroid + var end_relative_position : Vector2 = ends[i] - ends_centroid - valid = press_relative_position.length() < length_limit and \ - release_relative_position.length() < length_limit and \ - (release_relative_position - press_relative_position).length() < diff_limit + valid = start_relative_position.length() < length_limit and \ + end_relative_position.length() < length_limit and \ + (end_relative_position - start_relative_position).length() < diff_limit if !valid: break