commonMain.androidx.compose.foundation.lazy.grid.LazyGridMeasureResult.kt Maven / Gradle / Ivy
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.compose.foundation.lazy.grid
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.snapping.offsetOnMainAxis
import androidx.compose.ui.layout.MeasureResult
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.util.fastForEach
import kotlinx.coroutines.CoroutineScope
/** The result of the measure pass for lazy grid layout. */
internal class LazyGridMeasureResult(
// properties defining the scroll position:
/** The new first visible line of items. */
val firstVisibleLine: LazyGridMeasuredLine?,
/** The new value for [LazyGridState.firstVisibleItemScrollOffset]. */
val firstVisibleLineScrollOffset: Int,
/** True if there is some space available to continue scrolling in the forward direction. */
val canScrollForward: Boolean,
/** The amount of scroll consumed during the measure pass. */
val consumedScroll: Float,
/** MeasureResult defining the layout. */
private val measureResult: MeasureResult,
/** True when extra remeasure is required. */
val remeasureNeeded: Boolean,
/** Scope for animations. */
val coroutineScope: CoroutineScope,
/** Density of the last measure. */
val density: Density,
/** Amount of slots we have in each line. */
val slotsPerLine: Int,
/** Finds items on a line and their measurement constraints. Used for prefetching. */
val prefetchInfoRetriever: (line: Int) -> List>,
// properties representing the info needed for LazyListLayoutInfo:
/** see [LazyGridLayoutInfo.visibleItemsInfo] */
override val visibleItemsInfo: List,
/** see [LazyGridLayoutInfo.viewportStartOffset] */
override val viewportStartOffset: Int,
/** see [LazyGridLayoutInfo.viewportEndOffset] */
override val viewportEndOffset: Int,
/** see [LazyGridLayoutInfo.totalItemsCount] */
override val totalItemsCount: Int,
/** see [LazyGridLayoutInfo.reverseLayout] */
override val reverseLayout: Boolean,
/** see [LazyGridLayoutInfo.orientation] */
override val orientation: Orientation,
/** see [LazyGridLayoutInfo.afterContentPadding] */
override val afterContentPadding: Int,
/** see [LazyGridLayoutInfo.mainAxisItemSpacing] */
override val mainAxisItemSpacing: Int
) : LazyGridLayoutInfo, MeasureResult by measureResult {
val canScrollBackward
get() = (firstVisibleLine?.index ?: 0) != 0 || firstVisibleLineScrollOffset != 0
override val viewportSize: IntSize
get() = IntSize(width, height)
override val beforeContentPadding: Int
get() = -viewportStartOffset
override val maxSpan: Int
get() = slotsPerLine
/**
* Creates a new layout info with applying a scroll [delta] for this layout info. In some cases
* we can apply small scroll deltas by just changing the offsets for each [visibleItemsInfo].
* But we can only do so if after applying the delta we would not need to compose a new item or
* dispose an item which is currently visible. In this case this function will not apply the
* [delta] and return null.
*
* @return new layout info if we can safely apply a passed scroll [delta] to this layout info.
* If If new layout info is returned, only the placement phase is needed to apply new offsets.
* If null is returned, it means we have to rerun the full measure phase to apply the [delta].
*/
fun copyWithScrollDeltaWithoutRemeasure(delta: Int): LazyGridMeasureResult? {
if (
remeasureNeeded ||
visibleItemsInfo.isEmpty() ||
firstVisibleLine == null ||
// applying this delta will change firstVisibleLineScrollOffset
(firstVisibleLineScrollOffset - delta) !in
0 until firstVisibleLine.mainAxisSizeWithSpacings
) {
return null
}
val first = visibleItemsInfo.first()
val last = visibleItemsInfo.last()
if (first.nonScrollableItem || last.nonScrollableItem) {
// non scrollable items require special handling.
return null
}
val canApply =
if (delta < 0) {
// scrolling forward
val deltaToFirstItemChange =
first.offsetOnMainAxis(orientation) + first.mainAxisSizeWithSpacings -
viewportStartOffset
val deltaToLastItemChange =
last.offsetOnMainAxis(orientation) + last.mainAxisSizeWithSpacings -
viewportEndOffset
minOf(deltaToFirstItemChange, deltaToLastItemChange) > -delta
} else {
// scrolling backward
val deltaToFirstItemChange =
viewportStartOffset - first.offsetOnMainAxis(orientation)
val deltaToLastItemChange = viewportEndOffset - last.offsetOnMainAxis(orientation)
minOf(deltaToFirstItemChange, deltaToLastItemChange) > delta
}
return if (canApply) {
visibleItemsInfo.fastForEach { it.applyScrollDelta(delta) }
LazyGridMeasureResult(
firstVisibleLine = firstVisibleLine,
firstVisibleLineScrollOffset = firstVisibleLineScrollOffset - delta,
canScrollForward =
canScrollForward ||
delta > 0, // we scrolled backward, so now we can scroll forward
consumedScroll = delta.toFloat(),
measureResult = measureResult,
remeasureNeeded = remeasureNeeded,
coroutineScope = coroutineScope,
density = density,
slotsPerLine = slotsPerLine,
prefetchInfoRetriever = prefetchInfoRetriever,
visibleItemsInfo = visibleItemsInfo,
viewportStartOffset = viewportStartOffset,
viewportEndOffset = viewportEndOffset,
totalItemsCount = totalItemsCount,
reverseLayout = reverseLayout,
orientation = orientation,
afterContentPadding = afterContentPadding,
mainAxisItemSpacing = mainAxisItemSpacing
)
} else {
null
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy