WideButtonDefaults.kt

/*
 * Copyright 2023 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.tv.material3

import androidx.annotation.FloatRange
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsFocusedAsState
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.dp

internal object BaseWideButtonDefaults {
    const val SubtitleAlpha = 0.8f
    val MinWidth = 240.dp
    val MinHeight = 48.dp
    val MinHeightWithSubtitle = 64.dp
    val HorizontalContentGap = 12.dp
    val VerticalContentGap = 4.dp
}

@ExperimentalTvMaterial3Api
object WideButtonDefaults {
    private val HorizontalPadding = 16.dp
    private val VerticalPadding = 10.dp

    /** The default content padding used by [WideButton] */
    internal val ContentPadding = PaddingValues(
        start = HorizontalPadding,
        top = VerticalPadding,
        end = HorizontalPadding,
        bottom = VerticalPadding
    )

    private val ContainerShape = RoundedCornerShape(12.dp)

    /**
     * Default background for a [WideButton]
     */
    @Composable
    fun Background(
        enabled: Boolean,
        interactionSource: MutableInteractionSource,
    ) {
        val isFocused = interactionSource.collectIsFocusedAsState().value
        val isPressed = interactionSource.collectIsPressedAsState().value

        val backgroundColor = when {
            !enabled -> MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.4f)
            isPressed -> MaterialTheme.colorScheme.onSurface
            isFocused -> MaterialTheme.colorScheme.onSurface
            else -> MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.4f)
        }

        Box(modifier = Modifier
            .fillMaxSize()
            .background(backgroundColor))
    }

    /**
     * Creates a [ButtonShape] that represents the default container shapes used in a [WideButton]
     *
     * @param shape the shape used when the Button is enabled, and has no other [Interaction]s
     * @param focusedShape the shape used when the Button is enabled and focused
     * @param pressedShape the shape used when the Button is enabled pressed
     * @param disabledShape the shape used when the Button is not enabled
     * @param focusedDisabledShape the shape used when the Button is not enabled and focused
     */
    fun shape(
        shape: Shape = ContainerShape,
        focusedShape: Shape = shape,
        pressedShape: Shape = shape,
        disabledShape: Shape = shape,
        focusedDisabledShape: Shape = disabledShape
    ) = ButtonShape(
        shape = shape,
        focusedShape = focusedShape,
        pressedShape = pressedShape,
        disabledShape = disabledShape,
        focusedDisabledShape = focusedDisabledShape
    )

    /**
     * Creates a [WideButtonContentColor] that represents the default content colors used in a
     * [WideButton]
     *
     * @param color the content color of this Button when enabled
     * @param focusedColor the content color of this Button when enabled and focused
     * @param pressedColor the content color of this Button when enabled and pressed
     * @param disabledColor the content color of this Button when not enabled
     */
    @ReadOnlyComposable
    @Composable
    fun contentColor(
        color: Color = MaterialTheme.colorScheme.onSurface,
        focusedColor: Color = MaterialTheme.colorScheme.inverseOnSurface,
        pressedColor: Color = focusedColor,
        disabledColor: Color = color
    ) = WideButtonContentColor(
        contentColor = color,
        focusedContentColor = focusedColor,
        pressedContentColor = pressedColor,
        disabledContentColor = disabledColor
    )

    /**
     * Creates a [ButtonScale] that represents the default scales used in a [WideButton].
     * Scale is used to modify the size of a composable in different [Interaction]
     * states e.g. 1f (original) in default state, 1.2f (scaled up) in focused state,
     * 0.8f (scaled down) in pressed state, etc.
     *
     * @param scale the scale to be used for this Button when enabled
     * @param focusedScale the scale to be used for this Button when focused
     * @param pressedScale the scale to be used for this Button when pressed
     * @param disabledScale the scale to be used for this Button when disabled
     * @param focusedDisabledScale the scale to be used for this Button when disabled and
     * focused
     */
    fun scale(
        @FloatRange(from = 0.0) scale: Float = 1f,
        @FloatRange(from = 0.0) focusedScale: Float = 1.1f,
        @FloatRange(from = 0.0) pressedScale: Float = scale,
        @FloatRange(from = 0.0) disabledScale: Float = scale,
        @FloatRange(from = 0.0) focusedDisabledScale: Float = disabledScale
    ) = ButtonScale(
        scale = scale,
        focusedScale = focusedScale,
        pressedScale = pressedScale,
        disabledScale = disabledScale,
        focusedDisabledScale = focusedDisabledScale
    )

    /**
     * Creates a [ButtonBorder] that represents the default [Border]s applied on a
     * [WideButton] in different [Interaction] states
     *
     * @param border the [Border] to be used for this Button when enabled
     * @param focusedBorder the [Border] to be used for this Button when focused
     * @param pressedBorder the [Border] to be used for this Button when pressed
     * @param disabledBorder the [Border] to be used for this Button when disabled
     * @param focusedDisabledBorder the [Border] to be used for this Button when disabled and
     * focused
     */
    @ReadOnlyComposable
    @Composable
    fun border(
        border: Border = Border.None,
        focusedBorder: Border = border,
        pressedBorder: Border = focusedBorder,
        disabledBorder: Border = border,
        focusedDisabledBorder: Border = Border(
            border = BorderStroke(
                width = 2.dp,
                color = MaterialTheme.colorScheme.border
            ),
            inset = 0.dp,
            shape = ContainerShape
        )
    ) = ButtonBorder(
        border = border,
        focusedBorder = focusedBorder,
        pressedBorder = pressedBorder,
        disabledBorder = disabledBorder,
        focusedDisabledBorder = focusedDisabledBorder
    )

    /**
     * Creates a [ButtonGlow] that represents the default [Glow]s used in a [WideButton]
     *
     * @param glow the Glow behind this Button when enabled
     * @param focusedGlow the Glow behind this Button when focused
     * @param pressedGlow the Glow behind this Button when pressed
     */
    fun glow(
        glow: Glow = Glow.None,
        focusedGlow: Glow = glow,
        pressedGlow: Glow = glow
    ) = ButtonGlow(
        glow = glow,
        focusedGlow = focusedGlow,
        pressedGlow = pressedGlow
    )
}