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.interaction.Interaction
21 import androidx.compose.runtime.Immutable
22 import androidx.compose.ui.graphics.Color
23 import androidx.compose.ui.graphics.Shape
24 
25 /** Defines [Shape] for all TV [Interaction] states of Button. */
26 @Immutable
27 class ButtonShape
28 internal constructor(
29     internal val shape: Shape,
30     internal val focusedShape: Shape,
31     internal val pressedShape: Shape,
32     internal val disabledShape: Shape,
33     internal val focusedDisabledShape: Shape
34 ) {
equalsnull35     override fun equals(other: Any?): Boolean {
36         if (this === other) return true
37         if (other == null || this::class != other::class) return false
38 
39         other as ButtonShape
40 
41         if (shape != other.shape) return false
42         if (focusedShape != other.focusedShape) return false
43         if (pressedShape != other.pressedShape) return false
44         if (disabledShape != other.disabledShape) return false
45         if (focusedDisabledShape != other.focusedDisabledShape) return false
46 
47         return true
48     }
49 
hashCodenull50     override fun hashCode(): Int {
51         var result = shape.hashCode()
52         result = 31 * result + focusedShape.hashCode()
53         result = 31 * result + pressedShape.hashCode()
54         result = 31 * result + disabledShape.hashCode()
55         result = 31 * result + focusedDisabledShape.hashCode()
56 
57         return result
58     }
59 
toStringnull60     override fun toString(): String {
61         return "ButtonShape(shape=$shape, focusedShape=$focusedShape, pressedShape=$pressedShape," +
62             " disabledShape=$disabledShape, focusedDisabledShape=$focusedDisabledShape)"
63     }
64 }
65 
66 /** Defines [Color]s for all TV [Interaction] states of Button. */
67 @Immutable
68 class ButtonColors
69 internal constructor(
70     internal val containerColor: Color,
71     internal val contentColor: Color,
72     internal val focusedContainerColor: Color,
73     internal val focusedContentColor: Color,
74     internal val pressedContainerColor: Color,
75     internal val pressedContentColor: Color,
76     internal val disabledContainerColor: Color,
77     internal val disabledContentColor: Color,
78 ) {
equalsnull79     override fun equals(other: Any?): Boolean {
80         if (this === other) return true
81         if (other == null || this::class != other::class) return false
82 
83         other as ButtonColors
84 
85         if (containerColor != other.containerColor) return false
86         if (contentColor != other.contentColor) return false
87         if (focusedContainerColor != other.focusedContainerColor) return false
88         if (focusedContentColor != other.focusedContentColor) return false
89         if (pressedContainerColor != other.pressedContainerColor) return false
90         if (pressedContentColor != other.pressedContentColor) return false
91         if (disabledContainerColor != other.disabledContainerColor) return false
92         if (disabledContentColor != other.disabledContentColor) return false
93 
94         return true
95     }
96 
hashCodenull97     override fun hashCode(): Int {
98         var result = containerColor.hashCode()
99         result = 31 * result + contentColor.hashCode()
100         result = 31 * result + focusedContainerColor.hashCode()
101         result = 31 * result + focusedContentColor.hashCode()
102         result = 31 * result + pressedContainerColor.hashCode()
103         result = 31 * result + pressedContentColor.hashCode()
104         result = 31 * result + disabledContainerColor.hashCode()
105         result = 31 * result + disabledContentColor.hashCode()
106         return result
107     }
108 
toStringnull109     override fun toString(): String {
110         return "ButtonColors(containerColor=$containerColor, contentColor=$contentColor, " +
111             "focusedContainerColor=$focusedContainerColor, " +
112             "focusedContentColor=$focusedContentColor, " +
113             "pressedContainerColor=$pressedContainerColor, " +
114             "pressedContentColor=$pressedContentColor, " +
115             "disabledContainerColor=$disabledContainerColor, " +
116             "disabledContentColor=$disabledContentColor)"
117     }
118 }
119 
120 /** Defines [Color]s for all TV [Interaction] states of a WideButton */
121 @Immutable
122 class WideButtonContentColor
123 internal constructor(
124     internal val contentColor: Color,
125     internal val focusedContentColor: Color,
126     internal val pressedContentColor: Color,
127     internal val disabledContentColor: Color,
128 ) {
equalsnull129     override fun equals(other: Any?): Boolean {
130         if (this === other) return true
131         if (other == null || this::class != other::class) return false
132 
133         other as WideButtonContentColor
134 
135         if (contentColor != other.contentColor) return false
136         if (focusedContentColor != other.focusedContentColor) return false
137         if (pressedContentColor != other.pressedContentColor) return false
138         if (disabledContentColor != other.disabledContentColor) return false
139 
140         return true
141     }
142 
hashCodenull143     override fun hashCode(): Int {
144         var result = contentColor.hashCode()
145         result = 31 * result + focusedContentColor.hashCode()
146         result = 31 * result + pressedContentColor.hashCode()
147         result = 31 * result + disabledContentColor.hashCode()
148         return result
149     }
150 
toStringnull151     override fun toString(): String {
152         return "WideButtonContentColor(contentColor=$contentColor, " +
153             "focusedContentColor=$focusedContentColor, " +
154             "pressedContentColor=$pressedContentColor, " +
155             "disabledContentColor=$disabledContentColor)"
156     }
157 }
158 
159 /** Defines the scale for all TV [Interaction] states of Button. */
160 @Immutable
161 class ButtonScale
162 internal constructor(
163     @FloatRange(from = 0.0) internal val scale: Float,
164     @FloatRange(from = 0.0) internal val focusedScale: Float,
165     @FloatRange(from = 0.0) internal val pressedScale: Float,
166     @FloatRange(from = 0.0) internal val disabledScale: Float,
167     @FloatRange(from = 0.0) internal val focusedDisabledScale: Float
168 ) {
equalsnull169     override fun equals(other: Any?): Boolean {
170         if (this === other) return true
171         if (other == null || this::class != other::class) return false
172 
173         other as ButtonScale
174 
175         if (scale != other.scale) return false
176         if (focusedScale != other.focusedScale) return false
177         if (pressedScale != other.pressedScale) return false
178         if (disabledScale != other.disabledScale) return false
179         if (focusedDisabledScale != other.focusedDisabledScale) return false
180 
181         return true
182     }
183 
hashCodenull184     override fun hashCode(): Int {
185         var result = scale.hashCode()
186         result = 31 * result + focusedScale.hashCode()
187         result = 31 * result + pressedScale.hashCode()
188         result = 31 * result + disabledScale.hashCode()
189         result = 31 * result + focusedDisabledScale.hashCode()
190 
191         return result
192     }
193 
toStringnull194     override fun toString(): String {
195         return "ButtonScale(scale=$scale, focusedScale=$focusedScale, pressedScale=$pressedScale," +
196             " disabledScale=$disabledScale, focusedDisabledScale=$focusedDisabledScale)"
197     }
198 
199     companion object {
200         /** Signifies the absence of a [ScaleIndication] in Button component. */
201         val None =
202             ButtonScale(
203                 scale = 1f,
204                 focusedScale = 1f,
205                 pressedScale = 1f,
206                 disabledScale = 1f,
207                 focusedDisabledScale = 1f
208             )
209     }
210 }
211 
212 /** Defines [Border] for all TV [Interaction] states of Button. */
213 @Immutable
214 class ButtonBorder
215 internal constructor(
216     internal val border: Border,
217     internal val focusedBorder: Border,
218     internal val pressedBorder: Border,
219     internal val disabledBorder: Border,
220     internal val focusedDisabledBorder: Border
221 ) {
equalsnull222     override fun equals(other: Any?): Boolean {
223         if (this === other) return true
224         if (other == null || this::class != other::class) return false
225 
226         other as ButtonBorder
227 
228         if (border != other.border) return false
229         if (focusedBorder != other.focusedBorder) return false
230         if (pressedBorder != other.pressedBorder) return false
231         if (disabledBorder != other.disabledBorder) return false
232         if (focusedDisabledBorder != other.focusedDisabledBorder) return false
233 
234         return true
235     }
236 
hashCodenull237     override fun hashCode(): Int {
238         var result = border.hashCode()
239         result = 31 * result + focusedBorder.hashCode()
240         result = 31 * result + pressedBorder.hashCode()
241         result = 31 * result + disabledBorder.hashCode()
242         result = 31 * result + focusedDisabledBorder.hashCode()
243 
244         return result
245     }
246 
toStringnull247     override fun toString(): String {
248         return "ButtonBorder(border=$border, focusedBorder=$focusedBorder," +
249             "pressedBorder=$pressedBorder, disabledBorder=$disabledBorder, " +
250             "focusedDisabledBorder=$focusedDisabledBorder)"
251     }
252 }
253 
254 /** Defines [Glow] for all TV [Interaction] states of Button. */
255 @Immutable
256 class ButtonGlow
257 internal constructor(
258     internal val glow: Glow,
259     internal val focusedGlow: Glow,
260     internal val pressedGlow: Glow
261 ) {
equalsnull262     override fun equals(other: Any?): Boolean {
263         if (this === other) return true
264         if (other == null || this::class != other::class) return false
265 
266         other as ButtonGlow
267 
268         if (glow != other.glow) return false
269         if (focusedGlow != other.focusedGlow) return false
270         if (pressedGlow != other.pressedGlow) return false
271 
272         return true
273     }
274 
hashCodenull275     override fun hashCode(): Int {
276         var result = glow.hashCode()
277         result = 31 * result + focusedGlow.hashCode()
278         result = 31 * result + pressedGlow.hashCode()
279 
280         return result
281     }
282 
toStringnull283     override fun toString(): String {
284         return "ButtonGlow(glow=$glow, focusedGlow=$focusedGlow, pressedGlow=$pressedGlow)"
285     }
286 }
287 
288 private val WideButtonContainerColor = Color.Transparent
289 
toClickableSurfaceShapenull290 internal fun ButtonShape.toClickableSurfaceShape(): ClickableSurfaceShape =
291     ClickableSurfaceShape(
292         shape = shape,
293         focusedShape = focusedShape,
294         pressedShape = pressedShape,
295         disabledShape = disabledShape,
296         focusedDisabledShape = focusedDisabledShape
297     )
298 
299 internal fun ButtonColors.toClickableSurfaceColors(): ClickableSurfaceColors =
300     ClickableSurfaceColors(
301         containerColor = containerColor,
302         contentColor = contentColor,
303         focusedContainerColor = focusedContainerColor,
304         focusedContentColor = focusedContentColor,
305         pressedContainerColor = pressedContainerColor,
306         pressedContentColor = pressedContentColor,
307         disabledContainerColor = disabledContainerColor,
308         disabledContentColor = disabledContentColor
309     )
310 
311 internal fun WideButtonContentColor.toClickableSurfaceColors(): ClickableSurfaceColors =
312     ClickableSurfaceColors(
313         containerColor = WideButtonContainerColor,
314         contentColor = contentColor,
315         focusedContainerColor = WideButtonContainerColor,
316         focusedContentColor = focusedContentColor,
317         pressedContainerColor = WideButtonContainerColor,
318         pressedContentColor = pressedContentColor,
319         disabledContainerColor = WideButtonContainerColor,
320         disabledContentColor = disabledContentColor
321     )
322 
323 internal fun ButtonScale.toClickableSurfaceScale() =
324     ClickableSurfaceScale(
325         scale = scale,
326         focusedScale = focusedScale,
327         pressedScale = pressedScale,
328         disabledScale = disabledScale,
329         focusedDisabledScale = focusedDisabledScale
330     )
331 
332 internal fun ButtonBorder.toClickableSurfaceBorder() =
333     ClickableSurfaceBorder(
334         border = border,
335         focusedBorder = focusedBorder,
336         pressedBorder = pressedBorder,
337         disabledBorder = disabledBorder,
338         focusedDisabledBorder = focusedDisabledBorder
339     )
340 
341 internal fun ButtonGlow.toClickableSurfaceGlow() =
342     ClickableSurfaceGlow(glow = glow, focusedGlow = focusedGlow, pressedGlow = pressedGlow)
343