@@ -41,6 +41,7 @@ import androidx.compose.ui.geometry.Offset
4141import androidx.compose.ui.geometry.Rect
4242import androidx.compose.ui.geometry.isUnspecified
4343import androidx.compose.ui.graphics.Canvas
44+ import androidx.compose.ui.graphics.Matrix
4445import androidx.compose.ui.graphics.SkiaGraphicsContext
4546import androidx.compose.ui.graphics.layer.GraphicsLayer
4647import androidx.compose.ui.input.InputMode
@@ -89,8 +90,10 @@ import androidx.compose.ui.text.font.createFontFamilyResolver
8990import androidx.compose.ui.text.input.TextInputService
9091import androidx.compose.ui.unit.Constraints
9192import androidx.compose.ui.unit.Density
93+ import androidx.compose.ui.unit.IntOffset
9294import androidx.compose.ui.unit.IntSize
9395import androidx.compose.ui.unit.LayoutDirection
96+ import androidx.compose.ui.unit.round
9497import androidx.compose.ui.unit.toIntRect
9598import androidx.compose.ui.unit.toRect
9699import androidx.compose.ui.util.fastAll
@@ -159,19 +162,23 @@ internal class RootNodeOwner(
159162 private val pointerInputEventProcessor = PointerInputEventProcessor (owner.root)
160163 private val measureAndLayoutDelegate = MeasureAndLayoutDelegate (owner.root)
161164 private var isDisposed = false
162- private var currentCornersOnScreen: List <Offset >? = cornersOnScreen()
165+
166+ private var windowPosition: Offset ? = null
167+ private var globalPosition: Offset ? = null
168+
169+ // TODO: Android assumes matrix for some APIs, so we need store something to avoid extra
170+ // allocations. Clean up this APIs and remove it.
171+ private val identityMatrix = Matrix ()
163172
164173 init {
165174 snapshotObserver.startObserving()
166175 owner.root.attach(owner)
167176 platformContext.rootForTestListener?.onRootForTestCreated(rootForTest)
168177 onRootConstrainsChanged(size?.toConstraints())
169- onLightingInfoChanged ()
178+ updatePositionCacheAndDispatch ()
170179 coroutineScope.launch {
171180 snapshotFlow { platformContext.windowInfo.containerSize }
172- .collect {
173- onLightingInfoChanged()
174- }
181+ .collect { updatePositionCacheAndDispatch() }
175182 }
176183 }
177184
@@ -216,43 +223,65 @@ internal class RootNodeOwner(
216223
217224 fun measureAndLayout () {
218225 owner.measureAndLayout(sendPointerUpdate = true )
226+ updatePositionCacheAndDispatch()
219227 }
220228
221- fun invalidatePositionInWindow () {
222- owner.root.layoutDelegate.measurePassDelegate.notifyChildrenUsingCoordinatesWhilePlacing()
223- measureAndLayoutDelegate.dispatchOnPositionedCallbacks(forceDispatch = true )
224- onLightingInfoChanged()
229+ private fun updatePositionCacheAndDispatch () {
230+ val globalPosition = platformContext.convertLocalToScreenPosition(Offset .Zero )
231+ val hasGlobalPositionChanged = if (platformContext.hasNonTranslationComponents) {
232+ this .globalPosition = null
233+ true // Always invalidate in case of rotation, skew, etc.
234+ } else if (globalPosition != this .globalPosition) {
235+ this .globalPosition = globalPosition
236+ true
237+ } else false
238+
239+ val windowPosition = platformContext.convertLocalToWindowPosition(Offset .Zero )
240+ val hasWindowPositionChanged = if (platformContext.hasNonTranslationComponents) {
241+ this .windowPosition = null
242+ true // Always invalidate in case of rotation, skew, etc.
243+ } else if (windowPosition != this .windowPosition) {
244+ this .windowPosition = windowPosition
245+ true
246+ } else false
247+
248+ if (hasGlobalPositionChanged || hasWindowPositionChanged) {
249+ owner.root.layoutDelegate.measurePassDelegate.notifyChildrenUsingCoordinatesWhilePlacing()
250+ }
251+ val containerSize = platformContext.windowInfo.containerSize
252+ owner.rectManager.updateOffsets(
253+ screenOffset = globalPosition.round(),
254+ windowOffset = windowPosition.round(),
255+ viewToWindowMatrix = identityMatrix, // TODO: Replace viewToWindowMatrix to delegates
256+ windowWidth = containerSize.width,
257+ windowHeight = containerSize.height,
258+ )
259+ measureAndLayoutDelegate.dispatchOnPositionedCallbacks(
260+ forceDispatch = hasGlobalPositionChanged || hasWindowPositionChanged
261+ )
262+ if (ComposeUiFlags .isRectTrackingEnabled) {
263+ owner.rectManager.dispatchCallbacks()
264+ }
265+ if (hasWindowPositionChanged) {
266+ graphicsContext.setLightingInfo(
267+ canvasOffset = windowPosition,
268+ density = density,
269+ containerSize = containerSize
270+ )
271+ }
225272 }
226273
227- private fun cornersOnScreen () = size?.let { size ->
228- val width = size.width.toFloat()
229- val height = size.height.toFloat()
230- val corners =
231- listOf (
232- Offset .Zero ,
233- Offset (x = width, y = 0f ),
234- Offset (x = 0f , y = height),
235- Offset (x = width, y = height)
236- ).fastMap {
237- platformContext.convertLocalToScreenPosition(it)
238- }
239- if (corners.fastAny { it.isUnspecified }) null else corners
274+ fun invalidatePositionInWindow () {
275+ updatePositionCacheAndDispatch()
240276 }
241277
242278 fun invalidatePositionOnScreen () {
243- // Look at all corners, because platformContext.convertLocalToScreenPosition can also
244- // rotate, skew etc.
245- val cornersOnScreen = cornersOnScreen()
246- if (cornersOnScreen != currentCornersOnScreen) {
247- measureAndLayoutDelegate.dispatchOnPositionedCallbacks(forceDispatch = true )
248- currentCornersOnScreen = cornersOnScreen
249- }
279+ updatePositionCacheAndDispatch()
250280 }
251281
252282 fun draw (canvas : Canvas ) = trace(" RootNodeOwner:draw" ) {
253283 ownedLayerManager.draw(canvas)
254284 clearInvalidObservations()
255- @OptIn(ExperimentalComposeUiApi ::class )
256285 if (ComposeUiFlags .isRectTrackingEnabled) {
257286 owner.rectManager.dispatchCallbacks()
258287 }
@@ -269,14 +298,6 @@ internal class RootNodeOwner(
269298 }
270299 }
271300
272- private fun onLightingInfoChanged () {
273- graphicsContext.setLightingInfo(
274- canvasOffset = platformContext.convertLocalToWindowPosition(Offset .Zero ),
275- density = density,
276- containerSize = platformContext.windowInfo.containerSize
277- )
278- }
279-
280301 fun onCancelPointerInput () {
281302 pointerInputEventProcessor.processCancel()
282303 }
@@ -522,7 +543,6 @@ internal class RootNodeOwner(
522543 snapshotInvalidationTracker.requestDraw()
523544 }
524545 measureAndLayoutDelegate.dispatchOnPositionedCallbacks()
525- @OptIn(ExperimentalComposeUiApi ::class )
526546 if (ComposeUiFlags .isRectTrackingEnabled) {
527547 rectManager.dispatchCallbacks()
528548 }
@@ -540,7 +560,6 @@ internal class RootNodeOwner(
540560 if (! measureAndLayoutDelegate.hasPendingMeasureOrLayout) {
541561 measureAndLayoutDelegate.dispatchOnPositionedCallbacks()
542562 }
543- @OptIn(ExperimentalComposeUiApi ::class )
544563 if (ComposeUiFlags .isRectTrackingEnabled) {
545564 rectManager.dispatchCallbacks()
546565 }
0 commit comments