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.ui.modifier
18 
19 import androidx.compose.runtime.getValue
20 import androidx.compose.runtime.mutableStateMapOf
21 import androidx.compose.runtime.mutableStateOf
22 import androidx.compose.runtime.setValue
23 import androidx.compose.ui.internal.checkPrecondition
24 import androidx.compose.ui.internal.requirePrecondition
25 import androidx.compose.ui.node.DelegatableNode
26 import androidx.compose.ui.node.Nodes
27 import androidx.compose.ui.node.visitAncestors
28 import androidx.compose.ui.util.fastMap
29 
30 /**
31  * An opaque key-value holder of [ModifierLocal]s to be used with [ModifierLocalModifierNode].
32  *
33  * @see modifierLocalMapOf
34  */
35 sealed class ModifierLocalMap {
setnull36     internal abstract operator fun <T> set(key: ModifierLocal<T>, value: T)
37 
38     internal abstract operator fun <T> get(key: ModifierLocal<T>): T?
39 
40     internal abstract operator fun contains(key: ModifierLocal<*>): Boolean
41 }
42 
43 internal class SingleLocalMap(private val key: ModifierLocal<*>) : ModifierLocalMap() {
44     private var value: Any? by mutableStateOf(null)
45 
46     internal fun forceValue(value: Any?) {
47         this.value = value
48     }
49 
50     override operator fun <T> set(key: ModifierLocal<T>, value: T) {
51         @Suppress("ExceptionMessage") checkPrecondition(key === this.key)
52         this.value = value
53     }
54 
55     @Suppress("UNCHECKED_CAST", "ExceptionMessage")
56     override operator fun <T> get(key: ModifierLocal<T>): T? {
57         checkPrecondition(key === this.key)
58         return value as? T?
59     }
60 
61     override operator fun contains(key: ModifierLocal<*>): Boolean = key === this.key
62 }
63 
64 internal class BackwardsCompatLocalMap(var element: ModifierLocalProvider<*>) : ModifierLocalMap() {
setnull65     override operator fun <T> set(key: ModifierLocal<T>, value: T) {
66         error("Set is not allowed on a backwards compat provider")
67     }
68 
69     @Suppress("UNCHECKED_CAST", "ExceptionMessage")
getnull70     override operator fun <T> get(key: ModifierLocal<T>): T? {
71         checkPrecondition(key === element.key)
72         return element.value as T
73     }
74 
containsnull75     override operator fun contains(key: ModifierLocal<*>): Boolean = key === element.key
76 }
77 
78 internal class MultiLocalMap(
79     entry1: Pair<ModifierLocal<*>, Any?>,
80     vararg entries: Pair<ModifierLocal<*>, Any?>
81 ) : ModifierLocalMap() {
82     private val map = mutableStateMapOf<ModifierLocal<*>, Any?>()
83 
84     init {
85         map += entry1
86         map.putAll(entries.toMap())
87     }
88 
89     override operator fun <T> set(key: ModifierLocal<T>, value: T) {
90         map[key] = value
91     }
92 
93     override operator fun <T> get(key: ModifierLocal<T>): T? {
94         @Suppress("UNCHECKED_CAST") return map[key] as? T?
95     }
96 
97     override operator fun contains(key: ModifierLocal<*>): Boolean = map.containsKey(key)
98 }
99 
100 internal object EmptyMap : ModifierLocalMap() {
setnull101     override fun <T> set(key: ModifierLocal<T>, value: T) = error("")
102 
103     override fun <T> get(key: ModifierLocal<T>): T = error("")
104 
105     override fun contains(key: ModifierLocal<*>): Boolean = false
106 }
107 
108 /**
109  * A [androidx.compose.ui.Modifier.Node] that is capable of consuming and providing [ModifierLocal]
110  * values.
111  *
112  * This is the [androidx.compose.ui.Modifier.Node] equivalent of the [ModifierLocalConsumer] and
113  * [ModifierLocalProvider] interfaces.
114  *
115  * @sample androidx.compose.ui.samples.JustReadingOrProvidingModifierLocalNodeSample
116  * @see modifierLocalOf
117  * @see ModifierLocal
118  * @see androidx.compose.runtime.CompositionLocal
119  */
120 interface ModifierLocalModifierNode : ModifierLocalReadScope, DelegatableNode {
121     /**
122      * The map of provided ModifierLocal <-> value pairs that this node is providing. This value
123      * must be overridden if you are going to provide any values. It should be overridden as a
124      * field-backed property initialized with values for all of the keys that it will ever possibly
125      * provide.
126      *
127      * By default, this property will be set to an empty map, which means that this node will only
128      * consume [ModifierLocal]s and will not provide any new values.
129      *
130      * If you would like to change a value provided in the map over time, you must use the [provide]
131      * API.
132      *
133      * @see modifierLocalMapOf
134      * @see provide
135      */
136     val providedValues: ModifierLocalMap
137         get() = EmptyMap
138 
139     /**
140      * This method will cause this node to provide a new [value] for [key]. This can be called at
141      * any time on the UI thread, but in order to use this API, [providedValues] must be implemented
142      * and [key] must be a key that was included in it.
143      *
144      * By providing this new value, any [ModifierLocalModifierNode] below it in the tree will read
145      * this [value] when reading [current], until another [ModifierLocalModifierNode] provides a
146      * value for the same [key], however, consuming [ModifierLocalModifierNode]s will NOT be
147      * notified that a new value was provided.
148      */
149     fun <T> provide(key: ModifierLocal<T>, value: T) {
150         requirePrecondition(providedValues !== EmptyMap) {
151             "In order to provide locals you must override providedValues: ModifierLocalMap"
152         }
153         requirePrecondition(providedValues.contains(key)) {
154             "Any provided key must be initially provided in the overridden providedValues: " +
155                 "ModifierLocalMap property. Key $key was not found."
156         }
157         providedValues[key] = value
158     }
159 
160     /**
161      * Read a [ModifierLocal] that was provided by other modifiers to the left of this modifier, or
162      * above this modifier in the layout tree.
163      */
164     override val <T> ModifierLocal<T>.current: T
165         get() {
166             requirePrecondition(node.isAttached) {
167                 "ModifierLocal accessed from an unattached node"
168             }
169             val key = this
170             visitAncestors(Nodes.Locals) {
171                 if (it.providedValues.contains(key)) {
172                     @Suppress("UNCHECKED_CAST") return it.providedValues[key] as T
173                 }
174             }
175             return key.defaultFactory()
176         }
177 }
178 
179 /** Creates an empty [ModifierLocalMap] */
modifierLocalMapOfnull180 fun modifierLocalMapOf(): ModifierLocalMap = EmptyMap
181 
182 /** Creates a [ModifierLocalMap] with a single key and value initialized to null. */
183 fun <T> modifierLocalMapOf(key: ModifierLocal<T>): ModifierLocalMap = SingleLocalMap(key)
184 
185 /**
186  * Creates a [ModifierLocalMap] with a single key and value. The provided [entry] should have
187  * [Pair::first] be the [ModifierLocal] key, and the [Pair::second] be the corresponding value.
188  */
189 fun <T> modifierLocalMapOf(entry: Pair<ModifierLocal<T>, T>): ModifierLocalMap =
190     SingleLocalMap(entry.first).also { it[entry.first] = entry.second }
191 
192 /** Creates a [ModifierLocalMap] with several keys, all initialized with values of null */
modifierLocalMapOfnull193 fun modifierLocalMapOf(
194     key1: ModifierLocal<*>,
195     key2: ModifierLocal<*>,
196     vararg keys: ModifierLocal<*>
197 ): ModifierLocalMap =
198     MultiLocalMap(key1 to null, key2 to null, *keys.map { it to null }.toTypedArray())
199 
200 /**
201  * Creates a [ModifierLocalMap] with multiple keys and values. The provided [entries] should have
202  * each item's [Pair::first] be the [ModifierLocal] key, and the [Pair::second] be the corresponding
203  * value.
204  */
modifierLocalMapOfnull205 fun modifierLocalMapOf(
206     entry1: Pair<ModifierLocal<*>, Any>,
207     entry2: Pair<ModifierLocal<*>, Any>,
208     vararg entries: Pair<ModifierLocal<*>, Any>
209 ): ModifierLocalMap = MultiLocalMap(entry1, entry2, *entries)
210 
211 // b/280116113.
212 @Deprecated(
213     message = "Use a different overloaded version of this function",
214     level = DeprecationLevel.HIDDEN
215 )
216 fun modifierLocalMapOf(vararg keys: ModifierLocal<*>): ModifierLocalMap =
217     when (keys.size) {
218         0 -> EmptyMap
219         1 -> SingleLocalMap(keys.first())
220         else ->
221             MultiLocalMap(keys.first() to null, *keys.drop(1).fastMap { it to null }.toTypedArray())
222     }
223 
224 // b/280116113.
225 @Deprecated(
226     message = "Use a different overloaded version of this function",
227     level = DeprecationLevel.HIDDEN
228 )
modifierLocalMapOfnull229 fun modifierLocalMapOf(vararg entries: Pair<ModifierLocal<*>, Any>): ModifierLocalMap =
230     when (entries.size) {
231         0 -> EmptyMap
232         1 -> MultiLocalMap(entries.first())
233         else -> MultiLocalMap(entries.first(), *entries.drop(1).toTypedArray())
234     }
235