diff options
| -rw-r--r-- | packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt | 19 | ||||
| -rw-r--r-- | packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt | 36 |
2 files changed, 53 insertions, 2 deletions
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt index 80cbbea7659f..2ea9c487c27c 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt @@ -112,6 +112,14 @@ interface NestedDraggable { interface Controller { /** + * Whether drags that were started from nested scrolls should be automatically + * [stopped][onDragStopped] as soon as they don't consume the entire `delta` passed to + * [onDrag]. + */ + val autoStopNestedDrags: Boolean + get() = false + + /** * Drag by [delta] pixels. * * @return the consumed [delta]. Any non-consumed delta will be dispatched to the next @@ -609,8 +617,15 @@ private class NestedDraggableNode( } private fun scrollWithOverscroll(controller: NestedScrollController, offset: Offset): Offset { - return scrollWithOverscroll(offset) { - controller.controller.onDrag(it.toFloat()).toOffset() + return scrollWithOverscroll(offset) { delta -> + val available = delta.toFloat() + val consumed = controller.controller.onDrag(available) + if (controller.controller.autoStopNestedDrags && consumed != available) { + controller.ensureOnDragStoppedIsCalled() + this.nestedScrollController = null + } + + consumed.toOffset() } } diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt index a03d769c9c36..b247993de4e4 100644 --- a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt +++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt @@ -1000,6 +1000,39 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw assertThat(draggable.onDragStartedCalled).isTrue() } + @Test + fun autoStopNestedDrags() { + var consumeScrolls by mutableStateOf(true) + val draggable = + TestDraggable(autoStopNestedDrags = true, onDrag = { if (consumeScrolls) it else 0f }) + + val touchSlop = + rule.setContentWithTouchSlop { + Box( + Modifier.fillMaxSize() + .nestedDraggable(draggable, orientation) + .scrollable(rememberScrollableState { 0f }, orientation) + ) + } + + rule.onRoot().performTouchInput { + down(center) + moveBy((touchSlop + 1f).toOffset()) + } + + assertThat(draggable.onDragStartedCalled).isTrue() + assertThat(draggable.onDragStoppedCalled).isFalse() + + rule.onRoot().performTouchInput { moveBy(50f.toOffset()) } + + assertThat(draggable.onDragStoppedCalled).isFalse() + + consumeScrolls = false + rule.onRoot().performTouchInput { moveBy(1f.toOffset()) } + + assertThat(draggable.onDragStoppedCalled).isTrue() + } + private fun ComposeContentTestRule.setContentWithTouchSlop( content: @Composable () -> Unit ): Float { @@ -1027,6 +1060,7 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw }, private val shouldConsumeNestedPostScroll: (Float) -> Boolean = { true }, private val shouldConsumeNestedPreScroll: (Float) -> Boolean = { false }, + private val autoStopNestedDrags: Boolean = false, ) : NestedDraggable { var shouldStartDrag = true var onDragStartedCalled = false @@ -1056,6 +1090,8 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw onDragStarted.invoke(position, sign) return object : NestedDraggable.Controller { + override val autoStopNestedDrags: Boolean = this@TestDraggable.autoStopNestedDrags + override fun onDrag(delta: Float): Float { onDragCalled = true onDragDelta += delta |