1 /*
2  * Copyright 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.compose.foundation.content
18 
19 import androidx.compose.foundation.ExperimentalFoundationApi
20 import androidx.compose.foundation.draganddrop.dragAndDropTarget
21 
22 /**
23  * A set of callbacks for [contentReceiver] modifier to get information about certain Drag-and-Drop
24  * state changes, as well as receiving the payload carrying [TransferableContent].
25  *
26  * [contentReceiver]'s drop target behaves a little different compared to the regular
27  * [dragAndDropTarget] modifier. When two [contentReceiver] modifiers are nested on the composition
28  * tree, drop area of the parent node encapsulates drop area of the child node, meaning that they
29  * wouldn't be mutually exclusive like the regular [dragAndDropTarget] nesting. This becomes useful
30  * when you want to extend the drop area around a TextField by wrapping the TextField in a larger
31  * component with a [contentReceiver] modifier. We can guarantee that the container won't receive
32  * dragExit event when the dragging item moves over to TextField.
33  *
34  * Let's assume we have two [contentReceiver] boxes named A and B where B is a child of A, aligned
35  * to bottom end.
36  * ---------
37  * | A | | |---| | | B |
38  * ---------
39  * When a dragging item moves over to A from left, then over to B, then starts moving up and goes
40  * back to A leaving B, then finally leaves them both, the following would be the list of expected
41  * [ReceiveContentListener] calls in order to both nodes.
42  * - A#onStart
43  * - B#onStart
44  * - A#onEnter
45  * - B#onEnter
46  * - B#onExit
47  * - A#onExit
48  * - B#onEnd
49  * - A#onEnd
50  *
51  * The interesting part in this order of calls is that A does not receive an exit event when the
52  * item moves over to B. This is different than what would happen if you were to use
53  * [dragAndDropTarget] modifier because semantically [contentReceiver] works as a chain of nodes. If
54  * the item were to be dropped on B, its [onReceive] chain would also call A's [onReceive] with
55  * what's left from B.
56  */
57 @ExperimentalFoundationApi
interfacenull58 fun interface ReceiveContentListener {
59 
60     /**
61      * Optional callback that's called when a dragging session starts. All [contentReceiver] nodes
62      * in the current composition tree receives this callback immediately.
63      */
64     fun onDragStart() = Unit
65 
66     /**
67      * Optional callback that's called when a dragging session ends by either successful drop, or
68      * cancellation. All [contentReceiver] nodes in the current composition tree receives this
69      * callback immediately.
70      */
71     fun onDragEnd() = Unit
72 
73     /** Optional callback that's called when a dragging item moves into this node's coordinates. */
74     fun onDragEnter() = Unit
75 
76     /**
77      * Optional callback that's called when a dragging item moves out of this node's coordinates.
78      */
79     fun onDragExit() = Unit
80 
81     /**
82      * Callback that's triggered when a content is successfully committed.
83      *
84      * @return An optional [TransferableContent] that contains the ignored parts of the received
85      *   [TransferableContent] by this node. The remaining [TransferableContent] first will be sent
86      *   to to the closest ancestor [contentReceiver] modifier. This chain will continue until
87      *   there's no ancestor modifier left, or [TransferableContent] is fully consumed. After, the
88      *   source subsystem that created the original [TransferableContent] and initiated the chain
89      *   will receive any remaining items to apply its default behavior. For example a text editor
90      *   that receives content by DragAndDrop should insert the remaining text from the receive
91      *   chain to the drop position.
92      */
93     fun onReceive(transferableContent: TransferableContent): TransferableContent?
94 }
95