1 /*
<lambda>null2  * Copyright 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.compose.ui.node
18 
19 import androidx.compose.runtime.collection.MutableVector
20 import androidx.compose.ui.layout.AlignmentLine
21 import androidx.compose.ui.layout.Measurable
22 import androidx.compose.ui.layout.Placeable
23 import androidx.compose.ui.node.LayoutNode.LayoutState
24 import androidx.compose.ui.unit.Constraints
25 
26 /**
27  * This class works as a layout delegate for [LayoutNode]. It delegates all the measure/layout
28  * requests to its [measurePassDelegate] and [lookaheadPassDelegate] depending on whether the
29  * request is specific to lookahead.
30  */
31 internal class LayoutNodeLayoutDelegate(
32     internal val layoutNode: LayoutNode,
33 ) {
34     val outerCoordinator: NodeCoordinator
35         get() = layoutNode.nodes.outerCoordinator
36 
37     val lastConstraints: Constraints?
38         get() = measurePassDelegate.lastConstraints
39 
40     val lastLookaheadConstraints: Constraints?
41         get() = lookaheadPassDelegate?.lastConstraints
42 
43     internal val height: Int
44         get() = measurePassDelegate.height
45 
46     internal val width: Int
47         get() = measurePassDelegate.width
48 
49     /**
50      * Marks that a LayoutNode will not participate in its parent's lookahead pass. More
51      * specifically, when the parent does lookahead measurement and placement, the child that is
52      * marked [detachedFromParentLookaheadPass] will be skipped. The lookahead measurement and
53      * placement will instead happen during approach, as if the node was the lookahead root. This is
54      * needed in SubcomposeLayout where some content is only composed in approach pass, therefore
55      * they have already missed their parents' lookahead pass. They will instead do a make-up
56      * lookahead measurement/placement in approach.
57      *
58      * This gets set to true via [MeasurePassDelegate.markDetachedFromParentLookaheadPass] and
59      * automatically gets unset in `measure` when the measure call comes from parent with
60      * layoutState being LookaheadMeasuring or LookaheadLayingOut.
61      *
62      * Note: This is different than [detachedFromParentLookaheadPlacement] in that the node is
63      * detached from both parent's lookahead measurement and placement.
64      */
65     internal var detachedFromParentLookaheadPass: Boolean = false
66 
67     /**
68      * This is a flag indicating the node is not going to be placed in the parent's lookahead, but
69      * only placed during parent's approach. This is needed because in SubcomposeLayout, a custom
70      * measure policy may consider placement unnecessary after measuring the content in lookahead.
71      * However, the custom measure policy may require placement of the same content during approach,
72      * due to different measured size in approach vs. lookahead. As such, we are marking the nodes
73      * that were skipped by its parent's lookahead placement, so that we can do a make-up lookahead
74      * placement later in approach.
75      *
76      * This gets set to `true` when the node is SubComposeLayout's direct children (i.e. folded
77      * children of the SubComposeLayout's root) && not placed in lookahead but needs to be placed in
78      * approach. [detachedFromParentLookaheadPlacement] gets reset to false when there is a
79      * lookahead placement call coming from parent.
80      *
81      * Note: When [detachedFromParentLookaheadPlacement] is true, it is implied that the lookahead
82      * measurement is still triggered by its parent's lookahead measure. In contrast,
83      * [detachedFromParentLookaheadPass] indicates that both measurement and placement are detached
84      * from parent's lookahead.
85      */
86     internal var detachedFromParentLookaheadPlacement: Boolean = false
87 
88     /**
89      * The layout state the node is currently in.
90      *
91      * The mutation of [layoutState] is confined to [LayoutNodeLayoutDelegate], and is therefore
92      * read-only outside this class. This makes the state machine easier to reason about.
93      */
94     internal var layoutState = LayoutState.Idle
95 
96     /**
97      * Tracks whether another measure pass is needed for the LayoutNodeLayoutDelegate. Mutation to
98      * [measurePending] is confined to LayoutNodeLayoutDelegate. It can only be set true from
99      * outside of this class via [markMeasurePending]. It is cleared (i.e. set false) during the
100      * measure pass (i.e. in [measurePassDelegate.performMeasure]).
101      */
102     internal val measurePending: Boolean
103         get() = measurePassDelegate.measurePending
104 
105     /**
106      * Tracks whether another layout pass is needed for the LayoutNodeLayoutDelegate. Mutation to
107      * [layoutPending] is confined to this class. It can only be set true from outside of this class
108      * via [markLayoutPending]. It is cleared (i.e. set false) during the layout pass (i.e. in
109      * [MeasurePassDelegate.layoutChildren]).
110      */
111     internal val layoutPending: Boolean
112         get() = measurePassDelegate.layoutPending
113 
114     /**
115      * Tracks whether another lookahead measure pass is needed for the LayoutNodeLayoutDelegate.
116      * Mutation to [lookaheadMeasurePending] is confined to LayoutNodeLayoutDelegate. It can only be
117      * set true from outside of this class via [markLookaheadMeasurePending]. It is cleared (i.e.
118      * set false) during the lookahead measure pass (i.e. in [performLookaheadMeasure]).
119      */
120     internal var lookaheadMeasurePending: Boolean = false
121 
122     /**
123      * Tracks whether another lookahead layout pass is needed for the LayoutNodeLayoutDelegate.
124      * Mutation to [lookaheadLayoutPending] is confined to this class. It can only be set true from
125      * outside of this class via [markLookaheadLayoutPending]. It is cleared (i.e. set false) during
126      * the layout pass (i.e. in [LookaheadPassDelegate.layoutChildren]).
127      */
128     internal var lookaheadLayoutPending: Boolean = false
129 
130     /**
131      * Tracks whether another lookahead layout pass is needed for the LayoutNodeLayoutDelegate for
132      * the purposes of calculating alignment lines. After calculating alignment lines, if the
133      * [Placeable.PlacementScope.coordinates] have been accessed, there is no need to rerun layout
134      * for further alignment lines checks, but [lookaheadLayoutPending] will indicate that the
135      * normal placement still needs to be run.
136      */
137     internal var lookaheadLayoutPendingForAlignment = false
138 
139     /**
140      * The counter on a parent node which is used by its children to understand the order in which
141      * they were placed in the lookahead pass.
142      */
143     internal var nextChildLookaheadPlaceOrder: Int = 0
144 
145     /**
146      * The counter on a parent node which is used by its children to understand the order in which
147      * they were placed in the main pass.
148      */
149     internal var nextChildPlaceOrder: Int = 0
150 
151     /** Marks the layoutNode dirty for another layout pass. */
152     internal fun markLayoutPending() {
153         measurePassDelegate.markLayoutPending()
154     }
155 
156     /** Marks the layoutNode dirty for another measure pass. */
157     internal fun markMeasurePending() {
158         measurePassDelegate.markMeasurePending()
159     }
160 
161     /** Marks the layoutNode dirty for another lookahead layout pass. */
162     internal fun markLookaheadLayoutPending() {
163         lookaheadLayoutPending = true
164         lookaheadLayoutPendingForAlignment = true
165     }
166 
167     /** Marks the layoutNode dirty for another lookahead measure pass. */
168     internal fun markLookaheadMeasurePending() {
169         lookaheadMeasurePending = true
170     }
171 
172     internal val alignmentLinesOwner: AlignmentLinesOwner
173         get() = measurePassDelegate
174 
175     internal val lookaheadAlignmentLinesOwner: AlignmentLinesOwner?
176         get() = lookaheadPassDelegate
177 
178     /**
179      * This is used to track when the [Placeable.PlacementScope.coordinates] have been accessed
180      * while placement is run. When the coordinates are accessed during an alignment line query, it
181      * indicates that the placement is not final and must be run again so that the correct
182      * positioning is done. If the coordinates are not accessed during an alignment lines query (and
183      * it isn't just a [LookaheadCapablePlaceable.isShallowPlacing]), then the placement can be
184      * considered final and doesn't have to be run again.
185      *
186      * Also, if coordinates are accessed during placement, then a change in parent coordinates
187      * requires placement to be run again.
188      */
189     var coordinatesAccessedDuringPlacement = false
190         set(value) {
191             val oldValue = field
192             if (oldValue != value) {
193                 field = value
194                 if (value && !coordinatesAccessedDuringModifierPlacement) {
195                     // if first out of both flags changes to true increment
196                     childrenAccessingCoordinatesDuringPlacement++
197                 } else if (!value && !coordinatesAccessedDuringModifierPlacement) {
198                     // if both flags changes to false decrement
199                     childrenAccessingCoordinatesDuringPlacement--
200                 }
201             }
202         }
203 
204     /**
205      * Similar to [coordinatesAccessedDuringPlacement], but tracks the coordinates read happening
206      * during the modifier layout blocks run.
207      */
208     var coordinatesAccessedDuringModifierPlacement = false
209         set(value) {
210             val oldValue = field
211             if (oldValue != value) {
212                 field = value
213                 if (value && !coordinatesAccessedDuringPlacement) {
214                     // if first out of both flags changes to true increment
215                     childrenAccessingCoordinatesDuringPlacement++
216                 } else if (!value && !coordinatesAccessedDuringPlacement) {
217                     // if both flags changes to false decrement
218                     childrenAccessingCoordinatesDuringPlacement--
219                 }
220             }
221         }
222 
223     /**
224      * The number of children with [coordinatesAccessedDuringPlacement] or have descendants with
225      * [coordinatesAccessedDuringPlacement]. This also includes this, if
226      * [coordinatesAccessedDuringPlacement] is `true`.
227      */
228     var childrenAccessingCoordinatesDuringPlacement = 0
229         set(value) {
230             val oldValue = field
231             field = value
232             if ((oldValue == 0) != (value == 0)) {
233                 // A child is either newly listening for coordinates or stopped listening
234                 val parentLayoutDelegate = layoutNode.parent?.layoutDelegate
235                 if (parentLayoutDelegate != null) {
236                     if (value == 0) {
237                         parentLayoutDelegate.childrenAccessingCoordinatesDuringPlacement--
238                     } else {
239                         parentLayoutDelegate.childrenAccessingCoordinatesDuringPlacement++
240                     }
241                 }
242             }
243         }
244 
245     /** Equivalent flag of [coordinatesAccessedDuringPlacement] but for [lookaheadPassDelegate]. */
246     var lookaheadCoordinatesAccessedDuringPlacement = false
247         set(value) {
248             val oldValue = field
249             if (oldValue != value) {
250                 field = value
251                 if (value && !lookaheadCoordinatesAccessedDuringModifierPlacement) {
252                     // if first out of both flags changes to true increment
253                     childrenAccessingLookaheadCoordinatesDuringPlacement++
254                 } else if (!value && !lookaheadCoordinatesAccessedDuringModifierPlacement) {
255                     // if both flags changes to false decrement
256                     childrenAccessingLookaheadCoordinatesDuringPlacement--
257                 }
258             }
259         }
260 
261     /**
262      * Equivalent flag of [coordinatesAccessedDuringModifierPlacement] but for
263      * [lookaheadPassDelegate].
264      */
265     var lookaheadCoordinatesAccessedDuringModifierPlacement = false
266         set(value) {
267             val oldValue = field
268             if (oldValue != value) {
269                 field = value
270                 if (value && !lookaheadCoordinatesAccessedDuringPlacement) {
271                     // if first out of both flags changes to true increment
272                     childrenAccessingLookaheadCoordinatesDuringPlacement++
273                 } else if (!value && !lookaheadCoordinatesAccessedDuringPlacement) {
274                     // if both flags changes to false decrement
275                     childrenAccessingLookaheadCoordinatesDuringPlacement--
276                 }
277             }
278         }
279 
280     /**
281      * Equivalent flag of [childrenAccessingCoordinatesDuringPlacement] but for
282      * [lookaheadPassDelegate].
283      *
284      * Naturally, this flag should only be affected by the lookahead coordinates access flags.
285      */
286     var childrenAccessingLookaheadCoordinatesDuringPlacement = 0
287         set(value) {
288             val oldValue = field
289             field = value
290             if ((oldValue == 0) != (value == 0)) {
291                 // A child is either newly listening for coordinates or stopped listening
292                 val parentLayoutDelegate = layoutNode.parent?.layoutDelegate
293                 if (parentLayoutDelegate != null) {
294                     if (value == 0) {
295                         parentLayoutDelegate.childrenAccessingLookaheadCoordinatesDuringPlacement--
296                     } else {
297                         parentLayoutDelegate.childrenAccessingLookaheadCoordinatesDuringPlacement++
298                     }
299                 }
300             }
301         }
302 
303     /**
304      * measurePassDelegate manages the measure/layout and alignmentLine related queries for the
305      * actual measure/layout pass.
306      */
307     internal val measurePassDelegate = MeasurePassDelegate(this)
308 
309     /**
310      * lookaheadPassDelegate manages the measure/layout and alignmentLine related queries for the
311      * lookahead pass.
312      */
313     internal var lookaheadPassDelegate: LookaheadPassDelegate? = null
314         private set
315 
316     fun onCoordinatesUsed() {
317         val state = layoutNode.layoutState
318         if (state == LayoutState.LayingOut || state == LayoutState.LookaheadLayingOut) {
319             if (measurePassDelegate.layingOutChildren) {
320                 coordinatesAccessedDuringPlacement = true
321             } else {
322                 coordinatesAccessedDuringModifierPlacement = true
323             }
324         }
325         if (state == LayoutState.LookaheadLayingOut) {
326             if (lookaheadPassDelegate?.layingOutChildren == true) {
327                 lookaheadCoordinatesAccessedDuringPlacement = true
328             } else {
329                 lookaheadCoordinatesAccessedDuringModifierPlacement = true
330             }
331         }
332     }
333 
334     internal fun performLookaheadMeasure(constraints: Constraints) {
335         lookaheadPassDelegate?.performMeasure(constraints)
336     }
337 
338     internal fun ensureLookaheadDelegateCreated() {
339         if (lookaheadPassDelegate == null) {
340             lookaheadPassDelegate = LookaheadPassDelegate(this)
341         }
342     }
343 
344     fun updateParentData() {
345         if (measurePassDelegate.updateParentData()) {
346             layoutNode.parent?.requestRemeasure()
347         }
348         if (lookaheadPassDelegate?.updateParentData() == true) {
349             if (layoutNode.isOutMostLookaheadRoot) {
350                 layoutNode.parent?.requestRemeasure()
351             } else {
352                 layoutNode.parent?.requestLookaheadRemeasure()
353             }
354         }
355     }
356 
357     fun invalidateParentData() {
358         measurePassDelegate.invalidateParentData()
359         lookaheadPassDelegate?.invalidateParentData()
360     }
361 
362     fun resetAlignmentLines() {
363         measurePassDelegate.alignmentLines.reset()
364         lookaheadPassDelegate?.alignmentLines?.reset()
365     }
366 
367     fun markChildrenDirty() {
368         measurePassDelegate.childDelegatesDirty = true
369         lookaheadPassDelegate?.let { it.childDelegatesDirty = true }
370     }
371 
372     fun onRemovedFromLookaheadScope() {
373         lookaheadPassDelegate = null
374         // Clear lookahead invalidations when a LayoutNode is moved out of LookaheadScope.
375         lookaheadLayoutPending = false
376         lookaheadMeasurePending = false
377     }
378 }
379 
380 /**
381  * Returns if the we are at the lookahead root of the tree, by checking if the parent is has a
382  * lookahead root.
383  */
384 internal val LayoutNode.isOutMostLookaheadRoot: Boolean
385     get() =
386         lookaheadRoot != null &&
387             (parent?.lookaheadRoot == null || layoutDelegate.detachedFromParentLookaheadPass)
388 
updateChildMeasurablesnull389 internal inline fun <T : Measurable> LayoutNode.updateChildMeasurables(
390     destination: MutableVector<T>,
391     transform: (LayoutNode) -> T
392 ) {
393     forEachChildIndexed { i, layoutNode ->
394         if (destination.size <= i) {
395             destination.add(transform(layoutNode))
396         } else {
397             destination[i] = transform(layoutNode)
398         }
399     }
400     destination.removeRange(children.size, destination.size)
401 }
402 
403 internal const val MeasuredTwiceErrorMessage: String =
404     "measure() may not be called multiple times on the same Measurable. If you want to " +
405         "get the content size of the Measurable before calculating the final constraints, " +
406         "please use methods like minIntrinsicWidth()/maxIntrinsicWidth() and " +
407         "minIntrinsicHeight()/maxIntrinsicHeight()"
408 
409 /**
410  * AlignmentLinesOwner defines APIs that are needed to respond to alignment line changes, and to
411  * query alignment line related info.
412  *
413  * [LookaheadPassDelegate] and [MeasurePassDelegate] both implement this interface, and they
414  * encapsulate the difference in alignment lines handling for lookahead pass vs. actual
415  * measure/layout pass.
416  */
417 internal interface AlignmentLinesOwner : Measurable {
418     /** Whether the AlignmentLinesOwner has been placed. */
419     val isPlaced: Boolean
420 
421     /** InnerNodeCoordinator of the LayoutNode that the AlignmentLinesOwner operates on. */
422     val innerCoordinator: NodeCoordinator
423 
424     /**
425      * Alignment lines for either lookahead pass or post-lookahead pass, depending on the
426      * AlignmentLineOwner.
427      */
428     val alignmentLines: AlignmentLines
429 
430     /**
431      * The implementation for laying out children. Different types of AlignmentLinesOwner will
432      * layout children for either the lookahead pass, or the layout pass post-lookahead.
433      */
layoutChildrennull434     fun layoutChildren()
435 
436     /** Recalculate the alignment lines if dirty, and layout children as needed. */
437     fun calculateAlignmentLines(): Map<AlignmentLine, Int>
438 
439     /**
440      * Parent [AlignmentLinesOwner]. This will be the AlignmentLinesOwner for the same pass but for
441      * the parent [LayoutNode].
442      */
443     val parentAlignmentLinesOwner: AlignmentLinesOwner?
444 
445     /**
446      * This allows iterating all the AlignmentOwners for the same pass for each of the child
447      * LayoutNodes
448      */
449     fun forEachChildAlignmentLinesOwner(block: (AlignmentLinesOwner) -> Unit)
450 
451     /**
452      * Depending on which pass the [AlignmentLinesOwner] is created for, this could mean
453      * requestLookaheadLayout() for the lookahead pass, or requestLayout() for post- lookahead pass.
454      */
455     fun requestLayout()
456 
457     /**
458      * Depending on which pass the [AlignmentLinesOwner] is created for, this could mean
459      * requestLookaheadMeasure() for the lookahead pass, or requestMeasure() for post- lookahead
460      * pass.
461      */
462     fun requestMeasure()
463 }
464 
465 /**
466  * Interface for layout delegates, so that they can set the
467  * [LookaheadCapablePlaceable.isPlacedUnderMotionFrameOfReference] to the proper placeable.
468  */
469 internal interface MotionReferencePlacementDelegate {
470     /**
471      * Called when a layout is about to be placed.
472      *
473      * This updates the corresponding [LookaheadCapablePlaceable]'s
474      * [LookaheadCapablePlaceable.isPlacedUnderMotionFrameOfReference] flag IF AND ONLY IF the
475      * placement call comes from parent [LookaheadCapablePlaceable]. More specifically, for
476      * [LookaheadCapablePlaceable] that are the head of the modifier chain (e.g. outerCoordinator),
477      * the placement call doesn't always come from the parent, as the node can be independently
478      * replaced. For these [LookaheadCapablePlaceable], we maintain the old
479      * [isPlacedUnderMotionFrameOfReference] until the next placement call comes from the parent.
480      * This reason is that only placement from parent runs the placement lambda where
481      * [Placeable.PlacementScope.withMotionFrameOfReferencePlacement] is invoked. Also note, for
482      * [LookaheadCapablePlaceable] that are not the head of the modifier chain, the placement call
483      * always comes from the parent.
484      *
485      * The placeable should be tagged such that its corresponding coordinates reflect the flag in
486      * [androidx.compose.ui.layout.LayoutCoordinates.introducesMotionFrameOfReference]. Note that
487      * when it's placed on the current frame of reference, it means it doesn't introduce a new frame
488      * of reference.
489      */
490     fun updatePlacedUnderMotionFrameOfReference(newMFR: Boolean)
491 
492     /**
493      * Flag to indicate whether the [MotionReferencePlacementDelegate] is being placed under motion
494      * frame of reference. This is also reflected in
495      * [androidx.compose.ui.layout.LayoutCoordinates.introducesMotionFrameOfReference], which is
496      * used for local position calculation between coordinates.
497      */
498     val isPlacedUnderMotionFrameOfReference: Boolean
499 }
500