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