Loading packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt +31 −3 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import androidx.compose.ui.input.nestedscroll.nestedScrollModifierNode import androidx.compose.ui.input.pointer.AwaitPointerEventScope import androidx.compose.ui.input.pointer.PointerEvent import androidx.compose.ui.input.pointer.PointerEventPass import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.PointerId import androidx.compose.ui.input.pointer.PointerInputChange import androidx.compose.ui.input.pointer.PointerInputScope Loading Loading @@ -168,6 +169,12 @@ private class NestedDraggableNode( CompositionLocalConsumerModifierNode, OrientationAware { private val nestedScrollDispatcher = NestedScrollDispatcher() private var trackWheelScroll: SuspendingPointerInputModifierNode? = null set(value) { field?.let { undelegate(it) } field = value?.also { delegate(it) } } private var trackDownPositionDelegate: SuspendingPointerInputModifierNode? = null set(value) { field?.let { undelegate(it) } Loading @@ -189,6 +196,7 @@ private class NestedDraggableNode( * This is use to track the started position of a drag started on a nested scrollable. */ private var lastFirstDown: Offset? = null private var lastEventWasScrollWheel: Boolean = false /** The pointers currently down, in order of which they were done and mapping to their type. */ private val pointersDown = linkedMapOf<PointerId, PointerType>() Loading Loading @@ -218,8 +226,11 @@ private class NestedDraggableNode( nestedScrollController?.ensureOnDragStoppedIsCalled() nestedScrollController = null if (!enabled && trackDownPositionDelegate != null) { if (!enabled && trackWheelScroll != null) { check(trackDownPositionDelegate != null) check(detectDragsDelegate != null) trackWheelScroll = null trackDownPositionDelegate = null detectDragsDelegate = null } Loading @@ -232,17 +243,22 @@ private class NestedDraggableNode( ) { if (!enabled) return if (trackDownPositionDelegate == null) { if (trackWheelScroll == null) { check(trackDownPositionDelegate == null) check(detectDragsDelegate == null) trackWheelScroll = SuspendingPointerInputModifierNode { trackWheelScroll() } trackDownPositionDelegate = SuspendingPointerInputModifierNode { trackDownPosition() } detectDragsDelegate = SuspendingPointerInputModifierNode { detectDrags() } } checkNotNull(trackWheelScroll).onPointerEvent(pointerEvent, pass, bounds) checkNotNull(trackDownPositionDelegate).onPointerEvent(pointerEvent, pass, bounds) checkNotNull(detectDragsDelegate).onPointerEvent(pointerEvent, pass, bounds) } override fun onCancelPointerInput() { trackWheelScroll?.onCancelPointerInput() trackDownPositionDelegate?.onCancelPointerInput() detectDragsDelegate?.onCancelPointerInput() } Loading Loading @@ -457,6 +473,13 @@ private class NestedDraggableNode( * =============================== */ private suspend fun PointerInputScope.trackWheelScroll() { awaitEachGesture { val event = awaitPointerEvent(pass = PointerEventPass.Initial) lastEventWasScrollWheel = event.type == PointerEventType.Scroll } } private suspend fun PointerInputScope.trackDownPosition() { awaitEachGesture { try { Loading Loading @@ -501,7 +524,12 @@ private class NestedDraggableNode( } val sign = offset.sign if (nestedScrollController == null && draggable.shouldConsumeNestedScroll(sign)) { if ( nestedScrollController == null && // TODO(b/388231324): Remove this. !lastEventWasScrollWheel && draggable.shouldConsumeNestedScroll(sign) ) { val startedPosition = checkNotNull(lastFirstDown) { "lastFirstDown is not set" } // TODO(b/382665591): Ensure that there is at least one pointer down. Loading packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt +30 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.compose.gesture import androidx.compose.foundation.ScrollState import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.rememberScrollableState import androidx.compose.foundation.gestures.scrollable import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize Loading @@ -35,6 +37,7 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.pointer.PointerInputChange import androidx.compose.ui.input.pointer.PointerType import androidx.compose.ui.platform.LocalViewConfiguration import androidx.compose.ui.test.ScrollWheel import androidx.compose.ui.test.junit4.ComposeContentTestRule import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onRoot Loading Loading @@ -710,6 +713,33 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw rule.onRoot().performTouchInput { down(center) } } @Test // TODO(b/388231324): Remove this. fun nestedScrollWithMouseWheelIsIgnored() { val draggable = TestDraggable() val touchSlop = rule.setContentWithTouchSlop { Box( Modifier.fillMaxSize() .nestedDraggable(draggable, orientation) .scrollable(rememberScrollableState { 0f }, orientation) ) } rule.onRoot().performMouseInput { enter(center) scroll( touchSlop + 1f, when (orientation) { Orientation.Horizontal -> ScrollWheel.Horizontal Orientation.Vertical -> ScrollWheel.Vertical }, ) } assertThat(draggable.onDragStartedCalled).isFalse() } private fun ComposeContentTestRule.setContentWithTouchSlop( content: @Composable () -> Unit ): Float { Loading Loading
packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt +31 −3 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import androidx.compose.ui.input.nestedscroll.nestedScrollModifierNode import androidx.compose.ui.input.pointer.AwaitPointerEventScope import androidx.compose.ui.input.pointer.PointerEvent import androidx.compose.ui.input.pointer.PointerEventPass import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.PointerId import androidx.compose.ui.input.pointer.PointerInputChange import androidx.compose.ui.input.pointer.PointerInputScope Loading Loading @@ -168,6 +169,12 @@ private class NestedDraggableNode( CompositionLocalConsumerModifierNode, OrientationAware { private val nestedScrollDispatcher = NestedScrollDispatcher() private var trackWheelScroll: SuspendingPointerInputModifierNode? = null set(value) { field?.let { undelegate(it) } field = value?.also { delegate(it) } } private var trackDownPositionDelegate: SuspendingPointerInputModifierNode? = null set(value) { field?.let { undelegate(it) } Loading @@ -189,6 +196,7 @@ private class NestedDraggableNode( * This is use to track the started position of a drag started on a nested scrollable. */ private var lastFirstDown: Offset? = null private var lastEventWasScrollWheel: Boolean = false /** The pointers currently down, in order of which they were done and mapping to their type. */ private val pointersDown = linkedMapOf<PointerId, PointerType>() Loading Loading @@ -218,8 +226,11 @@ private class NestedDraggableNode( nestedScrollController?.ensureOnDragStoppedIsCalled() nestedScrollController = null if (!enabled && trackDownPositionDelegate != null) { if (!enabled && trackWheelScroll != null) { check(trackDownPositionDelegate != null) check(detectDragsDelegate != null) trackWheelScroll = null trackDownPositionDelegate = null detectDragsDelegate = null } Loading @@ -232,17 +243,22 @@ private class NestedDraggableNode( ) { if (!enabled) return if (trackDownPositionDelegate == null) { if (trackWheelScroll == null) { check(trackDownPositionDelegate == null) check(detectDragsDelegate == null) trackWheelScroll = SuspendingPointerInputModifierNode { trackWheelScroll() } trackDownPositionDelegate = SuspendingPointerInputModifierNode { trackDownPosition() } detectDragsDelegate = SuspendingPointerInputModifierNode { detectDrags() } } checkNotNull(trackWheelScroll).onPointerEvent(pointerEvent, pass, bounds) checkNotNull(trackDownPositionDelegate).onPointerEvent(pointerEvent, pass, bounds) checkNotNull(detectDragsDelegate).onPointerEvent(pointerEvent, pass, bounds) } override fun onCancelPointerInput() { trackWheelScroll?.onCancelPointerInput() trackDownPositionDelegate?.onCancelPointerInput() detectDragsDelegate?.onCancelPointerInput() } Loading Loading @@ -457,6 +473,13 @@ private class NestedDraggableNode( * =============================== */ private suspend fun PointerInputScope.trackWheelScroll() { awaitEachGesture { val event = awaitPointerEvent(pass = PointerEventPass.Initial) lastEventWasScrollWheel = event.type == PointerEventType.Scroll } } private suspend fun PointerInputScope.trackDownPosition() { awaitEachGesture { try { Loading Loading @@ -501,7 +524,12 @@ private class NestedDraggableNode( } val sign = offset.sign if (nestedScrollController == null && draggable.shouldConsumeNestedScroll(sign)) { if ( nestedScrollController == null && // TODO(b/388231324): Remove this. !lastEventWasScrollWheel && draggable.shouldConsumeNestedScroll(sign) ) { val startedPosition = checkNotNull(lastFirstDown) { "lastFirstDown is not set" } // TODO(b/382665591): Ensure that there is at least one pointer down. Loading
packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt +30 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.compose.gesture import androidx.compose.foundation.ScrollState import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.rememberScrollableState import androidx.compose.foundation.gestures.scrollable import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize Loading @@ -35,6 +37,7 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.pointer.PointerInputChange import androidx.compose.ui.input.pointer.PointerType import androidx.compose.ui.platform.LocalViewConfiguration import androidx.compose.ui.test.ScrollWheel import androidx.compose.ui.test.junit4.ComposeContentTestRule import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onRoot Loading Loading @@ -710,6 +713,33 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw rule.onRoot().performTouchInput { down(center) } } @Test // TODO(b/388231324): Remove this. fun nestedScrollWithMouseWheelIsIgnored() { val draggable = TestDraggable() val touchSlop = rule.setContentWithTouchSlop { Box( Modifier.fillMaxSize() .nestedDraggable(draggable, orientation) .scrollable(rememberScrollableState { 0f }, orientation) ) } rule.onRoot().performMouseInput { enter(center) scroll( touchSlop + 1f, when (orientation) { Orientation.Horizontal -> ScrollWheel.Horizontal Orientation.Vertical -> ScrollWheel.Vertical }, ) } assertThat(draggable.onDragStartedCalled).isFalse() } private fun ComposeContentTestRule.setContentWithTouchSlop( content: @Composable () -> Unit ): Float { Loading