| /* |
| * Copyright 2018 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.compose.ui.unit |
| |
| import androidx.compose.runtime.Immutable |
| import androidx.compose.runtime.Stable |
| import androidx.compose.ui.unit.Dp.Companion.Hairline |
| import androidx.compose.ui.util.lerp |
| import androidx.compose.ui.util.packFloats |
| import androidx.compose.ui.util.unpackFloat1 |
| import androidx.compose.ui.util.unpackFloat2 |
| import kotlin.math.max |
| import kotlin.math.min |
| import kotlin.math.sqrt |
| |
| /** |
| * Dimension value representing device-independent pixels (dp). Component APIs specify their |
| * dimensions such as line thickness in DP with Dp objects. Hairline (1 pixel) thickness |
| * may be specified with [Hairline], a dimension that take up no space. Dp are normally |
| * defined using [dp], which can be applied to [Int], [Double], and [Float]. |
| * val leftMargin = 10.dp |
| * val rightMargin = 10f.dp |
| * val topMargin = 20.0.dp |
| * val bottomMargin = 10.dp |
| * Drawing is done in pixels. To retrieve the pixel size of a Dp, use [toPx]: |
| * val lineThicknessPx = lineThickness.toPx(context) |
| * [toPx] is normally needed only for painting operations. |
| */ |
| @Suppress("EXPERIMENTAL_FEATURE_WARNING") |
| @Immutable |
| inline class Dp(val value: Float) : Comparable<Dp> { |
| /** |
| * Add two [Dp]s together. |
| */ |
| @Stable |
| inline operator fun plus(other: Dp) = |
| Dp(value = this.value + other.value) |
| |
| /** |
| * Subtract a Dp from another one. |
| */ |
| @Stable |
| inline operator fun minus(other: Dp) = |
| Dp(value = this.value - other.value) |
| |
| /** |
| * This is the same as multiplying the Dp by -1.0. |
| */ |
| @Stable |
| inline operator fun unaryMinus() = Dp(-value) |
| |
| /** |
| * Divide a Dp by a scalar. |
| */ |
| @Stable |
| inline operator fun div(other: Float): Dp = |
| Dp(value = value / other) |
| |
| @Stable |
| inline operator fun div(other: Int): Dp = |
| Dp(value = value / other) |
| |
| /** |
| * Divide by another Dp to get a scalar. |
| */ |
| @Stable |
| inline operator fun div(other: Dp): Float = value / other.value |
| |
| /** |
| * Divide by [DpSquared] to get a [DpInverse]. |
| */ |
| @Stable |
| inline operator fun div(other: DpSquared): DpInverse = |
| DpInverse(value = value / other.value) |
| |
| /** |
| * Multiply a Dp by a scalar. |
| */ |
| @Stable |
| inline operator fun times(other: Float): Dp = |
| Dp(value = value * other) |
| |
| @Stable |
| inline operator fun times(other: Int): Dp = |
| Dp(value = value * other) |
| |
| /** |
| * Multiply by a Dp to get a [DpSquared] result. |
| */ |
| @Stable |
| inline operator fun times(other: Dp): DpSquared = |
| DpSquared(value = value * other.value) |
| |
| /** |
| * Multiply by a Dp to get a [DpSquared] result. |
| */ |
| @Stable |
| inline operator fun times(other: DpSquared): DpCubed = |
| DpCubed(value = value * other.value) |
| |
| /** |
| * Support comparing Dimensions with comparison operators. |
| */ |
| @Stable |
| override /* TODO: inline */ operator fun compareTo(other: Dp) = value.compareTo(other.value) |
| |
| @Stable |
| override fun toString() = "$value.dp" |
| |
| companion object { |
| /** |
| * A dimension used to represent a hairline drawing element. Hairline elements take up no |
| * space, but will draw a single pixel, independent of the device's resolution and density. |
| */ |
| @Stable |
| val Hairline = Dp(value = 0f) |
| |
| /** |
| * Infinite dp dimension. |
| */ |
| @Stable |
| val Infinity = Dp(value = Float.POSITIVE_INFINITY) |
| |
| /** |
| * Constant that means unspecified Dp |
| */ |
| @Stable |
| val Unspecified = Dp(value = Float.NaN) |
| } |
| } |
| |
| /** |
| * Create a [Dp] using an [Int]: |
| * val left = 10 |
| * val x = left.dp |
| * // -- or -- |
| * val y = 10.dp |
| */ |
| @Stable |
| inline val Int.dp: Dp get() = Dp(value = this.toFloat()) |
| |
| /** |
| * Create a [Dp] using a [Double]: |
| * val left = 10.0 |
| * val x = left.dp |
| * // -- or -- |
| * val y = 10.0.dp |
| */ |
| @Stable |
| inline val Double.dp: Dp get() = Dp(value = this.toFloat()) |
| |
| /** |
| * Create a [Dp] using a [Float]: |
| * val left = 10f |
| * val x = left.dp |
| * // -- or -- |
| * val y = 10f.dp |
| */ |
| @Stable |
| inline val Float.dp: Dp get() = Dp(value = this) |
| |
| @Stable |
| inline operator fun Float.div(other: Dp) = |
| DpInverse(this / other.value) |
| |
| @Stable |
| inline operator fun Double.div(other: Dp) = |
| DpInverse(this.toFloat() / other.value) |
| |
| @Stable |
| inline operator fun Int.div(other: Dp) = |
| DpInverse(this / other.value) |
| |
| @Stable |
| inline operator fun Float.times(other: Dp) = |
| Dp(this * other.value) |
| |
| @Stable |
| inline operator fun Double.times(other: Dp) = |
| Dp(this.toFloat() * other.value) |
| |
| @Stable |
| inline operator fun Int.times(other: Dp) = |
| Dp(this * other.value) |
| |
| @Stable |
| inline fun min(a: Dp, b: Dp): Dp = Dp(value = min(a.value, b.value)) |
| |
| @Stable |
| inline fun max(a: Dp, b: Dp): Dp = Dp(value = max(a.value, b.value)) |
| |
| /** |
| * Ensures that this value lies in the specified range [minimumValue]..[maximumValue]. |
| * |
| * @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]. |
| */ |
| @Stable |
| inline fun Dp.coerceIn(minimumValue: Dp, maximumValue: Dp): Dp = |
| Dp(value = value.coerceIn(minimumValue.value, maximumValue.value)) |
| |
| /** |
| * Ensures that this value is not less than the specified [minimumValue]. |
| * @return this value if it's greater than or equal to the [minimumValue] or the |
| * [minimumValue] otherwise. |
| */ |
| @Stable |
| inline fun Dp.coerceAtLeast(minimumValue: Dp): Dp = |
| Dp(value = value.coerceAtLeast(minimumValue.value)) |
| |
| /** |
| * Ensures that this value is not greater than the specified [maximumValue]. |
| * |
| * @return this value if it's less than or equal to the [maximumValue] or the |
| * [maximumValue] otherwise. |
| */ |
| @Stable |
| inline fun Dp.coerceAtMost(maximumValue: Dp): Dp = |
| Dp(value = value.coerceAtMost(maximumValue.value)) |
| |
| /** |
| * |
| * Return `true` when it is finite or `false` when it is [Dp.Infinity] |
| */ |
| @Stable |
| inline fun Dp.isFinite(): Boolean = value != Float.POSITIVE_INFINITY |
| |
| /** |
| * Linearly interpolate between two [Dp]s. |
| * |
| * The [fraction] argument represents position on the timeline, with 0.0 meaning |
| * that the interpolation has not started, returning [start] (or something |
| * equivalent to [start]), 1.0 meaning that the interpolation has finished, |
| * returning [stop] (or something equivalent to [stop]), and values in between |
| * meaning that the interpolation is at the relevant point on the timeline |
| * between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and |
| * 1.0, so negative values and values greater than 1.0 are valid. |
| */ |
| @Stable |
| fun lerp(start: Dp, stop: Dp, fraction: Float): Dp { |
| return Dp(lerp(start.value, stop.value, fraction)) |
| } |
| |
| /** |
| * Holds a unit of squared dimensions, such as `1.value * 2.dp`. [DpSquared], [DpCubed], |
| * and [DpInverse] are used primarily for [Dp] calculations to ensure resulting |
| * units are as expected. Many times, [Dp] calculations use scalars to determine the final |
| * dimension during calculation: |
| * val width = oldWidth * stretchAmount |
| * Other times, it is useful to do intermediate calculations with Dimensions directly: |
| * val width = oldWidth * newTotalWidth / oldTotalWidth |
| */ |
| @Suppress("EXPERIMENTAL_FEATURE_WARNING") |
| @Immutable |
| inline class DpSquared(val value: Float) : Comparable<DpSquared> { |
| /** |
| * Add two DimensionSquares together. |
| */ |
| @Stable |
| inline operator fun plus(other: DpSquared) = |
| DpSquared(value = value + other.value) |
| |
| /** |
| * Subtract a DimensionSquare from another one. |
| */ |
| @Stable |
| inline operator fun minus(other: DpSquared) = |
| DpSquared(value = value - other.value) |
| |
| /** |
| * Divide a DimensionSquare by a scalar. |
| */ |
| @Stable |
| inline operator fun div(other: Float): DpSquared = |
| DpSquared(value = value / other) |
| |
| /** |
| * Divide by a [Dp] to get a [Dp] result. |
| */ |
| @Stable |
| inline operator fun div(other: Dp): Dp = |
| Dp(value = value / other.value) |
| |
| /** |
| * Divide by a DpSquared to get a scalar result. |
| */ |
| @Stable |
| inline operator fun div(other: DpSquared): Float = value / other.value |
| |
| /** |
| * Divide by a [DpCubed] to get a [DpInverse] result. |
| */ |
| @Stable |
| inline operator fun div(other: DpCubed): DpInverse = |
| DpInverse(value / other.value) |
| |
| /** |
| * Multiply by a scalar to get a DpSquared result. |
| */ |
| @Stable |
| inline operator fun times(other: Float): DpSquared = |
| DpSquared(value = value * other) |
| |
| /** |
| * Multiply by a scalar to get a DpSquared result. |
| */ |
| @Stable |
| inline operator fun times(other: Dp): DpCubed = |
| DpCubed(value = value * other.value) |
| |
| /** |
| * Support comparing DpSquared with comparison operators. |
| */ |
| @Stable |
| override /* TODO: inline */ operator fun compareTo(other: DpSquared) = |
| value.compareTo(other.value) |
| |
| @Stable |
| override fun toString(): String = "$value.dp^2" |
| } |
| |
| /** |
| * Holds a unit of cubed dimensions, such as `1.value * 2.value * 3.dp`. [DpSquared], |
| * [DpCubed], and [DpInverse] are used primarily for [Dp] calculations to |
| * ensure resulting units are as expected. Many times, [Dp] calculations use scalars to |
| * determine the final dimension during calculation: |
| * val width = oldWidth * stretchAmount |
| * Other times, it is useful to do intermediate calculations with Dimensions directly: |
| * val width = oldWidth * newTotalWidth / oldTotalWidth |
| */ |
| @Suppress("EXPERIMENTAL_FEATURE_WARNING") |
| @Immutable |
| inline class DpCubed(val value: Float) : Comparable<DpCubed> { |
| |
| /** |
| * Add two DpCubed together. |
| */ |
| @Stable |
| inline operator fun plus(dimension: DpCubed) = |
| DpCubed(value = value + dimension.value) |
| |
| /** |
| * Subtract a DpCubed from another one. |
| */ |
| @Stable |
| inline operator fun minus(dimension: DpCubed) = |
| DpCubed(value = value - dimension.value) |
| |
| /** |
| * Divide a DpCubed by a scalar. |
| */ |
| @Stable |
| inline operator fun div(other: Float): DpCubed = |
| DpCubed(value = value / other) |
| |
| /** |
| * Divide by a [Dp] to get a [DpSquared] result. |
| */ |
| @Stable |
| inline operator fun div(other: Dp): DpSquared = |
| DpSquared(value = value / other.value) |
| |
| /** |
| * Divide by a [DpSquared] to get a [Dp] result. |
| */ |
| @Stable |
| inline operator fun div(other: DpSquared): Dp = |
| Dp(value = value / other.value) |
| |
| /** |
| * Divide by a DpCubed to get a scalar result. |
| */ |
| @Stable |
| inline operator fun div(other: DpCubed): Float = value / other.value |
| |
| /** |
| * Multiply by a scalar to get a DpCubed result. |
| */ |
| @Stable |
| inline operator fun times(other: Float): DpCubed = |
| DpCubed(value = value * other) |
| |
| /** |
| * Support comparing DpCubed with comparison operators. |
| */ |
| @Stable |
| override /* TODO: inline */ operator fun compareTo(other: DpCubed) = |
| value.compareTo(other.value) |
| |
| @Stable |
| override fun toString(): String = "$value.dp^3" |
| } |
| /** |
| * Holds a unit of an inverse dimensions, such as `1.dp / (2.value * 3.dp)`. [DpSquared], |
| * [DpCubed], and [DpInverse] are used primarily for [Dp] calculations to |
| * ensure resulting units are as expected. Many times, [Dp] calculations use scalars to |
| * determine the final dimension during calculation: |
| * val width = oldWidth * stretchAmount |
| * Other times, it is useful to do intermediate calculations with Dimensions directly: |
| * val width = oldWidth * newTotalWidth / oldTotalWidth |
| */ |
| @Suppress("EXPERIMENTAL_FEATURE_WARNING") |
| @Immutable |
| inline class DpInverse(val value: Float) : Comparable<DpInverse> { |
| /** |
| * Add two DpInverse together. |
| */ |
| @Stable |
| inline operator fun plus(dimension: DpInverse) = |
| DpInverse(value = value + dimension.value) |
| |
| /** |
| * Subtract a DpInverse from another one. |
| */ |
| @Stable |
| inline operator fun minus(dimension: DpInverse) = |
| DpInverse(value = value - dimension.value) |
| |
| /** |
| * Divide a DpInverse by a scalar. |
| */ |
| @Stable |
| inline operator fun div(other: Float): DpInverse = |
| DpInverse(value = value / other) |
| |
| /** |
| * Multiply by a scalar to get a DpInverse result. |
| */ |
| @Stable |
| inline operator fun times(other: Float): DpInverse = |
| DpInverse(value = value * other) |
| |
| /** |
| * Multiply by a [Dp] to get a scalar result. |
| */ |
| @Stable |
| inline operator fun times(other: Dp): Float = value * other.value |
| |
| /** |
| * Multiply by a [DpSquared] to get a [Dp] result. |
| */ |
| @Stable |
| inline operator fun times(other: DpSquared): Dp = |
| Dp(value = value * other.value) |
| |
| /** |
| * Multiply by a [DpCubed] to get a [DpSquared] result. |
| */ |
| @Stable |
| inline operator fun times(other: DpCubed): DpSquared = |
| DpSquared(value = value * other.value) |
| |
| /** |
| * Support comparing DpInverse with comparison operators. |
| */ |
| @Stable |
| override /* TODO: inline */ operator fun compareTo(other: DpInverse) = |
| value.compareTo(other.value) |
| |
| @Stable |
| override fun toString(): String = "$value.dp^-1" |
| } |
| |
| // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
| // Structures using Dp |
| // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
| |
| /** |
| * A two-dimensional position using [Dp] for units |
| */ |
| @OptIn(ExperimentalUnsignedTypes::class) |
| @Suppress("EXPERIMENTAL_FEATURE_WARNING") |
| @Immutable |
| inline class Position(@PublishedApi internal val packedValue: Long) { |
| /** |
| * The horizontal aspect of the position in [Dp] |
| */ |
| @Stable |
| /*inline*/ val x: Dp |
| get() = unpackFloat1(packedValue).dp |
| |
| /** |
| * The vertical aspect of the position in [Dp] |
| */ |
| @Stable |
| /*inline*/ val y: Dp |
| get() = unpackFloat2(packedValue).dp |
| |
| /** |
| * Returns a copy of this Position instance optionally overriding the |
| * x or y parameter |
| */ |
| fun copy(x: Dp = this.x, y: Dp = this.y): Position = Position(x, y) |
| |
| /** |
| * Subtract a [Position] from another one. |
| */ |
| @Stable |
| inline operator fun minus(other: Position) = |
| Position(x - other.x, y - other.y) |
| |
| /** |
| * Add a [Position] to another one. |
| */ |
| @Stable |
| inline operator fun plus(other: Position) = |
| Position(x + other.x, y + other.y) |
| |
| @Stable |
| override fun toString(): String = "($x, $y)" |
| } |
| |
| /** |
| * Constructs a [Position] from [x] and [y] position [Dp] values. |
| */ |
| @OptIn(ExperimentalUnsignedTypes::class) |
| @Stable |
| inline fun Position(x: Dp, y: Dp): Position = Position(packFloats(x.value, y.value)) |
| |
| /** |
| * The magnitude of the offset represented by this [Position]. |
| */ |
| @Stable |
| fun Position.getDistance(): Dp { |
| return Dp(sqrt(x.value * x.value + y.value * y.value)) |
| } |
| |
| /** |
| * Linearly interpolate between two [Position]s. |
| * |
| * The [fraction] argument represents position on the timeline, with 0.0 meaning |
| * that the interpolation has not started, returning [start] (or something |
| * equivalent to [start]), 1.0 meaning that the interpolation has finished, |
| * returning [stop] (or something equivalent to [stop]), and values in between |
| * meaning that the interpolation is at the relevant point on the timeline |
| * between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and |
| * 1.0, so negative values and values greater than 1.0 are valid. |
| */ |
| @Stable |
| fun lerp(start: Position, stop: Position, fraction: Float): Position = |
| Position(lerp(start.x, stop.x, fraction), lerp(start.y, stop.y, fraction)) |
| |
| /** |
| * A four dimensional bounds using [Dp] for units |
| */ |
| @Immutable |
| data class Bounds( |
| @Stable |
| val left: Dp, |
| @Stable |
| val top: Dp, |
| @Stable |
| val right: Dp, |
| @Stable |
| val bottom: Dp |
| ) |
| |
| /** |
| * A width of this Bounds in [Dp]. |
| */ |
| @Stable |
| inline val Bounds.width: Dp get() = right - left |
| |
| /** |
| * A height of this Bounds in [Dp]. |
| */ |
| @Stable |
| inline val Bounds.height: Dp get() = bottom - top |