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

Commit 3df36b77 authored by Andreas Miko's avatar Andreas Miko
Browse files

Add new PropertyTransformation DrawScale

Test: added DrawScaleTest
Bug: b/290184746
Flag: NONE
Change-Id: I5f7107eb5b976ee9541a6ff7088cc2fa6c6a5b76
parent 23daba73
Loading
Loading
Loading
Loading
+50 −1
Original line number Diff line number Diff line
@@ -33,7 +33,9 @@ import androidx.compose.ui.composed
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.isSpecified
import androidx.compose.ui.geometry.isUnspecified
import androidx.compose.ui.geometry.lerp
import androidx.compose.ui.graphics.drawscope.scale
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.IntermediateMeasureScope
import androidx.compose.ui.layout.Measurable
@@ -85,6 +87,9 @@ internal class Element(val key: ElementKey) {
        /** The size of this element. */
        var size = SizeUnspecified

        /** The draw scale of this element. */
        var drawScale = Scale.Default

        /** The alpha of this element. */
        var alpha = AlphaUnspecified
    }
@@ -110,6 +115,13 @@ internal class Element(val key: ElementKey) {
    }
}

data class Scale(val scaleX: Float, val scaleY: Float, val pivot: Offset = Offset.Unspecified) {

    companion object {
        val Default = Scale(1f, 1f, Offset.Unspecified)
    }
}

/** The implementation of [SceneScope.element]. */
@OptIn(ExperimentalComposeUiApi::class)
internal fun Modifier.element(
@@ -160,9 +172,24 @@ internal fun Modifier.element(
        }
    }

    val drawScale by
        remember(layoutImpl, element, scene, sceneValues) {
            derivedStateOf { getDrawScale(layoutImpl, element, scene, sceneValues) }
        }

    drawWithContent {
            if (shouldDrawElement(layoutImpl, scene, element)) {
                drawContent()
                if (drawScale == Scale.Default) {
                    this@drawWithContent.drawContent()
                } else {
                    scale(
                        drawScale.scaleX,
                        drawScale.scaleY,
                        if (drawScale.pivot.isUnspecified) center else drawScale.pivot
                    ) {
                        this@drawWithContent.drawContent()
                    }
                }
            }
        }
        .modifierTransformations(layoutImpl, scene, element, sceneValues)
@@ -377,6 +404,28 @@ private fun IntermediateMeasureScope.measure(
    return placeable
}

private fun getDrawScale(
    layoutImpl: SceneTransitionLayoutImpl,
    element: Element,
    scene: Scene,
    sceneValues: Element.TargetValues
): Scale {
    return computeValue(
        layoutImpl,
        scene,
        element,
        sceneValue = { Scale.Default },
        transformation = { it.drawScale },
        idleValue = Scale.Default,
        currentValue = { Scale.Default },
        lastValue = {
            sceneValues.lastValues.drawScale.takeIf { it != Scale.Default }
                ?: element.lastSharedValues.drawScale
        },
        ::lerp,
    )
}

@OptIn(ExperimentalComposeUiApi::class)
private fun IntermediateMeasureScope.place(
    layoutImpl: SceneTransitionLayoutImpl,
+8 −1
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastMap
import com.android.compose.animation.scene.transformation.AnchoredSize
import com.android.compose.animation.scene.transformation.AnchoredTranslate
import com.android.compose.animation.scene.transformation.DrawScale
import com.android.compose.animation.scene.transformation.EdgeTranslate
import com.android.compose.animation.scene.transformation.Fade
import com.android.compose.animation.scene.transformation.ModifierTransformation
@@ -126,6 +127,7 @@ data class TransitionSpec(
        val modifier = mutableListOf<ModifierTransformation>()
        var offset: PropertyTransformation<Offset>? = null
        var size: PropertyTransformation<IntSize>? = null
        var drawScale: PropertyTransformation<Scale>? = null
        var alpha: PropertyTransformation<Float>? = null

        fun <T> onPropertyTransformation(
@@ -144,6 +146,10 @@ data class TransitionSpec(
                    throwIfNotNull(size, element, name = "size")
                    size = root as PropertyTransformation<IntSize>
                }
                is DrawScale -> {
                    throwIfNotNull(drawScale, element, name = "drawScale")
                    drawScale = root as PropertyTransformation<Scale>
                }
                is Fade -> {
                    throwIfNotNull(alpha, element, name = "alpha")
                    alpha = root as PropertyTransformation<Float>
@@ -167,7 +173,7 @@ data class TransitionSpec(
            }
        }

        return ElementTransformations(shared, modifier, offset, size, alpha)
        return ElementTransformations(shared, modifier, offset, size, drawScale, alpha)
    }

    private fun throwIfNotNull(
@@ -187,5 +193,6 @@ internal class ElementTransformations(
    val modifier: List<ModifierTransformation>,
    val offset: PropertyTransformation<Offset>?,
    val size: PropertyTransformation<IntSize>?,
    val drawScale: PropertyTransformation<Scale>?,
    val alpha: PropertyTransformation<Float>?,
)
+13 −2
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.compose.animation.scene

import androidx.compose.animation.core.AnimationSpec
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Dp
@@ -224,11 +225,21 @@ interface PropertyTransformationBuilder {
    /**
     * Scale the [width] and [height] of the element(s) matching [matcher]. Note that this scaling
     * is done during layout, so it will potentially impact the size and position of other elements.
     *
     * TODO(b/290184746): Also provide a scaleDrawing() to scale an element at drawing time.
     */
    fun scaleSize(matcher: ElementMatcher, width: Float = 1f, height: Float = 1f)

    /**
     * Scale the drawing with [scaleX] and [scaleY] of the element(s) matching [matcher]. Note this
     * will only scale the draw inside of an element, therefore it won't impact layout of elements
     * around it.
     */
    fun scaleDraw(
        matcher: ElementMatcher,
        scaleX: Float = 1f,
        scaleY: Float = 1f,
        pivot: Offset = Offset.Unspecified
    )

    /**
     * Scale the element(s) matching [matcher] so that it grows/shrinks to the same size as [anchor]
     * .
+6 −0
Original line number Diff line number Diff line
@@ -21,10 +21,12 @@ import androidx.compose.animation.core.DurationBasedAnimationSpec
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.VectorConverter
import androidx.compose.animation.core.spring
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Dp
import com.android.compose.animation.scene.transformation.AnchoredSize
import com.android.compose.animation.scene.transformation.AnchoredTranslate
import com.android.compose.animation.scene.transformation.DrawScale
import com.android.compose.animation.scene.transformation.EdgeTranslate
import com.android.compose.animation.scene.transformation.Fade
import com.android.compose.animation.scene.transformation.PropertyTransformation
@@ -178,6 +180,10 @@ internal class TransitionBuilderImpl : TransitionBuilder {
        transformation(ScaleSize(matcher, width, height))
    }

    override fun scaleDraw(matcher: ElementMatcher, scaleX: Float, scaleY: Float, pivot: Offset) {
        transformation(DrawScale(matcher, scaleX, scaleY, pivot))
    }

    override fun anchoredSize(matcher: ElementMatcher, anchor: ElementKey) {
        transformation(AnchoredSize(matcher, anchor))
    }
+48 −0
Original line number Diff line number Diff line
/*
 * Copyright 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.transformation

import androidx.compose.ui.geometry.Offset
import com.android.compose.animation.scene.Element
import com.android.compose.animation.scene.ElementMatcher
import com.android.compose.animation.scene.Scale
import com.android.compose.animation.scene.Scene
import com.android.compose.animation.scene.SceneTransitionLayoutImpl
import com.android.compose.animation.scene.TransitionState

/**
 * Scales the draw size of an element. Note this will only scale the draw inside of an element,
 * therefore it won't impact layout of elements around it.
 */
internal class DrawScale(
    override val matcher: ElementMatcher,
    private val scaleX: Float,
    private val scaleY: Float,
    private val pivot: Offset = Offset.Unspecified,
) : PropertyTransformation<Scale> {

    override fun transform(
        layoutImpl: SceneTransitionLayoutImpl,
        scene: Scene,
        element: Element,
        sceneValues: Element.TargetValues,
        transition: TransitionState.Transition,
        value: Scale,
    ): Scale {
        return Scale(scaleX, scaleY, pivot)
    }
}
Loading