Skip to content

Commit 6d2e040

Browse files
authored
Merge pull request #35 from MohamedRejeb/0.2.x
Support nested DraggableItem
2 parents bc7e587 + fe19853 commit 6d2e040

File tree

6 files changed

+105
-22
lines changed

6 files changed

+105
-22
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2023, Mohamed Ben Rejeb and the Compose Dnd project contributors
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+
package com.mohamedrejeb.compose.dnd
17+
18+
import androidx.compose.runtime.staticCompositionLocalOf
19+
20+
internal interface DragAndDropInfo {
21+
/**
22+
* Whether the current composition is a shadow composition or not.
23+
*/
24+
val isShadow: Boolean
25+
}
26+
27+
internal class DragAndDropInfoImpl(
28+
override val isShadow: Boolean,
29+
) : DragAndDropInfo
30+
31+
internal val LocalDragAndDropInfo = staticCompositionLocalOf<DragAndDropInfo> {
32+
DragAndDropInfoImpl(isShadow = false)
33+
}

compose-dnd/src/commonMain/kotlin/com/mohamedrejeb/compose/dnd/DragAndDropState.kt

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -152,15 +152,10 @@ class DragAndDropState<T>(
152152
* @param offset - offset of the drag start position
153153
*/
154154
internal suspend fun handleDragStart(
155+
key: Any,
155156
offset: Offset,
156157
) = coroutineScope {
157-
val draggableItemState = draggableItemMap.values.find {
158-
MathUtils.isPointInRectangle(
159-
point = offset,
160-
topLeft = it.positionInRoot,
161-
size = it.size,
162-
)
163-
} ?: return@coroutineScope
158+
val draggableItemState = draggableItemMap[key] ?: return@coroutineScope
164159

165160
launch {
166161
dragPositionAnimatable.snapTo(Offset.Zero)

compose-dnd/src/commonMain/kotlin/com/mohamedrejeb/compose/dnd/drag/CoreDraggableItem.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import androidx.compose.ui.layout.onSizeChanged
3030
import androidx.compose.ui.layout.positionInRoot
3131
import androidx.compose.ui.unit.toSize
3232
import com.mohamedrejeb.compose.dnd.DragAndDropState
33+
import com.mohamedrejeb.compose.dnd.LocalDragAndDropInfo
3334
import com.mohamedrejeb.compose.dnd.gesture.detectDragStartGesture
3435

3536
/**
@@ -64,6 +65,18 @@ internal fun <T> CoreDraggableItem(
6465
draggableContent: @Composable () -> Unit,
6566
content: @Composable () -> Unit,
6667
) {
68+
val dndInfo = LocalDragAndDropInfo.current
69+
70+
if (dndInfo.isShadow) {
71+
Box(
72+
modifier = modifier
73+
) {
74+
content()
75+
}
76+
77+
return
78+
}
79+
6780
val draggableItemState = remember(key) {
6881
DraggableItemState(
6982
key = key,
@@ -123,12 +136,14 @@ internal fun <T> CoreDraggableItem(
123136
enabled,
124137
state,
125138
state.enabled,
139+
draggableItemState,
126140
dragAfterLongPress,
127141
requireFirstDownUnconsumed,
128142
) {
129143
detectDragStartGesture(
130144
key = key,
131145
state = state,
146+
draggableItemState = draggableItemState,
132147
enabled = enabled && state.enabled,
133148
dragAfterLongPress = dragAfterLongPress,
134149
requireFirstDownUnconsumed = requireFirstDownUnconsumed,

compose-dnd/src/commonMain/kotlin/com/mohamedrejeb/compose/dnd/drag/DraggedItemShadow.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package com.mohamedrejeb.compose.dnd.drag
1818
import androidx.compose.foundation.layout.Box
1919
import androidx.compose.foundation.layout.size
2020
import androidx.compose.runtime.Composable
21+
import androidx.compose.runtime.CompositionLocalProvider
2122
import androidx.compose.runtime.mutableStateOf
2223
import androidx.compose.runtime.remember
2324
import androidx.compose.ui.Modifier
@@ -26,7 +27,9 @@ import androidx.compose.ui.graphics.graphicsLayer
2627
import androidx.compose.ui.layout.onPlaced
2728
import androidx.compose.ui.layout.positionInRoot
2829
import androidx.compose.ui.platform.LocalDensity
30+
import com.mohamedrejeb.compose.dnd.DragAndDropInfoImpl
2931
import com.mohamedrejeb.compose.dnd.DragAndDropState
32+
import com.mohamedrejeb.compose.dnd.LocalDragAndDropInfo
3033

3134
@Composable
3235
internal fun <T> DraggedItemShadow(
@@ -54,6 +57,10 @@ internal fun <T> DraggedItemShadow(
5457
translationY = dragPositionY - draggedItemPositionInRoot.value.y
5558
},
5659
) {
57-
state.currentDraggableItem?.content?.invoke()
60+
CompositionLocalProvider(
61+
LocalDragAndDropInfo provides DragAndDropInfoImpl(isShadow = true)
62+
) {
63+
state.currentDraggableItem?.content?.invoke()
64+
}
5865
}
5966
}

compose-dnd/src/commonMain/kotlin/com/mohamedrejeb/compose/dnd/drop/DropTarget.kt

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,15 @@ import androidx.compose.ui.geometry.Offset
2121
import androidx.compose.ui.geometry.Size
2222
import androidx.compose.ui.layout.LayoutCoordinates
2323
import androidx.compose.ui.layout.positionInRoot
24+
import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
2425
import androidx.compose.ui.node.LayoutAwareModifierNode
2526
import androidx.compose.ui.node.ModifierNodeElement
27+
import androidx.compose.ui.node.currentValueOf
2628
import androidx.compose.ui.platform.InspectorInfo
2729
import androidx.compose.ui.unit.IntSize
2830
import androidx.compose.ui.unit.toSize
2931
import com.mohamedrejeb.compose.dnd.DragAndDropState
32+
import com.mohamedrejeb.compose.dnd.LocalDragAndDropInfo
3033
import com.mohamedrejeb.compose.dnd.drag.DraggedItemState
3134

3235
/**
@@ -54,17 +57,18 @@ fun <T> Modifier.dropTarget(
5457
onDrop: (state: DraggedItemState<T>) -> Unit = {},
5558
onDragEnter: (state: DraggedItemState<T>) -> Unit = {},
5659
onDragExit: (state: DraggedItemState<T>) -> Unit = {},
57-
): Modifier = this then DropTargetNodeElement(
58-
key = key,
59-
state = state,
60-
zIndex = zIndex,
61-
dropAlignment = dropAlignment,
62-
dropOffset = dropOffset,
63-
dropAnimationEnabled = dropAnimationEnabled,
64-
onDrop = onDrop,
65-
onDragEnter = onDragEnter,
66-
onDragExit = onDragExit,
67-
)
60+
): Modifier =
61+
this then DropTargetNodeElement(
62+
key = key,
63+
state = state,
64+
zIndex = zIndex,
65+
dropAlignment = dropAlignment,
66+
dropOffset = dropOffset,
67+
dropAnimationEnabled = dropAnimationEnabled,
68+
onDrop = onDrop,
69+
onDragEnter = onDragEnter,
70+
onDragExit = onDragExit,
71+
)
6872

6973
private data class DropTargetNodeElement<T>(
7074
val key: Any,
@@ -133,15 +137,27 @@ private data class DropTargetNodeElement<T>(
133137
private data class DropTargetNode<T>(
134138
val dropTargetState: DropTargetState<T>,
135139
var state: DragAndDropState<T>,
136-
) : Modifier.Node(), LayoutAwareModifierNode {
140+
) : Modifier.Node(), LayoutAwareModifierNode, CompositionLocalConsumerModifierNode {
137141

138142
private val key get() = dropTargetState.key
139143

144+
private var isShadow = false
145+
140146
override fun onAttach() {
147+
isShadow = currentValueOf(LocalDragAndDropInfo).isShadow
148+
149+
if (isShadow) {
150+
return
151+
}
152+
141153
state.addDropTarget(dropTargetState)
142154
}
143155

144156
override fun onPlaced(coordinates: LayoutCoordinates) {
157+
if (isShadow) {
158+
return
159+
}
160+
145161
state.addDropTarget(dropTargetState)
146162

147163
val size = coordinates.size.toSize()
@@ -152,14 +168,26 @@ private data class DropTargetNode<T>(
152168
}
153169

154170
override fun onRemeasured(size: IntSize) {
171+
if (isShadow) {
172+
return
173+
}
174+
155175
dropTargetState.size = size.toSize()
156176
}
157177

158178
override fun onReset() {
179+
if (isShadow) {
180+
return
181+
}
182+
159183
state.removeDropTarget(key)
160184
}
161185

162186
override fun onDetach() {
187+
if (isShadow) {
188+
return
189+
}
190+
163191
state.removeDropTarget(key)
164192
}
165193
}

compose-dnd/src/commonMain/kotlin/com/mohamedrejeb/compose/dnd/gesture/DragGesture.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@ import androidx.compose.ui.input.pointer.PointerEventPass
2222
import androidx.compose.ui.input.pointer.PointerInputChange
2323
import androidx.compose.ui.input.pointer.PointerInputScope
2424
import com.mohamedrejeb.compose.dnd.DragAndDropState
25+
import com.mohamedrejeb.compose.dnd.drag.DraggableItemState
2526
import com.mohamedrejeb.compose.dnd.utils.awaitPointerSlopOrCancellation
2627
import kotlinx.coroutines.coroutineScope
2728
import kotlinx.coroutines.launch
2829

2930
internal suspend fun <T> PointerInputScope.detectDragStartGesture(
3031
key: Any,
3132
state: DragAndDropState<T>,
33+
draggableItemState: DraggableItemState<T>,
3234
enabled: Boolean,
3335
dragAfterLongPress: Boolean,
3436
requireFirstDownUnconsumed: Boolean,
@@ -53,10 +55,13 @@ internal suspend fun <T> PointerInputScope.detectDragStartGesture(
5355
}
5456

5557
if (drag != null) {
56-
val draggableItemState = state.draggableItemMap[key] ?: return@awaitEachGesture
58+
val draggableItemState = state.draggableItemMap.getOrPut(key) { draggableItemState }
5759

5860
launch {
59-
state.handleDragStart(drag.position + draggableItemState.positionInRoot)
61+
state.handleDragStart(
62+
key = key,
63+
offset = drag.position + draggableItemState.positionInRoot
64+
)
6065
}
6166

6267
state.pointerId = drag.id

0 commit comments

Comments
 (0)