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.FloatRange
20 import androidx.annotation.IntRange
21 import androidx.compose.foundation.internal.throwIllegalArgumentException
22 import androidx.compose.runtime.Immutable
23 import androidx.compose.runtime.Stable
24 import androidx.compose.ui.geometry.Size
25 import androidx.compose.ui.platform.InspectableValue
26 import androidx.compose.ui.unit.Density
27 import androidx.compose.ui.unit.Dp
28 
29 /** Defines size of a corner in pixels. For example for rounded shape it can be a corner radius. */
30 @Immutable
31 interface CornerSize {
32     /**
33      * Converts the [CornerSize] to pixels.
34      *
35      * @param shapeSize the size of the shape
36      * @param density the current density of the screen.
37      * @return resolved size of the corner in pixels
38      */
toPxnull39     fun toPx(shapeSize: Size, density: Density): Float
40 }
41 
42 /**
43  * Creates [CornerSize] with provided size.
44  *
45  * @param size the corner size defined in [Dp].
46  */
47 @Stable fun CornerSize(size: Dp): CornerSize = DpCornerSize(size)
48 
49 private data class DpCornerSize(private val size: Dp) : CornerSize, InspectableValue {
50     override fun toPx(shapeSize: Size, density: Density) = with(density) { size.toPx() }
51 
52     override fun toString(): String = "CornerSize(size = ${size.value}.dp)"
53 
54     override val valueOverride: Dp
55         get() = size
56 }
57 
58 /**
59  * Creates [CornerSize] with provided size.
60  *
61  * @param size the corner size defined in pixels.
62  */
CornerSizenull63 @Stable fun CornerSize(size: Float): CornerSize = PxCornerSize(size)
64 
65 private data class PxCornerSize(private val size: Float) : CornerSize, InspectableValue {
66     override fun toPx(shapeSize: Size, density: Density) = size
67 
68     override fun toString(): String = "CornerSize(size = $size.px)"
69 
70     override val valueOverride: String
71         get() = "${size}px"
72 }
73 
74 /**
75  * Creates [CornerSize] with provided size.
76  *
77  * @param percent the corner size defined in percents of the shape's smaller side. Can't be negative
78  *   or larger then 100 percents.
79  */
80 @Stable
CornerSizenull81 fun CornerSize(@IntRange(from = 0, to = 100) percent: Int): CornerSize =
82     PercentCornerSize(percent.toFloat())
83 
84 /**
85  * Creates [CornerSize] with provided size.
86  *
87  * @param percent the corner size defined in float percents of the shape's smaller side. Can't be
88  *   negative or larger then 100 percents.
89  */
90 private data class PercentCornerSize(
91     @FloatRange(from = 0.0, to = 100.0) private val percent: Float
92 ) : CornerSize, InspectableValue {
93     init {
94         if (percent < 0 || percent > 100) {
95             throwIllegalArgumentException("The percent should be in the range of [0, 100]")
96         }
97     }
98 
99     override fun toPx(shapeSize: Size, density: Density) = shapeSize.minDimension * (percent / 100f)
100 
101     override fun toString(): String = "CornerSize(size = $percent%)"
102 
103     override val valueOverride: String
104         get() = "$percent%"
105 }
106 
107 /** [CornerSize] always equals to zero. */
108 @Stable
109 val ZeroCornerSize: CornerSize =
110     object : CornerSize, InspectableValue {
toPxnull111         override fun toPx(shapeSize: Size, density: Density) = 0.0f
112 
113         override fun toString(): String = "ZeroCornerSize"
114 
115         override val valueOverride: String
116             get() = "ZeroCornerSize"
117     }
118