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

commonMain.androidx.compose.foundation.draganddrop.DragAndDropSource.kt Maven / Gradle / Ivy

/*
 * Copyright 2023 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
 *
 *     https://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.draganddrop

import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draganddrop.DragAndDropModifierNode
import androidx.compose.ui.draganddrop.DragAndDropTransferData
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.input.pointer.PointerInputScope
import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode
import androidx.compose.ui.node.DelegatingNode
import androidx.compose.ui.node.LayoutAwareModifierNode
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.toSize

/**
 * A scope that allows for the detection of the start of a drag and drop gesture, and subsequently
 * starting a drag and drop session.
 */
@ExperimentalFoundationApi
interface DragAndDropSourceScope : PointerInputScope {
    /**
     * Starts a drag and drop session with [transferData] as the data to be transferred on gesture
     * completion
     */
    fun startTransfer(transferData: DragAndDropTransferData)
}

/**
 * A Modifier that allows an element it is applied to to be treated like a source for
 * drag and drop operations.
 *
 * Learn how to use [Modifier.dragAndDropSource] while providing a custom drag shadow:
 * @sample androidx.compose.foundation.samples.DragAndDropSourceWithColoredDragShadowSample
 *
 * @param drawDragDecoration provides the visual representation of the item dragged during the
 * drag and drop gesture.
 * @param block A lambda with a [DragAndDropSourceScope] as a receiver
 * which provides a [PointerInputScope] to detect the drag gesture, after which a drag and drop
 * gesture can be started with [DragAndDropSourceScope.startTransfer].
 *
 */
@ExperimentalFoundationApi
fun Modifier.dragAndDropSource(
    drawDragDecoration: DrawScope.() -> Unit,
    block: suspend DragAndDropSourceScope.() -> Unit
): Modifier {
    return this then DragAndDropSourceElement(
        drawDragDecoration = drawDragDecoration,
        dragAndDropSourceHandler = block,
    )
}

@ExperimentalFoundationApi
private data class DragAndDropSourceElement(
    /**
     * @see Modifier.dragAndDropSource
     */
    val drawDragDecoration: DrawScope.() -> Unit,
    /**
     * @see Modifier.dragAndDropSource
     */
    val dragAndDropSourceHandler: suspend DragAndDropSourceScope.() -> Unit
) : ModifierNodeElement() {
    override fun create() = DragAndDropSourceNode(
        drawDragDecoration = drawDragDecoration,
        dragAndDropSourceHandler = dragAndDropSourceHandler,
    )

    override fun update(node: DragAndDropSourceNode) = with(node) {
        drawDragDecoration = [email protected]
        dragAndDropSourceHandler = [email protected]
    }

    override fun InspectorInfo.inspectableProperties() {
        name = "dragSource"
        properties["drawDragDecoration"] = drawDragDecoration
        properties["dragAndDropSourceHandler"] = dragAndDropSourceHandler
    }
}

@ExperimentalFoundationApi
internal class DragAndDropSourceNode(
    var drawDragDecoration: DrawScope.() -> Unit,
    var dragAndDropSourceHandler: suspend DragAndDropSourceScope.() -> Unit
) : DelegatingNode(),
    LayoutAwareModifierNode {

    private var size: IntSize = IntSize.Zero

    init {
        val dragAndDropModifierNode = delegate(
            DragAndDropModifierNode()
        )

        delegate(
            SuspendingPointerInputModifierNode {
                dragAndDropSourceHandler(
                    object : DragAndDropSourceScope, PointerInputScope by this {
                        override fun startTransfer(transferData: DragAndDropTransferData) =
                            dragAndDropModifierNode.drag(
                                transferData = transferData,
                                decorationSize = size.toSize(),
                                drawDragDecoration = drawDragDecoration
                            )
                    }
                )
            }
        )
    }

    override fun onRemeasured(size: IntSize) {
        this.size = size
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy