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