blob: d9aeaf89b759299227b5cd39d3698df0f42ba623 [file] [log] [blame]
George Mount652bd5d2018-10-25 15:45:02 -07001/*
shepshapard8a808e92018-12-19 15:10:30 -08002 * Copyright 2019 The Android Open Source Project
George Mount652bd5d2018-10-25 15:45:02 -07003 *
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 */
Louis Pullen-Freilicha03fd6c2020-07-24 23:26:29 +010016package androidx.compose.ui.node
George Mount652bd5d2018-10-25 15:45:02 -070017
Louis Pullen-Freilich1f10a592020-07-24 16:35:14 +010018import androidx.compose.runtime.collection.MutableVector
19import androidx.compose.runtime.collection.mutableVectorOf
Doris Liu894c9112021-08-09 16:12:47 -070020import androidx.compose.ui.ExperimentalComposeUiApi
Louis Pullen-Freilicha03fd6c2020-07-24 23:26:29 +010021import androidx.compose.ui.Modifier
Matvei Malkov0eb95892020-11-27 18:35:01 +000022import androidx.compose.ui.draw.DrawModifier
Ralston Da Silvaa19b9ec2020-12-03 16:09:45 -080023import androidx.compose.ui.focus.FocusEventModifier
Andrey Kulikov554f7362021-01-28 15:35:39 +000024import androidx.compose.ui.focus.FocusModifier
Ralston Da Silvac549cdd2020-12-17 14:27:19 -080025import androidx.compose.ui.focus.FocusOrderModifier
Andrey Kulikov554f7362021-01-28 15:35:39 +000026import androidx.compose.ui.focus.FocusRequesterModifier
Louis Pullen-Freilichf434a132020-07-22 14:19:24 +010027import androidx.compose.ui.geometry.Offset
Louis Pullen-Freilich4dc4dac2020-07-22 14:39:14 +010028import androidx.compose.ui.graphics.Canvas
Louis Pullen-Freilich534385a2020-08-14 14:10:12 +010029import androidx.compose.ui.input.key.KeyInputModifier
Andrey Kulikov31c11242021-03-02 13:21:43 +000030import androidx.compose.ui.input.nestedscroll.NestedScrollDelegatingWrapper
31import androidx.compose.ui.input.nestedscroll.NestedScrollModifier
Louis Pullen-Freilich534385a2020-08-14 14:10:12 +010032import androidx.compose.ui.input.pointer.PointerInputFilter
33import androidx.compose.ui.input.pointer.PointerInputModifier
Mihai Popa68db1e32020-10-09 16:17:44 +010034import androidx.compose.ui.layout.AlignmentLine
Louis Pullen-Freilicha03fd6c2020-07-24 23:26:29 +010035import androidx.compose.ui.layout.IntrinsicMeasurable
36import androidx.compose.ui.layout.IntrinsicMeasureScope
37import androidx.compose.ui.layout.LayoutCoordinates
Andrey Kulikov5fb82da2020-12-03 15:24:33 +000038import androidx.compose.ui.layout.LayoutInfo
Mihai Popa68db1e32020-10-09 16:17:44 +010039import androidx.compose.ui.layout.LayoutModifier
40import androidx.compose.ui.layout.Measurable
Mihai Popa49584752021-01-25 12:38:07 +000041import androidx.compose.ui.layout.MeasurePolicy
Mihai Popaaf03ea32020-10-19 14:47:36 +010042import androidx.compose.ui.layout.MeasureResult
Andrey Kulikovd82950b2020-11-11 15:23:18 +000043import androidx.compose.ui.layout.MeasureScope
Andrey Kulikov5fb82da2020-12-03 15:24:33 +000044import androidx.compose.ui.layout.ModifierInfo
Andrey Kulikovd82950b2020-11-11 15:23:18 +000045import androidx.compose.ui.layout.OnGloballyPositionedModifier
Doris Liu894c9112021-08-09 16:12:47 -070046import androidx.compose.ui.layout.OnPlacedModifier
Andrey Kulikovd82950b2020-11-11 15:23:18 +000047import androidx.compose.ui.layout.OnRemeasuredModifier
Mihai Popa68db1e32020-10-09 16:17:44 +010048import androidx.compose.ui.layout.ParentDataModifier
Andrey Kulikovd82950b2020-11-11 15:23:18 +000049import androidx.compose.ui.layout.Placeable
50import androidx.compose.ui.layout.Remeasurement
51import androidx.compose.ui.layout.RemeasurementModifier
Ralston Da Silvae8035a62021-09-01 16:20:16 -070052import androidx.compose.ui.modifier.ModifierLocalConsumer
53import androidx.compose.ui.modifier.ModifierLocalProvider
Andrey Kulikovfc91b882020-09-24 13:31:29 +010054import androidx.compose.ui.node.LayoutNode.LayoutState.LayingOut
Louis Pullen-Freilich534385a2020-08-14 14:10:12 +010055import androidx.compose.ui.node.LayoutNode.LayoutState.Measuring
56import androidx.compose.ui.node.LayoutNode.LayoutState.NeedsRelayout
57import androidx.compose.ui.node.LayoutNode.LayoutState.NeedsRemeasure
58import androidx.compose.ui.node.LayoutNode.LayoutState.Ready
George Mountd1bb6b82021-07-23 22:52:24 +000059import androidx.compose.ui.platform.ViewConfiguration
Nader Jawad793e86c2020-12-09 18:32:17 -080060import androidx.compose.ui.platform.nativeClass
Louis Pullen-Freilicha03fd6c2020-07-24 23:26:29 +010061import androidx.compose.ui.platform.simpleIdentityToString
Louis Pullen-Freilich534385a2020-08-14 14:10:12 +010062import androidx.compose.ui.semantics.SemanticsModifier
63import androidx.compose.ui.semantics.SemanticsWrapper
64import androidx.compose.ui.semantics.outerSemantics
Louis Pullen-Freilicha7eeb102020-07-22 17:54:24 +010065import androidx.compose.ui.unit.Constraints
66import androidx.compose.ui.unit.Density
George Mountd1bb6b82021-07-23 22:52:24 +000067import androidx.compose.ui.unit.DpSize
Louis Pullen-Freilicha7eeb102020-07-22 17:54:24 +010068import androidx.compose.ui.unit.LayoutDirection
George Mount652bd5d2018-10-25 15:45:02 -070069
70/**
George Mount17d6fe52020-05-15 08:08:23 -070071 * Enable to log changes to the LayoutNode tree. This logging is quite chatty.
Ryan Mentley7865a632019-08-20 20:10:29 -070072 */
73private const val DebugChanges = false
74
75/**
George Mount5469e252020-06-17 10:01:42 -070076 * An element in the layout hierarchy, built with compose UI.
George Mount652bd5d2018-10-25 15:45:02 -070077 */
Ralston Da Silva6d0c4322021-10-19 18:31:23 -070078internal class LayoutNode(
Andrey Kulikov2ca07012020-07-13 16:14:03 +010079 // Virtual LayoutNode is the temporary concept allows us to a node which is not a real node,
80 // but just a holder for its children - allows us to combine some children into something we
81 // can subcompose in(LayoutNode) without being required to define it as a real layout - we
82 // don't want to define the layout strategy for such nodes, instead the children of the
Ralston Da Silvae8035a62021-09-01 16:20:16 -070083 // virtual nodes will be treated as the direct children of the virtual node parent.
Andrey Kulikov2ca07012020-07-13 16:14:03 +010084 // This whole concept will be replaced with a proper subcomposition logic which allows to
85 // subcompose multiple times into the same LayoutNode and define offsets.
Ralston Da Silva6d0c4322021-10-19 18:31:23 -070086 private val isVirtual: Boolean = false
87) : Measurable, Remeasurement, OwnerScope, LayoutInfo, ComposeUiNode {
Andrey Kulikov2ca07012020-07-13 16:14:03 +010088
89 private var virtualChildrenCount = 0
90
91 // the list of nodes containing the virtual children as is
92 private val _foldedChildren = mutableVectorOf<LayoutNode>()
93 internal val foldedChildren: List<LayoutNode> get() = _foldedChildren.asMutableList()
94
95 // the list of nodes where the virtual children are unfolded (their children are represented
96 // as our direct children)
Andrey Kulikov1f07d0fbb52021-03-19 17:59:07 +000097 private var _unfoldedChildren: MutableVector<LayoutNode>? = null
Andrey Kulikov2ca07012020-07-13 16:14:03 +010098
99 private fun recreateUnfoldedChildrenIfDirty() {
100 if (unfoldedVirtualChildrenListDirty) {
101 unfoldedVirtualChildrenListDirty = false
Andrey Kulikov1f07d0fbb52021-03-19 17:59:07 +0000102 val unfoldedChildren = _unfoldedChildren ?: mutableVectorOf<LayoutNode>().also {
103 _unfoldedChildren = it
104 }
105 unfoldedChildren.clear()
Andrey Kulikov2ca07012020-07-13 16:14:03 +0100106 _foldedChildren.forEach {
107 if (it.isVirtual) {
Andrey Kulikov1f07d0fbb52021-03-19 17:59:07 +0000108 unfoldedChildren.addAll(it._children)
Andrey Kulikov2ca07012020-07-13 16:14:03 +0100109 } else {
Andrey Kulikov1f07d0fbb52021-03-19 17:59:07 +0000110 unfoldedChildren.add(it)
Andrey Kulikov2ca07012020-07-13 16:14:03 +0100111 }
112 }
113 }
114 }
115
116 // when the list of our children is modified it will be set to true if we are a virtual node
117 // or it will be set to true on a parent if the parent is a virtual node
118 private var unfoldedVirtualChildrenListDirty = false
119 private fun invalidateUnfoldedVirtualChildren() {
120 if (virtualChildrenCount > 0) {
121 unfoldedVirtualChildrenListDirty = true
122 }
123 if (isVirtual) {
George Mountfdd4f0f2020-11-19 23:42:29 +0000124 this.parent?.unfoldedVirtualChildrenListDirty = true
Andrey Kulikov2ca07012020-07-13 16:14:03 +0100125 }
126 }
127
Ralston Da Silva6d0c4322021-10-19 18:31:23 -0700128 @Suppress("PropertyName")
George Mount1e6c7ff2020-07-24 15:22:40 -0700129 internal val _children: MutableVector<LayoutNode>
Andrey Kulikov2ca07012020-07-13 16:14:03 +0100130 get() = if (virtualChildrenCount == 0) {
131 _foldedChildren
132 } else {
133 recreateUnfoldedChildrenIfDirty()
Andrey Kulikov1f07d0fbb52021-03-19 17:59:07 +0000134 _unfoldedChildren!!
Andrey Kulikov2ca07012020-07-13 16:14:03 +0100135 }
George Mount104b4382019-06-11 08:11:18 -0700136
George Mount652bd5d2018-10-25 15:45:02 -0700137 /**
George Mount17d6fe52020-05-15 08:08:23 -0700138 * The children of this LayoutNode, controlled by [insertAt], [move], and [removeAt].
139 */
Andrey Kulikov171309a2020-12-07 12:48:08 +0000140 internal val children: List<LayoutNode> get() = _children.asMutableList()
George Mount17d6fe52020-05-15 08:08:23 -0700141
142 /**
George Mountfdd4f0f2020-11-19 23:42:29 +0000143 * The parent node in the LayoutNode hierarchy. This is `null` when the [LayoutNode]
Andrey Kulikov804e0e62021-04-16 17:51:44 +0100144 * is not attached to a hierarchy or is the root of the hierarchy.
George Mount652bd5d2018-10-25 15:45:02 -0700145 */
Alexandre Eliase8b274d2021-06-03 19:38:18 -0700146 private var _foldedParent: LayoutNode? = null
147
148 /*
149 * The parent node in the LayoutNode hierarchy, skipping over virtual nodes.
150 */
151 internal val parent: LayoutNode?
Andrey Kulikov2ca07012020-07-13 16:14:03 +0100152 get() {
Alexandre Eliase8b274d2021-06-03 19:38:18 -0700153 return if (_foldedParent?.isVirtual == true) _foldedParent?.parent else _foldedParent
Andrey Kulikov2ca07012020-07-13 16:14:03 +0100154 }
George Mount652bd5d2018-10-25 15:45:02 -0700155
156 /**
157 * The view system [Owner]. This `null` until [attach] is called
158 */
Andrey Kulikov171309a2020-12-07 12:48:08 +0000159 internal var owner: Owner? = null
George Mount652bd5d2018-10-25 15:45:02 -0700160 private set
161
162 /**
Andrey Kulikov5fb82da2020-12-03 15:24:33 +0000163 * Returns true if this [LayoutNode] currently has an [LayoutNode.owner]. Semantically,
164 * this means that the LayoutNode is currently a part of a component tree.
165 */
166 override val isAttached: Boolean get() = owner != null
167
168 /**
George Mountfdd4f0f2020-11-19 23:42:29 +0000169 * The tree depth of the [LayoutNode]. This is valid only when it is attached to a hierarchy.
George Mount652bd5d2018-10-25 15:45:02 -0700170 */
Andrey Kulikov5fb82da2020-12-03 15:24:33 +0000171 internal var depth: Int = 0
George Mount652bd5d2018-10-25 15:45:02 -0700172
173 /**
Andrey Kulikovb33a3802020-05-21 20:02:53 +0100174 * The layout state the node is currently in.
175 */
Andrey Kulikov2ca07012020-07-13 16:14:03 +0100176 internal var layoutState = Ready
Andrey Kulikovb33a3802020-05-21 20:02:53 +0100177
Andrey Kulikovb33a3802020-05-21 20:02:53 +0100178 /**
George Mounta2e46b02020-06-15 10:33:15 -0700179 * A cache of modifiers to be used when setting and reusing previous modifiers.
180 */
George Mountdfc9ad72020-07-07 13:03:02 -0700181 private var wrapperCache = mutableVectorOf<DelegatingLayoutNodeWrapper<*>>()
George Mounta2e46b02020-06-15 10:33:15 -0700182
183 /**
Andrey Kulikov804e0e62021-04-16 17:51:44 +0100184 * [requestRemeasure] calls will be ignored while this flag is true.
185 */
186 private var ignoreRemeasureRequests = false
187
188 /**
George Mount17d6fe52020-05-15 08:08:23 -0700189 * Inserts a child [LayoutNode] at a particular index. If this LayoutNode [owner] is not `null`
George Mount1bcb07c2019-03-15 17:29:01 -0700190 * then [instance] will become [attach]ed also. [instance] must have a `null` [parent].
George Mount652bd5d2018-10-25 15:45:02 -0700191 */
George Mountfdd4f0f2020-11-19 23:42:29 +0000192 internal fun insertAt(index: Int, instance: LayoutNode) {
Alexandre Eliase8b274d2021-06-03 19:38:18 -0700193 check(instance._foldedParent == null) {
194 "Cannot insert $instance because it already has a parent." +
195 " This tree: " + debugTreeToString() +
196 " Other tree: " + instance._foldedParent?.debugTreeToString()
George Mounte11880a2020-07-17 15:28:22 -0700197 }
198 check(instance.owner == null) {
Alexandre Eliase8b274d2021-06-03 19:38:18 -0700199 "Cannot insert $instance because it already has an owner." +
200 " This tree: " + debugTreeToString() +
201 " Other tree: " + instance.debugTreeToString()
George Mounte11880a2020-07-17 15:28:22 -0700202 }
Ryan Mentley7865a632019-08-20 20:10:29 -0700203
204 if (DebugChanges) {
205 println("$instance added to $this at index $index")
206 }
207
Alexandre Eliase8b274d2021-06-03 19:38:18 -0700208 instance._foldedParent = this
Andrey Kulikov2ca07012020-07-13 16:14:03 +0100209 _foldedChildren.add(index, instance)
Andrey Kulikove80baea2021-05-18 13:57:40 +0100210 onZSortedChildrenInvalidated()
Andrey Kulikov2ca07012020-07-13 16:14:03 +0100211
212 if (instance.isVirtual) {
213 require(!isVirtual) { "Virtual LayoutNode can't be added into a virtual parent" }
214 virtualChildrenCount++
215 }
216 invalidateUnfoldedVirtualChildren()
George Mount104b4382019-06-11 08:11:18 -0700217
Andrey Kulikovb33a3802020-05-21 20:02:53 +0100218 instance.outerLayoutNodeWrapper.wrappedBy = innerLayoutNodeWrapper
George Mountd4741ec2020-01-23 07:30:38 -0800219
George Mount652bd5d2018-10-25 15:45:02 -0700220 val owner = this.owner
221 if (owner != null) {
222 instance.attach(owner)
223 }
224 }
225
Andrey Kulikove80baea2021-05-18 13:57:40 +0100226 private fun onZSortedChildrenInvalidated() {
227 if (isVirtual) {
228 parent?.onZSortedChildrenInvalidated()
229 } else {
230 zSortedChildrenInvalidated = true
231 }
232 }
233
George Mount652bd5d2018-10-25 15:45:02 -0700234 /**
235 * Removes one or more children, starting at [index].
236 */
George Mountfdd4f0f2020-11-19 23:42:29 +0000237 internal fun removeAt(index: Int, count: Int) {
George Mounte11880a2020-07-17 15:28:22 -0700238 require(count >= 0) {
239 "count ($count) must be greater than 0"
240 }
George Mount652bd5d2018-10-25 15:45:02 -0700241 val attached = owner != null
George Mount104b4382019-06-11 08:11:18 -0700242 for (i in index + count - 1 downTo index) {
Andrey Kulikov2ca07012020-07-13 16:14:03 +0100243 val child = _foldedChildren.removeAt(i)
Andrey Kulikove80baea2021-05-18 13:57:40 +0100244 onZSortedChildrenInvalidated()
Ryan Mentley7865a632019-08-20 20:10:29 -0700245 if (DebugChanges) {
246 println("$child removed from $this at index $i")
247 }
248
George Mount652bd5d2018-10-25 15:45:02 -0700249 if (attached) {
250 child.detach()
251 }
Alexandre Eliase8b274d2021-06-03 19:38:18 -0700252 child._foldedParent = null
Andrey Kulikov2ca07012020-07-13 16:14:03 +0100253
254 if (child.isVirtual) {
255 virtualChildrenCount--
256 }
257 invalidateUnfoldedVirtualChildren()
George Mount652bd5d2018-10-25 15:45:02 -0700258 }
259 }
260
George Mount3fec87b2020-03-31 14:33:02 -0700261 /**
Adam Powell8dec9b72020-06-19 14:24:14 -0700262 * Removes all children.
263 */
George Mountfdd4f0f2020-11-19 23:42:29 +0000264 internal fun removeAll() {
Adam Powell8dec9b72020-06-19 14:24:14 -0700265 val attached = owner != null
Andrey Kulikov2ca07012020-07-13 16:14:03 +0100266 for (i in _foldedChildren.size - 1 downTo 0) {
267 val child = _foldedChildren[i]
Adam Powell8dec9b72020-06-19 14:24:14 -0700268 if (attached) {
269 child.detach()
270 }
Alexandre Eliase8b274d2021-06-03 19:38:18 -0700271 child._foldedParent = null
Adam Powell8dec9b72020-06-19 14:24:14 -0700272 }
Andrey Kulikov2ca07012020-07-13 16:14:03 +0100273 _foldedChildren.clear()
Andrey Kulikove80baea2021-05-18 13:57:40 +0100274 onZSortedChildrenInvalidated()
Andrey Kulikov2ca07012020-07-13 16:14:03 +0100275
276 virtualChildrenCount = 0
277 invalidateUnfoldedVirtualChildren()
Adam Powell8dec9b72020-06-19 14:24:14 -0700278 }
279
280 /**
George Mount3fec87b2020-03-31 14:33:02 -0700281 * Moves [count] elements starting at index [from] to index [to]. The [to] index is related to
282 * the position before the change, so, for example, to move an element at position 1 to after
283 * the element at position 2, [from] should be `1` and [to] should be `3`. If the elements
George Mount17d6fe52020-05-15 08:08:23 -0700284 * were LayoutNodes A B C D E, calling `move(1, 3, 1)` would result in the LayoutNodes
George Mount3fec87b2020-03-31 14:33:02 -0700285 * being reordered to A C B D E.
286 */
George Mountfdd4f0f2020-11-19 23:42:29 +0000287 internal fun move(from: Int, to: Int, count: Int) {
George Mount104b4382019-06-11 08:11:18 -0700288 if (from == to) {
289 return // nothing to do
290 }
Alexandre Eliasf17ba992020-03-23 20:22:20 -0700291
George Mount104b4382019-06-11 08:11:18 -0700292 for (i in 0 until count) {
293 // if "from" is after "to," the from index moves because we're inserting before it
294 val fromIndex = if (from > to) from + i else from
295 val toIndex = if (from > to) to + i else to + count - 2
Andrey Kulikov2ca07012020-07-13 16:14:03 +0100296 val child = _foldedChildren.removeAt(fromIndex)
Ryan Mentley7865a632019-08-20 20:10:29 -0700297
298 if (DebugChanges) {
299 println("$child moved in $this from index $fromIndex to $toIndex")
300 }
301
Andrey Kulikov2ca07012020-07-13 16:14:03 +0100302 _foldedChildren.add(toIndex, child)
Ryan Mentley7865a632019-08-20 20:10:29 -0700303 }
Andrey Kulikove80baea2021-05-18 13:57:40 +0100304 onZSortedChildrenInvalidated()
Ryan Mentley7865a632019-08-20 20:10:29 -0700305
Andrey Kulikov2ca07012020-07-13 16:14:03 +0100306 invalidateUnfoldedVirtualChildren()
George Mount17d6fe52020-05-15 08:08:23 -0700307 requestRemeasure()
George Mount104b4382019-06-11 08:11:18 -0700308 }
309
George Mount652bd5d2018-10-25 15:45:02 -0700310 /**
George Mount17d6fe52020-05-15 08:08:23 -0700311 * Set the [Owner] of this LayoutNode. This LayoutNode must not already be attached.
George Mount652bd5d2018-10-25 15:45:02 -0700312 * [owner] must match its [parent].[owner].
313 */
Andrey Kulikov5fb82da2020-12-03 15:24:33 +0000314 internal fun attach(owner: Owner) {
George Mounte11880a2020-07-17 15:28:22 -0700315 check(this.owner == null) {
Alexandre Eliase8b274d2021-06-03 19:38:18 -0700316 "Cannot attach $this as it already is attached. Tree: " + debugTreeToString()
317 }
318 check(_foldedParent == null || _foldedParent?.owner == owner) {
319 "Attaching to a different owner($owner) than the parent's owner(${parent?.owner})." +
320 " This tree: " + debugTreeToString() +
321 " Parent tree: " + _foldedParent?.debugTreeToString()
George Mounte11880a2020-07-17 15:28:22 -0700322 }
George Mountfdd4f0f2020-11-19 23:42:29 +0000323 val parent = this.parent
Andrey Kulikov44e78282020-10-12 14:43:02 +0100324 if (parent == null) {
325 // it is a root node and attached root nodes are always placed (as there is no parent
326 // to place them explicitly)
327 isPlaced = true
328 }
329
George Mount652bd5d2018-10-25 15:45:02 -0700330 this.owner = owner
331 this.depth = (parent?.depth ?: -1) + 1
yingleiw6070eac2020-07-28 19:09:58 -0700332 if (outerSemantics != null) {
333 owner.onSemanticsChange()
334 }
George Mount652bd5d2018-10-25 15:45:02 -0700335 owner.onAttach(this)
Andrey Kulikov2ca07012020-07-13 16:14:03 +0100336 _foldedChildren.forEach { child ->
George Mount652bd5d2018-10-25 15:45:02 -0700337 child.attach(owner)
338 }
George Mount17d6fe52020-05-15 08:08:23 -0700339
340 requestRemeasure()
Andrey Kulikovb33a3802020-05-21 20:02:53 +0100341 parent?.requestRemeasure()
Andrey Kulikov11025272020-11-18 19:23:35 +0000342 innerLayoutNodeWrapper.attach()
George Mounta2e46b02020-06-15 10:33:15 -0700343 forEachDelegate { it.attach() }
George Mount17d6fe52020-05-15 08:08:23 -0700344 onAttach?.invoke(owner)
George Mount652bd5d2018-10-25 15:45:02 -0700345 }
346
347 /**
George Mount17d6fe52020-05-15 08:08:23 -0700348 * Remove the LayoutNode from the [Owner]. The [owner] must not be `null` before this call
George Mount652bd5d2018-10-25 15:45:02 -0700349 * and its [parent]'s [owner] must be `null` before calling this. This will also [detach] all
350 * children. After executing, the [owner] will be `null`.
351 */
Andrey Kulikov5fb82da2020-12-03 15:24:33 +0000352 internal fun detach() {
George Mounte11880a2020-07-17 15:28:22 -0700353 val owner = owner
354 checkNotNull(owner) {
Alexandre Eliase8b274d2021-06-03 19:38:18 -0700355 "Cannot detach node that is already detached! Tree: " + parent?.debugTreeToString()
George Mounte11880a2020-07-17 15:28:22 -0700356 }
George Mountfdd4f0f2020-11-19 23:42:29 +0000357 val parent = this.parent
358 if (parent != null) {
359 parent.invalidateLayer()
360 parent.requestRemeasure()
George Mount17d6fe52020-05-15 08:08:23 -0700361 }
Mihai Popa79815ff2021-04-27 17:05:22 +0100362 alignmentLines.reset()
George Mount17d6fe52020-05-15 08:08:23 -0700363 onDetach?.invoke(owner)
George Mounta2e46b02020-06-15 10:33:15 -0700364 forEachDelegate { it.detach() }
Andrey Kulikov11025272020-11-18 19:23:35 +0000365 innerLayoutNodeWrapper.detach()
George Mount17d6fe52020-05-15 08:08:23 -0700366
yingleiw8049eaa2020-04-30 15:05:57 -0700367 if (outerSemantics != null) {
368 owner.onSemanticsChange()
369 }
George Mount0b45f782019-07-09 13:30:28 -0700370 owner.onDetach(this)
George Mount652bd5d2018-10-25 15:45:02 -0700371 this.owner = null
372 depth = 0
Andrey Kulikov2ca07012020-07-13 16:14:03 +0100373 _foldedChildren.forEach { child ->
Andrey Kulikov9771c6d2020-03-04 17:19:19 +0000374 child.detach()
375 }
Andrey Kulikovfc91b882020-09-24 13:31:29 +0100376 placeOrder = NotPlacedPlaceOrder
Andrey Kulikov4aa63ec2021-05-24 18:06:35 +0100377 previousPlaceOrder = NotPlacedPlaceOrder
Andrey Kulikovfc91b882020-09-24 13:31:29 +0100378 isPlaced = false
George Mount652bd5d2018-10-25 15:45:02 -0700379 }
Ryan Mentley7865a632019-08-20 20:10:29 -0700380
Andrey Kulikovfc91b882020-09-24 13:31:29 +0100381 private val _zSortedChildren = mutableVectorOf<LayoutNode>()
Andrey Kulikovfa215832020-11-06 16:43:55 +0000382 private var zSortedChildrenInvalidated = true
Andrey Kulikovd3b694d2020-03-30 19:20:34 +0100383
384 /**
Andrey Kulikovfc91b882020-09-24 13:31:29 +0100385 * Returns the children list sorted by their [LayoutNode.zIndex] first (smaller first) and the
386 * order they were placed via [Placeable.placeAt] by parent (smaller first).
387 * Please note that this list contains not placed items as well, so you have to manually
388 * filter them.
389 *
Andrey Kulikovd3b694d2020-03-30 19:20:34 +0100390 * Note that the object is reused so you shouldn't save it for later.
391 */
392 @PublishedApi
Andrey Kulikovfc91b882020-09-24 13:31:29 +0100393 internal val zSortedChildren: MutableVector<LayoutNode>
Andrey Kulikovd3b694d2020-03-30 19:20:34 +0100394 get() {
Andrey Kulikovfa215832020-11-06 16:43:55 +0000395 if (zSortedChildrenInvalidated) {
396 _zSortedChildren.clear()
397 _zSortedChildren.addAll(_children)
398 _zSortedChildren.sortWith(ZComparator)
Andrey Kulikove80baea2021-05-18 13:57:40 +0100399 zSortedChildrenInvalidated = false
Andrey Kulikovfa215832020-11-06 16:43:55 +0000400 }
Andrey Kulikovfc91b882020-09-24 13:31:29 +0100401 return _zSortedChildren
Andrey Kulikovd3b694d2020-03-30 19:20:34 +0100402 }
403
George Mount669bd402020-09-02 18:12:10 -0700404 override val isValid: Boolean
Andrey Kulikov5fb82da2020-12-03 15:24:33 +0000405 get() = isAttached
George Mount669bd402020-09-02 18:12:10 -0700406
Ryan Mentley7865a632019-08-20 20:10:29 -0700407 override fun toString(): String {
Andrey Kulikov2ca07012020-07-13 16:14:03 +0100408 return "${simpleIdentityToString(this, null)} children: ${children.size} " +
Mihai Popa49584752021-01-25 12:38:07 +0000409 "measurePolicy: $measurePolicy"
Ryan Mentley7865a632019-08-20 20:10:29 -0700410 }
411
412 /**
George Mount17d6fe52020-05-15 08:08:23 -0700413 * Call this method from the debugger to see a dump of the LayoutNode tree structure
Ryan Mentley7865a632019-08-20 20:10:29 -0700414 */
Ralston Da Silva228085e2020-08-12 03:34:16 -0700415 @Suppress("unused")
Ryan Mentley7865a632019-08-20 20:10:29 -0700416 private fun debugTreeToString(depth: Int = 0): String {
417 val tree = StringBuilder()
418 for (i in 0 until depth) {
419 tree.append(" ")
420 }
421 tree.append("|-")
422 tree.append(toString())
423 tree.append('\n')
424
George Mountdfc9ad72020-07-07 13:03:02 -0700425 _children.forEach { child ->
Ryan Mentley7865a632019-08-20 20:10:29 -0700426 tree.append(child.debugTreeToString(depth + 1))
427 }
428
Ryan Mentley4b5c3552021-01-20 04:22:32 -0800429 var treeString = tree.toString()
Ryan Mentley7865a632019-08-20 20:10:29 -0700430 if (depth == 0) {
431 // Delete trailing newline
Ryan Mentley4b5c3552021-01-20 04:22:32 -0800432 treeString = treeString.substring(0, treeString.length - 1)
Ryan Mentley7865a632019-08-20 20:10:29 -0700433 }
Ryan Mentley4b5c3552021-01-20 04:22:32 -0800434
435 return treeString
Ryan Mentley7865a632019-08-20 20:10:29 -0700436 }
George Mount652bd5d2018-10-25 15:45:02 -0700437
Mihai Popa49584752021-01-25 12:38:07 +0000438 internal abstract class NoIntrinsicsMeasurePolicy(private val error: String) : MeasurePolicy {
439 override fun IntrinsicMeasureScope.minIntrinsicWidth(
Andrey Kulikovbd5cf9e2019-11-21 20:46:48 +0000440 measurables: List<IntrinsicMeasurable>,
Mihai Popa49584752021-01-25 12:38:07 +0000441 height: Int
Andrey Kulikovbd5cf9e2019-11-21 20:46:48 +0000442 ) = error(error)
443
Mihai Popa49584752021-01-25 12:38:07 +0000444 override fun IntrinsicMeasureScope.minIntrinsicHeight(
Andrey Kulikovbd5cf9e2019-11-21 20:46:48 +0000445 measurables: List<IntrinsicMeasurable>,
Mihai Popa49584752021-01-25 12:38:07 +0000446 width: Int
Andrey Kulikovbd5cf9e2019-11-21 20:46:48 +0000447 ) = error(error)
448
Mihai Popa49584752021-01-25 12:38:07 +0000449 override fun IntrinsicMeasureScope.maxIntrinsicWidth(
Andrey Kulikovbd5cf9e2019-11-21 20:46:48 +0000450 measurables: List<IntrinsicMeasurable>,
Mihai Popa49584752021-01-25 12:38:07 +0000451 height: Int
Andrey Kulikovbd5cf9e2019-11-21 20:46:48 +0000452 ) = error(error)
453
Mihai Popa49584752021-01-25 12:38:07 +0000454 override fun IntrinsicMeasureScope.maxIntrinsicHeight(
Andrey Kulikovbd5cf9e2019-11-21 20:46:48 +0000455 measurables: List<IntrinsicMeasurable>,
Mihai Popa49584752021-01-25 12:38:07 +0000456 width: Int
Andrey Kulikovbd5cf9e2019-11-21 20:46:48 +0000457 ) = error(error)
458 }
459
Mihai Popa24925ab2019-10-09 17:13:41 +0100460 /**
461 * Blocks that define the measurement and intrinsic measurement of the layout.
462 */
Mihai Popa49584752021-01-25 12:38:07 +0000463 override var measurePolicy: MeasurePolicy = ErrorMeasurePolicy
George Mount31c3edc2019-08-14 15:30:26 -0700464 set(value) {
Mihai Popaca1c4b92019-10-03 19:16:06 +0100465 if (field != value) {
466 field = value
Mihai Popaf411f762021-06-07 18:21:24 +0100467 intrinsicsPolicy.updateFrom(measurePolicy)
Mihai Popaca1c4b92019-10-03 19:16:06 +0100468 requestRemeasure()
469 }
George Mount31c3edc2019-08-14 15:30:26 -0700470 }
471
George Mount652bd5d2018-10-25 15:45:02 -0700472 /**
Mihai Popaf411f762021-06-07 18:21:24 +0100473 * The intrinsic measurements of this layout, backed up by states to trigger
474 * correct remeasurement for layouts using the intrinsics of this layout
475 * when the [measurePolicy] is changing.
476 */
477 internal val intrinsicsPolicy = IntrinsicsPolicy(this)
478
479 /**
Mihai Popac297be12020-08-13 14:05:35 +0100480 * The screen density to be used by this layout.
481 */
Andrey Kulikov057998a2021-01-28 18:02:50 +0000482 override var density: Density = Density(1f)
Andrey Kulikov51a67e52021-05-13 18:19:25 +0100483 set(value) {
484 if (field != value) {
485 field = value
486 onDensityOrLayoutDirectionChanged()
487 }
488 }
Mihai Popac297be12020-08-13 14:05:35 +0100489
490 /**
Mihai Popa49584752021-01-25 12:38:07 +0000491 * The scope used to [measure][MeasurePolicy.measure] children.
Mihai Popa24925ab2019-10-09 17:13:41 +0100492 */
George Mountfdd4f0f2020-11-19 23:42:29 +0000493 internal val measureScope: MeasureScope = object : MeasureScope, Density {
Mihai Popac297be12020-08-13 14:05:35 +0100494 override val density: Float get() = this@LayoutNode.density.density
495 override val fontScale: Float get() = this@LayoutNode.density.fontScale
Mihai Popafc355b12020-04-15 17:34:17 +0100496 override val layoutDirection: LayoutDirection get() = this@LayoutNode.layoutDirection
Mihai Popa24925ab2019-10-09 17:13:41 +0100497 }
498
499 /**
Anastasia Soboleva9474ff82020-02-19 19:02:15 +0000500 * The layout direction of the layout node.
501 */
Andrey Kulikov057998a2021-01-28 18:02:50 +0000502 override var layoutDirection: LayoutDirection = LayoutDirection.Ltr
Anastasia Soboleva2833c362020-07-23 20:19:13 +0100503 set(value) {
504 if (field != value) {
505 field = value
Andrey Kulikov51a67e52021-05-13 18:19:25 +0100506 onDensityOrLayoutDirectionChanged()
Anastasia Soboleva2833c362020-07-23 20:19:13 +0100507 }
508 }
Anastasia Soboleva9474ff82020-02-19 19:02:15 +0000509
George Mountd1bb6b82021-07-23 22:52:24 +0000510 override var viewConfiguration: ViewConfiguration = DummyViewConfiguration
511
Andrey Kulikov51a67e52021-05-13 18:19:25 +0100512 private fun onDensityOrLayoutDirectionChanged() {
513 // measure/layout modifiers on the node
514 requestRemeasure()
515 // draw modifiers on the node
516 parent?.invalidateLayer()
517 // and draw modifiers after graphics layers on the node
518 invalidateLayers()
519 }
520
Anastasia Soboleva9474ff82020-02-19 19:02:15 +0000521 /**
Adam Powell384bbaa2019-07-11 14:44:14 -0700522 * The measured width of this layout and all of its [modifier]s. Shortcut for `size.width`.
George Mount652bd5d2018-10-25 15:45:02 -0700523 */
Andrey Kulikov5fb82da2020-12-03 15:24:33 +0000524 override val width: Int get() = outerMeasurablePlaceable.width
George Mount4844e6c2019-02-25 13:43:44 -0800525
526 /**
Adam Powell384bbaa2019-07-11 14:44:14 -0700527 * The measured height of this layout and all of its [modifier]s. Shortcut for `size.height`.
George Mount4844e6c2019-02-25 13:43:44 -0800528 */
Andrey Kulikov5fb82da2020-12-03 15:24:33 +0000529 override val height: Int get() = outerMeasurablePlaceable.height
George Mount652bd5d2018-10-25 15:45:02 -0700530
531 /**
Mihai Popaf411f762021-06-07 18:21:24 +0100532 * State corresponding to the alignment lines of this layout, inherited + intrinsic.
Mihai Popa42aa7572019-07-30 15:46:36 +0100533 */
Mihai Popaf411f762021-06-07 18:21:24 +0100534 internal val alignmentLines = LayoutNodeAlignmentLines(this)
Mihai Popa42aa7572019-07-30 15:46:36 +0100535
Igor Demin4c1c15e2021-08-09 15:15:10 +0300536 internal val mDrawScope: LayoutNodeDrawScope
537 get() = requireOwner().sharedDrawScope
Nader Jawad83a44b72020-05-11 14:32:09 -0700538
Mihai Popa42aa7572019-07-30 15:46:36 +0100539 /**
George Mountfdd4f0f2020-11-19 23:42:29 +0000540 * Whether or not this [LayoutNode] and all of its parents have been placed in the hierarchy.
George Mount652bd5d2018-10-25 15:45:02 -0700541 */
Andrey Kulikov5fb82da2020-12-03 15:24:33 +0000542 override var isPlaced: Boolean = false
Andrey Kulikov44e78282020-10-12 14:43:02 +0100543 private set
George Mount652bd5d2018-10-25 15:45:02 -0700544
George Mount03113222019-03-25 14:30:21 -0700545 /**
Andrey Kulikovfc91b882020-09-24 13:31:29 +0100546 * The order in which this node was placed by its parent during the previous [layoutChildren].
547 * Before the placement the order is set to [NotPlacedPlaceOrder] to all the children. Then
548 * every placed node assigns this variable to [parent]s [nextChildPlaceOrder] and increments
549 * this counter. Not placed items will still have [NotPlacedPlaceOrder] set.
Andrey Kulikov308929c2020-09-03 17:07:40 +0300550 */
Andrey Kulikova3b56612021-05-20 20:57:53 +0100551 internal var placeOrder: Int = NotPlacedPlaceOrder
552 private set
Andrey Kulikovfc91b882020-09-24 13:31:29 +0100553
554 /**
Andrey Kulikov4aa63ec2021-05-24 18:06:35 +0100555 * The value [placeOrder] had during the previous parent [layoutChildren]. Helps us to
556 * understand if the order did change.
557 */
558 private var previousPlaceOrder: Int = NotPlacedPlaceOrder
559
560 /**
Andrey Kulikovfc91b882020-09-24 13:31:29 +0100561 * The counter on a parent node which is used by its children to understand the order in which
562 * they were placed.
563 * @see placeOrder
564 */
565 private var nextChildPlaceOrder: Int = 0
Andrey Kulikov308929c2020-09-03 17:07:40 +0300566
567 /**
Andrey Kulikovb33a3802020-05-21 20:02:53 +0100568 * Remembers how the node was measured by the parent.
George Mountef4bb052019-04-11 11:30:53 -0700569 */
Andrey Kulikovb33a3802020-05-21 20:02:53 +0100570 internal var measuredByParent: UsageByParent = UsageByParent.NotUsed
Mihai Popa42aa7572019-07-30 15:46:36 +0100571
Mihai Popa73815e22019-11-19 16:26:59 +0000572 @Deprecated("Temporary API to support ConstraintLayout prototyping.")
Andrey Kulikov5fb82da2020-12-03 15:24:33 +0000573 internal var canMultiMeasure: Boolean = false
Mihai Popa73815e22019-11-19 16:26:59 +0000574
George Mountd4741ec2020-01-23 07:30:38 -0800575 internal val innerLayoutNodeWrapper: LayoutNodeWrapper = InnerPlaceable(this)
Andrey Kulikovb33a3802020-05-21 20:02:53 +0100576 private val outerMeasurablePlaceable = OuterMeasurablePlaceable(this, innerLayoutNodeWrapper)
577 internal val outerLayoutNodeWrapper: LayoutNodeWrapper
578 get() = outerMeasurablePlaceable.outerWrapper
George Mount31c3edc2019-08-14 15:30:26 -0700579
Adam Powell384bbaa2019-07-11 14:44:14 -0700580 /**
Andrey Kulikovd3b694d2020-03-30 19:20:34 +0100581 * zIndex defines the drawing order of the LayoutNode. Children with larger zIndex are drawn
Andrey Kulikovfa215832020-11-06 16:43:55 +0000582 * on top of others (the original order is used for the nodes with the same zIndex).
583 * Default zIndex is 0. We use sum of the values passed as zIndex to place() by the
584 * parent layout and all the applied modifiers.
Andrey Kulikovd3b694d2020-03-30 19:20:34 +0100585 */
Andrey Kulikovfa215832020-11-06 16:43:55 +0000586 private var zIndex: Float = 0f
George Mountc3509772020-03-18 23:38:09 +0000587
588 /**
Andrey Kulikov671baa52021-12-23 17:41:40 +0000589 * The inner state associated with [androidx.compose.ui.layout.SubcomposeLayout].
590 */
591 internal var subcompositionsState: Any? = null
592
593 /**
George Mount2762fd92020-05-07 15:50:23 -0700594 * The inner-most layer wrapper. Used for performance for LayoutNodeWrapper.findLayer().
595 */
Andrey Kulikov11025272020-11-18 19:23:35 +0000596 private var _innerLayerWrapper: LayoutNodeWrapper? = null
Andrey Kulikov9e6f5252020-12-04 13:08:38 +0000597 internal var innerLayerWrapperIsDirty = true
Ralston Da Silva6d0c4322021-10-19 18:31:23 -0700598 private val innerLayerWrapper: LayoutNodeWrapper? get() {
Andrey Kulikov9e6f5252020-12-04 13:08:38 +0000599 if (innerLayerWrapperIsDirty) {
600 var delegate: LayoutNodeWrapper? = innerLayoutNodeWrapper
601 val final = outerLayoutNodeWrapper.wrappedBy
602 _innerLayerWrapper = null
603 while (delegate != final) {
604 if (delegate?.layer != null) {
605 _innerLayerWrapper = delegate
606 break
607 }
608 delegate = delegate?.wrappedBy
609 }
610 }
Andrey Kulikov11025272020-11-18 19:23:35 +0000611 val layerWrapper = _innerLayerWrapper
612 if (layerWrapper != null) {
613 requireNotNull(layerWrapper.layer)
614 }
615 return layerWrapper
616 }
George Mount2762fd92020-05-07 15:50:23 -0700617
618 /**
George Mount47330e92020-09-01 11:00:24 -0700619 * Invalidates the inner-most layer as part of this LayoutNode or from the containing
620 * LayoutNode. This is added for performance so that LayoutNodeWrapper.invalidateLayer() can be
621 * faster.
George Mount2762fd92020-05-07 15:50:23 -0700622 */
George Mount47330e92020-09-01 11:00:24 -0700623 internal fun invalidateLayer() {
624 val innerLayerWrapper = innerLayerWrapper
625 if (innerLayerWrapper != null) {
626 innerLayerWrapper.invalidateLayer()
627 } else {
George Mountfdd4f0f2020-11-19 23:42:29 +0000628 val parent = this.parent
George Mount47330e92020-09-01 11:00:24 -0700629 parent?.invalidateLayer()
630 }
George Mount2762fd92020-05-07 15:50:23 -0700631 }
632
633 /**
Adam Powell384bbaa2019-07-11 14:44:14 -0700634 * The [Modifier] currently applied to this node.
635 */
Doris Liu894c9112021-08-09 16:12:47 -0700636 @OptIn(ExperimentalComposeUiApi::class)
Andrey Kulikov057998a2021-01-28 18:02:50 +0000637 override var modifier: Modifier = Modifier
Adam Powell384bbaa2019-07-11 14:44:14 -0700638 set(value) {
639 if (value == field) return
Andrey Kulikov2ca07012020-07-13 16:14:03 +0100640 if (modifier != Modifier) {
641 require(!isVirtual) { "Modifiers are not supported on virtual LayoutNodes" }
642 }
Adam Powell384bbaa2019-07-11 14:44:14 -0700643 field = value
644
George Mountaf941762020-07-06 17:42:34 -0700645 val invalidateParentLayer = shouldInvalidateParentLayer()
George Mountaf941762020-07-06 17:42:34 -0700646
George Mounta2e46b02020-06-15 10:33:15 -0700647 copyWrappersToCache()
George Mount4058e842020-12-07 23:18:21 +0000648 markReusedModifiers(value)
George Mounta2e46b02020-06-15 10:33:15 -0700649
George Mountfdd4f0f2020-11-19 23:42:29 +0000650 // Rebuild LayoutNodeWrapper
Andrey Kulikovb33a3802020-05-21 20:02:53 +0100651 val oldOuterWrapper = outerMeasurablePlaceable.outerWrapper
Andrey Kulikov5fb82da2020-12-03 15:24:33 +0000652 if (outerSemantics != null && isAttached) {
yingleiw8049eaa2020-04-30 15:05:57 -0700653 owner!!.onSemanticsChange()
654 }
George Mountfb2c3532020-03-06 12:47:54 -0800655 val addedCallback = hasNewPositioningCallback()
Andrey Kulikov1f07d0fbb52021-03-19 17:59:07 +0000656 onPositionedCallbacks?.clear()
George Mounta2e46b02020-06-15 10:33:15 -0700657
George Mount60904752021-11-12 15:57:59 -0800658 innerLayoutNodeWrapper.onInitialize()
659
George Mounta2e46b02020-06-15 10:33:15 -0700660 // Create a new chain of LayoutNodeWrappers, reusing existing ones from wrappers
661 // when possible.
Andrey Kulikovb33a3802020-05-21 20:02:53 +0100662 val outerWrapper = modifier.foldOut(innerLayoutNodeWrapper) { mod, toWrap ->
Andrey Kulikov839b35c2020-07-08 18:55:13 +0100663 if (mod is RemeasurementModifier) {
664 mod.onRemeasurementAvailable(this)
665 }
George Mounta2e46b02020-06-15 10:33:15 -0700666
George Mount60904752021-11-12 15:57:59 -0800667 if (mod is DrawModifier) {
668 val drawEntity = DrawEntity(toWrap, mod)
669 drawEntity.next = toWrap.drawEntityHead
670 toWrap.drawEntityHead = drawEntity
671 drawEntity.onInitialize()
672 }
673
Ralston Da Silva6d0c4322021-10-19 18:31:23 -0700674 // Re-use the layoutNodeWrapper if possible.
675 reuseLayoutNodeWrapper(mod, toWrap)?.let {
676 return@foldOut it
677 }
678
679 // The order in which the following blocks occur matters. For example, the
680 // DrawModifier block should be before the LayoutModifier block so that a
681 // Modifier that implements both DrawModifier and LayoutModifier will have
682 // it's draw bounds reflect the dimensions defined by the LayoutModifier.
683 // Please ensure that ModifierLocalProvider is the first item here so that
684 // other layoutNodeWrappers don't accidentally use values that they provided.
685 // Also ensure that ModifierLocalConsumer is the next item here, so that it is
686 // created after all the other LayoutNodeWrappers are created, (So that the
687 // other layoutNodeWrappers are initialized by the time
688 // onModifierLocalsUpdated() is called.
689 var wrapper = toWrap
690 if (mod is ModifierLocalProvider<*>) {
691 wrapper = ModifierLocalProviderNode(wrapper, mod)
692 .initialize()
693 .assignChained(toWrap)
694 }
695 if (mod is ModifierLocalConsumer) {
696 wrapper = ModifierLocalConsumerNode(wrapper, mod)
697 .initialize()
698 .assignChained(toWrap)
699 }
Ralston Da Silva6d0c4322021-10-19 18:31:23 -0700700 if (mod is FocusModifier) {
701 wrapper = ModifiedFocusNode(wrapper, mod)
702 .initialize()
703 .assignChained(toWrap)
704 }
705 if (mod is FocusEventModifier) {
706 wrapper = ModifiedFocusEventNode(wrapper, mod)
707 .initialize()
708 .assignChained(toWrap)
709 }
710 if (mod is FocusRequesterModifier) {
711 wrapper = ModifiedFocusRequesterNode(wrapper, mod)
712 .initialize()
713 .assignChained(toWrap)
714 }
715 if (mod is FocusOrderModifier) {
716 wrapper = ModifiedFocusOrderNode(wrapper, mod)
717 .initialize()
718 .assignChained(toWrap)
719 }
720 if (mod is KeyInputModifier) {
721 wrapper = ModifiedKeyInputNode(wrapper, mod)
722 .initialize()
723 .assignChained(toWrap)
724 }
725 if (mod is PointerInputModifier) {
726 wrapper = PointerInputDelegatingWrapper(wrapper, mod)
727 .initialize()
728 .assignChained(toWrap)
729 }
730 if (mod is NestedScrollModifier) {
731 wrapper = NestedScrollDelegatingWrapper(wrapper, mod)
732 .initialize()
733 .assignChained(toWrap)
734 }
735 if (mod is LayoutModifier) {
736 wrapper = ModifiedLayoutNode(wrapper, mod)
737 .initialize()
738 .assignChained(toWrap)
739 }
740 if (mod is ParentDataModifier) {
741 wrapper = ModifiedParentDataNode(wrapper, mod)
742 .initialize()
743 .assignChained(toWrap)
744 }
745 if (mod is SemanticsModifier) {
746 wrapper = SemanticsWrapper(wrapper, mod)
747 .initialize()
748 .assignChained(toWrap)
749 }
750 if (mod is OnRemeasuredModifier) {
751 wrapper = RemeasureModifierWrapper(wrapper, mod)
752 .initialize()
753 .assignChained(toWrap)
754 }
Doris Liu894c9112021-08-09 16:12:47 -0700755 if (mod is OnPlacedModifier) {
756 wrapper = OnPlacedModifierWrapper(wrapper, mod)
757 .initialize()
758 .assignChained(toWrap)
759 }
Ralston Da Silva6d0c4322021-10-19 18:31:23 -0700760 if (mod is OnGloballyPositionedModifier) {
761 wrapper = OnGloballyPositionedModifierWrapper(wrapper, mod)
762 .initialize()
763 .assignChained(toWrap)
George Mounta2e46b02020-06-15 10:33:15 -0700764 }
George Mountf220d662019-12-20 15:17:50 -0800765 wrapper
Adam Powell384bbaa2019-07-11 14:44:14 -0700766 }
George Mounta2e46b02020-06-15 10:33:15 -0700767
Andrey Kulikovb33a3802020-05-21 20:02:53 +0100768 outerWrapper.wrappedBy = parent?.innerLayoutNodeWrapper
769 outerMeasurablePlaceable.outerWrapper = outerWrapper
George Mounta2e46b02020-06-15 10:33:15 -0700770
Andrey Kulikov5fb82da2020-12-03 15:24:33 +0000771 if (isAttached) {
George Mounta2e46b02020-06-15 10:33:15 -0700772 // call detach() on all removed LayoutNodeWrappers
Andrey Kulikov11025272020-11-18 19:23:35 +0000773 wrapperCache.forEach {
774 it.detach()
Andrey Kulikov11025272020-11-18 19:23:35 +0000775 }
George Mounta2e46b02020-06-15 10:33:15 -0700776
777 // attach() all new LayoutNodeWrappers
778 forEachDelegate {
779 if (!it.isAttached) {
780 it.attach()
781 }
782 }
783 }
George Mountdfc9ad72020-07-07 13:03:02 -0700784 wrapperCache.clear()
George Mounta2e46b02020-06-15 10:33:15 -0700785
786 // call onModifierChanged() on all LayoutNodeWrappers
787 forEachDelegate { it.onModifierChanged() }
788
Adam Powell384bbaa2019-07-11 14:44:14 -0700789 // Optimize the case where the layout itself is not modified. A common reason for
790 // this is if no wrapping actually occurs above because no LayoutModifiers are
791 // present in the modifier chain.
George Mounta2e46b02020-06-15 10:33:15 -0700792 if (oldOuterWrapper != innerLayoutNodeWrapper ||
Ralston Da Silvaca1c1f852020-07-20 13:29:18 -0700793 outerWrapper != innerLayoutNodeWrapper
794 ) {
Adam Powell384bbaa2019-07-11 14:44:14 -0700795 requestRemeasure()
Andrey Kulikovb33a3802020-05-21 20:02:53 +0100796 } else if (layoutState == Ready && addedCallback) {
George Mountfb2c3532020-03-06 12:47:54 -0800797 // We need to notify the callbacks of a change in position since there's
798 // a new one.
799 requestRemeasure()
Adam Powell384bbaa2019-07-11 14:44:14 -0700800 }
Mihai Popa8b37d1902020-10-06 12:47:41 +0100801 // If the parent data has changed, the parent needs remeasurement.
802 val oldParentData = parentData
803 outerMeasurablePlaceable.recalculateParentData()
804 if (oldParentData != parentData) {
805 parent?.requestRemeasure()
806 }
Andrey Kulikovfa215832020-11-06 16:43:55 +0000807 if (invalidateParentLayer || shouldInvalidateParentLayer()) {
George Mount47330e92020-09-01 11:00:24 -0700808 parent?.invalidateLayer()
George Mountaf941762020-07-06 17:42:34 -0700809 }
Adam Powell384bbaa2019-07-11 14:44:14 -0700810 }
811
George Mountd4741ec2020-01-23 07:30:38 -0800812 /**
George Mountfdd4f0f2020-11-19 23:42:29 +0000813 * Coordinates of just the contents of the [LayoutNode], after being affected by all modifiers.
George Mountd4741ec2020-01-23 07:30:38 -0800814 */
Andrey Kulikov5fb82da2020-12-03 15:24:33 +0000815 override val coordinates: LayoutCoordinates
George Mountd4741ec2020-01-23 07:30:38 -0800816 get() = innerLayoutNodeWrapper
Andrey Kulikov9c10dea2019-09-02 19:32:29 +0100817
Mihai Popaeb38a342019-11-27 13:17:49 +0000818 /**
819 * Callback to be executed whenever the [LayoutNode] is attached to a new [Owner].
820 */
George Mountfdd4f0f2020-11-19 23:42:29 +0000821 internal var onAttach: ((Owner) -> Unit)? = null
Mihai Popaeb38a342019-11-27 13:17:49 +0000822
Mihai Popaeb38a342019-11-27 13:17:49 +0000823 /**
824 * Callback to be executed whenever the [LayoutNode] is detached from an [Owner].
825 */
George Mountfdd4f0f2020-11-19 23:42:29 +0000826 internal var onDetach: ((Owner) -> Unit)? = null
Mihai Popaeb38a342019-11-27 13:17:49 +0000827
George Mountfb2c3532020-03-06 12:47:54 -0800828 /**
829 * List of all OnPositioned callbacks in the modifier chain.
830 */
Andrey Kulikoveef6d912021-05-14 13:12:45 +0100831 private var onPositionedCallbacks: MutableVector<OnGloballyPositionedModifierWrapper>? = null
832
Ralston Da Silva6d0c4322021-10-19 18:31:23 -0700833 internal fun getOrCreateOnPositionedCallbacks() = onPositionedCallbacks
Andrey Kulikoveef6d912021-05-14 13:12:45 +0100834 ?: mutableVectorOf<OnGloballyPositionedModifierWrapper>().also {
835 onPositionedCallbacks = it
836 }
George Mountfb2c3532020-03-06 12:47:54 -0800837
838 /**
George Mount1e6c7ff2020-07-24 15:22:40 -0700839 * Flag used by [OnPositionedDispatcher] to identify LayoutNodes that have already
George Mount43d20362020-09-21 13:40:43 -0700840 * had their [OnGloballyPositionedModifier]'s dispatch called so that they aren't called
George Mount1e6c7ff2020-07-24 15:22:40 -0700841 * multiple times.
842 */
843 internal var needsOnPositionedDispatch = false
844
Andrey Kulikov5fb82da2020-12-03 15:24:33 +0000845 internal fun place(x: Int, y: Int) {
Mihai Popa36b705d2020-09-14 11:59:30 +0100846 Placeable.PlacementScope.executeWithRtlMirroringValues(
847 outerMeasurablePlaceable.measuredWidth,
848 layoutDirection
849 ) {
Anastasia Soboleva63cab532020-08-06 11:06:37 +0100850 outerMeasurablePlaceable.placeRelative(x, y)
George Mount652bd5d2018-10-25 15:45:02 -0700851 }
852 }
853
Andrey Kulikovb33a3802020-05-21 20:02:53 +0100854 /**
855 * Place this layout node again on the same position it was placed last time
856 */
857 internal fun replace() {
Andrey Kulikov1fe98b82021-12-08 19:23:00 +0000858 try {
859 relayoutWithoutParentInProgress = true
860 outerMeasurablePlaceable.replace()
861 } finally {
862 relayoutWithoutParentInProgress = false
863 }
Andrey Kulikovb33a3802020-05-21 20:02:53 +0100864 }
865
Andrey Kulikov1fe98b82021-12-08 19:23:00 +0000866 /**
867 * Is true during [replace] invocation. Helps to differentiate between the cases when our
868 * parent is measuring us during the measure block, and when we are remeasured individually
869 * because of some change. This could be useful to know if we need to record the placing order.
870 */
871 private var relayoutWithoutParentInProgress = false
872
George Mountfdd4f0f2020-11-19 23:42:29 +0000873 internal fun draw(canvas: Canvas) = outerLayoutNodeWrapper.draw(canvas)
George Mount932dc5d2019-10-28 08:47:10 -0700874
George Mountd4741ec2020-01-23 07:30:38 -0800875 /**
Shep Shapardae9da8472020-02-06 11:33:08 -0800876 * Carries out a hit test on the [PointerInputModifier]s associated with this [LayoutNode] and
877 * all [PointerInputModifier]s on all descendant [LayoutNode]s.
878 *
George Mountf1cab882021-01-22 23:38:32 +0000879 * If [pointerPosition] is within the bounds of any tested
George Mountd1bb6b82021-07-23 22:52:24 +0000880 * [PointerInputModifier]s, the [PointerInputModifier] is added to [hitTestResult]
Shep Shapardae9da8472020-02-06 11:33:08 -0800881 * and true is returned.
882 *
George Mountf1cab882021-01-22 23:38:32 +0000883 * @param pointerPosition The tested pointer position, which is relative to
884 * the LayoutNode.
George Mountd1bb6b82021-07-23 22:52:24 +0000885 * @param hitTestResult The collection that the hit [PointerInputFilter]s will be
Shep Shapardae9da8472020-02-06 11:33:08 -0800886 * added to if hit.
Shep Shapardae9da8472020-02-06 11:33:08 -0800887 */
George Mountfdd4f0f2020-11-19 23:42:29 +0000888 internal fun hitTest(
George Mountf1cab882021-01-22 23:38:32 +0000889 pointerPosition: Offset,
George Mountd1bb6b82021-07-23 22:52:24 +0000890 hitTestResult: HitTestResult<PointerInputFilter>,
George Mounte4f4cac2021-11-08 16:33:01 -0800891 isTouchEvent: Boolean = false,
892 isInLayer: Boolean = true
Shep Shapard78f42892020-05-20 16:02:06 -0700893 ) {
George Mountf1cab882021-01-22 23:38:32 +0000894 val positionInWrapped = outerLayoutNodeWrapper.fromParentPosition(pointerPosition)
Alexandre Elias5370dbc2021-06-01 19:35:18 -0700895 outerLayoutNodeWrapper.hitTest(
896 positionInWrapped,
George Mountd1bb6b82021-07-23 22:52:24 +0000897 hitTestResult,
George Mounte4f4cac2021-11-08 16:33:01 -0800898 isTouchEvent,
899 isInLayer
Alexandre Elias5370dbc2021-06-01 19:35:18 -0700900 )
901 }
902
George Mountd1bb6b82021-07-23 22:52:24 +0000903 @Suppress("UNUSED_PARAMETER")
Alexandre Elias5370dbc2021-06-01 19:35:18 -0700904 internal fun hitTestSemantics(
905 pointerPosition: Offset,
George Mountd1bb6b82021-07-23 22:52:24 +0000906 hitSemanticsWrappers: HitTestResult<SemanticsWrapper>,
George Mounte4f4cac2021-11-08 16:33:01 -0800907 isTouchEvent: Boolean = true,
908 isInLayer: Boolean = true
Alexandre Elias5370dbc2021-06-01 19:35:18 -0700909 ) {
910 val positionInWrapped = outerLayoutNodeWrapper.fromParentPosition(pointerPosition)
911 outerLayoutNodeWrapper.hitTestSemantics(
912 positionInWrapped,
George Mounte4f4cac2021-11-08 16:33:01 -0800913 hitSemanticsWrappers,
914 isInLayer
Alexandre Elias5370dbc2021-06-01 19:35:18 -0700915 )
Shep Shapardae9da8472020-02-06 11:33:08 -0800916 }
917
918 /**
George Mount43d20362020-09-21 13:40:43 -0700919 * Return true if there is a new [OnGloballyPositionedModifier] assigned to this Layout.
George Mountfb2c3532020-03-06 12:47:54 -0800920 */
921 private fun hasNewPositioningCallback(): Boolean {
Andrey Kulikov1f07d0fbb52021-03-19 17:59:07 +0000922 val onPositionedCallbacks = onPositionedCallbacks
Andrey Kulikoveef6d912021-05-14 13:12:45 +0100923 return modifier.foldOut(false) { mod, hasNewCallback ->
924 hasNewCallback || mod is OnGloballyPositionedModifier &&
925 (onPositionedCallbacks?.firstOrNull { mod == it.modifier } == null)
George Mountfb2c3532020-03-06 12:47:54 -0800926 }
927 }
928
Andrey Kulikov308929c2020-09-03 17:07:40 +0300929 /**
930 * Invoked when the parent placed the node. It will trigger the layout.
931 */
932 internal fun onNodePlaced() {
Andrey Kulikovfc91b882020-09-24 13:31:29 +0100933 val parent = parent
934
Andrey Kulikov11025272020-11-18 19:23:35 +0000935 var newZIndex = innerLayoutNodeWrapper.zIndex
Andrey Kulikovfa215832020-11-06 16:43:55 +0000936 forEachDelegate {
937 newZIndex += it.zIndex
938 }
939 if (newZIndex != zIndex) {
940 zIndex = newZIndex
Andrey Kulikove80baea2021-05-18 13:57:40 +0100941 parent?.onZSortedChildrenInvalidated()
Andrey Kulikovfa215832020-11-06 16:43:55 +0000942 parent?.invalidateLayer()
943 }
944
Andrey Kulikov308929c2020-09-03 17:07:40 +0300945 if (!isPlaced) {
Andrey Kulikov308929c2020-09-03 17:07:40 +0300946 // when the visibility of a child has been changed we need to invalidate
947 // parents inner layer - the layer in which this child will be drawn
948 parent?.invalidateLayer()
Andrey Kulikova3b56612021-05-20 20:57:53 +0100949 markNodeAndSubtreeAsPlaced()
Andrey Kulikov308929c2020-09-03 17:07:40 +0300950 }
Andrey Kulikovfc91b882020-09-24 13:31:29 +0100951
952 if (parent != null) {
Andrey Kulikov1fe98b82021-12-08 19:23:00 +0000953 if (!relayoutWithoutParentInProgress && parent.layoutState == LayingOut) {
Andrey Kulikovfc91b882020-09-24 13:31:29 +0100954 // the parent is currently placing its children
955 check(placeOrder == NotPlacedPlaceOrder) {
956 "Place was called on a node which was placed already"
957 }
958 placeOrder = parent.nextChildPlaceOrder
959 parent.nextChildPlaceOrder++
960 }
Andrey Kulikov1fe98b82021-12-08 19:23:00 +0000961 // if relayoutWithoutParentInProgress is true we were asked to be relaid out without
962 // affecting the parent. this means our placeOrder didn't change since the last time
963 // parent placed us.
Andrey Kulikovfc91b882020-09-24 13:31:29 +0100964 } else {
965 // parent is null for the root node
966 placeOrder = 0
967 }
968
Andrey Kulikov308929c2020-09-03 17:07:40 +0300969 layoutChildren()
970 }
971
Mihai Popa79815ff2021-04-27 17:05:22 +0100972 internal fun layoutChildren() {
973 alignmentLines.recalculateQueryOwner()
974
Andrey Kulikovb33a3802020-05-21 20:02:53 +0100975 if (layoutState == NeedsRelayout) {
976 onBeforeLayoutChildren()
977 }
978 // as a result of the previous operation we can figure out a child has been resized
979 // and we need to be remeasured, not relaid out
980 if (layoutState == NeedsRelayout) {
George Mountfdd4f0f2020-11-19 23:42:29 +0000981 layoutState = LayingOut
Andrey Kulikov9ce118c2019-10-14 19:18:41 +0100982 val owner = requireOwner()
Andrey Kulikovd82950b2020-11-11 15:23:18 +0000983 owner.snapshotObserver.observeLayoutSnapshotReads(this) {
Andrey Kulikovfc91b882020-09-24 13:31:29 +0100984 // reset the place order counter which will be used by the children
985 nextChildPlaceOrder = 0
George Mountdfc9ad72020-07-07 13:03:02 -0700986 _children.forEach { child ->
Andrey Kulikovfc91b882020-09-24 13:31:29 +0100987 // and reset the place order for all the children before placing them
Andrey Kulikov4aa63ec2021-05-24 18:06:35 +0100988 child.previousPlaceOrder = child.placeOrder
Andrey Kulikovfc91b882020-09-24 13:31:29 +0100989 child.placeOrder = NotPlacedPlaceOrder
Mihai Popa79815ff2021-04-27 17:05:22 +0100990 child.alignmentLines.usedDuringParentLayout = false
Andrey Kulikov1fe98b82021-12-08 19:23:00 +0000991 // before rerunning the user's layout block reset previous measuredByParent
992 // for children which we measured in the layout block during the last run.
993 if (child.measuredByParent == UsageByParent.InLayoutBlock) {
994 child.measuredByParent = UsageByParent.NotUsed
995 }
Mihai Popae5178352019-09-03 17:04:13 +0100996 }
Mihai Popa79815ff2021-04-27 17:05:22 +0100997
Anastasia Soboleva2833c362020-07-23 20:19:13 +0100998 innerLayoutNodeWrapper.measureResult.placeChildren()
George Mountdfc9ad72020-07-07 13:03:02 -0700999 _children.forEach { child ->
Andrey Kulikovfc91b882020-09-24 13:31:29 +01001000 // we set `placeOrder` to NotPlacedPlaceOrder for all the children, then
1001 // during the placeChildren() invocation the real order will be assigned for
1002 // all the placed children.
Andrey Kulikov4aa63ec2021-05-24 18:06:35 +01001003 if (child.previousPlaceOrder != child.placeOrder) {
1004 onZSortedChildrenInvalidated()
Andrey Kulikov6f0b4cb2021-03-30 21:40:19 +01001005 invalidateLayer()
Andrey Kulikov4aa63ec2021-05-24 18:06:35 +01001006 if (child.placeOrder == NotPlacedPlaceOrder) {
1007 child.markSubtreeAsNotPlaced()
1008 }
Andrey Kulikov308929c2020-09-03 17:07:40 +03001009 }
Mihai Popa79815ff2021-04-27 17:05:22 +01001010 child.alignmentLines.previousUsedDuringParentLayout =
1011 child.alignmentLines.usedDuringParentLayout
George Mount6623eb42019-12-04 14:38:44 -08001012 }
Andrey Kulikov9c10dea2019-09-02 19:32:29 +01001013 }
Mihai Popa42aa7572019-07-30 15:46:36 +01001014
Andrey Kulikovb33a3802020-05-21 20:02:53 +01001015 layoutState = Ready
1016 }
Mihai Popa79815ff2021-04-27 17:05:22 +01001017
1018 if (alignmentLines.usedDuringParentLayout) {
1019 alignmentLines.previousUsedDuringParentLayout = true
1020 }
1021 if (alignmentLines.dirty && alignmentLines.required) alignmentLines.recalculate()
Andrey Kulikovb33a3802020-05-21 20:02:53 +01001022 }
1023
Andrey Kulikova3b56612021-05-20 20:57:53 +01001024 private fun markNodeAndSubtreeAsPlaced() {
1025 isPlaced = true
1026 // invalidate all the nodes layers that were invalidated while the node was not placed
1027 forEachDelegateIncludingInner {
1028 if (it.lastLayerDrawingWasSkipped) {
1029 it.invalidateLayer()
Andrey Kulikov308929c2020-09-03 17:07:40 +03001030 }
1031 }
Andrey Kulikova3b56612021-05-20 20:57:53 +01001032 _children.forEach {
1033 // this child was placed during the previous parent's layoutChildren(). this means that
1034 // before the parent became not placed this child was placed. we need to restore that
1035 if (it.placeOrder != NotPlacedPlaceOrder) {
1036 it.markNodeAndSubtreeAsPlaced()
1037 rescheduleRemeasureOrRelayout(it)
1038 }
1039 }
1040 }
1041
1042 private fun rescheduleRemeasureOrRelayout(it: LayoutNode) {
1043 when (val state = it.layoutState) {
1044 NeedsRemeasure, NeedsRelayout -> {
1045 // we need to reset the state before requesting as otherwise the request
1046 // would be ignored.
1047 it.layoutState = Ready
1048 // this node was scheduled for remeasure or relayout while it was not
1049 // placed. such requests are ignored for non-placed nodes so we have to
1050 // re-schedule remeasure or relayout.
1051 if (state == NeedsRemeasure) {
1052 it.requestRemeasure()
1053 } else {
1054 it.requestRelayout()
1055 }
1056 }
1057 Ready -> {
1058 // no extra work required and node is ready to be displayed
1059 }
1060 else -> throw IllegalStateException("Unexpected state ${it.layoutState}")
1061 }
Andrey Kulikov308929c2020-09-03 17:07:40 +03001062 }
1063
1064 private fun markSubtreeAsNotPlaced() {
1065 if (isPlaced) {
1066 isPlaced = false
1067 _children.forEach {
Andrey Kulikov7e1446e2020-11-16 20:52:40 +00001068 it.markSubtreeAsNotPlaced()
Andrey Kulikov308929c2020-09-03 17:07:40 +03001069 }
1070 }
1071 }
1072
Andrey Kulikovb33a3802020-05-21 20:02:53 +01001073 /**
1074 * The callback to be executed before running layoutChildren.
1075 *
1076 * There are possible cases when we run layoutChildren() on the parent node, but some of its
1077 * children are not yet measured even if they are supposed to be measured in the measure
1078 * block of our parent.
1079 *
1080 * Example:
1081 * val child = Layout(...)
Ralston Da Silva6d0c4322021-10-19 18:31:23 -07001082 * Layout(child) { measurable, constraints ->
1083 * val placeable = measurable.first().measure(constraints)
Andrey Kulikovb33a3802020-05-21 20:02:53 +01001084 * layout(placeable.width, placeable.height) {
1085 * placeable.place(0, 0)
1086 * }
1087 * }
1088 * And now some set of changes scheduled remeasure for child and relayout for parent.
1089 *
1090 * During the [MeasureAndLayoutDelegate.measureAndLayout] we will start with the parent as it
1091 * has lower depth. Inside the layout block we will call placeable.width which is currently
1092 * dirty as the child was scheduled to remeasure. This callback will ensure it never happens
1093 * and pre-remeasure everything required for this layoutChildren().
1094 */
1095 private fun onBeforeLayoutChildren() {
George Mountdfc9ad72020-07-07 13:03:02 -07001096 _children.forEach {
Andrey Kulikovb33a3802020-05-21 20:02:53 +01001097 if (it.layoutState == NeedsRemeasure &&
1098 it.measuredByParent == UsageByParent.InMeasureBlock
1099 ) {
1100 if (it.remeasure()) {
1101 requestRemeasure()
1102 }
1103 }
1104 }
1105 }
1106
1107 internal fun onAlignmentsChanged() {
Mihai Popa79815ff2021-04-27 17:05:22 +01001108 if (alignmentLines.dirty) return
1109 alignmentLines.dirty = true
1110
1111 val parent = parent ?: return
1112 if (alignmentLines.usedDuringParentMeasurement) {
1113 parent.requestRemeasure()
1114 } else if (alignmentLines.previousUsedDuringParentLayout) {
1115 parent.requestRelayout()
Mihai Popa42aa7572019-07-30 15:46:36 +01001116 }
Mihai Popa79815ff2021-04-27 17:05:22 +01001117 if (alignmentLines.usedByModifierMeasurement) {
1118 requestRemeasure()
1119 }
1120 if (alignmentLines.usedByModifierLayout) {
1121 parent.requestRelayout()
1122 }
1123 parent.onAlignmentsChanged()
Mihai Popa42aa7572019-07-30 15:46:36 +01001124 }
1125
George Mount8f237572020-04-30 12:08:30 -07001126 internal fun calculateAlignmentLines(): Map<AlignmentLine, Int> {
Mihai Popa79815ff2021-04-27 17:05:22 +01001127 if (!outerMeasurablePlaceable.duringAlignmentLinesQuery) {
1128 alignmentLinesQueriedByModifier()
Andrey Kulikovb33a3802020-05-21 20:02:53 +01001129 }
Mihai Popa79815ff2021-04-27 17:05:22 +01001130 layoutChildren()
1131 return alignmentLines.getLastCalculation()
1132 }
1133
1134 private fun alignmentLinesQueriedByModifier() {
1135 if (layoutState == Measuring) {
1136 alignmentLines.usedByModifierMeasurement = true
1137 // We quickly transition to NeedsRelayout as we need the alignment lines now.
1138 // Later we will see that we also laid out as part of measurement and will skip layout.
1139 if (alignmentLines.dirty) layoutState = NeedsRelayout
1140 } else {
1141 // Note this can also happen for onGloballyPositioned queries.
1142 alignmentLines.usedByModifierLayout = true
Andrey Kulikovb33a3802020-05-21 20:02:53 +01001143 }
George Mount31c3edc2019-08-14 15:30:26 -07001144 }
1145
Mihai Popaaf03ea32020-10-19 14:47:36 +01001146 internal fun handleMeasureResult(measureResult: MeasureResult) {
Mihai Popa155d7222020-03-27 15:02:29 +00001147 innerLayoutNodeWrapper.measureResult = measureResult
George Mount652bd5d2018-10-25 15:45:02 -07001148 }
Mihai Popa8d4dcaa2019-03-12 17:05:24 +00001149
1150 /**
Mihai Popaeb38a342019-11-27 13:17:49 +00001151 * Used to request a new measurement + layout pass from the owner.
Mihai Popa8d4dcaa2019-03-12 17:05:24 +00001152 */
George Mountfdd4f0f2020-11-19 23:42:29 +00001153 internal fun requestRemeasure() {
Mihai Popaf411f762021-06-07 18:21:24 +01001154 val owner = owner ?: return
Andrey Kulikov56217e22021-06-03 20:02:31 +01001155 if (!ignoreRemeasureRequests && !isVirtual) {
Mihai Popaf411f762021-06-07 18:21:24 +01001156 owner.onRequestMeasure(this)
Andrey Kulikov804e0e62021-04-16 17:51:44 +01001157 }
1158 }
1159
1160 internal inline fun ignoreRemeasureRequests(block: () -> Unit) {
1161 ignoreRemeasureRequests = true
1162 block()
1163 ignoreRemeasureRequests = false
Adam Powell384bbaa2019-07-11 14:44:14 -07001164 }
George Mount31c3edc2019-08-14 15:30:26 -07001165
Andrey Kulikovbd5cf9e2019-11-21 20:46:48 +00001166 /**
Andrey Kulikovb33a3802020-05-21 20:02:53 +01001167 * Used to request a new layout pass from the owner.
1168 */
George Mountfdd4f0f2020-11-19 23:42:29 +00001169 internal fun requestRelayout() {
Andrey Kulikov56217e22021-06-03 20:02:31 +01001170 if (!isVirtual) {
1171 owner?.onRequestRelayout(this)
1172 }
Andrey Kulikovb33a3802020-05-21 20:02:53 +01001173 }
1174
1175 /**
Andrey Kulikovbd5cf9e2019-11-21 20:46:48 +00001176 * Execute your code within the [block] if you want some code to not be observed for the
1177 * model reads even if you are currently inside some observed scope like measuring.
1178 */
Andrey Kulikov915e3f22021-02-04 17:05:19 +00001179 internal fun withNoSnapshotReadObservation(block: () -> Unit) {
1180 requireOwner().snapshotObserver.withNoSnapshotReadObservation(block)
Andrey Kulikovbd5cf9e2019-11-21 20:46:48 +00001181 }
1182
Andrey Kulikov9c10dea2019-09-02 19:32:29 +01001183 internal fun dispatchOnPositionedCallbacks() {
Andrey Kulikovb33a3802020-05-21 20:02:53 +01001184 if (layoutState != Ready) {
1185 return // it hasn't yet been properly positioned, so don't make a call
George Mounta04d2fa2020-03-26 17:04:49 -07001186 }
Andrey Kulikovb87ddf62020-03-31 17:27:27 +01001187 if (!isPlaced) {
1188 return // it hasn't been placed, so don't make a call
1189 }
Andrey Kulikoveef6d912021-05-14 13:12:45 +01001190 onPositionedCallbacks?.forEach {
1191 it.modifier.onGloballyPositioned(it)
1192 }
George Mount31c3edc2019-08-14 15:30:26 -07001193 }
1194
George Mount2db95b02020-04-13 15:19:34 -07001195 /**
1196 * This returns a new List of Modifiers and the coordinates and any extra information
1197 * that may be useful. This is used for tooling to retrieve layout modifier and layer
1198 * information.
1199 */
Andrey Kulikov5fb82da2020-12-03 15:24:33 +00001200 override fun getModifierInfo(): List<ModifierInfo> {
George Mountdfc9ad72020-07-07 13:03:02 -07001201 val infoList = mutableVectorOf<ModifierInfo>()
George Mounta2e46b02020-06-15 10:33:15 -07001202 forEachDelegate { wrapper ->
Andrey Kulikov11025272020-11-18 19:23:35 +00001203 wrapper as DelegatingLayoutNodeWrapper<*>
George Mount60904752021-11-12 15:57:59 -08001204 val layer = wrapper.layer
1205 val info = ModifierInfo(wrapper.modifier, wrapper, layer)
George Mount2db95b02020-04-13 15:19:34 -07001206 infoList += info
George Mount60904752021-11-12 15:57:59 -08001207 var node = wrapper.drawEntityHead // head
1208 while (node != null) {
1209 infoList += ModifierInfo(node.modifier, wrapper, layer)
1210 node = node.next
1211 }
1212 }
1213 var innerNode = innerLayoutNodeWrapper.drawEntityHead
1214 while (innerNode != null) {
1215 infoList += ModifierInfo(
1216 innerNode.modifier,
1217 innerLayoutNodeWrapper,
1218 innerLayoutNodeWrapper.layer
1219 )
1220 innerNode = innerNode.next
George Mount2db95b02020-04-13 15:19:34 -07001221 }
George Mountdfc9ad72020-07-07 13:03:02 -07001222 return infoList.asMutableList()
George Mount2db95b02020-04-13 15:19:34 -07001223 }
1224
George Mounta2e46b02020-06-15 10:33:15 -07001225 /**
George Mount4de8f5a2020-10-15 16:09:41 -07001226 * Invalidates layers defined on this LayoutNode.
1227 */
1228 internal fun invalidateLayers() {
1229 forEachDelegate { wrapper ->
Andrey Kulikov11025272020-11-18 19:23:35 +00001230 wrapper.layer?.invalidate()
George Mount4de8f5a2020-10-15 16:09:41 -07001231 }
George Mounta41ac4c2021-01-14 18:57:01 +00001232 innerLayoutNodeWrapper.layer?.invalidate()
George Mount4de8f5a2020-10-15 16:09:41 -07001233 }
1234
1235 /**
George Mounta2e46b02020-06-15 10:33:15 -07001236 * Reuses a [DelegatingLayoutNodeWrapper] from [wrapperCache] if one matches the class
1237 * type of [modifier]. This walks backward through the [wrapperCache] and
1238 * extracts all [DelegatingLayoutNodeWrapper]s that are
1239 * [chained][DelegatingLayoutNodeWrapper.isChained] together.
1240 * If none can be reused, `null` is returned.
1241 */
1242 private fun reuseLayoutNodeWrapper(
1243 modifier: Modifier.Element,
1244 wrapper: LayoutNodeWrapper
1245 ): DelegatingLayoutNodeWrapper<*>? {
George Mountdfc9ad72020-07-07 13:03:02 -07001246 if (wrapperCache.isEmpty()) {
George Mounta2e46b02020-06-15 10:33:15 -07001247 return null
1248 }
George Mount4058e842020-12-07 23:18:21 +00001249 // Look for exact match
Ralston Da Silva6d0c4322021-10-19 18:31:23 -07001250 var lastIndex = wrapperCache.indexOfLast {
George Mount4058e842020-12-07 23:18:21 +00001251 it.toBeReusedForSameModifier && it.modifier === modifier
1252 }
1253
Ralston Da Silva6d0c4322021-10-19 18:31:23 -07001254 if (lastIndex < 0) {
George Mount4058e842020-12-07 23:18:21 +00001255 // Look for class match
Ralston Da Silva6d0c4322021-10-19 18:31:23 -07001256 lastIndex = wrapperCache.indexOfLast {
George Mount4058e842020-12-07 23:18:21 +00001257 !it.toBeReusedForSameModifier && it.modifier.nativeClass() == modifier.nativeClass()
1258 }
George Mountdfc9ad72020-07-07 13:03:02 -07001259 }
George Mounta2e46b02020-06-15 10:33:15 -07001260
Ralston Da Silva6d0c4322021-10-19 18:31:23 -07001261 if (lastIndex < 0) {
George Mounta2e46b02020-06-15 10:33:15 -07001262 return null
1263 }
1264
Ralston Da Silva6d0c4322021-10-19 18:31:23 -07001265 val endWrapper = wrapperCache.removeAt(lastIndex--)
George Mounta2e46b02020-06-15 10:33:15 -07001266 endWrapper.wrapped = wrapper
Ralston Da Silva6d0c4322021-10-19 18:31:23 -07001267 endWrapper.setModifierTo(modifier)
1268 endWrapper.initialize()
1269
1270 var startWrapper = endWrapper
1271 while (startWrapper.isChained) {
1272 startWrapper = wrapperCache.removeAt(lastIndex--)
1273 startWrapper.setModifierTo(modifier)
1274 startWrapper.initialize()
1275 }
George Mounta2e46b02020-06-15 10:33:15 -07001276 return startWrapper
1277 }
1278
George Mounta2e46b02020-06-15 10:33:15 -07001279 /**
1280 * Copies all [DelegatingLayoutNodeWrapper]s currently in use and returns them in a new
1281 * Array.
1282 */
1283 private fun copyWrappersToCache() {
George Mounta2e46b02020-06-15 10:33:15 -07001284 forEachDelegate {
George Mountdfc9ad72020-07-07 13:03:02 -07001285 wrapperCache += it as DelegatingLayoutNodeWrapper<*>
George Mount60904752021-11-12 15:57:59 -08001286 it.drawEntityHead = null
George Mounta2e46b02020-06-15 10:33:15 -07001287 }
George Mount60904752021-11-12 15:57:59 -08001288 innerLayoutNodeWrapper.drawEntityHead = null
George Mounta2e46b02020-06-15 10:33:15 -07001289 }
1290
George Mount4058e842020-12-07 23:18:21 +00001291 private fun markReusedModifiers(modifier: Modifier) {
1292 wrapperCache.forEach {
1293 it.toBeReusedForSameModifier = false
1294 }
1295
1296 modifier.foldIn(Unit) { _, mod ->
Leland Richardsonb6c89a52021-03-24 14:20:56 -07001297 var wrapper = wrapperCache.lastOrNull {
1298 it.modifier === mod && !it.toBeReusedForSameModifier
1299 }
1300 // we want to walk up the chain up all LayoutNodeWrappers for the same modifier
1301 while (wrapper != null) {
1302 wrapper.toBeReusedForSameModifier = true
1303 wrapper = if (wrapper.isChained)
1304 wrapper.wrappedBy as? DelegatingLayoutNodeWrapper<*>
1305 else
1306 null
1307 }
George Mount4058e842020-12-07 23:18:21 +00001308 }
1309 }
1310
Andrey Kulikovb33a3802020-05-21 20:02:53 +01001311 // Delegation from Measurable to measurableAndPlaceable
Anastasia Soboleva2833c362020-07-23 20:19:13 +01001312 override fun measure(constraints: Constraints) =
1313 outerMeasurablePlaceable.measure(constraints)
Andrey Kulikovb33a3802020-05-21 20:02:53 +01001314
1315 /**
1316 * Return true if the measured size has been changed
1317 */
1318 internal fun remeasure(
Andrey Kulikova8cd5642021-06-17 18:03:08 +01001319 constraints: Constraints? = outerMeasurablePlaceable.lastConstraints
1320 ): Boolean {
1321 return if (constraints != null) {
1322 outerMeasurablePlaceable.remeasure(constraints)
1323 } else {
1324 false
1325 }
1326 }
Andrey Kulikovb33a3802020-05-21 20:02:53 +01001327
1328 override val parentData: Any? get() = outerMeasurablePlaceable.parentData
1329
Anastasia Soboleva2833c362020-07-23 20:19:13 +01001330 override fun minIntrinsicWidth(height: Int): Int =
1331 outerMeasurablePlaceable.minIntrinsicWidth(height)
Andrey Kulikovb33a3802020-05-21 20:02:53 +01001332
Anastasia Soboleva2833c362020-07-23 20:19:13 +01001333 override fun maxIntrinsicWidth(height: Int): Int =
1334 outerMeasurablePlaceable.maxIntrinsicWidth(height)
Andrey Kulikovb33a3802020-05-21 20:02:53 +01001335
Anastasia Soboleva2833c362020-07-23 20:19:13 +01001336 override fun minIntrinsicHeight(width: Int): Int =
1337 outerMeasurablePlaceable.minIntrinsicHeight(width)
Andrey Kulikovb33a3802020-05-21 20:02:53 +01001338
Anastasia Soboleva2833c362020-07-23 20:19:13 +01001339 override fun maxIntrinsicHeight(width: Int): Int =
1340 outerMeasurablePlaceable.maxIntrinsicHeight(width)
Andrey Kulikovb33a3802020-05-21 20:02:53 +01001341
Andrey Kulikov839b35c2020-07-08 18:55:13 +01001342 override fun forceRemeasure() {
1343 requestRemeasure()
1344 owner?.measureAndLayout()
1345 }
1346
George Mounta2e46b02020-06-15 10:33:15 -07001347 /**
1348 * Calls [block] on all [DelegatingLayoutNodeWrapper]s in the LayoutNodeWrapper chain.
1349 */
1350 private inline fun forEachDelegate(block: (LayoutNodeWrapper) -> Unit) {
1351 var delegate = outerLayoutNodeWrapper
1352 val inner = innerLayoutNodeWrapper
1353 while (delegate != inner) {
1354 block(delegate)
1355 delegate = delegate.wrapped!!
1356 }
1357 }
1358
Andrey Kulikov11025272020-11-18 19:23:35 +00001359 /**
1360 * Calls [block] on all [DelegatingLayoutNodeWrapper]s in the LayoutNodeWrapper chain.
1361 */
1362 private inline fun forEachDelegateIncludingInner(block: (LayoutNodeWrapper) -> Unit) {
1363 var delegate: LayoutNodeWrapper? = outerLayoutNodeWrapper
1364 val final = innerLayoutNodeWrapper.wrapped
1365 while (delegate != final && delegate != null) {
1366 block(delegate)
1367 delegate = delegate.wrapped
1368 }
1369 }
1370
George Mountaf941762020-07-06 17:42:34 -07001371 private fun shouldInvalidateParentLayer(): Boolean {
Andrey Kulikov11025272020-11-18 19:23:35 +00001372 forEachDelegateIncludingInner {
Andrey Kulikov9e6f5252020-12-04 13:08:38 +00001373 if (it.layer != null) {
George Mountaf941762020-07-06 17:42:34 -07001374 return false
George Mount60904752021-11-12 15:57:59 -08001375 } else if (it.drawEntityHead != null) {
Andrey Kulikov9e6f5252020-12-04 13:08:38 +00001376 return true
George Mountaf941762020-07-06 17:42:34 -07001377 }
1378 }
Andrey Kulikov9e6f5252020-12-04 13:08:38 +00001379 return true
George Mountaf941762020-07-06 17:42:34 -07001380 }
1381
Andrey Kulikovfc91b882020-09-24 13:31:29 +01001382 /**
1383 * Comparator allowing to sort nodes by zIndex and placement order.
1384 */
1385 private val ZComparator = Comparator<LayoutNode> { node1, node2 ->
1386 if (node1.zIndex == node2.zIndex) {
1387 // if zIndex is the same we use the placement order
1388 node1.placeOrder.compareTo(node2.placeOrder)
1389 } else {
1390 node1.zIndex.compareTo(node2.zIndex)
1391 }
1392 }
1393
Andrey Kulikov5fb82da2020-12-03 15:24:33 +00001394 override val parentInfo: LayoutInfo?
1395 get() = parent
1396
George Mount31c3edc2019-08-14 15:30:26 -07001397 internal companion object {
Mihai Popa49584752021-01-25 12:38:07 +00001398 private val ErrorMeasurePolicy: NoIntrinsicsMeasurePolicy =
1399 object : NoIntrinsicsMeasurePolicy(
Nikolay Igotti438fdbf2020-06-25 12:05:58 +03001400 error = "Undefined intrinsics block and it is required"
1401 ) {
Mihai Popa49584752021-01-25 12:38:07 +00001402 override fun MeasureScope.measure(
Nikolay Igotti438fdbf2020-06-25 12:05:58 +03001403 measurables: List<Measurable>,
Anastasia Soboleva2833c362020-07-23 20:19:13 +01001404 constraints: Constraints
Nikolay Igotti438fdbf2020-06-25 12:05:58 +03001405 ) = error("Undefined measure and it is required")
Ralston Da Silvaca1c1f852020-07-20 13:29:18 -07001406 }
Andrey Kulikovfc91b882020-09-24 13:31:29 +01001407
1408 /**
1409 * Constant used by [placeOrder].
1410 */
Andrey Kulikova3b56612021-05-20 20:57:53 +01001411 internal const val NotPlacedPlaceOrder = Int.MAX_VALUE
Andrey Kulikov057998a2021-01-28 18:02:50 +00001412
1413 /**
1414 * Pre-allocated constructor to be used with ComposeNode
1415 */
1416 internal val Constructor: () -> LayoutNode = { LayoutNode() }
George Mountd1bb6b82021-07-23 22:52:24 +00001417
1418 /**
1419 * All of these values are only used in tests. The real ViewConfiguration should
1420 * be set in Layout()
1421 */
1422 internal val DummyViewConfiguration = object : ViewConfiguration {
1423 override val longPressTimeoutMillis: Long
1424 get() = 400L
1425 override val doubleTapTimeoutMillis: Long
1426 get() = 300L
1427 override val doubleTapMinTimeMillis: Long
1428 get() = 40L
1429 override val touchSlop: Float
1430 get() = 16f
1431 override val minimumTouchTargetSize: DpSize
1432 get() = DpSize.Zero
1433 }
George Mount31c3edc2019-08-14 15:30:26 -07001434 }
Andrey Kulikovb33a3802020-05-21 20:02:53 +01001435
1436 /**
1437 * Describes the current state the [LayoutNode] is in.
1438 */
Andrey Kulikov2ca07012020-07-13 16:14:03 +01001439 internal enum class LayoutState {
Andrey Kulikovb33a3802020-05-21 20:02:53 +01001440 /**
1441 * Request remeasure was called on the node.
1442 */
1443 NeedsRemeasure,
1444 /**
1445 * Node is currently being measured.
1446 */
1447 Measuring,
1448 /**
1449 * Request relayout was called on the node or the node was just measured and is going to
1450 * layout soon (measure stage is always being followed by the layout stage).
1451 */
1452 NeedsRelayout,
1453 /**
1454 * Node is currently being laid out.
1455 */
1456 LayingOut,
1457 /**
1458 * Node is measured and laid out or not yet attached to the [Owner] (see [LayoutNode.owner]).
1459 */
1460 Ready
1461 }
1462
1463 internal enum class UsageByParent {
1464 InMeasureBlock,
1465 InLayoutBlock,
1466 NotUsed,
1467 }
George Mount652bd5d2018-10-25 15:45:02 -07001468}
1469
1470/**
George Mount17d6fe52020-05-15 08:08:23 -07001471 * Returns [LayoutNode.owner] or throws if it is null.
Andrey Kulikov8d7bb4a2019-05-13 17:53:55 +01001472 */
George Mounte11880a2020-07-17 15:28:22 -07001473internal fun LayoutNode.requireOwner(): Owner {
1474 val owner = owner
1475 checkNotNull(owner) {
1476 "LayoutNode should be attached to an owner"
1477 }
1478 return owner
1479}
Andrey Kulikov8d7bb4a2019-05-13 17:53:55 +01001480
1481/**
Andrey Kulikov5fb82da2020-12-03 15:24:33 +00001482 * Inserts a child [LayoutNode] at a last index. If this LayoutNode [LayoutNode.isAttached]
1483 * then [child] will become [LayoutNode.isAttached] also. [child] must have a `null`
1484 * [LayoutNode.parent].
George Mount652bd5d2018-10-25 15:45:02 -07001485 */
George Mount17d6fe52020-05-15 08:08:23 -07001486internal fun LayoutNode.add(child: LayoutNode) {
1487 insertAt(children.size, child)
George Mount652bd5d2018-10-25 15:45:02 -07001488}
1489
Andrey Kulikov3d6c1be2019-01-14 15:49:07 +00001490/**
Ralston Da Silva228085e2020-08-12 03:34:16 -07001491 * Sets [DelegatingLayoutNodeWrapper#isChained] to `true` of the [wrapped][this.wrapped] when it
George Mounta2e46b02020-06-15 10:33:15 -07001492 * is part of a chain of LayoutNodes for the same modifier.
1493 *
1494 * @param originalWrapper The LayoutNodeWrapper that the modifier chain should be wrapping.
1495 */
1496@Suppress("NOTHING_TO_INLINE")
1497private inline fun <T : DelegatingLayoutNodeWrapper<*>> T.assignChained(
1498 originalWrapper: LayoutNodeWrapper
1499): T {
1500 if (originalWrapper !== wrapped) {
Ralston Da Silva228085e2020-08-12 03:34:16 -07001501 val wrapper = wrapped as DelegatingLayoutNodeWrapper<*>
George Mounta2e46b02020-06-15 10:33:15 -07001502 wrapper.isChained = true
1503 }
1504 return this
George Mountd1bb6b82021-07-23 22:52:24 +00001505}
Ralston Da Silva6d0c4322021-10-19 18:31:23 -07001506
1507@Suppress("NOTHING_TO_INLINE")
1508private inline fun <T : DelegatingLayoutNodeWrapper<*>> T.initialize(): T {
1509 onInitialize()
1510 return this
1511}