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