Improve compose-reordering code

This commit is contained in:
vfsfitvnm
2022-08-16 20:55:35 +02:00
parent 610b047019
commit 4ae3c604e4
10 changed files with 312 additions and 300 deletions

View File

@@ -35,8 +35,10 @@ import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex import androidx.compose.ui.zIndex
import it.vfsfitvnm.reordering.animateItemPlacement
import it.vfsfitvnm.reordering.draggedItem
import it.vfsfitvnm.reordering.rememberReorderingState import it.vfsfitvnm.reordering.rememberReorderingState
import it.vfsfitvnm.reordering.verticalDragAfterLongPressToReorder import it.vfsfitvnm.reordering.verticalDragToReorder
import it.vfsfitvnm.route.RouteHandler import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.vimusic.Database import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
@@ -89,7 +91,44 @@ fun LocalPlaylistScreen(playlistId: Long) {
val thumbnailSize = Dimensions.thumbnails.song.px 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 { var isRenaming by rememberSaveable {
mutableStateOf(false) mutableStateOf(false)
@@ -99,9 +138,7 @@ fun LocalPlaylistScreen(playlistId: Long) {
TextFieldDialog( TextFieldDialog(
hintText = "Enter the playlist name", hintText = "Enter the playlist name",
initialTextInput = playlistWithSongs.playlist.name, initialTextInput = playlistWithSongs.playlist.name,
onDismiss = { onDismiss = { isRenaming = false },
isRenaming = false
},
onDone = { text -> onDone = { text ->
query { query {
Database.update(playlistWithSongs.playlist.copy(name = text)) Database.update(playlistWithSongs.playlist.copy(name = text))
@@ -117,9 +154,7 @@ fun LocalPlaylistScreen(playlistId: Long) {
if (isDeleting) { if (isDeleting) {
ConfirmationDialog( ConfirmationDialog(
text = "Do you really want to delete this playlist?", text = "Do you really want to delete this playlist?",
onDismiss = { onDismiss = { isDeleting = false },
isDeleting = false
},
onConfirm = { onConfirm = {
query { query {
Database.delete(playlistWithSongs.playlist) Database.delete(playlistWithSongs.playlist)
@@ -131,7 +166,8 @@ fun LocalPlaylistScreen(playlistId: Long) {
LazyColumn( LazyColumn(
state = lazyListState, state = lazyListState,
contentPadding = WindowInsets.systemBars.asPaddingValues().add(bottom = Dimensions.collapsedPlayer), contentPadding = WindowInsets.systemBars.asPaddingValues()
.add(bottom = Dimensions.collapsedPlayer),
modifier = Modifier modifier = Modifier
.background(colorPalette.background0) .background(colorPalette.background0)
.fillMaxSize() .fillMaxSize()
@@ -274,47 +310,18 @@ fun LocalPlaylistScreen(playlistId: Long) {
contentDescription = null, contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.textSecondary), colorFilter = ColorFilter.tint(colorPalette.textSecondary),
modifier = Modifier modifier = Modifier
.clickable {} .clickable { }
.verticalDragToReorder(
reorderingState = reorderingState,
index = index
)
.padding(horizontal = 8.dp, vertical = 4.dp) .padding(horizontal = 8.dp, vertical = 4.dp)
.size(20.dp) .size(20.dp)
) )
}, },
modifier = Modifier modifier = Modifier
.animateItemPlacement() .animateItemPlacement(reorderingState = reorderingState)
.verticalDragAfterLongPressToReorder( .draggedItem(reorderingState = reorderingState, index = index)
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
)
)
}
}
)
) )
} }
} }

View File

@@ -3,6 +3,7 @@ package it.vfsfitvnm.vimusic.ui.views
import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable 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.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.valentinilk.shimmer.shimmer import com.valentinilk.shimmer.shimmer
import it.vfsfitvnm.reordering.animateItemPlacement
import it.vfsfitvnm.reordering.draggedItem
import it.vfsfitvnm.reordering.rememberReorderingState import it.vfsfitvnm.reordering.rememberReorderingState
import it.vfsfitvnm.reordering.verticalDragAfterLongPressToReorder import it.vfsfitvnm.reordering.verticalDragToReorder
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.R import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness 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.rememberWindows
import it.vfsfitvnm.vimusic.utils.shuffleQueue import it.vfsfitvnm.vimusic.utils.shuffleQueue
@ExperimentalFoundationApi
@ExperimentalAnimationApi @ExperimentalAnimationApi
@Composable @Composable
fun CurrentPlaylistView( fun CurrentPlaylistView(
@@ -78,7 +82,22 @@ fun CurrentPlaylistView(
val lazyListState = val lazyListState =
rememberLazyListState(initialFirstVisibleItemIndex = mediaItemIndex) 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 paddingValues = WindowInsets.systemBars.asPaddingValues()
val bottomPadding = paddingValues.calculateBottomPadding() val bottomPadding = paddingValues.calculateBottomPadding()
@@ -162,24 +181,20 @@ fun CurrentPlaylistView(
contentDescription = null, contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.textSecondary), colorFilter = ColorFilter.tint(colorPalette.textSecondary),
modifier = Modifier modifier = Modifier
.clickable {} .clickable { }
.verticalDragToReorder(
reorderingState = reorderingState,
index = window.firstPeriodIndex
)
.padding(horizontal = 8.dp, vertical = 4.dp) .padding(horizontal = 8.dp, vertical = 4.dp)
.size(20.dp) .size(20.dp)
) )
}, },
modifier = Modifier modifier = Modifier
// .animateItemPlacement() .animateItemPlacement(reorderingState)
.verticalDragAfterLongPressToReorder( .draggedItem(
reorderingState = reorderingState, reorderingState = reorderingState,
index = window.firstPeriodIndex, index = window.firstPeriodIndex
onDragStart = {
hapticFeedback.performHapticFeedback(
HapticFeedbackType.LongPress
)
},
onDragEnd = { reachedIndex ->
binder.player.moveMediaItem(window.firstPeriodIndex, reachedIndex)
}
) )
) )
} }

View File

@@ -1,6 +1,7 @@
package it.vfsfitvnm.vimusic.ui.views package it.vfsfitvnm.vimusic.ui.views
import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* 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.components.BottomSheetState
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
@ExperimentalFoundationApi
@ExperimentalAnimationApi @ExperimentalAnimationApi
@Composable @Composable
fun PlayerBottomSheet( fun PlayerBottomSheet(

View File

@@ -7,6 +7,7 @@ import android.widget.Toast
import androidx.activity.compose.LocalActivityResultRegistryOwner import androidx.activity.compose.LocalActivityResultRegistryOwner
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
@@ -72,6 +73,7 @@ import it.vfsfitvnm.vimusic.utils.thumbnail
import it.vfsfitvnm.youtubemusic.models.NavigationEndpoint import it.vfsfitvnm.youtubemusic.models.NavigationEndpoint
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
@ExperimentalFoundationApi
@ExperimentalAnimationApi @ExperimentalAnimationApi
@Composable @Composable
fun PlayerView( fun PlayerView(

View File

@@ -37,6 +37,7 @@ android {
} }
kotlinOptions { kotlinOptions {
freeCompilerArgs += "-Xcontext-receivers"
jvmTarget = "1.8" jvmTarget = "1.8"
} }
} }

View File

@@ -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

View File

@@ -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<PointerInputScope, (Offset) -> 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,
)

View File

@@ -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)
}

View File

@@ -4,23 +4,38 @@ import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationVector1D import androidx.compose.animation.core.AnimationVector1D
import androidx.compose.animation.core.VectorConverter import androidx.compose.animation.core.VectorConverter
import androidx.compose.animation.core.animateIntAsState 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( class ReorderingState(
draggingIndexState: MutableState<Int>, internal val itemSizeProvider: ((Int) -> Int?)?,
reachedIndexState: MutableState<Int>, internal val coroutineScope: CoroutineScope,
draggingItemSizeState: MutableState<Int>,
internal val offset: Animatable<Int, AnimationVector1D>,
internal val lastIndex: Int, 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 val offset: Animatable<Int, AnimationVector1D> = Animatable(0, Int.VectorConverter)
internal var reachedIndex by reachedIndexState
internal var draggingItemSize by draggingItemSizeState internal var draggingIndex by mutableStateOf(-1)
internal var reachedIndex by mutableStateOf(-1)
internal var draggingItemSize by mutableStateOf(0)
private val noTranslation = object : State<Int> {
override val value = 0
}
@Composable @Composable
internal fun translationFor(index: Int): State<Int> = when (draggingIndex) { internal fun translationFor(index: Int): State<Int> = when (draggingIndex) {
-1 -> derivedStateOf { 0 } -1 -> noTranslation
index -> offset.asState() index -> offset.asState()
else -> animateIntAsState( else -> animateIntAsState(
when (index) { when (index) {
@@ -33,33 +48,24 @@ class ReorderingState(
} }
@Composable @Composable
fun rememberReorderingState(items: List<Any>): ReorderingState { fun rememberReorderingState(
val draggingIndexState = remember(items) { items: List<Any>,
mutableStateOf(-1) onDragEnd: (Int, Int) -> Unit,
} onDragStart: () -> Unit = {},
orientation: Orientation = Orientation.Vertical,
val reachedIndexState = remember(items) { itemSizeProvider: ((Int) -> Int?)? = null
mutableStateOf(-1) ): ReorderingState {
} val coroutineScope = rememberCoroutineScope()
val draggingItemHeightState = remember {
mutableStateOf(0)
}
val offset = remember(items) {
Animatable(0, Int.VectorConverter)
}
return remember(items) { return remember(items) {
ReorderingState( ReorderingState(
draggingIndexState = draggingIndexState, itemSizeProvider = itemSizeProvider,
reachedIndexState = reachedIndexState, coroutineScope = coroutineScope,
draggingItemSizeState = draggingItemHeightState, orientation = orientation,
offset = offset,
lastIndex = items.lastIndex, lastIndex = items.lastIndex,
areEquals = { i, j -> areEquals = { i, j -> items[i] == items[j] },
items[i] == items[j] onDragStart = onDragStart,
} onDragEnd = onDragEnd,
) )
} }
} }

View File

@@ -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<PointerInputScope, (Offset) -> 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,
)