Add new gesture detector method for looping over gestures
Fixes: 251260206
Relnote: "A new method, awaitEachGesture(), for gesture detectors
was added. It operates similar to forEachGesture(), but the loop
over gestures operates entirely within the AwaitPointerEventScope
so events can't be lost between iterations.
forEachGesture() has been deprecated in favor of awaitEachGesture()
because it allows events to be lost between gestures."
Test: new tests
Change-Id: Iffc3fb8cf53d0e5eb9b529c023b3e2d29003e86f
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index 4fc8b33..d7dd537 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -203,7 +203,8 @@
}
public final class ForEachGestureKt {
- method public static suspend Object? forEachGesture(androidx.compose.ui.input.pointer.PointerInputScope, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static suspend Object? awaitEachGesture(androidx.compose.ui.input.pointer.PointerInputScope, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.AwaitPointerEventScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @Deprecated public static suspend Object? forEachGesture(androidx.compose.ui.input.pointer.PointerInputScope, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
}
public final class GestureCancellationException extends java.util.concurrent.CancellationException {
diff --git a/compose/foundation/foundation/api/public_plus_experimental_current.txt b/compose/foundation/foundation/api/public_plus_experimental_current.txt
index b514e7d..d23f77a 100644
--- a/compose/foundation/foundation/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation/api/public_plus_experimental_current.txt
@@ -254,7 +254,8 @@
}
public final class ForEachGestureKt {
- method public static suspend Object? forEachGesture(androidx.compose.ui.input.pointer.PointerInputScope, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static suspend Object? awaitEachGesture(androidx.compose.ui.input.pointer.PointerInputScope, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.AwaitPointerEventScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @Deprecated public static suspend Object? forEachGesture(androidx.compose.ui.input.pointer.PointerInputScope, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
}
public final class GestureCancellationException extends java.util.concurrent.CancellationException {
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index 4fc8b33..d7dd537 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -203,7 +203,8 @@
}
public final class ForEachGestureKt {
- method public static suspend Object? forEachGesture(androidx.compose.ui.input.pointer.PointerInputScope, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public static suspend Object? awaitEachGesture(androidx.compose.ui.input.pointer.PointerInputScope, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.AwaitPointerEventScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method @Deprecated public static suspend Object? forEachGesture(androidx.compose.ui.input.pointer.PointerInputScope, kotlin.jvm.functions.Function2<? super androidx.compose.ui.input.pointer.PointerInputScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
}
public final class GestureCancellationException extends java.util.concurrent.CancellationException {
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/SuspendingGesturesDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/SuspendingGesturesDemo.kt
index 786b6ed..183a7d9 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/SuspendingGesturesDemo.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/SuspendingGesturesDemo.kt
@@ -21,11 +21,11 @@
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.gestures.detectDragGestures
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.detectHorizontalDragGestures
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.gestures.detectTransformGestures
import androidx.compose.foundation.gestures.detectVerticalDragGestures
-import androidx.compose.foundation.gestures.forEachGesture
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -461,19 +461,17 @@
var pointerType by remember { mutableStateOf<PointerType?>(null) }
Box(
Modifier.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- val pointer = awaitPointerEvent().changes.first()
- pointerType = pointer.type
- do {
- val event = awaitPointerEvent()
- } while (event.changes.first().pressed)
- pointerType = null
- }
+ awaitEachGesture {
+ val pointer = awaitPointerEvent().changes.first()
+ pointerType = pointer.type
+ do {
+ val event = awaitPointerEvent()
+ } while (event.changes.first().pressed)
+ pointerType = null
}
}
) {
Text("Touch or click the area to see what type of input it is.")
Text("PointerType: ${pointerType ?: ""}", Modifier.align(Alignment.BottomStart))
}
-}
\ No newline at end of file
+}
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/DragGestureDetectorSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/DragGestureDetectorSamples.kt
index 4ea4c6a..ed0b6f4 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/DragGestureDetectorSamples.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/DragGestureDetectorSamples.kt
@@ -27,10 +27,10 @@
import androidx.compose.foundation.gestures.awaitVerticalTouchSlopOrCancellation
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.detectHorizontalDragGestures
import androidx.compose.foundation.gestures.detectVerticalDragGestures
import androidx.compose.foundation.gestures.drag
-import androidx.compose.foundation.gestures.forEachGesture
import androidx.compose.foundation.gestures.horizontalDrag
import androidx.compose.foundation.gestures.verticalDrag
import androidx.compose.foundation.layout.Box
@@ -74,26 +74,24 @@
.width(50.dp)
.background(Color.Blue)
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown()
- var change =
- awaitHorizontalTouchSlopOrCancellation(down.id) { change, over ->
- val originalX = offsetX.value
- val newValue =
- (originalX + over).coerceIn(0f, width - 50.dp.toPx())
- change.consume()
- offsetX.value = newValue
- }
- while (change != null && change.pressed) {
- change = awaitHorizontalDragOrCancellation(change.id)
- if (change != null && change.pressed) {
- val originalX = offsetX.value
- val newValue = (originalX + change.positionChange().x)
- .coerceIn(0f, width - 50.dp.toPx())
- change.consume()
- offsetX.value = newValue
- }
+ awaitEachGesture {
+ val down = awaitFirstDown()
+ var change =
+ awaitHorizontalTouchSlopOrCancellation(down.id) { change, over ->
+ val originalX = offsetX.value
+ val newValue =
+ (originalX + over).coerceIn(0f, width - 50.dp.toPx())
+ change.consume()
+ offsetX.value = newValue
+ }
+ while (change != null && change.pressed) {
+ change = awaitHorizontalDragOrCancellation(change.id)
+ if (change != null && change.pressed) {
+ val originalX = offsetX.value
+ val newValue = (originalX + change.positionChange().x)
+ .coerceIn(0f, width - 50.dp.toPx())
+ change.consume()
+ offsetX.value = newValue
}
}
}
@@ -118,25 +116,23 @@
.width(50.dp)
.background(Color.Blue)
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown()
- val change =
- awaitHorizontalTouchSlopOrCancellation(down.id) { change, over ->
- val originalX = offsetX.value
- val newValue =
- (originalX + over).coerceIn(0f, width - 50.dp.toPx())
- change.consume()
- offsetX.value = newValue
- }
- if (change != null) {
- horizontalDrag(change.id) {
- val originalX = offsetX.value
- val newValue = (originalX + it.positionChange().x)
- .coerceIn(0f, width - 50.dp.toPx())
- it.consume()
- offsetX.value = newValue
- }
+ awaitEachGesture {
+ val down = awaitFirstDown()
+ val change =
+ awaitHorizontalTouchSlopOrCancellation(down.id) { change, over ->
+ val originalX = offsetX.value
+ val newValue =
+ (originalX + over).coerceIn(0f, width - 50.dp.toPx())
+ change.consume()
+ offsetX.value = newValue
+ }
+ if (change != null) {
+ horizontalDrag(change.id) {
+ val originalX = offsetX.value
+ val newValue = (originalX + it.positionChange().x)
+ .coerceIn(0f, width - 50.dp.toPx())
+ it.consume()
+ offsetX.value = newValue
}
}
}
@@ -187,26 +183,24 @@
.height(50.dp)
.background(Color.Blue)
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown()
- var change =
- awaitVerticalTouchSlopOrCancellation(down.id) { change, over ->
- val originalY = offsetY.value
- val newValue = (originalY + over)
- .coerceIn(0f, height - 50.dp.toPx())
- change.consume()
- offsetY.value = newValue
- }
- while (change != null && change.pressed) {
- change = awaitVerticalDragOrCancellation(change.id)
- if (change != null && change.pressed) {
- val originalY = offsetY.value
- val newValue = (originalY + change.positionChange().y)
- .coerceIn(0f, height - 50.dp.toPx())
- change.consume()
- offsetY.value = newValue
- }
+ awaitEachGesture {
+ val down = awaitFirstDown()
+ var change =
+ awaitVerticalTouchSlopOrCancellation(down.id) { change, over ->
+ val originalY = offsetY.value
+ val newValue = (originalY + over)
+ .coerceIn(0f, height - 50.dp.toPx())
+ change.consume()
+ offsetY.value = newValue
+ }
+ while (change != null && change.pressed) {
+ change = awaitVerticalDragOrCancellation(change.id)
+ if (change != null && change.pressed) {
+ val originalY = offsetY.value
+ val newValue = (originalY + change.positionChange().y)
+ .coerceIn(0f, height - 50.dp.toPx())
+ change.consume()
+ offsetY.value = newValue
}
}
}
@@ -231,25 +225,23 @@
.height(50.dp)
.background(Color.Blue)
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown()
- val change =
- awaitVerticalTouchSlopOrCancellation(down.id) { change, over ->
- val originalY = offsetY.value
- val newValue = (originalY + over)
- .coerceIn(0f, height - 50.dp.toPx())
- change.consume()
- offsetY.value = newValue
- }
- if (change != null) {
- verticalDrag(change.id) {
- val originalY = offsetY.value
- val newValue = (originalY + it.positionChange().y)
- .coerceIn(0f, height - 50.dp.toPx())
- it.consume()
- offsetY.value = newValue
- }
+ awaitEachGesture {
+ val down = awaitFirstDown()
+ val change =
+ awaitVerticalTouchSlopOrCancellation(down.id) { change, over ->
+ val originalY = offsetY.value
+ val newValue = (originalY + over)
+ .coerceIn(0f, height - 50.dp.toPx())
+ change.consume()
+ offsetY.value = newValue
+ }
+ if (change != null) {
+ verticalDrag(change.id) {
+ val originalY = offsetY.value
+ val newValue = (originalY + it.positionChange().y)
+ .coerceIn(0f, height - 50.dp.toPx())
+ it.consume()
+ offsetY.value = newValue
}
}
}
@@ -299,12 +291,24 @@
.size(50.dp)
.background(Color.Blue)
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown()
- var change = awaitTouchSlopOrCancellation(down.id) { change, over ->
+ awaitEachGesture {
+ val down = awaitFirstDown()
+ var change = awaitTouchSlopOrCancellation(down.id) { change, over ->
+ val original = Offset(offsetX.value, offsetY.value)
+ val summed = original + over
+ val newValue = Offset(
+ x = summed.x.coerceIn(0f, size.width - 50.dp.toPx()),
+ y = summed.y.coerceIn(0f, size.height - 50.dp.toPx())
+ )
+ change.consume()
+ offsetX.value = newValue.x
+ offsetY.value = newValue.y
+ }
+ while (change != null && change.pressed) {
+ change = awaitDragOrCancellation(change.id)
+ if (change != null && change.pressed) {
val original = Offset(offsetX.value, offsetY.value)
- val summed = original + over
+ val summed = original + change.positionChange()
val newValue = Offset(
x = summed.x.coerceIn(0f, size.width - 50.dp.toPx()),
y = summed.y.coerceIn(0f, size.height - 50.dp.toPx())
@@ -313,20 +317,6 @@
offsetX.value = newValue.x
offsetY.value = newValue.y
}
- while (change != null && change.pressed) {
- change = awaitDragOrCancellation(change.id)
- if (change != null && change.pressed) {
- val original = Offset(offsetX.value, offsetY.value)
- val summed = original + change.positionChange()
- val newValue = Offset(
- x = summed.x.coerceIn(0f, size.width - 50.dp.toPx()),
- y = summed.y.coerceIn(0f, size.height - 50.dp.toPx())
- )
- change.consume()
- offsetX.value = newValue.x
- offsetY.value = newValue.y
- }
- }
}
}
}
@@ -349,33 +339,31 @@
.size(50.dp)
.background(Color.Blue)
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown()
- val change = awaitTouchSlopOrCancellation(down.id) { change, over ->
+ awaitEachGesture {
+ val down = awaitFirstDown()
+ val change = awaitTouchSlopOrCancellation(down.id) { change, over ->
+ val original = Offset(offsetX.value, offsetY.value)
+ val summed = original + over
+ val newValue = Offset(
+ x = summed.x.coerceIn(0f, size.width - 50.dp.toPx()),
+ y = summed.y.coerceIn(0f, size.height - 50.dp.toPx())
+ )
+ change.consume()
+ offsetX.value = newValue.x
+ offsetY.value = newValue.y
+ }
+ if (change != null) {
+ drag(change.id) {
val original = Offset(offsetX.value, offsetY.value)
- val summed = original + over
+ val summed = original + it.positionChange()
val newValue = Offset(
x = summed.x.coerceIn(0f, size.width - 50.dp.toPx()),
y = summed.y.coerceIn(0f, size.height - 50.dp.toPx())
)
- change.consume()
+ it.consume()
offsetX.value = newValue.x
offsetY.value = newValue.y
}
- if (change != null) {
- drag(change.id) {
- val original = Offset(offsetX.value, offsetY.value)
- val summed = original + it.positionChange()
- val newValue = Offset(
- x = summed.x.coerceIn(0f, size.width - 50.dp.toPx()),
- y = summed.y.coerceIn(0f, size.height - 50.dp.toPx())
- )
- it.consume()
- offsetX.value = newValue.x
- offsetY.value = newValue.y
- }
- }
}
}
}
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/TapGestureSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/TapGestureSamples.kt
index 2e759f3..9255c67 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/TapGestureSamples.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/TapGestureSamples.kt
@@ -22,7 +22,7 @@
import androidx.compose.foundation.border
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.awaitLongPressOrCancellation
-import androidx.compose.foundation.gestures.forEachGesture
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
@@ -56,12 +56,10 @@
.wrapContentSize(Alignment.Center)
.size(192.dp)
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown(requireUnconsumed = false)
- awaitLongPressOrCancellation(down.id)?.let {
- count++
- }
+ awaitEachGesture {
+ val down = awaitFirstDown(requireUnconsumed = false)
+ awaitLongPressOrCancellation(down.id)?.let {
+ count++
}
}
}
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/TransformGestureSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/TransformGestureSamples.kt
index 279d7ea..d4940a6 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/TransformGestureSamples.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/TransformGestureSamples.kt
@@ -24,8 +24,8 @@
import androidx.compose.foundation.gestures.calculateRotation
import androidx.compose.foundation.gestures.calculateZoom
import androidx.compose.foundation.gestures.detectTransformGestures
-import androidx.compose.foundation.gestures.forEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
@@ -113,15 +113,13 @@
.graphicsLayer(rotationZ = angle)
.background(Color.Blue)
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- awaitFirstDown()
- do {
- val event = awaitPointerEvent()
- val rotation = event.calculateRotation()
- angle += rotation
- } while (event.changes.any { it.pressed })
- }
+ awaitEachGesture {
+ awaitFirstDown()
+ do {
+ val event = awaitPointerEvent()
+ val rotation = event.calculateRotation()
+ angle += rotation
+ } while (event.changes.any { it.pressed })
}
}
.fillMaxSize()
@@ -137,14 +135,12 @@
.graphicsLayer(scaleX = zoom, scaleY = zoom)
.background(Color.Blue)
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- awaitFirstDown()
- do {
- val event = awaitPointerEvent()
- zoom *= event.calculateZoom()
- } while (event.changes.any { it.pressed })
- }
+ awaitEachGesture {
+ awaitFirstDown()
+ do {
+ val event = awaitPointerEvent()
+ zoom *= event.calculateZoom()
+ } while (event.changes.any { it.pressed })
}
}
.fillMaxSize()
@@ -162,16 +158,14 @@
.graphicsLayer()
.background(Color.Blue)
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- awaitFirstDown()
- do {
- val event = awaitPointerEvent()
- val offset = event.calculatePan()
- offsetX.value += offset.x
- offsetY.value += offset.y
- } while (event.changes.any { it.pressed })
- }
+ awaitEachGesture {
+ awaitFirstDown()
+ do {
+ val event = awaitPointerEvent()
+ val offset = event.calculatePan()
+ offsetX.value += offset.x
+ offsetY.value += offset.y
+ } while (event.changes.any { it.pressed })
}
}
.fillMaxSize()
@@ -190,23 +184,21 @@
drawCircle(Color.Blue, centroidSize, center = position)
}
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- awaitFirstDown().also {
- position = it.position
- }
- do {
- val event = awaitPointerEvent()
- val size = event.calculateCentroidSize()
- if (size != 0f) {
- centroidSize = event.calculateCentroidSize()
- }
- val centroid = event.calculateCentroid()
- if (centroid != Offset.Unspecified) {
- position = centroid
- }
- } while (event.changes.any { it.pressed })
+ awaitEachGesture {
+ awaitFirstDown().also {
+ position = it.position
}
+ do {
+ val event = awaitPointerEvent()
+ val size = event.calculateCentroidSize()
+ if (size != 0f) {
+ centroidSize = event.calculateCentroidSize()
+ }
+ val centroid = event.calculateCentroid()
+ if (centroid != Offset.Unspecified) {
+ position = centroid
+ }
+ } while (event.changes.any { it.pressed })
}
}
.fillMaxSize()
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/AwaitEachGestureTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/AwaitEachGestureTest.kt
new file mode 100644
index 0000000..ef7040b
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/AwaitEachGestureTest.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2021 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.foundation.gesture
+
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.pointer.PointerEventType
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.coroutineScope
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class AwaitEachGestureTest {
+ @get:Rule
+ val rule = createComposeRule()
+
+ @Test
+ fun awaitEachGestureInternalCancellation() {
+ val inputLatch = CountDownLatch(1)
+ rule.setContent {
+ Box(
+ Modifier.pointerInput(Unit) {
+ try {
+ var count = 0
+ coroutineScope {
+ awaitEachGesture {
+ when (count++) {
+ 0 -> Unit // continue
+ 1 -> throw CancellationException("internal exception")
+ else -> {
+ // detectGestures will loop infinitely with nothing in the
+ // middle so wait for cancellation
+ cancel("really canceled")
+ }
+ }
+ }
+ }
+ } catch (cancellationException: CancellationException) {
+ assertWithMessage("The internal exception shouldn't cancel detectGestures")
+ .that(cancellationException.message)
+ .isEqualTo("really canceled")
+ }
+ inputLatch.countDown()
+ }.size(10.dp)
+ )
+ }
+ rule.waitForIdle()
+ assertThat(inputLatch.await(1, TimeUnit.SECONDS)).isTrue()
+ }
+
+ @Test
+ fun awaitEachGestureLoops() {
+ val events = mutableListOf<PointerEventType>()
+ val tag = "input rect"
+ rule.setContent {
+ Box(
+ Modifier.fillMaxSize()
+ .testTag(tag)
+ .pointerInput(Unit) {
+ awaitEachGesture {
+ val event = awaitPointerEvent()
+ events += event.type
+ }
+ }
+ )
+ }
+
+ rule.onNodeWithTag(tag).performTouchInput {
+ down(Offset.Zero)
+ moveBy(Offset(10f, 10f))
+ up()
+ down(Offset(3f, 3f))
+ moveBy(Offset(10f, 10f))
+ moveBy(Offset(1f, 1f))
+ up()
+ }
+ assertThat(events).hasSize(2)
+ assertThat(events).containsExactly(PointerEventType.Press, PointerEventType.Press)
+ }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/AwaitTouchEventTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/AwaitTouchEventTest.kt
index 52d0430..591e968 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/AwaitTouchEventTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/AwaitTouchEventTest.kt
@@ -18,7 +18,7 @@
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.awaitLongPressOrCancellation
-import androidx.compose.foundation.gestures.forEachGesture
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.text.BasicText
import androidx.compose.ui.Modifier
@@ -73,12 +73,10 @@
modifier = Modifier
.testTag("myLongPress")
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown(requireUnconsumed = false)
- awaitLongPressOrCancellation(down.id)?.let {
- counter++
- }
+ awaitEachGesture {
+ val down = awaitFirstDown(requireUnconsumed = false)
+ awaitLongPressOrCancellation(down.id)?.let {
+ counter++
}
}
}
@@ -111,12 +109,10 @@
modifier = Modifier
.testTag("myLongPress")
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown(requireUnconsumed = false)
- awaitLongPressOrCancellation(down.id)?.let {
- counter++
- }
+ awaitEachGesture {
+ val down = awaitFirstDown(requireUnconsumed = false)
+ awaitLongPressOrCancellation(down.id)?.let {
+ counter++
}
}
}
@@ -149,12 +145,10 @@
modifier = Modifier
.testTag("myLongPress")
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown(requireUnconsumed = false)
- awaitLongPressOrCancellation(down.id)?.let {
- counter++
- }
+ awaitEachGesture {
+ val down = awaitFirstDown(requireUnconsumed = false)
+ awaitLongPressOrCancellation(down.id)?.let {
+ counter++
}
}
}
@@ -197,12 +191,10 @@
modifier = Modifier
.testTag("myLongPress")
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown(requireUnconsumed = false)
- awaitLongPressOrCancellation(down.id)?.let {
- counter++
- }
+ awaitEachGesture {
+ val down = awaitFirstDown(requireUnconsumed = false)
+ awaitLongPressOrCancellation(down.id)?.let {
+ counter++
}
}
}
@@ -245,12 +237,10 @@
modifier = Modifier
.testTag("myLongPress")
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown(requireUnconsumed = false)
- awaitLongPressOrCancellation(down.id)?.let {
- counter++
- }
+ awaitEachGesture {
+ val down = awaitFirstDown(requireUnconsumed = false)
+ awaitLongPressOrCancellation(down.id)?.let {
+ counter++
}
}
}
@@ -301,12 +291,10 @@
modifier = Modifier
.testTag("myLongPress")
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown(requireUnconsumed = false)
- awaitLongPressOrCancellation(down.id)?.let {
- counter++
- }
+ awaitEachGesture {
+ val down = awaitFirstDown(requireUnconsumed = false)
+ awaitLongPressOrCancellation(down.id)?.let {
+ counter++
}
}
}
@@ -358,12 +346,10 @@
modifier = Modifier
.testTag("myLongPress")
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown(requireUnconsumed = false)
- awaitLongPressOrCancellation(down.id)?.let {
- counter++
- }
+ awaitEachGesture {
+ val down = awaitFirstDown(requireUnconsumed = false)
+ awaitLongPressOrCancellation(down.id)?.let {
+ counter++
}
}
}
@@ -406,13 +392,11 @@
modifier = Modifier
.testTag("MyLongPressParent")
.pointerInput(Unit) {
- forEachGesture() {
- awaitPointerEventScope {
- while (true) {
- val event = awaitPointerEvent(PointerEventPass.Final)
- if (event.type == PointerEventType.Move) {
- event.changes[0].consume()
- }
+ awaitEachGesture {
+ while (true) {
+ val event = awaitPointerEvent(PointerEventPass.Final)
+ if (event.type == PointerEventType.Move) {
+ event.changes[0].consume()
}
}
}
@@ -423,12 +407,10 @@
modifier = Modifier
.testTag("myLongPress")
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown(requireUnconsumed = false)
- awaitLongPressOrCancellation(down.id)?.let {
- counter++
- }
+ awaitEachGesture {
+ val down = awaitFirstDown(requireUnconsumed = false)
+ awaitLongPressOrCancellation(down.id)?.let {
+ counter++
}
}
}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/ForEachGestureTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/ForEachGestureTest.kt
index 123be5e..2fcb00d 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/ForEachGestureTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/ForEachGestureTest.kt
@@ -38,6 +38,7 @@
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
+@Suppress("DEPRECATION")
@MediumTest
@RunWith(AndroidJUnit4::class)
class ForEachGestureTest {
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/AndroidOverscroll.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/AndroidOverscroll.kt
index cf212ef..32047cf 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/AndroidOverscroll.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/AndroidOverscroll.kt
@@ -25,7 +25,7 @@
import androidx.compose.foundation.EdgeEffectCompat.onPullDistanceCompat
import androidx.compose.foundation.EdgeEffectCompat.onReleaseWithOppositeDelta
import androidx.compose.foundation.gestures.awaitFirstDown
-import androidx.compose.foundation.gestures.forEachGesture
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.neverEqualPolicy
@@ -319,27 +319,25 @@
override val effectModifier: Modifier = Modifier
.then(StretchOverscrollNonClippingLayer)
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown(requireUnconsumed = false)
- pointerId = down.id
- pointerPosition = down.position
- do {
- val pressedChanges = awaitPointerEvent().changes.fastFilter { it.pressed }
- // If the same ID we are already tracking is down, use that. Otherwise, use
- // the next down, to move the overscroll to the next pointer.
- val change = pressedChanges
- .fastFirstOrNull { it.id == pointerId } ?: pressedChanges.firstOrNull()
- if (change != null) {
- // Update the id if we are now tracking a new down
- pointerId = change.id
- pointerPosition = change.position
- }
- } while (pressedChanges.isNotEmpty())
- pointerId = null
- // Explicitly not resetting the pointer position until the next down, so we
- // don't change any existing effects
- }
+ awaitEachGesture {
+ val down = awaitFirstDown(requireUnconsumed = false)
+ pointerId = down.id
+ pointerPosition = down.position
+ do {
+ val pressedChanges = awaitPointerEvent().changes.fastFilter { it.pressed }
+ // If the same ID we are already tracking is down, use that. Otherwise, use
+ // the next down, to move the overscroll to the next pointer.
+ val change = pressedChanges
+ .fastFirstOrNull { it.id == pointerId } ?: pressedChanges.firstOrNull()
+ if (change != null) {
+ // Update the id if we are now tracking a new down
+ pointerId = change.id
+ pointerPosition = change.position
+ }
+ } while (pressedChanges.isNotEmpty())
+ pointerId = null
+ // Explicitly not resetting the pointer position until the next down, so we
+ // don't change any existing effects
}
}
.onSizeChanged(onNewSize)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/DragGestureDetector.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/DragGestureDetector.kt
index fb26f90..7aed630 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/DragGestureDetector.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/DragGestureDetector.kt
@@ -172,34 +172,32 @@
onDragCancel: () -> Unit = { },
onDrag: (change: PointerInputChange, dragAmount: Offset) -> Unit
) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown(requireUnconsumed = false)
- var drag: PointerInputChange?
- var overSlop = Offset.Zero
- do {
- drag = awaitPointerSlopOrCancellation(
- down.id,
- down.type,
- triggerOnMainAxisSlop = false
- ) { change, over ->
- change.consume()
- overSlop = over
+ awaitEachGesture {
+ val down = awaitFirstDown(requireUnconsumed = false)
+ var drag: PointerInputChange?
+ var overSlop = Offset.Zero
+ do {
+ drag = awaitPointerSlopOrCancellation(
+ down.id,
+ down.type,
+ triggerOnMainAxisSlop = false
+ ) { change, over ->
+ change.consume()
+ overSlop = over
+ }
+ } while (drag != null && !drag.isConsumed)
+ if (drag != null) {
+ onDragStart.invoke(drag.position)
+ onDrag(drag, overSlop)
+ if (
+ !drag(drag.id) {
+ onDrag(it, it.positionChange())
+ it.consume()
}
- } while (drag != null && !drag.isConsumed)
- if (drag != null) {
- onDragStart.invoke(drag.position)
- onDrag(drag, overSlop)
- if (
- !drag(drag.id) {
- onDrag(it, it.positionChange())
- it.consume()
- }
- ) {
- onDragCancel()
- } else {
- onDragEnd()
- }
+ ) {
+ onDragCancel()
+ } else {
+ onDragEnd()
}
}
}
@@ -232,28 +230,26 @@
onDragCancel: () -> Unit = { },
onDrag: (change: PointerInputChange, dragAmount: Offset) -> Unit
) {
- forEachGesture {
+ awaitEachGesture {
try {
- awaitPointerEventScope {
- val down = awaitFirstDown(requireUnconsumed = false)
- val drag = awaitLongPressOrCancellation(down.id)
- if (drag != null) {
- onDragStart.invoke(drag.position)
+ val down = awaitFirstDown(requireUnconsumed = false)
+ val drag = awaitLongPressOrCancellation(down.id)
+ if (drag != null) {
+ onDragStart.invoke(drag.position)
- if (
- drag(drag.id) {
- onDrag(it, it.positionChange())
- it.consume()
- }
- ) {
- // consume up if we quit drag gracefully with the up
- currentEvent.changes.fastForEach {
- if (it.changedToUp()) it.consume()
- }
- onDragEnd()
- } else {
- onDragCancel()
+ if (
+ drag(drag.id) {
+ onDrag(it, it.positionChange())
+ it.consume()
}
+ ) {
+ // consume up if we quit drag gracefully with the up
+ currentEvent.changes.fastForEach {
+ if (it.changedToUp()) it.consume()
+ }
+ onDragEnd()
+ } else {
+ onDragCancel()
}
}
} catch (c: CancellationException) {
@@ -391,27 +387,25 @@
onDragCancel: () -> Unit = { },
onVerticalDrag: (change: PointerInputChange, dragAmount: Float) -> Unit
) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown(requireUnconsumed = false)
- var overSlop = 0f
- val drag = awaitVerticalPointerSlopOrCancellation(down.id, down.type) { change, over ->
- change.consume()
- overSlop = over
- }
- if (drag != null) {
- onDragStart.invoke(drag.position)
- onVerticalDrag.invoke(drag, overSlop)
- if (
- verticalDrag(drag.id) {
- onVerticalDrag(it, it.positionChange().y)
- it.consume()
- }
- ) {
- onDragEnd()
- } else {
- onDragCancel()
+ awaitEachGesture {
+ val down = awaitFirstDown(requireUnconsumed = false)
+ var overSlop = 0f
+ val drag = awaitVerticalPointerSlopOrCancellation(down.id, down.type) { change, over ->
+ change.consume()
+ overSlop = over
+ }
+ if (drag != null) {
+ onDragStart.invoke(drag.position)
+ onVerticalDrag.invoke(drag, overSlop)
+ if (
+ verticalDrag(drag.id) {
+ onVerticalDrag(it, it.positionChange().y)
+ it.consume()
}
+ ) {
+ onDragEnd()
+ } else {
+ onDragCancel()
}
}
}
@@ -541,30 +535,28 @@
onDragCancel: () -> Unit = { },
onHorizontalDrag: (change: PointerInputChange, dragAmount: Float) -> Unit
) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown(requireUnconsumed = false)
- var overSlop = 0f
- val drag = awaitHorizontalPointerSlopOrCancellation(
- down.id,
- down.type
- ) { change, over ->
- change.consume()
- overSlop = over
- }
- if (drag != null) {
- onDragStart.invoke(drag.position)
- onHorizontalDrag(drag, overSlop)
- if (
- horizontalDrag(drag.id) {
- onHorizontalDrag(it, it.positionChange().x)
- it.consume()
- }
- ) {
- onDragEnd()
- } else {
- onDragCancel()
+ awaitEachGesture {
+ val down = awaitFirstDown(requireUnconsumed = false)
+ var overSlop = 0f
+ val drag = awaitHorizontalPointerSlopOrCancellation(
+ down.id,
+ down.type
+ ) { change, over ->
+ change.consume()
+ overSlop = over
+ }
+ if (drag != null) {
+ onDragStart.invoke(drag.position)
+ onHorizontalDrag(drag, overSlop)
+ if (
+ horizontalDrag(drag.id) {
+ onHorizontalDrag(it, it.positionChange().x)
+ it.consume()
}
+ ) {
+ onDragEnd()
+ } else {
+ onDragCancel()
}
}
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/ForEachGesture.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/ForEachGesture.kt
index c13e405..4901cab 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/ForEachGesture.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/ForEachGesture.kt
@@ -33,7 +33,14 @@
* Repeatedly calls [block] to handle gestures. If there is a [CancellationException],
* it will wait until all pointers are raised before another gesture is detected, or it
* exits if [isActive] is `false`.
+ *
+ * [awaitEachGesture] does the same thing without the possibility of missing events between
+ * gestures, but also lacks the ability to call arbitrary suspending functions within [block].
*/
+@Deprecated(
+ message = "Use awaitEachGesture instead. forEachGesture() can drop events between gestures.",
+ replaceWith = ReplaceWith("awaitEachGesture(block)")
+)
suspend fun PointerInputScope.forEachGesture(block: suspend PointerInputScope.() -> Unit) {
val currentContext = currentCoroutineContext()
while (currentContext.isActive) {
@@ -79,4 +86,36 @@
val events = awaitPointerEvent(PointerEventPass.Final)
} while (events.changes.fastAny { it.pressed })
}
+}
+
+/**
+ * Repeatedly calls [block] to handle gestures. If there is a [CancellationException],
+ * it will wait until all pointers are raised before another gesture is detected, or it
+ * exits if [isActive] is `false`.
+ *
+ * [block] is run within [PointerInputScope.awaitPointerEventScope] and will loop entirely
+ * within the [AwaitPointerEventScope] so events will not be lost between gestures.
+ */
+suspend fun PointerInputScope.awaitEachGesture(block: suspend AwaitPointerEventScope.() -> Unit) {
+ val currentContext = currentCoroutineContext()
+ awaitPointerEventScope {
+ while (currentContext.isActive) {
+ try {
+ block()
+
+ // Wait for all pointers to be up. Gestures start when a finger goes down.
+ awaitAllPointersUp()
+ } catch (e: CancellationException) {
+ if (currentContext.isActive) {
+ // The current gesture was canceled. Wait for all fingers to be "up" before
+ // looping again.
+ awaitAllPointersUp()
+ } else {
+ // detectGesture was cancelled externally. Rethrow the cancellation exception to
+ // propagate it upwards.
+ throw e
+ }
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TapGestureDetector.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TapGestureDetector.kt
index b0158ed..13695ce 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TapGestureDetector.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TapGestureDetector.kt
@@ -96,73 +96,87 @@
// cancel/up events as we're only require down events
val pressScope = PressGestureScopeImpl(this@detectTapGestures)
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown()
- down.consume()
+ awaitEachGesture {
+ val down = awaitFirstDown()
+ down.consume()
+ launch {
pressScope.reset()
- if (onPress !== NoPressGesture) launch {
- pressScope.onPress(down.position)
+ }
+ if (onPress !== NoPressGesture) launch {
+ pressScope.onPress(down.position)
+ }
+ val longPressTimeout = onLongPress?.let {
+ viewConfiguration.longPressTimeoutMillis
+ } ?: (Long.MAX_VALUE / 2)
+ var upOrCancel: PointerInputChange? = null
+ try {
+ // wait for first tap up or long press
+ upOrCancel = withTimeout(longPressTimeout) {
+ waitForUpOrCancellation()
}
- val longPressTimeout = onLongPress?.let {
- viewConfiguration.longPressTimeoutMillis
- } ?: (Long.MAX_VALUE / 2)
- var upOrCancel: PointerInputChange? = null
- try {
- // wait for first tap up or long press
- upOrCancel = withTimeout(longPressTimeout) {
- waitForUpOrCancellation()
- }
- if (upOrCancel == null) {
+ if (upOrCancel == null) {
+ launch {
pressScope.cancel() // tap-up was canceled
- } else {
- upOrCancel.consume()
+ }
+ } else {
+ upOrCancel.consume()
+ launch {
pressScope.release()
}
- } catch (_: PointerEventTimeoutCancellationException) {
- onLongPress?.invoke(down.position)
- consumeUntilUp()
+ }
+ } catch (_: PointerEventTimeoutCancellationException) {
+ onLongPress?.invoke(down.position)
+ consumeUntilUp()
+ launch {
pressScope.release()
}
+ }
- if (upOrCancel != null) {
- // tap was successful.
- if (onDoubleTap == null) {
- onTap?.invoke(upOrCancel.position) // no need to check for double-tap.
+ if (upOrCancel != null) {
+ // tap was successful.
+ if (onDoubleTap == null) {
+ onTap?.invoke(upOrCancel.position) // no need to check for double-tap.
+ } else {
+ // check for second tap
+ val secondDown = awaitSecondDown(upOrCancel)
+
+ if (secondDown == null) {
+ onTap?.invoke(upOrCancel.position) // no valid second tap started
} else {
- // check for second tap
- val secondDown = awaitSecondDown(upOrCancel)
-
- if (secondDown == null) {
- onTap?.invoke(upOrCancel.position) // no valid second tap started
- } else {
- // Second tap down detected
+ // Second tap down detected
+ launch {
pressScope.reset()
- if (onPress !== NoPressGesture) {
- launch { pressScope.onPress(secondDown.position) }
- }
+ }
+ if (onPress !== NoPressGesture) {
+ launch { pressScope.onPress(secondDown.position) }
+ }
- try {
- // Might have a long second press as the second tap
- withTimeout(longPressTimeout) {
- val secondUp = waitForUpOrCancellation()
- if (secondUp != null) {
- secondUp.consume()
+ try {
+ // Might have a long second press as the second tap
+ withTimeout(longPressTimeout) {
+ val secondUp = waitForUpOrCancellation()
+ if (secondUp != null) {
+ secondUp.consume()
+ launch {
pressScope.release()
- onDoubleTap(secondUp.position)
- } else {
- pressScope.cancel()
- onTap?.invoke(upOrCancel.position)
}
+ onDoubleTap(secondUp.position)
+ } else {
+ launch {
+ pressScope.cancel()
+ }
+ onTap?.invoke(upOrCancel.position)
}
- } catch (e: PointerEventTimeoutCancellationException) {
- // The first tap was valid, but the second tap is a long press.
- // notify for the first tap
- onTap?.invoke(upOrCancel.position)
+ }
+ } catch (e: PointerEventTimeoutCancellationException) {
+ // The first tap was valid, but the second tap is a long press.
+ // notify for the first tap
+ onTap?.invoke(upOrCancel.position)
- // notify for the long press
- onLongPress?.invoke(secondDown.position)
- consumeUntilUp()
+ // notify for the long press
+ onLongPress?.invoke(secondDown.position)
+ consumeUntilUp()
+ launch {
pressScope.release()
}
}
@@ -214,25 +228,31 @@
onTap: ((Offset) -> Unit)? = null
) {
val pressScope = PressGestureScopeImpl(this)
- forEachGesture {
- coroutineScope {
- pressScope.reset()
- awaitPointerEventScope {
+ coroutineScope {
+ awaitEachGesture {
+ launch {
+ pressScope.reset()
+ }
- val down = awaitFirstDown().also { it.consume() }
+ val down = awaitFirstDown().also { it.consume() }
- if (onPress !== NoPressGesture) {
- launch { pressScope.onPress(down.position) }
+ if (onPress !== NoPressGesture) {
+ launch {
+ pressScope.onPress(down.position)
}
+ }
- val up = waitForUpOrCancellation()
- if (up == null) {
+ val up = waitForUpOrCancellation()
+ if (up == null) {
+ launch {
pressScope.cancel() // tap-up was canceled
- } else {
- up.consume()
- pressScope.release()
- onTap?.invoke(up.position)
}
+ } else {
+ up.consume()
+ launch {
+ pressScope.release()
+ }
+ onTap?.invoke(up.position)
}
}
}
@@ -322,8 +342,8 @@
/**
* Called when a new gesture has started.
*/
- fun reset() {
- mutex.tryLock() // If tryAwaitRelease wasn't called, this will be unlocked.
+ suspend fun reset() {
+ mutex.lock()
isReleased = false
isCanceled = false
}
@@ -337,7 +357,8 @@
override suspend fun tryAwaitRelease(): Boolean {
if (!isReleased && !isCanceled) {
mutex.lock()
+ mutex.unlock()
}
return isReleased
}
-}
\ No newline at end of file
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TransformGestureDetector.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TransformGestureDetector.kt
index 4213b98..8d8c460 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TransformGestureDetector.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TransformGestureDetector.kt
@@ -48,61 +48,59 @@
panZoomLock: Boolean = false,
onGesture: (centroid: Offset, pan: Offset, zoom: Float, rotation: Float) -> Unit
) {
- forEachGesture {
- awaitPointerEventScope {
- var rotation = 0f
- var zoom = 1f
- var pan = Offset.Zero
- var pastTouchSlop = false
- val touchSlop = viewConfiguration.touchSlop
- var lockedToPanZoom = false
+ awaitEachGesture {
+ var rotation = 0f
+ var zoom = 1f
+ var pan = Offset.Zero
+ var pastTouchSlop = false
+ val touchSlop = viewConfiguration.touchSlop
+ var lockedToPanZoom = false
- awaitFirstDown(requireUnconsumed = false)
- do {
- val event = awaitPointerEvent()
- val canceled = event.changes.fastAny { it.isConsumed }
- if (!canceled) {
- val zoomChange = event.calculateZoom()
- val rotationChange = event.calculateRotation()
- val panChange = event.calculatePan()
+ awaitFirstDown(requireUnconsumed = false)
+ do {
+ val event = awaitPointerEvent()
+ val canceled = event.changes.fastAny { it.isConsumed }
+ if (!canceled) {
+ val zoomChange = event.calculateZoom()
+ val rotationChange = event.calculateRotation()
+ val panChange = event.calculatePan()
- if (!pastTouchSlop) {
- zoom *= zoomChange
- rotation += rotationChange
- pan += panChange
+ if (!pastTouchSlop) {
+ zoom *= zoomChange
+ rotation += rotationChange
+ pan += panChange
- val centroidSize = event.calculateCentroidSize(useCurrent = false)
- val zoomMotion = abs(1 - zoom) * centroidSize
- val rotationMotion = abs(rotation * PI.toFloat() * centroidSize / 180f)
- val panMotion = pan.getDistance()
+ val centroidSize = event.calculateCentroidSize(useCurrent = false)
+ val zoomMotion = abs(1 - zoom) * centroidSize
+ val rotationMotion = abs(rotation * PI.toFloat() * centroidSize / 180f)
+ val panMotion = pan.getDistance()
- if (zoomMotion > touchSlop ||
- rotationMotion > touchSlop ||
- panMotion > touchSlop
- ) {
- pastTouchSlop = true
- lockedToPanZoom = panZoomLock && rotationMotion < touchSlop
- }
+ if (zoomMotion > touchSlop ||
+ rotationMotion > touchSlop ||
+ panMotion > touchSlop
+ ) {
+ pastTouchSlop = true
+ lockedToPanZoom = panZoomLock && rotationMotion < touchSlop
}
+ }
- if (pastTouchSlop) {
- val centroid = event.calculateCentroid(useCurrent = false)
- val effectiveRotation = if (lockedToPanZoom) 0f else rotationChange
- if (effectiveRotation != 0f ||
- zoomChange != 1f ||
- panChange != Offset.Zero
- ) {
- onGesture(centroid, panChange, zoomChange, effectiveRotation)
- }
- event.changes.fastForEach {
- if (it.positionChanged()) {
- it.consume()
- }
+ if (pastTouchSlop) {
+ val centroid = event.calculateCentroid(useCurrent = false)
+ val effectiveRotation = if (lockedToPanZoom) 0f else rotationChange
+ if (effectiveRotation != 0f ||
+ zoomChange != 1f ||
+ panChange != Offset.Zero
+ ) {
+ onGesture(centroid, panChange, zoomChange, effectiveRotation)
+ }
+ event.changes.fastForEach {
+ if (it.positionChanged()) {
+ it.consume()
}
}
}
- } while (!canceled && event.changes.fastAny { it.pressed })
- }
+ }
+ } while (!canceled && event.changes.fastAny { it.pressed })
}
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Transformable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Transformable.kt
index 117fc97..397ee29 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Transformable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Transformable.kt
@@ -68,6 +68,14 @@
val updatePanZoomLock = rememberUpdatedState(lockRotationOnZoomPan)
val block: suspend PointerInputScope.() -> Unit = remember {
{
+ /**
+ * This cannot be converted to awaitEachGesture() because
+ * [TransformableState.transform] is a suspend function. Unfortunately, this means
+ * that events can be lost in the middle of a gesture.
+ *
+ * TODO(b/251826790) Convert to awaitEachGesture()
+ */
+ @Suppress("DEPRECATION")
forEachGesture {
detectZoom(updatePanZoomLock, updatedState)
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/LongPressTextDragObserver.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/LongPressTextDragObserver.kt
index 72b63e9..49c2cb8 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/LongPressTextDragObserver.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/LongPressTextDragObserver.kt
@@ -19,7 +19,7 @@
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress
-import androidx.compose.foundation.gestures.forEachGesture
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.pointer.PointerInputScope
import androidx.compose.ui.util.fastAny
@@ -94,16 +94,14 @@
private suspend fun PointerInputScope.detectPreDragGesturesWithObserver(
observer: TextDragObserver
) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown()
- observer.onDown(down.position)
- // Wait for that pointer to come up.
- do {
- val event = awaitPointerEvent()
- } while (event.changes.fastAny { it.id == down.id && it.pressed })
- observer.onUp()
- }
+ awaitEachGesture {
+ val down = awaitFirstDown()
+ observer.onDown(down.position)
+ // Wait for that pointer to come up.
+ do {
+ val event = awaitPointerEvent()
+ } while (event.changes.fastAny { it.id == down.id && it.pressed })
+ observer.onUp()
}
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt
index 7538250..87053fb 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt
@@ -20,7 +20,7 @@
import androidx.compose.foundation.fastFold
import androidx.compose.foundation.focusable
-import androidx.compose.foundation.gestures.forEachGesture
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.waitForUpOrCancellation
import androidx.compose.foundation.text.Handle
import androidx.compose.foundation.text.TextDragObserver
@@ -54,7 +54,6 @@
import kotlin.math.absoluteValue
import kotlin.math.max
import kotlin.math.min
-import kotlinx.coroutines.coroutineScope
/**
* A bridge class between user interaction to the text composables for text selection.
@@ -615,13 +614,9 @@
* Detect tap without consuming the up event.
*/
private suspend fun PointerInputScope.detectNonConsumingTap(onTap: (Offset) -> Unit) {
- forEachGesture {
- coroutineScope {
- awaitPointerEventScope {
- waitForUpOrCancellation()?.let {
- onTap(it.position)
- }
- }
+ awaitEachGesture {
+ waitForUpOrCancellation()?.let {
+ onTap(it.position)
}
}
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextSelectionMouseDetector.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextSelectionMouseDetector.kt
index 4db3b64..c154770 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextSelectionMouseDetector.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextSelectionMouseDetector.kt
@@ -16,8 +16,8 @@
package androidx.compose.foundation.text.selection
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.drag
-import androidx.compose.foundation.gestures.forEachGesture
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.pointer.AwaitPointerEventScope
import androidx.compose.ui.input.pointer.PointerEvent
@@ -83,36 +83,34 @@
internal suspend fun PointerInputScope.mouseSelectionDetector(
observer: MouseSelectionObserver
) {
- forEachGesture {
- awaitPointerEventScope {
- val clicksCounter = ClicksCounter(viewConfiguration)
- while (true) {
- val down = awaitMouseEventDown()
- clicksCounter.update(down)
- val downChange = down.changes[0]
- if (down.isShiftPressed) {
- val started = observer.onExtend(downChange.position)
- if (started) {
- downChange.consume()
- drag(downChange.id) {
- if (observer.onExtendDrag(it.position)) {
- it.consume()
- }
+ awaitEachGesture {
+ val clicksCounter = ClicksCounter(viewConfiguration)
+ while (true) {
+ val down = awaitMouseEventDown()
+ clicksCounter.update(down)
+ val downChange = down.changes[0]
+ if (down.isShiftPressed) {
+ val started = observer.onExtend(downChange.position)
+ if (started) {
+ downChange.consume()
+ drag(downChange.id) {
+ if (observer.onExtendDrag(it.position)) {
+ it.consume()
}
}
- } else {
- val selectionMode = when (clicksCounter.clicks) {
- 1 -> SelectionAdjustment.None
- 2 -> SelectionAdjustment.Word
- else -> SelectionAdjustment.Paragraph
- }
- val started = observer.onStart(downChange.position, selectionMode)
- if (started) {
- downChange.consume()
- drag(downChange.id) {
- if (observer.onDrag(it.position, selectionMode)) {
- it.consume()
- }
+ }
+ } else {
+ val selectionMode = when (clicksCounter.clicks) {
+ 1 -> SelectionAdjustment.None
+ 2 -> SelectionAdjustment.Word
+ else -> SelectionAdjustment.Paragraph
+ }
+ val started = observer.onStart(downChange.position, selectionMode)
+ if (started) {
+ downChange.consume()
+ drag(downChange.id) {
+ if (observer.onDrag(it.position, selectionMode)) {
+ it.consume()
}
}
}
diff --git a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/Clickable.desktop.kt b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/Clickable.desktop.kt
index f03d71f..6d663a8 100644
--- a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/Clickable.desktop.kt
+++ b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/Clickable.desktop.kt
@@ -16,7 +16,7 @@
package androidx.compose.foundation
-import androidx.compose.foundation.gestures.forEachGesture
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.runtime.Composable
@@ -51,7 +51,6 @@
import androidx.compose.ui.unit.toOffset
import androidx.compose.ui.util.fastAll
import java.awt.event.KeyEvent.VK_ENTER
-import kotlinx.coroutines.coroutineScope
@Composable
internal actual fun isComposeRootInScrollableContainer(): () -> Boolean = { false }
@@ -146,20 +145,15 @@
internal suspend fun PointerInputScope.detectTapWithContext(
onTap: ((PointerEvent, PointerEvent) -> Unit)? = null
) {
- forEachGesture {
- coroutineScope {
- awaitPointerEventScope {
+ awaitEachGesture {
+ val down = awaitEventFirstDown().also {
+ it.changes.forEach { it.consume() }
+ }
- val down = awaitEventFirstDown().also {
- it.changes.forEach { it.consume() }
- }
-
- val up = waitForFirstInboundUp()
- if (up != null) {
- up.changes.forEach { it.consume() }
- onTap?.invoke(down, up)
- }
- }
+ val up = waitForFirstInboundUp()
+ if (up != null) {
+ up.changes.forEach { it.consume() }
+ onTap?.invoke(down, up)
}
}
}
diff --git a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/ContextMenuProvider.desktop.kt b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/ContextMenuProvider.desktop.kt
index 6d6102f..2a7fa89 100644
--- a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/ContextMenuProvider.desktop.kt
+++ b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/ContextMenuProvider.desktop.kt
@@ -16,7 +16,7 @@
package androidx.compose.foundation
-import androidx.compose.foundation.gestures.forEachGesture
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
@@ -107,14 +107,12 @@
enabled && state.status == ContextMenuState.Status.Closed
) {
this.pointerInput(state) {
- forEachGesture {
- awaitPointerEventScope {
- val event = awaitEventFirstDown()
- if (event.buttons.isSecondaryPressed) {
- event.changes.forEach { it.consume() }
- state.status =
- ContextMenuState.Status.Open(Rect(event.changes[0].position, 0f))
- }
+ awaitEachGesture {
+ val event = awaitEventFirstDown()
+ if (event.buttons.isSecondaryPressed) {
+ event.changes.forEach { it.consume() }
+ state.status =
+ ContextMenuState.Status.Open(Rect(event.changes[0].position, 0f))
}
}
}
diff --git a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/Scrollbar.desktop.kt b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/Scrollbar.desktop.kt
index 1b7aa2a..77c5476 100644
--- a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/Scrollbar.desktop.kt
+++ b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/Scrollbar.desktop.kt
@@ -21,7 +21,7 @@
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.detectTapAndPress
import androidx.compose.foundation.gestures.drag
-import androidx.compose.foundation.gestures.forEachGesture
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.foundation.interaction.DragInteraction
import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -275,25 +275,23 @@
val currentOnDelta by rememberUpdatedState(onDelta)
val currentOnFinished by rememberUpdatedState(onFinished)
pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown(requireUnconsumed = false)
- val interaction = DragInteraction.Start()
- currentInteractionSource.tryEmit(interaction)
- currentDraggedInteraction.value = interaction
- val isSuccess = drag(down.id) { change ->
- currentOnDelta.invoke(change.positionChange())
- change.consume()
- }
- val finishInteraction = if (isSuccess) {
- DragInteraction.Stop(interaction)
- } else {
- DragInteraction.Cancel(interaction)
- }
- currentInteractionSource.tryEmit(finishInteraction)
- currentDraggedInteraction.value = null
- currentOnFinished.invoke()
+ awaitEachGesture {
+ val down = awaitFirstDown(requireUnconsumed = false)
+ val interaction = DragInteraction.Start()
+ currentInteractionSource.tryEmit(interaction)
+ currentDraggedInteraction.value = interaction
+ val isSuccess = drag(down.id) { change ->
+ currentOnDelta.invoke(change.positionChange())
+ change.consume()
}
+ val finishInteraction = if (isSuccess) {
+ DragInteraction.Stop(interaction)
+ } else {
+ DragInteraction.Cancel(interaction)
+ }
+ currentInteractionSource.tryEmit(finishInteraction)
+ currentDraggedInteraction.value = null
+ currentOnFinished.invoke()
}
}
}
diff --git a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/window/WindowDraggableArea.desktop.kt b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/window/WindowDraggableArea.desktop.kt
index 36eeaa8..b285b1c 100644
--- a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/window/WindowDraggableArea.desktop.kt
+++ b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/window/WindowDraggableArea.desktop.kt
@@ -17,7 +17,7 @@
package androidx.compose.foundation.window
import androidx.compose.foundation.gestures.awaitFirstDown
-import androidx.compose.foundation.gestures.forEachGesture
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
@@ -46,11 +46,9 @@
Box(
modifier = modifier.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- awaitFirstDown()
- handler.register()
- }
+ awaitEachGesture {
+ awaitFirstDown()
+ handler.register()
}
}
) {
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/DragGestureDetectorTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/DragGestureDetectorTest.kt
index 30db9bd..9479a6c 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/DragGestureDetectorTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/DragGestureDetectorTest.kt
@@ -133,99 +133,93 @@
}
private val AwaitVerticalDragUtil = SuspendingGestureTestUtil(width = 100, height = 100) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown()
- val slopChange = awaitVerticalTouchSlopOrCancellation(down.id) { change, overSlop ->
- if (change.positionChange().y > 0f || !consumePositiveOnly) {
- dragged = true
- dragDistance = overSlop
+ awaitEachGesture {
+ val down = awaitFirstDown()
+ val slopChange = awaitVerticalTouchSlopOrCancellation(down.id) { change, overSlop ->
+ if (change.positionChange().y > 0f || !consumePositiveOnly) {
+ dragged = true
+ dragDistance = overSlop
+ change.consume()
+ }
+ }
+ if (slopChange != null || sloppyDetector) {
+ gestureStarted = true
+ var pointer = if (sloppyDetector) down.id else slopChange!!.id
+ do {
+ val change = awaitVerticalDragOrCancellation(pointer)
+ if (change == null) {
+ gestureCanceled = true
+ } else {
+ dragDistance += change.positionChange().y
change.consume()
- }
- }
- if (slopChange != null || sloppyDetector) {
- gestureStarted = true
- var pointer = if (sloppyDetector) down.id else slopChange!!.id
- do {
- val change = awaitVerticalDragOrCancellation(pointer)
- if (change == null) {
- gestureCanceled = true
- } else {
- dragDistance += change.positionChange().y
- change.consume()
- if (change.changedToUpIgnoreConsumed()) {
- gestureEnded = true
- }
- pointer = change.id
+ if (change.changedToUpIgnoreConsumed()) {
+ gestureEnded = true
}
- } while (!gestureEnded && !gestureCanceled)
- }
+ pointer = change.id
+ }
+ } while (!gestureEnded && !gestureCanceled)
}
}
}
private val AwaitHorizontalDragUtil = SuspendingGestureTestUtil(width = 100, height = 100) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown()
- val slopChange =
- awaitHorizontalTouchSlopOrCancellation(down.id) { change, overSlop ->
- if (change.positionChange().x > 0f || !consumePositiveOnly) {
- dragged = true
- dragDistance = overSlop
- change.consume()
- }
+ awaitEachGesture {
+ val down = awaitFirstDown()
+ val slopChange =
+ awaitHorizontalTouchSlopOrCancellation(down.id) { change, overSlop ->
+ if (change.positionChange().x > 0f || !consumePositiveOnly) {
+ dragged = true
+ dragDistance = overSlop
+ change.consume()
}
- if (slopChange != null || sloppyDetector) {
- gestureStarted = true
- var pointer = if (sloppyDetector) down.id else slopChange!!.id
- do {
- val change = awaitHorizontalDragOrCancellation(pointer)
- if (change == null) {
- gestureCanceled = true
- } else {
- dragDistance += change.positionChange().x
- change.consume()
- if (change.changedToUpIgnoreConsumed()) {
- gestureEnded = true
- }
- pointer = change.id
- }
- } while (!gestureEnded && !gestureCanceled)
}
+ if (slopChange != null || sloppyDetector) {
+ gestureStarted = true
+ var pointer = if (sloppyDetector) down.id else slopChange!!.id
+ do {
+ val change = awaitHorizontalDragOrCancellation(pointer)
+ if (change == null) {
+ gestureCanceled = true
+ } else {
+ dragDistance += change.positionChange().x
+ change.consume()
+ if (change.changedToUpIgnoreConsumed()) {
+ gestureEnded = true
+ }
+ pointer = change.id
+ }
+ } while (!gestureEnded && !gestureCanceled)
}
}
}
private val AwaitDragUtil = SuspendingGestureTestUtil(width = 100, height = 100) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown()
- val slopChange = awaitTouchSlopOrCancellation(down.id) { change, overSlop ->
- val positionChange = change.positionChange()
- if (positionChange.x > 0f || positionChange.y > 0f || !consumePositiveOnly) {
- dragged = true
- dragDistance = overSlop.getDistance()
+ awaitEachGesture {
+ val down = awaitFirstDown()
+ val slopChange = awaitTouchSlopOrCancellation(down.id) { change, overSlop ->
+ val positionChange = change.positionChange()
+ if (positionChange.x > 0f || positionChange.y > 0f || !consumePositiveOnly) {
+ dragged = true
+ dragDistance = overSlop.getDistance()
+ change.consume()
+ }
+ }
+ if (slopChange != null || sloppyDetector) {
+ gestureStarted = true
+ var pointer = if (sloppyDetector) down.id else slopChange!!.id
+ do {
+ val change = awaitDragOrCancellation(pointer)
+ if (change == null) {
+ gestureCanceled = true
+ } else {
+ dragDistance += change.positionChange().getDistance()
change.consume()
- }
- }
- if (slopChange != null || sloppyDetector) {
- gestureStarted = true
- var pointer = if (sloppyDetector) down.id else slopChange!!.id
- do {
- val change = awaitDragOrCancellation(pointer)
- if (change == null) {
- gestureCanceled = true
- } else {
- dragDistance += change.positionChange().getDistance()
- change.consume()
- if (change.changedToUpIgnoreConsumed()) {
- gestureEnded = true
- }
- pointer = change.id
+ if (change.changedToUpIgnoreConsumed()) {
+ gestureEnded = true
}
- } while (!gestureEnded && !gestureCanceled)
- }
+ pointer = change.id
+ }
+ } while (!gestureEnded && !gestureCanceled)
}
}
}
diff --git a/compose/material/material/build.gradle b/compose/material/material/build.gradle
index fe57f44..8b59f0e 100644
--- a/compose/material/material/build.gradle
+++ b/compose/material/material/build.gradle
@@ -33,7 +33,7 @@
* corresponding block below
*/
api("androidx.compose.animation:animation-core:1.2.1")
- api("androidx.compose.foundation:foundation:1.2.1")
+ api(project(":compose:foundation:foundation"))
api(project(":compose:material:material-icons-core"))
api(project(":compose:material:material-ripple"))
api("androidx.compose.runtime:runtime:1.2.1")
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ColorPickerDemo.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ColorPickerDemo.kt
index e878f9a..4aff510 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ColorPickerDemo.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ColorPickerDemo.kt
@@ -25,8 +25,8 @@
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.drag
-import androidx.compose.foundation.gestures.forEachGesture
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
@@ -112,17 +112,15 @@
}
}
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown()
- hasInput = true
- updateColorWheel(down.position)
- drag(down.id) { change ->
- change.consume()
- updateColorWheel(change.position)
- }
- hasInput = false
+ awaitEachGesture {
+ val down = awaitFirstDown()
+ hasInput = true
+ updateColorWheel(down.position)
+ drag(down.id) { change ->
+ change.consume()
+ updateColorWheel(change.position)
}
+ hasInput = false
}
}
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SurfaceTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SurfaceTest.kt
index e4301df..99ffbe5 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SurfaceTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SurfaceTest.kt
@@ -17,7 +17,7 @@
package androidx.compose.material
import android.os.Build
-import androidx.compose.foundation.gestures.forEachGesture
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.PressInteraction
@@ -594,13 +594,11 @@
.fillMaxSize()
.testTag("clickable")
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- hitTested.value = true
- val event = awaitPointerEvent(PointerEventPass.Final)
- Truth.assertThat(event.changes[0].isConsumed)
- .isFalse()
- }
+ awaitEachGesture {
+ hitTested.value = true
+ val event = awaitPointerEvent(PointerEventPass.Final)
+ Truth.assertThat(event.changes[0].isConsumed)
+ .isFalse()
}
}
)
diff --git a/compose/material/material/src/androidMain/kotlin/androidx/compose/material/ExposedDropdownMenu.kt b/compose/material/material/src/androidMain/kotlin/androidx/compose/material/ExposedDropdownMenu.kt
index 20b95ff..3214402 100644
--- a/compose/material/material/src/androidMain/kotlin/androidx/compose/material/ExposedDropdownMenu.kt
+++ b/compose/material/material/src/androidMain/kotlin/androidx/compose/material/ExposedDropdownMenu.kt
@@ -21,7 +21,7 @@
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.tween
-import androidx.compose.foundation.gestures.forEachGesture
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.interaction.InteractionSource
import androidx.compose.foundation.interaction.collectIsFocusedAsState
import androidx.compose.foundation.layout.Box
@@ -63,7 +63,6 @@
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.util.fastAll
-import kotlinx.coroutines.coroutineScope
import kotlin.math.max
/**
@@ -513,18 +512,14 @@
onExpandedChange: () -> Unit,
menuLabel: String
) = pointerInput(Unit) {
- forEachGesture {
- coroutineScope {
- awaitPointerEventScope {
- var event: PointerEvent
- do {
- event = awaitPointerEvent(PointerEventPass.Initial)
- } while (
- !event.changes.fastAll { it.changedToUp() }
- )
- onExpandedChange.invoke()
- }
- }
+ awaitEachGesture {
+ var event: PointerEvent
+ do {
+ event = awaitPointerEvent(PointerEventPass.Initial)
+ } while (
+ !event.changes.fastAll { it.changedToUp() }
+ )
+ onExpandedChange.invoke()
}
}.semantics {
contentDescription = menuLabel // this should be a localised string
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
index 580ab3a..7c76824 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
@@ -30,7 +30,7 @@
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.gestures.draggable
-import androidx.compose.foundation.gestures.forEachGesture
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.horizontalDrag
import androidx.compose.foundation.hoverable
import androidx.compose.foundation.indication
@@ -980,56 +980,54 @@
onDrag
)
coroutineScope {
- forEachGesture {
- awaitPointerEventScope {
- val event = awaitFirstDown(requireUnconsumed = false)
- val interaction = DragInteraction.Start()
- var posX = if (isRtl) maxPx - event.position.x else event.position.x
- val compare = rangeSliderLogic.compareOffsets(posX)
- var draggingStart = if (compare != 0) {
- compare < 0
+ awaitEachGesture {
+ val event = awaitFirstDown(requireUnconsumed = false)
+ val interaction = DragInteraction.Start()
+ var posX = if (isRtl) maxPx - event.position.x else event.position.x
+ val compare = rangeSliderLogic.compareOffsets(posX)
+ var draggingStart = if (compare != 0) {
+ compare < 0
+ } else {
+ rawOffsetStart.value > posX
+ }
+
+ awaitSlop(event.id, event.type)?.let {
+ val slop = viewConfiguration.pointerSlop(event.type)
+ val shouldUpdateCapturedThumb = abs(rawOffsetEnd.value - posX) < slop &&
+ abs(rawOffsetStart.value - posX) < slop
+ if (shouldUpdateCapturedThumb) {
+ val dir = it.second
+ draggingStart = if (isRtl) dir >= 0f else dir < 0f
+ posX += it.first.positionChange().x
+ }
+ }
+
+ rangeSliderLogic.captureThumb(
+ draggingStart,
+ posX,
+ interaction,
+ this@coroutineScope
+ )
+
+ val finishInteraction = try {
+ val success = horizontalDrag(pointerId = event.id) {
+ val deltaX = it.positionChange().x
+ onDrag.value.invoke(draggingStart, if (isRtl) -deltaX else deltaX)
+ }
+ if (success) {
+ DragInteraction.Stop(interaction)
} else {
- rawOffsetStart.value > posX
- }
-
- awaitSlop(event.id, event.type)?.let {
- val slop = viewConfiguration.pointerSlop(event.type)
- val shouldUpdateCapturedThumb = abs(rawOffsetEnd.value - posX) < slop &&
- abs(rawOffsetStart.value - posX) < slop
- if (shouldUpdateCapturedThumb) {
- val dir = it.second
- draggingStart = if (isRtl) dir >= 0f else dir < 0f
- posX += it.first.positionChange().x
- }
- }
-
- rangeSliderLogic.captureThumb(
- draggingStart,
- posX,
- interaction,
- this@coroutineScope
- )
-
- val finishInteraction = try {
- val success = horizontalDrag(pointerId = event.id) {
- val deltaX = it.positionChange().x
- onDrag.value.invoke(draggingStart, if (isRtl) -deltaX else deltaX)
- }
- if (success) {
- DragInteraction.Stop(interaction)
- } else {
- DragInteraction.Cancel(interaction)
- }
- } catch (e: CancellationException) {
DragInteraction.Cancel(interaction)
}
+ } catch (e: CancellationException) {
+ DragInteraction.Cancel(interaction)
+ }
- gestureEndAction.value.invoke(draggingStart)
- launch {
- rangeSliderLogic
- .activeInteraction(draggingStart)
- .emit(finishInteraction)
- }
+ gestureEndAction.value.invoke(draggingStart)
+ launch {
+ rangeSliderLogic
+ .activeInteraction(draggingStart)
+ .emit(finishInteraction)
}
}
}
diff --git a/compose/material3/material3/build.gradle b/compose/material3/material3/build.gradle
index be01d8b..c479be1 100644
--- a/compose/material3/material3/build.gradle
+++ b/compose/material3/material3/build.gradle
@@ -39,7 +39,7 @@
implementation("androidx.compose.animation:animation-core:1.3.0-rc01")
implementation("androidx.compose.foundation:foundation-layout:1.3.0-rc01")
implementation("androidx.compose.ui:ui-util:1.3.0-rc01")
- api("androidx.compose.foundation:foundation:1.3.0-rc01")
+ api(project(":compose:foundation:foundation"))
api("androidx.compose.material:material-icons-core:1.3.0-rc01")
api("androidx.compose.material:material-ripple:1.3.0-rc01")
api("androidx.compose.runtime:runtime:1.3.0-rc01")
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SurfaceTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SurfaceTest.kt
index 0882f40..cbb0705 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SurfaceTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SurfaceTest.kt
@@ -18,7 +18,7 @@
import android.os.Build
import androidx.compose.foundation.clickable
-import androidx.compose.foundation.gestures.forEachGesture
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.PressInteraction
@@ -707,13 +707,11 @@
.fillMaxSize()
.testTag("clickable")
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- hitTested.value = true
- val event = awaitPointerEvent(PointerEventPass.Final)
- Truth.assertThat(event.changes[0].isConsumed)
- .isFalse()
- }
+ awaitEachGesture {
+ hitTested.value = true
+ val event = awaitPointerEvent(PointerEventPass.Final)
+ Truth.assertThat(event.changes[0].isConsumed)
+ .isFalse()
}
}
)
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt
index e270215..3fa4084 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt
@@ -20,7 +20,7 @@
import android.view.View
import android.view.ViewTreeObserver
import androidx.compose.animation.core.MutableTransitionState
-import androidx.compose.foundation.gestures.forEachGesture
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.PaddingValues
@@ -66,7 +66,6 @@
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastAll
import kotlin.math.max
-import kotlinx.coroutines.coroutineScope
/**
* <a href="https://m3.material.io/components/menus/overview" class="external" target="_blank">Material Design Exposed Dropdown Menu</a>.
@@ -527,18 +526,14 @@
expandedDescription: String = getString(Strings.MenuExpanded),
collapsedDescription: String = getString(Strings.MenuCollapsed),
) = pointerInput(Unit) {
- forEachGesture {
- coroutineScope {
- awaitPointerEventScope {
- var event: PointerEvent
- do {
- event = awaitPointerEvent(PointerEventPass.Initial)
- } while (
- !event.changes.fastAll { it.changedToUp() }
- )
- onExpandedChange()
- }
- }
+ awaitEachGesture {
+ var event: PointerEvent
+ do {
+ event = awaitPointerEvent(PointerEventPass.Initial)
+ } while (
+ !event.changes.fastAll { it.changedToUp() }
+ )
+ onExpandedChange()
}
}.semantics {
stateDescription = if (expanded) expandedDescription else collapsedDescription
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
index c9f2656..5eb67f3 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
@@ -30,7 +30,7 @@
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.gestures.draggable
-import androidx.compose.foundation.gestures.forEachGesture
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.horizontalDrag
import androidx.compose.foundation.hoverable
import androidx.compose.foundation.indication
@@ -1200,56 +1200,54 @@
onDrag
)
coroutineScope {
- forEachGesture {
- awaitPointerEventScope {
- val event = awaitFirstDown(requireUnconsumed = false)
- val interaction = DragInteraction.Start()
- var posX = if (isRtl) maxPx - event.position.x else event.position.x
- val compare = rangeSliderLogic.compareOffsets(posX)
- var draggingStart = if (compare != 0) {
- compare < 0
+ awaitEachGesture {
+ val event = awaitFirstDown(requireUnconsumed = false)
+ val interaction = DragInteraction.Start()
+ var posX = if (isRtl) maxPx - event.position.x else event.position.x
+ val compare = rangeSliderLogic.compareOffsets(posX)
+ var draggingStart = if (compare != 0) {
+ compare < 0
+ } else {
+ rawOffsetStart.value > posX
+ }
+
+ awaitSlop(event.id, event.type)?.let {
+ val slop = viewConfiguration.pointerSlop(event.type)
+ val shouldUpdateCapturedThumb = abs(rawOffsetEnd.value - posX) < slop &&
+ abs(rawOffsetStart.value - posX) < slop
+ if (shouldUpdateCapturedThumb) {
+ val dir = it.second
+ draggingStart = if (isRtl) dir >= 0f else dir < 0f
+ posX += it.first.positionChange().x
+ }
+ }
+
+ rangeSliderLogic.captureThumb(
+ draggingStart,
+ posX,
+ interaction,
+ this@coroutineScope
+ )
+
+ val finishInteraction = try {
+ val success = horizontalDrag(pointerId = event.id) {
+ val deltaX = it.positionChange().x
+ onDrag.value.invoke(draggingStart, if (isRtl) -deltaX else deltaX)
+ }
+ if (success) {
+ DragInteraction.Stop(interaction)
} else {
- rawOffsetStart.value > posX
- }
-
- awaitSlop(event.id, event.type)?.let {
- val slop = viewConfiguration.pointerSlop(event.type)
- val shouldUpdateCapturedThumb = abs(rawOffsetEnd.value - posX) < slop &&
- abs(rawOffsetStart.value - posX) < slop
- if (shouldUpdateCapturedThumb) {
- val dir = it.second
- draggingStart = if (isRtl) dir >= 0f else dir < 0f
- posX += it.first.positionChange().x
- }
- }
-
- rangeSliderLogic.captureThumb(
- draggingStart,
- posX,
- interaction,
- this@coroutineScope
- )
-
- val finishInteraction = try {
- val success = horizontalDrag(pointerId = event.id) {
- val deltaX = it.positionChange().x
- onDrag.value.invoke(draggingStart, if (isRtl) -deltaX else deltaX)
- }
- if (success) {
- DragInteraction.Stop(interaction)
- } else {
- DragInteraction.Cancel(interaction)
- }
- } catch (e: CancellationException) {
DragInteraction.Cancel(interaction)
}
+ } catch (e: CancellationException) {
+ DragInteraction.Cancel(interaction)
+ }
- gestureEndAction.value.invoke(draggingStart)
- launch {
- rangeSliderLogic
- .activeInteraction(draggingStart)
- .emit(finishInteraction)
- }
+ gestureEndAction.value.invoke(draggingStart)
+ launch {
+ rangeSliderLogic
+ .activeInteraction(draggingStart)
+ .emit(finishInteraction)
}
}
}
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/ButtonMetaStateDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/ButtonMetaStateDemo.kt
index 1e5c90c..3740ea8 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/ButtonMetaStateDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/ButtonMetaStateDemo.kt
@@ -17,7 +17,7 @@
package androidx.compose.ui.demos.gestures
import androidx.compose.foundation.background
-import androidx.compose.foundation.gestures.forEachGesture
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
@@ -71,45 +71,43 @@
Column(
Modifier.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- do {
- val event = awaitPointerEvent()
- val metaState = event.keyboardModifiers
- control = metaState.isCtrlPressed
- alt = metaState.isAltPressed
- shift = metaState.isShiftPressed
- meta = metaState.isMetaPressed
- sym = metaState.isSymPressed
- function = metaState.isFunctionPressed
- numLock = metaState.isNumLockOn
- scrollLock = metaState.isScrollLockOn
- capsLock = metaState.isCapsLockOn
+ awaitEachGesture {
+ do {
+ val event = awaitPointerEvent()
+ val metaState = event.keyboardModifiers
+ control = metaState.isCtrlPressed
+ alt = metaState.isAltPressed
+ shift = metaState.isShiftPressed
+ meta = metaState.isMetaPressed
+ sym = metaState.isSymPressed
+ function = metaState.isFunctionPressed
+ numLock = metaState.isNumLockOn
+ scrollLock = metaState.isScrollLockOn
+ capsLock = metaState.isCapsLockOn
- val buttons = event.buttons
- primary = buttons.isPrimaryPressed
- secondary = buttons.isSecondaryPressed
- tertiary = buttons.isTertiaryPressed
- back = buttons.isBackPressed
- forward = buttons.isForwardPressed
- } while (event.changes.any { it.pressed })
- // In the future, hover events should work also, but it isn't
- // implemented yet.
- control = false
- alt = false
- shift = false
- meta = false
- sym = false
- function = false
- numLock = false
- scrollLock = false
- capsLock = false
- primary = false
- secondary = false
- tertiary = false
- back = false
- forward = false
- }
+ val buttons = event.buttons
+ primary = buttons.isPrimaryPressed
+ secondary = buttons.isSecondaryPressed
+ tertiary = buttons.isTertiaryPressed
+ back = buttons.isBackPressed
+ forward = buttons.isForwardPressed
+ } while (event.changes.any { it.pressed })
+ // In the future, hover events should work also, but it isn't
+ // implemented yet.
+ control = false
+ alt = false
+ shift = false
+ meta = false
+ sym = false
+ function = false
+ numLock = false
+ scrollLock = false
+ capsLock = false
+ primary = false
+ secondary = false
+ tertiary = false
+ back = false
+ forward = false
}
}
) {
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/DragSlopExceededGestureFilterDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/DragSlopExceededGestureFilterDemo.kt
index 76e1e3f..4ffbac4 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/DragSlopExceededGestureFilterDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/DragSlopExceededGestureFilterDemo.kt
@@ -19,7 +19,7 @@
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.awaitTouchSlopOrCancellation
-import androidx.compose.foundation.gestures.forEachGesture
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
@@ -64,13 +64,11 @@
.wrapContentSize(Alignment.Center)
.size(192.dp)
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown(requireUnconsumed = false)
- awaitTouchSlopOrCancellation(down.id) { change, _ ->
- alternativeColor.value = !alternativeColor.value
- change.consume()
- }
+ awaitEachGesture {
+ val down = awaitFirstDown(requireUnconsumed = false)
+ awaitTouchSlopOrCancellation(down.id) { change, _ ->
+ alternativeColor.value = !alternativeColor.value
+ change.consume()
}
}
}
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/RawDragGestureDetectorDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/RawDragGestureDetectorDemo.kt
index e48488c..004e2b7 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/RawDragGestureDetectorDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/RawDragGestureDetectorDemo.kt
@@ -18,8 +18,8 @@
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.drag
-import androidx.compose.foundation.gestures.forEachGesture
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
@@ -57,13 +57,11 @@
.offset(offsetX, offsetY)
.size(192.dp)
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown(requireUnconsumed = false)
- drag(down.id) { change ->
- offset.value += change.positionChange()
- change.consume()
- }
+ awaitEachGesture {
+ val down = awaitFirstDown(requireUnconsumed = false)
+ drag(down.id) { change ->
+ offset.value += change.positionChange()
+ change.consume()
}
}
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PositionInWindowTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PositionInWindowTest.kt
index faf7cf4..5952258 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PositionInWindowTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/window/PositionInWindowTest.kt
@@ -20,8 +20,8 @@
import androidx.activity.ComponentActivity
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.drag
-import androidx.compose.foundation.gestures.forEachGesture
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.requiredSize
@@ -246,15 +246,13 @@
.background(Color.Black)
.testTag(smallBoxTag)
.pointerInput(Unit) {
- forEachGesture {
- awaitPointerEventScope {
- val down = awaitFirstDown()
- var previous = down.position
- drag(down.id) {
- it.consume()
- offset += it.position - previous
- previous = it.position
- }
+ awaitEachGesture {
+ val down = awaitFirstDown()
+ var previous = down.position
+ drag(down.id) {
+ it.consume()
+ offset += it.position - previous
+ previous = it.position
}
}
}