1 /*
2  * Copyright 2019 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 @file:Suppress("NOTHING_TO_INLINE")
17 
18 package androidx.compose.ui.unit
19 
20 import androidx.compose.runtime.Immutable
21 import androidx.compose.runtime.Stable
22 import androidx.compose.ui.geometry.isSpecified
23 import androidx.compose.ui.util.floatFromBits
24 import androidx.compose.ui.util.lerp
25 
26 /**
27  * We encode the unit information and float value into the single 64-bit long integer. The higher
28  * 32bit represents the metadata of this value and lower 32bit represents the bit representation of
29  * the float value. Currently lower 8bits in the metadata bits are used for unit information.
30  *
31  * Bits
32  *
33  * ```
34  * |-------|-------|-------|-------|-------|-------|-------|-------|
35  *                                  FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF: Float Value
36  *                          UUUUUUUU                                : Unit Information
37  *  XXXXXXXXXXXXXXXXXXXXXXXX                                        : Unused bits
38  * ```
39  */
40 private const val UNIT_MASK = 0xFFL shl 32 // 0xFF_0000_0000
41 private const val UNIT_TYPE_UNSPECIFIED = 0x00L shl 32 // 0x00_0000_0000
42 private const val UNIT_TYPE_SP = 0x01L shl 32 // 0x01_0000_0000
43 private const val UNIT_TYPE_EM = 0x02L shl 32 // 0x02_0000_0000
44 
45 /** An enum class defining for type of [TextUnit]. */
46 @kotlin.jvm.JvmInline
47 value class TextUnitType(internal val type: Long) {
toStringnull48     override fun toString(): String {
49         return when (this) {
50             Unspecified -> "Unspecified"
51             Sp -> "Sp"
52             Em -> "Em"
53             else -> "Invalid"
54         }
55     }
56 
57     companion object {
58         val Unspecified = TextUnitType(UNIT_TYPE_UNSPECIFIED)
59         val Sp = TextUnitType(UNIT_TYPE_SP)
60         val Em = TextUnitType(UNIT_TYPE_EM)
61     }
62 }
63 
64 /**
65  * Construct a new TextUnit.
66  *
67  * @param value of the dimension
68  * @param type dimension
69  */
TextUnitnull70 fun TextUnit(value: Float, type: TextUnitType): TextUnit = pack(type.type, value)
71 
72 /**
73  * The unit used for text related dimension value.
74  *
75  * This unit can hold either scaled pixels (SP), relative font size (EM) and special unit
76  * Unspecified for indicating inheriting from other style or using the default value. It can be
77  * created with [sp] or [em]. (e.g. 15.sp or 18.em) which can be applied to [Int], [Double], and
78  * [Float].
79  *
80  * Note that do not store this value in your persistent storage or send to another process since the
81  * internal representation may be changed in future.
82  */
83 @Immutable
84 @kotlin.jvm.JvmInline
85 value class TextUnit internal constructor(internal val packedValue: Long) {
86     /**
87      * This is the same as multiplying the [TextUnit] by -1.0.
88      *
89      * This operation works only if the operand is not equal to [TextUnit.Unspecified]. The result
90      * of this operation is the same unit type of the given one.
91      *
92      * @throws IllegalArgumentException if this [TextUnit]'s type is [TextUnitType.Unspecified].
93      */
94     inline operator fun unaryMinus(): TextUnit {
95         checkArithmetic(this)
96         return pack(rawType, -value)
97     }
98 
99     /**
100      * Divide a [TextUnit] by a scalar.
101      *
102      * This operation works only if the left operand is not equal to [TextUnit.Unspecified]. The
103      * result of this operation is the same unit type of the given one.
104      *
105      * @throws IllegalArgumentException if this [TextUnit]'s type is [TextUnitType.Unspecified].
106      */
107     inline operator fun div(other: Float): TextUnit {
108         checkArithmetic(this)
109         return pack(rawType, value / other)
110     }
111 
112     /**
113      * Divide a [TextUnit] by a scalar.
114      *
115      * This operation works only if the left operand is not equal to [TextUnit.Unspecified]. The
116      * result of this operation is the same unit type of the given one.
117      *
118      * @throws IllegalArgumentException if this [TextUnit]'s type is [TextUnitType.Unspecified].
119      */
120     inline operator fun div(other: Double): TextUnit {
121         checkArithmetic(this)
122         return pack(rawType, (value / other).toFloat())
123     }
124 
125     /**
126      * Divide a [TextUnit] by a scalar.
127      *
128      * This operation works only if the left operand is not equal to [TextUnit.Unspecified]. The
129      * result of this operation is the same unit type of the given one.
130      *
131      * @throws IllegalArgumentException if this [TextUnit]'s type is [TextUnitType.Unspecified].
132      */
133     inline operator fun div(other: Int): TextUnit {
134         checkArithmetic(this)
135         return pack(rawType, value / other)
136     }
137 
138     /**
139      * Multiply a [TextUnit] by a scalar.
140      *
141      * This operation works only if the left operand is not equal to [TextUnit.Unspecified]. The
142      * result of this operation is the same unit type of the given one.
143      *
144      * @throws IllegalArgumentException if this [TextUnit]'s type is [TextUnitType.Unspecified].
145      */
146     inline operator fun times(other: Float): TextUnit {
147         checkArithmetic(this)
148         return pack(rawType, value * other)
149     }
150 
151     /**
152      * Multiply a [TextUnit] by a scalar.
153      *
154      * This operation works only if the left operand is not equal to [TextUnit.Unspecified]. The
155      * result of this operation is the same unit type of the given one.
156      *
157      * @throws IllegalArgumentException if this [TextUnit]'s type is [TextUnitType.Unspecified].
158      */
159     inline operator fun times(other: Double): TextUnit {
160         checkArithmetic(this)
161         return pack(rawType, (value * other).toFloat())
162     }
163 
164     /**
165      * Multiply a [TextUnit] by a scalar.
166      *
167      * This operation works only if the left operand is not equal to [TextUnit.Unspecified]. The
168      * result of this operation is the same unit type of the given one.
169      *
170      * @throws IllegalArgumentException if this [TextUnit]'s type is [TextUnitType.Unspecified].
171      */
172     inline operator fun times(other: Int): TextUnit {
173         checkArithmetic(this)
174         return pack(rawType, value * other)
175     }
176 
177     /**
178      * Support comparing Dimensions with comparison operators.
179      *
180      * @return 0 if this [TextUnit] equals to the [other], a negative number if it's less than the
181      *   [other], or a positive number if it's greater than the [other].
182      * @throws IllegalArgumentException if this [TextUnit] and the [other] has different
183      *   [TextUnitType]s or either of the two has the [TextUnitType] equals to
184      *   [TextUnitType.Unspecified].
185      */
186     inline operator fun compareTo(other: TextUnit): Int {
187         checkArithmetic(this, other)
188         return value.compareTo(other.value)
189     }
190 
191     override fun toString(): String {
192         return when (type) {
193             TextUnitType.Unspecified -> "Unspecified"
194             TextUnitType.Sp -> "$value.sp"
195             TextUnitType.Em -> "$value.em"
196             else -> "Invalid"
197         }
198     }
199 
200     companion object {
201         internal val TextUnitTypes =
202             arrayOf(TextUnitType.Unspecified, TextUnitType.Sp, TextUnitType.Em)
203 
204         /**
205          * A special [TextUnit] instance for representing inheriting from parent value.
206          *
207          * Notice that performing arithmetic operations on [Unspecified] may result in an
208          * [IllegalArgumentException].
209          */
210         @Stable val Unspecified = pack(UNIT_TYPE_UNSPECIFIED, Float.NaN)
211     }
212 
213     /**
214      * A helper function for getting underlying type information in raw bits.
215      *
216      * Use [TextUnit.type] in public places.
217      */
218     @PublishedApi
219     internal val rawType: Long
220         get() = packedValue and UNIT_MASK
221 
222     /** A type information of this TextUnit. */
223     val type: TextUnitType
224         get() = TextUnitTypes[(rawType ushr 32).toInt()]
225 
226     /** True if this is a SP unit type. */
227     val isSp
228         get() = rawType == UNIT_TYPE_SP
229 
230     /** True if this is a EM unit type. */
231     val isEm
232         get() = rawType == UNIT_TYPE_EM
233 
234     /**
235      * Returns the value of this [TextUnit].
236      *
237      * For example, the value of 3.sp equals to 3, and value of 5.em equals to 5. The value of
238      * [TextUnit]s whose [TextUnitType] is [TextUnitType.Unspecified] is undefined.
239      */
240     val value
241         get() = floatFromBits((packedValue and 0xFFFF_FFFFL).toInt())
242 }
243 
244 /** `false` when this is [TextUnit.Unspecified]. */
245 @Stable
246 inline val TextUnit.isSpecified: Boolean
247     get() = !isUnspecified
248 
249 /** `true` when this is [TextUnit.Unspecified]. */
250 @Stable
251 inline val TextUnit.isUnspecified: Boolean
252     get() = rawType == 0x0L // UNIT_TYPE_UNSPECIFIED
253 
254 /**
255  * If this [TextUnit] [isSpecified] then this is returned, otherwise [block] is executed and its
256  * result is returned.
257  */
takeOrElsenull258 inline fun TextUnit.takeOrElse(block: () -> TextUnit): TextUnit = if (isSpecified) this else block()
259 
260 /** Creates a SP unit [TextUnit] */
261 @Stable
262 val Float.sp: TextUnit
263     get() = pack(UNIT_TYPE_SP, this)
264 
265 /** Creates an EM unit [TextUnit] */
266 @Stable
267 val Float.em: TextUnit
268     get() = pack(UNIT_TYPE_EM, this)
269 
270 /** Creates a SP unit [TextUnit] */
271 @Stable
272 val Double.sp: TextUnit
273     get() = pack(UNIT_TYPE_SP, this.toFloat())
274 
275 /** Creates an EM unit [TextUnit] */
276 @Stable
277 val Double.em: TextUnit
278     get() = pack(UNIT_TYPE_EM, this.toFloat())
279 
280 /** Creates a SP unit [TextUnit] */
281 @Stable
282 val Int.sp: TextUnit
283     get() = pack(UNIT_TYPE_SP, this.toFloat())
284 
285 /** Creates an EM unit [TextUnit] */
286 @Stable
287 val Int.em: TextUnit
288     get() = pack(UNIT_TYPE_EM, this.toFloat())
289 
290 /**
291  * Multiply a [TextUnit] by a scalar.
292  *
293  * This operation works only if the right operand is not equal to [TextUnit.Unspecified]. The result
294  * of this operation is the same unit type of the given one.
295  */
296 @Stable
297 inline operator fun Float.times(other: TextUnit): TextUnit {
298     checkArithmetic(other)
299     return pack(other.rawType, this * other.value)
300 }
301 
302 /**
303  * Multiply a [TextUnit] by a scalar.
304  *
305  * This operation works only if the right operand is not equal to [TextUnit.Unspecified]. The result
306  * of this operation is the same unit type of the given one.
307  */
308 @Stable
timesnull309 inline operator fun Double.times(other: TextUnit): TextUnit {
310     checkArithmetic(other)
311     return pack(other.rawType, this.toFloat() * other.value)
312 }
313 
314 /**
315  * Multiply a [TextUnit] by a scalar.
316  *
317  * This operation works only if the right operand is not equal to [TextUnit.Unspecified]. The result
318  * of this operation is the same unit type of the given one.
319  */
320 @Stable
timesnull321 inline operator fun Int.times(other: TextUnit): TextUnit {
322     checkArithmetic(other)
323     return pack(other.rawType, this * other.value)
324 }
325 
326 @PublishedApi
packnull327 internal fun pack(unitType: Long, v: Float): TextUnit =
328     TextUnit(unitType or (v.toRawBits().toLong() and 0xFFFF_FFFFL))
329 
330 @PublishedApi
331 internal fun checkArithmetic(a: TextUnit) {
332     requirePrecondition(!a.isUnspecified) { "Cannot perform operation for Unspecified type." }
333 }
334 
335 @PublishedApi
checkArithmeticnull336 internal fun checkArithmetic(a: TextUnit, b: TextUnit) {
337     requirePrecondition(!a.isUnspecified && !b.isUnspecified) {
338         "Cannot perform operation for Unspecified type."
339     }
340     requirePrecondition(a.type == b.type) { "Cannot perform operation for ${a.type} and ${b.type}" }
341 }
342 
343 @PublishedApi
checkArithmeticnull344 internal fun checkArithmetic(a: TextUnit, b: TextUnit, c: TextUnit) {
345     requirePrecondition(!a.isUnspecified && !b.isUnspecified && !c.isUnspecified) {
346         "Cannot perform operation for Unspecified type."
347     }
348     requirePrecondition(a.type == b.type && b.type == c.type) {
349         "Cannot perform operation for ${a.type} and ${b.type}"
350     }
351 }
352 
353 /**
354  * Linearly interpolate between two [TextUnit]s.
355  *
356  * The [fraction] argument represents position on the timeline, with 0.0 meaning that the
357  * interpolation has not started, returning [start] (or something equivalent to [start]), 1.0
358  * meaning that the interpolation has finished, returning [stop] (or something equivalent to
359  * [stop]), and values in between meaning that the interpolation is at the relevant point on the
360  * timeline between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and 1.0, so
361  * negative values and values greater than 1.0 are valid.
362  *
363  * @throws IllegalArgumentException if [start] and [stop] have different [TextUnitType]s, or either
364  *   of the two has its [TextUnitType] equal to [TextUnitType.Unspecified].
365  */
366 @Stable
lerpnull367 fun lerp(start: TextUnit, stop: TextUnit, fraction: Float): TextUnit {
368     checkArithmetic(start, stop)
369     return pack(start.rawType, lerp(start.value, stop.value, fraction))
370 }
371