Reorderable icon indicating copy to clipboard operation
Reorderable copied to clipboard

Item loses drag state when dragged into different list/different ui

Open mdrlzy opened this issue 10 months ago • 9 comments

Issue is similar to https://github.com/Calvin-LL/Reorderable/issues/49, but i have only 1 list When I drag from item{} to items{} drag is lost I think the reason is in different composable functions for elements. I will be glad to any advice!

https://github.com/user-attachments/assets/f1f4d622-1d57-4a2b-a409-299f0811a26a

Pull request with source code

I tried solution from #49 issue with merging lists into one, but it didn't work. Workaround branch

Reorderable code snippet:

private fun LazyListScope.currencies(
    state: AddQuickScreenState,
    reorderableLazyColumnState: ReorderableLazyListState,
    haptic: ReorderHapticFeedback,
    onAmountChanged: (String) -> Unit,
    onCurrencyRemove: (Int) -> Unit,
    onCodeChange: (Int) -> Unit,
    onSwapClick: () -> Unit,
) {
    val from = state.currencies.first()
    val to = state.currencies.drop(1)

    item {
        Text(
            modifier = Modifier.padding(top = 16.dp, start = 52.dp),
            text = stringResource(CoreRString.quick_from),
            fontWeight = FontWeight.Medium,
            color = ArkColor.TextSecondary,
        )
    }
    item(key = from.code) {
        ReorderableItem(state = reorderableLazyColumnState, key = from.code) {
            FromInput(
                code = from.code,
                amount = from.value,
                haptic = haptic,
                scope = this,
                onAmountChanged = onAmountChanged,
                onCodeChange = {
                    val index = state.currencies.indexOfFirst { it.code == from.code }
                    onCodeChange(index)
                },
            )
        }
    }
    item {
        SwapBtn(modifier = Modifier.padding(top = 16.dp), onClick = onSwapClick)
        Text(
            modifier = Modifier.padding(top = 16.dp, start = 52.dp),
            text = stringResource(CoreRString.quick_to),
            fontWeight = FontWeight.Medium,
            color = ArkColor.TextSecondary,
        )
    }
    itemsIndexed(to, key = { _, amount -> amount.code }) { index, item ->
        ReorderableItem(state = reorderableLazyColumnState, key = item.code) {
            ToResult(
                code = item.code,
                amount = item.value,
                scope = this,
                haptic = haptic,
                onCurrencyRemove = {
                    val index = state.currencies.indexOfFirst { it.code == item.code }
                    onCurrencyRemove(index)
                },
                onCodeChange = {
                    val index = state.currencies.indexOfFirst { it.code == item.code }
                    onCodeChange(index)
                },
            )
        }
    }
}

mdrlzy avatar Feb 02 '25 18:02 mdrlzy

I think the problem in the 🔁 part isn't in a ReorderableItem. Try wrapping it in there and setting enabled to false

Calvin-LL avatar Feb 02 '25 19:02 Calvin-LL

I didn't quite understand what you meant by

Try wrapping it in there and setting enabled to false

🔁 code

    val reorderableLazyColumnState =
        rememberReorderableLazyListState(lazyListState) { from, to ->
            val fromIndex = state.currencies.indexOfFirst { it.code == from.key }
            val toIndex = state.currencies.indexOfFirst { it.code == to.key }
            onPairsSwap(fromIndex, toIndex)
            haptic.performHapticFeedback(ReorderHapticFeedbackType.MOVE)
        }

    fun onPairsSwap(
        from: Int,
        to: Int,
    ) = intent {
        val new =
            state.currencies.toMutableList().apply {
                add(to, removeAt(from))
            }
        reduce {
            state.copy(currencies = new)
        }
    }

mdrlzy avatar Feb 02 '25 19:02 mdrlzy

Wrap the content of the item with 🔁 in a ReorderableItem

Calvin-LL avatar Feb 02 '25 19:02 Calvin-LL

Ah, you mean icon button Same behavior..

    item {
        Text(
            modifier = Modifier.padding(top = 16.dp, start = 52.dp),
            text = stringResource(CoreRString.quick_from),
            fontWeight = FontWeight.Medium,
            color = ArkColor.TextSecondary,
        )
    }
    item(key = from.code) {
        ReorderableItem(state = reorderableLazyColumnState, key = from.code) {
            FromInput(
                code = from.code,
                amount = from.value,
                haptic = haptic,
                scope = this,
                onAmountChanged = onAmountChanged,
                onCodeChange = {
                    val index = state.currencies.indexOfFirst { it.code == from.code }
                    onCodeChange(index)
                },
            )
        }
    }
    item(key = "To") {
        ReorderableItem(state = reorderableLazyColumnState, key = "To", enabled = false) {
            Column {
                SwapBtn(modifier = Modifier.padding(top = 16.dp), onClick = onSwapClick)
                Text(
                    modifier = Modifier.padding(top = 16.dp, start = 52.dp),
                    text = stringResource(CoreRString.quick_to),
                    fontWeight = FontWeight.Medium,
                    color = ArkColor.TextSecondary,
                )
            }
        }
    }
    itemsIndexed(to, key = { _, amount -> amount.code }) { index, item ->
        ReorderableItem(state = reorderableLazyColumnState, key = item.code) {
            ToResult(
                code = item.code,
                amount = item.value,
                scope = this,
                haptic = haptic,
                onCurrencyRemove = {
                    val index = state.currencies.indexOfFirst { it.code == item.code }
                    onCurrencyRemove(index)
                },
                onCodeChange = {
                    val index = state.currencies.indexOfFirst { it.code == item.code }
                    onCodeChange(index)
                },
            )
        }
    }

mdrlzy avatar Feb 02 '25 19:02 mdrlzy

Hmm let me give it a try. I wonder if this was a Compose bug that got fixed in a later version of compose.

Any idea which version you're using?

Calvin-LL avatar Feb 02 '25 19:02 Calvin-LL

1.7.5

mdrlzy avatar Feb 02 '25 19:02 mdrlzy

Bug still exists in versions 1.7.7, 1.8.0-beta01

mdrlzy avatar Feb 02 '25 21:02 mdrlzy

https://github.com/Calvin-LL/Reorderable/blob/main/demoApp/composeApp/src/commonMain/kotlin/sh/calvin/reorderable/demo/ui/TwoReorderableLazyColumnScreen.kt

See here this works.

https://github.com/user-attachments/assets/44173fcb-385f-4a78-9b5c-d52c01ccd519

It seems like a very strange composable bug. The commented out code below is semantically identical, the same functions are called in the same order except the commented out code is unrolled.

Calvin-LL avatar Feb 03 '25 06:02 Calvin-LL

Still not working If you have time can you add an example with 2 different ui items?

    val from = state.currencies.first()
    val to = state.currencies.drop(1)

    val combinedList = listOf(listOf(from), to)

    item {
        Text(
            modifier = Modifier.padding(top = 16.dp, start = 52.dp),
            text = stringResource(CoreRString.quick_from),
            fontWeight = FontWeight.Medium,
            color = ArkColor.TextSecondary,
        )
    }
    combinedList.forEachIndexed { listIndex, items ->
        if (listIndex == 1) {
            item {
                Column {
                    SwapBtn(modifier = Modifier.padding(top = 16.dp), onClick = onSwapClick)
                    Text(
                        modifier = Modifier.padding(top = 16.dp, start = 52.dp),
                        text = stringResource(CoreRString.quick_to),
                        fontWeight = FontWeight.Medium,
                        color = ArkColor.TextSecondary,
                    )
                }
            }
        }
        items(items, key = { item -> item.code }) { item ->
            ReorderableItem(state = reorderableLazyColumnState, key = item.code) {
                FromOrToItem(
                    listIndex = listIndex,
                    code = item.code,
                    amount = item.value,
                    haptic = haptic,
                    scope = this,
                    onAmountChanged = onAmountChanged,
                    onCurrencyRemove = {
                        val index = state.currencies.indexOfFirst { it.code == item.code }
                        onCurrencyRemove(index)
                    },
                    onCodeChange = {
                        val index = state.currencies.indexOfFirst { it.code == from.code }
                        onCodeChange(index)
                    },
                )
            }
        }
    }

mdrlzy avatar Feb 03 '25 10:02 mdrlzy