1 /*
2  * Copyright 2024 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.xr.compose.subspace.layout
18 
19 import androidx.annotation.RestrictTo
20 import androidx.xr.compose.subspace.node.LayoutCoordinatesAwareModifierNode
21 import androidx.xr.compose.subspace.node.SubspaceModifierNodeElement
22 
23 /**
24  * Invoke [onGloballyPositioned] with the [SubspaceLayoutCoordinates] of the element when the global
25  * position of the content may have changed. Note that it will be called **after** a composition
26  * when the coordinates are finalized.
27  *
28  * This callback will be invoked at least once when the [SubspaceLayoutCoordinates] are available,
29  * and every time the element's position changes within the window. However, it is not guaranteed to
30  * be invoked every time the position _relative to the screen_ of the modified element changes. For
31  * example, the system may move the contents inside a window around without firing a callback. If
32  * you are using the [SubspaceLayoutCoordinates] to calculate position on the screen, and not just
33  * inside the window, you may not receive a callback.
34  */
35 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
onGloballyPositionednull36 public fun SubspaceModifier.onGloballyPositioned(
37     onGloballyPositioned: (SubspaceLayoutCoordinates) -> Unit
38 ): SubspaceModifier = this then OnGloballyPositionedVolumeElement(onGloballyPositioned)
39 
40 private class OnGloballyPositionedVolumeElement(
41     public val onGloballyPositioned: (SubspaceLayoutCoordinates) -> Unit
42 ) : SubspaceModifierNodeElement<OnGloballyPositionedNode>() {
43     override fun create(): OnGloballyPositionedNode {
44         return OnGloballyPositionedNode(onGloballyPositioned)
45     }
46 
47     override fun update(node: OnGloballyPositionedNode) {
48         node.callback = onGloballyPositioned
49     }
50 
51     override fun equals(other: Any?): Boolean {
52         if (this === other) return true
53         if (other !is OnGloballyPositionedVolumeElement) return false
54         return onGloballyPositioned === other.onGloballyPositioned
55     }
56 
57     override fun hashCode(): Int {
58         return onGloballyPositioned.hashCode()
59     }
60 }
61 
62 /** Node associated with [onGloballyPositioned]. */
63 private class OnGloballyPositionedNode(public var callback: (SubspaceLayoutCoordinates) -> Unit) :
64     SubspaceModifier.Node(), LayoutCoordinatesAwareModifierNode {
onLayoutCoordinatesnull65     override fun onLayoutCoordinates(coordinates: SubspaceLayoutCoordinates) {
66         callback(coordinates)
67     }
68 }
69