Do not show floating action buttons when reordering items
This commit is contained in:
@@ -22,8 +22,8 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerAwarePaddingValues
|
import it.vfsfitvnm.vimusic.LocalPlayerAwarePaddingValues
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.utils.isScrollingDown
|
import it.vfsfitvnm.vimusic.utils.ScrollingInfo
|
||||||
import it.vfsfitvnm.vimusic.utils.isScrollingDownToIsFar
|
import it.vfsfitvnm.vimusic.utils.scrollingInfo
|
||||||
import it.vfsfitvnm.vimusic.utils.smoothScrollToTop
|
import it.vfsfitvnm.vimusic.utils.smoothScrollToTop
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -32,12 +32,13 @@ import kotlinx.coroutines.launch
|
|||||||
fun BoxScope.FloatingActionsContainerWithScrollToTop(
|
fun BoxScope.FloatingActionsContainerWithScrollToTop(
|
||||||
lazyGridState: LazyGridState,
|
lazyGridState: LazyGridState,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
visible: Boolean = true,
|
||||||
iconId: Int? = null,
|
iconId: Int? = null,
|
||||||
onClick: (() -> Unit)? = null,
|
onClick: (() -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
val transitionState = remember {
|
val transitionState = remember {
|
||||||
MutableTransitionState(false to false)
|
MutableTransitionState<ScrollingInfo?>(ScrollingInfo())
|
||||||
}.apply { targetState = lazyGridState.isScrollingDownToIsFar() }
|
}.apply { targetState = if (visible) lazyGridState.scrollingInfo() else null }
|
||||||
|
|
||||||
FloatingActions(
|
FloatingActions(
|
||||||
transitionState = transitionState,
|
transitionState = transitionState,
|
||||||
@@ -53,12 +54,13 @@ fun BoxScope.FloatingActionsContainerWithScrollToTop(
|
|||||||
fun BoxScope.FloatingActionsContainerWithScrollToTop(
|
fun BoxScope.FloatingActionsContainerWithScrollToTop(
|
||||||
lazyListState: LazyListState,
|
lazyListState: LazyListState,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
visible: Boolean = true,
|
||||||
iconId: Int? = null,
|
iconId: Int? = null,
|
||||||
onClick: (() -> Unit)? = null,
|
onClick: (() -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
val transitionState = remember {
|
val transitionState = remember {
|
||||||
MutableTransitionState(false to false)
|
MutableTransitionState<ScrollingInfo?>(ScrollingInfo())
|
||||||
}.apply { targetState = lazyListState.isScrollingDownToIsFar() }
|
}.apply { targetState = if (visible) lazyListState.scrollingInfo() else null }
|
||||||
|
|
||||||
FloatingActions(
|
FloatingActions(
|
||||||
transitionState = transitionState,
|
transitionState = transitionState,
|
||||||
@@ -74,12 +76,13 @@ fun BoxScope.FloatingActionsContainerWithScrollToTop(
|
|||||||
fun BoxScope.FloatingActionsContainerWithScrollToTop(
|
fun BoxScope.FloatingActionsContainerWithScrollToTop(
|
||||||
scrollState: ScrollState,
|
scrollState: ScrollState,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
visible: Boolean = true,
|
||||||
iconId: Int? = null,
|
iconId: Int? = null,
|
||||||
onClick: (() -> Unit)? = null,
|
onClick: (() -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
val transitionState = remember {
|
val transitionState = remember {
|
||||||
MutableTransitionState(false to false)
|
MutableTransitionState<ScrollingInfo?>(ScrollingInfo())
|
||||||
}.apply { targetState = scrollState.isScrollingDown() to false }
|
}.apply { targetState = if (visible) scrollState.scrollingInfo() else null }
|
||||||
|
|
||||||
FloatingActions(
|
FloatingActions(
|
||||||
transitionState = transitionState,
|
transitionState = transitionState,
|
||||||
@@ -92,13 +95,13 @@ fun BoxScope.FloatingActionsContainerWithScrollToTop(
|
|||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
@Composable
|
@Composable
|
||||||
fun BoxScope.FloatingActions(
|
fun BoxScope.FloatingActions(
|
||||||
transitionState: MutableTransitionState<Pair<Boolean, Boolean>>,
|
transitionState: MutableTransitionState<ScrollingInfo?>,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onScrollToTop: (suspend () -> Unit)? = null,
|
onScrollToTop: (suspend () -> Unit)? = null,
|
||||||
iconId: Int? = null,
|
iconId: Int? = null,
|
||||||
onClick: (() -> Unit)? = null,
|
onClick: (() -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
val transition = updateTransition(transitionState, "FloatingActionsContainer")
|
val transition = updateTransition(transitionState, "")
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
@@ -110,7 +113,7 @@ fun BoxScope.FloatingActions(
|
|||||||
) {
|
) {
|
||||||
onScrollToTop?.let {
|
onScrollToTop?.let {
|
||||||
transition.AnimatedVisibility(
|
transition.AnimatedVisibility(
|
||||||
visible = { it.first && it.second },
|
visible = { it?.isScrollingDown == false && it.isFar },
|
||||||
enter = slideInVertically(tween(500, if (iconId == null) 0 else 100)) { it },
|
enter = slideInVertically(tween(500, if (iconId == null) 0 else 100)) { it },
|
||||||
exit = slideOutVertically(tween(500, 0)) { it },
|
exit = slideOutVertically(tween(500, 0)) { it },
|
||||||
) {
|
) {
|
||||||
@@ -122,6 +125,7 @@ fun BoxScope.FloatingActions(
|
|||||||
onScrollToTop()
|
onScrollToTop()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
enabled = transition.targetState?.isScrollingDown == false && transition.targetState?.isFar == true,
|
||||||
iconId = R.drawable.chevron_up,
|
iconId = R.drawable.chevron_up,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(bottom = 16.dp)
|
.padding(bottom = 16.dp)
|
||||||
@@ -132,13 +136,14 @@ fun BoxScope.FloatingActions(
|
|||||||
iconId?.let {
|
iconId?.let {
|
||||||
onClick?.let {
|
onClick?.let {
|
||||||
transition.AnimatedVisibility(
|
transition.AnimatedVisibility(
|
||||||
visible = { it.first },
|
visible = { it?.isScrollingDown == false },
|
||||||
enter = slideInVertically(tween(500, 0)) { it },
|
enter = slideInVertically(tween(500, 0)) { it },
|
||||||
exit = slideOutVertically(tween(500, 100)) { it },
|
exit = slideOutVertically(tween(500, 100)) { it },
|
||||||
) {
|
) {
|
||||||
PrimaryButton(
|
PrimaryButton(
|
||||||
iconId = iconId,
|
iconId = iconId,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
|
enabled = transition.targetState?.isScrollingDown == false,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(bottom = 16.dp)
|
.padding(bottom = 16.dp)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ fun PrimaryButton(
|
|||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
@DrawableRes iconId: Int,
|
@DrawableRes iconId: Int,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
isEnabled: Boolean = true,
|
enabled: Boolean = true,
|
||||||
) {
|
) {
|
||||||
val (colorPalette) = LocalAppearance.current
|
val (colorPalette) = LocalAppearance.current
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.clip(RoundedCornerShape(16.dp))
|
.clip(RoundedCornerShape(16.dp))
|
||||||
.clickable(enabled = isEnabled, onClick = onClick)
|
.clickable(enabled = enabled, onClick = onClick)
|
||||||
.background(colorPalette.background2)
|
.background(colorPalette.background2)
|
||||||
.size(62.dp)
|
.size(62.dp)
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ fun SecondaryButton(
|
|||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
@DrawableRes iconId: Int,
|
@DrawableRes iconId: Int,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
isEnabled: Boolean = true,
|
enabled: Boolean = true,
|
||||||
) {
|
) {
|
||||||
val (colorPalette) = LocalAppearance.current
|
val (colorPalette) = LocalAppearance.current
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.clip(CircleShape)
|
.clip(CircleShape)
|
||||||
.clickable(enabled = isEnabled, onClick = onClick)
|
.clickable(enabled = enabled, onClick = onClick)
|
||||||
.background(colorPalette.background2)
|
.background(colorPalette.background2)
|
||||||
.size(48.dp)
|
.size(48.dp)
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -277,6 +277,7 @@ fun LocalPlaylistSongs(
|
|||||||
FloatingActionsContainerWithScrollToTop(
|
FloatingActionsContainerWithScrollToTop(
|
||||||
lazyListState = lazyListState,
|
lazyListState = lazyListState,
|
||||||
iconId = R.drawable.shuffle,
|
iconId = R.drawable.shuffle,
|
||||||
|
visible = !reorderingState.isDragging,
|
||||||
onClick = {
|
onClick = {
|
||||||
playlistWithSongs?.songs?.let { songs ->
|
playlistWithSongs?.songs?.let { songs ->
|
||||||
if (songs.isNotEmpty()) {
|
if (songs.isNotEmpty()) {
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
package it.vfsfitvnm.vimusic.utils
|
|
||||||
|
|
||||||
import androidx.compose.foundation.lazy.grid.LazyGridState
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.derivedStateOf
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
|
|
||||||
suspend fun LazyGridState.smoothScrollToTop() {
|
|
||||||
if (firstVisibleItemIndex > layoutInfo.visibleItemsInfo.size) {
|
|
||||||
scrollToItem(layoutInfo.visibleItemsInfo.size)
|
|
||||||
}
|
|
||||||
animateScrollToItem(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun LazyGridState.isScrollingDownToIsFar(): Pair<Boolean, Boolean> {
|
|
||||||
var previousIndex by remember(this) {
|
|
||||||
mutableStateOf(firstVisibleItemIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
var previousScrollOffset by remember(this) {
|
|
||||||
mutableStateOf(firstVisibleItemScrollOffset)
|
|
||||||
}
|
|
||||||
|
|
||||||
return remember(this) {
|
|
||||||
derivedStateOf {
|
|
||||||
if (previousIndex != firstVisibleItemIndex) {
|
|
||||||
previousIndex > firstVisibleItemIndex
|
|
||||||
} else {
|
|
||||||
previousScrollOffset >= firstVisibleItemScrollOffset
|
|
||||||
}.also {
|
|
||||||
previousIndex = firstVisibleItemIndex
|
|
||||||
previousScrollOffset = firstVisibleItemScrollOffset
|
|
||||||
} to (firstVisibleItemIndex > layoutInfo.visibleItemsInfo.size)
|
|
||||||
}
|
|
||||||
}.value
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
package it.vfsfitvnm.vimusic.utils
|
|
||||||
|
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.derivedStateOf
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
|
|
||||||
suspend fun LazyListState.smoothScrollToTop() {
|
|
||||||
if (firstVisibleItemIndex > layoutInfo.visibleItemsInfo.size) {
|
|
||||||
scrollToItem(layoutInfo.visibleItemsInfo.size)
|
|
||||||
}
|
|
||||||
animateScrollToItem(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun LazyListState.isScrollingDownToIsFar(): Pair<Boolean, Boolean> {
|
|
||||||
var previousIndex by remember(this) {
|
|
||||||
mutableStateOf(firstVisibleItemIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
var previousScrollOffset by remember(this) {
|
|
||||||
mutableStateOf(firstVisibleItemScrollOffset)
|
|
||||||
}
|
|
||||||
|
|
||||||
return remember(this) {
|
|
||||||
derivedStateOf {
|
|
||||||
if (previousIndex != firstVisibleItemIndex) {
|
|
||||||
previousIndex > firstVisibleItemIndex
|
|
||||||
} else {
|
|
||||||
previousScrollOffset >= firstVisibleItemScrollOffset
|
|
||||||
}.also {
|
|
||||||
previousIndex = firstVisibleItemIndex
|
|
||||||
previousScrollOffset = firstVisibleItemScrollOffset
|
|
||||||
} to (firstVisibleItemIndex > layoutInfo.visibleItemsInfo.size)
|
|
||||||
}
|
|
||||||
}.value
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package it.vfsfitvnm.vimusic.utils
|
|
||||||
|
|
||||||
import androidx.compose.foundation.ScrollState
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.derivedStateOf
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun ScrollState.isScrollingDown(): Boolean {
|
|
||||||
var previousValue by remember(this) {
|
|
||||||
mutableStateOf(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return remember(this) {
|
|
||||||
derivedStateOf {
|
|
||||||
(previousValue >= value).also {
|
|
||||||
previousValue = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.value
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package it.vfsfitvnm.vimusic.utils
|
||||||
|
|
||||||
|
import androidx.compose.foundation.ScrollState
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
import androidx.compose.foundation.lazy.grid.LazyGridState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
|
||||||
|
data class ScrollingInfo(
|
||||||
|
val isScrollingDown: Boolean = false,
|
||||||
|
val isFar: Boolean = false
|
||||||
|
) {
|
||||||
|
fun and(condition: Boolean) =
|
||||||
|
// copy(isScrollingDown = isScrollingDown && condition, isFar = isFar && condition)
|
||||||
|
if (condition) this else copy(isScrollingDown = !isScrollingDown, isFar = !isFar)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LazyListState.scrollingInfo(): ScrollingInfo {
|
||||||
|
var previousIndex by remember(this) {
|
||||||
|
mutableStateOf(firstVisibleItemIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
var previousScrollOffset by remember(this) {
|
||||||
|
mutableStateOf(firstVisibleItemScrollOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
return remember(this) {
|
||||||
|
derivedStateOf {
|
||||||
|
val isScrollingDown = if (previousIndex == firstVisibleItemIndex) {
|
||||||
|
firstVisibleItemScrollOffset > previousScrollOffset
|
||||||
|
} else {
|
||||||
|
firstVisibleItemIndex > previousIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
val isFar = firstVisibleItemIndex > layoutInfo.visibleItemsInfo.size
|
||||||
|
|
||||||
|
previousIndex = firstVisibleItemIndex
|
||||||
|
previousScrollOffset = firstVisibleItemScrollOffset
|
||||||
|
|
||||||
|
ScrollingInfo(isScrollingDown, isFar)
|
||||||
|
}
|
||||||
|
}.value
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LazyGridState.scrollingInfo(): ScrollingInfo {
|
||||||
|
var previousIndex by remember(this) {
|
||||||
|
mutableStateOf(firstVisibleItemIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
var previousScrollOffset by remember(this) {
|
||||||
|
mutableStateOf(firstVisibleItemScrollOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
return remember(this) {
|
||||||
|
derivedStateOf {
|
||||||
|
val isScrollingDown = if (previousIndex == firstVisibleItemIndex) {
|
||||||
|
firstVisibleItemScrollOffset > previousScrollOffset
|
||||||
|
} else {
|
||||||
|
firstVisibleItemIndex > previousIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
val isFar = firstVisibleItemIndex > layoutInfo.visibleItemsInfo.size
|
||||||
|
|
||||||
|
previousIndex = firstVisibleItemIndex
|
||||||
|
previousScrollOffset = firstVisibleItemScrollOffset
|
||||||
|
|
||||||
|
ScrollingInfo(isScrollingDown, isFar)
|
||||||
|
}
|
||||||
|
}.value
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ScrollState.scrollingInfo(): ScrollingInfo {
|
||||||
|
var previousValue by remember(this) {
|
||||||
|
mutableStateOf(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return remember(this) {
|
||||||
|
derivedStateOf {
|
||||||
|
val isScrollingDown = value > previousValue
|
||||||
|
|
||||||
|
previousValue = value
|
||||||
|
|
||||||
|
ScrollingInfo(isScrollingDown, false)
|
||||||
|
}
|
||||||
|
}.value
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package it.vfsfitvnm.vimusic.utils
|
||||||
|
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
import androidx.compose.foundation.lazy.grid.LazyGridState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
|
||||||
|
suspend fun LazyGridState.smoothScrollToTop() {
|
||||||
|
if (firstVisibleItemIndex > layoutInfo.visibleItemsInfo.size) {
|
||||||
|
scrollToItem(layoutInfo.visibleItemsInfo.size)
|
||||||
|
}
|
||||||
|
animateScrollToItem(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun LazyListState.smoothScrollToTop() {
|
||||||
|
if (firstVisibleItemIndex > layoutInfo.visibleItemsInfo.size) {
|
||||||
|
scrollToItem(layoutInfo.visibleItemsInfo.size)
|
||||||
|
}
|
||||||
|
animateScrollToItem(0)
|
||||||
|
}
|
||||||
@@ -52,6 +52,9 @@ class ReorderingState(
|
|||||||
internal var indexesToAnimate = mutableStateMapOf<Int, Animatable<Int, AnimationVector1D>>()
|
internal var indexesToAnimate = mutableStateMapOf<Int, Animatable<Int, AnimationVector1D>>()
|
||||||
private var animatablesPool: AnimatablesPool<Int, AnimationVector1D>? = null
|
private var animatablesPool: AnimatablesPool<Int, AnimationVector1D>? = null
|
||||||
|
|
||||||
|
val isDragging: Boolean
|
||||||
|
get() = draggingIndex != -1
|
||||||
|
|
||||||
fun onDragStart(index: Int) {
|
fun onDragStart(index: Int) {
|
||||||
overscrolled = 0
|
overscrolled = 0
|
||||||
itemInfo = lazyListState.layoutInfo.visibleItemsInfo.find {
|
itemInfo = lazyListState.layoutInfo.visibleItemsInfo.find {
|
||||||
|
|||||||
Reference in New Issue
Block a user