This repository has been archived by the owner on Jul 24, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
DragGestureDetectorCopy.kt
135 lines (125 loc) · 5.58 KB
/
DragGestureDetectorCopy.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.compose.material3
import androidx.compose.foundation.gestures.awaitTouchSlopOrCancellation
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.pointer.AwaitPointerEventScope
import androidx.compose.ui.input.pointer.PointerEvent
import androidx.compose.ui.input.pointer.PointerEventPass
import androidx.compose.ui.input.pointer.PointerId
import androidx.compose.ui.input.pointer.PointerInputChange
import androidx.compose.ui.input.pointer.PointerType
import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
import androidx.compose.ui.platform.ViewConfiguration
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastFirstOrNull
import kotlin.math.abs
import kotlin.math.sign
// Copy-paste version of DragGestureDetector.kt. Please don't change this file without changing
// DragGestureDetector.kt
internal suspend fun AwaitPointerEventScope.awaitHorizontalPointerSlopOrCancellation(
pointerId: PointerId,
pointerType: PointerType,
onPointerSlopReached: (change: PointerInputChange, overSlop: Float) -> Unit
) = awaitPointerSlopOrCancellation(
pointerId = pointerId,
pointerType = pointerType,
onPointerSlopReached = onPointerSlopReached,
getDragDirectionValue = { it.x }
)
/**
* Waits for drag motion along one axis based on [getDragDirectionValue] to pass pointer slop,
* using [pointerId] as the pointer to examine. If [pointerId] is raised, another pointer
* from those that are down will be chosen to lead the gesture, and if none are down,
* `null` is returned. If [pointerId] is not down when [awaitPointerSlopOrCancellation] is called,
* then `null` is returned.
*
* When pointer slop is detected, [onPointerSlopReached] is called with the change and the distance
* beyond the pointer slop. [getDragDirectionValue] should return the position change in the
* direction of the drag axis. If [onPointerSlopReached] does not consume the position change,
* pointer slop will not have been considered detected and the detection will continue or,
* if it is consumed, the [PointerInputChange] that was consumed will be returned.
*
* This works with [awaitTouchSlopOrCancellation] for the other axis to ensure that only horizontal
* or vertical dragging is done, but not both.
*
* @return The [PointerInputChange] of the event that was consumed in [onPointerSlopReached] or
* `null` if all pointers are raised or the position change was consumed by another gesture
* detector.
*/
private suspend inline fun AwaitPointerEventScope.awaitPointerSlopOrCancellation(
pointerId: PointerId,
pointerType: PointerType,
onPointerSlopReached: (PointerInputChange, Float) -> Unit,
getDragDirectionValue: (Offset) -> Float
): PointerInputChange? {
if (currentEvent.isPointerUp(pointerId)) {
return null // The pointer has already been lifted, so the gesture is canceled
}
val touchSlop = viewConfiguration.pointerSlop(pointerType)
var pointer: PointerId = pointerId
var totalPositionChange = 0f
while (true) {
val event = awaitPointerEvent()
val dragEvent = event.changes.fastFirstOrNull { it.id == pointer }!!
if (dragEvent.isConsumed) {
return null
} else if (dragEvent.changedToUpIgnoreConsumed()) {
val otherDown = event.changes.fastFirstOrNull { it.pressed }
if (otherDown == null) {
// This is the last "up"
return null
} else {
pointer = otherDown.id
}
} else {
val currentPosition = dragEvent.position
val previousPosition = dragEvent.previousPosition
val positionChange = getDragDirectionValue(currentPosition) -
getDragDirectionValue(previousPosition)
totalPositionChange += positionChange
val inDirection = abs(totalPositionChange)
if (inDirection < touchSlop) {
// verify that nothing else consumed the drag event
awaitPointerEvent(PointerEventPass.Final)
if (dragEvent.isConsumed) {
return null
}
} else {
onPointerSlopReached(
dragEvent,
totalPositionChange - (sign(totalPositionChange) * touchSlop)
)
if (dragEvent.isConsumed) {
return dragEvent
} else {
totalPositionChange = 0f
}
}
}
}
}
private fun PointerEvent.isPointerUp(pointerId: PointerId): Boolean =
changes.fastFirstOrNull { it.id == pointerId }?.pressed != true
private val mouseSlop = 0.125.dp
private val defaultTouchSlop = 18.dp // The default touch slop on Android devices
private val mouseToTouchSlopRatio = mouseSlop / defaultTouchSlop
internal fun ViewConfiguration.pointerSlop(pointerType: PointerType): Float {
return when (pointerType) {
PointerType.Mouse -> touchSlop * mouseToTouchSlopRatio
else -> touchSlop
}
}