Do not show floating action buttons when reordering items

This commit is contained in:
vfsfitvnm
2022-10-06 18:01:48 +02:00
parent b30b282628
commit 5b47484a40
10 changed files with 142 additions and 120 deletions

View File

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

View File

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

View File

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

View File

@@ -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()) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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