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.annotation.IntRange
20 import androidx.compose.ui.geometry.CornerRadius
21 import androidx.compose.ui.geometry.RoundRect
22 import androidx.compose.ui.geometry.Size
23 import androidx.compose.ui.geometry.toRect
24 import androidx.compose.ui.graphics.Outline
25 import androidx.compose.ui.graphics.Shape
26 import androidx.compose.ui.unit.Dp
27 import androidx.compose.ui.unit.LayoutDirection
28 import androidx.compose.ui.unit.LayoutDirection.Ltr
29 import androidx.compose.ui.unit.dp
30 
31 /**
32  * A shape describing the rectangle with rounded corners.
33  *
34  * This shape will automatically mirror the corner sizes in [LayoutDirection.Rtl], use
35  * [AbsoluteRoundedCornerShape] for the layout direction unaware version of this shape.
36  *
37  * @param topStart a size of the top start corner
38  * @param topEnd a size of the top end corner
39  * @param bottomEnd a size of the bottom end corner
40  * @param bottomStart a size of the bottom start corner
41  */
42 class RoundedCornerShape(
43     topStart: CornerSize,
44     topEnd: CornerSize,
45     bottomEnd: CornerSize,
46     bottomStart: CornerSize
47 ) :
48     CornerBasedShape(
49         topStart = topStart,
50         topEnd = topEnd,
51         bottomEnd = bottomEnd,
52         bottomStart = bottomStart
53     ) {
54 
createOutlinenull55     override fun createOutline(
56         size: Size,
57         topStart: Float,
58         topEnd: Float,
59         bottomEnd: Float,
60         bottomStart: Float,
61         layoutDirection: LayoutDirection
62     ) =
63         if (topStart + topEnd + bottomEnd + bottomStart == 0.0f) {
64             Outline.Rectangle(size.toRect())
65         } else {
66             Outline.Rounded(
67                 RoundRect(
68                     rect = size.toRect(),
69                     topLeft = CornerRadius(if (layoutDirection == Ltr) topStart else topEnd),
70                     topRight = CornerRadius(if (layoutDirection == Ltr) topEnd else topStart),
71                     bottomRight =
72                         CornerRadius(if (layoutDirection == Ltr) bottomEnd else bottomStart),
73                     bottomLeft =
74                         CornerRadius(if (layoutDirection == Ltr) bottomStart else bottomEnd)
75                 )
76             )
77         }
78 
copynull79     override fun copy(
80         topStart: CornerSize,
81         topEnd: CornerSize,
82         bottomEnd: CornerSize,
83         bottomStart: CornerSize
84     ) =
85         RoundedCornerShape(
86             topStart = topStart,
87             topEnd = topEnd,
88             bottomEnd = bottomEnd,
89             bottomStart = bottomStart
90         )
91 
92     override fun toString(): String {
93         return "RoundedCornerShape(topStart = $topStart, topEnd = $topEnd, bottomEnd = " +
94             "$bottomEnd, bottomStart = $bottomStart)"
95     }
96 
equalsnull97     override fun equals(other: Any?): Boolean {
98         if (this === other) return true
99         if (other !is RoundedCornerShape) return false
100 
101         if (topStart != other.topStart) return false
102         if (topEnd != other.topEnd) return false
103         if (bottomEnd != other.bottomEnd) return false
104         if (bottomStart != other.bottomStart) return false
105 
106         return true
107     }
108 
hashCodenull109     override fun hashCode(): Int {
110         var result = topStart.hashCode()
111         result = 31 * result + topEnd.hashCode()
112         result = 31 * result + bottomEnd.hashCode()
113         result = 31 * result + bottomStart.hashCode()
114         return result
115     }
116 }
117 
118 /** Circular [Shape] with all the corners sized as the 50 percent of the shape size. */
119 val CircleShape = RoundedCornerShape(50)
120 
121 /**
122  * Creates [RoundedCornerShape] with the same size applied for all four corners.
123  *
124  * @param corner [CornerSize] to apply.
125  */
RoundedCornerShapenull126 fun RoundedCornerShape(corner: CornerSize) = RoundedCornerShape(corner, corner, corner, corner)
127 
128 /**
129  * Creates [RoundedCornerShape] with the same size applied for all four corners.
130  *
131  * @param size Size in [Dp] to apply.
132  */
133 fun RoundedCornerShape(size: Dp) = RoundedCornerShape(CornerSize(size))
134 
135 /**
136  * Creates [RoundedCornerShape] with the same size applied for all four corners.
137  *
138  * @param size Size in pixels to apply.
139  */
140 fun RoundedCornerShape(size: Float) = RoundedCornerShape(CornerSize(size))
141 
142 /**
143  * Creates [RoundedCornerShape] with the same size applied for all four corners.
144  *
145  * @param percent Size in percents to apply.
146  */
147 fun RoundedCornerShape(percent: Int) = RoundedCornerShape(CornerSize(percent))
148 
149 /** Creates [RoundedCornerShape] with sizes defined in [Dp]. */
150 fun RoundedCornerShape(
151     topStart: Dp = 0.dp,
152     topEnd: Dp = 0.dp,
153     bottomEnd: Dp = 0.dp,
154     bottomStart: Dp = 0.dp
155 ) =
156     RoundedCornerShape(
157         topStart = CornerSize(topStart),
158         topEnd = CornerSize(topEnd),
159         bottomEnd = CornerSize(bottomEnd),
160         bottomStart = CornerSize(bottomStart)
161     )
162 
163 /** Creates [RoundedCornerShape] with sizes defined in pixels. */
164 fun RoundedCornerShape(
165     topStart: Float = 0.0f,
166     topEnd: Float = 0.0f,
167     bottomEnd: Float = 0.0f,
168     bottomStart: Float = 0.0f
169 ) =
170     RoundedCornerShape(
171         topStart = CornerSize(topStart),
172         topEnd = CornerSize(topEnd),
173         bottomEnd = CornerSize(bottomEnd),
174         bottomStart = CornerSize(bottomStart)
175     )
176 
177 /**
178  * Creates [RoundedCornerShape] with sizes defined in percents of the shape's smaller side.
179  *
180  * @param topStartPercent The top start corner radius as a percentage of the smaller side, with a
181  *   range of 0 - 100.
182  * @param topEndPercent The top end corner radius as a percentage of the smaller side, with a range
183  *   of 0 - 100.
184  * @param bottomEndPercent The bottom end corner radius as a percentage of the smaller side, with a
185  *   range of 0 - 100.
186  * @param bottomStartPercent The bottom start corner radius as a percentage of the smaller side,
187  *   with a range of 0 - 100.
188  */
189 fun RoundedCornerShape(
190     @IntRange(from = 0, to = 100) topStartPercent: Int = 0,
191     @IntRange(from = 0, to = 100) topEndPercent: Int = 0,
192     @IntRange(from = 0, to = 100) bottomEndPercent: Int = 0,
193     @IntRange(from = 0, to = 100) bottomStartPercent: Int = 0
194 ) =
195     RoundedCornerShape(
196         topStart = CornerSize(topStartPercent),
197         topEnd = CornerSize(topEndPercent),
198         bottomEnd = CornerSize(bottomEndPercent),
199         bottomStart = CornerSize(bottomStartPercent)
200     )
201