diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/LocalPlaylistScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/LocalPlaylistScreen.kt index 3ab7ad8..9bda7fc 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/LocalPlaylistScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/LocalPlaylistScreen.kt @@ -35,8 +35,10 @@ import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex +import it.vfsfitvnm.reordering.animateItemPlacement +import it.vfsfitvnm.reordering.draggedItem import it.vfsfitvnm.reordering.rememberReorderingState -import it.vfsfitvnm.reordering.verticalDragAfterLongPressToReorder +import it.vfsfitvnm.reordering.verticalDragToReorder import it.vfsfitvnm.route.RouteHandler import it.vfsfitvnm.vimusic.Database import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder @@ -89,7 +91,44 @@ fun LocalPlaylistScreen(playlistId: Long) { val thumbnailSize = Dimensions.thumbnails.song.px - val reorderingState = rememberReorderingState(playlistWithSongs.songs) + val reorderingState = rememberReorderingState( + items = playlistWithSongs.songs, + onDragStart = { + hapticFeedback.performHapticFeedback( + HapticFeedbackType.LongPress + ) + }, + onDragEnd = { fromIndex, toIndex -> + transaction { + if (fromIndex > toIndex) { + Database.incrementSongPositions( + playlistId = playlistWithSongs.playlist.id, + fromPosition = toIndex, + toPosition = fromIndex - 1 + ) + } else if (fromIndex < toIndex) { + Database.decrementSongPositions( + playlistId = playlistWithSongs.playlist.id, + fromPosition = fromIndex + 1, + toPosition = toIndex + ) + } + + Database.update( + SongPlaylistMap( + songId = playlistWithSongs.songs[fromIndex].id, + playlistId = playlistWithSongs.playlist.id, + position = toIndex + ) + ) + } + }, + itemSizeProvider = { index -> + lazyListState.layoutInfo.visibleItemsInfo.find { + it.index == index + 3 + }?.size + } + ) var isRenaming by rememberSaveable { mutableStateOf(false) @@ -99,9 +138,7 @@ fun LocalPlaylistScreen(playlistId: Long) { TextFieldDialog( hintText = "Enter the playlist name", initialTextInput = playlistWithSongs.playlist.name, - onDismiss = { - isRenaming = false - }, + onDismiss = { isRenaming = false }, onDone = { text -> query { Database.update(playlistWithSongs.playlist.copy(name = text)) @@ -117,9 +154,7 @@ fun LocalPlaylistScreen(playlistId: Long) { if (isDeleting) { ConfirmationDialog( text = "Do you really want to delete this playlist?", - onDismiss = { - isDeleting = false - }, + onDismiss = { isDeleting = false }, onConfirm = { query { Database.delete(playlistWithSongs.playlist) @@ -131,7 +166,8 @@ fun LocalPlaylistScreen(playlistId: Long) { LazyColumn( state = lazyListState, - contentPadding = WindowInsets.systemBars.asPaddingValues().add(bottom = Dimensions.collapsedPlayer), + contentPadding = WindowInsets.systemBars.asPaddingValues() + .add(bottom = Dimensions.collapsedPlayer), modifier = Modifier .background(colorPalette.background0) .fillMaxSize() @@ -274,47 +310,18 @@ fun LocalPlaylistScreen(playlistId: Long) { contentDescription = null, colorFilter = ColorFilter.tint(colorPalette.textSecondary), modifier = Modifier - .clickable {} + .clickable { } + .verticalDragToReorder( + reorderingState = reorderingState, + index = index + ) .padding(horizontal = 8.dp, vertical = 4.dp) .size(20.dp) ) }, modifier = Modifier - .animateItemPlacement() - .verticalDragAfterLongPressToReorder( - reorderingState = reorderingState, - index = index, - onDragStart = { - hapticFeedback.performHapticFeedback( - HapticFeedbackType.LongPress - ) - }, - onDragEnd = { reachedIndex -> - transaction { - if (index > reachedIndex) { - Database.incrementSongPositions( - playlistId = playlistWithSongs.playlist.id, - fromPosition = reachedIndex, - toPosition = index - 1 - ) - } else if (index < reachedIndex) { - Database.decrementSongPositions( - playlistId = playlistWithSongs.playlist.id, - fromPosition = index + 1, - toPosition = reachedIndex - ) - } - - Database.update( - SongPlaylistMap( - songId = playlistWithSongs.songs[index].id, - playlistId = playlistWithSongs.playlist.id, - position = reachedIndex - ) - ) - } - } - ) + .animateItemPlacement(reorderingState = reorderingState) + .draggedItem(reorderingState = reorderingState, index = index) ) } } diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/CurrentPlaylistView.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/CurrentPlaylistView.kt index 9cf0c90..d46f9e4 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/CurrentPlaylistView.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/CurrentPlaylistView.kt @@ -3,6 +3,7 @@ package it.vfsfitvnm.vimusic.ui.views import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -36,8 +37,10 @@ import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import com.valentinilk.shimmer.shimmer +import it.vfsfitvnm.reordering.animateItemPlacement +import it.vfsfitvnm.reordering.draggedItem import it.vfsfitvnm.reordering.rememberReorderingState -import it.vfsfitvnm.reordering.verticalDragAfterLongPressToReorder +import it.vfsfitvnm.reordering.verticalDragToReorder import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder import it.vfsfitvnm.vimusic.R import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness @@ -56,6 +59,7 @@ import it.vfsfitvnm.vimusic.utils.rememberShouldBePlaying import it.vfsfitvnm.vimusic.utils.rememberWindows import it.vfsfitvnm.vimusic.utils.shuffleQueue +@ExperimentalFoundationApi @ExperimentalAnimationApi @Composable fun CurrentPlaylistView( @@ -78,7 +82,22 @@ fun CurrentPlaylistView( val lazyListState = rememberLazyListState(initialFirstVisibleItemIndex = mediaItemIndex) - val reorderingState = rememberReorderingState(windows) + val reorderingState = rememberReorderingState( + items = windows, + onDragStart = { + hapticFeedback.performHapticFeedback( + HapticFeedbackType.LongPress + ) + }, + onDragEnd = { fromIndex, toIndex -> + binder.player.moveMediaItem(fromIndex, toIndex) + }, + itemSizeProvider = { index -> + lazyListState.layoutInfo.visibleItemsInfo.find { + it.index == index + }?.size + } + ) val paddingValues = WindowInsets.systemBars.asPaddingValues() val bottomPadding = paddingValues.calculateBottomPadding() @@ -162,24 +181,20 @@ fun CurrentPlaylistView( contentDescription = null, colorFilter = ColorFilter.tint(colorPalette.textSecondary), modifier = Modifier - .clickable {} + .clickable { } + .verticalDragToReorder( + reorderingState = reorderingState, + index = window.firstPeriodIndex + ) .padding(horizontal = 8.dp, vertical = 4.dp) .size(20.dp) ) }, modifier = Modifier -// .animateItemPlacement() - .verticalDragAfterLongPressToReorder( + .animateItemPlacement(reorderingState) + .draggedItem( reorderingState = reorderingState, - index = window.firstPeriodIndex, - onDragStart = { - hapticFeedback.performHapticFeedback( - HapticFeedbackType.LongPress - ) - }, - onDragEnd = { reachedIndex -> - binder.player.moveMediaItem(window.firstPeriodIndex, reachedIndex) - } + index = window.firstPeriodIndex ) ) } diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlayerBottomSheet.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlayerBottomSheet.kt index 81d2c98..4fa5c2b 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlayerBottomSheet.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlayerBottomSheet.kt @@ -1,6 +1,7 @@ package it.vfsfitvnm.vimusic.ui.views import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.* @@ -17,6 +18,7 @@ import it.vfsfitvnm.vimusic.ui.components.BottomSheet import it.vfsfitvnm.vimusic.ui.components.BottomSheetState import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance +@ExperimentalFoundationApi @ExperimentalAnimationApi @Composable fun PlayerBottomSheet( diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlayerView.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlayerView.kt index f07cb34..324032d 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlayerView.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlayerView.kt @@ -7,6 +7,7 @@ import android.widget.Toast import androidx.activity.compose.LocalActivityResultRegistryOwner import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -72,6 +73,7 @@ import it.vfsfitvnm.vimusic.utils.thumbnail import it.vfsfitvnm.youtubemusic.models.NavigationEndpoint import kotlin.math.absoluteValue +@ExperimentalFoundationApi @ExperimentalAnimationApi @Composable fun PlayerView( diff --git a/compose-reordering/build.gradle.kts b/compose-reordering/build.gradle.kts index 79adaad..22dc0a1 100644 --- a/compose-reordering/build.gradle.kts +++ b/compose-reordering/build.gradle.kts @@ -37,6 +37,7 @@ android { } kotlinOptions { + freeCompilerArgs += "-Xcontext-receivers" jvmTarget = "1.8" } } diff --git a/compose-reordering/src/main/kotlin/it/vfsfitvnm/reordering/AnimateItemPlacement.kt b/compose-reordering/src/main/kotlin/it/vfsfitvnm/reordering/AnimateItemPlacement.kt new file mode 100644 index 0000000..79c612d --- /dev/null +++ b/compose-reordering/src/main/kotlin/it/vfsfitvnm/reordering/AnimateItemPlacement.kt @@ -0,0 +1,10 @@ +package it.vfsfitvnm.reordering + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.lazy.LazyItemScope +import androidx.compose.ui.Modifier + +context(LazyItemScope) +@ExperimentalFoundationApi +fun Modifier.animateItemPlacement(reorderingState: ReorderingState) = + if (reorderingState.draggingIndex == -1) animateItemPlacement() else this diff --git a/compose-reordering/src/main/kotlin/it/vfsfitvnm/reordering/DragToReorder.kt b/compose-reordering/src/main/kotlin/it/vfsfitvnm/reordering/DragToReorder.kt new file mode 100644 index 0000000..024fb27 --- /dev/null +++ b/compose-reordering/src/main/kotlin/it/vfsfitvnm/reordering/DragToReorder.kt @@ -0,0 +1,153 @@ +package it.vfsfitvnm.reordering + +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.gestures.detectDragGestures +import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.pointer.PointerInputChange +import androidx.compose.ui.input.pointer.PointerInputScope +import androidx.compose.ui.input.pointer.pointerInput +import kotlin.math.roundToInt +import kotlin.reflect.KSuspendFunction5 +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +private fun Modifier.dragToReorder( + reorderingState: ReorderingState, + index: Int, + orientation: Orientation, + function: KSuspendFunction5 Unit, () -> Unit, () -> Unit, (change: PointerInputChange, dragAmount: Offset) -> Unit, Unit>, +): Modifier = pointerInput(reorderingState) { +// require(index in 0..reorderingState.lastIndex) + + var previousItemSize = 0 + var nextItemSize = 0 + + function( + this, + { + reorderingState.onDragStart.invoke() + reorderingState.draggingIndex = index + reorderingState.reachedIndex = index + reorderingState.draggingItemSize = reorderingState.itemSizeProvider?.invoke(index) ?: when (orientation) { + Orientation.Vertical -> size.height + Orientation.Horizontal -> size.width + } + + nextItemSize = reorderingState.draggingItemSize + previousItemSize = -reorderingState.draggingItemSize + + reorderingState.offset.updateBounds( + lowerBound = -index * reorderingState.draggingItemSize, + upperBound = (reorderingState.lastIndex - index) * reorderingState.draggingItemSize + ) + }, + { + reorderingState.coroutineScope.launch { + reorderingState.offset.animateTo((previousItemSize + nextItemSize) / 2) + + withContext(Dispatchers.Main) { + reorderingState.onDragEnd.invoke(index, reorderingState.reachedIndex) + } + + if (reorderingState.areEquals( + reorderingState.draggingIndex, + reorderingState.reachedIndex + ) + ) { + reorderingState.draggingIndex = -1 + reorderingState.reachedIndex = -1 + reorderingState.draggingItemSize = 0 + reorderingState.offset.snapTo(0) + } + } + }, + {}, + { _, offset -> + val delta = when (orientation) { + Orientation.Vertical -> offset.y + Orientation.Horizontal -> offset.x + }.roundToInt() + + val targetOffset = reorderingState.offset.value + delta + + if (targetOffset > nextItemSize) { + if (reorderingState.reachedIndex < reorderingState.lastIndex) { + reorderingState.reachedIndex += 1 + nextItemSize += reorderingState.draggingItemSize + previousItemSize += reorderingState.draggingItemSize + } + } else if (targetOffset < previousItemSize) { + if (reorderingState.reachedIndex > 0) { + reorderingState.reachedIndex -= 1 + previousItemSize -= reorderingState.draggingItemSize + nextItemSize -= reorderingState.draggingItemSize + } + } + + reorderingState.coroutineScope.launch { + reorderingState.offset.snapTo(targetOffset) + } + }, + ) +} + +fun Modifier.dragToReorder( + reorderingState: ReorderingState, + index: Int, + orientation: Orientation, +): Modifier = dragToReorder( + reorderingState = reorderingState, + index = index, + orientation = orientation, + function = PointerInputScope::detectDragGestures, +) + +fun Modifier.verticalDragToReorder( + reorderingState: ReorderingState, + index: Int, +): Modifier = dragToReorder( + reorderingState = reorderingState, + index = index, + orientation = Orientation.Vertical, +) + +fun Modifier.horizontalDragToReorder( + reorderingState: ReorderingState, + index: Int, +): Modifier = dragToReorder( + reorderingState = reorderingState, + index = index, + orientation = Orientation.Horizontal, +) + +fun Modifier.dragAfterLongPressToReorder( + reorderingState: ReorderingState, + index: Int, + orientation: Orientation, +): Modifier = dragToReorder( + reorderingState = reorderingState, + index = index, + orientation = orientation, + function = PointerInputScope::detectDragGesturesAfterLongPress, +) + +fun Modifier.verticalDragAfterLongPressToReorder( + reorderingState: ReorderingState, + index: Int, +): Modifier = dragAfterLongPressToReorder( + reorderingState = reorderingState, + index = index, + orientation = Orientation.Vertical, +) + +fun Modifier.horizontalDragAfterLongPressToReorder( + reorderingState: ReorderingState, + index: Int +): Modifier = dragAfterLongPressToReorder( + reorderingState = reorderingState, + index = index, + orientation = Orientation.Horizontal, +) diff --git a/compose-reordering/src/main/kotlin/it/vfsfitvnm/reordering/DraggedItem.kt b/compose-reordering/src/main/kotlin/it/vfsfitvnm/reordering/DraggedItem.kt new file mode 100644 index 0000000..2abcb99 --- /dev/null +++ b/compose-reordering/src/main/kotlin/it/vfsfitvnm/reordering/DraggedItem.kt @@ -0,0 +1,24 @@ +package it.vfsfitvnm.reordering + +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.layout.offset +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.composed +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.zIndex + +fun Modifier.draggedItem( + reorderingState: ReorderingState, + index: Int +): Modifier = composed { + val translation by reorderingState.translationFor(index) + + offset { + when (reorderingState.orientation) { + Orientation.Vertical -> IntOffset(0, translation) + Orientation.Horizontal -> IntOffset(translation, 0) + } + } + .zIndex(if (reorderingState.draggingIndex == index) 1f else 0f) +} diff --git a/compose-reordering/src/main/kotlin/it/vfsfitvnm/reordering/ReorderingState.kt b/compose-reordering/src/main/kotlin/it/vfsfitvnm/reordering/ReorderingState.kt index 4e2e6f7..bf9b2f0 100644 --- a/compose-reordering/src/main/kotlin/it/vfsfitvnm/reordering/ReorderingState.kt +++ b/compose-reordering/src/main/kotlin/it/vfsfitvnm/reordering/ReorderingState.kt @@ -4,23 +4,38 @@ import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.AnimationVector1D import androidx.compose.animation.core.VectorConverter import androidx.compose.animation.core.animateIntAsState -import androidx.compose.runtime.* +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import kotlinx.coroutines.CoroutineScope class ReorderingState( - draggingIndexState: MutableState, - reachedIndexState: MutableState, - draggingItemSizeState: MutableState, - internal val offset: Animatable, + internal val itemSizeProvider: ((Int) -> Int?)?, + internal val coroutineScope: CoroutineScope, internal val lastIndex: Int, - internal val areEquals: (Int, Int) -> Boolean + internal val areEquals: (Int, Int) -> Boolean, + internal val orientation: Orientation, + internal val onDragStart: () -> Unit, + internal val onDragEnd: (Int, Int) -> Unit, ) { - internal var draggingIndex by draggingIndexState - internal var reachedIndex by reachedIndexState - internal var draggingItemSize by draggingItemSizeState + internal val offset: Animatable = Animatable(0, Int.VectorConverter) + + internal var draggingIndex by mutableStateOf(-1) + internal var reachedIndex by mutableStateOf(-1) + internal var draggingItemSize by mutableStateOf(0) + + private val noTranslation = object : State { + override val value = 0 + } @Composable internal fun translationFor(index: Int): State = when (draggingIndex) { - -1 -> derivedStateOf { 0 } + -1 -> noTranslation index -> offset.asState() else -> animateIntAsState( when (index) { @@ -33,33 +48,24 @@ class ReorderingState( } @Composable -fun rememberReorderingState(items: List): ReorderingState { - val draggingIndexState = remember(items) { - mutableStateOf(-1) - } - - val reachedIndexState = remember(items) { - mutableStateOf(-1) - } - - val draggingItemHeightState = remember { - mutableStateOf(0) - } - - val offset = remember(items) { - Animatable(0, Int.VectorConverter) - } +fun rememberReorderingState( + items: List, + onDragEnd: (Int, Int) -> Unit, + onDragStart: () -> Unit = {}, + orientation: Orientation = Orientation.Vertical, + itemSizeProvider: ((Int) -> Int?)? = null +): ReorderingState { + val coroutineScope = rememberCoroutineScope() return remember(items) { ReorderingState( - draggingIndexState = draggingIndexState, - reachedIndexState = reachedIndexState, - draggingItemSizeState = draggingItemHeightState, - offset = offset, + itemSizeProvider = itemSizeProvider, + coroutineScope = coroutineScope, + orientation = orientation, lastIndex = items.lastIndex, - areEquals = { i, j -> - items[i] == items[j] - } + areEquals = { i, j -> items[i] == items[j] }, + onDragStart = onDragStart, + onDragEnd = onDragEnd, ) } } diff --git a/compose-reordering/src/main/kotlin/it/vfsfitvnm/reordering/dragToReorder.kt b/compose-reordering/src/main/kotlin/it/vfsfitvnm/reordering/dragToReorder.kt deleted file mode 100644 index 11f22e9..0000000 --- a/compose-reordering/src/main/kotlin/it/vfsfitvnm/reordering/dragToReorder.kt +++ /dev/null @@ -1,208 +0,0 @@ -package it.vfsfitvnm.reordering - -import androidx.compose.foundation.gestures.Orientation -import androidx.compose.foundation.gestures.detectDragGestures -import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress -import androidx.compose.foundation.layout.offset -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.composed -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.input.pointer.PointerInputChange -import androidx.compose.ui.input.pointer.PointerInputScope -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.unit.IntOffset -import androidx.compose.ui.zIndex -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import kotlin.math.roundToInt -import kotlin.reflect.KSuspendFunction5 - -private fun Modifier.dragToReorder( - reorderingState: ReorderingState, - index: Int, - orientation: Orientation, - function: KSuspendFunction5 Unit, () -> Unit, () -> Unit, (change: PointerInputChange, dragAmount: Offset) -> Unit, Unit>, - onDragStart: (() -> Unit)? = null, - onMove: (() -> Unit)? = null, - onDragEnd: ((Int) -> Unit)? = null -): Modifier = composed { - val coroutineScope = rememberCoroutineScope() - val translation by reorderingState.translationFor(index) - - pointerInput(reorderingState) { -// require(index in 0..reorderingState.lastIndex) - - var previousItemSize = 0 - var nextItemSize = 0 - - function( - this, - { - onDragStart?.invoke() - reorderingState.draggingIndex = index - reorderingState.reachedIndex = index - reorderingState.draggingItemSize = size.height - - nextItemSize = reorderingState.draggingItemSize - previousItemSize = -reorderingState.draggingItemSize - - reorderingState.offset.updateBounds( - lowerBound = -index * reorderingState.draggingItemSize, - upperBound = (reorderingState.lastIndex - index) * reorderingState.draggingItemSize - ) - }, - { - coroutineScope.launch { - reorderingState.offset.animateTo((previousItemSize + nextItemSize) / 2) - - withContext(Dispatchers.Main) { - onDragEnd?.invoke(reorderingState.reachedIndex) - } - - if (reorderingState.areEquals( - reorderingState.draggingIndex, - reorderingState.reachedIndex - ) - ) { - reorderingState.draggingIndex = -1 - reorderingState.reachedIndex = -1 - reorderingState.draggingItemSize = 0 - reorderingState.offset.snapTo(0) - } - } - }, - {}, - { _, offset -> - val delta = when (orientation) { - Orientation.Vertical -> offset.y - Orientation.Horizontal -> offset.x - }.roundToInt() - - val targetOffset = reorderingState.offset.value + delta - - if (targetOffset > nextItemSize) { - if (reorderingState.reachedIndex < reorderingState.lastIndex) { - reorderingState.reachedIndex += 1 - nextItemSize += reorderingState.draggingItemSize - previousItemSize += reorderingState.draggingItemSize - onMove?.invoke() - } - } else if (targetOffset < previousItemSize) { - if (reorderingState.reachedIndex > 0) { - reorderingState.reachedIndex -= 1 - previousItemSize -= reorderingState.draggingItemSize - nextItemSize -= reorderingState.draggingItemSize - onMove?.invoke() - } - } - - coroutineScope.launch { - reorderingState.offset.snapTo(targetOffset) - } - }, - ) - } - .offset { - when (orientation) { - Orientation.Vertical -> IntOffset(0, translation) - Orientation.Horizontal -> IntOffset(translation, 0) - } - } - .zIndex(if (reorderingState.draggingIndex == index) 1f else 0f) -} - -fun Modifier.dragToReorder( - reorderingState: ReorderingState, - index: Int, - orientation: Orientation, - onDragStart: (() -> Unit)? = null, - onMove: (() -> Unit)? = null, - onDragEnd: ((Int) -> Unit)? = null -): Modifier = dragToReorder( - reorderingState = reorderingState, - index = index, - orientation = orientation, - function = PointerInputScope::detectDragGestures, - onDragStart = onDragStart, - onMove = onMove, - onDragEnd = onDragEnd, -) - -fun Modifier.verticalDragToReorder( - reorderingState: ReorderingState, - index: Int, - onDragStart: (() -> Unit)? = null, - onMove: (() -> Unit)? = null, - onDragEnd: ((Int) -> Unit)? = null -): Modifier = dragToReorder( - reorderingState = reorderingState, - index = index, - orientation = Orientation.Vertical, - onDragStart = onDragStart, - onMove = onMove, - onDragEnd = onDragEnd, -) - -fun Modifier.horizontalDragToReorder( - reorderingState: ReorderingState, - index: Int, - onDragStart: (() -> Unit)? = null, - onMove: (() -> Unit)? = null, - onDragEnd: ((Int) -> Unit)? = null -): Modifier = dragToReorder( - reorderingState = reorderingState, - index = index, - orientation = Orientation.Horizontal, - onDragStart = onDragStart, - onMove = onMove, - onDragEnd = onDragEnd, -) - -fun Modifier.dragAfterLongPressToReorder( - reorderingState: ReorderingState, - index: Int, - orientation: Orientation, - onDragStart: (() -> Unit)? = null, - onMove: (() -> Unit)? = null, - onDragEnd: ((Int) -> Unit)? = null -): Modifier = dragToReorder( - reorderingState = reorderingState, - index = index, - orientation = orientation, - function = PointerInputScope::detectDragGesturesAfterLongPress, - onDragStart = onDragStart, - onMove = onMove, - onDragEnd = onDragEnd, -) - -fun Modifier.verticalDragAfterLongPressToReorder( - reorderingState: ReorderingState, - index: Int, - onDragStart: (() -> Unit)? = null, - onMove: (() -> Unit)? = null, - onDragEnd: ((Int) -> Unit)? = null -): Modifier = dragAfterLongPressToReorder( - reorderingState = reorderingState, - index = index, - orientation = Orientation.Vertical, - onDragStart = onDragStart, - onMove = onMove, - onDragEnd = onDragEnd, -) - -fun Modifier.horizontalDragAfterLongPressToReorder( - reorderingState: ReorderingState, - index: Int, - onDragStart: (() -> Unit)? = null, - onMove: (() -> Unit)? = null, - onDragEnd: ((Int) -> Unit)? = null -): Modifier = dragAfterLongPressToReorder( - reorderingState = reorderingState, - index = index, - orientation = Orientation.Horizontal, - onDragStart = onDragStart, - onMove = onMove, - onDragEnd = onDragEnd, -)