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

commonMain.androidx.compose.foundation.draganddrop.DragAndDropTarget.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

There is a newer version: 1.7.1
Show newest version
/*
 * 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.DragAndDropEvent
import androidx.compose.ui.draganddrop.DragAndDropModifierNode
import androidx.compose.ui.draganddrop.DragAndDropTarget
import androidx.compose.ui.node.DelegatingNode
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.platform.InspectorInfo

/**
 * A modifier that allows for receiving from a drag and drop gesture.
 *
 * Learn how to use [Modifier.dragAndDropTarget] to receive drag and drop events from inside your
 * app or from other apps:
 *
 * @sample androidx.compose.foundation.samples.TextDragAndDropTargetSample
 *
 * @param shouldStartDragAndDrop Allows the Composable to decide if it wants to receive from
 * a given drag and drop session by inspecting the [DragAndDropEvent] that started the session.
 *
 * @param target The [DragAndDropTarget] that will receive events for a given drag and drop
 * session.
 *
 * All drag and drop target modifiers in the hierarchy will be given an opportunity to participate
 * in a given drag and drop session via [shouldStartDragAndDrop].
 *
 * @see [DragAndDropModifierNode.acceptDragAndDropTransfer]
 */
@ExperimentalFoundationApi
fun Modifier.dragAndDropTarget(
    shouldStartDragAndDrop: (startEvent: DragAndDropEvent) -> Boolean,
    target: DragAndDropTarget,
): Modifier {
    return this then DropTargetElement(
        target = target,
        shouldStartDragAndDrop = shouldStartDragAndDrop,
    )
}

@ExperimentalFoundationApi
private class DropTargetElement(
    val shouldStartDragAndDrop: (event: DragAndDropEvent) -> Boolean,
    val target: DragAndDropTarget,
) : ModifierNodeElement() {
    override fun create() = DragAndDropTargetNode(
        target = target,
        shouldStartDragAndDrop = shouldStartDragAndDrop,
    )

    override fun update(node: DragAndDropTargetNode) = node.update(
        target = target,
        shouldStartDragAndDrop = shouldStartDragAndDrop
    )

    override fun InspectorInfo.inspectableProperties() {
        name = "dropTarget"
        properties["target"] = target
        properties["shouldStartDragAndDrop"] = shouldStartDragAndDrop
    }

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is DropTargetElement) return false
        if (target != other.target) return false

        return shouldStartDragAndDrop === other.shouldStartDragAndDrop
    }

    override fun hashCode(): Int {
        var result = target.hashCode()
        result = 31 * result + shouldStartDragAndDrop.hashCode()
        return result
    }
}

@ExperimentalFoundationApi
private class DragAndDropTargetNode(
    private var shouldStartDragAndDrop: (event: DragAndDropEvent) -> Boolean,
    private var target: DragAndDropTarget
) : DelegatingNode() {

    private var dragAndDropNode: DragAndDropModifierNode? = null

    override fun onAttach() {
        createAndAttachDragAndDropModifierNode()
    }

    fun update(
        shouldStartDragAndDrop: (event: DragAndDropEvent) -> Boolean,
        target: DragAndDropTarget
    ) {
        this.shouldStartDragAndDrop = shouldStartDragAndDrop
        if (target != this.target) {
            dragAndDropNode?.let { undelegate(it) }
            this.target = target
            createAndAttachDragAndDropModifierNode()
        }
    }

    override fun onDetach() {
        undelegate(dragAndDropNode!!)
    }

    private fun createAndAttachDragAndDropModifierNode() {
        dragAndDropNode = delegate(
            DragAndDropModifierNode(
                // We wrap the this.shouldStartDragAndDrop invocation in a lambda as it might change over
                // time, and updates to shouldStartDragAndDrop are not destructive.
                shouldStartDragAndDrop = { this.shouldStartDragAndDrop(it) },
                target = this.target
            )
        )
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy