blob: a11215306b7609cabf5784cd74b47aed2713af83 [file] [log] [blame]
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("NOTHING_TO_INLINE")
package androidx.ui.core
import androidx.compose.Immutable
/**
* We encode the unit information and float value into the single 64-bit long integer.
* The higher 32bit represents the metadata of this value and lower 32bit represents the bit
* representation of the float value. Currently lower 8bits in the metadata bits are used for
* unit information.
*
* Bits
* |-------|-------|-------|-------|-------|-------|-------|-------|
* FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF: Float Value
* UUUUUUUU : Unit Information
* XXXXXXXXXXXXXXXXXXXXXXXX : Unused bits
*/
private const val UNIT_MASK = 0xFFL shl 32 // 0xFF_0000_0000
private const val UNIT_TYPE_INHERIT = 0x00L shl 32 // 0x00_0000_0000
private const val UNIT_TYPE_SP = 0x01L shl 32 // 0x01_0000_0000
private const val UNIT_TYPE_EM = 0x02L shl 32 // 0x2_0000_0000
/**
* An enum class defining for type of thextUnit.
*/
enum class TextUnitType { Inherit, Sp, Em }
/**
* The unit used for text related dimension value.
*
* This unit can hold either scaled pixels (SP), relative font size (em) and special unit for
* indicating inheriting from other style.
*
* Note that do not store this value in your persistent storage or send to another process since
* the internal representation may be changed in future.
*/
@Suppress("EXPERIMENTAL_FEATURE_WARNING")
@Immutable
data /*inline*/ class TextUnit(val packedValue: Long) {
/**
* Add two [TextUnit]s together.
*
* This operation works only if all the operands are the same unit type and not they are not
* equal to [TextUnit.Inherit].
* The result of this operation is the same unit type of the given one.
*/
inline operator fun plus(other: TextUnit) = checkArithmetic(this, other) {
pack(rawType, value + other.value)
}
/**
* Subtract a [TextUnit] from another one.
* This operation works only if all the operands are the same unit type and not they are not
* equal to [TextUnit.Inherit].
* The result of this operation is the same unit type of the given one.
*/
inline operator fun minus(other: TextUnit) = checkArithmetic(this, other) {
pack(rawType, value - other.value)
}
/**
* This is the same as multiplying the [TextUnit] by -1.0.
*
* This operation works only if the operand is not equal to [TextUnit.Inherit].
* The result of this operation is the same unit type of the given one.
*/
inline operator fun unaryMinus() = checkArithmetic(this) {
pack(rawType, -value)
}
/**
* Divide a [TextUnit] by a scalar.
*
* This operation works only if the left operand is not equal to [TextUnit.Inherit].
* The result of this operation is the same unit type of the given one.
*/
inline operator fun div(other: Float): TextUnit = checkArithmetic(this) {
pack(rawType, value / other)
}
/**
* Divide a [TextUnit] by a scalar.
*
* This operation works only if the left operand is not equal to [TextUnit.Inherit].
* The result of this operation is the same unit type of the given one.
*/
inline operator fun div(other: Double): TextUnit = checkArithmetic(this) {
pack(rawType, (value / other).toFloat())
}
/**
* Divide a [TextUnit] by a scalar.
*
* This operation works only if the left operand is not equal to [TextUnit.Inherit].
* The result of this operation is the same unit type of the given one.
*/
inline operator fun div(other: Int): TextUnit = checkArithmetic(this) {
pack(rawType, value / other)
}
/**
* Divide by another [TextUnit] to get a scalar.
*
* This operation works only if all the operands are the same unit type and they are not
* equal to [TextUnit.Inherit].
*/
inline operator fun div(other: TextUnit): Float = checkArithmetic(this, other) {
value / other.value
}
/**
* Multiply a [TextUnit] by a scalar.
*
* This operation works only if the left operand is not equal to [TextUnit.Inherit].
* The result of this operation is the same unit type of the given one.
*/
inline operator fun times(other: Float): TextUnit = checkArithmetic(this) {
pack(rawType, value * other)
}
/**
* Multiply a [TextUnit] by a scalar.
*
* This operation works only if the left operand is not equal to [TextUnit.Inherit].
* The result of this operation is the same unit type of the given one.
*/
inline operator fun times(other: Double): TextUnit = checkArithmetic(this) {
pack(rawType, (value * other).toFloat())
}
/**
* Multiply a [TextUnit] by a scalar.
*
* This operation works only if the left operand is not equal to [TextUnit.Inherit].
* The result of this operation is the same unit type of the given one.
*/
inline operator fun times(other: Int): TextUnit = checkArithmetic(this) {
pack(rawType, value * other)
}
/**
* Support comparing Dimensions with comparison operators.
*/
inline operator fun compareTo(other: TextUnit): Int = checkArithmetic(this, other) {
value.compareTo(other.value)
}
companion object {
/**
* Creates a SP unit [TextUnit].
*/
fun Sp(value: Int) = pack(UNIT_TYPE_SP, value.toFloat())
/**
* Creates a SP unit [TextUnit].
*/
fun Sp(value: Float) = pack(UNIT_TYPE_SP, value)
/**
* Creates a SP unit [TextUnit].
*/
fun Sp(value: Double) = pack(UNIT_TYPE_SP, value.toFloat())
/**
* Creates an EM unit [TextUnit].
*/
fun Em(value: Int) = pack(UNIT_TYPE_EM, value.toFloat())
/**
* Creates an EM unit [TextUnit].
*/
fun Em(value: Float) = pack(UNIT_TYPE_EM, value)
/**
* Creates an EM unit [TextUnit].
*/
fun Em(value: Double) = pack(UNIT_TYPE_EM, value.toFloat())
/**
* A special [TextUnit] instance for representing inheriting from parent value.
*/
val Inherit = pack(UNIT_TYPE_INHERIT, 0f)
}
/**
* A helper function for getting underlying type information in raw bits.
*
* Use [TextUnit.type] in public places.
*/
@PublishedApi
internal val rawType: Long get() = packedValue and UNIT_MASK
/**
* A type information of this TextUnit.
*
* @throws RuntimeException if unknown unknown unit type is appeared.
*/
val type: TextUnitType get() = when (rawType) {
UNIT_TYPE_INHERIT -> TextUnitType.Inherit
UNIT_TYPE_SP -> TextUnitType.Sp
UNIT_TYPE_EM -> TextUnitType.Em
else -> throw RuntimeException("packed TextUnit has unknown unit type.")
}
/**
* True if this is [TextUnit.Inherit], otherwise false.
*/
val isInherit get() = rawType == UNIT_TYPE_INHERIT
/**
* True if this is a SP unit type.
*/
val isSp get() = rawType == UNIT_TYPE_SP
/**
* True if this is a EM unit type.
*/
val isEm get() = rawType == UNIT_TYPE_EM
/**
* Returns the value
*/
val value get() = Float.fromBits((packedValue and 0xFFFF_FFFFL).toInt())
}
/**
* Creates a SP unit [TextUnit]
*/
val Float.sp: TextUnit get() = pack(UNIT_TYPE_SP, this)
/**
* Creates an EM unit [TextUnit]
*/
val Float.em: TextUnit get() = pack(UNIT_TYPE_EM, this)
/**
* Creates a SP unit [TextUnit]
*/
val Double.sp: TextUnit get() = pack(UNIT_TYPE_SP, this.toFloat())
/**
* Creates an EM unit [TextUnit]
*/
val Double.em: TextUnit get() = pack(UNIT_TYPE_EM, this.toFloat())
/**
* Creates a SP unit [TextUnit]
*/
val Int.sp: TextUnit get() = pack(UNIT_TYPE_SP, this.toFloat())
/**
* Creates an EM unit [TextUnit]
*/
val Int.em: TextUnit get() = pack(UNIT_TYPE_EM, this.toFloat())
/**
* Multiply a [TextUnit] by a scalar.
*
* This operation works only if the right operand is not equal to [TextUnit.Inherit].
* The result of this operation is the same unit type of the given one.
*/
inline operator fun Float.times(other: TextUnit) = checkArithmetic(other) {
pack(other.rawType, this * other.value)
}
/**
* Multiply a [TextUnit] by a scalar.
*
* This operation works only if the right operand is not equal to [TextUnit.Inherit].
* The result of this operation is the same unit type of the given one.
*/
inline operator fun Double.times(other: TextUnit) = checkArithmetic(other) {
pack(other.rawType, this.toFloat() * other.value)
}
/**
* Multiply a [TextUnit] by a scalar.
*
* This operation works only if the right operand is not equal to [TextUnit.Inherit].
* The result of this operation is the same unit type of the given one.
*/
inline operator fun Int.times(other: TextUnit) = checkArithmetic(other) {
pack(other.rawType, this * other.value)
}
/**
* Returns the smaller value from the given values.
*
* This operation works only if all the operands are the same unit type and they are not
* equal to [TextUnit.Inherit].
* The result of this operation is the same unit type of the given one.
*/
inline fun min(a: TextUnit, b: TextUnit): TextUnit = checkArithmetic(a, b) {
if (a.value < b.value) a else b
}
/**
* Returns the smaller value from the given values.
*
* This operation works only if all the operands are the same unit type and they are not
* equal to [TextUnit.Inherit].
* The result of this operation is the same unit type of the given one.
*/
inline fun max(a: TextUnit, b: TextUnit): TextUnit = checkArithmetic(a, b) {
if (a.value < b.value) b else a
}
/**
* Ensures that the value of [TextUnit] lies in the specified range [minimumValue]..[maximumValue].
*
*
* This operation works only if all the operands are the same unit type and they are not
* equal to [TextUnit.Inherit].
* The result of this operation is the same unit type of the given one.
*
* @return this value if it's in the range, or [minimumValue] if this value is less than
* [minimumValue], or [maximumValue] if this value is greater than [maximumValue].
*/
inline fun TextUnit.coerceIn(minimumValue: TextUnit, maximumValue: TextUnit): TextUnit =
checkArithmetic(this, minimumValue, maximumValue) {
pack(rawType, value.coerceIn(minimumValue.value, maximumValue.value))
}
/**
* Ensures that the value of [TextUnit] is not less than the specified [minimumValue].
*
* @return this value if it's greater than or equal to the [minimumValue] or the
* [minimumValue] otherwise.
*/
inline fun TextUnit.coerceAtLeast(minimumValue: TextUnit): TextUnit =
checkArithmetic(this, minimumValue) {
pack(rawType, value.coerceAtLeast(minimumValue.value))
}
/**
* Ensures that the value of [TextUnit] is not greater than the specified [maximumValue].
*
* @return this value if it's less than or equal to the [maximumValue] or the
* [maximumValue] otherwise.
*/
inline fun TextUnit.coerceAtMost(maximumValue: TextUnit): TextUnit =
checkArithmetic(this, maximumValue) {
pack(rawType, value.coerceAtMost(maximumValue.value))
}
@PublishedApi
internal inline fun pack(unitType: Long, v: Float): TextUnit =
TextUnit(unitType or (v.toBits().toLong() and 0xFFFF_FFFFL))
@PublishedApi
internal inline fun <T> checkArithmetic(a: TextUnit, block: () -> T): T {
require(a.type != TextUnitType.Inherit) {
"Cannot perform operation for Inherit type."
}
return block()
}
@PublishedApi
internal inline fun <T> checkArithmetic(a: TextUnit, b: TextUnit, block: () -> T): T {
require(a.type != TextUnitType.Inherit && b.type != TextUnitType.Inherit) {
"Cannot perform operation for Inherit type."
}
require(a.type == b.type) {
"Cannot perform operation for ${a.type} and ${b.type}"
}
return block()
}
@PublishedApi
internal inline fun <T> checkArithmetic(a: TextUnit, b: TextUnit, c: TextUnit, block: () -> T): T {
require(a.type != TextUnitType.Inherit && b.type != TextUnitType.Inherit &&
c.type != TextUnitType.Inherit) {
"Cannot perform operation for Inherit type."
}
require(a.type == b.type && b.type == c.type) {
"Cannot perform operation for ${a.type} and ${b.type}"
}
return block()
}
fun lerpTextUnit(a: TextUnit, b: TextUnit, t: Float): TextUnit = checkArithmetic(a, b) {
return pack(a.rawType, a.value + (b.value - a.value) * t)
}