1 /*
2  * Copyright 2022 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.grid
18 
19 import androidx.compose.foundation.gestures.Orientation
20 import androidx.compose.ui.unit.IntSize
21 import kotlin.math.max
22 
23 /**
24  * Contains useful information about the currently displayed layout state of lazy grids like
25  * [LazyVerticalGrid]. For example you can get the list of currently displayed items.
26  *
27  * Use [LazyGridState.layoutInfo] to retrieve this
28  */
29 sealed interface LazyGridLayoutInfo {
30     /** The list of [LazyGridItemInfo] representing all the currently visible items. */
31     val visibleItemsInfo: List<LazyGridItemInfo>
32 
33     /**
34      * The start offset of the layout's viewport in pixels. You can think of it as a minimum offset
35      * which would be visible. Usually it is 0, but it can be negative if non-zero
36      * [beforeContentPadding] was applied as the content displayed in the content padding area is
37      * still visible.
38      *
39      * You can use it to understand what items from [visibleItemsInfo] are fully visible.
40      */
41     val viewportStartOffset: Int
42 
43     /**
44      * The end offset of the layout's viewport in pixels. You can think of it as a maximum offset
45      * which would be visible. It is the size of the lazy grid layout minus [beforeContentPadding].
46      *
47      * You can use it to understand what items from [visibleItemsInfo] are fully visible.
48      */
49     val viewportEndOffset: Int
50 
51     /** The total count of items passed to [LazyVerticalGrid]. */
52     val totalItemsCount: Int
53 
54     /**
55      * The size of the viewport in pixels. It is the lazy grid layout size including all the content
56      * paddings.
57      */
58     val viewportSize: IntSize
59 
60     /** The orientation of the lazy grid. */
61     val orientation: Orientation
62 
63     /** True if the direction of scrolling and layout is reversed. */
64     val reverseLayout: Boolean
65 
66     /**
67      * The content padding in pixels applied before the first row/column in the direction of
68      * scrolling. For example it is a top content padding for LazyVerticalGrid with reverseLayout
69      * set to false.
70      */
71     val beforeContentPadding: Int
72 
73     /**
74      * The content padding in pixels applied after the last row/column in the direction of
75      * scrolling. For example it is a bottom content padding for LazyVerticalGrid with reverseLayout
76      * set to false.
77      */
78     val afterContentPadding: Int
79 
80     /** The spacing between lines in the direction of scrolling. */
81     val mainAxisItemSpacing: Int
82 
83     /**
84      * The max line span an item can occupy. This will be the number of columns in vertical grids or
85      * the number of rows in horizontal grids.
86      *
87      * For example if [LazyVerticalGrid] has 3 columns this value will be 3 for each cell.
88      */
89     val maxSpan: Int
90 }
91 
visibleLinesAverageMainAxisSizenull92 internal fun LazyGridLayoutInfo.visibleLinesAverageMainAxisSize(): Int {
93     val isVertical = orientation == Orientation.Vertical
94     val visibleItems = visibleItemsInfo
95     fun lineOf(index: Int): Int =
96         if (isVertical) visibleItemsInfo[index].row else visibleItemsInfo[index].column
97 
98     var totalLinesMainAxisSize = 0
99     var linesCount = 0
100 
101     var lineStartIndex = 0
102     while (lineStartIndex < visibleItems.size) {
103         val currentLine = lineOf(lineStartIndex)
104         if (currentLine == -1) {
105             // Filter out exiting items.
106             ++lineStartIndex
107             continue
108         }
109 
110         var lineMainAxisSize = 0
111         var lineEndIndex = lineStartIndex
112         while (lineEndIndex < visibleItems.size && lineOf(lineEndIndex) == currentLine) {
113             lineMainAxisSize =
114                 max(
115                     lineMainAxisSize,
116                     if (isVertical) {
117                         visibleItems[lineEndIndex].size.height
118                     } else {
119                         visibleItems[lineEndIndex].size.width
120                     }
121                 )
122             ++lineEndIndex
123         }
124 
125         totalLinesMainAxisSize += lineMainAxisSize
126         ++linesCount
127 
128         lineStartIndex = lineEndIndex
129     }
130 
131     return totalLinesMainAxisSize / linesCount + mainAxisItemSpacing
132 }
133 
134 internal val LazyGridLayoutInfo.singleAxisViewportSize: Int
135     get() = if (orientation == Orientation.Vertical) viewportSize.height else viewportSize.width
136