LookaheadLayoutCoordinates.kt
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:OptIn(ExperimentalComposeUiApi::class)
package androidx.compose.ui.layout
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Matrix
import androidx.compose.ui.node.LookaheadDelegate
import androidx.compose.ui.node.NodeCoordinator
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.round
import androidx.compose.ui.unit.toOffset
/**
* [LookaheadLayoutCoordinates] interface holds layout coordinates from both the lookahead
* calculation and the post-lookahead layout pass.
*/
@Deprecated(
"LookaheadLayoutCoordinates class has been removed. localLookaheadPositionOf" +
"can be achieved in LookaheadScope using" +
" LayoutCoordinates.localLookaheadPositionOf(LayoutCoordinates) function.",
replaceWith = ReplaceWith("LayoutCoordinates")
)
@ExperimentalComposeUiApi
sealed interface LookaheadLayoutCoordinates : LayoutCoordinates
@Suppress("DEPRECATION")
internal class LookaheadLayoutCoordinatesImpl(val lookaheadDelegate: LookaheadDelegate) :
LookaheadLayoutCoordinates {
val coordinator: NodeCoordinator
get() = lookaheadDelegate.coordinator
override val size: IntSize
get() = lookaheadDelegate.let { IntSize(it.width, it.height) }
override val providedAlignmentLines: Set<AlignmentLine>
get() = coordinator.providedAlignmentLines
override val parentLayoutCoordinates: LayoutCoordinates?
get() {
check(isAttached) { NodeCoordinator.ExpectAttachedLayoutCoordinates }
return coordinator.layoutNode.outerCoordinator.wrappedBy?.let {
it.lookaheadDelegate?.coordinates
}
}
override val parentCoordinates: LayoutCoordinates?
get() {
check(isAttached) { NodeCoordinator.ExpectAttachedLayoutCoordinates }
return coordinator.wrappedBy?.lookaheadDelegate?.coordinates
}
override val isAttached: Boolean
get() = coordinator.isAttached
private val lookaheadOffset: Offset
get() = lookaheadDelegate.rootLookaheadDelegate.let {
localPositionOf(it.coordinates, Offset.Zero) -
coordinator.localPositionOf(it.coordinator, Offset.Zero)
}
override fun windowToLocal(relativeToWindow: Offset): Offset =
coordinator.windowToLocal(relativeToWindow) + lookaheadOffset
override fun localToWindow(relativeToLocal: Offset): Offset =
coordinator.localToWindow(relativeToLocal + lookaheadOffset)
override fun localToRoot(relativeToLocal: Offset): Offset =
coordinator.localToRoot(relativeToLocal + lookaheadOffset)
override fun localPositionOf(
sourceCoordinates: LayoutCoordinates,
relativeToSource: Offset
): Offset {
if (sourceCoordinates is LookaheadLayoutCoordinatesImpl) {
val source = sourceCoordinates.lookaheadDelegate
val commonAncestor = coordinator.findCommonAncestor(source.coordinator)
return commonAncestor.lookaheadDelegate?.let { ancestor ->
// Common ancestor is in lookahead
(source.positionIn(ancestor) + relativeToSource.round() -
lookaheadDelegate.positionIn(ancestor)).toOffset()
} ?: commonAncestor.let {
// The two coordinates are in two separate LookaheadLayouts
val sourceRoot = source.rootLookaheadDelegate
val relativePosition = source.positionIn(sourceRoot) +
sourceRoot.position + relativeToSource.round() -
with(lookaheadDelegate) {
(positionIn(rootLookaheadDelegate) + rootLookaheadDelegate.position)
}
lookaheadDelegate.rootLookaheadDelegate.coordinator.wrappedBy!!.localPositionOf(
sourceRoot.coordinator.wrappedBy!!, relativePosition.toOffset()
)
}
} else {
return coordinator.localPositionOf(sourceCoordinates, relativeToSource)
}
}
override fun localBoundingBoxOf(
sourceCoordinates: LayoutCoordinates,
clipBounds: Boolean
): Rect = coordinator.localBoundingBoxOf(sourceCoordinates, clipBounds)
override fun transformFrom(sourceCoordinates: LayoutCoordinates, matrix: Matrix) {
coordinator.transformFrom(sourceCoordinates, matrix)
}
override fun get(alignmentLine: AlignmentLine): Int = lookaheadDelegate.get(alignmentLine)
}
private val LookaheadDelegate.rootLookaheadDelegate: LookaheadDelegate
get() {
var root = layoutNode.lookaheadRoot!!
while (root.parent?.lookaheadRoot != null) {
root = root.parent!!.lookaheadRoot!!
}
return root.outerCoordinator.lookaheadDelegate!!
}