commonMain.androidx.compose.ui.modifier.ModifierLocalModifierNode.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ui-desktop Show documentation
Show all versions of ui-desktop Show documentation
Compose UI primitives. This library contains the primitives that form the Compose UI Toolkit, such as drawing, measurement and layout.
/*
* 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.ui.modifier
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.internal.checkPrecondition
import androidx.compose.ui.internal.requirePrecondition
import androidx.compose.ui.node.DelegatableNode
import androidx.compose.ui.node.Nodes
import androidx.compose.ui.node.visitAncestors
import androidx.compose.ui.util.fastMap
/**
* An opaque key-value holder of [ModifierLocal]s to be used with [ModifierLocalModifierNode].
*
* @see modifierLocalMapOf
*/
sealed class ModifierLocalMap {
internal abstract operator fun set(key: ModifierLocal, value: T)
internal abstract operator fun get(key: ModifierLocal): T?
internal abstract operator fun contains(key: ModifierLocal<*>): Boolean
}
internal class SingleLocalMap(
private val key: ModifierLocal<*>
) : ModifierLocalMap() {
private var value: Any? by mutableStateOf(null)
internal fun forceValue(value: Any?) {
this.value = value
}
override operator fun set(key: ModifierLocal, value: T) {
@Suppress("ExceptionMessage")
checkPrecondition(key === this.key)
this.value = value
}
@Suppress("UNCHECKED_CAST", "ExceptionMessage")
override operator fun get(key: ModifierLocal): T? {
checkPrecondition(key === this.key)
return value as? T?
}
override operator fun contains(key: ModifierLocal<*>): Boolean = key === this.key
}
internal class BackwardsCompatLocalMap(
var element: ModifierLocalProvider<*>
) : ModifierLocalMap() {
override operator fun set(key: ModifierLocal, value: T) {
error("Set is not allowed on a backwards compat provider")
}
@Suppress("UNCHECKED_CAST", "ExceptionMessage")
override operator fun get(key: ModifierLocal): T? {
checkPrecondition(key === element.key)
return element.value as T
}
override operator fun contains(key: ModifierLocal<*>): Boolean = key === element.key
}
internal class MultiLocalMap(
entry1: Pair, Any?>,
vararg entries: Pair, Any?>
) : ModifierLocalMap() {
private val map = mutableStateMapOf, Any?>()
init {
map += entry1
map.putAll(entries.toMap())
}
override operator fun set(key: ModifierLocal, value: T) {
map[key] = value
}
override operator fun get(key: ModifierLocal): T? {
@Suppress("UNCHECKED_CAST")
return map[key] as? T?
}
override operator fun contains(key: ModifierLocal<*>): Boolean = map.containsKey(key)
}
internal object EmptyMap : ModifierLocalMap() {
override fun set(key: ModifierLocal, value: T) = error("")
override fun get(key: ModifierLocal): T = error("")
override fun contains(key: ModifierLocal<*>): Boolean = false
}
/**
* A [androidx.compose.ui.Modifier.Node] that is capable of consuming and providing [ModifierLocal]
* values.
*
* This is the [androidx.compose.ui.Modifier.Node] equivalent of the [ModifierLocalConsumer]
* and [ModifierLocalProvider] interfaces.
*
* @sample androidx.compose.ui.samples.JustReadingOrProvidingModifierLocalNodeSample
*
* @see modifierLocalOf
* @see ModifierLocal
* @see androidx.compose.runtime.CompositionLocal
*/
interface ModifierLocalModifierNode : ModifierLocalReadScope, DelegatableNode {
/**
* The map of provided ModifierLocal <-> value pairs that this node is providing. This value
* must be overridden if you are going to provide any values. It should be overridden as a
* field-backed property initialized with values for all of the keys that it will ever possibly
* provide.
*
* By default, this property will be set to an empty map, which means that this node will only
* consume [ModifierLocal]s and will not provide any new values.
*
* If you would like to change a value provided in the map over time, you must use the [provide]
* API.
*
* @see modifierLocalMapOf
* @see provide
*/
val providedValues: ModifierLocalMap get() = EmptyMap
/**
* This method will cause this node to provide a new [value] for [key]. This can be called at
* any time on the UI thread, but in order to use this API, [providedValues] must be
* implemented and [key] must be a key that was included in it.
*
* By providing this new value, any [ModifierLocalModifierNode] below it in the tree will read
* this [value] when reading [current], until another [ModifierLocalModifierNode] provides a
* value for the same [key], however, consuming [ModifierLocalModifierNode]s will NOT be
* notified that a new value was provided.
*/
fun provide(key: ModifierLocal, value: T) {
requirePrecondition(providedValues !== EmptyMap) {
"In order to provide locals you must override providedValues: ModifierLocalMap"
}
requirePrecondition(providedValues.contains(key)) {
"Any provided key must be initially provided in the overridden providedValues: " +
"ModifierLocalMap property. Key $key was not found."
}
providedValues[key] = value
}
/**
* Read a [ModifierLocal] that was provided by other modifiers to the left of this modifier,
* or above this modifier in the layout tree.
*/
override val ModifierLocal.current: T
get() {
requirePrecondition(node.isAttached) {
"ModifierLocal accessed from an unattached node"
}
val key = this
visitAncestors(Nodes.Locals) {
if (it.providedValues.contains(key)) {
@Suppress("UNCHECKED_CAST")
return it.providedValues[key] as T
}
}
return key.defaultFactory()
}
}
/**
* Creates an empty [ModifierLocalMap]
*/
fun modifierLocalMapOf(): ModifierLocalMap = EmptyMap
/**
* Creates a [ModifierLocalMap] with a single key and value initialized to null.
*/
fun modifierLocalMapOf(
key: ModifierLocal
): ModifierLocalMap = SingleLocalMap(key)
/**
* Creates a [ModifierLocalMap] with a single key and value. The provided [entry] should have
* [Pair::first] be the [ModifierLocal] key, and the [Pair::second] be the corresponding value.
*/
fun modifierLocalMapOf(
entry: Pair, T>
): ModifierLocalMap = SingleLocalMap(entry.first).also { it[entry.first] = entry.second }
/**
* Creates a [ModifierLocalMap] with several keys, all initialized with values of null
*/
fun modifierLocalMapOf(
key1: ModifierLocal<*>,
key2: ModifierLocal<*>,
vararg keys: ModifierLocal<*>
): ModifierLocalMap = MultiLocalMap(
key1 to null,
key2 to null,
*keys.map { it to null }.toTypedArray()
)
/**
* Creates a [ModifierLocalMap] with multiple keys and values. The provided [entries] should have
* each item's [Pair::first] be the [ModifierLocal] key, and the [Pair::second] be the
* corresponding value.
*/
fun modifierLocalMapOf(
entry1: Pair, Any>,
entry2: Pair, Any>,
vararg entries: Pair, Any>
): ModifierLocalMap = MultiLocalMap(entry1, entry2, *entries)
// b/280116113.
@Deprecated(
message = "Use a different overloaded version of this function",
level = DeprecationLevel.HIDDEN
)
fun modifierLocalMapOf(
vararg keys: ModifierLocal<*>
): ModifierLocalMap = when (keys.size) {
0 -> EmptyMap
1 -> SingleLocalMap(keys.first())
else -> MultiLocalMap(
keys.first() to null,
*keys.drop(1).fastMap { it to null }.toTypedArray()
)
}
// b/280116113.
@Deprecated(
message = "Use a different overloaded version of this function",
level = DeprecationLevel.HIDDEN
)
fun modifierLocalMapOf(
vararg entries: Pair, Any>
): ModifierLocalMap = when (entries.size) {
0 -> EmptyMap
1 -> MultiLocalMap(entries.first())
else -> MultiLocalMap(entries.first(), *entries.drop(1).toTypedArray())
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy