1 /*
2  * Copyright 2022 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.compose.material
18 
19 import androidx.compose.foundation.BorderStroke
20 import androidx.compose.foundation.background
21 import androidx.compose.foundation.interaction.Interaction
22 import androidx.compose.foundation.interaction.MutableInteractionSource
23 import androidx.compose.foundation.layout.Arrangement
24 import androidx.compose.foundation.layout.Box
25 import androidx.compose.foundation.layout.IntrinsicSize
26 import androidx.compose.foundation.layout.Row
27 import androidx.compose.foundation.layout.RowScope
28 import androidx.compose.foundation.layout.Spacer
29 import androidx.compose.foundation.layout.defaultMinSize
30 import androidx.compose.foundation.layout.padding
31 import androidx.compose.foundation.layout.requiredSize
32 import androidx.compose.foundation.layout.width
33 import androidx.compose.foundation.shape.CircleShape
34 import androidx.compose.foundation.shape.CornerSize
35 import androidx.compose.runtime.Composable
36 import androidx.compose.runtime.CompositionLocalProvider
37 import androidx.compose.runtime.Immutable
38 import androidx.compose.runtime.Stable
39 import androidx.compose.runtime.State
40 import androidx.compose.runtime.getValue
41 import androidx.compose.runtime.rememberUpdatedState
42 import androidx.compose.ui.Alignment
43 import androidx.compose.ui.Modifier
44 import androidx.compose.ui.draw.clip
45 import androidx.compose.ui.graphics.Color
46 import androidx.compose.ui.graphics.Shape
47 import androidx.compose.ui.graphics.compositeOver
48 import androidx.compose.ui.semantics.Role
49 import androidx.compose.ui.semantics.role
50 import androidx.compose.ui.semantics.semantics
51 import androidx.compose.ui.unit.dp
52 
53 /**
54  * [Material Design action chip](https://material.io/components/chips#action-chips).
55  *
56  * Action chips offer actions related to primary content. They should appear dynamically and
57  * contextually in a UI.
58  *
59  * @sample androidx.compose.material.samples.ChipSample
60  *
61  * You can create an [outlined action chip](https://material.io/components/chips#action-chips) using
62  * [ChipDefaults.outlinedChipColors] and [ChipDefaults.outlinedBorder]
63  *
64  * @sample androidx.compose.material.samples.OutlinedChipWithIconSample
65  *
66  * Action chips should appear in a set and can be horizontally scrollable
67  *
68  * @sample androidx.compose.material.samples.ChipGroupSingleLineSample
69  *
70  * Alternatively, use [androidx.compose.foundation.layout.FlowRow] to wrap chips to a new line.
71  *
72  * @sample androidx.compose.material.samples.ChipGroupReflowSample
73  * @param onClick called when the chip is clicked.
74  * @param modifier Modifier to be applied to the chip
75  * @param enabled When disabled, chip will not respond to user input. It will also appear visually
76  *   disabled and disabled to accessibility services.
77  * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
78  *   emitting [Interaction]s for this chip. You can use this to change the chip's appearance or
79  *   preview the chip in different states. Note that if `null` is provided, interactions will still
80  *   happen internally.
81  * @param shape defines the chip's shape as well as its shadow
82  * @param border Border to draw around the chip. Pass `null` here for no border.
83  * @param colors [ChipColors] that will be used to resolve the background and content color for this
84  *   chip in different states. See [ChipDefaults.chipColors].
85  * @param leadingIcon Optional icon at the start of the chip, preceding the content text.
86  * @param content the content of this chip
87  */
88 @ExperimentalMaterialApi
89 @Composable
Chipnull90 fun Chip(
91     onClick: () -> Unit,
92     modifier: Modifier = Modifier,
93     enabled: Boolean = true,
94     interactionSource: MutableInteractionSource? = null,
95     shape: Shape = MaterialTheme.shapes.small.copy(CornerSize(percent = 50)),
96     border: BorderStroke? = null,
97     colors: ChipColors = ChipDefaults.chipColors(),
98     leadingIcon: @Composable (() -> Unit)? = null,
99     content: @Composable RowScope.() -> Unit
100 ) {
101     val contentColor by colors.contentColor(enabled)
102     Surface(
103         onClick = onClick,
104         modifier = modifier.semantics { role = Role.Button },
105         enabled = enabled,
106         shape = shape,
107         color = colors.backgroundColor(enabled).value,
108         contentColor = contentColor.copy(1.0f),
109         border = border,
110         interactionSource = interactionSource,
111     ) {
112         CompositionLocalProvider(LocalContentAlpha provides contentColor.alpha) {
113             ProvideTextStyle(value = MaterialTheme.typography.body2) {
114                 Row(
115                     Modifier.defaultMinSize(minHeight = ChipDefaults.MinHeight)
116                         .padding(
117                             start =
118                                 if (leadingIcon == null) {
119                                     HorizontalPadding
120                                 } else 0.dp,
121                             end = HorizontalPadding,
122                         ),
123                     horizontalArrangement = Arrangement.Start,
124                     verticalAlignment = Alignment.CenterVertically
125                 ) {
126                     if (leadingIcon != null) {
127                         Spacer(Modifier.width(LeadingIconStartSpacing))
128                         val leadingIconContentColor by colors.leadingIconContentColor(enabled)
129                         CompositionLocalProvider(
130                             LocalContentColor provides leadingIconContentColor,
131                             LocalContentAlpha provides leadingIconContentColor.alpha,
132                             content = leadingIcon
133                         )
134                         Spacer(Modifier.width(LeadingIconEndSpacing))
135                     }
136                     content()
137                 }
138             }
139         }
140     }
141 }
142 
143 /**
144  * [Material Design filter chip](https://material.io/components/chips#filter-chips)
145  *
146  * Filter chips use tags or descriptive words to filter a collection. They are a good alternative to
147  * toggle buttons or checkboxes.
148  *
149  * @sample androidx.compose.material.samples.FilterChipSample
150  *
151  * A filter chip with leading icon and selected icon looks like:
152  *
153  * @sample androidx.compose.material.samples.FilterChipWithLeadingIconSample
154  *
155  * You can create an [outlined filter chip](https://material.io/components/chips#action-chips) using
156  * [ChipDefaults.outlinedFilterChipColors] and [ChipDefaults.outlinedBorder]
157  *
158  * @sample androidx.compose.material.samples.OutlinedFilterChipSample
159  * @param selected boolean state for this chip: either it is selected or not
160  * @param onClick will be called when the user clicks the chip
161  * @param modifier Modifier to be applied to the chip
162  * @param enabled controls the enabled state of the chip. When `false`, this chip will not be
163  *   clickable
164  * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
165  *   emitting [Interaction]s for this chip. You can use this to change the chip's appearance or
166  *   preview the chip in different states. Note that if `null` is provided, interactions will still
167  *   happen internally.
168  * @param shape defines the chip's shape as well as its shadow
169  * @param border border to draw around the chip
170  * @param colors [SelectableChipColors] that will be used to resolve the background and content
171  *   color for this chip in different states. See [ChipDefaults.filterChipColors].
172  * @param leadingIcon Optional icon at the start of the chip, preceding the content text.
173  * @param selectedIcon Icon used to indicate a chip's selected state, it is commonly a
174  *   [androidx.compose.material.icons.Icons.Filled.Done]. By default, if a leading icon is also
175  *   provided, the leading icon will be obscured by a circle overlay and then the selected icon.
176  * @param trailingIcon Optional icon at the end of the chip, following the content text. Filter
177  *   chips commonly do not display any trailing icon.
178  * @param content the content of this chip
179  */
180 @ExperimentalMaterialApi
181 @Composable
FilterChipnull182 fun FilterChip(
183     selected: Boolean,
184     onClick: () -> Unit,
185     modifier: Modifier = Modifier,
186     enabled: Boolean = true,
187     interactionSource: MutableInteractionSource? = null,
188     shape: Shape = MaterialTheme.shapes.small.copy(CornerSize(percent = 50)),
189     border: BorderStroke? = null,
190     colors: SelectableChipColors = ChipDefaults.filterChipColors(),
191     leadingIcon: @Composable (() -> Unit)? = null,
192     selectedIcon: @Composable (() -> Unit)? = null,
193     trailingIcon: @Composable (() -> Unit)? = null,
194     content: @Composable RowScope.() -> Unit
195 ) {
196     // TODO(b/113855296): Animate transition between unselected and selected
197     val contentColor = colors.contentColor(enabled, selected)
198     Surface(
199         selected = selected,
200         onClick = onClick,
201         modifier = modifier.semantics { role = Role.Checkbox },
202         enabled = enabled,
203         shape = shape,
204         color = colors.backgroundColor(enabled, selected).value,
205         contentColor = contentColor.value.copy(1.0f),
206         interactionSource = interactionSource,
207         border = border,
208     ) {
209         CompositionLocalProvider(LocalContentAlpha provides contentColor.value.alpha) {
210             ProvideTextStyle(value = MaterialTheme.typography.body2) {
211                 Row(
212                     Modifier.width(IntrinsicSize.Max)
213                         .defaultMinSize(minHeight = ChipDefaults.MinHeight)
214                         .padding(
215                             start =
216                                 if (leadingIcon != null || (selected && selectedIcon != null)) {
217                                     0.dp
218                                 } else {
219                                     HorizontalPadding
220                                 },
221                             end =
222                                 if (trailingIcon == null) {
223                                     HorizontalPadding
224                                 } else {
225                                     0.dp
226                                 }
227                         ),
228                     horizontalArrangement = Arrangement.Start,
229                     verticalAlignment = Alignment.CenterVertically
230                 ) {
231                     if (leadingIcon != null || (selected && selectedIcon != null)) {
232                         Spacer(Modifier.width(LeadingIconStartSpacing))
233                         Box {
234                             if (leadingIcon != null) {
235                                 val leadingIconColor = colors.leadingIconColor(enabled, selected)
236                                 CompositionLocalProvider(
237                                     LocalContentColor provides leadingIconColor.value,
238                                     LocalContentAlpha provides leadingIconColor.value.alpha,
239                                     content = leadingIcon
240                                 )
241                             }
242                             if (selected && selectedIcon != null) {
243                                 var overlayModifier: Modifier = Modifier
244                                 var iconColor = contentColor.value
245                                 if (leadingIcon != null) {
246                                     overlayModifier =
247                                         Modifier.requiredSize(SelectedIconContainerSize)
248                                             .background(
249                                                 color = contentColor.value,
250                                                 shape = CircleShape
251                                             )
252                                             .clip(CircleShape)
253 
254                                     iconColor = colors.backgroundColor(enabled, selected).value
255                                 }
256                                 Box(
257                                     modifier = overlayModifier,
258                                     contentAlignment = Alignment.Center
259                                 ) {
260                                     CompositionLocalProvider(
261                                         LocalContentColor provides iconColor,
262                                         content = selectedIcon
263                                     )
264                                 }
265                             }
266                         }
267                         Spacer(Modifier.width(LeadingIconEndSpacing))
268                     }
269                     Row(
270                         modifier = Modifier.weight(1f),
271                         horizontalArrangement = Arrangement.Start,
272                         verticalAlignment = Alignment.CenterVertically,
273                         content = content,
274                     )
275                     if (trailingIcon != null) {
276                         Spacer(Modifier.width(TrailingIconSpacing))
277                         trailingIcon()
278                         Spacer(Modifier.width(TrailingIconSpacing))
279                     }
280                 }
281             }
282         }
283     }
284 }
285 
286 /**
287  * Represents the background and content colors used in a clickable chip in different states.
288  *
289  * See [ChipDefaults.chipColors] for the default colors used in a filled [Chip]. See
290  * [ChipDefaults.outlinedChipColors] for the default colors used in a outlined [Chip],
291  */
292 @Stable
293 @ExperimentalMaterialApi
294 interface ChipColors {
295     /**
296      * Represents the background color for this chip, depending on [enabled].
297      *
298      * @param enabled whether the chip is enabled
299      */
backgroundColornull300     @Composable fun backgroundColor(enabled: Boolean): State<Color>
301 
302     /**
303      * Represents the content color for this chip, depending on [enabled], see
304      * [leadingIconContentColor].
305      *
306      * @param enabled whether the chip is enabled
307      */
308     @Composable fun contentColor(enabled: Boolean): State<Color>
309 
310     /**
311      * Represents the leading icon's content color for this chip, depending on [enabled].
312      *
313      * @param enabled whether the chip is enabled
314      */
315     @Composable fun leadingIconContentColor(enabled: Boolean): State<Color>
316 }
317 
318 // TODO(b/182821022): Add links choice and input chip colors.
319 /**
320  * Represents the background and content colors used in a selectable chip in different states.
321  * [FilterChip], choice chip and input chip are types of selectable chips. See
322  * [ChipDefaults.filterChipColors] for the default colors used in a filled [FilterChip]. See
323  * [ChipDefaults.outlinedFilterChipColors] for the default colors used in a outlined [FilterChip].
324  */
325 @ExperimentalMaterialApi
326 interface SelectableChipColors {
327     /**
328      * Represents the background color for this chip, depending on [enabled] and [selected].
329      *
330      * @param enabled whether the chip is enabled
331      * @param selected whether the chip is selected
332      */
333     @Composable fun backgroundColor(enabled: Boolean, selected: Boolean): State<Color>
334 
335     /**
336      * Represents the content color for this chip, depending on [enabled] and [selected].
337      *
338      * @param enabled whether the chip is enabled
339      * @param selected whether the chip is selected
340      */
341     @Composable fun contentColor(enabled: Boolean, selected: Boolean): State<Color>
342 
343     /**
344      * Represents the leading icon color for this chip, depending on [enabled] and [selected].
345      *
346      * @param enabled whether the chip is enabled
347      * @param selected whether the chip is selected
348      */
349     @Composable fun leadingIconColor(enabled: Boolean, selected: Boolean): State<Color>
350 }
351 
352 /** Contains the baseline values used by chips. */
353 @ExperimentalMaterialApi
354 object ChipDefaults {
355     /**
356      * The min height applied for a chip. Note that you can override it by applying Modifier.height
357      * directly on a chip.
358      */
359     val MinHeight = 32.dp
360 
361     /**
362      * Creates a [ChipColors] that represents the default background and content colors used in a
363      * filled [Chip].
364      *
365      * @param backgroundColor the background color of this chip when enabled
366      * @param contentColor the content color of this chip when enabled, there is a separate param
367      *   for icon colors
368      * @param leadingIconContentColor the color of this chip's start icon when enabled
369      * @param disabledBackgroundColor the background color of this chip when not enabled
370      * @param disabledContentColor the content color of this chip when not enabled
371      * @param disabledLeadingIconContentColor the color of this chip's start icon when not enabled
372      */
373     @Composable
chipColorsnull374     fun chipColors(
375         backgroundColor: Color =
376             MaterialTheme.colors.onSurface
377                 .copy(alpha = SurfaceOverlayOpacity)
378                 .compositeOver(MaterialTheme.colors.surface),
379         contentColor: Color = MaterialTheme.colors.onSurface.copy(alpha = ContentOpacity),
380         leadingIconContentColor: Color = contentColor.copy(alpha = LeadingIconOpacity),
381         disabledBackgroundColor: Color =
382             MaterialTheme.colors.onSurface
383                 .copy(alpha = ContentAlpha.disabled * SurfaceOverlayOpacity)
384                 .compositeOver(MaterialTheme.colors.surface),
385         disabledContentColor: Color =
386             contentColor.copy(alpha = ContentAlpha.disabled * ContentOpacity),
387         disabledLeadingIconContentColor: Color =
388             leadingIconContentColor.copy(alpha = ContentAlpha.disabled * LeadingIconOpacity),
389     ): ChipColors =
390         DefaultChipColors(
391             backgroundColor = backgroundColor,
392             contentColor = contentColor,
393             leadingIconContentColor = leadingIconContentColor,
394             disabledBackgroundColor = disabledBackgroundColor,
395             disabledContentColor = disabledContentColor,
396             disabledLeadingIconContentColor = disabledLeadingIconContentColor,
397         )
398 
399     /**
400      * Creates a [ChipColors] that represents the default background and content colors used in an
401      * outlined [Chip].
402      *
403      * @param backgroundColor the background color of this chip when enabled
404      * @param contentColor the content color of this chip when enabled, there is a separate param
405      *   for icon colors
406      * @param leadingIconContentColor the leading icon content color of this chip when enabled
407      * @param disabledBackgroundColor the background color of this chip when not enabled
408      * @param disabledContentColor the content color of this chip when not enabled
409      * @param disabledLeadingIconContentColor the color of this chip's start icon when not enabled
410      * @para leadingIconContentColor the color of this chip's start icon when enabled
411      */
412     @Composable
413     fun outlinedChipColors(
414         backgroundColor: Color = MaterialTheme.colors.surface,
415         contentColor: Color = MaterialTheme.colors.onSurface.copy(alpha = ContentOpacity),
416         leadingIconContentColor: Color = contentColor.copy(alpha = LeadingIconOpacity),
417         disabledBackgroundColor: Color = backgroundColor,
418         disabledContentColor: Color =
419             contentColor.copy(alpha = ContentAlpha.disabled * ContentOpacity),
420         disabledLeadingIconContentColor: Color =
421             leadingIconContentColor.copy(alpha = ContentAlpha.disabled * LeadingIconOpacity),
422     ): ChipColors =
423         chipColors(
424             backgroundColor = backgroundColor,
425             contentColor = contentColor,
426             leadingIconContentColor = leadingIconContentColor,
427             disabledBackgroundColor = disabledBackgroundColor,
428             disabledContentColor = disabledContentColor,
429             disabledLeadingIconContentColor = disabledLeadingIconContentColor
430         )
431 
432     /**
433      * Creates a [SelectableChipColors] that represents the default background and content colors
434      * used in a filled [FilterChip].
435      *
436      * @param backgroundColor the background color of this chip when enabled
437      * @param contentColor the content color of this chip when enabled
438      * @param leadingIconColor the color of this chip's start icon when enabled
439      * @param disabledBackgroundColor the background color of this chip when not enabled
440      * @param disabledContentColor the content color of this chip when not enabled
441      * @param disabledLeadingIconColor the color of this chip's start icon when not enabled
442      * @param selectedBackgroundColor the background color of this chip when selected
443      * @param selectedContentColor the content color of this chip when selected
444      * @param selectedLeadingIconColor the color of this chip's start icon when selected
445      */
446     @Composable
447     fun filterChipColors(
448         backgroundColor: Color =
449             MaterialTheme.colors.onSurface
450                 .copy(alpha = SurfaceOverlayOpacity)
451                 .compositeOver(MaterialTheme.colors.surface),
452         contentColor: Color = MaterialTheme.colors.onSurface.copy(alpha = ContentOpacity),
453         leadingIconColor: Color = contentColor.copy(LeadingIconOpacity),
454         disabledBackgroundColor: Color =
455             MaterialTheme.colors.onSurface
456                 .copy(alpha = ContentAlpha.disabled * SurfaceOverlayOpacity)
457                 .compositeOver(MaterialTheme.colors.surface),
458         disabledContentColor: Color =
459             contentColor.copy(alpha = ContentAlpha.disabled * ContentOpacity),
460         disabledLeadingIconColor: Color =
461             leadingIconColor.copy(alpha = ContentAlpha.disabled * LeadingIconOpacity),
462         selectedBackgroundColor: Color =
463             MaterialTheme.colors.onSurface
464                 .copy(alpha = SurfaceOverlayOpacity)
465                 .compositeOver(backgroundColor),
466         selectedContentColor: Color =
467             MaterialTheme.colors.onSurface
468                 .copy(alpha = SelectedOverlayOpacity)
469                 .compositeOver(contentColor),
470         selectedLeadingIconColor: Color =
471             MaterialTheme.colors.onSurface
472                 .copy(alpha = SelectedOverlayOpacity)
473                 .compositeOver(leadingIconColor)
474     ): SelectableChipColors =
475         DefaultSelectableChipColors(
476             backgroundColor = backgroundColor,
477             contentColor = contentColor,
478             leadingIconColor = leadingIconColor,
479             disabledBackgroundColor = disabledBackgroundColor,
480             disabledContentColor = disabledContentColor,
481             disabledLeadingIconColor = disabledLeadingIconColor,
482             selectedBackgroundColor = selectedBackgroundColor,
483             selectedContentColor = selectedContentColor,
484             selectedLeadingIconColor = selectedLeadingIconColor
485         )
486 
487     /**
488      * Creates a [ChipColors] that represents the default background and content colors used in a
489      * selectable outlined [FilterChip].
490      *
491      * @param backgroundColor the background color of this chip when enabled
492      * @param contentColor the content color of this chip when enabled
493      * @param leadingIconColor the color of this chip's start icon when enabled
494      * @param disabledBackgroundColor the background color of this chip when not enabled
495      * @param disabledContentColor the content color of this chip when not enabled
496      * @param disabledLeadingIconColor the color of this chip's start icon when not enabled
497      * @param selectedBackgroundColor the background color of this chip when selected
498      * @param selectedContentColor the content color of this chip when selected
499      * @param selectedLeadingIconColor the color of this chip's start icon when selected
500      */
501     @Composable
502     fun outlinedFilterChipColors(
503         backgroundColor: Color = MaterialTheme.colors.surface,
504         contentColor: Color = MaterialTheme.colors.onSurface.copy(ContentOpacity),
505         leadingIconColor: Color = contentColor.copy(LeadingIconOpacity),
506         disabledBackgroundColor: Color = backgroundColor,
507         disabledContentColor: Color =
508             contentColor.copy(alpha = ContentAlpha.disabled * ContentOpacity),
509         disabledLeadingIconColor: Color =
510             leadingIconColor.copy(alpha = ContentAlpha.disabled * LeadingIconOpacity),
511         selectedBackgroundColor: Color =
512             MaterialTheme.colors.onSurface
513                 .copy(alpha = SelectedOverlayOpacity)
514                 .compositeOver(backgroundColor),
515         selectedContentColor: Color =
516             MaterialTheme.colors.onSurface
517                 .copy(alpha = SelectedOverlayOpacity)
518                 .compositeOver(contentColor),
519         selectedLeadingIconColor: Color =
520             MaterialTheme.colors.onSurface
521                 .copy(alpha = SelectedOverlayOpacity)
522                 .compositeOver(leadingIconColor)
523     ): SelectableChipColors =
524         DefaultSelectableChipColors(
525             backgroundColor = backgroundColor,
526             contentColor = contentColor,
527             leadingIconColor = leadingIconColor,
528             disabledBackgroundColor = disabledBackgroundColor,
529             disabledContentColor = disabledContentColor,
530             disabledLeadingIconColor = disabledLeadingIconColor,
531             selectedBackgroundColor = selectedBackgroundColor,
532             selectedContentColor = selectedContentColor,
533             selectedLeadingIconColor = selectedLeadingIconColor
534         )
535 
536     /** The border used by all types of outlined chips */
537     val outlinedBorder: BorderStroke
538         @Composable
539         get() =
540             BorderStroke(
541                 OutlinedBorderSize,
542                 MaterialTheme.colors.onSurface.copy(alpha = OutlinedBorderOpacity)
543             )
544 
545     /** The color opacity used for chip's leading icon color */
546     const val LeadingIconOpacity = 0.54f
547 
548     /** The color opacity used for chip's content color */
549     const val ContentOpacity = 0.87f
550 
551     /** The color opacity used for the outlined chip's border color */
552     const val OutlinedBorderOpacity = 0.12f
553 
554     /** The outlined chip's border size */
555     val OutlinedBorderSize = 1.dp
556 
557     /** The size of a chip's leading icon */
558     val LeadingIconSize = 20.dp
559 
560     /** The size of a standalone selected icon */
561     val SelectedIconSize = 18.dp
562 }
563 
564 /** Default [ChipColors] implementation. */
565 @ExperimentalMaterialApi
566 @Immutable
567 private class DefaultChipColors(
568     private val backgroundColor: Color,
569     private val contentColor: Color,
570     private val leadingIconContentColor: Color,
571     private val disabledBackgroundColor: Color,
572     private val disabledContentColor: Color,
573     private val disabledLeadingIconContentColor: Color
574     // TODO(b/113855296): Support other states: hover, focus, drag
575 ) : ChipColors {
576     @Composable
577     override fun backgroundColor(enabled: Boolean): State<Color> {
578         return rememberUpdatedState(if (enabled) backgroundColor else disabledBackgroundColor)
579     }
580 
581     @Composable
582     override fun contentColor(enabled: Boolean): State<Color> {
583         return rememberUpdatedState(if (enabled) contentColor else disabledContentColor)
584     }
585 
586     @Composable
587     override fun leadingIconContentColor(enabled: Boolean): State<Color> {
588         return rememberUpdatedState(
589             if (enabled) leadingIconContentColor else disabledLeadingIconContentColor
590         )
591     }
592 
593     override fun equals(other: Any?): Boolean {
594         if (this === other) return true
595         if (other == null || this::class != other::class) return false
596 
597         other as DefaultChipColors
598 
599         if (backgroundColor != other.backgroundColor) return false
600         if (contentColor != other.contentColor) return false
601         if (leadingIconContentColor != other.leadingIconContentColor) return false
602         if (disabledBackgroundColor != other.disabledBackgroundColor) return false
603         if (disabledContentColor != other.disabledContentColor) return false
604         if (disabledLeadingIconContentColor != other.disabledLeadingIconContentColor) return false
605 
606         return true
607     }
608 
609     override fun hashCode(): Int {
610         var result = backgroundColor.hashCode()
611         result = 31 * result + contentColor.hashCode()
612         result = 31 * result + leadingIconContentColor.hashCode()
613         result = 31 * result + disabledBackgroundColor.hashCode()
614         result = 31 * result + disabledContentColor.hashCode()
615         result = 31 * result + disabledLeadingIconContentColor.hashCode()
616 
617         return result
618     }
619 }
620 
621 /** Default [SelectableChipColors] implementation. */
622 @ExperimentalMaterialApi
623 @Immutable
624 private class DefaultSelectableChipColors(
625     private val backgroundColor: Color,
626     private val contentColor: Color,
627     private val leadingIconColor: Color,
628     private val disabledBackgroundColor: Color,
629     private val disabledContentColor: Color,
630     private val disabledLeadingIconColor: Color,
631     private val selectedBackgroundColor: Color,
632     private val selectedContentColor: Color,
633     private val selectedLeadingIconColor: Color
634     // TODO(b/113855296): Support other states: hover, focus, drag
635 ) : SelectableChipColors {
636     @Composable
backgroundColornull637     override fun backgroundColor(enabled: Boolean, selected: Boolean): State<Color> {
638         val target =
639             when {
640                 !enabled -> disabledBackgroundColor
641                 !selected -> backgroundColor
642                 else -> selectedBackgroundColor
643             }
644         return rememberUpdatedState(target)
645     }
646 
647     @Composable
contentColornull648     override fun contentColor(enabled: Boolean, selected: Boolean): State<Color> {
649         val target =
650             when {
651                 !enabled -> disabledContentColor
652                 !selected -> contentColor
653                 else -> selectedContentColor
654             }
655         return rememberUpdatedState(target)
656     }
657 
658     @Composable
leadingIconColornull659     override fun leadingIconColor(enabled: Boolean, selected: Boolean): State<Color> {
660         val target =
661             when {
662                 !enabled -> disabledLeadingIconColor
663                 !selected -> leadingIconColor
664                 else -> selectedLeadingIconColor
665             }
666         return rememberUpdatedState(target)
667     }
668 
equalsnull669     override fun equals(other: Any?): Boolean {
670         if (this === other) return true
671         if (other == null || this::class != other::class) return false
672 
673         other as DefaultSelectableChipColors
674 
675         if (backgroundColor != other.backgroundColor) return false
676         if (contentColor != other.contentColor) return false
677         if (leadingIconColor != other.leadingIconColor) return false
678         if (disabledBackgroundColor != other.disabledBackgroundColor) return false
679         if (disabledContentColor != other.disabledContentColor) return false
680         if (disabledLeadingIconColor != other.disabledLeadingIconColor) return false
681         if (selectedBackgroundColor != other.selectedBackgroundColor) return false
682         if (selectedContentColor != other.selectedContentColor) return false
683         if (selectedLeadingIconColor != other.selectedLeadingIconColor) return false
684 
685         return true
686     }
687 
hashCodenull688     override fun hashCode(): Int {
689         var result = backgroundColor.hashCode()
690         result = 31 * result + contentColor.hashCode()
691         result = 31 * result + leadingIconColor.hashCode()
692         result = 31 * result + disabledBackgroundColor.hashCode()
693         result = 31 * result + disabledContentColor.hashCode()
694         result = 31 * result + disabledLeadingIconColor.hashCode()
695         result = 31 * result + selectedBackgroundColor.hashCode()
696         result = 31 * result + selectedContentColor.hashCode()
697         result = 31 * result + selectedLeadingIconColor.hashCode()
698 
699         return result
700     }
701 }
702 
703 /**
704  * The content padding used by a chip. Used as start padding when there's leading icon, used as eng
705  * padding when there's no trailing icon.
706  */
707 private val HorizontalPadding = 12.dp
708 
709 /** The size of the spacing before the leading icon when they used inside a chip. */
710 private val LeadingIconStartSpacing = 4.dp
711 
712 /** The size of the spacing between the leading icon and a text inside a chip. */
713 private val LeadingIconEndSpacing = 8.dp
714 
715 /** The size of the horizontal spacing before and after the trailing icon inside an InputChip. */
716 private val TrailingIconSpacing = 8.dp
717 
718 /** The color opacity used for chip's surface overlay. */
719 private const val SurfaceOverlayOpacity = 0.12f
720 
721 /** The color opacity used for a selected chip's leading icon overlay. */
722 private const val SelectedOverlayOpacity = 0.16f
723 
724 /**
725  * The size of a circle used to obscure the leading icon before a selected icon is displayed on top.
726  */
727 private val SelectedIconContainerSize = 24.dp
728