@@ -71,37 +71,46 @@ public fun <Item> Modifier.gridDragSelect(
71
71
if (! enableHaptics) null
72
72
else hapticFeedback ? : GridDragSelectDefaults .hapticsFeedback
73
73
74
+ val isSelected: (Item ) -> Boolean = { item ->
75
+ state.selected.contains(item)
76
+ }
77
+
74
78
pointerInput(Unit ) {
75
79
detectDragGesturesAfterLongPress(
76
80
onDragStart = { offset ->
77
81
state.gridState.itemIndexAtPosition(offset)?.let { startIndex ->
78
82
val item = items.getOrNull(startIndex)
79
83
if (item != null && state.selected.contains(item).not ()) {
80
84
haptics?.performHapticFeedback(HapticFeedbackType .LongPress )
81
- state.initialIndex = startIndex
82
- state.addSelected(item)
85
+ state.startDrag(item, startIndex)
83
86
}
84
87
}
85
88
},
86
- onDragCancel = state::resetDrag ,
87
- onDragEnd = state::resetDrag ,
89
+ onDragCancel = state::stopDrag ,
90
+ onDragEnd = state::stopDrag ,
88
91
onDrag = { change, _ ->
89
- state.withInitialIndex { initial ->
92
+ state.whenDragging { dragState ->
90
93
autoScrollSpeed.value = gridState.calculateScrollSpeed(change, scrollThreshold)
91
94
92
- val newSelected =
93
- gridState.getSelectedByPosition(items, initial, change)
94
- ? : gridState.getOverscrollItems(items, initial, change)
95
+ val itemPosition = gridState.getItemPosition(change.position)
96
+ ? : return @whenDragging
95
97
96
- if (newSelected != null ) {
97
- updateSelected(newSelected )
98
- }
98
+ val newSelection = items.getSelectedItems(itemPosition, dragState, isSelected)
99
+ updateDrag(current = itemPosition )
100
+ updateSelected(newSelection)
99
101
}
100
102
},
101
103
)
102
104
}
103
105
}
104
106
107
+ /* *
108
+ * Calculates the auto-scroll speed based on the current drag position.
109
+ *
110
+ * @param[change] The [PointerInputChange] for the current drag position.
111
+ * @param[scrollThreshold] The distance from the edge of the grid to start auto-scrolling.
112
+ * @return The auto-scroll speed.
113
+ */
105
114
private fun LazyGridState.calculateScrollSpeed (
106
115
change : PointerInputChange ,
107
116
scrollThreshold : Float ,
@@ -116,6 +125,12 @@ private fun LazyGridState.calculateScrollSpeed(
116
125
}
117
126
}
118
127
128
+ /* *
129
+ * Gets the index of the item that was hit by the drag.
130
+ *
131
+ * @param[hitPoint] The point where the drag hit the grid.
132
+ * @return The index of the item that was hit by the drag.
133
+ */
119
134
private fun LazyGridState.itemIndexAtPosition (hitPoint : Offset ): Int? {
120
135
val found = layoutInfo.visibleItemsInfo.find { itemInfo ->
121
136
itemInfo.size.toIntRect().contains(hitPoint.round() - itemInfo.offset)
@@ -124,36 +139,58 @@ private fun LazyGridState.itemIndexAtPosition(hitPoint: Offset): Int? {
124
139
return found?.index
125
140
}
126
141
127
- private fun <Item > LazyGridState.getSelectedByPosition (
128
- items : List <Item >,
129
- initialIndex : Int ,
130
- change : PointerInputChange ,
131
- ): List <Item >? {
132
- val itemAtPosition = itemIndexAtPosition(change.position) ? : return null
133
- return items.filterIndexed { index, _ ->
134
- index in initialIndex.. itemAtPosition || index in itemAtPosition.. initialIndex
135
- }
142
+ /* *
143
+ * Gets the index of the item that was hit by the drag.
144
+ *
145
+ * @param[hitPoint] The point where the drag hit the grid.
146
+ * @return The index of the item that was hit by the drag, or the index of the last item if the
147
+ * drag has gone past the last item.
148
+ */
149
+ private fun LazyGridState.getItemPosition (hitPoint : Offset ): Int? {
150
+ return itemIndexAtPosition(hitPoint)
151
+ ? : if (isPastLastItem(hitPoint)) layoutInfo.totalItemsCount - 1 else null
136
152
}
137
153
138
154
/* *
139
- * Get the items that are overscrolled when dragging .
155
+ * Determines if the drag has gone past the last item in the list .
140
156
*
141
- * If the user has dragged past the last item in the list, this will return all items after the
142
- * initial index .
157
+ * @param[hitPoint] The point where the drag hit the grid.
158
+ * @return True if the drag has gone past the last item in the list, false otherwise .
143
159
*/
144
- private fun <Item > LazyGridState.getOverscrollItems (
145
- items : List <Item >,
146
- initialIndex : Int ,
147
- change : PointerInputChange ,
148
- ): List <Item >? {
160
+ private fun LazyGridState.isPastLastItem (hitPoint : Offset ): Boolean {
149
161
// Get the last item in the list
150
162
val lastItem = layoutInfo.visibleItemsInfo.lastOrNull()
151
163
?.takeIf { it.index == layoutInfo.totalItemsCount - 1 }
152
- ? : return null
164
+ ? : return false
153
165
154
166
// Determine if we have dragged past the last item in the list
155
- return if (change.position.y > lastItem.offset.y) {
156
- // If we have, return all items after the initial index
157
- items.filterIndexed { index, _ -> index >= initialIndex }
158
- } else null
167
+ return hitPoint.y > lastItem.offset.y
168
+ }
169
+
170
+ /* *
171
+ * Gets a list of items that are selected based on the current drag state.
172
+ *
173
+ * @receiver The list of items in the grid.
174
+ * @param[itemPosition] The position of the item that was hit by the drag.
175
+ * @param[dragState] The current drag state.
176
+ * @param[isSelected] A function to determine if an item is selected.
177
+ * @return A list of items that are selected based on the current drag state.
178
+ */
179
+ private fun <Item > List<Item>.getSelectedItems (
180
+ itemPosition : Int ,
181
+ dragState : DragState ,
182
+ isSelected : (Item ) -> Boolean ,
183
+ ): List <Item > {
184
+ val (initial, current) = dragState
185
+ return filterIndexed { index, item ->
186
+ // Determine if the item is within the drag range
187
+ val withinRange = index in initial.. itemPosition || index in itemPosition.. initial
188
+
189
+ // Determine if the item was previously selected and is still within the drag range
190
+ val selected = isSelected(item)
191
+ val previouslySelectedInRange =
192
+ selected && index !in initial.. current && index !in current.. initial
193
+
194
+ withinRange || previouslySelectedInRange
195
+ }
159
196
}
0 commit comments