Browse Source

Fix Scrollbar when the list contains sticky header (#8181)

* Fix Scrollbar when the list contains sticky header

* Fix VerticalFastScroller when the list contains sticky header

* exposé
Ivan Iskandar 2 years ago
parent
commit
fba244423f

+ 2 - 1
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)

+ 15 - 3
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)

+ 13 - 1
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)