All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.androidx.compose.foundation.text.LongPressTextDragObserver.kt Maven / Gradle / Ivy

Go to download

Higher level abstractions of the Compose UI primitives. This library is design system agnostic, providing the high-level building blocks for both application and design-system developers

The newest version!
/*
 * 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.text

import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.pointer.PointerInputScope
import androidx.compose.ui.util.fastAny
import kotlin.coroutines.cancellation.CancellationException
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch

internal interface TextDragObserver {
    /**
     * Called as soon as a down event is received. If the pointer eventually moves while remaining
     * down, a drag gesture may be started. After this method:
     * - [onUp] will always be called eventually, once the pointer is released.
     * - [onStart] _may_ be called, if there is a drag that exceeds touch slop.
     *
     * This method will not be called before [onStart] in the case when a down event happens that
     * may not result in a drag, e.g. on the down before a long-press that starts a selection.
     */
    fun onDown(point: Offset)

    /** Called after [onDown] if an up event is received without dragging. */
    fun onUp()

    /**
     * Called once a drag gesture has started, which means touch slop has been exceeded. [onDown]
     * _may_ be called before this method if the down event could not have started a different
     * gesture.
     */
    fun onStart(startPoint: Offset)

    fun onDrag(delta: Offset)

    fun onStop()

    fun onCancel()
}

internal suspend fun PointerInputScope.detectDragGesturesAfterLongPressWithObserver(
    observer: TextDragObserver
) =
    detectDragGesturesAfterLongPress(
        onDragEnd = { observer.onStop() },
        onDrag = { _, offset -> observer.onDrag(offset) },
        onDragStart = { observer.onStart(it) },
        onDragCancel = { observer.onCancel() }
    )

/**
 * Detects gesture events for a [TextDragObserver], including both initial down events and drag
 * events.
 */
internal suspend fun PointerInputScope.detectDownAndDragGesturesWithObserver(
    observer: TextDragObserver
) {
    coroutineScope {
        launch(start = CoroutineStart.UNDISPATCHED) { detectPreDragGesturesWithObserver(observer) }
        launch(start = CoroutineStart.UNDISPATCHED) { detectDragGesturesWithObserver(observer) }
            .invokeOnCompletion {
                // Otherwise observer won't be notified if
                // composable was disposed before the drag cancellation
                if (it is CancellationException){
                    observer.onCancel()
                }
            }
    }
}

/** Detects initial down events and calls [TextDragObserver.onDown] and [TextDragObserver.onUp]. */
private suspend fun PointerInputScope.detectPreDragGesturesWithObserver(
    observer: TextDragObserver
) {
    awaitEachGesture {
        val down = awaitFirstDown(requireUnconsumed = false)
        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()
    }
}

/** Detects drag gestures for a [TextDragObserver]. */
private suspend fun PointerInputScope.detectDragGesturesWithObserver(observer: TextDragObserver) {
    detectDragGestures(
        onDragEnd = { observer.onStop() },
        onDrag = { _, offset -> observer.onDrag(offset) },
        onDragStart = { observer.onStart(it) },
        onDragCancel = { observer.onCancel() }
    )
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy