fix: refine drag-and-drop logic in ListsScreen and ManageColumnsDialog to correctly adjust indices during reordering

This commit is contained in:
2025-10-26 14:40:18 +01:00
parent eb5fee1ad2
commit 74fecb3ec2
2 changed files with 16 additions and 25 deletions
@@ -2506,30 +2506,26 @@ fun ManageColumnsDialog(
Divider() Divider()
// Column list with drag-and-drop reordering // Column list with drag-and-drop reordering
// Local extension mirroring the working behavior used in ListsScreen // Local extension mirroring the working behavior used in ListsScreen,
// adjusting target index when moving downwards to account for prior removal.
fun <T> MutableList<T>.move( fun <T> MutableList<T>.move(
from: Int, from: Int,
to: Int, to: Int,
) { ) {
if (from == to) return if (from == to) return
if (isEmpty()) return if (isEmpty() || from !in indices) return
if (from !in indices) return
val item = removeAt(from) val item = removeAt(from)
val insertIndex = val insertIndex = (if (to > from) to - 1 else to).coerceIn(0, size)
when {
to > size -> size
to < 0 -> 0
else -> to
}
add(insertIndex, item) add(insertIndex, item)
} }
val reorderState = val reorderState =
rememberReorderableLazyListState( rememberReorderableLazyListState(
onMove = { from, to -> onMove = { from, to ->
// Map from Lazy positions (including dividers) to data indices // Map from Lazy positions (with dividers) to data indices.
// Insert AFTER the upper item when hovering between rows.
val fromData = from.index / 2 val fromData = from.index / 2
val toData = to.index / 2 val toData = (to.index + 1) / 2
reorderedFields.move(fromData, toData) reorderedFields.move(fromData, toData)
}, },
) )
@@ -2546,7 +2542,7 @@ fun ManageColumnsDialog(
) { ) {
// Interleave dividers as separate Lazy items so dividers don't move with dragged rows // Interleave dividers as separate Lazy items so dividers don't move with dragged rows
items( items(
count = reorderedFields.size * 2 - 1, count = if (reorderedFields.isEmpty()) 0 else reorderedFields.size * 2 - 1,
key = { pos -> key = { pos ->
if (pos % 2 == 0) reorderedFields[pos / 2].id else "divider-$pos" if (pos % 2 == 0) reorderedFields[pos / 2].id else "divider-$pos"
}, },
@@ -161,17 +161,11 @@ fun ListsScreen(
to: Int, to: Int,
) { ) {
if (from == to) return if (from == to) return
if (isEmpty() || from !in indices) return
val item = removeAt(from) val item = removeAt(from)
add( // When moving downwards, the target index shifts left by one after removal
if (to > size) { val insertIndex = (if (to > from) to - 1 else to).coerceIn(0, size)
size add(insertIndex, item)
} else if (to < 0) {
0
} else {
to
},
item,
)
} }
val reorderState = val reorderState =
@@ -179,9 +173,10 @@ fun ListsScreen(
onMove = { from, to -> onMove = { from, to ->
dragging = true dragging = true
val newList = working.toMutableList() val newList = working.toMutableList()
// Map from Lazy positions (including dividers) to data indices // Map from Lazy positions (with dividers) to data indices.
// to.index can be odd (between items). Insert AFTER the upper item for better feel.
val fromData = from.index / 2 val fromData = from.index / 2
val toData = to.index / 2 val toData = (to.index + 1) / 2
newList.move(fromData, toData) newList.move(fromData, toData)
working = newList working = newList
}, },
@@ -202,7 +197,7 @@ fun ListsScreen(
) { ) {
// Interleave dividers as separate list items so they don't move with dragged rows // Interleave dividers as separate list items so they don't move with dragged rows
items( items(
count = working.size * 2 - 1, count = if (working.isEmpty()) 0 else working.size * 2 - 1,
key = { pos -> key = { pos ->
if (pos % 2 == 0) working[pos / 2].id else "divider-$pos" if (pos % 2 == 0) working[pos / 2].id else "divider-$pos"
}, },