summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Andreas Miko <amiko@google.com> 2023-11-03 14:00:12 +0100
committer Andreas Miko <amiko@google.com> 2023-11-08 09:52:08 +0100
commit3df36b776107cf703ccda9c1b65ea888c2a2146e (patch)
tree9ff31b1efdac935f78a27cb389015a7c83d4f35e
parent23daba73c529fcd7e52c64af7c0806f2f0a4e809 (diff)
Add new PropertyTransformation DrawScale
Test: added DrawScaleTest Bug: b/290184746 Flag: NONE Change-Id: I5f7107eb5b976ee9541a6ff7088cc2fa6c6a5b76
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt51
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt9
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt15
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt6
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt48
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt19
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/ui/util/MathHelpersTest.kt56
7 files changed, 200 insertions, 4 deletions
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index eb10afc1ee57..aae61bd0f554 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -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,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
index b163a2a96039..72a2d612d8e3 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
@@ -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>?,
)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
index 7b7ddfa5ec4e..ca66dff5e231 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
@@ -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,12 +225,22 @@ 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]
* .
*
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
index d2bfd91842ae..d4909892f492 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
@@ -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))
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
new file mode 100644
index 000000000000..d1cf8ee6ad4a
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
@@ -0,0 +1,48 @@
+/*
+ * 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)
+ }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt b/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt
index eb1a634ff491..13747b72724e 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt
@@ -17,7 +17,10 @@
package com.android.compose.ui.util
+import androidx.compose.ui.geometry.isSpecified
+import androidx.compose.ui.geometry.lerp
import androidx.compose.ui.unit.IntSize
+import com.android.compose.animation.scene.Scale
import kotlin.math.roundToInt
import kotlin.math.roundToLong
@@ -43,3 +46,19 @@ fun lerp(start: IntSize, stop: IntSize, fraction: Float): IntSize {
lerp(start.height, stop.height, fraction)
)
}
+
+/** Linearly interpolate between [start] and [stop] with [fraction] fraction between them. */
+fun lerp(start: Scale, stop: Scale, fraction: Float): Scale {
+ val pivot =
+ when {
+ start.pivot.isSpecified && stop.pivot.isSpecified ->
+ lerp(start.pivot, stop.pivot, fraction)
+ start.pivot.isSpecified -> start.pivot
+ else -> stop.pivot
+ }
+ return Scale(
+ lerp(start.scaleX, stop.scaleX, fraction),
+ lerp(start.scaleY, stop.scaleY, fraction),
+ pivot
+ )
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/ui/util/MathHelpersTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/ui/util/MathHelpersTest.kt
new file mode 100644
index 000000000000..3ec73c793049
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/ui/util/MathHelpersTest.kt
@@ -0,0 +1,56 @@
+package com.android.compose.ui.util
+
+import androidx.compose.ui.geometry.Offset
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.Scale
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class MathHelpersTest {
+
+ @Test
+ fun lerpScaleWithPivotUnspecified() {
+ val scale1 = Scale(1f, 1f)
+ val scale2 = Scale(5f, 3f)
+ val expectedScale = Scale(3f, 2f)
+
+ val actualScale = lerp(scale1, scale2, 0.5f)
+
+ assertThat(actualScale).isEqualTo(expectedScale)
+ }
+
+ @Test
+ fun lerpScaleWithFirstPivotSpecified() {
+ val scale1 = Scale(1f, 1f, Offset(1f, 1f))
+ val scale2 = Scale(5f, 3f)
+ val expectedScale = Scale(3f, 2f, Offset(1f, 1f))
+
+ val actualScale = lerp(scale1, scale2, 0.5f)
+
+ assertThat(actualScale).isEqualTo(expectedScale)
+ }
+
+ @Test
+ fun lerpScaleWithSecondPivotSpecified() {
+ val scale1 = Scale(1f, 1f)
+ val scale2 = Scale(5f, 3f, Offset(1f, 1f))
+ val expectedScale = Scale(3f, 2f, Offset(1f, 1f))
+
+ val actualScale = lerp(scale1, scale2, 0.5f)
+
+ assertThat(actualScale).isEqualTo(expectedScale)
+ }
+
+ @Test
+ fun lerpScaleWithBothPivotsSpecified() {
+ val scale1 = Scale(1f, 1f, Offset(1f, 1f))
+ val scale2 = Scale(5f, 3f, Offset(3f, 5f))
+ val expectedScale = Scale(3f, 2f, Offset(2f, 3f))
+
+ val actualScale = lerp(scale1, scale2, 0.5f)
+
+ assertThat(actualScale).isEqualTo(expectedScale)
+ }
+}