How can I make `detectDragGesturesAfterLongPress` ...
# compose
z
How can I make
detectDragGesturesAfterLongPress
where I'm able to handle the gesture differently depending if its horizontal or vertical? there's handlers for those directions but neither of them are after a long press
t
Copy code
/** 
  * Gesture detector that waits for pointer down, then a long press, then any horizontal movement. 
  * 
  * @param onDragStart Called when a long press is detected. 
  * @param onDragEnd Called after all pointers are up. 
  * @param onDragCancel Called after another gesture has consumed pointer input. 
  * @param onHorizontalDrag Called for each drag event with the last known pointer position. 
  */ 
 suspend fun PointerInputScope.detectHorizontalDragGesturesAfterLongPress( 
     onDragStart: (Offset) -> Unit = { }, 
     onDragEnd: () -> Unit = { }, 
     onDragCancel: () -> Unit = { }, 
     onHorizontalDrag: (change: PointerInputChange, dragAmount: Float) -> Unit 
 ) { 
     forEachGesture { 
         val down = awaitPointerEventScope { 
             awaitFirstDown(requireUnconsumed = false) 
         } 
         try { 
             val drag = awaitLongPressOrCancellation(down) 
             if (drag != null) { 
                 onDragStart.invoke(drag.position) 
  
                 awaitPointerEventScope { 
                     if ( 
                         horizontalDrag(drag.id) { 
                             onHorizontalDrag(it, it.positionChange().x) 
                             it.consumePositionChange() 
                         } 
                     ) { 
                         // Consume up event if we quit the drag. 
                         currentEvent.changes.fastForEach { 
                             if (it.changedToUp()) { 
                                 it.consumeDownChange() 
                             } 
                         } 
                         onDragEnd() 
                     } else { 
                         onDragCancel() 
                     } 
                 } 
             } 
         } catch (c: CancellationException) { 
             onDragCancel() 
             throw c 
         } 
     } 
 } 
  
 private suspend fun PointerInputScope.awaitLongPressOrCancellation( 
     initialDown: PointerInputChange 
 ): PointerInputChange? { 
     var longPress: PointerInputChange? = null 
     var currentDown = initialDown 
     val longPressTimeout = viewConfiguration.longPressTimeoutMillis 
     return try { 
         // wait for first tap up or long press 
         withTimeout(longPressTimeout) { 
             awaitPointerEventScope { 
                 var finished = false 
                 while (!finished) { 
                     val event = awaitPointerEvent(PointerEventPass.Main) 
                     if (event.changes.fastAll { it.changedToUpIgnoreConsumed() }) { 
                         // All pointers are up 
                         finished = true 
                     } 
  
                     if ( 
                         event.changes.fastAny { 
                             it.consumed.downChange || it.isOutOfBounds(size, extendedTouchPadding) 
                         } 
                     ) { 
                         finished = true // Canceled 
                     } 
  
                     // Check for cancel by position consumption. We can look on the Final pass of 
                     // the existing pointer event because it comes after the Main pass we checked 
                     // above. 
                     val consumeCheck = awaitPointerEvent(PointerEventPass.Final) 
                     if (consumeCheck.changes.fastAny { it.positionChangeConsumed() }) { 
                         finished = true 
                     } 
                     if (!event.isPointerUp(currentDown.id)) { 
                         longPress = event.changes.fastFirstOrNull { it.id == currentDown.id } 
                     } else { 
                         val newPressed = event.changes.fastFirstOrNull { it.pressed } 
                         if (newPressed != null) { 
                             currentDown = newPressed 
                             longPress = currentDown 
                         } else { 
                             // should technically never happen as we checked it above 
                             finished = true 
                         } 
                     } 
                 } 
             } 
         } 
         null 
     } catch (_: TimeoutCancellationException) { 
         longPress ?: initialDown 
     } 
 } 
  
 private fun PointerEvent.isPointerUp(pointerId: PointerId): Boolean = 
     changes.fastFirstOrNull { it.id == pointerId }?.pressed != true
Easily adaptable to detect vertical drags, or you could add a
onVerticalDrag
callback
awaitLongPressOrCancellation
is copied from compose-ui because it's internal there
z
I'll try this when I get a chance