1 /* 2 * Copyright 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.tv.material3 18 19 import androidx.annotation.FloatRange 20 import androidx.compose.foundation.BorderStroke 21 import androidx.compose.foundation.background 22 import androidx.compose.foundation.interaction.Interaction 23 import androidx.compose.foundation.interaction.MutableInteractionSource 24 import androidx.compose.foundation.interaction.collectIsFocusedAsState 25 import androidx.compose.foundation.interaction.collectIsPressedAsState 26 import androidx.compose.foundation.layout.Box 27 import androidx.compose.foundation.layout.PaddingValues 28 import androidx.compose.foundation.layout.fillMaxSize 29 import androidx.compose.foundation.shape.RoundedCornerShape 30 import androidx.compose.runtime.Composable 31 import androidx.compose.runtime.ReadOnlyComposable 32 import androidx.compose.ui.Modifier 33 import androidx.compose.ui.graphics.Color 34 import androidx.compose.ui.graphics.Shape 35 import androidx.compose.ui.unit.dp 36 37 internal object BaseWideButtonDefaults { 38 const val SubtitleAlpha = 0.8f 39 val MinWidth = 240.dp 40 val MinHeight = 48.dp 41 val MinHeightWithSubtitle = 64.dp 42 val HorizontalContentGap = 12.dp 43 val VerticalContentGap = 4.dp 44 } 45 46 object WideButtonDefaults { 47 private val HorizontalPadding = 16.dp 48 private val VerticalPadding = 10.dp 49 50 /** The default content padding used by [WideButton] */ 51 internal val ContentPadding = 52 PaddingValues( 53 start = HorizontalPadding, 54 top = VerticalPadding, 55 end = HorizontalPadding, 56 bottom = VerticalPadding 57 ) 58 59 private val ContainerShape = RoundedCornerShape(12.dp) 60 61 /** Default background for a [WideButton] */ 62 @Composable Backgroundnull63 fun Background( 64 enabled: Boolean, 65 interactionSource: MutableInteractionSource, 66 ) { 67 val isFocused = interactionSource.collectIsFocusedAsState().value 68 val isPressed = interactionSource.collectIsPressedAsState().value 69 70 val backgroundColor = 71 when { 72 !enabled -> MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.4f) 73 isPressed -> MaterialTheme.colorScheme.onSurface 74 isFocused -> MaterialTheme.colorScheme.onSurface 75 else -> MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.4f) 76 } 77 78 Box(modifier = Modifier.fillMaxSize().background(backgroundColor)) 79 } 80 81 /** 82 * Creates a [ButtonShape] that represents the default container shapes used in a [WideButton] 83 * 84 * @param shape the shape used when the Button is enabled, and has no other [Interaction]s 85 * @param focusedShape the shape used when the Button is enabled and focused 86 * @param pressedShape the shape used when the Button is enabled pressed 87 * @param disabledShape the shape used when the Button is not enabled 88 * @param focusedDisabledShape the shape used when the Button is not enabled and focused 89 */ shapenull90 fun shape( 91 shape: Shape = ContainerShape, 92 focusedShape: Shape = shape, 93 pressedShape: Shape = shape, 94 disabledShape: Shape = shape, 95 focusedDisabledShape: Shape = disabledShape 96 ) = 97 ButtonShape( 98 shape = shape, 99 focusedShape = focusedShape, 100 pressedShape = pressedShape, 101 disabledShape = disabledShape, 102 focusedDisabledShape = focusedDisabledShape 103 ) 104 105 /** 106 * Creates a [WideButtonContentColor] that represents the default content colors used in a 107 * [WideButton] 108 * 109 * @param color the content color of this Button when enabled 110 * @param focusedColor the content color of this Button when enabled and focused 111 * @param pressedColor the content color of this Button when enabled and pressed 112 * @param disabledColor the content color of this Button when not enabled 113 */ 114 @ReadOnlyComposable 115 @Composable 116 fun contentColor( 117 color: Color = MaterialTheme.colorScheme.onSurface, 118 focusedColor: Color = MaterialTheme.colorScheme.inverseOnSurface, 119 pressedColor: Color = focusedColor, 120 disabledColor: Color = color 121 ) = 122 WideButtonContentColor( 123 contentColor = color, 124 focusedContentColor = focusedColor, 125 pressedContentColor = pressedColor, 126 disabledContentColor = disabledColor 127 ) 128 129 /** 130 * Creates a [ButtonScale] that represents the default scales used in a [WideButton]. Scale is 131 * used to modify the size of a composable in different [Interaction] states e.g. 1f (original) 132 * in default state, 1.2f (scaled up) in focused state, 0.8f (scaled down) in pressed state, 133 * etc. 134 * 135 * @param scale the scale to be used for this Button when enabled 136 * @param focusedScale the scale to be used for this Button when focused 137 * @param pressedScale the scale to be used for this Button when pressed 138 * @param disabledScale the scale to be used for this Button when disabled 139 * @param focusedDisabledScale the scale to be used for this Button when disabled and focused 140 */ 141 fun scale( 142 @FloatRange(from = 0.0) scale: Float = 1f, 143 @FloatRange(from = 0.0) focusedScale: Float = 1.1f, 144 @FloatRange(from = 0.0) pressedScale: Float = scale, 145 @FloatRange(from = 0.0) disabledScale: Float = scale, 146 @FloatRange(from = 0.0) focusedDisabledScale: Float = disabledScale 147 ) = 148 ButtonScale( 149 scale = scale, 150 focusedScale = focusedScale, 151 pressedScale = pressedScale, 152 disabledScale = disabledScale, 153 focusedDisabledScale = focusedDisabledScale 154 ) 155 156 /** 157 * Creates a [ButtonBorder] that represents the default [Border]s applied on a [WideButton] in 158 * different [Interaction] states 159 * 160 * @param border the [Border] to be used for this Button when enabled 161 * @param focusedBorder the [Border] to be used for this Button when focused 162 * @param pressedBorder the [Border] to be used for this Button when pressed 163 * @param disabledBorder the [Border] to be used for this Button when disabled 164 * @param focusedDisabledBorder the [Border] to be used for this Button when disabled and 165 * focused 166 */ 167 @ReadOnlyComposable 168 @Composable 169 fun border( 170 border: Border = Border.None, 171 focusedBorder: Border = border, 172 pressedBorder: Border = focusedBorder, 173 disabledBorder: Border = border, 174 focusedDisabledBorder: Border = 175 Border( 176 border = BorderStroke(width = 2.dp, color = MaterialTheme.colorScheme.border), 177 inset = 0.dp, 178 shape = ContainerShape 179 ) 180 ) = 181 ButtonBorder( 182 border = border, 183 focusedBorder = focusedBorder, 184 pressedBorder = pressedBorder, 185 disabledBorder = disabledBorder, 186 focusedDisabledBorder = focusedDisabledBorder 187 ) 188 189 /** 190 * Creates a [ButtonGlow] that represents the default [Glow]s used in a [WideButton] 191 * 192 * @param glow the Glow behind this Button when enabled 193 * @param focusedGlow the Glow behind this Button when focused 194 * @param pressedGlow the Glow behind this Button when pressed 195 */ 196 fun glow(glow: Glow = Glow.None, focusedGlow: Glow = glow, pressedGlow: Glow = glow) = 197 ButtonGlow(glow = glow, focusedGlow = focusedGlow, pressedGlow = pressedGlow) 198 } 199