From 2aea08364b8e93916a4b984bd8ed622f1ee0f01d Mon Sep 17 00:00:00 2001 From: Rodrigo Dias Ferreira Date: Mon, 15 Jul 2024 16:07:39 -0300 Subject: [PATCH 1/3] Bug fix: scroll bar thumb jumping when at the end of the LazyStaggeredList with more than one lane Signed-off-by: Rodrigo Dias Ferreira --- .../scrollbar/LazyScrollbarUtilities.kt | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/LazyScrollbarUtilities.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/LazyScrollbarUtilities.kt index 57e567b5d..b7e610695 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/LazyScrollbarUtilities.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/LazyScrollbarUtilities.kt @@ -74,6 +74,19 @@ internal fun itemVisibilityPercentage( viewportEndOffset: Int, ): Float { if (itemSize == 0) return 0f + // TODO: Workaround due to issue b/353143657, monitor if compose team will agree it's an issue + // and fix it, if so, below safeguard probably can be removed + if ( + isItemOutOfViewport( + itemSize = itemSize, + itemStartOffset = itemStartOffset, + viewportStartOffset = viewportStartOffset, + viewportEndOffset = viewportEndOffset, + ) + ) { + return 0f + } + val itemEnd = itemStartOffset + itemSize val startOffset = when { itemStartOffset > viewportStartOffset -> 0 @@ -86,3 +99,28 @@ internal fun itemVisibilityPercentage( val size = itemSize.toFloat() return (size - startOffset - endOffset) / size } + +private fun isItemOutOfViewport( + itemSize: Int, + itemStartOffset: Int, + viewportStartOffset: Int, + viewportEndOffset: Int, +) = isItemBeforeViewport( + itemSize = itemSize, + itemStartOffset = itemStartOffset, + viewportStartOffset = viewportStartOffset, +) || isItemAfterViewport( + itemStartOffset = itemStartOffset, + viewportEndOffset = viewportEndOffset, +) + +private fun isItemBeforeViewport( + itemSize: Int, + itemStartOffset: Int, + viewportStartOffset: Int, +) = (viewportStartOffset - itemStartOffset) > itemSize + +private fun isItemAfterViewport( + itemStartOffset: Int, + viewportEndOffset: Int, +) = itemStartOffset > viewportEndOffset From 32514c1b68d0557a330c4563e0ee5506d40754c9 Mon Sep 17 00:00:00 2001 From: Rodrigo Dias Ferreira Date: Sun, 11 Aug 2024 12:53:36 -0300 Subject: [PATCH 2/3] Fix the scroll bar thumb jumping a little bit due to lack of smothness on interpolatedIndex --- .../component/scrollbar/LazyScrollbarUtilities.kt | 2 +- .../component/scrollbar/ScrollbarExt.kt | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/LazyScrollbarUtilities.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/LazyScrollbarUtilities.kt index b7e610695..9326613b7 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/LazyScrollbarUtilities.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/LazyScrollbarUtilities.kt @@ -33,7 +33,7 @@ import kotlin.math.abs * @return a [Float] in the range [firstItemPosition..nextItemPosition) where nextItemPosition * is the index of the consecutive item along the major axis. * */ -internal inline fun LazyState.interpolateFirstItemIndex( +internal inline fun LazyState.interpolateIndex( visibleItems: List, crossinline itemSize: LazyState.(LazyStateItem) -> Int, crossinline offset: LazyState.(LazyStateItem) -> Int, diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ScrollbarExt.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ScrollbarExt.kt index 3fcc8f2c0..946b8a3b1 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ScrollbarExt.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ScrollbarExt.kt @@ -51,7 +51,7 @@ fun LazyListState.scrollbarState( if (visibleItemsInfo.isEmpty()) return@snapshotFlow null val firstIndex = min( - a = interpolateFirstItemIndex( + a = interpolateIndex( visibleItems = visibleItemsInfo, itemSize = { it.size }, offset = { it.offset }, @@ -114,8 +114,11 @@ fun LazyGridState.scrollbarState( if (visibleItemsInfo.isEmpty()) return@snapshotFlow null val firstIndex = min( - a = interpolateFirstItemIndex( - visibleItems = visibleItemsInfo, + a = interpolateIndex( + visibleItems = when (layoutInfo.orientation) { + Orientation.Vertical -> layoutInfo.visibleItemsInfo.filter { it.row == 0 } + Orientation.Horizontal -> layoutInfo.visibleItemsInfo.filter { it.column == 0 } + }, itemSize = { layoutInfo.orientation.valueOf(it.size) }, offset = { layoutInfo.orientation.valueOf(it.offset) }, nextItemOnMainAxis = { first -> @@ -188,8 +191,8 @@ fun LazyStaggeredGridState.scrollbarState( if (visibleItemsInfo.isEmpty()) return@snapshotFlow null val firstIndex = min( - a = interpolateFirstItemIndex( - visibleItems = visibleItemsInfo, + a = interpolateIndex( + visibleItems = layoutInfo.visibleItemsInfo.filter { it.lane == 0 }, itemSize = { layoutInfo.orientation.valueOf(it.size) }, offset = { layoutInfo.orientation.valueOf(it.offset) }, nextItemOnMainAxis = { first -> From 3c7e48565921e5e5acb04dfb417d4e725ad4f75a Mon Sep 17 00:00:00 2001 From: Rodrigo Dias Ferreira Date: Mon, 12 Aug 2024 09:31:32 -0300 Subject: [PATCH 3/3] Returning the function name: interpolateIndex to the original name: interpolateFirstItemIndex --- .../component/scrollbar/LazyScrollbarUtilities.kt | 2 +- .../core/designsystem/component/scrollbar/ScrollbarExt.kt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/LazyScrollbarUtilities.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/LazyScrollbarUtilities.kt index 9326613b7..b7e610695 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/LazyScrollbarUtilities.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/LazyScrollbarUtilities.kt @@ -33,7 +33,7 @@ import kotlin.math.abs * @return a [Float] in the range [firstItemPosition..nextItemPosition) where nextItemPosition * is the index of the consecutive item along the major axis. * */ -internal inline fun LazyState.interpolateIndex( +internal inline fun LazyState.interpolateFirstItemIndex( visibleItems: List, crossinline itemSize: LazyState.(LazyStateItem) -> Int, crossinline offset: LazyState.(LazyStateItem) -> Int, diff --git a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ScrollbarExt.kt b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ScrollbarExt.kt index 946b8a3b1..051c8b5ab 100644 --- a/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ScrollbarExt.kt +++ b/core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/component/scrollbar/ScrollbarExt.kt @@ -51,7 +51,7 @@ fun LazyListState.scrollbarState( if (visibleItemsInfo.isEmpty()) return@snapshotFlow null val firstIndex = min( - a = interpolateIndex( + a = interpolateFirstItemIndex( visibleItems = visibleItemsInfo, itemSize = { it.size }, offset = { it.offset }, @@ -114,7 +114,7 @@ fun LazyGridState.scrollbarState( if (visibleItemsInfo.isEmpty()) return@snapshotFlow null val firstIndex = min( - a = interpolateIndex( + a = interpolateFirstItemIndex( visibleItems = when (layoutInfo.orientation) { Orientation.Vertical -> layoutInfo.visibleItemsInfo.filter { it.row == 0 } Orientation.Horizontal -> layoutInfo.visibleItemsInfo.filter { it.column == 0 } @@ -191,7 +191,7 @@ fun LazyStaggeredGridState.scrollbarState( if (visibleItemsInfo.isEmpty()) return@snapshotFlow null val firstIndex = min( - a = interpolateIndex( + a = interpolateFirstItemIndex( visibleItems = layoutInfo.visibleItemsInfo.filter { it.lane == 0 }, itemSize = { layoutInfo.orientation.valueOf(it.size) }, offset = { layoutInfo.orientation.valueOf(it.offset) },