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