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