1 /*
<lambda>null2  * 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.node
18 
19 import androidx.compose.runtime.collection.MutableVector
20 import androidx.compose.runtime.collection.mutableVectorOf
21 import androidx.compose.ui.Modifier
22 import androidx.compose.ui.geometry.Offset
23 import androidx.compose.ui.graphics.GraphicsContext
24 import androidx.compose.ui.internal.checkPrecondition
25 import androidx.compose.ui.internal.checkPreconditionNotNull
26 import androidx.compose.ui.layout.LayoutCoordinates
27 import androidx.compose.ui.semantics.SemanticsInfo
28 import androidx.compose.ui.unit.Density
29 import androidx.compose.ui.unit.LayoutDirection
30 
31 /**
32  * Represents a [Modifier.Node] which can be a delegate of another [Modifier.Node]. Since
33  * [Modifier.Node] implements this interface, in practice any [Modifier.Node] can be delegated.
34  *
35  * @see DelegatingNode
36  * @see DelegatingNode.delegate
37  */
38 // TODO(lmr): this interface needs a better name
39 interface DelegatableNode {
40     /**
41      * A reference of the [Modifier.Node] that holds this node's position in the node hierarchy. If
42      * the node is a delegate of another node, this will point to the root delegating node that is
43      * actually part of the node tree. Otherwise, this will point to itself.
44      */
45     val node: Modifier.Node
46 
47     /**
48      * Invoked when the density changes for this node. This affects Dp to pixel conversions, and can
49      * cause coordinates / other values to change.
50      *
51      * Changes to density will automatically invalidate layout / draw modifiers, as layout,
52      * measurement, and draw depend on density. This callback can be used to update any other node
53      * state that depends on density, outside of these phases. Density can be retrieved inside a
54      * node by using [androidx.compose.ui.node.requireDensity].
55      */
56     fun onDensityChange() {}
57 
58     /**
59      * Invoked when the layout direction changes for this node. This can affect the layout and
60      * drawing of nodes.
61      *
62      * Changes to layout direction will automatically invalidate layout / draw modifiers, as layout,
63      * measurement, and draw depend on layout direction. This callback can be used to update any
64      * other node state that depends on layout direction, outside of these phases. Layout direction
65      * can be retrieved inside a node by using [androidx.compose.ui.node.requireLayoutDirection].
66      */
67     fun onLayoutDirectionChange() {}
68 
69     fun interface RegistrationHandle {
70         fun unregister()
71     }
72 }
73 
74 internal val DelegatableNode.isDelegationRoot: Boolean
75     get() = node === this
76 
77 // TREE TRAVERSAL APIS
78 // For now, traversing the node tree and layout node tree will be kept out of public API.
79 // However, when we add APIs here, we should add corresponding tests.
80 // Some internal modifiers, such as Focus, PointerInput, etc. will all need to utilize this
81 // a bit, but I think we want to avoid giving this power to public API just yet. We can
82 // introduce this as valid cases arise
visitAncestorsnull83 internal inline fun DelegatableNode.visitAncestors(
84     mask: Int,
85     includeSelf: Boolean = false,
86     block: (Modifier.Node) -> Unit
87 ) {
88     // TODO(lmr): we might want to add some safety wheels to prevent this from being called
89     //  while one of the chains is being diffed / updated. Although that might only be
90     //  necessary for visiting subtree.
91     checkPrecondition(node.isAttached) { "visitAncestors called on an unattached node" }
92     var node: Modifier.Node? = if (includeSelf) node else node.parent
93     var layout: LayoutNode? = requireLayoutNode()
94     while (layout != null) {
95         val head = layout.nodes.head
96         if (head.aggregateChildKindSet and mask != 0) {
97             while (node != null) {
98                 if (node.kindSet and mask != 0) {
99                     block(node)
100                 }
101                 node = node.parent
102             }
103         }
104         layout = layout.parent
105         node = layout?.nodes?.tail
106     }
107 }
108 
109 @Suppress("unused")
nearestAncestornull110 internal fun DelegatableNode.nearestAncestor(mask: Int): Modifier.Node? {
111     checkPrecondition(node.isAttached) { "nearestAncestor called on an unattached node" }
112     var node: Modifier.Node? = node.parent
113     var layout: LayoutNode? = requireLayoutNode()
114     while (layout != null) {
115         val head = layout.nodes.head
116         if (head.aggregateChildKindSet and mask != 0) {
117             while (node != null) {
118                 if (node.kindSet and mask != 0) {
119                     return node
120                 }
121                 node = node.parent
122             }
123         }
124         layout = layout.parent
125         node = layout?.nodes?.tail
126     }
127     return null
128 }
129 
LayoutNodenull130 private fun LayoutNode.getChildren(zOrder: Boolean) =
131     if (zOrder) {
132         zSortedChildren
133     } else {
134         _children
135     }
136 
MutableVectornull137 private fun MutableVector<Modifier.Node>.addLayoutNodeChildren(
138     node: Modifier.Node,
139     zOrder: Boolean,
140 ) {
141     node.requireLayoutNode().getChildren(zOrder).forEachReversed { add(it.nodes.head) }
142 }
143 
visitChildrennull144 internal inline fun DelegatableNode.visitChildren(
145     mask: Int,
146     zOrder: Boolean,
147     block: (Modifier.Node) -> Unit
148 ) {
149     checkPrecondition(node.isAttached) { "visitChildren called on an unattached node" }
150     val branches = mutableVectorOf<Modifier.Node>()
151     val child = node.child
152     if (child == null) branches.addLayoutNodeChildren(node, zOrder) else branches.add(child)
153     while (branches.isNotEmpty()) {
154         val branch = branches.removeAt(branches.lastIndex)
155         if (branch.aggregateChildKindSet and mask == 0) {
156             branches.addLayoutNodeChildren(branch, zOrder)
157             // none of these nodes match the mask, so don't bother traversing them
158             continue
159         }
160         var node: Modifier.Node? = branch
161         while (node != null) {
162             if (node.kindSet and mask != 0) {
163                 block(node)
164                 break
165             }
166             node = node.child
167         }
168     }
169 }
170 
171 /**
172  * visit the shallow tree of children of a given mask, but if block returns true, we will continue
173  * traversing below it
174  */
visitSubtreeIfnull175 internal inline fun DelegatableNode.visitSubtreeIf(
176     mask: Int,
177     zOrder: Boolean,
178     block: (Modifier.Node) -> Boolean
179 ) {
180     checkPrecondition(node.isAttached) { "visitSubtreeIf called on an unattached node" }
181     val branches = mutableVectorOf<Modifier.Node>()
182     val child = node.child
183     if (child == null) branches.addLayoutNodeChildren(node, zOrder) else branches.add(child)
184     outer@ while (branches.isNotEmpty()) {
185         val branch = branches.removeAt(branches.size - 1)
186         if (branch.aggregateChildKindSet and mask != 0) {
187             var node: Modifier.Node? = branch
188             while (node != null) {
189                 if (node.kindSet and mask != 0) {
190                     val diveDeeper = block(node)
191                     if (!diveDeeper) continue@outer
192                 }
193                 node = node.child
194             }
195         }
196         branches.addLayoutNodeChildren(branch, zOrder)
197     }
198 }
199 
visitLocalDescendantsnull200 internal inline fun DelegatableNode.visitLocalDescendants(
201     mask: Int,
202     block: (Modifier.Node) -> Unit
203 ) = visitLocalDescendants(mask = mask, includeSelf = false, block = block)
204 
205 internal inline fun DelegatableNode.visitLocalDescendants(
206     mask: Int,
207     includeSelf: Boolean = false,
208     block: (Modifier.Node) -> Unit
209 ) {
210     checkPrecondition(node.isAttached) { "visitLocalDescendants called on an unattached node" }
211     val self = node
212     if (self.aggregateChildKindSet and mask == 0) return
213     var next = if (includeSelf) self else self.child
214     while (next != null) {
215         if (next.kindSet and mask != 0) {
216             block(next)
217         }
218         next = next.child
219     }
220 }
221 
visitLocalAncestorsnull222 internal inline fun DelegatableNode.visitLocalAncestors(mask: Int, block: (Modifier.Node) -> Unit) {
223     checkPrecondition(node.isAttached) { "visitLocalAncestors called on an unattached node" }
224     var next = node.parent
225     while (next != null) {
226         if (next.kindSet and mask != 0) {
227             block(next)
228         }
229         next = next.parent
230     }
231 }
232 
visitSelfAndLocalDescendantsnull233 internal inline fun <reified T> DelegatableNode.visitSelfAndLocalDescendants(
234     type: NodeKind<T>,
235     block: (T) -> Unit
236 ) = visitLocalDescendants(mask = type.mask, includeSelf = true) { it.dispatchForKind(type, block) }
237 
visitLocalDescendantsnull238 internal inline fun <reified T> DelegatableNode.visitLocalDescendants(
239     type: NodeKind<T>,
240     block: (T) -> Unit
241 ) = visitLocalDescendants(type.mask) { it.dispatchForKind(type, block) }
242 
visitLocalAncestorsnull243 internal inline fun <reified T> DelegatableNode.visitLocalAncestors(
244     type: NodeKind<T>,
245     block: (T) -> Unit
246 ) = visitLocalAncestors(type.mask) { it.dispatchForKind(type, block) }
247 
visitAncestorsnull248 internal inline fun <reified T> DelegatableNode.visitAncestors(
249     type: NodeKind<T>,
250     includeSelf: Boolean = false,
251     block: (T) -> Unit
252 ) = visitAncestors(type.mask, includeSelf) { it.dispatchForKind(type, block) }
253 
visitSelfAndAncestorsnull254 internal inline fun <reified T> DelegatableNode.visitSelfAndAncestors(
255     type: NodeKind<T>,
256     untilType: NodeKind<*>,
257     block: (T) -> Unit
258 ) {
259     val self = node
260     visitAncestors(type.mask or untilType.mask, true) {
261         if (it !== self && it.isKind(untilType)) return
262         if (it.isKind(type)) {
263             it.dispatchForKind(type, block)
264         }
265     }
266 }
267 
ancestorsnull268 internal inline fun <reified T> DelegatableNode.ancestors(type: NodeKind<T>): List<T>? {
269     var result: MutableList<T>? = null
270     visitAncestors(type) {
271         if (result == null) result = mutableListOf()
272         result?.add(it)
273     }
274     return result
275 }
276 
nearestAncestornull277 internal inline fun <reified T : Any> DelegatableNode.nearestAncestor(type: NodeKind<T>): T? {
278     visitAncestors(type) {
279         return it
280     }
281     return null
282 }
283 
visitChildrennull284 internal inline fun <reified T> DelegatableNode.visitChildren(
285     type: NodeKind<T>,
286     zOrder: Boolean = false,
287     block: (T) -> Unit
288 ) = visitChildren(type.mask, zOrder) { it.dispatchForKind(type, block) }
289 
visitSelfAndChildrennull290 internal inline fun <reified T> DelegatableNode.visitSelfAndChildren(
291     type: NodeKind<T>,
292     zOrder: Boolean = false,
293     block: (T) -> Unit
294 ) {
295     node.dispatchForKind(type, block)
296     visitChildren(type.mask, zOrder) { it.dispatchForKind(type, block) }
297 }
298 
visitSubtreeIfnull299 internal inline fun <reified T> DelegatableNode.visitSubtreeIf(
300     type: NodeKind<T>,
301     zOrder: Boolean = false,
302     block: (T) -> Boolean
303 ) =
304     visitSubtreeIf(type.mask, zOrder) foo@{ node ->
305         node.dispatchForKind(type) { if (!block(it)) return@foo false }
306         true
307     }
308 
visitSubtreenull309 internal inline fun <reified T> DelegatableNode.visitSubtree(
310     type: NodeKind<T>,
311     zOrder: Boolean = false,
312     block: (T) -> Unit
313 ) =
314     visitSubtreeIf(type.mask, zOrder) {
315         it.dispatchForKind(type, block)
316         true
317     }
318 
hasnull319 internal fun DelegatableNode.has(type: NodeKind<*>): Boolean =
320     node.aggregateChildKindSet and type.mask != 0
321 
322 internal fun DelegatableNode.requireCoordinator(kind: NodeKind<*>): NodeCoordinator {
323     val coordinator = node.coordinator!!
324     return if (coordinator.tail !== this) coordinator
325     else if (kind.includeSelfInTraversal) coordinator.wrapped!! else coordinator
326 }
327 
requireLayoutNodenull328 internal fun DelegatableNode.requireLayoutNode(): LayoutNode =
329     checkPreconditionNotNull(node.coordinator) {
330             "Cannot obtain node coordinator. Is the Modifier.Node attached?"
331         }
332         .layoutNode
333 
requireSemanticsInfonull334 internal fun DelegatableNode.requireSemanticsInfo(): SemanticsInfo = requireLayoutNode()
335 
336 internal fun DelegatableNode.requireOwner(): Owner =
337     checkPreconditionNotNull(requireLayoutNode().owner) { "This node does not have an owner." }
338 
339 /**
340  * Requests autofill for the LayoutNode that this [DelegatableNode] is attached to. If the node does
341  * not have any autofill semantic properties set, then the request still may be sent to the Autofill
342  * service, but no response is expected.
343  */
DelegatableNodenull344 fun DelegatableNode.requestAutofill() = requireLayoutNode().requestAutofill()
345 
346 /**
347  * Returns the current [Density] of the LayoutNode that this [DelegatableNode] is attached to. If
348  * the node is not attached, this function will throw an [IllegalStateException].
349  */
350 fun DelegatableNode.requireDensity(): Density = requireLayoutNode().density
351 
352 /** Returns the current [GraphicsContext] of the [Owner] */
353 fun DelegatableNode.requireGraphicsContext(): GraphicsContext = requireOwner().graphicsContext
354 
355 /**
356  * Returns the current [LayoutDirection] of the LayoutNode that this [DelegatableNode] is attached
357  * to. If the node is not attached, this function will throw an [IllegalStateException].
358  */
359 fun DelegatableNode.requireLayoutDirection(): LayoutDirection = requireLayoutNode().layoutDirection
360 
361 /**
362  * Returns the [LayoutCoordinates] of this node.
363  *
364  * To get a signal when the [LayoutCoordinates] become available, or when its parent places it,
365  * implement [LayoutAwareModifierNode].
366  *
367  * @throws IllegalStateException When either this node is not attached, or the [LayoutCoordinates]
368  *   object is not attached.
369  */
370 fun DelegatableNode.requireLayoutCoordinates(): LayoutCoordinates {
371     checkPrecondition(node.isAttached) {
372         "Cannot get LayoutCoordinates, Modifier.Node is not attached."
373     }
374     val coordinates = requireCoordinator(Nodes.Layout).coordinates
375     checkPrecondition(coordinates.isAttached) { "LayoutCoordinates is not attached." }
376     return coordinates
377 }
378 
379 /**
380  * Invalidates the subtree of this layout, including layout, drawing, parent data, etc.
381  *
382  * Calling this method can be a relatively expensive operation as it will cause the entire subtree
383  * to relayout and redraw instead of just parts that are otherwise invalidated. Its use should be
384  * limited to structural changes.
385  */
invalidateSubtreenull386 fun DelegatableNode.invalidateSubtree() {
387     if (node.isAttached) {
388         requireLayoutNode().invalidateSubtree()
389     }
390 }
391 
392 /**
393  * Call this function whenever a scroll chang happened in the LayoutNode that this [DelegatableNode]
394  * is attached to to let the underlying platform know that a scroll event happened in this
395  * [LayoutNode].
396  *
397  * On Android this will trigger a ViewTreeObserver onScrollChanged callback.
398  *
399  * @param delta The scroll delta that was consumed by this node.
400  */
dispatchOnScrollChangednull401 fun DelegatableNode.dispatchOnScrollChanged(delta: Offset) =
402     requireOwner().dispatchOnScrollChanged(delta)
403 
404 // It is safe to do this for LayoutModifierNode because we enforce only a single delegate is
405 // a LayoutModifierNode, however for other NodeKinds that is not true. As a result, this function
406 // is not generic and instead is made specific to LayoutModifierNode.
407 internal fun Modifier.Node.asLayoutModifierNode(): LayoutModifierNode? {
408     if (!isKind(Nodes.Layout)) return null
409     if (this is LayoutModifierNode) return this
410     if (this is DelegatingNode) {
411         var node: Modifier.Node? = delegate
412         while (node != null) {
413             if (node is LayoutModifierNode) return node
414             node =
415                 if (node is DelegatingNode && node.isKind(Nodes.Layout)) {
416                     // NOTE: we can only do this here because we are enforcing that a delegating
417                     // node
418                     // only behaves as a single LayoutModifierNode, not multiple, so we know that if
419                     // the node is of kind "Layout", then one of its delegates has to be a
420                     // LayoutModifierNode and *none of the other delegates of its parent can be*. As
421                     // a
422                     // result, we can avoid allocating a collection here and instead just dive down
423                     // into
424                     // this delegate directly.
425                     node.delegate
426                 } else {
427                     node.child
428                 }
429         }
430     }
431     return null
432 }
433 
434 /**
435  * Since Modifier.Nodes can have multiple delegates of the same type, generally we should use this
436  * method in lieu of casting a modifier.node to a particular NodeKind's interface type. This will
437  * allow us to properly perform operations on the right delegates for a given node instance.
438  *
439  * If a Node implements T, then this will just be called once. if it does NOT implement T, it will
440  * effectively dispatch recursively (although this is implemented iteratively) to all of its direct
441  * delegates where delegate.isKind(kind) is true.
442  *
443  * In the common case of the node implementing the type directly, this method will not allocate,
444  * however it allocates a vector if it dispatches to delegates.
445  */
dispatchForKindnull446 internal inline fun <reified T> Modifier.Node.dispatchForKind(
447     kind: NodeKind<T>,
448     block: (T) -> Unit
449 ) {
450     var stack: MutableVector<Modifier.Node>? = null
451     var node: Modifier.Node? = this
452     while (node != null) {
453         if (node is T) {
454             block(node)
455         } else if (node.isKind(kind) && node is DelegatingNode) {
456             // We jump through a few extra hoops here to avoid the vector allocation in the
457             // case where there is only one delegate node that implements this particular kind.
458             // It is very likely that a delegating node will have one or zero delegates of a
459             // particular kind, so this seems like a worthwhile optimization to make.
460             var count = 0
461             node.forEachImmediateDelegate { next ->
462                 if (next.isKind(kind)) {
463                     count++
464                     if (count == 1) {
465                         node = next
466                     } else {
467                         // turns out there are multiple delegates that implement this kind, so we
468                         // have to allocate in this case.
469                         stack = stack ?: mutableVectorOf()
470                         val theNode = node
471                         if (theNode != null) {
472                             stack?.add(theNode)
473                             node = null
474                         }
475                         stack?.add(next)
476                     }
477                 }
478             }
479             if (count == 1) {
480                 // if count == 1 then `node` is pointing to the "next" node we need to look at
481                 continue
482             }
483         }
484         node = stack.pop()
485     }
486 }
487 
popnull488 private fun MutableVector<Modifier.Node>?.pop(): Modifier.Node? {
489     return if (this == null || isEmpty()) null else removeAt(size - 1)
490 }
491