1 /*
2  * Copyright 2020 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.platform
18 
19 import androidx.compose.ui.Modifier
20 import androidx.compose.ui.internal.JvmDefaultWithCompatibility
21 
22 /** An empty [InspectorInfo] DSL. */
<lambda>null23 val NoInspectorInfo: InspectorInfo.() -> Unit = {}
24 
25 /** Turn on inspector debug information. Used internally during inspection. */
26 var isDebugInspectorInfoEnabled = false
27 
28 /** A compose value that is inspectable by tools. It gives access to private parts of a value. */
29 @JvmDefaultWithCompatibility
30 interface InspectableValue {
31 
32     /** The elements of a compose value. */
33     val inspectableElements: Sequence<ValueElement>
34         get() = emptySequence()
35 
36     /**
37      * Use this name as the reference name shown in tools of this value if there is no explicit
38      * reference name given to the value. Example: a modifier in a modifier list.
39      */
40     val nameFallback: String?
41         get() = null
42 
43     /** Use this value as a readable representation of the value. */
44     val valueOverride: Any?
45         get() = null
46 }
47 
48 /**
49  * A [ValueElement] describes an element of a compose value instance. The [name] typically refers to
50  * a (possibly private) property name with its corresponding [value].
51  */
52 @Suppress("DataClassDefinition") data class ValueElement(val name: String, val value: Any?)
53 
54 /** A builder for an [InspectableValue]. */
55 class InspectorInfo {
56     /** Provides a [InspectableValue.nameFallback]. */
57     var name: String? = null
58 
59     /** Provides a [InspectableValue.valueOverride]. */
60     var value: Any? = null
61 
62     /** Provides a [InspectableValue.inspectableElements]. */
63     val properties = ValueElementSequence()
64 }
65 
66 /** A builder for a sequence of [ValueElement]. */
67 class ValueElementSequence : Sequence<ValueElement> {
68     private val elements = mutableListOf<ValueElement>()
69 
iteratornull70     override fun iterator(): Iterator<ValueElement> = elements.iterator()
71 
72     /** Specify a sub element with name and value. */
73     operator fun set(name: String, value: Any?) {
74         elements.add(ValueElement(name, value))
75     }
76 }
77 
78 /** Implementation of [InspectableValue] based on a builder [InspectorInfo] DSL. */
79 abstract class InspectorValueInfo(private val info: InspectorInfo.() -> Unit) : InspectableValue {
80     private var _values: InspectorInfo? = null
81 
82     private val values: InspectorInfo
83         get() {
84             val valueInfo = _values ?: InspectorInfo().apply(info)
85             _values = valueInfo
86             return valueInfo
87         }
88 
89     override val nameFallback: String?
90         get() = values.name
91 
92     override val valueOverride: Any?
93         get() = values.value
94 
95     override val inspectableElements: Sequence<ValueElement>
96         get() = values.properties
97 }
98 
99 /**
100  * Use this to specify modifier information for compose tooling.
101  *
102  * This factory method allows the specified information to be stripped out by ProGuard in release
103  * builds.
104  *
105  * @sample androidx.compose.ui.samples.InspectableModifierSample
106  */
debugInspectorInfonull107 inline fun debugInspectorInfo(
108     crossinline definitions: InspectorInfo.() -> Unit
109 ): InspectorInfo.() -> Unit =
110     if (isDebugInspectorInfoEnabled) ({ definitions() }) else NoInspectorInfo
111 
112 /**
113  * Use this to group a common set of modifiers and provide [InspectorInfo] for the resulting
114  * modifier.
115  *
116  * @sample androidx.compose.ui.samples.InspectableModifierSample
117  */
118 @Suppress("DeprecatedCallableAddReplaceWith")
119 @Deprecated(
120     "This API will create more invalidations of your modifier than necessary, so it's " +
121         "use is discouraged. Implementing the inspectableProperties method on " +
122         "ModifierNodeElement is the recommended zero-cost alternative to exposing properties " +
123         "on a Modifier to tooling.",
124     level = DeprecationLevel.WARNING,
125 )
inspectablenull126 inline fun Modifier.inspectable(
127     noinline inspectorInfo: InspectorInfo.() -> Unit,
128     factory: Modifier.() -> Modifier
129 ): Modifier = inspectableWrapper(inspectorInfo, factory(Modifier))
130 
131 /** Do not use this explicitly. Instead use [Modifier.inspectable]. */
132 @Suppress("DEPRECATION")
133 @PublishedApi
134 internal fun Modifier.inspectableWrapper(
135     inspectorInfo: InspectorInfo.() -> Unit,
136     wrapped: Modifier
137 ): Modifier {
138     val begin = InspectableModifier(inspectorInfo)
139     return then(begin).then(wrapped).then(begin.end)
140 }
141 
142 @Deprecated(
143     "This API will create more invalidations of your modifier than necessary, so it's " +
144         "use is discouraged. Implementing the inspectableProperties method on " +
145         "ModifierNodeElement is the recommended zero-cost alternative to exposing properties " +
146         "on a Modifier to tooling.",
147     level = DeprecationLevel.WARNING,
148 )
149 /** Annotates a range of modifiers in a chain with inspector metadata. */
150 class InspectableModifier(inspectorInfo: InspectorInfo.() -> Unit) :
151     Modifier.Element, InspectorValueInfo(inspectorInfo) {
152     inner class End : Modifier.Element
153 
154     val end = End()
155 }
156