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.collection.MutableObjectIntMap
import androidx.collection.ObjectIntMap
import androidx.collection.emptyObjectIntMap
import androidx.compose.foundation.internal.checkPrecondition

/**
 * 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.
 */
internal class NearestRangeKeyIndexMap(
    nearestRange: IntRange,
    intervalContent: LazyLayoutIntervalContent<*>
) : LazyLayoutKeyIndexMap {
    private val map: ObjectIntMap
    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
        checkPrecondition(first >= 0) { "negative nearestRange.first" }
        val last = minOf(nearestRange.last, list.size - 1)
        if (last < first) {
            map = emptyObjectIntMap()
            keys = emptyArray()
            keysStartIndex = 0
        } else {
            val size = last - first + 1
            keys = arrayOfNulls(size)
            keysStartIndex = first
            map =
                MutableObjectIntMap(size).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