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

Commit 9006c1a9 authored by Jordan Demeulenaere's avatar Jordan Demeulenaere
Browse files

Merge Content modifiers into Content{Element,Node}

This CL refactors the modifier chain used in Content into a ContentNode,
using the Modifier Node API. This is a pure refactoring that will allow
ag/32131525 to do something in onDetach() without having to add a new
DisposableEffect.

Bug: 400688335
Test: Existing tests
Flag: com.android.systemui.scene_container
Change-Id: Iccac180e3f3f8daa887cdba45bb3227510819698
parent c282e6cf
Loading
Loading
Loading
Loading
+82 −26
Original line number Diff line number Diff line
@@ -32,12 +32,17 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
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.approachLayout
import androidx.compose.ui.layout.layout
import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.layout.MeasureResult
import androidx.compose.ui.layout.MeasureScope
import androidx.compose.ui.node.DelegatingNode
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.zIndex
import com.android.compose.animation.scene.Ancestor
import com.android.compose.animation.scene.AnimatedState
import com.android.compose.animation.scene.ContentKey
@@ -68,8 +73,8 @@ import com.android.compose.gesture.NestedScrollControlState
import com.android.compose.gesture.NestedScrollableBound
import com.android.compose.gesture.nestedScrollController
import com.android.compose.modifiers.thenIf
import com.android.compose.ui.graphics.ContainerNode
import com.android.compose.ui.graphics.ContainerState
import com.android.compose.ui.graphics.container
import kotlin.math.pow

/** A content defined in a [SceneTransitionLayout], i.e. a scene or an overlay. */
@@ -158,24 +163,14 @@ internal sealed class Content(
    fun Content(modifier: Modifier = Modifier, isInvisible: Boolean = false) {
        // If this content has a custom factory, provide it to the content so that the factory is
        // automatically used when calling rememberOverscrollEffect().
        val isElevationPossible =
            layoutImpl.state.isElevationPossible(content = key, element = null)
        Box(
            modifier
                .thenIf(isInvisible) { InvisibleModifier }
                .zIndex(zIndex)
                .approachLayout(
                    isMeasurementApproachInProgress = { layoutImpl.state.isTransitioning() }
                ) { measurable, constraints ->
                    // TODO(b/353679003): Use the ModifierNode API to set this *before* the
                    // approach
                    // pass is started.
                    targetSize = lookaheadSize
                    val placeable = measurable.measure(constraints)
                    layout(placeable.width, placeable.height) { placeable.place(0, 0) }
                }
                .thenIf(layoutImpl.state.isElevationPossible(content = key, element = null)) {
                    Modifier.container(containerState)
                }
                .thenIf(layoutImpl.implicitTestTags) { Modifier.testTag(key.testTag) }
            modifier.then(ContentElement(this, isElevationPossible, isInvisible)).thenIf(
                layoutImpl.implicitTestTags
            ) {
                Modifier.testTag(key.testTag)
            }
        ) {
            CompositionLocalProvider(LocalOverscrollFactory provides lastFactory) {
                scope.content()
@@ -194,6 +189,72 @@ internal sealed class Content(
    }
}

private data class ContentElement(
    private val content: Content,
    private val isElevationPossible: Boolean,
    private val isInvisible: Boolean,
) : ModifierNodeElement<ContentNode>() {
    override fun create(): ContentNode = ContentNode(content, isElevationPossible, isInvisible)

    override fun update(node: ContentNode) {
        node.update(content, isElevationPossible, isInvisible)
    }
}

private class ContentNode(
    private var content: Content,
    private var isElevationPossible: Boolean,
    private var isInvisible: Boolean,
) : DelegatingNode(), ApproachLayoutModifierNode {
    private var containerDelegate = containerDelegate(isElevationPossible)

    private fun containerDelegate(isElevationPossible: Boolean): ContainerNode? {
        return if (isElevationPossible) delegate(ContainerNode(content.containerState)) else null
    }

    fun update(content: Content, isElevationPossible: Boolean, isInvisible: Boolean) {
        if (content != this.content || isElevationPossible != this.isElevationPossible) {
            this.content = content
            this.isElevationPossible = isElevationPossible

            containerDelegate?.let { undelegate(it) }
            containerDelegate = containerDelegate(isElevationPossible)
        }

        this.isInvisible = isInvisible
    }

    override fun isMeasurementApproachInProgress(lookaheadSize: IntSize): Boolean = false

    override fun MeasureScope.measure(
        measurable: Measurable,
        constraints: Constraints,
    ): MeasureResult {
        check(isLookingAhead)
        return measurable.measure(constraints).run {
            content.targetSize = IntSize(width, height)
            layout(width, height) {
                if (!isInvisible) {
                    place(0, 0, zIndex = content.zIndex)
                }
            }
        }
    }

    override fun ApproachMeasureScope.approachMeasure(
        measurable: Measurable,
        constraints: Constraints,
    ): MeasureResult {
        return measurable.measure(constraints).run {
            layout(width, height) {
                if (!isInvisible) {
                    place(0, 0, zIndex = content.zIndex)
                }
            }
        }
    }
}

internal class ContentEffects(factory: OverscrollFactory) {
    val overscrollEffect = factory.createOverscrollEffect()
    val gestureEffect = GestureEffect(overscrollEffect)
@@ -307,8 +368,3 @@ internal class ContentScopeImpl(
        )
    }
}

private val InvisibleModifier =
    Modifier.layout { measurable, constraints ->
        measurable.measure(constraints).run { layout(width, height) {} }
    }