1 /*
<lambda>null2 * Copyright 2019 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 package androidx.compose.ui.node
17
18 import androidx.compose.runtime.ComposeNodeLifecycleCallback
19 import androidx.compose.runtime.CompositionLocalMap
20 import androidx.compose.runtime.collection.MutableVector
21 import androidx.compose.runtime.collection.mutableVectorOf
22 import androidx.compose.runtime.tooling.CompositionErrorContext
23 import androidx.compose.runtime.tooling.LocalCompositionErrorContext
24 import androidx.compose.ui.ComposeUiFlags
25 import androidx.compose.ui.ExperimentalComposeUiApi
26 import androidx.compose.ui.InternalComposeUiApi
27 import androidx.compose.ui.Modifier
28 import androidx.compose.ui.geometry.Offset
29 import androidx.compose.ui.graphics.Canvas
30 import androidx.compose.ui.graphics.layer.GraphicsLayer
31 import androidx.compose.ui.input.pointer.PointerInputFilter
32 import androidx.compose.ui.input.pointer.PointerInputModifier
33 import androidx.compose.ui.input.pointer.PointerType
34 import androidx.compose.ui.internal.checkPrecondition
35 import androidx.compose.ui.internal.checkPreconditionNotNull
36 import androidx.compose.ui.internal.requirePrecondition
37 import androidx.compose.ui.layout.IntrinsicMeasurable
38 import androidx.compose.ui.layout.IntrinsicMeasureScope
39 import androidx.compose.ui.layout.LayoutCoordinates
40 import androidx.compose.ui.layout.LayoutInfo
41 import androidx.compose.ui.layout.LayoutNodeSubcompositionsState
42 import androidx.compose.ui.layout.Measurable
43 import androidx.compose.ui.layout.MeasurePolicy
44 import androidx.compose.ui.layout.MeasureScope
45 import androidx.compose.ui.layout.ModifierInfo
46 import androidx.compose.ui.layout.OnGloballyPositionedModifier
47 import androidx.compose.ui.layout.Placeable
48 import androidx.compose.ui.layout.Remeasurement
49 import androidx.compose.ui.node.LayoutNode.LayoutState.Idle
50 import androidx.compose.ui.node.LayoutNode.LayoutState.LayingOut
51 import androidx.compose.ui.node.LayoutNode.LayoutState.LookaheadLayingOut
52 import androidx.compose.ui.node.LayoutNode.LayoutState.LookaheadMeasuring
53 import androidx.compose.ui.node.LayoutNode.LayoutState.Measuring
54 import androidx.compose.ui.node.Nodes.PointerInput
55 import androidx.compose.ui.platform.LocalDensity
56 import androidx.compose.ui.platform.LocalLayoutDirection
57 import androidx.compose.ui.platform.LocalViewConfiguration
58 import androidx.compose.ui.platform.ViewConfiguration
59 import androidx.compose.ui.platform.simpleIdentityToString
60 import androidx.compose.ui.semantics.SemanticsConfiguration
61 import androidx.compose.ui.semantics.SemanticsInfo
62 import androidx.compose.ui.semantics.generateSemanticsId
63 import androidx.compose.ui.unit.Constraints
64 import androidx.compose.ui.unit.Density
65 import androidx.compose.ui.unit.DpSize
66 import androidx.compose.ui.unit.IntOffset
67 import androidx.compose.ui.unit.IntSize
68 import androidx.compose.ui.unit.LayoutDirection
69 import androidx.compose.ui.viewinterop.InteropView
70 import androidx.compose.ui.viewinterop.InteropViewFactoryHolder
71
72 /** Enable to log changes to the LayoutNode tree. This logging is quite chatty. */
73 private const val DebugChanges = false
74
75 private val DefaultDensity = Density(1f)
76
77 /** An element in the layout hierarchy, built with compose UI. */
78 @OptIn(InternalComposeUiApi::class)
79 internal class LayoutNode(
80 // Virtual LayoutNode is the temporary concept allows us to a node which is not a real node,
81 // but just a holder for its children - allows us to combine some children into something we
82 // can subcompose in(LayoutNode) without being required to define it as a real layout - we
83 // don't want to define the layout strategy for such nodes, instead the children of the
84 // virtual nodes will be treated as the direct children of the virtual node parent.
85 // This whole concept will be replaced with a proper subcomposition logic which allows to
86 // subcompose multiple times into the same LayoutNode and define offsets.
87 private val isVirtual: Boolean = false,
88 // The unique semantics ID that is used by all semantics modifiers attached to this LayoutNode.
89 // TODO(b/281907968): Implement this with a getter that returns the compositeKeyHash.
90 override var semanticsId: Int = generateSemanticsId(),
91 ) :
92 ComposeNodeLifecycleCallback,
93 Remeasurement,
94 OwnerScope,
95 LayoutInfo,
96 SemanticsInfo,
97 ComposeUiNode,
98 InteroperableComposeUiNode,
99 Owner.OnLayoutCompletedListener {
100
101 internal var offsetFromRoot: IntOffset = IntOffset.Max
102 internal var lastSize: IntSize = IntSize.Zero
103 internal var outerToInnerOffset: IntOffset = IntOffset.Max
104 internal var outerToInnerOffsetDirty: Boolean = true
105
106 var forceUseOldLayers: Boolean = false
107
108 override var compositeKeyHash: Int = 0
109
110 internal var isVirtualLookaheadRoot: Boolean = false
111
112 /**
113 * This lookaheadRoot references the closest root to the LayoutNode, not the top-level lookahead
114 * root.
115 */
116 internal var lookaheadRoot: LayoutNode? = null
117 private set(newRoot) {
118 if (newRoot != field) {
119 field = newRoot
120 if (newRoot != null) {
121 layoutDelegate.ensureLookaheadDelegateCreated()
122 forEachCoordinatorIncludingInner { it.ensureLookaheadDelegateCreated() }
123 } else {
124 // When lookahead root is set to null, clear the lookahead pass delegate.
125 // This can happen when lookaheadScope is removed in one of the parents, or
126 // more likely when movableContent moves from a parent in a LookaheadScope to
127 // a parent not in a LookaheadScope.
128 layoutDelegate.onRemovedFromLookaheadScope()
129 }
130 invalidateMeasurements()
131 }
132 }
133
134 val isPlacedInLookahead: Boolean?
135 get() = lookaheadPassDelegate?.isPlaced
136
137 private var virtualChildrenCount = 0
138
139 // the list of nodes containing the virtual children as is
140 private val _foldedChildren =
141 MutableVectorWithMutationTracking(mutableVectorOf<LayoutNode>()) {
142 layoutDelegate.markChildrenDirty()
143 }
144 internal val foldedChildren: List<LayoutNode>
145 get() = _foldedChildren.asList()
146
147 // the list of nodes where the virtual children are unfolded (their children are represented
148 // as our direct children)
149 private var _unfoldedChildren: MutableVector<LayoutNode>? = null
150
151 private fun recreateUnfoldedChildrenIfDirty() {
152 if (unfoldedVirtualChildrenListDirty) {
153 unfoldedVirtualChildrenListDirty = false
154 val unfoldedChildren =
155 _unfoldedChildren ?: mutableVectorOf<LayoutNode>().also { _unfoldedChildren = it }
156 unfoldedChildren.clear()
157 _foldedChildren.forEach {
158 if (it.isVirtual) {
159 unfoldedChildren.addAll(it._children)
160 } else {
161 unfoldedChildren.add(it)
162 }
163 }
164 layoutDelegate.markChildrenDirty()
165 }
166 }
167
168 internal val childMeasurables: List<Measurable>
169 get() = measurePassDelegate.childDelegates
170
171 internal val childLookaheadMeasurables: List<Measurable>
172 get() = lookaheadPassDelegate!!.childDelegates
173
174 // when the list of our children is modified it will be set to true if we are a virtual node
175 // or it will be set to true on a parent if the parent is a virtual node
176 private var unfoldedVirtualChildrenListDirty = false
177
178 private fun invalidateUnfoldedVirtualChildren() {
179 if (virtualChildrenCount > 0) {
180 unfoldedVirtualChildrenListDirty = true
181 }
182 if (isVirtual) {
183 // Invalidate all virtual unfolded parent until we reach a non-virtual one
184 this._foldedParent?.invalidateUnfoldedVirtualChildren()
185 }
186 }
187
188 /**
189 * This should **not** be mutated or even accessed directly from outside of [LayoutNode]. Use
190 * [forEachChild]/[forEachChildIndexed] when there's a need to iterate through the vector.
191 */
192 internal val _children: MutableVector<LayoutNode>
193 get() {
194 updateChildrenIfDirty()
195 return if (virtualChildrenCount == 0) {
196 _foldedChildren.vector
197 } else {
198 _unfoldedChildren!!
199 }
200 }
201
202 /** Update children if the list is not up to date. */
203 internal fun updateChildrenIfDirty() {
204 if (virtualChildrenCount > 0) {
205 recreateUnfoldedChildrenIfDirty()
206 }
207 }
208
209 inline fun forEachChild(block: (LayoutNode) -> Unit) = _children.forEach(block)
210
211 inline fun forEachChildIndexed(block: (Int, LayoutNode) -> Unit) =
212 _children.forEachIndexed(block)
213
214 /** The children of this LayoutNode, controlled by [insertAt], [move], and [removeAt]. */
215 internal val children: List<LayoutNode>
216 get() = _children.asMutableList()
217
218 /**
219 * The parent node in the LayoutNode hierarchy. This is `null` when the [LayoutNode] is not
220 * attached to a hierarchy or is the root of the hierarchy.
221 */
222 private var _foldedParent: LayoutNode? = null
223
224 /*
225 * The parent node in the LayoutNode hierarchy, skipping over virtual nodes.
226 */
227 internal val parent: LayoutNode?
228 get() {
229 var parent = _foldedParent
230 while (parent?.isVirtual == true) {
231 parent = parent._foldedParent
232 }
233 return parent
234 }
235
236 /** The view system [Owner]. This `null` until [attach] is called */
237 internal var owner: Owner? = null
238 private set
239
240 /**
241 * The [InteropViewFactoryHolder] associated with this node, which is used to instantiate and
242 * manage platform View instances that are hosted in Compose.
243 */
244 internal var interopViewFactoryHolder: InteropViewFactoryHolder? = null
245
246 @InternalComposeUiApi
247 override fun getInteropView(): InteropView? = interopViewFactoryHolder?.getInteropView()
248
249 /**
250 * Returns true if this [LayoutNode] currently has an [LayoutNode.owner]. Semantically, this
251 * means that the LayoutNode is currently a part of a component tree.
252 */
253 override val isAttached: Boolean
254 get() = owner != null
255
256 /**
257 * The tree depth of the [LayoutNode]. This is valid only when it is attached to a hierarchy.
258 */
259 internal var depth: Int = 0
260
261 /**
262 * The layout state the node is currently in.
263 *
264 * The mutation of [layoutState] is confined to [LayoutNode], and is therefore read-only outside
265 * LayoutNode. This makes the state machine easier to reason about.
266 */
267 internal val layoutState
268 get() = layoutDelegate.layoutState
269
270 /**
271 * The lookahead pass delegate for the [LayoutNode]. This should only be used for measure and
272 * layout related impl during *lookahead*. For the actual measure & layout, use
273 * [measurePassDelegate].
274 */
275 internal val lookaheadPassDelegate
276 get() = layoutDelegate.lookaheadPassDelegate
277
278 /**
279 * The measure pass delegate for the [LayoutNode]. This delegate is responsible for the actual
280 * measure & layout, after lookahead if any.
281 */
282 internal val measurePassDelegate
283 get() = layoutDelegate.measurePassDelegate
284
285 /** [requestRemeasure] calls will be ignored while this flag is true. */
286 private var ignoreRemeasureRequests = false
287
288 /**
289 * Inserts a child [LayoutNode] at a particular index. If this LayoutNode [owner] is not `null`
290 * then [instance] will become [attach]ed also. [instance] must have a `null` [parent].
291 */
292 internal fun insertAt(index: Int, instance: LayoutNode) {
293 checkPrecondition(instance._foldedParent == null || instance.owner == null) {
294 exceptionMessageForParentingOrOwnership(instance)
295 }
296
297 if (DebugChanges) {
298 println("$instance added to $this at index $index")
299 }
300
301 instance._foldedParent = this
302 _foldedChildren.add(index, instance)
303 onZSortedChildrenInvalidated()
304
305 if (instance.isVirtual) {
306 virtualChildrenCount++
307 }
308 invalidateUnfoldedVirtualChildren()
309
310 val owner = this.owner
311 if (owner != null) {
312 instance.attach(owner)
313 }
314
315 if (instance.layoutDelegate.childrenAccessingCoordinatesDuringPlacement > 0) {
316 layoutDelegate.childrenAccessingCoordinatesDuringPlacement++
317 }
318 }
319
320 private fun exceptionMessageForParentingOrOwnership(instance: LayoutNode) =
321 "Cannot insert $instance because it already has a parent or an owner." +
322 " This tree: " +
323 debugTreeToString() +
324 " Other tree: " +
325 instance._foldedParent?.debugTreeToString()
326
327 internal fun onZSortedChildrenInvalidated() {
328 if (isVirtual) {
329 parent?.onZSortedChildrenInvalidated()
330 } else {
331 zSortedChildrenInvalidated = true
332 }
333 }
334
335 /** Removes one or more children, starting at [index]. */
336 internal fun removeAt(index: Int, count: Int) {
337 requirePrecondition(count >= 0) { "count ($count) must be greater than 0" }
338 for (i in index + count - 1 downTo index) {
339 // Call detach callbacks before removing from _foldedChildren, so the child is still
340 // visible to parents traversing downwards, such as when clearing focus.
341 onChildRemoved(_foldedChildren[i])
342 val child = _foldedChildren.removeAt(i)
343 if (DebugChanges) {
344 println("$child removed from $this at index $i")
345 }
346 }
347 }
348
349 /** Removes all children. */
350 internal fun removeAll() {
351 for (i in _foldedChildren.size - 1 downTo 0) {
352 onChildRemoved(_foldedChildren[i])
353 }
354 _foldedChildren.clear()
355
356 if (DebugChanges) {
357 println("Removed all children from $this")
358 }
359 }
360
361 private fun onChildRemoved(child: LayoutNode) {
362 if (child.layoutDelegate.childrenAccessingCoordinatesDuringPlacement > 0) {
363 layoutDelegate.childrenAccessingCoordinatesDuringPlacement--
364 }
365 if (owner != null) {
366 child.detach()
367 }
368 child._foldedParent = null
369 child.outerCoordinator.wrappedBy = null
370
371 if (child.isVirtual) {
372 virtualChildrenCount--
373 child._foldedChildren.forEach { it.outerCoordinator.wrappedBy = null }
374 }
375 invalidateUnfoldedVirtualChildren()
376 onZSortedChildrenInvalidated()
377 }
378
379 /**
380 * Moves [count] elements starting at index [from] to index [to]. The [to] index is related to
381 * the position before the change, so, for example, to move an element at position 1 to after
382 * the element at position 2, [from] should be `1` and [to] should be `3`. If the elements were
383 * LayoutNodes A B C D E, calling `move(1, 3, 1)` would result in the LayoutNodes being
384 * reordered to A C B D E.
385 */
386 internal fun move(from: Int, to: Int, count: Int) {
387 if (from == to) {
388 return // nothing to do
389 }
390
391 for (i in 0 until count) {
392 // if "from" is after "to," the from index moves because we're inserting before it
393 val fromIndex = if (from > to) from + i else from
394 val toIndex = if (from > to) to + i else to + count - 2
395 val child = _foldedChildren.removeAt(fromIndex)
396
397 if (DebugChanges) {
398 println("$child moved in $this from index $fromIndex to $toIndex")
399 }
400
401 _foldedChildren.add(toIndex, child)
402 }
403 onZSortedChildrenInvalidated()
404
405 invalidateUnfoldedVirtualChildren()
406 invalidateMeasurements()
407 }
408
409 override fun isTransparent(): Boolean = outerCoordinator.isTransparent()
410
411 internal var isSemanticsInvalidated = false
412
413 internal fun requestAutofill() {
414 // Ignore calls while semantics are being applied (b/378114177).
415 if (isCurrentlyCalculatingSemanticsConfiguration) return
416
417 val owner = requireOwner()
418 owner.requestAutofill(this)
419 }
420
421 internal fun invalidateSemantics() {
422 // Ignore calls to invalidate Semantics while semantics are being applied (b/378114177).
423 if (isCurrentlyCalculatingSemanticsConfiguration) return
424
425 if (@OptIn(ExperimentalComposeUiApi::class) !ComposeUiFlags.isSemanticAutofillEnabled) {
426 _semanticsConfiguration = null
427
428 // TODO(lmr): this ends up scheduling work that diffs the entire tree, but we should
429 // eventually move to marking just this node as invalidated since we are invalidating
430 // on a per-node level. This should preserve current behavior for now..
431 requireOwner().onSemanticsChange()
432 } else if (nodes.isUpdating || applyingModifierOnAttach) {
433 // We are currently updating the modifier, so just schedule an invalidation. After
434 // applying the modifier, we will notify listeners of semantics changes.
435 isSemanticsInvalidated = true
436 } else {
437 // We are not currently updating the modifier, so instead of scheduling invalidation,
438 // we update the semantics configuration and send the notification event right away.
439 val prev = _semanticsConfiguration
440 _semanticsConfiguration = calculateSemanticsConfiguration()
441 isSemanticsInvalidated = false
442
443 val owner = requireOwner()
444 owner.semanticsOwner.notifySemanticsChange(this, prev)
445
446 // This is needed for Accessibility and ContentCapture. Remove after these systems
447 // are migrated to use SemanticsInfo and SemanticListeners.
448 owner.onSemanticsChange()
449 }
450 }
451
452 // This is needed until we completely move to the new world where we always pre-compute the
453 // semantics configuration. At that point, this can just be a property with a private setter.
454 private var _semanticsConfiguration: SemanticsConfiguration? = null
455 override val semanticsConfiguration: SemanticsConfiguration?
456 get() {
457 // TODO: investigate if there's a better way to approach "half attached" state and
458 // whether or not deactivated nodes should be considered removed or not.
459 if (!isAttached || isDeactivated || !nodes.has(Nodes.Semantics)) return null
460
461 @OptIn(ExperimentalComposeUiApi::class)
462 if (!ComposeUiFlags.isSemanticAutofillEnabled && _semanticsConfiguration == null) {
463 _semanticsConfiguration = calculateSemanticsConfiguration()
464 }
465 return _semanticsConfiguration
466 }
467
468 private var isCurrentlyCalculatingSemanticsConfiguration = false
469
470 private fun calculateSemanticsConfiguration(): SemanticsConfiguration {
471 // Ignore calls to invalidate Semantics while semantics are being calculated.
472 isCurrentlyCalculatingSemanticsConfiguration = true
473
474 var config = SemanticsConfiguration()
475 requireOwner().snapshotObserver.observeSemanticsReads(this) {
476 nodes.tailToHead(Nodes.Semantics) {
477 if (it.shouldClearDescendantSemantics) {
478 config = SemanticsConfiguration()
479 config.isClearingSemantics = true
480 }
481 if (it.shouldMergeDescendantSemantics) {
482 config.isMergingSemanticsOfDescendants = true
483 }
484 with(it) { config.applySemantics() }
485 }
486 }
487
488 isCurrentlyCalculatingSemanticsConfiguration = false
489
490 return config
491 }
492
493 /**
494 * Set the [Owner] of this LayoutNode. This LayoutNode must not already be attached. [owner]
495 * must match its [parent].[owner].
496 */
497 internal fun attach(owner: Owner) {
498 checkPrecondition(this.owner == null) {
499 "Cannot attach $this as it already is attached. Tree: " + debugTreeToString()
500 }
501 checkPrecondition(_foldedParent == null || _foldedParent?.owner == owner) {
502 "Attaching to a different owner($owner) than the parent's owner(${parent?.owner})." +
503 " This tree: " +
504 debugTreeToString() +
505 " Parent tree: " +
506 _foldedParent?.debugTreeToString()
507 }
508 val parent = this.parent
509 if (parent == null) {
510 measurePassDelegate.isPlaced = true
511 lookaheadPassDelegate?.onAttachedToNullParent()
512 }
513
514 // Use the inner coordinator of first non-virtual parent
515 outerCoordinator.wrappedBy = parent?.innerCoordinator
516
517 this.owner = owner
518 this.depth = (parent?.depth ?: -1) + 1
519
520 pendingModifier?.let { applyModifier(it) }
521 pendingModifier = null
522
523 // Note: With precomputed semantics config, calling invalidateSemantics() before the
524 // layoutNode is marked as attached would result in semantics not being calculated..
525 @OptIn(ExperimentalComposeUiApi::class)
526 if (!ComposeUiFlags.isSemanticAutofillEnabled && nodes.has(Nodes.Semantics)) {
527 invalidateSemantics()
528 }
529 owner.onPreAttach(this)
530
531 // Update lookahead root when attached. For nested cases, we'll always use the
532 // closest lookahead root
533 if (isVirtualLookaheadRoot) {
534 lookaheadRoot = this
535 } else {
536 // Favor lookahead root from parent than locally created scope, unless current node
537 // is a virtual lookahead root
538 lookaheadRoot = _foldedParent?.lookaheadRoot ?: lookaheadRoot
539 if (lookaheadRoot == null && nodes.has(Nodes.ApproachMeasure)) {
540 // This could happen when movableContent containing intermediateLayout is moved
541 lookaheadRoot = this
542 }
543 }
544 if (!isDeactivated) {
545 nodes.markAsAttached()
546 }
547 _foldedChildren.forEach { child -> child.attach(owner) }
548 if (!isDeactivated) {
549 nodes.runAttachLifecycle()
550 }
551
552 invalidateMeasurements()
553 parent?.invalidateMeasurements()
554
555 forEachCoordinatorIncludingInner { it.onLayoutNodeAttach() }
556 onAttach?.invoke(owner)
557
558 layoutDelegate.updateParentData()
559
560 if (@OptIn(ExperimentalComposeUiApi::class) ComposeUiFlags.isSemanticAutofillEnabled) {
561 if (!isDeactivated && nodes.has(Nodes.Semantics)) {
562 invalidateSemantics()
563 }
564 }
565
566 owner.onPostAttach(this)
567 }
568
569 /**
570 * Remove the LayoutNode from the [Owner]. The [owner] must not be `null` before this call and
571 * its [parent]'s [owner] must be `null` before calling this. This will also [detach] all
572 * children. After executing, the [owner] will be `null`.
573 */
574 internal fun detach() {
575 val owner = owner
576 checkPreconditionNotNull(owner) {
577 "Cannot detach node that is already detached! Tree: " + parent?.debugTreeToString()
578 }
579 val parent = this.parent
580 if (parent != null) {
581 parent.invalidateLayer()
582 parent.invalidateMeasurements()
583 measurePassDelegate.measuredByParent = UsageByParent.NotUsed
584 lookaheadPassDelegate?.let { it.measuredByParent = UsageByParent.NotUsed }
585 }
586 layoutDelegate.resetAlignmentLines()
587 onDetach?.invoke(owner)
588
589 @OptIn(ExperimentalComposeUiApi::class)
590 if (!ComposeUiFlags.isSemanticAutofillEnabled && nodes.has(Nodes.Semantics)) {
591 invalidateSemantics()
592 }
593 nodes.runDetachLifecycle()
594 ignoreRemeasureRequests { _foldedChildren.forEach { child -> child.detach() } }
595 nodes.markAsDetached()
596 owner.onDetach(this)
597 this.owner = null
598
599 lookaheadRoot = null
600 depth = 0
601 measurePassDelegate.onNodeDetached()
602 lookaheadPassDelegate?.onNodeDetached()
603
604 // Note: Don't call invalidateSemantics() from within detach() because the modifier nodes
605 // are detached before the LayoutNode, and invalidateSemantics() can trigger a call to
606 // calculateSemanticsConfiguration() which will encounter unattached nodes. Instead, just
607 // set the semantics configuration to null over here since we know the node is detached.
608 @OptIn(ExperimentalComposeUiApi::class)
609 if (ComposeUiFlags.isSemanticAutofillEnabled && nodes.has(Nodes.Semantics)) {
610 val prev = _semanticsConfiguration
611 _semanticsConfiguration = null
612 isSemanticsInvalidated = false
613 owner.semanticsOwner.notifySemanticsChange(this, prev)
614
615 // This is needed for Accessibility and ContentCapture. Remove after these systems
616 // are migrated to use SemanticsInfo and SemanticListeners.
617 owner.onSemanticsChange()
618 }
619 }
620
621 private val _zSortedChildren = mutableVectorOf<LayoutNode>()
622 private var zSortedChildrenInvalidated = true
623
624 /**
625 * Returns the children list sorted by their [LayoutNode.zIndex] first (smaller first) and the
626 * order they were placed via [Placeable.placeAt] by parent (smaller first). Please note that
627 * this list contains not placed items as well, so you have to manually filter them.
628 *
629 * Note that the object is reused so you shouldn't save it for later.
630 */
631 @PublishedApi
632 internal val zSortedChildren: MutableVector<LayoutNode>
633 get() {
634 if (zSortedChildrenInvalidated) {
635 _zSortedChildren.clear()
636 _zSortedChildren.addAll(_children)
637 _zSortedChildren.sortWith(ZComparator)
638 zSortedChildrenInvalidated = false
639 }
640 return _zSortedChildren
641 }
642
643 override val isValidOwnerScope: Boolean
644 get() = isAttached
645
646 override fun toString(): String {
647 return "${simpleIdentityToString(this, null)} children: ${children.size} " +
648 "measurePolicy: $measurePolicy"
649 }
650
651 internal val hasFixedInnerContentConstraints: Boolean
652 get() {
653 // it is the constraints we have after all the modifiers applied on this node,
654 // the one to be passed into user provided [measurePolicy.measure]. if those
655 // constraints are fixed this means the children size changes can't affect
656 // this LayoutNode size.
657 val innerContentConstraints = innerCoordinator.lastMeasurementConstraints
658 return innerContentConstraints.hasFixedWidth && innerContentConstraints.hasFixedHeight
659 }
660
661 /** Call this method from the debugger to see a dump of the LayoutNode tree structure */
662 @Suppress("unused")
663 private fun debugTreeToString(depth: Int = 0): String {
664 val tree = StringBuilder()
665 for (i in 0 until depth) {
666 tree.append(" ")
667 }
668 tree.append("|-")
669 tree.append(toString())
670 tree.append('\n')
671
672 forEachChild { child -> tree.append(child.debugTreeToString(depth + 1)) }
673
674 var treeString = tree.toString()
675 if (depth == 0) {
676 // Delete trailing newline
677 treeString = treeString.substring(0, treeString.length - 1)
678 }
679
680 return treeString
681 }
682
683 internal abstract class NoIntrinsicsMeasurePolicy(private val error: String) : MeasurePolicy {
684 override fun IntrinsicMeasureScope.minIntrinsicWidth(
685 measurables: List<IntrinsicMeasurable>,
686 height: Int
687 ) = error(error)
688
689 override fun IntrinsicMeasureScope.minIntrinsicHeight(
690 measurables: List<IntrinsicMeasurable>,
691 width: Int
692 ) = error(error)
693
694 override fun IntrinsicMeasureScope.maxIntrinsicWidth(
695 measurables: List<IntrinsicMeasurable>,
696 height: Int
697 ) = error(error)
698
699 override fun IntrinsicMeasureScope.maxIntrinsicHeight(
700 measurables: List<IntrinsicMeasurable>,
701 width: Int
702 ) = error(error)
703 }
704
705 /** Blocks that define the measurement and intrinsic measurement of the layout. */
706 override var measurePolicy: MeasurePolicy = ErrorMeasurePolicy
707 set(value) {
708 if (field != value) {
709 field = value
710 intrinsicsPolicy?.updateFrom(measurePolicy)
711 invalidateMeasurements()
712 }
713 }
714
715 /**
716 * The intrinsic measurements of this layout, backed up by states to trigger correct
717 * remeasurement for layouts using the intrinsics of this layout when the [measurePolicy] is
718 * changing.
719 */
720 private var intrinsicsPolicy: IntrinsicsPolicy? = null
721
722 private fun getOrCreateIntrinsicsPolicy(): IntrinsicsPolicy {
723 return intrinsicsPolicy
724 ?: IntrinsicsPolicy(this, measurePolicy).also { intrinsicsPolicy = it }
725 }
726
727 fun minLookaheadIntrinsicWidth(height: Int) =
728 getOrCreateIntrinsicsPolicy().minLookaheadIntrinsicWidth(height)
729
730 fun minLookaheadIntrinsicHeight(width: Int) =
731 getOrCreateIntrinsicsPolicy().minLookaheadIntrinsicHeight(width)
732
733 fun maxLookaheadIntrinsicWidth(height: Int) =
734 getOrCreateIntrinsicsPolicy().maxLookaheadIntrinsicWidth(height)
735
736 fun maxLookaheadIntrinsicHeight(width: Int) =
737 getOrCreateIntrinsicsPolicy().maxLookaheadIntrinsicHeight(width)
738
739 fun minIntrinsicWidth(height: Int) = getOrCreateIntrinsicsPolicy().minIntrinsicWidth(height)
740
741 fun minIntrinsicHeight(width: Int) = getOrCreateIntrinsicsPolicy().minIntrinsicHeight(width)
742
743 fun maxIntrinsicWidth(height: Int) = getOrCreateIntrinsicsPolicy().maxIntrinsicWidth(height)
744
745 fun maxIntrinsicHeight(width: Int) = getOrCreateIntrinsicsPolicy().maxIntrinsicHeight(width)
746
747 /** The screen density to be used by this layout. */
748 override var density: Density = DefaultDensity
749 set(value) {
750 if (field != value) {
751 field = value
752 onDensityOrLayoutDirectionChanged()
753
754 nodes.headToTail { it.onDensityChange() }
755 }
756 }
757
758 /** The layout direction of the layout node. */
759 override var layoutDirection: LayoutDirection = LayoutDirection.Ltr
760 set(value) {
761 if (field != value) {
762 field = value
763 onDensityOrLayoutDirectionChanged()
764
765 nodes.headToTail { it.onLayoutDirectionChange() }
766 }
767 }
768
769 override var viewConfiguration: ViewConfiguration = DummyViewConfiguration
770 set(value) {
771 if (field != value) {
772 field = value
773
774 nodes.headToTail(type = PointerInput) { it.onViewConfigurationChange() }
775 }
776 }
777
778 override var compositionLocalMap = CompositionLocalMap.Empty
779 set(value) {
780 field = value
781 density = value[LocalDensity]
782 layoutDirection = value[LocalLayoutDirection]
783 viewConfiguration = value[LocalViewConfiguration]
784 nodes.headToTail(Nodes.CompositionLocalConsumer) { modifierNode ->
785 val delegatedNode = modifierNode.node
786 if (delegatedNode.isAttached) {
787 autoInvalidateUpdatedNode(delegatedNode)
788 } else {
789 delegatedNode.updatedNodeAwaitingAttachForInvalidation = true
790 }
791 }
792 }
793
794 private val traceContext: CompositionErrorContext?
795 get() = compositionLocalMap[LocalCompositionErrorContext]
796
797 fun rethrowWithComposeStackTrace(e: Throwable): Nothing =
798 throw e.also { traceContext?.apply { e.attachComposeStackTrace(this@LayoutNode) } }
799
800 private fun onDensityOrLayoutDirectionChanged() {
801 // TODO(b/242120396): it seems like we need to update some densities in the node
802 // coordinators here
803 // measure/layout modifiers on the node
804 invalidateMeasurements()
805 // draw modifiers on the node
806 parent?.invalidateLayer()
807 // and draw modifiers after graphics layers on the node
808 invalidateLayers()
809 }
810
811 /** The measured width of this layout and all of its [modifier]s. Shortcut for `size.width`. */
812 override val width: Int
813 get() = layoutDelegate.width
814
815 /**
816 * The measured height of this layout and all of its [modifier]s. Shortcut for `size.height`.
817 */
818 override val height: Int
819 get() = layoutDelegate.height
820
821 internal val alignmentLinesRequired: Boolean
822 get() =
823 layoutDelegate.run {
824 alignmentLinesOwner.alignmentLines.required ||
825 lookaheadAlignmentLinesOwner?.alignmentLines?.required == true
826 }
827
828 internal val mDrawScope: LayoutNodeDrawScope
829 get() = requireOwner().sharedDrawScope
830
831 /**
832 * Whether or not this [LayoutNode] and all of its parents have been placed in the hierarchy.
833 */
834 override val isPlaced: Boolean
835 get() = measurePassDelegate.isPlaced
836
837 /**
838 * Whether or not this [LayoutNode] was placed by its parent. The node can still be considered
839 * not placed if some of the modifiers on it not placed the placeable.
840 */
841 val isPlacedByParent: Boolean
842 get() = measurePassDelegate.isPlacedByParent
843
844 /**
845 * The order in which this node was placed by its parent during the previous `layoutChildren`.
846 * Before the placement the order is set to [NotPlacedPlaceOrder] to all the children. Then
847 * every placed node assigns this variable to [parent]s MeasurePassDelegate's
848 * nextChildPlaceOrder and increments this counter. Not placed items will still have
849 * [NotPlacedPlaceOrder] set.
850 */
851 internal val placeOrder: Int
852 get() = measurePassDelegate.placeOrder
853
854 /** Remembers how the node was measured by the parent. */
855 internal val measuredByParent: UsageByParent
856 get() = measurePassDelegate.measuredByParent
857
858 /** Remembers how the node was measured by the parent in lookahead. */
859 internal val measuredByParentInLookahead: UsageByParent
860 get() = lookaheadPassDelegate?.measuredByParent ?: UsageByParent.NotUsed
861
862 /** Remembers how the node was measured using intrinsics by an ancestor. */
863 internal var intrinsicsUsageByParent: UsageByParent = UsageByParent.NotUsed
864
865 /**
866 * We must cache a previous value of [intrinsicsUsageByParent] because measurement is sometimes
867 * skipped. When it is skipped, the subtree must be restored to this value.
868 */
869 private var previousIntrinsicsUsageByParent: UsageByParent = UsageByParent.NotUsed
870
871 @Deprecated("Temporary API to support ConstraintLayout prototyping.")
872 internal var canMultiMeasure: Boolean = false
873
874 internal val nodes = NodeChain(this)
875 internal val innerCoordinator: NodeCoordinator
876 get() = nodes.innerCoordinator
877
878 internal val layoutDelegate = LayoutNodeLayoutDelegate(this)
879 internal val outerCoordinator: NodeCoordinator
880 get() = nodes.outerCoordinator
881
882 /**
883 * zIndex defines the drawing order of the LayoutNode. Children with larger zIndex are drawn on
884 * top of others (the original order is used for the nodes with the same zIndex). Default zIndex
885 * is 0. We use sum of the values passed as zIndex to place() by the parent layout and all the
886 * applied modifiers.
887 */
888 private val zIndex: Float
889 get() = measurePassDelegate.zIndex
890
891 /** The inner state associated with [androidx.compose.ui.layout.SubcomposeLayout]. */
892 internal var subcompositionsState: LayoutNodeSubcompositionsState? = null
893
894 /** The inner-most layer coordinator. Used for performance for NodeCoordinator.findLayer(). */
895 private var _innerLayerCoordinator: NodeCoordinator? = null
896 internal var innerLayerCoordinatorIsDirty = true
897 internal val innerLayerCoordinator: NodeCoordinator?
898 get() {
899 if (innerLayerCoordinatorIsDirty) {
900 var coordinator: NodeCoordinator? = innerCoordinator
901 val final = outerCoordinator.wrappedBy
902 _innerLayerCoordinator = null
903 while (coordinator != final) {
904 if (coordinator?.layer != null) {
905 _innerLayerCoordinator = coordinator
906 break
907 }
908 coordinator = coordinator?.wrappedBy
909 }
910 }
911 val layerCoordinator = _innerLayerCoordinator
912 if (layerCoordinator != null) {
913 checkPreconditionNotNull(layerCoordinator.layer) { "layer was not set" }
914 }
915 return layerCoordinator
916 }
917
918 /**
919 * Invalidates the inner-most layer as part of this LayoutNode or from the containing
920 * LayoutNode. This is added for performance so that NodeCoordinator.invalidateLayer() can be
921 * faster.
922 */
923 internal fun invalidateLayer() {
924 val innerLayerCoordinator = innerLayerCoordinator
925 if (innerLayerCoordinator != null) {
926 innerLayerCoordinator.invalidateLayer()
927 } else {
928 val parent = this.parent
929 parent?.invalidateLayer()
930 }
931 }
932
933 private var _modifier: Modifier = Modifier
934 private var pendingModifier: Modifier? = null
935 internal val applyingModifierOnAttach
936 get() = pendingModifier != null
937
938 /** The [Modifier] currently applied to this node. */
939 override var modifier: Modifier
940 get() = _modifier
941 set(value) {
942 requirePrecondition(!isVirtual || modifier === Modifier) {
943 "Modifiers are not supported on virtual LayoutNodes"
944 }
945 requirePrecondition(!isDeactivated) { "modifier is updated when deactivated" }
946 if (isAttached) {
947 applyModifier(value)
948 if (isSemanticsInvalidated) {
949 invalidateSemantics()
950 }
951 } else {
952 pendingModifier = value
953 }
954 }
955
956 private fun applyModifier(modifier: Modifier) {
957 val hadPointerInput = nodes.has(Nodes.PointerInput)
958 val hadFocusTarget = nodes.has(Nodes.FocusTarget)
959 _modifier = modifier
960 nodes.updateFrom(modifier)
961 val hasPointerInput = nodes.has(Nodes.PointerInput)
962 val hasFocusTarget = nodes.has(Nodes.FocusTarget)
963 layoutDelegate.updateParentData()
964 if (lookaheadRoot == null && nodes.has(Nodes.ApproachMeasure)) {
965 lookaheadRoot = this
966 }
967
968 if (hadPointerInput != hasPointerInput || hadFocusTarget != hasFocusTarget) {
969 requireOwner().rectManager.updateFlagsFor(this, hasFocusTarget, hasPointerInput)
970 }
971 }
972
973 private fun resetModifierState() {
974 nodes.resetState()
975 }
976
977 internal fun invalidateParentData() {
978 layoutDelegate.invalidateParentData()
979 }
980
981 /**
982 * Coordinates of just the contents of the [LayoutNode], after being affected by all modifiers.
983 */
984 override val coordinates: LayoutCoordinates
985 get() = innerCoordinator
986
987 /** Callback to be executed whenever the [LayoutNode] is attached to a new [Owner]. */
988 internal var onAttach: ((Owner) -> Unit)? = null
989
990 /** Callback to be executed whenever the [LayoutNode] is detached from an [Owner]. */
991 internal var onDetach: ((Owner) -> Unit)? = null
992
993 /**
994 * Flag used by [OnPositionedDispatcher] to identify LayoutNodes that have already had their
995 * [OnGloballyPositionedModifier]'s dispatch called so that they aren't called multiple times.
996 */
997 internal var needsOnPositionedDispatch = false
998
999 internal fun place(x: Int, y: Int) {
1000 if (intrinsicsUsageByParent == UsageByParent.NotUsed) {
1001 // This LayoutNode may have asked children for intrinsics. If so, we should
1002 // clear the intrinsics usage for everything that was requested previously.
1003 clearSubtreePlacementIntrinsicsUsage()
1004 }
1005 with(parent?.innerCoordinator?.placementScope ?: requireOwner().placementScope) {
1006 measurePassDelegate.placeRelative(x, y)
1007 }
1008 }
1009
1010 /** Place this layout node again on the same position it was placed last time */
1011 internal fun replace() {
1012 if (intrinsicsUsageByParent == UsageByParent.NotUsed) {
1013 // This LayoutNode may have asked children for intrinsics. If so, we should
1014 // clear the intrinsics usage for everything that was requested previously.
1015 clearSubtreePlacementIntrinsicsUsage()
1016 }
1017 measurePassDelegate.replace()
1018 }
1019
1020 internal fun lookaheadReplace() {
1021 if (intrinsicsUsageByParent == UsageByParent.NotUsed) {
1022 // This LayoutNode may have asked children for intrinsics. If so, we should
1023 // clear the intrinsics usage for everything that was requested previously.
1024 clearSubtreePlacementIntrinsicsUsage()
1025 }
1026 lookaheadPassDelegate!!.replace()
1027 }
1028
1029 internal fun draw(canvas: Canvas, graphicsLayer: GraphicsLayer?) =
1030 withComposeStackTrace(this) { outerCoordinator.draw(canvas, graphicsLayer) }
1031
1032 /**
1033 * Carries out a hit test on the [PointerInputModifier]s associated with this [LayoutNode] and
1034 * all [PointerInputModifier]s on all descendant [LayoutNode]s.
1035 *
1036 * If [pointerPosition] is within the bounds of any tested [PointerInputModifier]s, the
1037 * [PointerInputModifier] is added to [hitTestResult] and true is returned.
1038 *
1039 * @param pointerPosition The tested pointer position, which is relative to the LayoutNode.
1040 * @param hitTestResult The collection that the hit [PointerInputFilter]s will be added to if
1041 * hit.
1042 */
1043 internal fun hitTest(
1044 pointerPosition: Offset,
1045 hitTestResult: HitTestResult,
1046 pointerType: PointerType = PointerType.Unknown,
1047 isInLayer: Boolean = true
1048 ) {
1049 val positionInWrapped = outerCoordinator.fromParentPosition(pointerPosition)
1050 outerCoordinator.hitTest(
1051 NodeCoordinator.PointerInputSource,
1052 positionInWrapped,
1053 hitTestResult,
1054 pointerType,
1055 isInLayer
1056 )
1057 }
1058
1059 @Suppress("UNUSED_PARAMETER")
1060 internal fun hitTestSemantics(
1061 pointerPosition: Offset,
1062 hitSemanticsEntities: HitTestResult,
1063 pointerType: PointerType = PointerType.Touch,
1064 isInLayer: Boolean = true
1065 ) {
1066 val positionInWrapped = outerCoordinator.fromParentPosition(pointerPosition)
1067 outerCoordinator.hitTest(
1068 NodeCoordinator.SemanticsSource,
1069 positionInWrapped,
1070 hitSemanticsEntities,
1071 pointerType = PointerType.Touch,
1072 isInLayer = isInLayer
1073 )
1074 }
1075
1076 internal fun rescheduleRemeasureOrRelayout(it: LayoutNode) {
1077 when (it.layoutState) {
1078 Idle -> {
1079 // this node was scheduled for remeasure or relayout while it was not
1080 // placed. such requests are ignored for non-placed nodes so we have to
1081 // re-schedule remeasure or relayout.
1082 if (it.lookaheadMeasurePending) {
1083 it.requestLookaheadRemeasure(forceRequest = true)
1084 } else {
1085 if (it.lookaheadLayoutPending) {
1086 it.requestLookaheadRelayout(forceRequest = true)
1087 }
1088 if (it.measurePending) {
1089 it.requestRemeasure(forceRequest = true)
1090 } else if (it.layoutPending) {
1091 it.requestRelayout(forceRequest = true)
1092 }
1093 }
1094 }
1095 else -> throw IllegalStateException("Unexpected state ${it.layoutState}")
1096 }
1097 }
1098
1099 /** Used to request a new measurement + layout pass from the owner. */
1100 internal fun requestRemeasure(
1101 forceRequest: Boolean = false,
1102 scheduleMeasureAndLayout: Boolean = true,
1103 invalidateIntrinsics: Boolean = true
1104 ) {
1105 if (!ignoreRemeasureRequests && !isVirtual) {
1106 val owner = owner ?: return
1107 owner.onRequestMeasure(
1108 layoutNode = this,
1109 forceRequest = forceRequest,
1110 scheduleMeasureAndLayout = scheduleMeasureAndLayout
1111 )
1112 if (invalidateIntrinsics) {
1113 measurePassDelegate.invalidateIntrinsicsParent(forceRequest)
1114 }
1115 }
1116 }
1117
1118 /**
1119 * Used to request a new lookahead measurement, lookahead layout, and subsequently measure and
1120 * layout from the owner.
1121 */
1122 internal fun requestLookaheadRemeasure(
1123 forceRequest: Boolean = false,
1124 scheduleMeasureAndLayout: Boolean = true,
1125 invalidateIntrinsics: Boolean = true
1126 ) {
1127 checkPrecondition(lookaheadRoot != null) {
1128 "Lookahead measure cannot be requested on a node that is not a part of the " +
1129 "LookaheadScope"
1130 }
1131 val owner = owner ?: return
1132 if (!ignoreRemeasureRequests && !isVirtual) {
1133 owner.onRequestMeasure(
1134 layoutNode = this,
1135 affectsLookahead = true,
1136 forceRequest = forceRequest,
1137 scheduleMeasureAndLayout = scheduleMeasureAndLayout
1138 )
1139 if (invalidateIntrinsics) {
1140 lookaheadPassDelegate!!.invalidateIntrinsicsParent(forceRequest)
1141 }
1142 }
1143 }
1144
1145 /**
1146 * This gets called when both lookahead measurement (if in a LookaheadScope) and actual
1147 * measurement need to be re-done. Such events include modifier change, attach/detach, etc.
1148 */
1149 internal fun invalidateMeasurements() {
1150 if (isVirtual) {
1151 // If the node is virtual, we need to invalidate the parent node (as it is non-virtual)
1152 // instead so that children get properly invalidated.
1153 parent?.invalidateMeasurements()
1154 return
1155 }
1156 outerToInnerOffsetDirty = true
1157 if (lookaheadRoot != null) {
1158 requestLookaheadRemeasure()
1159 } else {
1160 requestRemeasure()
1161 }
1162 }
1163
1164 internal fun invalidateOnPositioned() {
1165 // If we've already scheduled a measure, the positioned callbacks will get called anyway
1166 if (layoutPending || measurePending || needsOnPositionedDispatch) return
1167 requireOwner().requestOnPositionedCallback(this)
1168 }
1169
1170 internal inline fun <T> ignoreRemeasureRequests(block: () -> T): T {
1171 ignoreRemeasureRequests = true
1172 val result = block()
1173 ignoreRemeasureRequests = false
1174 return result
1175 }
1176
1177 /** Used to request a new layout pass from the owner. */
1178 internal fun requestRelayout(forceRequest: Boolean = false) {
1179 outerToInnerOffsetDirty = true
1180 if (!isVirtual) {
1181 owner?.onRequestRelayout(this, forceRequest = forceRequest)
1182 }
1183 }
1184
1185 internal fun requestLookaheadRelayout(forceRequest: Boolean = false) {
1186 if (!isVirtual) {
1187 owner?.onRequestRelayout(this, affectsLookahead = true, forceRequest)
1188 }
1189 }
1190
1191 internal fun dispatchOnPositionedCallbacks() {
1192 if (layoutState != Idle || layoutPending || measurePending || isDeactivated) {
1193 return // it hasn't yet been properly positioned, so don't make a call
1194 }
1195 if (!isPlaced) {
1196 return // it hasn't been placed, so don't make a call
1197 }
1198 nodes.headToTail(Nodes.GlobalPositionAware) {
1199 it.onGloballyPositioned(it.requireCoordinator(Nodes.GlobalPositionAware))
1200 }
1201 }
1202
1203 /**
1204 * This returns a new List of Modifiers and the coordinates and any extra information that may
1205 * be useful. This is used for tooling to retrieve layout modifier and layer information.
1206 */
1207 override fun getModifierInfo(): List<ModifierInfo> = nodes.getModifierInfo()
1208
1209 /** Invalidates layers defined on this LayoutNode. */
1210 internal fun invalidateLayers() {
1211 forEachCoordinator { coordinator -> coordinator.layer?.invalidate() }
1212 innerCoordinator.layer?.invalidate()
1213 }
1214
1215 internal fun lookaheadRemeasure(
1216 constraints: Constraints? = layoutDelegate.lastLookaheadConstraints
1217 ): Boolean {
1218 // Only lookahead remeasure when the constraints are valid and the node is in
1219 // a LookaheadScope (by checking whether the lookaheadScope is set)
1220 return if (constraints != null && lookaheadRoot != null) {
1221 lookaheadPassDelegate!!.remeasure(constraints)
1222 } else {
1223 false
1224 }
1225 }
1226
1227 /** Return true if the measured size has been changed */
1228 internal fun remeasure(constraints: Constraints? = layoutDelegate.lastConstraints): Boolean {
1229 return if (constraints != null) {
1230 if (intrinsicsUsageByParent == UsageByParent.NotUsed) {
1231 // This LayoutNode may have asked children for intrinsics. If so, we should
1232 // clear the intrinsics usage for everything that was requested previously.
1233 clearSubtreeIntrinsicsUsage()
1234 }
1235 measurePassDelegate.remeasure(constraints)
1236 } else {
1237 false
1238 }
1239 }
1240
1241 /**
1242 * Tracks whether another measure pass is needed for the LayoutNode. Mutation to
1243 * [measurePending] is confined to LayoutNodeLayoutDelegate. It can only be set true from
1244 * outside of LayoutNode via [markMeasurePending]. It is cleared (i.e. set false) during the
1245 * measure pass ( i.e. in [LayoutNodeLayoutDelegate.performMeasure]).
1246 */
1247 internal val measurePending: Boolean
1248 get() = layoutDelegate.measurePending
1249
1250 /**
1251 * Tracks whether another layout pass is needed for the LayoutNode. Mutation to [layoutPending]
1252 * is confined to LayoutNode. It can only be set true from outside of LayoutNode via
1253 * [markLayoutPending]. It is cleared (i.e. set false) during the layout pass (i.e. in
1254 * layoutChildren).
1255 */
1256 internal val layoutPending: Boolean
1257 get() = layoutDelegate.layoutPending
1258
1259 internal val lookaheadMeasurePending: Boolean
1260 get() = layoutDelegate.lookaheadMeasurePending
1261
1262 internal val lookaheadLayoutPending: Boolean
1263 get() = layoutDelegate.lookaheadLayoutPending
1264
1265 /** Marks the layoutNode dirty for another layout pass. */
1266 internal fun markLayoutPending() = layoutDelegate.markLayoutPending()
1267
1268 /** Marks the layoutNode dirty for another measure pass. */
1269 internal fun markMeasurePending() = layoutDelegate.markMeasurePending()
1270
1271 /** Marks the layoutNode dirty for another lookahead layout pass. */
1272 internal fun markLookaheadLayoutPending() = layoutDelegate.markLookaheadLayoutPending()
1273
1274 fun invalidateSubtree(isRootOfInvalidation: Boolean = true) {
1275 if (isRootOfInvalidation) {
1276 parent?.invalidateLayer()
1277 }
1278 invalidateSemantics()
1279 requestRemeasure()
1280 nodes.headToTail(Nodes.Layout) { it.requireCoordinator(Nodes.Layout).layer?.invalidate() }
1281 // TODO: invalidate parent data
1282 _children.forEach { it.invalidateSubtree(false) }
1283 }
1284
1285 /** Marks the layoutNode dirty for another lookahead measure pass. */
1286 internal fun markLookaheadMeasurePending() = layoutDelegate.markLookaheadMeasurePending()
1287
1288 override fun forceRemeasure() {
1289 // we do not schedule measure and layout as we are going to call it manually right after
1290 if (lookaheadRoot != null) {
1291 requestLookaheadRemeasure(scheduleMeasureAndLayout = false)
1292 } else {
1293 requestRemeasure(scheduleMeasureAndLayout = false)
1294 }
1295 val lastConstraints = layoutDelegate.lastConstraints
1296 if (lastConstraints != null) {
1297 owner?.measureAndLayout(this, lastConstraints)
1298 } else {
1299 owner?.measureAndLayout()
1300 }
1301 }
1302
1303 override fun onLayoutComplete() {
1304 innerCoordinator.visitNodes(Nodes.LayoutAware) { it.onPlaced(innerCoordinator) }
1305 }
1306
1307 /** Calls [block] on all [LayoutModifierNodeCoordinator]s in the NodeCoordinator chain. */
1308 internal inline fun forEachCoordinator(block: (LayoutModifierNodeCoordinator) -> Unit) {
1309 var coordinator: NodeCoordinator? = outerCoordinator
1310 val inner = innerCoordinator
1311 while (coordinator !== inner) {
1312 block(coordinator as LayoutModifierNodeCoordinator)
1313 coordinator = coordinator.wrapped
1314 }
1315 }
1316
1317 /** Calls [block] on all [NodeCoordinator]s in the NodeCoordinator chain. */
1318 internal inline fun forEachCoordinatorIncludingInner(block: (NodeCoordinator) -> Unit) {
1319 var delegate: NodeCoordinator? = outerCoordinator
1320 val final = innerCoordinator.wrapped
1321 while (delegate != final && delegate != null) {
1322 block(delegate)
1323 delegate = delegate.wrapped
1324 }
1325 }
1326
1327 /**
1328 * Walks the subtree and clears all [intrinsicsUsageByParent] that this LayoutNode's measurement
1329 * used intrinsics on.
1330 *
1331 * The layout that asks for intrinsics of its children is the node to call this to request all
1332 * of its subtree to be cleared.
1333 *
1334 * We can't do clearing as part of measure() because the child's measure() call is normally done
1335 * after the intrinsics is requested and we don't want to clear the usage at that point.
1336 */
1337 internal fun clearSubtreeIntrinsicsUsage() {
1338 // save the usage in case we short-circuit the measure call
1339 previousIntrinsicsUsageByParent = intrinsicsUsageByParent
1340 intrinsicsUsageByParent = UsageByParent.NotUsed
1341 forEachChild {
1342 if (it.intrinsicsUsageByParent != UsageByParent.NotUsed) {
1343 it.clearSubtreeIntrinsicsUsage()
1344 }
1345 }
1346 }
1347
1348 /**
1349 * Walks the subtree and clears all [intrinsicsUsageByParent] that this LayoutNode's layout
1350 * block used intrinsics on.
1351 *
1352 * The layout that asks for intrinsics of its children is the node to call this to request all
1353 * of its subtree to be cleared.
1354 *
1355 * We can't do clearing as part of measure() because the child's measure() call is normally done
1356 * after the intrinsics is requested and we don't want to clear the usage at that point.
1357 */
1358 private fun clearSubtreePlacementIntrinsicsUsage() {
1359 // save the usage in case we short-circuit the measure call
1360 previousIntrinsicsUsageByParent = intrinsicsUsageByParent
1361 intrinsicsUsageByParent = UsageByParent.NotUsed
1362 forEachChild {
1363 if (it.intrinsicsUsageByParent == UsageByParent.InLayoutBlock) {
1364 it.clearSubtreePlacementIntrinsicsUsage()
1365 }
1366 }
1367 }
1368
1369 /**
1370 * For a subtree that skips measurement, this resets the [intrinsicsUsageByParent] to what it
1371 * was prior to [clearSubtreeIntrinsicsUsage].
1372 */
1373 internal fun resetSubtreeIntrinsicsUsage() {
1374 forEachChild {
1375 it.intrinsicsUsageByParent = it.previousIntrinsicsUsageByParent
1376 if (it.intrinsicsUsageByParent != UsageByParent.NotUsed) {
1377 it.resetSubtreeIntrinsicsUsage()
1378 }
1379 }
1380 }
1381
1382 override val parentInfo: SemanticsInfo?
1383 get() = parent
1384
1385 override val childrenInfo: List<SemanticsInfo>
1386 get() = children
1387
1388 override var isDeactivated = false
1389 private set
1390
1391 override fun onReuse() {
1392 requirePrecondition(isAttached) { "onReuse is only expected on attached node" }
1393 if (@OptIn(ExperimentalComposeUiApi::class) !ComposeUiFlags.isRemoveFocusedViewFixEnabled) {
1394 interopViewFactoryHolder?.onReuse()
1395 subcompositionsState?.onReuse()
1396 }
1397 isCurrentlyCalculatingSemanticsConfiguration = false
1398 if (isDeactivated) {
1399 isDeactivated = false
1400 if (@OptIn(ExperimentalComposeUiApi::class) !ComposeUiFlags.isSemanticAutofillEnabled) {
1401 invalidateSemantics()
1402 }
1403 // we don't need to reset state as it was done when deactivated
1404 } else {
1405 resetModifierState()
1406 }
1407 val oldSemanticsId = semanticsId
1408 semanticsId = generateSemanticsId()
1409 owner?.onPreLayoutNodeReused(this, oldSemanticsId)
1410 if (@OptIn(ExperimentalComposeUiApi::class) ComposeUiFlags.isRemoveFocusedViewFixEnabled) {
1411 interopViewFactoryHolder?.onReuse()
1412 subcompositionsState?.onReuse()
1413 }
1414 // resetModifierState detaches all nodes, so we need to re-attach them upon reuse.
1415 nodes.markAsAttached()
1416 nodes.runAttachLifecycle()
1417 @OptIn(ExperimentalComposeUiApi::class)
1418 if (ComposeUiFlags.isSemanticAutofillEnabled && nodes.has(Nodes.Semantics)) {
1419 invalidateSemantics()
1420 }
1421 rescheduleRemeasureOrRelayout(this)
1422 owner?.onPostLayoutNodeReused(this, oldSemanticsId)
1423 }
1424
1425 override fun onDeactivate() {
1426 if (@OptIn(ExperimentalComposeUiApi::class) !ComposeUiFlags.isRemoveFocusedViewFixEnabled) {
1427 interopViewFactoryHolder?.onDeactivate()
1428 subcompositionsState?.onDeactivate()
1429 }
1430 isDeactivated = true
1431 resetModifierState()
1432 // if the node is detached the semantics were already updated without this node.
1433 if (isAttached) {
1434 if (@OptIn(ExperimentalComposeUiApi::class) !ComposeUiFlags.isSemanticAutofillEnabled) {
1435 invalidateSemantics()
1436 } else {
1437 _semanticsConfiguration = null
1438 isSemanticsInvalidated = false
1439 }
1440 }
1441 owner?.onLayoutNodeDeactivated(this)
1442 if (@OptIn(ExperimentalComposeUiApi::class) ComposeUiFlags.isRemoveFocusedViewFixEnabled) {
1443 interopViewFactoryHolder?.onDeactivate()
1444 subcompositionsState?.onDeactivate()
1445 }
1446 }
1447
1448 override fun onRelease() {
1449 if (@OptIn(ExperimentalComposeUiApi::class) !ComposeUiFlags.isRemoveFocusedViewFixEnabled) {
1450 interopViewFactoryHolder?.onRelease()
1451 subcompositionsState?.onRelease()
1452 }
1453 forEachCoordinatorIncludingInner { it.onRelease() }
1454 if (@OptIn(ExperimentalComposeUiApi::class) ComposeUiFlags.isRemoveFocusedViewFixEnabled) {
1455 interopViewFactoryHolder?.onRelease()
1456 subcompositionsState?.onRelease()
1457 }
1458 }
1459
1460 internal companion object {
1461 private val ErrorMeasurePolicy: NoIntrinsicsMeasurePolicy =
1462 object :
1463 NoIntrinsicsMeasurePolicy(error = "Undefined intrinsics block and it is required") {
1464 override fun MeasureScope.measure(
1465 measurables: List<Measurable>,
1466 constraints: Constraints
1467 ) = error("Undefined measure and it is required")
1468 }
1469
1470 /** Constant used by [placeOrder]. */
1471 @Suppress("ConstPropertyName") internal const val NotPlacedPlaceOrder = Int.MAX_VALUE
1472
1473 /** Pre-allocated constructor to be used with ComposeNode */
1474 internal val Constructor: () -> LayoutNode = { LayoutNode() }
1475
1476 /**
1477 * All of these values are only used in tests. The real ViewConfiguration should be set in
1478 * Layout()
1479 */
1480 internal val DummyViewConfiguration =
1481 object : ViewConfiguration {
1482 override val longPressTimeoutMillis: Long
1483 get() = 400L
1484
1485 override val doubleTapTimeoutMillis: Long
1486 get() = 300L
1487
1488 override val doubleTapMinTimeMillis: Long
1489 get() = 40L
1490
1491 override val touchSlop: Float
1492 get() = 16f
1493
1494 override val minimumTouchTargetSize: DpSize
1495 get() = DpSize.Zero
1496 }
1497
1498 /** Comparator allowing to sort nodes by zIndex and placement order. */
1499 internal val ZComparator =
1500 Comparator<LayoutNode> { node1, node2 ->
1501 if (node1.zIndex == node2.zIndex) {
1502 // if zIndex is the same we use the placement order
1503 node1.placeOrder.compareTo(node2.placeOrder)
1504 } else {
1505 node1.zIndex.compareTo(node2.zIndex)
1506 }
1507 }
1508 }
1509
1510 /**
1511 * Describes the current state the [LayoutNode] is in. A [LayoutNode] is expected to be in
1512 * [LookaheadMeasuring] first, followed by [LookaheadLayingOut] if it is in a LookaheadScope.
1513 * After the lookahead is finished, [Measuring] and then [LayingOut] will happen as needed.
1514 */
1515 internal enum class LayoutState {
1516 /** Node is currently being measured. */
1517 Measuring,
1518
1519 /** Node is being measured in lookahead. */
1520 LookaheadMeasuring,
1521
1522 /** Node is currently being laid out. */
1523 LayingOut,
1524
1525 /** Node is being laid out in lookahead. */
1526 LookaheadLayingOut,
1527
1528 /**
1529 * Node is not currently measuring or laying out. It could be pending measure or pending
1530 * layout depending on the [measurePending] and [layoutPending] flags.
1531 */
1532 Idle,
1533 }
1534
1535 internal enum class UsageByParent {
1536 InMeasureBlock,
1537 InLayoutBlock,
1538 NotUsed,
1539 }
1540 }
1541
withComposeStackTracenull1542 internal inline fun <T> withComposeStackTrace(layoutNode: LayoutNode, block: () -> T): T =
1543 try {
1544 block()
1545 } catch (e: Throwable) {
1546 layoutNode.rethrowWithComposeStackTrace(e)
1547 }
1548
1549 /** Returns [LayoutNode.owner] or throws if it is null. */
requireOwnernull1550 internal fun LayoutNode.requireOwner(): Owner {
1551 val owner = owner
1552 checkPreconditionNotNull(owner) { "LayoutNode should be attached to an owner" }
1553 return owner
1554 }
1555
1556 /**
1557 * Inserts a child [LayoutNode] at a last index. If this LayoutNode [LayoutNode.isAttached] then
1558 * [child] will become [LayoutNode.isAttached] also. [child] must have a `null` [LayoutNode.parent].
1559 */
addnull1560 internal fun LayoutNode.add(child: LayoutNode) {
1561 insertAt(children.size, child)
1562 }
1563