1 /*
2  * Copyright 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.compose.animation.scene.transformation
18 
19 import androidx.compose.ui.unit.IntSize
20 import com.android.compose.animation.scene.Element
21 import com.android.compose.animation.scene.ElementKey
22 import com.android.compose.animation.scene.ElementMatcher
23 import com.android.compose.animation.scene.SceneKey
24 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
25 import com.android.compose.animation.scene.TransitionState
26 
27 /** Anchor the size of an element to the size of another element. */
28 internal class AnchoredSize(
29     override val matcher: ElementMatcher,
30     private val anchor: ElementKey,
31     private val anchorWidth: Boolean,
32     private val anchorHeight: Boolean,
33 ) : PropertyTransformation<IntSize> {
transformnull34     override fun transform(
35         layoutImpl: SceneTransitionLayoutImpl,
36         scene: SceneKey,
37         element: Element,
38         sceneState: Element.SceneState,
39         transition: TransitionState.Transition,
40         value: IntSize,
41     ): IntSize {
42         fun anchorSizeIn(scene: SceneKey): IntSize {
43             val size =
44                 layoutImpl.elements[anchor]?.sceneStates?.get(scene)?.targetSize?.takeIf {
45                     it != Element.SizeUnspecified
46                 }
47                     ?: throwMissingAnchorException(
48                         transformation = "AnchoredSize",
49                         anchor = anchor,
50                         scene = scene,
51                     )
52 
53             return IntSize(
54                 width = if (anchorWidth) size.width else value.width,
55                 height = if (anchorHeight) size.height else value.height,
56             )
57         }
58 
59         // This simple implementation assumes that the size of [element] is the same as the size of
60         // the [anchor] in [scene], so simply transform to the size of the anchor in the other
61         // scene.
62         return if (scene == transition.fromScene) {
63             anchorSizeIn(transition.toScene)
64         } else {
65             anchorSizeIn(transition.fromScene)
66         }
67     }
68 }
69