WindowInsetsPadding.android.kt

/*
 * Copyright 2022 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.
 */

package androidx.compose.foundation.layout

import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.platform.debugInspectorInfo

/**
 * Adds padding to accommodate the [safe drawing][WindowInsets.Companion.safeDrawing] insets.
 *
 * Any insets consumed by other insets padding modifiers or [consumeWindowInsets] on a parent layout
 * will be excluded from the padding. [WindowInsets.Companion.safeDrawing] will be
 * [consumed][consumeWindowInsets] for child layouts as well.
 *
 * For example, if a parent layout uses [statusBarsPadding], the area that the parent
 * pads for the status bars will not be padded again by this [safeDrawingPadding] modifier.
 *
 * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
 *
 * @sample androidx.compose.foundation.layout.samples.safeDrawingPaddingSample
 */
fun Modifier.safeDrawingPadding() =
    windowInsetsPadding(debugInspectorInfo { name = "safeDrawingPadding" }) { safeDrawing }

/**
 * Adds padding to accommodate the [safe gestures][WindowInsets.Companion.safeGestures] insets.
 *
 * Any insets consumed by other insets padding modifiers or [consumeWindowInsets] on a parent layout
 * will be excluded from the padding. [WindowInsets.Companion.safeGestures] will be
 * [consumed][consumeWindowInsets] for child layouts as well.
 *
 * For example, if a parent layout uses [navigationBarsPadding],
 * the area that the parent layout pads for the status bars will not be padded again by this
 * [safeGesturesPadding] modifier.
 *
 * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
 *
 * @sample androidx.compose.foundation.layout.samples.safeGesturesPaddingSample
 */
fun Modifier.safeGesturesPadding() =
    windowInsetsPadding(debugInspectorInfo { name = "safeGesturesPadding" }) { safeGestures }

/**
 * Adds padding to accommodate the [safe content][WindowInsets.Companion.safeContent] insets.
 *
 * Any insets consumed by other insets padding modifiers or [consumeWindowInsets] on a parent layout
 * will be excluded from the padding. [WindowInsets.Companion.safeContent] will be
 * [consumed][consumeWindowInsets] for child layouts as well.
 *
 * For example, if a parent layout uses [navigationBarsPadding],
 * the area that the parent layout pads for the status bars will not be padded again by this
 * [safeContentPadding] modifier.
 *
 * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
 *
 * @sample androidx.compose.foundation.layout.samples.safeContentPaddingSample
 */
fun Modifier.safeContentPadding() =
    windowInsetsPadding(debugInspectorInfo { name = "safeContentPadding" }) { safeContent }

/**
 * Adds padding to accommodate the [system bars][WindowInsets.Companion.systemBars] insets.
 *
 * Any insets consumed by other insets padding modifiers or [consumeWindowInsets] on a parent layout
 * will be excluded from the padding. [WindowInsets.Companion.systemBars] will be
 * [consumed][consumeWindowInsets] for child layouts as well.
 *
 * For example, if a parent layout uses [statusBarsPadding], the
 * area that the parent layout pads for the status bars will not be padded again by this
 * [systemBarsPadding] modifier.
 *
 * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
 *
 * @sample androidx.compose.foundation.layout.samples.systemBarsPaddingSample
 */
fun Modifier.systemBarsPadding() =
    windowInsetsPadding(debugInspectorInfo { name = "systemBarsPadding" }) { systemBars }

/**
 * Adds padding to accommodate the [display cutout][WindowInsets.Companion.displayCutout].
 *
 * Any insets consumed by other insets padding modifiers or [consumeWindowInsets] on a parent layout
 * will be excluded from the padding. [WindowInsets.Companion.displayCutout] will be
 * [consumed][consumeWindowInsets] for child layouts as well.
 *
 * For example, if a parent layout uses [statusBarsPadding], the
 * area that the parent layout pads for the status bars will not be padded again by this
 * [displayCutoutPadding] modifier.
 *
 * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
 *
 * @sample androidx.compose.foundation.layout.samples.displayCutoutPaddingSample
 */
fun Modifier.displayCutoutPadding() =
    windowInsetsPadding(debugInspectorInfo { name = "displayCutoutPadding" }) { displayCutout }

/**
 * Adds padding to accommodate the [status bars][WindowInsets.Companion.statusBars] insets.
 *
 * Any insets consumed by other insets padding modifiers or [consumeWindowInsets] on a parent layout
 * will be excluded from the padding. [WindowInsets.Companion.statusBars] will be
 * [consumed][consumeWindowInsets] for child layouts as well.
 *
 * For example, if a parent layout uses [displayCutoutPadding], the
 * area that the parent layout pads for the status bars will not be padded again by this
 * [statusBarsPadding] modifier.
 *
 * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
 *
 * @sample androidx.compose.foundation.layout.samples.statusBarsAndNavigationBarsPaddingSample
 */
fun Modifier.statusBarsPadding() =
    windowInsetsPadding(debugInspectorInfo { name = "statusBarsPadding" }) { statusBars }

/**
 * Adds padding to accommodate the [ime][WindowInsets.Companion.ime] insets.
 *
 * Any insets consumed by other insets padding modifiers or [consumeWindowInsets] on a parent layout
 * will be excluded from the padding. [WindowInsets.Companion.ime] will be
 * [consumed][consumeWindowInsets] for child layouts as well.
 *
 * For example, if a parent layout uses [navigationBarsPadding],
 * the area that the parent layout pads for the status bars will not be padded again by this
 * [imePadding] modifier.
 *
 * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
 *
 * @sample androidx.compose.foundation.layout.samples.imePaddingSample
 */
fun Modifier.imePadding() =
    windowInsetsPadding(debugInspectorInfo { name = "imePadding" }) { ime }

/**
 * Adds padding to accommodate the [navigation bars][WindowInsets.Companion.navigationBars] insets.
 *
 * Any insets consumed by other insets padding modifiers or [consumeWindowInsets] on a parent layout
 * will be excluded from the padding. [WindowInsets.Companion.navigationBars] will be
 * [consumed][consumeWindowInsets] for child layouts as well.
 *
 * For example, if a parent layout uses [systemBarsPadding], the
 * area that the parent layout pads for the status bars will not be padded again by this
 * [navigationBarsPadding] modifier.
 *
 * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
 *
 * @sample androidx.compose.foundation.layout.samples.statusBarsAndNavigationBarsPaddingSample
 */
fun Modifier.navigationBarsPadding() =
    windowInsetsPadding(debugInspectorInfo { name = "navigationBarsPadding" }) { navigationBars }

/**
 * Adds padding to accommodate the [caption bar][WindowInsets.Companion.captionBar] insets.
 *
 * Any insets consumed by other insets padding modifiers or [consumeWindowInsets] on a parent layout
 * will be excluded from the padding. [WindowInsets.Companion.captionBar] will be
 * [consumed][consumeWindowInsets] for child layouts as well.
 *
 * For example, if a parent layout uses [displayCutoutPadding], the
 * area that the parent layout pads for the status bars will not be padded again by this
 * [captionBarPadding] modifier.
 *
 * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
 *
 * @sample androidx.compose.foundation.layout.samples.captionBarPaddingSample
 */
fun Modifier.captionBarPadding() =
    windowInsetsPadding(debugInspectorInfo { name = "captionBarPadding" }) { captionBar }

/**
 * Adds padding to accommodate the [waterfall][WindowInsets.Companion.waterfall] insets.
 *
 * Any insets consumed by other insets padding modifiers or [consumeWindowInsets] on a parent layout
 * will be excluded from the padding. [WindowInsets.Companion.waterfall] will be
 * [consumed][consumeWindowInsets] for child layouts as well.
 *
 * For example, if a parent layout uses [systemGesturesPadding],
 * the area that the parent layout pads for the status bars will not be padded again by this
 * [waterfallPadding] modifier.
 *
 * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
 *
 * @sample androidx.compose.foundation.layout.samples.waterfallPaddingSample
 */
fun Modifier.waterfallPadding() =
    windowInsetsPadding(debugInspectorInfo { name = "waterfallPadding" }) { waterfall }

/**
 * Adds padding to accommodate the [system gestures][WindowInsets.Companion.systemGestures] insets.
 *
 * Any insets consumed by other insets padding modifiers or [consumeWindowInsets] on a parent layout
 * will be excluded from the padding. [WindowInsets.Companion.systemGestures] will be
 * [consumed][consumeWindowInsets] for child layouts as well.
 *
 * For example, if a parent layout uses [waterfallPadding], the
 * area that the parent layout pads for the status bars will not be padded again by this
 * [systemGesturesPadding] modifier.
 *
 * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
 *
 * @sample androidx.compose.foundation.layout.samples.systemGesturesPaddingSample
 */
fun Modifier.systemGesturesPadding() =
    windowInsetsPadding(debugInspectorInfo { name = "systemGesturesPadding" }) { systemGestures }

/**
 * Adds padding to accommodate the
 * [mandatory system gestures][WindowInsets.Companion.mandatorySystemGestures] insets.
 *
 * Any insets consumed by other insets padding modifiers or [consumeWindowInsets] on a parent layout
 * will be excluded from the padding. [WindowInsets.Companion.mandatorySystemGestures] will be
 * [consumed][consumeWindowInsets] for child layouts as well.
 *
 * For example, if a parent layout uses [navigationBarsPadding],
 * the area that the parent layout pads for the status bars will not be padded again by this
 * [mandatorySystemGesturesPadding] modifier.
 *
 * When used, the [WindowInsets][android.view.WindowInsets] will be consumed.
 *
 * @sample androidx.compose.foundation.layout.samples.mandatorySystemGesturesPaddingSample
 */
fun Modifier.mandatorySystemGesturesPadding() =
    windowInsetsPadding(debugInspectorInfo { name = "mandatorySystemGesturesPadding" }) {
        mandatorySystemGestures
    }

@Suppress("NOTHING_TO_INLINE", "ModifierInspectorInfo")
@Stable
private inline fun Modifier.windowInsetsPadding(
    noinline inspectorInfo: InspectorInfo.() -> Unit,
    crossinline insetsCalculation: WindowInsetsHolder.() -> WindowInsets
): Modifier = composed(inspectorInfo) {
    val composeInsets = WindowInsetsHolder.current()
    remember(composeInsets) {
        val insets = composeInsets.insetsCalculation()
        InsetsPaddingModifier(insets)
    }
}