1 /*
2  * Copyright 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.compose.foundation.lazy.layout
18 
19 import androidx.compose.runtime.State
20 import androidx.compose.runtime.getValue
21 import androidx.compose.runtime.mutableStateOf
22 import androidx.compose.runtime.setValue
23 import androidx.compose.runtime.structuralEqualityPolicy
24 
25 internal class LazyLayoutNearestRangeState(
26     firstVisibleItem: Int,
27     private val slidingWindowSize: Int,
28     private val extraItemCount: Int
29 ) : State<IntRange> {
30 
31     override var value: IntRange by
32         mutableStateOf(
33             calculateNearestItemsRange(firstVisibleItem, slidingWindowSize, extraItemCount),
34             structuralEqualityPolicy()
35         )
36         private set
37 
38     private var lastFirstVisibleItem = firstVisibleItem
39 
updatenull40     fun update(firstVisibleItem: Int) {
41         if (firstVisibleItem != lastFirstVisibleItem) {
42             lastFirstVisibleItem = firstVisibleItem
43             value = calculateNearestItemsRange(firstVisibleItem, slidingWindowSize, extraItemCount)
44         }
45     }
46 
47     private companion object {
48         /**
49          * Returns a range of indexes which contains at least [extraItemCount] items near the first
50          * visible item. It is optimized to return the same range for small changes in the
51          * firstVisibleItem value so we do not regenerate the map on each scroll.
52          */
calculateNearestItemsRangenull53         private fun calculateNearestItemsRange(
54             firstVisibleItem: Int,
55             slidingWindowSize: Int,
56             extraItemCount: Int
57         ): IntRange {
58             val slidingWindowStart = slidingWindowSize * (firstVisibleItem / slidingWindowSize)
59 
60             val start = maxOf(slidingWindowStart - extraItemCount, 0)
61             val end = slidingWindowStart + slidingWindowSize + extraItemCount
62             return start until end
63         }
64     }
65 }
66