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

Commit e19ed79d authored by Jordan Demeulenaere's avatar Jordan Demeulenaere Committed by Android (Google) Code Review
Browse files

Merge changes from topic "qs-pager-noresize" into main

* changes:
  Update element last offset in all scenes
  Share the Picture object of MovableElement
  Add Modifier.noResizeDuringTransitions()
parents 4dd3f905 afaf3b32
Loading
Loading
Loading
Loading
+23 −11
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.compose.animation.scene

import android.graphics.Picture
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
@@ -66,12 +67,21 @@ internal class Element(val key: ElementKey) {
     * The movable content of this element, if this element is composed using
     * [SceneScope.MovableElement].
     */
    val movableContent by
        // This is only accessed from the composition (main) thread, so no need to use the default
        // lock of lazy {} to synchronize.
        lazy(mode = LazyThreadSafetyMode.NONE) {
            movableContentOf { content: @Composable () -> Unit -> content() }
        }
    private var _movableContent: (@Composable (@Composable () -> Unit) -> Unit)? = null
    val movableContent: @Composable (@Composable () -> Unit) -> Unit
        get() =
            _movableContent
                ?: movableContentOf { content: @Composable () -> Unit -> content() }
                    .also { _movableContent = it }

    /**
     * The [Picture] to which we save the last drawing commands of this element, if it is movable.
     * This is necessary because the content of this element might not be composed in the scene it
     * should currently be drawn.
     */
    private var _picture: Picture? = null
    val picture: Picture
        get() = _picture ?: Picture().also { _picture = it }

    override fun toString(): String {
        return "Element(key=$key)"
@@ -521,11 +531,6 @@ private fun IntermediateMeasureScope.place(
            sceneValues.targetOffset = targetOffsetInScene
        }

        // No need to place the element in this scene if we don't want to draw it anyways.
        if (!shouldDrawElement(layoutImpl, scene, element)) {
            return
        }

        val currentOffset = lookaheadScopeCoordinates.localPositionOf(coords, Offset.Zero)
        val lastSharedValues = element.lastSharedValues
        val lastValues = sceneValues.lastValues
@@ -548,6 +553,13 @@ private fun IntermediateMeasureScope.place(
        lastSharedValues.offset = targetOffset
        lastValues.offset = targetOffset

        // No need to place the element in this scene if we don't want to draw it anyways. Note that
        // it's still important to compute the target offset and update lastValues, otherwise it
        // will be out of date.
        if (!shouldDrawElement(layoutImpl, scene, element)) {
            return
        }

        val offset = (targetOffset - currentOffset).round()
        if (isElementOpaque(layoutImpl, element, scene, sceneValues)) {
            // TODO(b/291071158): Call placeWithLayer() if offset != IntOffset.Zero and size is not
+1 −2
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package com.android.compose.animation.scene

import android.graphics.Picture
import android.util.Log
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
@@ -60,7 +59,7 @@ internal fun MovableElement(
        // The [Picture] to which we save the last drawing commands of this element. This is
        // necessary because the content of this element might not be composed in this scene, in
        // which case we still need to draw it.
        val picture = remember { Picture() }
        val picture = element.picture

        // Whether we should compose the movable element here. The scene picker logic to know in
        // which scene we should compose/draw a movable element might depend on the current
+5 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import androidx.compose.ui.layout.intermediateLayout
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.zIndex
import com.android.compose.animation.scene.modifiers.noResizeDuringTransitions

/** A scene in a [SceneTransitionLayout]. */
@Stable
@@ -152,4 +153,8 @@ private class SceneScopeImpl(
        bounds: ElementKey,
        shape: Shape
    ): Modifier = punchHole(layoutImpl, element, bounds, shape)

    override fun Modifier.noResizeDuringTransitions(): Modifier {
        return noResizeDuringTransitions(layoutState = layoutImpl.state)
    }
}
+13 −7
Original line number Diff line number Diff line
@@ -65,11 +65,11 @@ fun SceneTransitionLayout(
    SceneTransitionLayoutForTesting(
        currentScene,
        onChangeScene,
        modifier,
        transitions,
        state,
        edgeDetector,
        transitionInterceptionThreshold,
        modifier,
        onLayoutImpl = null,
        scenes,
    )
@@ -205,6 +205,12 @@ interface SceneScope {
     * the result.
     */
    fun Modifier.punchHole(element: ElementKey, bounds: ElementKey, shape: Shape): Modifier

    /**
     * Don't resize during transitions. This can for instance be used to make sure that scrollable
     * lists keep a constant size during transitions even if its elements are growing/shrinking.
     */
    fun Modifier.noResizeDuringTransitions(): Modifier
}

// TODO(b/291053742): Add animateSharedValueAsState(targetValue) without any ValueKey and ElementKey
@@ -257,12 +263,12 @@ enum class SwipeDirection(val orientation: Orientation) {
internal fun SceneTransitionLayoutForTesting(
    currentScene: SceneKey,
    onChangeScene: (SceneKey) -> Unit,
    transitions: SceneTransitions,
    state: SceneTransitionLayoutState,
    edgeDetector: EdgeDetector,
    transitionInterceptionThreshold: Float,
    modifier: Modifier,
    onLayoutImpl: ((SceneTransitionLayoutImpl) -> Unit)?,
    modifier: Modifier = Modifier,
    transitions: SceneTransitions = transitions {},
    state: SceneTransitionLayoutState = remember { SceneTransitionLayoutState(currentScene) },
    edgeDetector: EdgeDetector = DefaultEdgeDetector,
    transitionInterceptionThreshold: Float = 0f,
    onLayoutImpl: ((SceneTransitionLayoutImpl) -> Unit)? = null,
    scenes: SceneTransitionLayoutScope.() -> Unit,
) {
    val density = LocalDensity.current
+40 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.
 */

package com.android.compose.animation.scene.modifiers

import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.intermediateLayout
import androidx.compose.ui.unit.Constraints
import com.android.compose.animation.scene.SceneTransitionLayoutState

@OptIn(ExperimentalComposeUiApi::class)
internal fun Modifier.noResizeDuringTransitions(layoutState: SceneTransitionLayoutState): Modifier {
    return intermediateLayout { measurable, constraints ->
        if (layoutState.currentTransition == null) {
            return@intermediateLayout measurable.measure(constraints).run {
                layout(width, height) { place(0, 0) }
            }
        }

        // Make sure that this layout node has the same size than when we are at rest.
        val sizeAtRest = lookaheadSize
        measurable.measure(Constraints.fixed(sizeAtRest.width, sizeAtRest.height)).run {
            layout(width, height) { place(0, 0) }
        }
    }
}
Loading