diff --git a/app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt index cf8a61598..7ad8a87d9 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt @@ -32,6 +32,7 @@ import eu.kanade.presentation.components.BadgeGroup import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.components.LoadingScreen import eu.kanade.presentation.components.ScrollbarLazyColumn +import eu.kanade.presentation.components.Scroller.STICKY_HEADER_KEY_PREFIX import eu.kanade.presentation.theme.header import eu.kanade.presentation.util.horizontalPadding import eu.kanade.presentation.util.plus @@ -85,7 +86,7 @@ private fun MigrateSourceList( ScrollbarLazyColumn( contentPadding = contentPadding + topPaddingValues, ) { - stickyHeader(key = "header") { + stickyHeader(key = STICKY_HEADER_KEY_PREFIX) { Row( modifier = Modifier .background(MaterialTheme.colorScheme.background) diff --git a/app/src/main/java/eu/kanade/presentation/components/VerticalFastScroller.kt b/app/src/main/java/eu/kanade/presentation/components/VerticalFastScroller.kt index 06e762fb1..05c3dc3a2 100644 --- a/app/src/main/java/eu/kanade/presentation/components/VerticalFastScroller.kt +++ b/app/src/main/java/eu/kanade/presentation/components/VerticalFastScroller.kt @@ -42,9 +42,10 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp +import androidx.compose.ui.util.fastFirstOrNull import androidx.compose.ui.util.fastForEach import androidx.compose.ui.util.fastMaxBy -import eu.kanade.presentation.util.plus +import eu.kanade.presentation.components.Scroller.STICKY_HEADER_KEY_PREFIX import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.collectLatest @@ -53,6 +54,11 @@ import kotlin.math.max import kotlin.math.min import kotlin.math.roundToInt +/** + * Draws vertical fast scroller to a lazy list + * + * Set key with [STICKY_HEADER_KEY_PREFIX] prefix to any sticky header item in the list. + */ @Composable fun VerticalFastScroller( listState: LazyListState, @@ -386,7 +392,8 @@ private fun computeScrollRange(state: LazyGridState): Int { private fun computeScrollOffset(state: LazyListState): Int { if (state.layoutInfo.totalItemsCount == 0) return 0 val visibleItems = state.layoutInfo.visibleItemsInfo - val startChild = visibleItems.first() + val startChild = visibleItems + .fastFirstOrNull { (it.key as? String)?.startsWith(STICKY_HEADER_KEY_PREFIX)?.not() ?: true }!! val endChild = visibleItems.last() val minPosition = min(startChild.index, endChild.index) val maxPosition = max(startChild.index, endChild.index) @@ -401,13 +408,18 @@ private fun computeScrollOffset(state: LazyListState): Int { private fun computeScrollRange(state: LazyListState): Int { if (state.layoutInfo.totalItemsCount == 0) return 0 val visibleItems = state.layoutInfo.visibleItemsInfo - val startChild = visibleItems.first() + val startChild = visibleItems + .fastFirstOrNull { (it.key as? String)?.startsWith(STICKY_HEADER_KEY_PREFIX)?.not() ?: true }!! val endChild = visibleItems.last() val laidOutArea = endChild.bottom - startChild.top val laidOutRange = abs(startChild.index - endChild.index) + 1 return (laidOutArea.toFloat() / laidOutRange * state.layoutInfo.totalItemsCount).roundToInt() } +object Scroller { + const val STICKY_HEADER_KEY_PREFIX = "sticky:" +} + private val ThumbLength = 48.dp private val ThumbThickness = 8.dp private val ThumbShape = RoundedCornerShape(ThumbThickness / 2) diff --git a/app/src/main/java/eu/kanade/presentation/util/Scrollbar.kt b/app/src/main/java/eu/kanade/presentation/util/Scrollbar.kt index 38b9f6886..ab7f5b7ab 100644 --- a/app/src/main/java/eu/kanade/presentation/util/Scrollbar.kt +++ b/app/src/main/java/eu/kanade/presentation/util/Scrollbar.kt @@ -62,11 +62,18 @@ import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp +import androidx.compose.ui.util.fastFirstOrNull import androidx.compose.ui.util.fastSumBy +import eu.kanade.presentation.components.Scroller.STICKY_HEADER_KEY_PREFIX import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.collectLatest +/** + * Draws horizontal scrollbar to a LazyList. + * + * Set key with [STICKY_HEADER_KEY_PREFIX] prefix to any sticky header item in the list. + */ fun Modifier.drawHorizontalScrollbar( state: LazyListState, reverseScrolling: Boolean = false, @@ -74,6 +81,11 @@ fun Modifier.drawHorizontalScrollbar( positionOffsetPx: Float = 0f, ): Modifier = drawScrollbar(state, Orientation.Horizontal, reverseScrolling, positionOffsetPx) +/** + * Draws vertical scrollbar to a LazyList. + * + * Set key with [STICKY_HEADER_KEY_PREFIX] prefix to any sticky header item in the list. + */ fun Modifier.drawVerticalScrollbar( state: LazyListState, reverseScrolling: Boolean = false, @@ -106,7 +118,7 @@ private fun Modifier.drawScrollbar( 0f } else { items - .first() + .fastFirstOrNull { (it.key as? String)?.startsWith(STICKY_HEADER_KEY_PREFIX)?.not() ?: true }!! .run { val startPadding = if (reverseDirection) layoutInfo.afterContentPadding else layoutInfo.beforeContentPadding startPadding + ((estimatedItemSize * index - offset) / totalSize * viewportSize)