Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3f379a74 authored by Jordan Demeulenaere's avatar Jordan Demeulenaere
Browse files

Move the Element placement logic into ElementNode

This CL is a pure move of the place() logic into ElementNode. This will
be useful for ag/27685078, which will make the placeWithLayer {} block
access the list of current transitions (ElementNode.currentTransitions).

Bug: 290930950
Test: atest PlatformComposeSceneTransitionLayoutTests
Flag: com.android.systemui.scene_container
Change-Id: I5b882188b8f7073369d635795227400b8aeca2ee
parent 96f26cee
Loading
Loading
Loading
Loading
+75 −87
Original line number Diff line number Diff line
@@ -299,15 +299,81 @@ internal class ElementNode(
        val placeable =
            measure(layoutImpl, scene, element, transition, sceneState, measurable, constraints)
        sceneState.lastSize = placeable.size()
        return layout(placeable.width, placeable.height) {
            place(
        return layout(placeable.width, placeable.height) { place(transition, placeable) }
    }

    @OptIn(ExperimentalComposeUiApi::class)
    private fun Placeable.PlacementScope.place(
        transition: TransitionState.Transition?,
        placeable: Placeable,
    ) {
        with(layoutImpl.lookaheadScope) {
            // Update the offset (relative to the SceneTransitionLayout) this element has in this
            // scene when idle.
            val coords =
                coordinates ?: error("Element ${element.key} does not have any coordinates")
            val targetOffsetInScene = lookaheadScopeCoordinates.localLookaheadPositionOf(coords)

            // No need to place the element in this scene if we don't want to draw it anyways.
            if (!shouldPlaceElement(layoutImpl, scene, element, transition)) {
                sceneState.lastOffset = Offset.Unspecified
                sceneState.lastScale = Scale.Unspecified
                sceneState.lastAlpha = Element.AlphaUnspecified

                sceneState.clearValuesBeforeInterruption()
                sceneState.clearInterruptionDeltas()
                return
            }

            val currentOffset = lookaheadScopeCoordinates.localPositionOf(coords, Offset.Zero)
            val targetOffset =
                computeValue(
                    layoutImpl,
                    scene,
                    element,
                    transition,
                sceneState,
                placeable,
                    sceneValue = { it.targetOffset },
                    transformation = { it.offset },
                    idleValue = targetOffsetInScene,
                    currentValue = { currentOffset },
                    isSpecified = { it != Offset.Unspecified },
                    ::lerp,
                )

            val interruptedOffset =
                computeInterruptedValue(
                    layoutImpl,
                    transition,
                    value = targetOffset,
                    unspecifiedValue = Offset.Unspecified,
                    zeroValue = Offset.Zero,
                    getValueBeforeInterruption = { sceneState.offsetBeforeInterruption },
                    setValueBeforeInterruption = { sceneState.offsetBeforeInterruption = it },
                    getInterruptionDelta = { sceneState.offsetInterruptionDelta },
                    setInterruptionDelta = { sceneState.offsetInterruptionDelta = it },
                    diff = { a, b -> a - b },
                    add = { a, b, bProgress -> a + b * bProgress },
                )

            sceneState.lastOffset = interruptedOffset

            val offset = (interruptedOffset - currentOffset).round()
            if (
                isElementOpaque(scene, element, transition) &&
                    interruptedAlpha(layoutImpl, transition, sceneState, alpha = 1f) == 1f
            ) {
                sceneState.lastAlpha = 1f

                // TODO(b/291071158): Call placeWithLayer() if offset != IntOffset.Zero and size is
                // not animated once b/305195729 is fixed. Test that drawing is not invalidated in
                // that case.
                placeable.place(offset)
            } else {
                placeable.placeWithLayer(offset) {
                    alpha = elementAlpha(layoutImpl, scene, element, transition, sceneState)
                    compositingStrategy = CompositingStrategy.ModulateAlpha
                }
            }
        }
    }

@@ -807,84 +873,6 @@ private fun ContentDrawScope.getDrawScale(
    return interruptedScale
}

@OptIn(ExperimentalComposeUiApi::class)
private fun Placeable.PlacementScope.place(
    layoutImpl: SceneTransitionLayoutImpl,
    scene: Scene,
    element: Element,
    transition: TransitionState.Transition?,
    sceneState: Element.SceneState,
    placeable: Placeable,
) {
    with(layoutImpl.lookaheadScope) {
        // Update the offset (relative to the SceneTransitionLayout) this element has in this scene
        // when idle.
        val coords = coordinates ?: error("Element ${element.key} does not have any coordinates")
        val targetOffsetInScene = lookaheadScopeCoordinates.localLookaheadPositionOf(coords)

        // No need to place the element in this scene if we don't want to draw it anyways.
        if (!shouldPlaceElement(layoutImpl, scene, element, transition)) {
            sceneState.lastOffset = Offset.Unspecified
            sceneState.lastScale = Scale.Unspecified
            sceneState.lastAlpha = Element.AlphaUnspecified

            sceneState.clearValuesBeforeInterruption()
            sceneState.clearInterruptionDeltas()
            return
        }

        val currentOffset = lookaheadScopeCoordinates.localPositionOf(coords, Offset.Zero)
        val targetOffset =
            computeValue(
                layoutImpl,
                scene,
                element,
                transition,
                sceneValue = { it.targetOffset },
                transformation = { it.offset },
                idleValue = targetOffsetInScene,
                currentValue = { currentOffset },
                isSpecified = { it != Offset.Unspecified },
                ::lerp,
            )

        val interruptedOffset =
            computeInterruptedValue(
                layoutImpl,
                transition,
                value = targetOffset,
                unspecifiedValue = Offset.Unspecified,
                zeroValue = Offset.Zero,
                getValueBeforeInterruption = { sceneState.offsetBeforeInterruption },
                setValueBeforeInterruption = { sceneState.offsetBeforeInterruption = it },
                getInterruptionDelta = { sceneState.offsetInterruptionDelta },
                setInterruptionDelta = { sceneState.offsetInterruptionDelta = it },
                diff = { a, b -> a - b },
                add = { a, b, bProgress -> a + b * bProgress },
            )

        sceneState.lastOffset = interruptedOffset

        val offset = (interruptedOffset - currentOffset).round()
        if (
            isElementOpaque(scene, element, transition) &&
                interruptedAlpha(layoutImpl, transition, sceneState, alpha = 1f) == 1f
        ) {
            sceneState.lastAlpha = 1f

            // TODO(b/291071158): Call placeWithLayer() if offset != IntOffset.Zero and size is not
            // animated once b/305195729 is fixed. Test that drawing is not invalidated in that
            // case.
            placeable.place(offset)
        } else {
            placeable.placeWithLayer(offset) {
                alpha = elementAlpha(layoutImpl, scene, element, transition, sceneState)
                compositingStrategy = CompositingStrategy.ModulateAlpha
            }
        }
    }
}

/**
 * Return the value that should be used depending on the current layout state and transition.
 *