All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.androidx.compose.foundation.lazy.layout.LazyLayoutKeyIndexMap.kt Maven / Gradle / Ivy

/*
 * Copyright 2022 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.layout

import androidx.compose.foundation.ExperimentalFoundationApi

/**
 * A key-index mapping used inside the [LazyLayoutItemProvider]. It might not contain all items
 * in the lazy layout as optimization, but it must cover items the provider is requesting
 * during layout pass.
 * See [NearestRangeKeyIndexMap] as sample implementation that samples items near current viewport.
 */
internal interface LazyLayoutKeyIndexMap {
    /**
     * @return current index for given [key] or `-1` if not found.
     */
    fun getIndex(key: Any): Int

    /**
     * @return key for a given [index] if it is known, or null otherwise.
     */
    fun getKey(index: Int): Any?

    /**
     * Empty map implementation, always returning `-1` for any key.
     */
    companion object Empty : LazyLayoutKeyIndexMap {
        @Suppress("AutoBoxing")
        override fun getIndex(key: Any): Int = -1
        override fun getKey(index: Int) = null
    }
}

/**
 * Implementation of [LazyLayoutKeyIndexMap] indexing over given [IntRange] of items.
 * Items outside of given range are considered unknown, with null returned as the index.
 */
@ExperimentalFoundationApi
internal class NearestRangeKeyIndexMap(
    nearestRange: IntRange,
    intervalContent: LazyLayoutIntervalContent<*>
) : LazyLayoutKeyIndexMap {
    private val map: Map
    private val keys: Array
    private val keysStartIndex: Int

    init {
        // Traverses the interval [list] in order to create a mapping from the key to the index for
        // all the indexes in the passed [range].
        val list = intervalContent.intervals
        val first = nearestRange.first
        check(first >= 0)
        val last = minOf(nearestRange.last, list.size - 1)
        if (last < first) {
            map = emptyMap()
            keys = emptyArray()
            keysStartIndex = 0
        } else {
            keys = arrayOfNulls(last - first + 1)
            keysStartIndex = first
            map = hashMapOf().also { map ->
                list.forEach(
                    fromIndex = first,
                    toIndex = last,
                ) {
                    val keyFactory = it.value.key
                    val start = maxOf(first, it.startIndex)
                    val end = minOf(last, it.startIndex + it.size - 1)
                    for (i in start..end) {
                        val key =
                            keyFactory?.invoke(i - it.startIndex) ?: getDefaultLazyLayoutKey(i)
                        map[key] = i
                        keys[i - keysStartIndex] = key
                    }
                }
            }
        }
    }

    override fun getIndex(key: Any): Int = map.getOrElse(key) { -1 }

    override fun getKey(index: Int) =
        keys.getOrElse(index - keysStartIndex) { null }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy