1
- @file:Suppress(" UNCHECKED_CAST" )
1
+ @file:Suppress(" UNCHECKED_CAST" , " INVISIBLE_MEMBER " , " INVISIBLE_REFERENCE " , " DEPRECATED " )
2
2
3
3
package io.github.jpy.wangposefluent.gallery.window
4
4
5
5
import androidx.compose.runtime.Composable
6
- import androidx.compose.runtime.ProvidableCompositionLocal
7
- import androidx.compose.runtime.Stable
8
6
import androidx.compose.runtime.remember
9
7
import androidx.compose.ui.InternalComposeUiApi
10
- import androidx.compose.ui.Modifier
8
+ import androidx.compose.ui.geometry.Offset
9
+ import androidx.compose.ui.node.HitTestResult
10
+ import androidx.compose.ui.node.LayoutNode
11
11
import androidx.compose.ui.node.PointerInputModifierNode
12
+ import androidx.compose.ui.node.RootNodeOwner
12
13
import androidx.compose.ui.scene.ComposeScene
14
+ import androidx.compose.ui.scene.CopiedList
15
+ import androidx.compose.ui.scene.LocalComposeScene
13
16
import androidx.compose.ui.util.fastForEachReversed
14
17
import androidx.compose.ui.util.packFloats
15
18
16
19
@OptIn(InternalComposeUiApi ::class )
17
20
@Composable
18
21
fun rememberLayoutHitTestOwner (): LayoutHitTestOwner {
19
- val scene = getLocalComposeScene()?.current ? : error(" no compose scene" )
22
+ // TODO Remove LocalComposeScene
23
+ val scene = LocalComposeScene .current ? : error(" no compose scene" )
20
24
return remember(scene) {
21
25
when (scene::class .qualifiedName) {
22
26
" androidx.compose.ui.scene.CanvasLayersComposeSceneImpl" -> {
@@ -30,16 +34,6 @@ fun rememberLayoutHitTestOwner(): LayoutHitTestOwner {
30
34
}
31
35
}
32
36
33
- @OptIn(InternalComposeUiApi ::class )
34
- @Stable
35
- private fun getLocalComposeScene (): ProvidableCompositionLocal <ComposeScene >? {
36
- val classLoader = ComposeScene ::class .java.classLoader
37
- val composeSceneClass = classLoader.loadClass(" androidx.compose.ui.scene.ComposeScene_skikoKt" )
38
- val methodRef = composeSceneClass.getMethod(" getLocalComposeScene" )
39
- methodRef.trySetAccessible()
40
- return methodRef.invoke(null ) as ? ProvidableCompositionLocal <ComposeScene >
41
- }
42
-
43
37
interface LayoutHitTestOwner {
44
38
45
39
fun hitTest (x : Float , y : Float ): Boolean {
@@ -48,73 +42,28 @@ interface LayoutHitTestOwner {
48
42
}
49
43
50
44
/*
51
- * reflect implementation for compose 1.6
45
+ * reflect implementation for compose 1.8
52
46
*/
53
47
internal abstract class ReflectLayoutHitTestOwner : LayoutHitTestOwner {
54
48
55
49
@OptIn(InternalComposeUiApi ::class )
56
50
protected val classLoader = ComposeScene ::class .java.classLoader!!
57
51
58
- private val rootNodeOwnerOwnerField = classLoader.loadClass(" androidx.compose.ui.node.RootNodeOwner" )
59
- .getDeclaredField(" owner" ).apply {
60
- trySetAccessible()
61
- }
62
-
63
- private val ownerRootField = classLoader.loadClass(" androidx.compose.ui.node.Owner" )
64
- .getDeclaredMethod(" getRoot" ).apply {
65
- trySetAccessible()
66
- }
67
-
68
- private val hitTestResultClass = classLoader.loadClass(" androidx.compose.ui.node.HitTestResult" )
69
-
70
- private val layoutNodeHitTestMethod = classLoader.loadClass(" androidx.compose.ui.node.LayoutNode" )
71
- .declaredMethods.first { it.name.startsWith(" hitTest-" ) && it.parameterCount == 4 }
72
-
73
- protected fun getLayoutNode (rootNodeOwner : Any ): Any {
74
- val owner = rootNodeOwnerOwnerField.get(rootNodeOwner)
75
- return ownerRootField.invoke(owner)
52
+ protected fun getLayoutNode (rootNodeOwner : RootNodeOwner ): LayoutNode {
53
+ return rootNodeOwner.owner.root
76
54
}
77
55
78
- protected fun Any .layoutNodeHitTest (x : Float , y : Float ): Boolean {
56
+ protected fun LayoutNode .layoutNodeHitTest (x : Float , y : Float ): Boolean {
79
57
try {
80
- val result = hitTestResultClass.getDeclaredConstructor().newInstance()
81
- // Try with the original parameter order
82
- try {
83
- layoutNodeHitTestMethod.invoke(this , packFloats(x, y), result, false , true )
84
- } catch (e: IllegalArgumentException ) {
85
- // If that fails, try with a different parameter order
86
- try {
87
- layoutNodeHitTestMethod.invoke(this , packFloats(x, y), result, true , false )
88
- } catch (e2: IllegalArgumentException ) {
89
- // If both fail, try without the boolean parameters
90
- layoutNodeHitTestMethod.invoke(this , packFloats(x, y), result)
91
- }
92
- }
93
- val resultAsList = result as ? List <* > ? : return false
94
- val lastNode = resultAsList.lastOrNull()
58
+ val result = HitTestResult ()
59
+ this .hitTest(pointerPosition = Offset (x, y), hitTestResult = result)
60
+ val lastNode = result.lastOrNull()
95
61
return lastNode is PointerInputModifierNode
96
62
} catch (e: Exception ) {
97
63
// If anything goes wrong, return false to be safe
98
64
return false
99
65
}
100
66
}
101
-
102
- protected class CopiedList <T >(
103
- private val populate : (MutableList <T >) -> Unit
104
- ) : MutableList<T> by mutableListOf() {
105
- inline fun withCopy (
106
- block : (List <T >) -> Unit
107
- ) {
108
- // In case of recursive calls, allocate new list
109
- val copy = if (isEmpty()) this else mutableListOf ()
110
- populate(copy)
111
- try {
112
- block(copy)
113
- } finally {
114
- copy.clear()
115
- }
116
- }
117
- }
118
67
}
119
68
120
69
@OptIn(InternalComposeUiApi ::class )
@@ -123,11 +72,11 @@ internal class PlatformLayersLayoutHitTestOwner(scene: ComposeScene) : ReflectLa
123
72
124
73
private val mainOwnerRef = sceneClass.getDeclaredMethod(" getMainOwner" ).let {
125
74
it.trySetAccessible()
126
- it.invoke(scene)
75
+ it.invoke(scene) as RootNodeOwner
127
76
}
128
77
129
78
override fun hitTest (x : Float , y : Float ): Boolean {
130
- return getLayoutNode( mainOwnerRef) .layoutNodeHitTest(x, y)
79
+ return mainOwnerRef.owner.root .layoutNodeHitTest(x, y)
131
80
}
132
81
}
133
82
@@ -138,13 +87,13 @@ internal class CanvasLayersLayoutHitTestOwner(private val scene: ComposeScene) :
138
87
139
88
private val mainOwnerRef = sceneClass.getDeclaredField(" mainOwner" ).let {
140
89
it.trySetAccessible()
141
- it.get(scene)
90
+ it.get(scene) as RootNodeOwner
142
91
}
143
92
144
- private val layersRef = sceneClass.getDeclaredField(" layers " ).let {
93
+ private val _layers = sceneClass.getDeclaredField(" _layersCopyCache " ).let {
145
94
it.trySetAccessible()
146
95
it.get(scene)
147
- } as MutableList < Any >
96
+ } as CopiedList < * >
148
97
149
98
private val focusedLayerField = sceneClass.getDeclaredField(" focusedLayer" ).apply {
150
99
trySetAccessible()
@@ -160,17 +109,11 @@ internal class CanvasLayersLayoutHitTestOwner(private val scene: ComposeScene) :
160
109
trySetAccessible()
161
110
}
162
111
163
- private val _layers = CopiedList {
164
- for (layer in layersRef) {
165
- it.add(layer)
166
- }
167
- }
168
-
169
112
override fun hitTest (x : Float , y : Float ): Boolean {
170
113
_layers .withCopy {
171
114
it.fastForEachReversed { layer ->
172
115
if (layerIsInBoundMethod.invoke(layer, packFloats(x, y)) == true ) {
173
- return getLayoutNode(layerOwnerField.get(layer)).layoutNodeHitTest(x, y)
116
+ return getLayoutNode(layerOwnerField.get(layer) as RootNodeOwner ).layoutNodeHitTest(x, y)
174
117
} else if (layer == focusedLayerField.get(scene)) {
175
118
return false
176
119
}
0 commit comments