react-native-gesture-handler 2.18.1 → 2.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (133) hide show
  1. package/README.md +1 -0
  2. package/android/build.gradle +4 -17
  3. package/android/src/main/AndroidManifest.xml +1 -3
  4. package/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt +21 -21
  5. package/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt +2 -2
  6. package/android/src/main/java/com/swmansion/gesturehandler/core/HoverGestureHandler.kt +5 -0
  7. package/android/src/main/java/com/swmansion/gesturehandler/core/LongPressGestureHandler.kt +80 -4
  8. package/android/src/main/java/com/swmansion/gesturehandler/core/PinchGestureHandler.kt +2 -1
  9. package/android/src/main/java/com/swmansion/gesturehandler/core/ScaleGestureDetector.java +10 -0
  10. package/android/src/main/java/com/swmansion/gesturehandler/core/Vector.kt +2 -2
  11. package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt +3 -0
  12. package/apple/Handlers/RNFlingHandler.h +1 -0
  13. package/apple/Handlers/RNFlingHandler.m +153 -19
  14. package/apple/Handlers/RNHoverHandler.m +44 -2
  15. package/apple/Handlers/RNLongPressHandler.m +109 -20
  16. package/apple/Handlers/RNManualHandler.m +53 -29
  17. package/apple/Handlers/RNNativeViewHandler.mm +22 -15
  18. package/apple/RNGHUIKit.h +2 -0
  19. package/apple/RNGHVector.h +31 -0
  20. package/apple/RNGHVector.m +67 -0
  21. package/apple/RNGestureHandler.h +7 -0
  22. package/apple/{RNGestureHandler.m → RNGestureHandler.mm} +63 -1
  23. package/apple/RNGestureHandlerButtonComponentView.mm +6 -0
  24. package/apple/RNGestureHandlerDirection.h +25 -0
  25. package/lib/commonjs/PointerType.js +2 -1
  26. package/lib/commonjs/PointerType.js.map +1 -1
  27. package/lib/commonjs/components/Pressable/Pressable.js +67 -70
  28. package/lib/commonjs/components/Pressable/Pressable.js.map +1 -1
  29. package/lib/commonjs/components/Pressable/index.js +0 -8
  30. package/lib/commonjs/components/Pressable/index.js.map +1 -1
  31. package/lib/commonjs/components/ReanimatedSwipeable.js +60 -41
  32. package/lib/commonjs/components/ReanimatedSwipeable.js.map +1 -1
  33. package/lib/commonjs/handlers/LongPressGestureHandler.js +1 -1
  34. package/lib/commonjs/handlers/LongPressGestureHandler.js.map +1 -1
  35. package/lib/commonjs/handlers/gestures/GestureDetector/utils.js +1 -1
  36. package/lib/commonjs/handlers/gestures/GestureDetector/utils.js.map +1 -1
  37. package/lib/commonjs/handlers/gestures/longPressGesture.js +10 -0
  38. package/lib/commonjs/handlers/gestures/longPressGesture.js.map +1 -1
  39. package/lib/commonjs/mocks.js +16 -3
  40. package/lib/commonjs/mocks.js.map +1 -1
  41. package/lib/commonjs/utils.js +4 -0
  42. package/lib/commonjs/utils.js.map +1 -1
  43. package/lib/commonjs/web/constants.js +3 -3
  44. package/lib/commonjs/web/constants.js.map +1 -1
  45. package/lib/commonjs/web/handlers/GestureHandler.js +1 -0
  46. package/lib/commonjs/web/handlers/GestureHandler.js.map +1 -1
  47. package/lib/commonjs/web/handlers/LongPressGestureHandler.js +43 -9
  48. package/lib/commonjs/web/handlers/LongPressGestureHandler.js.map +1 -1
  49. package/lib/commonjs/web/handlers/NativeViewGestureHandler.js +14 -3
  50. package/lib/commonjs/web/handlers/NativeViewGestureHandler.js.map +1 -1
  51. package/lib/commonjs/web/handlers/PanGestureHandler.js +4 -0
  52. package/lib/commonjs/web/handlers/PanGestureHandler.js.map +1 -1
  53. package/lib/commonjs/web/interfaces.js.map +1 -1
  54. package/lib/commonjs/web/tools/GestureHandlerWebDelegate.js +55 -8
  55. package/lib/commonjs/web/tools/GestureHandlerWebDelegate.js.map +1 -1
  56. package/lib/commonjs/web/tools/KeyboardEventManager.js +110 -0
  57. package/lib/commonjs/web/tools/KeyboardEventManager.js.map +1 -0
  58. package/lib/commonjs/web/tools/Vector.js +4 -2
  59. package/lib/commonjs/web/tools/Vector.js.map +1 -1
  60. package/lib/commonjs/web/utils.js +14 -13
  61. package/lib/commonjs/web/utils.js.map +1 -1
  62. package/lib/module/PointerType.js +2 -1
  63. package/lib/module/PointerType.js.map +1 -1
  64. package/lib/module/components/Pressable/Pressable.js +66 -70
  65. package/lib/module/components/Pressable/Pressable.js.map +1 -1
  66. package/lib/module/components/Pressable/index.js +0 -1
  67. package/lib/module/components/Pressable/index.js.map +1 -1
  68. package/lib/module/components/ReanimatedSwipeable.js +58 -37
  69. package/lib/module/components/ReanimatedSwipeable.js.map +1 -1
  70. package/lib/module/handlers/LongPressGestureHandler.js +1 -1
  71. package/lib/module/handlers/LongPressGestureHandler.js.map +1 -1
  72. package/lib/module/handlers/gestures/GestureDetector/utils.js +2 -2
  73. package/lib/module/handlers/gestures/GestureDetector/utils.js.map +1 -1
  74. package/lib/module/handlers/gestures/longPressGesture.js +10 -0
  75. package/lib/module/handlers/gestures/longPressGesture.js.map +1 -1
  76. package/lib/module/mocks.js +13 -3
  77. package/lib/module/mocks.js.map +1 -1
  78. package/lib/module/utils.js +1 -0
  79. package/lib/module/utils.js.map +1 -1
  80. package/lib/module/web/constants.js +1 -1
  81. package/lib/module/web/constants.js.map +1 -1
  82. package/lib/module/web/handlers/GestureHandler.js +1 -0
  83. package/lib/module/web/handlers/GestureHandler.js.map +1 -1
  84. package/lib/module/web/handlers/LongPressGestureHandler.js +43 -9
  85. package/lib/module/web/handlers/LongPressGestureHandler.js.map +1 -1
  86. package/lib/module/web/handlers/NativeViewGestureHandler.js +14 -3
  87. package/lib/module/web/handlers/NativeViewGestureHandler.js.map +1 -1
  88. package/lib/module/web/handlers/PanGestureHandler.js +4 -0
  89. package/lib/module/web/handlers/PanGestureHandler.js.map +1 -1
  90. package/lib/module/web/interfaces.js.map +1 -1
  91. package/lib/module/web/tools/GestureHandlerWebDelegate.js +54 -8
  92. package/lib/module/web/tools/GestureHandlerWebDelegate.js.map +1 -1
  93. package/lib/module/web/tools/KeyboardEventManager.js +96 -0
  94. package/lib/module/web/tools/KeyboardEventManager.js.map +1 -0
  95. package/lib/module/web/tools/Vector.js +5 -3
  96. package/lib/module/web/tools/Vector.js.map +1 -1
  97. package/lib/module/web/utils.js +14 -13
  98. package/lib/module/web/utils.js.map +1 -1
  99. package/lib/typescript/PointerType.d.ts +2 -1
  100. package/lib/typescript/components/Pressable/index.d.ts +1 -1
  101. package/lib/typescript/handlers/LongPressGestureHandler.d.ts +5 -1
  102. package/lib/typescript/handlers/gestures/longPressGesture.d.ts +5 -0
  103. package/lib/typescript/mocks.d.ts +4 -3
  104. package/lib/typescript/utils.d.ts +1 -0
  105. package/lib/typescript/web/constants.d.ts +1 -1
  106. package/lib/typescript/web/handlers/GestureHandler.d.ts +1 -1
  107. package/lib/typescript/web/handlers/LongPressGestureHandler.d.ts +3 -0
  108. package/lib/typescript/web/handlers/NativeViewGestureHandler.d.ts +1 -0
  109. package/lib/typescript/web/interfaces.d.ts +1 -1
  110. package/lib/typescript/web/tools/GestureHandlerDelegate.d.ts +1 -0
  111. package/lib/typescript/web/tools/GestureHandlerWebDelegate.d.ts +6 -0
  112. package/lib/typescript/web/tools/KeyboardEventManager.d.ts +13 -0
  113. package/package.json +3 -3
  114. package/src/PointerType.ts +1 -0
  115. package/src/components/Pressable/Pressable.tsx +70 -50
  116. package/src/components/Pressable/index.ts +1 -1
  117. package/src/components/ReanimatedSwipeable.tsx +70 -47
  118. package/src/handlers/LongPressGestureHandler.ts +6 -0
  119. package/src/handlers/gestures/GestureDetector/utils.ts +2 -2
  120. package/src/handlers/gestures/longPressGesture.ts +9 -0
  121. package/src/{mocks.ts → mocks.tsx} +8 -3
  122. package/src/utils.ts +2 -0
  123. package/src/web/constants.ts +1 -1
  124. package/src/web/handlers/GestureHandler.ts +3 -1
  125. package/src/web/handlers/LongPressGestureHandler.ts +49 -10
  126. package/src/web/handlers/NativeViewGestureHandler.ts +14 -4
  127. package/src/web/handlers/PanGestureHandler.ts +4 -0
  128. package/src/web/interfaces.ts +1 -1
  129. package/src/web/tools/GestureHandlerDelegate.ts +1 -0
  130. package/src/web/tools/GestureHandlerWebDelegate.ts +67 -8
  131. package/src/web/tools/KeyboardEventManager.ts +91 -0
  132. package/src/web/tools/Vector.ts +4 -3
  133. package/src/web/utils.ts +15 -13
package/README.md CHANGED
@@ -37,6 +37,7 @@ You will need to have an Android or iOS device or emulator connected.
37
37
 
38
38
  | version | react-native version |
39
39
  | ------- | -------------------- |
40
+ | 2.18.0+ | 0.73.0+ |
40
41
  | 2.16.0+ | 0.68.0+ |
41
42
  | 2.14.0+ | 0.67.0+ |
42
43
  | 2.10.0+ | 0.64.0+ |
@@ -1,8 +1,5 @@
1
1
  import groovy.json.JsonSlurper
2
2
 
3
- import javax.inject.Inject
4
- import java.nio.file.Files
5
-
6
3
  buildscript {
7
4
  def kotlin_version = rootProject.ext.has('kotlinVersion') ? rootProject.ext.get('kotlinVersion') : project.properties['RNGH_kotlinVersion']
8
5
 
@@ -98,15 +95,11 @@ repositories {
98
95
 
99
96
  android {
100
97
  compileSdkVersion safeExtGet("compileSdkVersion", 33)
101
- def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
102
- if (agpVersion.tokenize('.')[0].toInteger() >= 7) {
103
- namespace "com.swmansion.gesturehandler"
104
- }
105
98
 
106
- if (agpVersion.tokenize('.')[0].toInteger() >= 8) {
107
- buildFeatures {
108
- buildConfig = true
109
- }
99
+ namespace "com.swmansion.gesturehandler"
100
+ buildFeatures {
101
+ buildConfig = true
102
+ prefab = true
110
103
  }
111
104
 
112
105
  // Used to override the NDK path/version on internal CI or by allowing
@@ -118,12 +111,6 @@ android {
118
111
  ndkVersion rootProject.ext.ndkVersion
119
112
  }
120
113
 
121
- if (REACT_NATIVE_MINOR_VERSION >= 71) {
122
- buildFeatures {
123
- prefab true
124
- }
125
- }
126
-
127
114
  defaultConfig {
128
115
  minSdkVersion safeExtGet('minSdkVersion', 21)
129
116
  targetSdkVersion safeExtGet('targetSdkVersion', 33)
@@ -1,3 +1 @@
1
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
- package="com.swmansion.gesturehandler">
3
- </manifest>
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" />
@@ -397,19 +397,19 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
397
397
  }
398
398
  }
399
399
 
400
- private fun dispatchTouchDownEvent(event: MotionEvent) {
400
+ private fun dispatchTouchDownEvent(event: MotionEvent, sourceEvent: MotionEvent) {
401
401
  changedTouchesPayload = null
402
402
  touchEventType = RNGestureHandlerTouchEvent.EVENT_TOUCH_DOWN
403
403
  val pointerId = event.getPointerId(event.actionIndex)
404
- val offsetX = event.rawX - event.x
405
- val offsetY = event.rawY - event.y
404
+ val offsetX = sourceEvent.rawX - sourceEvent.x
405
+ val offsetY = sourceEvent.rawY - sourceEvent.y
406
406
 
407
407
  trackedPointers[pointerId] = PointerData(
408
408
  pointerId,
409
409
  event.getX(event.actionIndex),
410
410
  event.getY(event.actionIndex),
411
- event.getX(event.actionIndex) + offsetX - windowOffset[0],
412
- event.getY(event.actionIndex) + offsetY - windowOffset[1],
411
+ sourceEvent.getX(event.actionIndex) + offsetX - windowOffset[0],
412
+ sourceEvent.getY(event.actionIndex) + offsetY - windowOffset[1],
413
413
  )
414
414
  trackedPointersCount++
415
415
  addChangedPointer(trackedPointers[pointerId]!!)
@@ -418,20 +418,20 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
418
418
  dispatchTouchEvent()
419
419
  }
420
420
 
421
- private fun dispatchTouchUpEvent(event: MotionEvent) {
421
+ private fun dispatchTouchUpEvent(event: MotionEvent, sourceEvent: MotionEvent) {
422
422
  extractAllPointersData()
423
423
  changedTouchesPayload = null
424
424
  touchEventType = RNGestureHandlerTouchEvent.EVENT_TOUCH_UP
425
425
  val pointerId = event.getPointerId(event.actionIndex)
426
- val offsetX = event.rawX - event.x
427
- val offsetY = event.rawY - event.y
426
+ val offsetX = sourceEvent.rawX - sourceEvent.x
427
+ val offsetY = sourceEvent.rawY - sourceEvent.y
428
428
 
429
429
  trackedPointers[pointerId] = PointerData(
430
430
  pointerId,
431
431
  event.getX(event.actionIndex),
432
432
  event.getY(event.actionIndex),
433
- event.getX(event.actionIndex) + offsetX - windowOffset[0],
434
- event.getY(event.actionIndex) + offsetY - windowOffset[1],
433
+ sourceEvent.getX(event.actionIndex) + offsetX - windowOffset[0],
434
+ sourceEvent.getY(event.actionIndex) + offsetY - windowOffset[1],
435
435
  )
436
436
  addChangedPointer(trackedPointers[pointerId]!!)
437
437
  trackedPointers[pointerId] = null
@@ -440,11 +440,11 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
440
440
  dispatchTouchEvent()
441
441
  }
442
442
 
443
- private fun dispatchTouchMoveEvent(event: MotionEvent) {
443
+ private fun dispatchTouchMoveEvent(event: MotionEvent, sourceEvent: MotionEvent) {
444
444
  changedTouchesPayload = null
445
445
  touchEventType = RNGestureHandlerTouchEvent.EVENT_TOUCH_MOVE
446
- val offsetX = event.rawX - event.x
447
- val offsetY = event.rawY - event.y
446
+ val offsetX = sourceEvent.rawX - sourceEvent.x
447
+ val offsetY = sourceEvent.rawY - sourceEvent.y
448
448
  var pointersAdded = 0
449
449
 
450
450
  for (i in 0 until event.pointerCount) {
@@ -454,8 +454,8 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
454
454
  if (pointer.x != event.getX(i) || pointer.y != event.getY(i)) {
455
455
  pointer.x = event.getX(i)
456
456
  pointer.y = event.getY(i)
457
- pointer.absoluteX = event.getX(i) + offsetX - windowOffset[0]
458
- pointer.absoluteY = event.getY(i) + offsetY - windowOffset[1]
457
+ pointer.absoluteX = sourceEvent.getX(i) + offsetX - windowOffset[0]
458
+ pointer.absoluteY = sourceEvent.getY(i) + offsetY - windowOffset[1]
459
459
 
460
460
  addChangedPointer(pointer)
461
461
  pointersAdded++
@@ -471,15 +471,15 @@ open class GestureHandler<ConcreteGestureHandlerT : GestureHandler<ConcreteGestu
471
471
  }
472
472
  }
473
473
 
474
- fun updatePointerData(event: MotionEvent) {
474
+ fun updatePointerData(event: MotionEvent, sourceEvent: MotionEvent) {
475
475
  if (event.actionMasked == MotionEvent.ACTION_DOWN || event.actionMasked == MotionEvent.ACTION_POINTER_DOWN) {
476
- dispatchTouchDownEvent(event)
477
- dispatchTouchMoveEvent(event)
476
+ dispatchTouchDownEvent(event, sourceEvent)
477
+ dispatchTouchMoveEvent(event, sourceEvent)
478
478
  } else if (event.actionMasked == MotionEvent.ACTION_UP || event.actionMasked == MotionEvent.ACTION_POINTER_UP) {
479
- dispatchTouchMoveEvent(event)
480
- dispatchTouchUpEvent(event)
479
+ dispatchTouchMoveEvent(event, sourceEvent)
480
+ dispatchTouchUpEvent(event, sourceEvent)
481
481
  } else if (event.actionMasked == MotionEvent.ACTION_MOVE) {
482
- dispatchTouchMoveEvent(event)
482
+ dispatchTouchMoveEvent(event, sourceEvent)
483
483
  }
484
484
  }
485
485
 
@@ -270,7 +270,7 @@ class GestureHandlerOrchestrator(
270
270
  // the first `onTouchesDown` event after the handler processes it and changes state
271
271
  // to `BEGAN`.
272
272
  if (handler.needsPointerData && handler.state != 0) {
273
- handler.updatePointerData(event)
273
+ handler.updatePointerData(event, sourceEvent)
274
274
  }
275
275
 
276
276
  if (!handler.isAwaiting || action != MotionEvent.ACTION_MOVE) {
@@ -292,7 +292,7 @@ class GestureHandlerOrchestrator(
292
292
  }
293
293
 
294
294
  if (handler.needsPointerData && isFirstEvent) {
295
- handler.updatePointerData(event)
295
+ handler.updatePointerData(event, sourceEvent)
296
296
  }
297
297
 
298
298
  // if event was of type UP or POINTER_UP we request handler to stop tracking now that
@@ -5,6 +5,7 @@ import android.os.Looper
5
5
  import android.view.MotionEvent
6
6
  import android.view.View
7
7
  import android.view.ViewGroup
8
+ import com.swmansion.gesturehandler.react.RNGestureHandlerRootHelper
8
9
  import com.swmansion.gesturehandler.react.RNViewConfigurationHelper
9
10
 
10
11
  class HoverGestureHandler : GestureHandler<HoverGestureHandler>() {
@@ -70,6 +71,10 @@ class HoverGestureHandler : GestureHandler<HoverGestureHandler>() {
70
71
  return true
71
72
  }
72
73
 
74
+ if (handler is RNGestureHandlerRootHelper.RootViewGestureHandler) {
75
+ return true
76
+ }
77
+
73
78
  return super.shouldRecognizeSimultaneously(handler)
74
79
  }
75
80
 
@@ -12,11 +12,13 @@ class LongPressGestureHandler(context: Context) : GestureHandler<LongPressGestur
12
12
  get() = (previousTime - startTime).toInt()
13
13
  private val defaultMaxDistSq: Float
14
14
  private var maxDistSq: Float
15
+ private var numberOfPointersRequired: Int
15
16
  private var startX = 0f
16
17
  private var startY = 0f
17
18
  private var startTime: Long = 0
18
19
  private var previousTime: Long = 0
19
20
  private var handler: Handler? = null
21
+ private var currentPointers = 0
20
22
 
21
23
  init {
22
24
  setShouldCancelWhenOutside(true)
@@ -24,6 +26,7 @@ class LongPressGestureHandler(context: Context) : GestureHandler<LongPressGestur
24
26
  val defaultMaxDist = DEFAULT_MAX_DIST_DP * context.resources.displayMetrics.density
25
27
  defaultMaxDistSq = defaultMaxDist * defaultMaxDist
26
28
  maxDistSq = defaultMaxDistSq
29
+ numberOfPointersRequired = 1
27
30
  }
28
31
 
29
32
  override fun resetConfig() {
@@ -37,6 +40,37 @@ class LongPressGestureHandler(context: Context) : GestureHandler<LongPressGestur
37
40
  return this
38
41
  }
39
42
 
43
+ fun setNumberOfPointers(numberOfPointers: Int): LongPressGestureHandler {
44
+ numberOfPointersRequired = numberOfPointers
45
+ return this
46
+ }
47
+
48
+ private fun getAverageCoords(ev: MotionEvent, excludePointer: Boolean = false): Pair<Float, Float> {
49
+ if (!excludePointer) {
50
+ val x = (0 until ev.pointerCount).map { ev.getX(it) }.average().toFloat()
51
+ val y = (0 until ev.pointerCount).map { ev.getY(it) }.average().toFloat()
52
+
53
+ return Pair(x, y)
54
+ }
55
+
56
+ var sumX = 0f
57
+ var sumY = 0f
58
+
59
+ for (i in 0 until ev.pointerCount) {
60
+ if (i == ev.actionIndex) {
61
+ continue
62
+ }
63
+
64
+ sumX += ev.getX(i)
65
+ sumY += ev.getY(i)
66
+ }
67
+
68
+ val x = sumX / (ev.pointerCount - 1)
69
+ val y = sumY / (ev.pointerCount - 1)
70
+
71
+ return Pair(x, y)
72
+ }
73
+
40
74
  override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
41
75
  if (!shouldActivateWithMouse(sourceEvent)) {
42
76
  return
@@ -46,8 +80,28 @@ class LongPressGestureHandler(context: Context) : GestureHandler<LongPressGestur
46
80
  previousTime = SystemClock.uptimeMillis()
47
81
  startTime = previousTime
48
82
  begin()
49
- startX = sourceEvent.rawX
50
- startY = sourceEvent.rawY
83
+
84
+ val (x, y) = getAverageCoords(sourceEvent)
85
+ startX = x
86
+ startY = y
87
+
88
+ currentPointers++
89
+ }
90
+
91
+ if (sourceEvent.actionMasked == MotionEvent.ACTION_POINTER_DOWN) {
92
+ currentPointers++
93
+
94
+ val (x, y) = getAverageCoords(sourceEvent)
95
+ startX = x
96
+ startY = y
97
+
98
+ if (currentPointers > numberOfPointersRequired) {
99
+ fail()
100
+ currentPointers = 0
101
+ }
102
+ }
103
+
104
+ if (state == STATE_BEGAN && currentPointers == numberOfPointersRequired && (sourceEvent.actionMasked == MotionEvent.ACTION_DOWN || sourceEvent.actionMasked == MotionEvent.ACTION_POINTER_DOWN)) {
51
105
  handler = Handler(Looper.getMainLooper())
52
106
  if (minDurationMs > 0) {
53
107
  handler!!.postDelayed({ activate() }, minDurationMs)
@@ -56,20 +110,37 @@ class LongPressGestureHandler(context: Context) : GestureHandler<LongPressGestur
56
110
  }
57
111
  }
58
112
  if (sourceEvent.actionMasked == MotionEvent.ACTION_UP || sourceEvent.actionMasked == MotionEvent.ACTION_BUTTON_RELEASE) {
113
+ currentPointers--
114
+
59
115
  handler?.let {
60
116
  it.removeCallbacksAndMessages(null)
61
117
  handler = null
62
118
  }
119
+
63
120
  if (state == STATE_ACTIVE) {
64
121
  end()
65
122
  } else {
66
123
  fail()
67
124
  }
125
+ } else if (sourceEvent.actionMasked == MotionEvent.ACTION_POINTER_UP) {
126
+ currentPointers--
127
+
128
+ if (currentPointers < numberOfPointersRequired && state != STATE_ACTIVE) {
129
+ fail()
130
+ currentPointers = 0
131
+ } else {
132
+ val (x, y) = getAverageCoords(sourceEvent, true)
133
+ startX = x
134
+ startY = y
135
+ }
68
136
  } else {
69
137
  // calculate distance from start
70
- val deltaX = sourceEvent.rawX - startX
71
- val deltaY = sourceEvent.rawY - startY
138
+ val (x, y) = getAverageCoords(sourceEvent)
139
+
140
+ val deltaX = x - startX
141
+ val deltaY = y - startY
72
142
  val distSq = deltaX * deltaX + deltaY * deltaY
143
+
73
144
  if (distSq > maxDistSq) {
74
145
  if (state == STATE_ACTIVE) {
75
146
  cancel()
@@ -97,6 +168,11 @@ class LongPressGestureHandler(context: Context) : GestureHandler<LongPressGestur
97
168
  super.dispatchHandlerUpdate(event)
98
169
  }
99
170
 
171
+ override fun onReset() {
172
+ super.onReset()
173
+ currentPointers = 0
174
+ }
175
+
100
176
  companion object {
101
177
  private const val DEFAULT_MIN_DURATION_MS: Long = 500
102
178
  private const val DEFAULT_MAX_DIST_DP = 10f
@@ -23,7 +23,8 @@ class PinchGestureHandler : GestureHandler<PinchGestureHandler>() {
23
23
  override fun onScale(detector: ScaleGestureDetector): Boolean {
24
24
  val prevScaleFactor: Double = scale
25
25
  scale *= detector.scaleFactor.toDouble()
26
- val delta = detector.timeDelta
26
+ val delta = detector.timeDeltaSeconds
27
+
27
28
  if (delta > 0) {
28
29
  velocity = (scale - prevScaleFactor) / delta
29
30
  }
@@ -547,6 +547,16 @@ public class ScaleGestureDetector {
547
547
  return mCurrTime - mPrevTime;
548
548
  }
549
549
 
550
+ /**
551
+ * Return the time difference in seconds between the previous
552
+ * accepted scaling event and the current scaling event.
553
+ *
554
+ * @return Time difference since the last scaling event in seconds.
555
+ */
556
+ public double getTimeDeltaSeconds() {
557
+ return (double)this.getTimeDelta() / 1000;
558
+ }
559
+
550
560
  /**
551
561
  * Return the event time of the current event being processed.
552
562
  *
@@ -13,7 +13,7 @@ class Vector(val x: Double, val y: Double) {
13
13
  val magnitude = hypot(x, y)
14
14
 
15
15
  init {
16
- val isMagnitudeSufficient = magnitude > MINIMAL_MAGNITUDE
16
+ val isMagnitudeSufficient = magnitude > MINIMAL_RECOGNIZABLE_MAGNITUDE
17
17
 
18
18
  unitX = if (isMagnitudeSufficient) x / magnitude else 0.0
19
19
  unitY = if (isMagnitudeSufficient) y / magnitude else 0.0
@@ -39,7 +39,7 @@ class Vector(val x: Double, val y: Double) {
39
39
  private val VECTOR_LEFT_DOWN: Vector = Vector(-1.0, 1.0)
40
40
 
41
41
  private val VECTOR_ZERO: Vector = Vector(0.0, 0.0)
42
- private const val MINIMAL_MAGNITUDE = 0.1
42
+ private const val MINIMAL_RECOGNIZABLE_MAGNITUDE = 0.1
43
43
 
44
44
  fun fromDirection(direction: Int): Vector =
45
45
  when (direction) {
@@ -154,6 +154,9 @@ class RNGestureHandlerModule(reactContext: ReactApplicationContext?) :
154
154
  if (config.hasKey(KEY_LONG_PRESS_MAX_DIST)) {
155
155
  handler.setMaxDist(PixelUtil.toPixelFromDIP(config.getDouble(KEY_LONG_PRESS_MAX_DIST)))
156
156
  }
157
+ if (config.hasKey(KEY_NUMBER_OF_POINTERS)) {
158
+ handler.setNumberOfPointers(config.getInt(KEY_NUMBER_OF_POINTERS))
159
+ }
157
160
  }
158
161
 
159
162
  override fun createEventBuilder(handler: LongPressGestureHandler) = LongPressGestureHandlerEventDataBuilder(handler)
@@ -1,3 +1,4 @@
1
+ #import "../RNGHVector.h"
1
2
  #import "RNGestureHandler.h"
2
3
 
3
4
  @interface RNFlingGestureHandler : RNGestureHandler
@@ -87,6 +87,154 @@
87
87
 
88
88
  @end
89
89
 
90
+ #else
91
+
92
+ @interface RNBetterSwipeGestureRecognizer : NSGestureRecognizer {
93
+ dispatch_block_t failFlingAction;
94
+ int maxDuration;
95
+ int minVelocity;
96
+ double defaultAlignmentCone;
97
+ double axialDeviationCosine;
98
+ double diagonalDeviationCosine;
99
+ }
100
+
101
+ @property (atomic, assign) RNGestureHandlerDirection direction;
102
+ @property (atomic, assign) int numberOfTouchesRequired;
103
+
104
+ - (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler;
105
+
106
+ @end
107
+
108
+ @implementation RNBetterSwipeGestureRecognizer {
109
+ __weak RNGestureHandler *_gestureHandler;
110
+
111
+ NSPoint startPosition;
112
+ double startTime;
113
+ }
114
+
115
+ - (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler
116
+ {
117
+ if ((self = [super initWithTarget:self action:@selector(handleGesture:)])) {
118
+ _gestureHandler = gestureHandler;
119
+
120
+ maxDuration = 1.0;
121
+ minVelocity = 700;
122
+
123
+ defaultAlignmentCone = 30;
124
+ axialDeviationCosine = [self coneToDeviation:defaultAlignmentCone];
125
+ diagonalDeviationCosine = [self coneToDeviation:(90 - defaultAlignmentCone)];
126
+ }
127
+ return self;
128
+ }
129
+
130
+ - (void)handleGesture:(NSPanGestureRecognizer *)gestureRecognizer
131
+ {
132
+ [_gestureHandler handleGesture:self];
133
+ }
134
+
135
+ - (void)mouseDown:(NSEvent *)event
136
+ {
137
+ [super mouseDown:event];
138
+
139
+ startPosition = [self locationInView:self.view];
140
+ startTime = CACurrentMediaTime();
141
+
142
+ self.state = NSGestureRecognizerStatePossible;
143
+
144
+ __weak typeof(self) weakSelf = self;
145
+
146
+ failFlingAction = dispatch_block_create(0, ^{
147
+ __strong typeof(self) strongSelf = weakSelf;
148
+
149
+ if (strongSelf) {
150
+ strongSelf.state = NSGestureRecognizerStateFailed;
151
+ }
152
+ });
153
+
154
+ dispatch_after(
155
+ dispatch_time(DISPATCH_TIME_NOW, (int64_t)(maxDuration * NSEC_PER_SEC)),
156
+ dispatch_get_main_queue(),
157
+ failFlingAction);
158
+ }
159
+
160
+ - (void)mouseDragged:(NSEvent *)event
161
+ {
162
+ [super mouseDragged:event];
163
+
164
+ NSPoint currentPosition = [self locationInView:self.view];
165
+ double currentTime = CACurrentMediaTime();
166
+
167
+ NSPoint distance;
168
+ distance.x = currentPosition.x - startPosition.x;
169
+ distance.y = startPosition.y - currentPosition.y;
170
+
171
+ double timeDelta = currentTime - startTime;
172
+
173
+ Vector *velocityVector = [Vector fromVelocityX:(distance.x / timeDelta) withVelocityY:(distance.y / timeDelta)];
174
+
175
+ [self tryActivate:velocityVector];
176
+ }
177
+
178
+ - (void)mouseUp:(NSEvent *)event
179
+ {
180
+ [super mouseUp:event];
181
+
182
+ dispatch_block_cancel(failFlingAction);
183
+
184
+ self.state =
185
+ self.state == NSGestureRecognizerStateChanged ? NSGestureRecognizerStateEnded : NSGestureRecognizerStateFailed;
186
+ }
187
+
188
+ - (void)tryActivate:(Vector *)velocityVector
189
+ {
190
+ bool isAligned = NO;
191
+
192
+ for (int i = 0; i < directionsSize; ++i) {
193
+ if ([self getAlignment:axialDirections[i]
194
+ withMinimalAlignmentCosine:axialDeviationCosine
195
+ withVelocityVector:velocityVector]) {
196
+ isAligned = YES;
197
+ break;
198
+ }
199
+ }
200
+
201
+ if (!isAligned) {
202
+ for (int i = 0; i < directionsSize; ++i) {
203
+ if ([self getAlignment:diagonalDirections[i]
204
+ withMinimalAlignmentCosine:diagonalDeviationCosine
205
+ withVelocityVector:velocityVector]) {
206
+ isAligned = YES;
207
+ break;
208
+ }
209
+ }
210
+ }
211
+
212
+ bool isFastEnough = velocityVector.magnitude >= minVelocity;
213
+
214
+ if (isAligned && isFastEnough) {
215
+ self.state = NSGestureRecognizerStateChanged;
216
+ }
217
+ }
218
+
219
+ - (BOOL)getAlignment:(RNGestureHandlerDirection)direction
220
+ withMinimalAlignmentCosine:(double)minimalAlignmentCosine
221
+ withVelocityVector:(Vector *)velocityVector
222
+ {
223
+ Vector *directionVector = [Vector fromDirection:direction];
224
+ return ((self.direction & direction) == direction) &&
225
+ [velocityVector isSimilar:directionVector withThreshold:minimalAlignmentCosine];
226
+ }
227
+
228
+ - (double)coneToDeviation:(double)degrees
229
+ {
230
+ double radians = (degrees * M_PI) / 180;
231
+ return cos(radians / 2);
232
+ }
233
+
234
+ @end
235
+
236
+ #endif
237
+
90
238
  @implementation RNFlingGestureHandler
91
239
 
92
240
  - (instancetype)initWithTag:(NSNumber *)tag
@@ -100,8 +248,8 @@
100
248
  - (void)resetConfig
101
249
  {
102
250
  [super resetConfig];
103
- UISwipeGestureRecognizer *recognizer = (UISwipeGestureRecognizer *)_recognizer;
104
- recognizer.direction = UISwipeGestureRecognizerDirectionRight;
251
+ RNBetterSwipeGestureRecognizer *recognizer = (RNBetterSwipeGestureRecognizer *)_recognizer;
252
+ recognizer.direction = RNGestureHandlerDirectionRight;
105
253
  #if !TARGET_OS_TV
106
254
  recognizer.numberOfTouchesRequired = 1;
107
255
  #endif
@@ -110,7 +258,7 @@
110
258
  - (void)configure:(NSDictionary *)config
111
259
  {
112
260
  [super configure:config];
113
- UISwipeGestureRecognizer *recognizer = (UISwipeGestureRecognizer *)_recognizer;
261
+ RNBetterSwipeGestureRecognizer *recognizer = (RNBetterSwipeGestureRecognizer *)_recognizer;
114
262
 
115
263
  id prop = config[@"direction"];
116
264
  if (prop != nil) {
@@ -125,6 +273,7 @@
125
273
  #endif
126
274
  }
127
275
 
276
+ #if !TARGET_OS_OSX
128
277
  - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
129
278
  {
130
279
  RNGestureHandlerState savedState = _lastState;
@@ -154,21 +303,6 @@
154
303
  withNumberOfTouches:recognizer.numberOfTouches
155
304
  withPointerType:_pointerType];
156
305
  }
157
- @end
158
-
159
- #else
160
-
161
- @implementation RNFlingGestureHandler
162
-
163
- - (instancetype)initWithTag:(NSNumber *)tag
164
- {
165
- RCTLogWarn(@"FlingGestureHandler is not supported on macOS");
166
- if ((self = [super initWithTag:tag])) {
167
- _recognizer = [NSGestureRecognizer alloc];
168
- }
169
- return self;
170
- }
306
+ #endif
171
307
 
172
308
  @end
173
-
174
- #endif
@@ -6,6 +6,7 @@
6
6
  //
7
7
 
8
8
  #import "RNHoverHandler.h"
9
+ #import <React/UIView+React.h>
9
10
 
10
11
  #if !TARGET_OS_OSX
11
12
 
@@ -163,17 +164,58 @@ API_AVAILABLE(ios(13.4))
163
164
 
164
165
  #else
165
166
 
166
- @implementation RNHoverGestureHandler
167
+ @implementation RNHoverGestureHandler {
168
+ NSTrackingArea *trackingArea;
169
+ RNGHUIView *_view;
170
+ }
167
171
 
168
172
  - (instancetype)initWithTag:(NSNumber *)tag
169
173
  {
170
- RCTLogWarn(@"HoverGestureHandler is not supported on macOS");
171
174
  if ((self = [super initWithTag:tag])) {
172
175
  _recognizer = [NSGestureRecognizer alloc];
173
176
  }
177
+
174
178
  return self;
175
179
  }
176
180
 
181
+ - (void)bindToView:(RNGHUIView *)view
182
+ {
183
+ _view = view;
184
+
185
+ NSTrackingAreaOptions options =
186
+ NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp | NSTrackingInVisibleRect;
187
+
188
+ trackingArea = [[NSTrackingArea alloc] initWithRect:_view.bounds options:options owner:self userInfo:nil];
189
+ [_view addTrackingArea:trackingArea];
190
+ }
191
+
192
+ - (void)unbindFromView
193
+ {
194
+ [_view removeTrackingArea:trackingArea];
195
+ _view = nil;
196
+ }
197
+
198
+ - (void)mouseEntered:(NSEvent *)event
199
+ {
200
+ [self sendEventsInState:RNGestureHandlerStateBegan
201
+ forViewWithTag:_view.reactTag
202
+ withExtraData:[RNGestureHandlerEventExtraData forPointerInside:YES withPointerType:_pointerType]];
203
+ [self sendEventsInState:RNGestureHandlerStateActive
204
+ forViewWithTag:_view.reactTag
205
+ withExtraData:[RNGestureHandlerEventExtraData forPointerInside:YES withPointerType:_pointerType]];
206
+ }
207
+
208
+ - (void)mouseExited:(NSEvent *)theEvent
209
+ {
210
+ [self sendEventsInState:RNGestureHandlerStateEnd
211
+ forViewWithTag:_view.reactTag
212
+ withExtraData:[RNGestureHandlerEventExtraData forPointerInside:YES withPointerType:_pointerType]];
213
+
214
+ [self sendEventsInState:RNGestureHandlerStateUndetermined
215
+ forViewWithTag:_view.reactTag
216
+ withExtraData:[RNGestureHandlerEventExtraData forPointerInside:YES withPointerType:_pointerType]];
217
+ }
218
+
177
219
  @end
178
220
 
179
221
  #endif