DpConstraints.kt

/*
 * 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("DEPRECATION")

package androidx.compose.foundation.layout

import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.coerceAtLeast
import androidx.compose.ui.unit.coerceIn
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.isFinite

/**
 * Similar to [Constraints], but with constraint values expressed in [Dp].
 */
@Immutable
@Deprecated("DpConstraints and APIs using it will be removed.")
data class DpConstraints(
    @Stable
    val minWidth: Dp = 0.dp,
    @Stable
    val maxWidth: Dp = Dp.Infinity,
    @Stable
    val minHeight: Dp = 0.dp,
    @Stable
    val maxHeight: Dp = Dp.Infinity
) {
    init {
        require(minWidth.isFinite()) { "Constraints#minWidth should be finite" }
        require(minHeight.isFinite()) { "Constraints#minHeight should be finite" }
        require(!minWidth.value.isNaN()) { "Constraints#minWidth should not be NaN" }
        require(!maxWidth.value.isNaN()) { "Constraints#maxWidth should not be NaN" }
        require(!minHeight.value.isNaN()) { "Constraints#minHeight should not be NaN" }
        require(!maxHeight.value.isNaN()) { "Constraints#maxHeight should not be NaN" }
        require(minWidth <= maxWidth) {
            "Constraints should be satisfiable, but minWidth > maxWidth"
        }
        require(minHeight <= maxHeight) {
            "Constraints should be satisfiable, but minHeight > maxHeight"
        }
        require(minWidth >= 0.dp) { "Constraints#minWidth should be non-negative" }
        require(maxWidth >= 0.dp) { "Constraints#maxWidth should be non-negative" }
        require(minHeight >= 0.dp) { "Constraints#minHeight should be non-negative" }
        require(maxHeight >= 0.dp) { "Constraints#maxHeight should be non-negative" }
    }

    companion object {
        /**
         * Creates constraints tight in both dimensions.
         */
        @Stable
        @Deprecated("DpConstraints and APIs using it will be removed.")
        fun fixed(width: Dp, height: Dp) = DpConstraints(width, width, height, height)

        /**
         * Creates constraints with tight width and loose height.
         */
        @Stable
        @Deprecated("DpConstraints and APIs using it will be removed.")
        fun fixedWidth(width: Dp) = DpConstraints(
            minWidth = width,
            maxWidth = width,
            minHeight = 0.dp,
            maxHeight = Dp.Infinity
        )

        /**
         * Creates constraints with tight height and loose width.
         */
        @Stable
        @Deprecated("DpConstraints and APIs using it will be removed.")
        fun fixedHeight(height: Dp) = DpConstraints(
            minWidth = 0.dp,
            maxWidth = Dp.Infinity,
            minHeight = height,
            maxHeight = height
        )
    }
}

/**
 * Whether or not the upper bound on the maximum height.
 * @see hasBoundedWidth
 */
@Stable
@Deprecated("DpConstraints and APIs using it will be removed.")
val DpConstraints.hasBoundedHeight get() = maxHeight.isFinite()

/**
 * Whether or not the upper bound on the maximum width.
 * @see hasBoundedHeight
 */
@Stable
@Deprecated("DpConstraints and APIs using it will be removed.")
val DpConstraints.hasBoundedWidth get() = maxWidth.isFinite()

/**
 * Whether there is exactly one width value that satisfies the constraints.
 */
@Stable
@Deprecated("DpConstraints and APIs using it will be removed.")
val DpConstraints.hasFixedWidth get() = maxWidth == minWidth

/**
 * Whether there is exactly one height value that satisfies the constraints.
 */
@Stable
@Deprecated("DpConstraints and APIs using it will be removed.")
val DpConstraints.hasFixedHeight get() = maxHeight == minHeight

/**
 * Whether the area of a component respecting these constraints will definitely be 0.
 * This is true when at least one of maxWidth and maxHeight are 0.
 */
@Stable
@Deprecated("DpConstraints and APIs using it will be removed.")
val DpConstraints.isZero get() = maxWidth == 0.dp || maxHeight == 0.dp

/**
 * Whether there is any size that satisfies the current constraints.
 */
@Stable
@Deprecated("DpConstraints and APIs using it will be removed.")
val DpConstraints.satisfiable get() = minWidth <= maxWidth && minHeight <= maxHeight

/**
 * Returns the result of coercing the current constraints in a different set of constraints.
 */
@Stable
@Deprecated("DpConstraints and APIs using it will be removed.")
fun DpConstraints.enforce(otherConstraints: DpConstraints) = DpConstraints(
    minWidth = minWidth.coerceIn(otherConstraints.minWidth, otherConstraints.maxWidth),
    maxWidth = maxWidth.coerceIn(otherConstraints.minWidth, otherConstraints.maxWidth),
    minHeight = minHeight.coerceIn(otherConstraints.minHeight, otherConstraints.maxHeight),
    maxHeight = maxHeight.coerceIn(otherConstraints.minHeight, otherConstraints.maxHeight)
)

/**
 * Returns the DpConstraints obtained by offsetting the current instance with the given values.
 */
@Stable
@Deprecated("DpConstraints and APIs using it will be removed.")
fun DpConstraints.offset(horizontal: Dp = 0.dp, vertical: Dp = 0.dp) = DpConstraints(
    (minWidth + horizontal).coerceAtLeast(0.dp),
    (maxWidth + horizontal).coerceAtLeast(0.dp),
    (minHeight + vertical).coerceAtLeast(0.dp),
    (maxHeight + vertical).coerceAtLeast(0.dp)
)

/**
 * Creates the [Constraints] corresponding to the current [DpConstraints].
 */
@Stable
@Deprecated("DpConstraints and APIs using it will be removed.")
fun Density.Constraints(dpConstraints: DpConstraints) = Constraints(
    minWidth = dpConstraints.minWidth.toIntPx(),
    maxWidth = dpConstraints.maxWidth.toIntPx(),
    minHeight = dpConstraints.minHeight.toIntPx(),
    maxHeight = dpConstraints.maxHeight.toIntPx()
)

/**
 * Creates the [DpConstraints] corresponding to the current [Constraints].
 */
@Stable
@Deprecated("DpConstraints and APIs using it will be removed.")
fun Density.DpConstraints(constraints: Constraints) = DpConstraints(
    minWidth = constraints.minWidth.toDp(),
    maxWidth = constraints.maxWidth.toDp(),
    minHeight = constraints.minHeight.toDp(),
    maxHeight = constraints.maxHeight.toDp()
)