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.interaction.Interaction
22 import androidx.compose.foundation.layout.PaddingValues
23 import androidx.compose.foundation.shape.CircleShape
24 import androidx.compose.runtime.Composable
25 import androidx.compose.runtime.ReadOnlyComposable
26 import androidx.compose.ui.graphics.Color
27 import androidx.compose.ui.graphics.Shape
28 import androidx.compose.ui.unit.dp
29 
30 internal object BaseButtonDefaults {
31     val MinWidth = 58.dp
32     val MinHeight = 40.dp
33 }
34 
35 object ButtonDefaults {
36     private val ContainerShape = CircleShape
37     private val ButtonHorizontalPadding = 16.dp
38     private val ButtonVerticalPadding = 10.dp
39     private val ButtonWithIconHorizontalStartPadding = 12.dp
40 
41     val ContentPadding =
42         PaddingValues(
43             start = ButtonHorizontalPadding,
44             top = ButtonVerticalPadding,
45             end = ButtonHorizontalPadding,
46             bottom = ButtonVerticalPadding
47         )
48 
49     val ButtonWithIconContentPadding =
50         PaddingValues(
51             start = ButtonWithIconHorizontalStartPadding,
52             top = ButtonVerticalPadding,
53             end = ButtonHorizontalPadding,
54             bottom = ButtonVerticalPadding
55         )
56 
57     /** The default size of the icon when used inside any button. */
58     val IconSize = 20.dp
59 
60     /**
61      * The default size of the spacing between an icon and a text when they used inside any button.
62      */
63     val IconSpacing = 8.dp
64 
65     /**
66      * Creates a [ButtonShape] that represents the default container shapes used in a FilledButton.
67      *
68      * @param shape the shape used when the Button is enabled, and has no other [Interaction]s.
69      * @param focusedShape the shape used when the Button is enabled and focused.
70      * @param pressedShape the shape used when the Button is enabled pressed.
71      * @param disabledShape the shape used when the Button is not enabled.
72      * @param focusedDisabledShape the shape used when the Button is not enabled and focused.
73      */
shapenull74     fun shape(
75         shape: Shape = ContainerShape,
76         focusedShape: Shape = shape,
77         pressedShape: Shape = shape,
78         disabledShape: Shape = shape,
79         focusedDisabledShape: Shape = disabledShape
80     ) =
81         ButtonShape(
82             shape = shape,
83             focusedShape = focusedShape,
84             pressedShape = pressedShape,
85             disabledShape = disabledShape,
86             focusedDisabledShape = focusedDisabledShape
87         )
88 
89     /**
90      * Creates a [ButtonColors] that represents the default colors used in a FilledButton.
91      *
92      * @param containerColor the container color of this Button when enabled
93      * @param contentColor the content color of this Button when enabled
94      * @param focusedContainerColor the container color of this Button when enabled and focused
95      * @param focusedContentColor the content color of this Button when enabled and focused
96      * @param pressedContainerColor the container color of this Button when enabled and pressed
97      * @param pressedContentColor the content color of this Button when enabled and pressed
98      * @param disabledContainerColor the container color of this Button when not enabled
99      * @param disabledContentColor the content color of this Button when not enabled
100      */
101     @ReadOnlyComposable
102     @Composable
103     fun colors(
104         containerColor: Color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.8f),
105         contentColor: Color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f),
106         focusedContainerColor: Color = MaterialTheme.colorScheme.onSurface,
107         focusedContentColor: Color = MaterialTheme.colorScheme.inverseOnSurface,
108         pressedContainerColor: Color = focusedContainerColor,
109         pressedContentColor: Color = focusedContentColor,
110         disabledContainerColor: Color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.4f),
111         disabledContentColor: Color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.4f),
112     ) =
113         ButtonColors(
114             containerColor = containerColor,
115             contentColor = contentColor,
116             focusedContainerColor = focusedContainerColor,
117             focusedContentColor = focusedContentColor,
118             pressedContainerColor = pressedContainerColor,
119             pressedContentColor = pressedContentColor,
120             disabledContainerColor = disabledContainerColor,
121             disabledContentColor = disabledContentColor,
122         )
123 
124     /**
125      * Creates a [ButtonScale] that represents the default scales used in a FilledButton. scales are
126      * used to modify the size of a composable in different [Interaction] states e.g. 1f (original)
127      * in default state, 1.2f (scaled up) in focused state, 0.8f (scaled down) in pressed state,
128      * etc.
129      *
130      * @param scale the scale to be used for this Button when enabled
131      * @param focusedScale the scale to be used for this Button when focused
132      * @param pressedScale the scale to be used for this Button when pressed
133      * @param disabledScale the scale to be used for this Button when disabled
134      * @param focusedDisabledScale the scale to be used for this Button when disabled and focused
135      */
136     fun scale(
137         @FloatRange(from = 0.0) scale: Float = 1f,
138         @FloatRange(from = 0.0) focusedScale: Float = 1.1f,
139         @FloatRange(from = 0.0) pressedScale: Float = scale,
140         @FloatRange(from = 0.0) disabledScale: Float = scale,
141         @FloatRange(from = 0.0) focusedDisabledScale: Float = disabledScale
142     ) =
143         ButtonScale(
144             scale = scale,
145             focusedScale = focusedScale,
146             pressedScale = pressedScale,
147             disabledScale = disabledScale,
148             focusedDisabledScale = focusedDisabledScale
149         )
150 
151     /**
152      * Creates a [ButtonBorder] that represents the default [Border]s applied on a FilledButton in
153      * different [Interaction] states.
154      *
155      * @param border the [Border] to be used for this Button when enabled
156      * @param focusedBorder the [Border] to be used for this Button when focused
157      * @param pressedBorder the [Border] to be used for this Button when pressed
158      * @param disabledBorder the [Border] to be used for this Button when disabled
159      * @param focusedDisabledBorder the [Border] to be used for this Button when disabled and
160      *   focused
161      */
162     @ReadOnlyComposable
163     @Composable
164     fun border(
165         border: Border = Border.None,
166         focusedBorder: Border = border,
167         pressedBorder: Border = focusedBorder,
168         disabledBorder: Border = border,
169         focusedDisabledBorder: Border =
170             Border(
171                 border =
172                     BorderStroke(
173                         width = 1.5.dp,
174                         color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.2f)
175                     ),
176                 shape = ContainerShape
177             )
178     ) =
179         ButtonBorder(
180             border = border,
181             focusedBorder = focusedBorder,
182             pressedBorder = pressedBorder,
183             disabledBorder = disabledBorder,
184             focusedDisabledBorder = focusedDisabledBorder
185         )
186 
187     /**
188      * Creates a [ButtonGlow] that represents the default [Glow]s used in a FilledButton.
189      *
190      * @param glow the Glow behind this Button when enabled
191      * @param focusedGlow the Glow behind this Button when focused
192      * @param pressedGlow the Glow behind this Button when pressed
193      */
194     fun glow(glow: Glow = Glow.None, focusedGlow: Glow = glow, pressedGlow: Glow = glow) =
195         ButtonGlow(glow = glow, focusedGlow = focusedGlow, pressedGlow = pressedGlow)
196 }
197 
198 object OutlinedButtonDefaults {
199     private val ContainerShape = CircleShape
200     private val ButtonHorizontalPadding = 16.dp
201     private val ButtonVerticalPadding = 10.dp
202     private val ButtonWithIconHorizontalStartPadding = 12.dp
203 
204     val ContentPadding =
205         PaddingValues(
206             start = ButtonHorizontalPadding,
207             top = ButtonVerticalPadding,
208             end = ButtonHorizontalPadding,
209             bottom = ButtonVerticalPadding
210         )
211 
212     /** The default size of the icon when used inside any button. */
213     val IconSize = 20.dp
214 
215     /**
216      * The default size of the spacing between an icon and a text when they used inside any button.
217      */
218     val IconSpacing = 8.dp
219 
220     /** The default content padding used by [OutlinedButton] that contains an [Icon]. */
221     val ButtonWithIconContentPadding =
222         PaddingValues(
223             start = ButtonWithIconHorizontalStartPadding,
224             top = ButtonVerticalPadding,
225             end = ButtonHorizontalPadding,
226             bottom = ButtonVerticalPadding
227         )
228 
229     /**
230      * Creates a [ButtonShape] that represents the default container shapes used in an
231      * OutlinedButton.
232      *
233      * @param shape the shape used when the Button is enabled, and has no other [Interaction]s.
234      * @param focusedShape the shape used when the Button is enabled and focused.
235      * @param pressedShape the shape used when the Button is enabled pressed.
236      * @param disabledShape the shape used when the Button is not enabled.
237      * @param focusedDisabledShape the shape used when the Button is not enabled and focused.
238      */
239     fun shape(
240         shape: Shape = ContainerShape,
241         focusedShape: Shape = shape,
242         pressedShape: Shape = shape,
243         disabledShape: Shape = shape,
244         focusedDisabledShape: Shape = disabledShape
245     ) =
246         ButtonShape(
247             shape = shape,
248             focusedShape = focusedShape,
249             pressedShape = pressedShape,
250             disabledShape = disabledShape,
251             focusedDisabledShape = focusedDisabledShape
252         )
253 
254     /**
255      * Creates a [ButtonColors] that represents the default colors used in a OutlinedButton.
256      *
257      * @param containerColor the container color of this Button when enabled
258      * @param contentColor the content color of this Button when enabled
259      * @param focusedContainerColor the container color of this Button when enabled and focused
260      * @param focusedContentColor the content color of this Button when enabled and focused
261      * @param pressedContainerColor the container color of this Button when enabled and pressed
262      * @param pressedContentColor the content color of this Button when enabled and pressed
263      * @param disabledContainerColor the container color of this Button when not enabled
264      * @param disabledContentColor the content color of this Button when not enabled
265      */
266     @ReadOnlyComposable
267     @Composable
268     fun colors(
269         containerColor: Color = Color.Transparent,
270         contentColor: Color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f),
271         focusedContainerColor: Color = MaterialTheme.colorScheme.onSurface,
272         focusedContentColor: Color = MaterialTheme.colorScheme.inverseOnSurface,
273         pressedContainerColor: Color = focusedContainerColor,
274         pressedContentColor: Color = focusedContentColor,
275         disabledContainerColor: Color = containerColor,
276         disabledContentColor: Color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.4f),
277     ) =
278         ButtonColors(
279             containerColor = containerColor,
280             contentColor = contentColor,
281             focusedContainerColor = focusedContainerColor,
282             focusedContentColor = focusedContentColor,
283             pressedContainerColor = pressedContainerColor,
284             pressedContentColor = pressedContentColor,
285             disabledContainerColor = disabledContainerColor,
286             disabledContentColor = disabledContentColor,
287         )
288 
289     /**
290      * Creates a [ButtonScale] that represents the default scales used in an OutlinedButton. scales
291      * are used to modify the size of a composable in different [Interaction] states e.g. 1f
292      * (original) in default state, 1.2f (scaled up) in focused state, 0.8f (scaled down) in pressed
293      * state, etc.
294      *
295      * @param scale the scale to be used for this Button when enabled
296      * @param focusedScale the scale to be used for this Button when focused
297      * @param pressedScale the scale to be used for this Button when pressed
298      * @param disabledScale the scale to be used for this Button when disabled
299      * @param focusedDisabledScale the scale to be used for this Button when disabled and focused
300      */
301     fun scale(
302         @FloatRange(from = 0.0) scale: Float = 1f,
303         @FloatRange(from = 0.0) focusedScale: Float = 1.1f,
304         @FloatRange(from = 0.0) pressedScale: Float = scale,
305         @FloatRange(from = 0.0) disabledScale: Float = scale,
306         @FloatRange(from = 0.0) focusedDisabledScale: Float = disabledScale
307     ) =
308         ButtonScale(
309             scale = scale,
310             focusedScale = focusedScale,
311             pressedScale = pressedScale,
312             disabledScale = disabledScale,
313             focusedDisabledScale = focusedDisabledScale
314         )
315 
316     /**
317      * Creates a [ButtonBorder] that represents the default [Border]s applied on an OutlinedButton
318      * in different [Interaction] states.
319      *
320      * @param border the [Border] to be used for this Button when enabled
321      * @param focusedBorder the [Border] to be used for this Button when focused
322      * @param pressedBorder the [Border] to be used for this Button when pressed
323      * @param disabledBorder the [Border] to be used for this Button when disabled
324      * @param focusedDisabledBorder the [Border] to be used for this Button when disabled and
325      *   focused
326      */
327     @ReadOnlyComposable
328     @Composable
329     fun border(
330         border: Border =
331             Border(
332                 border =
333                     BorderStroke(
334                         width = 1.5.dp,
335                         color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.4f)
336                     ),
337                 shape = ContainerShape
338             ),
339         focusedBorder: Border =
340             Border(
341                 border =
342                     BorderStroke(
343                         width = 1.65.dp,
344                         color = MaterialTheme.colorScheme.onSurfaceVariant
345                     ),
346                 shape = ContainerShape
347             ),
348         pressedBorder: Border =
349             Border(
350                 border =
351                     BorderStroke(
352                         width = 1.5.dp,
353                         color = MaterialTheme.colorScheme.onSurfaceVariant
354                     ),
355                 shape = ContainerShape
356             ),
357         disabledBorder: Border =
358             Border(
359                 border =
360                     BorderStroke(
361                         width = 1.5.dp,
362                         color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.2f)
363                     ),
364                 shape = ContainerShape
365             ),
366         focusedDisabledBorder: Border = disabledBorder
367     ) =
368         ButtonBorder(
369             border = border,
370             focusedBorder = focusedBorder,
371             pressedBorder = pressedBorder,
372             disabledBorder = disabledBorder,
373             focusedDisabledBorder = focusedDisabledBorder
374         )
375 
376     /**
377      * Creates a [ButtonGlow] that represents the default [Glow]s used in an OutlinedButton.
378      *
379      * @param glow the Glow behind this Button when enabled
380      * @param focusedGlow the Glow behind this Button when focused
381      * @param pressedGlow the Glow behind this Button when pressed
382      */
383     fun glow(glow: Glow = Glow.None, focusedGlow: Glow = glow, pressedGlow: Glow = glow) =
384         ButtonGlow(glow = glow, focusedGlow = focusedGlow, pressedGlow = pressedGlow)
385 }
386