1 /*
2  * Copyright 2020 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 androidx.compose.foundation.shape
18 
19 import androidx.compose.foundation.internal.requirePrecondition
20 import androidx.compose.ui.geometry.Size
21 import androidx.compose.ui.graphics.Outline
22 import androidx.compose.ui.graphics.Shape
23 import androidx.compose.ui.unit.Density
24 import androidx.compose.ui.unit.LayoutDirection
25 
26 /**
27  * Base class for [Shape]s defined by four [CornerSize]s.
28  *
29  * @param topStart a size of the top start corner
30  * @param topEnd a size of the top end corner
31  * @param bottomEnd a size of the bottom end corner
32  * @param bottomStart a size of the bottom start corner
33  * @see RoundedCornerShape for an example of the usage.
34  */
35 abstract class CornerBasedShape(
36     val topStart: CornerSize,
37     val topEnd: CornerSize,
38     val bottomEnd: CornerSize,
39     val bottomStart: CornerSize
40 ) : Shape {
41 
createOutlinenull42     final override fun createOutline(
43         size: Size,
44         layoutDirection: LayoutDirection,
45         density: Density
46     ): Outline {
47         var topStart = topStart.toPx(size, density)
48         var topEnd = topEnd.toPx(size, density)
49         var bottomEnd = bottomEnd.toPx(size, density)
50         var bottomStart = bottomStart.toPx(size, density)
51         val minDimension = size.minDimension
52         if (topStart + bottomStart > minDimension) {
53             val scale = minDimension / (topStart + bottomStart)
54             topStart *= scale
55             bottomStart *= scale
56         }
57         if (topEnd + bottomEnd > minDimension) {
58             val scale = minDimension / (topEnd + bottomEnd)
59             topEnd *= scale
60             bottomEnd *= scale
61         }
62         requirePrecondition(
63             topStart >= 0.0f && topEnd >= 0.0f && bottomEnd >= 0.0f && bottomStart >= 0.0f
64         ) {
65             "Corner size in Px can't be negative(topStart = $topStart, topEnd = $topEnd, " +
66                 "bottomEnd = $bottomEnd, bottomStart = $bottomStart)!"
67         }
68         return createOutline(
69             size = size,
70             topStart = topStart,
71             topEnd = topEnd,
72             bottomEnd = bottomEnd,
73             bottomStart = bottomStart,
74             layoutDirection = layoutDirection
75         )
76     }
77 
78     /**
79      * Creates [Outline] of this shape for the given [size].
80      *
81      * @param size the size of the shape boundary.
82      * @param topStart the resolved size of the top start corner
83      * @param topEnd the resolved size for the top end corner
84      * @param bottomEnd the resolved size for the bottom end corner
85      * @param bottomStart the resolved size for the bottom start corner
86      * @param layoutDirection the current layout direction.
87      */
createOutlinenull88     abstract fun createOutline(
89         size: Size,
90         topStart: Float,
91         topEnd: Float,
92         bottomEnd: Float,
93         bottomStart: Float,
94         layoutDirection: LayoutDirection
95     ): Outline
96 
97     /**
98      * Creates a copy of this Shape with a new corner sizes.
99      *
100      * @param topStart a size of the top start corner
101      * @param topEnd a size of the top end corner
102      * @param bottomEnd a size of the bottom end corner
103      * @param bottomStart a size of the bottom start corner
104      */
105     abstract fun copy(
106         topStart: CornerSize = this.topStart,
107         topEnd: CornerSize = this.topEnd,
108         bottomEnd: CornerSize = this.bottomEnd,
109         bottomStart: CornerSize = this.bottomStart
110     ): CornerBasedShape
111 
112     /**
113      * Creates a copy of this Shape with a new corner size.
114      *
115      * @param all a size to apply for all four corners
116      */
117     fun copy(all: CornerSize): CornerBasedShape = copy(all, all, all, all)
118 }
119