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

Commit 89918554 authored by Jordan Demeulenaere's avatar Jordan Demeulenaere
Browse files

Migrate STLImpl to ApproachLayoutModifierNode

Modifier.intermediateLayout {} is deprecated, so this CL migrates the
size animation logic of STLImpl to the new ApproachLayoutModifierNode
API.

Bug: 291071158
Test: Existing STL tests
Flag: N/A
Change-Id: I0c1745309e998583bd3e15fbcbfb5a9e91bfbab3
parent ab438ccc
Loading
Loading
Loading
Loading
+58 −33
Original line number Diff line number Diff line
@@ -25,8 +25,13 @@ import androidx.compose.runtime.key
import androidx.compose.runtime.snapshots.SnapshotStateMap
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ApproachLayoutModifierNode
import androidx.compose.ui.layout.ApproachMeasureScope
import androidx.compose.ui.layout.LookaheadScope
import androidx.compose.ui.layout.intermediateLayout
import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.layout.MeasureResult
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.util.fastForEach
@@ -172,7 +177,6 @@ internal class SceneTransitionLayoutImpl(
    }

    @Composable
    @OptIn(ExperimentalComposeUiApi::class)
    internal fun Content(modifier: Modifier) {
        Box(
            modifier
@@ -181,37 +185,7 @@ internal class SceneTransitionLayoutImpl(
                // swipes.
                .swipeToScene(horizontalGestureHandler)
                .swipeToScene(verticalGestureHandler)
                // Animate the size of this layout.
                .intermediateLayout { measurable, constraints ->
                    // Measure content normally.
                    val placeable = measurable.measure(constraints)

                    val width: Int
                    val height: Int
                    val transition = state.currentTransition
                    if (transition == null) {
                        width = placeable.width
                        height = placeable.height
                    } else {
                        // Interpolate the size.
                        val fromSize = scene(transition.fromScene).targetSize
                        val toSize = scene(transition.toScene).targetSize

                        // Optimization: make sure we don't read state.progress if fromSize ==
                        // toSize to avoid running this code every frame when the layout size does
                        // not change.
                        if (fromSize == toSize) {
                            width = fromSize.width
                            height = fromSize.height
                        } else {
                            val size = lerp(fromSize, toSize, transition.progress)
                            width = size.width.coerceAtLeast(0)
                            height = size.height.coerceAtLeast(0)
                        }
                    }

                    layout(width, height) { placeable.place(0, 0) }
                }
                .then(LayoutElement(layoutImpl = this))
        ) {
            LookaheadScope {
                val scenesToCompose =
@@ -254,3 +228,54 @@ internal class SceneTransitionLayoutImpl(
        scenes.values.forEach { it.targetSize = size }
    }
}

private data class LayoutElement(private val layoutImpl: SceneTransitionLayoutImpl) :
    ModifierNodeElement<LayoutNode>() {
    override fun create(): LayoutNode = LayoutNode(layoutImpl)

    override fun update(node: LayoutNode) {
        node.layoutImpl = layoutImpl
    }
}

private class LayoutNode(var layoutImpl: SceneTransitionLayoutImpl) :
    Modifier.Node(), ApproachLayoutModifierNode {
    override fun isMeasurementApproachComplete(lookaheadSize: IntSize): Boolean {
        return layoutImpl.state.currentTransition == null
    }

    @ExperimentalComposeUiApi
    override fun ApproachMeasureScope.approachMeasure(
        measurable: Measurable,
        constraints: Constraints,
    ): MeasureResult {
        // Measure content normally.
        val placeable = measurable.measure(constraints)

        val width: Int
        val height: Int
        val transition = layoutImpl.state.currentTransition
        if (transition == null) {
            width = placeable.width
            height = placeable.height
        } else {
            // Interpolate the size.
            val fromSize = layoutImpl.scene(transition.fromScene).targetSize
            val toSize = layoutImpl.scene(transition.toScene).targetSize

            // Optimization: make sure we don't read state.progress if fromSize ==
            // toSize to avoid running this code every frame when the layout size does
            // not change.
            if (fromSize == toSize) {
                width = fromSize.width
                height = fromSize.height
            } else {
                val size = lerp(fromSize, toSize, transition.progress)
                width = size.width.coerceAtLeast(0)
                height = size.height.coerceAtLeast(0)
            }
        }

        return layout(width, height) { placeable.place(0, 0) }
    }
}