1 /*
<lambda>null2 * Copyright 2021 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 package androidx.wear.compose.material
17
18 import androidx.compose.foundation.interaction.Interaction
19 import androidx.compose.foundation.interaction.MutableInteractionSource
20 import androidx.compose.foundation.layout.BoxScope
21 import androidx.compose.foundation.layout.IntrinsicSize
22 import androidx.compose.foundation.layout.PaddingValues
23 import androidx.compose.foundation.layout.RowScope
24 import androidx.compose.foundation.layout.defaultMinSize
25 import androidx.compose.foundation.layout.height
26 import androidx.compose.material.icons.materialIcon
27 import androidx.compose.material.icons.materialPath
28 import androidx.compose.runtime.Composable
29 import androidx.compose.runtime.Immutable
30 import androidx.compose.runtime.Stable
31 import androidx.compose.runtime.State
32 import androidx.compose.runtime.rememberUpdatedState
33 import androidx.compose.ui.Modifier
34 import androidx.compose.ui.draw.paint
35 import androidx.compose.ui.graphics.Brush
36 import androidx.compose.ui.graphics.Color
37 import androidx.compose.ui.graphics.PathFillType
38 import androidx.compose.ui.graphics.Shape
39 import androidx.compose.ui.graphics.compositeOver
40 import androidx.compose.ui.graphics.painter.BrushPainter
41 import androidx.compose.ui.graphics.painter.Painter
42 import androidx.compose.ui.graphics.vector.ImageVector
43 import androidx.compose.ui.layout.ContentScale
44 import androidx.compose.ui.platform.LocalLayoutDirection
45 import androidx.compose.ui.unit.Dp
46 import androidx.compose.ui.unit.LayoutDirection
47 import androidx.compose.ui.unit.dp
48
49 /**
50 * A [ToggleChip] is a specialized type of [Chip] that includes a slot for a bi-state toggle control
51 * such as a toggle or checkbox. This overload provides suitable accessibility semantics for a
52 * toggleable control like [Checkbox] and [Switch]. For selectable controls like [RadioButton], use
53 * [SelectableChip] in order to provide the correct semantics for accessibility.
54 *
55 * The Wear Material [ToggleChip] offers four slots and a specific layout for an application icon, a
56 * label, a secondaryLabel and toggle control. The application icon and secondaryLabel are optional.
57 * The items are laid out in a row with the optional icon at the start, a column containing the two
58 * label slots in the middle and a slot for the toggle control at the end.
59 *
60 * The [ToggleChip] is Stadium shaped and has a max height designed to take no more than two lines
61 * of text of [Typography.button] style. With localisation and/or large font sizes, the [ToggleChip]
62 * height adjusts to accommodate the contents. The label and secondary label should be consistently
63 * aligned.
64 *
65 * The recommended set of [ToggleChipColors] can be obtained from [ToggleChipDefaults], e.g.
66 * [ToggleChipDefaults.toggleChipColors].
67 *
68 * Chips can be enabled or disabled. A disabled chip will not respond to click events.
69 *
70 * Example of a [ToggleChip] with an icon, label and secondary label (defaults to switch toggle):
71 *
72 * @sample androidx.wear.compose.material.samples.ToggleChipWithSwitch
73 *
74 * For more information, see the
75 * [Toggle Chips](https://developer.android.com/training/wearables/components/toggle-chips) guide.
76 *
77 * @param checked Boolean flag indicating whether this button is currently checked.
78 * @param onCheckedChange Callback to be invoked when this button's checked status changes
79 * @param label A slot for providing the chip's main label. The contents are expected to be text
80 * which is "start" aligned.
81 * @param toggleControl A slot for providing the chip's toggle control. Two built-in types of toggle
82 * control are supported - [Checkbox] and [Switch]. For [RadioButton], use [SelectableChip], in
83 * order to provide the correct semantics for accessibility.
84 * @param modifier Modifier to be applied to the chip
85 * @param appIcon An optional slot for providing an icon to indicate the purpose of the chip. The
86 * contents are expected to be a horizontally and vertically centre aligned icon of size
87 * [ToggleChipDefaults.IconSize]. In order to correctly render when the Chip is not enabled the
88 * icon must set its alpha value to [LocalContentAlpha].
89 * @param secondaryLabel A slot for providing the chip's secondary label. The contents are expected
90 * to be text which is "start" aligned if there is an icon preset and "start" or "center" aligned
91 * if not. label and secondaryLabel contents should be consistently aligned.
92 * @param colors [ToggleChipColors] that will be used to resolve the background and content color
93 * for this chip in different states, see [ToggleChipDefaults.toggleChipColors].
94 * @param enabled Controls the enabled state of the chip. When `false`, this chip will not be
95 * clickable
96 * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
97 * emitting [Interaction]s for this chip's "toggleable" tap area. You can use this to change the
98 * chip's appearance or preview the chip in different states. Note that if `null` is provided,
99 * interactions will still happen internally.
100 * @param contentPadding The spacing values to apply internally between the container and the
101 * content
102 * @param shape Defines the chip's shape. It is strongly recommended to use the default as this
103 * shape is a key characteristic of the Wear Material Theme
104 */
105 @Composable
106 public fun ToggleChip(
107 checked: Boolean,
108 onCheckedChange: (Boolean) -> Unit,
109 label: @Composable RowScope.() -> Unit,
110 toggleControl: @Composable () -> Unit,
111 modifier: Modifier = Modifier,
112 appIcon: @Composable (BoxScope.() -> Unit)? = null,
113 secondaryLabel: @Composable (RowScope.() -> Unit)? = null,
114 colors: ToggleChipColors = ToggleChipDefaults.toggleChipColors(),
115 enabled: Boolean = true,
116 interactionSource: MutableInteractionSource? = null,
117 contentPadding: PaddingValues = ToggleChipDefaults.ContentPadding,
118 shape: Shape = MaterialTheme.shapes.large,
119 ): Unit =
120 androidx.wear.compose.materialcore.ToggleButton(
121 checked = checked,
122 onCheckedChange = onCheckedChange,
123 label =
124 provideScopeContent(
125 contentColor = colors.contentColor(enabled = enabled, checked),
126 textStyle = MaterialTheme.typography.button,
127 content = label
128 ),
129 toggleControl =
130 provideContent(
131 contentColor = colors.toggleControlColor(enabled, checked),
132 content = toggleControl
133 ),
134 selectionControl = null,
135 modifier =
136 modifier
137 .defaultMinSize(minHeight = ToggleChipDefaults.Height)
138 .height(IntrinsicSize.Min),
139 icon =
140 provideNullableScopeContent(
141 contentColor = colors.contentColor(enabled = enabled, checked = checked),
142 content = appIcon
143 ),
144 secondaryLabel =
145 provideNullableScopeContent(
146 contentColor = colors.secondaryContentColor(enabled = enabled, checked),
147 textStyle = MaterialTheme.typography.caption2,
148 content = secondaryLabel
149 ),
150 background = { isEnabled, isChecked ->
151 val painter = colors.background(enabled = isEnabled, checked = isChecked).value
152
153 Modifier.paint(painter = painter, contentScale = ContentScale.Crop)
154 },
155 enabled = enabled,
156 interactionSource = interactionSource,
157 contentPadding = contentPadding,
158 shape = shape,
159 toggleControlHeight = TOGGLE_CONTROL_HEIGHT,
160 toggleControlWidth = TOGGLE_CONTROL_WIDTH,
161 labelSpacerSize = 0.dp,
162 toggleControlSpacing = TOGGLE_CONTROL_SPACING,
163 iconSpacing = ICON_SPACING,
164 ripple = ripple()
165 )
166
167 /**
168 * A [SplitToggleChip] is a specialized type of [Chip] that includes a slot for a toggle control,
169 * such as a toggle or checkbox. The [SplitToggleChip] differs from the [ToggleChip] by having two
170 * "tappable" areas, one clickable and one toggleable.
171 *
172 * This overload provides suitable accessibility semantics for a toggleable control like [Checkbox]
173 * and [Switch]. For selectable controls like [RadioButton], use [SelectableChip] instead.
174 *
175 * The Wear Material [SplitToggleChip] offers three slots and a specific layout for a label,
176 * secondaryLabel and toggle control. The secondaryLabel is optional. The items are laid out with a
177 * column containing the two label slots and a slot for the toggle control at the end.
178 *
179 * A [SplitToggleChip] has two tappable areas, one tap area for the labels and another for the
180 * toggle control. The [onClick] listener will be associated with the main body of the split toggle
181 * chip with the [onCheckedChange] listener associated with the toggle control area only.
182 *
183 * For a split toggle chip the background of the tappable background area behind the toggle control
184 * will have a visual effect applied to provide a "divider" between the two tappable areas.
185 *
186 * The [SplitToggleChip] is Stadium shaped and has a max height designed to take no more than two
187 * lines of text of [Typography.button] style. With localisation and/or large font sizes, the
188 * [SplitToggleChip] height adjusts to accommodate the contents. The label and secondary label
189 * should be consistently aligned.
190 *
191 * The recommended set of [SplitToggleChipColors] can be obtained from [ToggleChipDefaults], e.g.
192 * [ToggleChipDefaults.splitToggleChipColors].
193 *
194 * Chips can be enabled or disabled. A disabled chip will not respond to click events.
195 *
196 * Example of a [SplitToggleChip] with a label and the toggle control changed to checkbox:
197 *
198 * @sample androidx.wear.compose.material.samples.SplitToggleChipWithCheckbox
199 *
200 * For more information, see the
201 * [Toggle Chips](https://developer.android.com/training/wearables/components/toggle-chips) guide.
202 *
203 * @param checked Boolean flag indicating whether this button is currently checked.
204 * @param onCheckedChange Callback to be invoked when this buttons checked status is changed.
205 * @param label A slot for providing the chip's main label. The contents are expected to be text
206 * which is "start" aligned.
207 * @param onClick Click listener called when the user clicks the main body of the chip, the area
208 * behind the labels.
209 * @param toggleControl A slot for providing the chip's toggle controls(s). Two built-in types of
210 * toggle control are supported, see [Checkbox] and [Switch]. For [RadioButton], use
211 * [SelectableChip] instead. [ImageVector]s can be obtained from [ToggleChipDefaults.switchIcon],
212 * [ToggleChipDefaults.radioIcon] and [ToggleChipDefaults.checkboxIcon]. In order to correctly
213 * render when the Chip is not enabled the icon must set its alpha value to [LocalContentAlpha].
214 * @param modifier Modifier to be applied to the chip
215 * @param secondaryLabel A slot for providing the chip's secondary label. The contents are expected
216 * to be "start" or "center" aligned. label and secondaryLabel contents should be consistently
217 * aligned.
218 * @param colors [SplitToggleChipColors] that will be used to resolve the background and content
219 * color for this chip in different states, see [ToggleChipDefaults.splitToggleChipColors].
220 * @param enabled Controls the enabled state of the chip. When `false`, this chip will not be
221 * clickable
222 * @param checkedInteractionSource an optional hoisted [MutableInteractionSource] for observing and
223 * emitting [Interaction]s for this chip's "toggleable" tap area. You can use this to change the
224 * chip's appearance or preview the chip in different states. Note that if `null` is provided,
225 * interactions will still happen internally.
226 * @param clickInteractionSource an optional hoisted [MutableInteractionSource] for observing and
227 * emitting [Interaction]s for this chip's "clickable" tap area. You can use this to change the
228 * chip's appearance or preview the chip in different states. Note that if `null` is provided,
229 * interactions will still happen internally.
230 * @param contentPadding The spacing values to apply internally between the container and the
231 * content
232 * @param shape Defines the chip's shape. It is strongly recommended to use the default as this
233 * shape is a key characteristic of the Wear Material Theme
234 */
235 @Composable
SplitToggleChipnull236 public fun SplitToggleChip(
237 checked: Boolean,
238 onCheckedChange: (Boolean) -> Unit,
239 label: @Composable RowScope.() -> Unit,
240 onClick: () -> Unit,
241 toggleControl: @Composable BoxScope.() -> Unit,
242 modifier: Modifier = Modifier,
243 secondaryLabel: @Composable (RowScope.() -> Unit)? = null,
244 colors: SplitToggleChipColors = ToggleChipDefaults.splitToggleChipColors(),
245 enabled: Boolean = true,
246 checkedInteractionSource: MutableInteractionSource? = null,
247 clickInteractionSource: MutableInteractionSource? = null,
248 contentPadding: PaddingValues = ToggleChipDefaults.ContentPadding,
249 shape: Shape = MaterialTheme.shapes.large,
250 ): Unit =
251 androidx.wear.compose.materialcore.SplitToggleButton(
252 checked = checked,
253 onCheckedChange = onCheckedChange,
254 label =
255 provideScopeContent(
256 contentColor = colors.contentColor(enabled = enabled),
257 textStyle = MaterialTheme.typography.button,
258 content = label
259 ),
260 onClick = onClick,
261 toggleControl =
262 provideScopeContent(
263 contentColor = colors.toggleControlColor(enabled = enabled, checked = checked),
264 content = toggleControl
265 ),
266 selectionControl = null,
267 modifier =
268 modifier
269 .defaultMinSize(minHeight = ToggleChipDefaults.Height)
270 .height(IntrinsicSize.Min),
271 secondaryLabel =
272 provideNullableScopeContent(
273 contentColor = colors.secondaryContentColor(enabled = enabled),
274 textStyle = MaterialTheme.typography.caption2,
275 content = secondaryLabel
276 ),
277 backgroundColor = { isEnabled, _ -> colors.backgroundColor(enabled = isEnabled) },
isEnablednull278 splitBackgroundColor = { isEnabled, isChecked ->
279 colors.splitBackgroundOverlay(enabled = isEnabled, checked = isChecked)
280 },
281 enabled = enabled,
282 checkedInteractionSource = checkedInteractionSource,
283 clickInteractionSource = clickInteractionSource,
284 onClickLabel = null,
285 contentPadding = contentPadding,
286 shape = shape,
287 labelSpacerSize = 0.dp,
288 ripple = ripple()
289 )
290
291 /** Represents the background and content colors used in [ToggleChip]s in different states. */
292 @Stable
293 public interface ToggleChipColors {
294 /**
295 * Represents the background treatment for this chip, depending on the [enabled] and [checked]
296 * properties. Backgrounds are typically a linear gradient when the chip is checked and solid
297 * when it is not.
298 *
299 * @param enabled Whether the chip is enabled
300 * @param checked Whether the chip is currently checked or unchecked
301 */
backgroundnull302 @Composable public fun background(enabled: Boolean, checked: Boolean): State<Painter>
303
304 /**
305 * Represents the content color for this chip, depending on the [enabled] and [checked]
306 * properties.
307 *
308 * @param enabled Whether the chip is enabled
309 * @param checked Whether the chip is currently checked or unchecked
310 */
311 @Composable public fun contentColor(enabled: Boolean, checked: Boolean): State<Color>
312
313 /**
314 * Represents the secondary content color for this chip, depending on the [enabled] and
315 * [checked] properties.
316 *
317 * @param enabled Whether the chip is enabled
318 * @param checked Whether the chip is currently checked or unchecked
319 */
320 @Composable public fun secondaryContentColor(enabled: Boolean, checked: Boolean): State<Color>
321
322 /**
323 * Represents the color for the toggle control content for this chip, depending on the [enabled]
324 * and [checked] properties.
325 *
326 * @param enabled Whether the chip is enabled
327 * @param checked Whether the chip is currently checked or unchecked
328 */
329 @Composable public fun toggleControlColor(enabled: Boolean, checked: Boolean): State<Color>
330 }
331
332 /** Represents the background and content colors used in [SplitToggleChip]s in different states. */
333 @Stable
334 public interface SplitToggleChipColors {
335 /**
336 * Represents the background color for this chip, depending on whether it is [enabled].
337 *
338 * @param enabled Whether the chip is enabled
339 */
340 @Composable public fun backgroundColor(enabled: Boolean): State<Color>
341
342 /**
343 * Represents the content color for this chip, depending on whether it is [enabled]
344 *
345 * @param enabled Whether the chip is enabled
346 */
347 @Composable public fun contentColor(enabled: Boolean): State<Color>
348
349 /**
350 * Represents the secondary content color for this chip, depending on whether it is [enabled]
351 *
352 * @param enabled Whether the chip is enabled
353 */
354 @Composable public fun secondaryContentColor(enabled: Boolean): State<Color>
355
356 /**
357 * Represents the color for the toggle control content for this chip, depending on the [enabled]
358 * and [checked] properties.
359 *
360 * @param enabled Whether the chip is enabled
361 * @param checked Whether the chip is currently checked or unchecked
362 */
363 @Composable public fun toggleControlColor(enabled: Boolean, checked: Boolean): State<Color>
364
365 /**
366 * Represents the overlay to apply to a split background SplitToggleChip to distinguish between
367 * the two tappable areas. The overlay will be applied to "lighten" the background of area under
368 * the toggle control, depending on the [enabled] and [checked] properties.
369 *
370 * @param enabled Whether the chip is enabled
371 * @param checked Whether the chip is currently checked or unchecked
372 */
373 @Composable public fun splitBackgroundOverlay(enabled: Boolean, checked: Boolean): State<Color>
374 }
375
376 /** Contains the default values used by [ToggleChip]s and [SplitToggleChip]s */
377 public object ToggleChipDefaults {
378
379 /**
380 * Creates a [ToggleChipColors] for use in a [ToggleChip]. [ToggleChip]s are expected to have a
381 * linear gradient background when checked, similar to a
382 * [ChipDefaults.gradientBackgroundChipColors] and a solid neutral background when not checked
383 * (similar to a [ChipDefaults.secondaryChipColors])
384 *
385 * @param checkedStartBackgroundColor The background color used at the start of the gradient of
386 * a [ToggleChip] when enabled and checked.
387 * @param checkedEndBackgroundColor The background color used at the end of the gradient of a
388 * [ToggleChip] when enabled and checked.
389 * @param checkedContentColor The content color of a [ToggleChip] when enabled and checked.
390 * @param checkedSecondaryContentColor The secondary content color of this [ToggleChip] when
391 * enabled and checked, used for secondaryLabel content
392 * @param checkedToggleControlColor The toggle control color of this [ToggleChip] when enabled
393 * and checked, used for toggleControl content
394 * @param uncheckedStartBackgroundColor The background color used at the start of the gradient
395 * of a [ToggleChip] when enabled and unchecked.
396 * @param uncheckedEndBackgroundColor The background color used at the end of the gradient of a
397 * [ToggleChip] when enabled and unchecked.
398 * @param uncheckedContentColor The content color of a [ToggleChip] when enabled and checked.
399 * @param uncheckedSecondaryContentColor The secondary content color of this [ToggleChip] when
400 * enabled and unchecked, used for secondaryLabel content
401 * @param uncheckedToggleControlColor The toggle control color of this [ToggleChip] when enabled
402 * and unchecked.
403 * @param gradientDirection Whether the chips gradient should be start to end (indicated by
404 * [LayoutDirection.Ltr]) or end to start (indicated by [LayoutDirection.Rtl]).
405 */
406 @Composable
toggleChipColorsnull407 public fun toggleChipColors(
408 checkedStartBackgroundColor: Color =
409 MaterialTheme.colors.surface
410 .copy(alpha = 0f)
411 .compositeOver(MaterialTheme.colors.surface),
412 checkedEndBackgroundColor: Color =
413 MaterialTheme.colors.primary
414 .copy(alpha = 0.5f)
415 .compositeOver(MaterialTheme.colors.surface),
416 checkedContentColor: Color = MaterialTheme.colors.onSurface,
417 checkedSecondaryContentColor: Color = MaterialTheme.colors.onSurfaceVariant,
418 checkedToggleControlColor: Color = MaterialTheme.colors.secondary,
419 uncheckedStartBackgroundColor: Color = MaterialTheme.colors.surface,
420 uncheckedEndBackgroundColor: Color = uncheckedStartBackgroundColor,
421 uncheckedContentColor: Color = contentColorFor(checkedEndBackgroundColor),
422 uncheckedSecondaryContentColor: Color = uncheckedContentColor,
423 uncheckedToggleControlColor: Color = uncheckedContentColor,
424 gradientDirection: LayoutDirection = LocalLayoutDirection.current
425 ): ToggleChipColors {
426 val checkedBackgroundColors: List<Color>
427 val disabledCheckedBackgroundColors: List<Color>
428 if (gradientDirection == LayoutDirection.Ltr) {
429 checkedBackgroundColors = listOf(checkedStartBackgroundColor, checkedEndBackgroundColor)
430 disabledCheckedBackgroundColors =
431 listOf(
432 checkedStartBackgroundColor.copy(alpha = ContentAlpha.disabled),
433 checkedEndBackgroundColor.copy(alpha = ContentAlpha.disabled)
434 )
435 } else {
436 checkedBackgroundColors = listOf(checkedEndBackgroundColor, checkedStartBackgroundColor)
437 disabledCheckedBackgroundColors =
438 listOf(
439 checkedEndBackgroundColor.copy(alpha = ContentAlpha.disabled),
440 checkedStartBackgroundColor.copy(alpha = ContentAlpha.disabled),
441 )
442 }
443 val uncheckedBackgroundColors: List<Color>
444 val disabledUncheckedBackgroundColors: List<Color>
445 if (gradientDirection == LayoutDirection.Ltr) {
446 uncheckedBackgroundColors =
447 listOf(uncheckedStartBackgroundColor, uncheckedEndBackgroundColor)
448 disabledUncheckedBackgroundColors =
449 listOf(
450 uncheckedStartBackgroundColor.copy(alpha = ContentAlpha.disabled),
451 uncheckedEndBackgroundColor.copy(alpha = ContentAlpha.disabled)
452 )
453 } else {
454 uncheckedBackgroundColors =
455 listOf(uncheckedEndBackgroundColor, uncheckedStartBackgroundColor)
456 disabledUncheckedBackgroundColors =
457 listOf(
458 uncheckedEndBackgroundColor.copy(alpha = ContentAlpha.disabled),
459 uncheckedStartBackgroundColor.copy(alpha = ContentAlpha.disabled),
460 )
461 }
462
463 return DefaultToggleChipColors(
464 checkedBackgroundPainter = BrushPainter(Brush.linearGradient(checkedBackgroundColors)),
465 checkedContentColor = checkedContentColor,
466 checkedSecondaryContentColor = checkedSecondaryContentColor,
467 checkedIconColor = checkedToggleControlColor,
468 uncheckedBackgroundPainter =
469 BrushPainter(Brush.linearGradient(uncheckedBackgroundColors)),
470 uncheckedContentColor = uncheckedContentColor,
471 uncheckedSecondaryContentColor = uncheckedSecondaryContentColor,
472 uncheckedIconColor = uncheckedToggleControlColor,
473 disabledCheckedBackgroundPainter =
474 BrushPainter(Brush.linearGradient(disabledCheckedBackgroundColors)),
475 disabledCheckedContentColor = checkedContentColor.copy(alpha = ContentAlpha.disabled),
476 disabledCheckedSecondaryContentColor =
477 checkedSecondaryContentColor.copy(alpha = ContentAlpha.disabled),
478 disabledCheckedIconColor =
479 checkedToggleControlColor.copy(alpha = ContentAlpha.disabled),
480 disabledUncheckedBackgroundPainter =
481 BrushPainter(Brush.linearGradient(disabledUncheckedBackgroundColors)),
482 disabledUncheckedContentColor =
483 uncheckedContentColor.copy(alpha = ContentAlpha.disabled),
484 disabledUncheckedSecondaryContentColor =
485 uncheckedSecondaryContentColor.copy(alpha = ContentAlpha.disabled),
486 disabledUncheckedIconColor =
487 uncheckedToggleControlColor.copy(alpha = ContentAlpha.disabled),
488 )
489 }
490
491 /**
492 * Creates a [SplitToggleChipColors] for use in a [SplitToggleChip].
493 *
494 * @param backgroundColor The background color of this [SplitToggleChip] when enabled
495 * @param contentColor The content color of this [SplitToggleChip] when enabled.
496 * @param secondaryContentColor The secondary content color of this[SplitToggleChip] when
497 * enabled
498 * @param checkedToggleControlColor The toggle control content color of this [SplitToggleChip]
499 * when enabled.
500 * @param uncheckedToggleControlColor The toggle control content color of this [SplitToggleChip]
501 * when enabled.
502 * @param splitBackgroundOverlayColor The color to use to lighten/distinguish the background
503 * behind the ToggleControl for a split background chip. A split background chip has two
504 * tappable areas, one for the main body of the chip and one for area around the toggle
505 * control icon.
506 */
507 @Composable
splitToggleChipColorsnull508 public fun splitToggleChipColors(
509 backgroundColor: Color = MaterialTheme.colors.surface,
510 contentColor: Color = MaterialTheme.colors.onSurface,
511 secondaryContentColor: Color = MaterialTheme.colors.onSurfaceVariant,
512 checkedToggleControlColor: Color = MaterialTheme.colors.secondary,
513 uncheckedToggleControlColor: Color = contentColor,
514 splitBackgroundOverlayColor: Color = Color.White.copy(alpha = 0.05f),
515 ): SplitToggleChipColors {
516 return DefaultSplitToggleChipColors(
517 backgroundColor = backgroundColor,
518 contentColor = contentColor,
519 secondaryContentColor = secondaryContentColor,
520 checkedIconColor = checkedToggleControlColor,
521 checkedSplitBackgroundOverlay = splitBackgroundOverlayColor,
522 uncheckedIconColor = uncheckedToggleControlColor,
523 uncheckedSplitBackgroundOverlay = splitBackgroundOverlayColor,
524 disabledBackgroundColor = backgroundColor.copy(alpha = ContentAlpha.disabled),
525 disabledContentColor = contentColor.copy(alpha = ContentAlpha.disabled),
526 disabledSecondaryContentColor =
527 secondaryContentColor.copy(alpha = ContentAlpha.disabled),
528 disabledCheckedIconColor =
529 checkedToggleControlColor.copy(alpha = ContentAlpha.disabled),
530 disabledCheckedSplitBackgroundOverlay = splitBackgroundOverlayColor,
531 disabledUncheckedIconColor =
532 uncheckedToggleControlColor.copy(alpha = ContentAlpha.disabled),
533 disabledUncheckedSplitBackgroundOverlay = splitBackgroundOverlayColor,
534 )
535 }
536
537 /** The Wear Material UX recommended color to use for an unchecked switch icon. */
538 public val SwitchUncheckedIconColor: Color
539 @Composable get() = MaterialTheme.colors.onSurface.copy(0.6f)
540
541 private val ChipHorizontalPadding = 14.dp
542 private val ChipVerticalPadding = 6.dp
543
544 /** The default content padding used by [ToggleChip] and [SplitToggleChip] */
545 public val ContentPadding: PaddingValues =
546 PaddingValues(
547 start = ChipHorizontalPadding,
548 top = ChipVerticalPadding,
549 end = ChipHorizontalPadding,
550 bottom = ChipVerticalPadding
551 )
552
553 /**
554 * Creates switch style toggle [ImageVector]s for use in the toggleControl slot of a
555 * [ToggleChip] or [SplitToggleChip]. Depending on [checked] will return either an 'on'
556 * (checked) or 'off' (unchecked) switch icon.
557 *
558 * @param checked whether the [ToggleChip] or [SplitToggleChip] is currently 'on' (checked/true)
559 * or 'off' (unchecked/false)
560 */
switchIconnull561 public fun switchIcon(
562 checked: Boolean,
563 ): ImageVector = if (checked) SwitchOn else SwitchOff
564
565 /**
566 * Creates a radio button style toggle [ImageVector]s for use in the toggleControl slot of a
567 * [ToggleChip] or [SplitToggleChip]. Depending on [checked] will return either an 'on'
568 * (checked) or 'off' (unchecked) radio button icon.
569 *
570 * @param checked whether the [ToggleChip] or [SplitToggleChip] is currently 'on' (checked/true)
571 * or 'off' (unchecked/false)
572 */
573 public fun radioIcon(
574 checked: Boolean,
575 ): ImageVector = if (checked) RadioOn else RadioOff
576
577 /**
578 * Creates checkbox style toggle [ImageVector]s for use in the toggleControl slot of a
579 * [ToggleChip] or [SplitToggleChip]. Depending on [checked] will return either an 'on'
580 * (ticked/checked) or 'off' (unticked/unchecked) checkbox image.
581 *
582 * @param checked whether the [ToggleChip] or [SplitToggleChip] is currently 'on' (checked/true)
583 * or 'off' (unchecked/false)
584 */
585 public fun checkboxIcon(
586 checked: Boolean,
587 ): ImageVector = if (checked) CheckboxOn else CheckboxOff
588
589 /**
590 * The default height applied for the [ToggleChip] or [SplitToggleChip]. Note that you can
591 * override it by applying Modifier.heightIn directly on [ToggleChip] or [SplitToggleChip].
592 */
593 public val Height: Dp = 52.dp
594
595 /**
596 * The default size of app icons or toggle controls when used inside a [ToggleChip] or
597 * [SplitToggleChip].
598 */
599 public val IconSize: Dp = 24.dp
600
601 private val SwitchOn: ImageVector
602 get() {
603 if (_switchOn != null) {
604 return _switchOn!!
605 }
606 _switchOn =
607 materialIcon(name = "SwitchOn") {
608 materialPath(fillAlpha = 0.38f, strokeAlpha = 0.38f) {
609 moveTo(5.0f, 7.0f)
610 lineTo(19.0f, 7.0f)
611 arcTo(5.0f, 5.0f, 0.0f, false, true, 24.0f, 12.0f)
612 lineTo(24.0f, 12.0f)
613 arcTo(5.0f, 5.0f, 0.0f, false, true, 19.0f, 17.0f)
614 lineTo(5.0f, 17.0f)
615 arcTo(5.0f, 5.0f, 0.0f, false, true, 0.0f, 12.0f)
616 lineTo(0.0f, 12.0f)
617 arcTo(5.0f, 5.0f, 0.0f, false, true, 5.0f, 7.0f)
618 close()
619 }
620 materialPath(pathFillType = PathFillType.EvenOdd) {
621 moveTo(17.0f, 19.0f)
622 curveTo(20.866f, 19.0f, 24.0f, 15.866f, 24.0f, 12.0f)
623 curveTo(24.0f, 8.134f, 20.866f, 5.0f, 17.0f, 5.0f)
624 curveTo(13.134f, 5.0f, 10.0f, 8.134f, 10.0f, 12.0f)
625 curveTo(10.0f, 15.866f, 13.134f, 19.0f, 17.0f, 19.0f)
626 close()
627 }
628 }
629 return _switchOn!!
630 }
631
632 private var _switchOn: ImageVector? = null
633
634 private val SwitchOff: ImageVector
635 get() {
636 if (_switchOff != null) {
637 return _switchOff!!
638 }
639 _switchOff =
<lambda>null640 materialIcon(name = "SwitchOff") {
641 materialPath(fillAlpha = 0.38f, strokeAlpha = 0.38f) {
642 moveTo(5.0f, 7.0f)
643 lineTo(19.0f, 7.0f)
644 arcTo(5.0f, 5.0f, 0.0f, false, true, 24.0f, 12.0f)
645 lineTo(24.0f, 12.0f)
646 arcTo(5.0f, 5.0f, 0.0f, false, true, 19.0f, 17.0f)
647 lineTo(5.0f, 17.0f)
648 arcTo(5.0f, 5.0f, 0.0f, false, true, 0.0f, 12.0f)
649 lineTo(0.0f, 12.0f)
650 arcTo(5.0f, 5.0f, 0.0f, false, true, 5.0f, 7.0f)
651 close()
652 }
653 materialPath(pathFillType = PathFillType.EvenOdd) {
654 moveTo(7.0f, 19.0f)
655 curveTo(10.866f, 19.0f, 14.0f, 15.866f, 14.0f, 12.0f)
656 curveTo(14.0f, 8.134f, 10.866f, 5.0f, 7.0f, 5.0f)
657 curveTo(3.134f, 5.0f, 0.0f, 8.134f, 0.0f, 12.0f)
658 curveTo(0.0f, 15.866f, 3.134f, 19.0f, 7.0f, 19.0f)
659 close()
660 }
661 }
662 return _switchOff!!
663 }
664
665 private var _switchOff: ImageVector? = null
666
667 public val RadioOn: ImageVector
668 get() {
669 if (_radioOn != null) {
670 return _radioOn!!
671 }
672 _radioOn =
<lambda>null673 materialIcon(name = "RadioOn") {
674 materialPath {
675 moveTo(12.0f, 2.0f)
676 curveTo(6.48f, 2.0f, 2.0f, 6.48f, 2.0f, 12.0f)
677 curveTo(2.0f, 17.52f, 6.48f, 22.0f, 12.0f, 22.0f)
678 curveTo(17.52f, 22.0f, 22.0f, 17.52f, 22.0f, 12.0f)
679 curveTo(22.0f, 6.48f, 17.52f, 2.0f, 12.0f, 2.0f)
680 close()
681 moveTo(12.0f, 20.0f)
682 curveTo(7.58f, 20.0f, 4.0f, 16.42f, 4.0f, 12.0f)
683 curveTo(4.0f, 7.58f, 7.58f, 4.0f, 12.0f, 4.0f)
684 curveTo(16.42f, 4.0f, 20.0f, 7.58f, 20.0f, 12.0f)
685 curveTo(20.0f, 16.42f, 16.42f, 20.0f, 12.0f, 20.0f)
686 close()
687 }
688 materialPath {
689 moveTo(12.0f, 12.0f)
690 moveToRelative(-5.0f, 0.0f)
691 arcToRelative(5.0f, 5.0f, 0.0f, true, true, 10.0f, 0.0f)
692 arcToRelative(5.0f, 5.0f, 0.0f, true, true, -10.0f, 0.0f)
693 }
694 }
695 return _radioOn!!
696 }
697
698 private var _radioOn: ImageVector? = null
699
700 public val RadioOff: ImageVector
701 get() {
702 if (_radioOff != null) {
703 return _radioOff!!
704 }
705 _radioOff =
<lambda>null706 materialIcon(name = "RadioOff") {
707 materialPath {
708 moveTo(12.0f, 2.0f)
709 curveTo(6.48f, 2.0f, 2.0f, 6.48f, 2.0f, 12.0f)
710 curveTo(2.0f, 17.52f, 6.48f, 22.0f, 12.0f, 22.0f)
711 curveTo(17.52f, 22.0f, 22.0f, 17.52f, 22.0f, 12.0f)
712 curveTo(22.0f, 6.48f, 17.52f, 2.0f, 12.0f, 2.0f)
713 close()
714 moveTo(12.0f, 20.0f)
715 curveTo(7.58f, 20.0f, 4.0f, 16.42f, 4.0f, 12.0f)
716 curveTo(4.0f, 7.58f, 7.58f, 4.0f, 12.0f, 4.0f)
717 curveTo(16.42f, 4.0f, 20.0f, 7.58f, 20.0f, 12.0f)
718 curveTo(20.0f, 16.42f, 16.42f, 20.0f, 12.0f, 20.0f)
719 close()
720 }
721 }
722 return _radioOff!!
723 }
724
725 private var _radioOff: ImageVector? = null
726
727 public val CheckboxOn: ImageVector
728 get() {
729 if (_checkboxOn != null) {
730 return _checkboxOn!!
731 }
732 _checkboxOn =
<lambda>null733 materialIcon(name = "CheckboxOn") {
734 materialPath {
735 moveTo(19.0f, 3.0f)
736 horizontalLineTo(5.0f)
737 curveTo(3.9f, 3.0f, 3.0f, 3.9f, 3.0f, 5.0f)
738 verticalLineTo(19.0f)
739 curveTo(3.0f, 20.1f, 3.9f, 21.0f, 5.0f, 21.0f)
740 horizontalLineTo(19.0f)
741 curveTo(20.1f, 21.0f, 21.0f, 20.1f, 21.0f, 19.0f)
742 verticalLineTo(5.0f)
743 curveTo(21.0f, 3.9f, 20.1f, 3.0f, 19.0f, 3.0f)
744 close()
745 moveTo(19.0f, 19.0f)
746 horizontalLineTo(5.0f)
747 verticalLineTo(5.0f)
748 horizontalLineTo(19.0f)
749 verticalLineTo(19.0f)
750 close()
751 moveTo(18.0f, 9.0f)
752 lineTo(16.6f, 7.6f)
753 lineTo(13.3f, 10.9f)
754 lineTo(10.0f, 14.2f)
755 lineTo(7.4f, 11.6f)
756 lineTo(6.0f, 13.0f)
757 lineTo(10.0f, 17.0f)
758 lineTo(18.0f, 9.0f)
759 close()
760 }
761 }
762 return _checkboxOn!!
763 }
764
765 private var _checkboxOn: ImageVector? = null
766
767 private val CheckboxOff: ImageVector
768 get() {
769 if (_checkboxOff != null) {
770 return _checkboxOff!!
771 }
772 _checkboxOff =
<lambda>null773 materialIcon(name = "CheckboxOff") {
774 materialPath {
775 moveTo(19.0f, 5.0f)
776 verticalLineTo(19.0f)
777 horizontalLineTo(5.0f)
778 verticalLineTo(5.0f)
779 horizontalLineTo(19.0f)
780 close()
781 moveTo(19.0f, 3.0f)
782 horizontalLineTo(5.0f)
783 curveTo(3.9f, 3.0f, 3.0f, 3.9f, 3.0f, 5.0f)
784 verticalLineTo(19.0f)
785 curveTo(3.0f, 20.1f, 3.9f, 21.0f, 5.0f, 21.0f)
786 horizontalLineTo(19.0f)
787 curveTo(20.1f, 21.0f, 21.0f, 20.1f, 21.0f, 19.0f)
788 verticalLineTo(5.0f)
789 curveTo(21.0f, 3.9f, 20.1f, 3.0f, 19.0f, 3.0f)
790 close()
791 }
792 }
793 return _checkboxOff!!
794 }
795
796 private var _checkboxOff: ImageVector? = null
797 }
798
799 /** Default [ToggleChipColors] implementation. */
800 @Immutable
801 private class DefaultToggleChipColors(
802 private val checkedBackgroundPainter: Painter,
803 private val checkedContentColor: Color,
804 private val checkedSecondaryContentColor: Color,
805 private val checkedIconColor: Color,
806 private val disabledCheckedBackgroundPainter: Painter,
807 private val disabledCheckedContentColor: Color,
808 private val disabledCheckedSecondaryContentColor: Color,
809 private val disabledCheckedIconColor: Color,
810 private val uncheckedBackgroundPainter: Painter,
811 private val uncheckedContentColor: Color,
812 private val uncheckedSecondaryContentColor: Color,
813 private val uncheckedIconColor: Color,
814 private val disabledUncheckedBackgroundPainter: Painter,
815 private val disabledUncheckedContentColor: Color,
816 private val disabledUncheckedSecondaryContentColor: Color,
817 private val disabledUncheckedIconColor: Color,
818 ) : ToggleChipColors {
819
820 @Composable
backgroundnull821 override fun background(enabled: Boolean, checked: Boolean): State<Painter> {
822 return rememberUpdatedState(
823 if (enabled) {
824 if (checked) checkedBackgroundPainter else uncheckedBackgroundPainter
825 } else {
826 if (checked) disabledCheckedBackgroundPainter
827 else disabledUncheckedBackgroundPainter
828 }
829 )
830 }
831
832 @Composable
contentColornull833 override fun contentColor(enabled: Boolean, checked: Boolean): State<Color> {
834 return rememberUpdatedState(
835 if (enabled) {
836 if (checked) checkedContentColor else uncheckedContentColor
837 } else {
838 if (checked) disabledCheckedContentColor else disabledUncheckedContentColor
839 }
840 )
841 }
842
843 @Composable
secondaryContentColornull844 override fun secondaryContentColor(enabled: Boolean, checked: Boolean): State<Color> {
845 return rememberUpdatedState(
846 if (enabled) {
847 if (checked) checkedSecondaryContentColor else uncheckedSecondaryContentColor
848 } else {
849 if (checked) disabledCheckedSecondaryContentColor
850 else disabledUncheckedSecondaryContentColor
851 }
852 )
853 }
854
855 @Composable
toggleControlColornull856 override fun toggleControlColor(enabled: Boolean, checked: Boolean): State<Color> {
857 return rememberUpdatedState(
858 if (enabled) {
859 if (checked) checkedIconColor else uncheckedIconColor
860 } else {
861 if (checked) disabledCheckedIconColor else disabledUncheckedIconColor
862 }
863 )
864 }
865
equalsnull866 override fun equals(other: Any?): Boolean {
867 if (this === other) return true
868 if (other == null) return false
869 if (this::class != other::class) return false
870
871 other as DefaultToggleChipColors
872
873 if (checkedBackgroundPainter != other.checkedBackgroundPainter) return false
874 if (checkedContentColor != other.checkedContentColor) return false
875 if (checkedIconColor != other.checkedIconColor) return false
876 if (checkedSecondaryContentColor != other.checkedSecondaryContentColor) return false
877 if (uncheckedBackgroundPainter != other.uncheckedBackgroundPainter) return false
878 if (uncheckedContentColor != other.uncheckedContentColor) return false
879 if (uncheckedIconColor != other.uncheckedIconColor) return false
880 if (uncheckedSecondaryContentColor != other.uncheckedSecondaryContentColor) return false
881 if (disabledCheckedBackgroundPainter != other.disabledCheckedBackgroundPainter) return false
882 if (disabledCheckedContentColor != other.disabledCheckedContentColor) return false
883 if (disabledCheckedIconColor != other.disabledCheckedIconColor) return false
884 if (disabledCheckedSecondaryContentColor != other.disabledCheckedSecondaryContentColor)
885 return false
886 if (disabledUncheckedBackgroundPainter != other.disabledUncheckedBackgroundPainter)
887 return false
888 if (disabledUncheckedContentColor != other.disabledUncheckedContentColor) return false
889 if (disabledUncheckedIconColor != other.disabledUncheckedIconColor) return false
890 if (disabledUncheckedSecondaryContentColor != other.disabledUncheckedSecondaryContentColor)
891 return false
892
893 return true
894 }
895
hashCodenull896 override fun hashCode(): Int {
897 var result = checkedBackgroundPainter.hashCode()
898 result = 31 * result + checkedContentColor.hashCode()
899 result = 31 * result + checkedSecondaryContentColor.hashCode()
900 result = 31 * result + checkedIconColor.hashCode()
901 result = 31 * result + uncheckedBackgroundPainter.hashCode()
902 result = 31 * result + uncheckedContentColor.hashCode()
903 result = 31 * result + uncheckedSecondaryContentColor.hashCode()
904 result = 31 * result + uncheckedIconColor.hashCode()
905 result = 31 * result + disabledCheckedBackgroundPainter.hashCode()
906 result = 31 * result + disabledCheckedContentColor.hashCode()
907 result = 31 * result + disabledCheckedSecondaryContentColor.hashCode()
908 result = 31 * result + disabledCheckedIconColor.hashCode()
909 result = 31 * result + disabledUncheckedBackgroundPainter.hashCode()
910 result = 31 * result + disabledUncheckedContentColor.hashCode()
911 result = 31 * result + disabledUncheckedSecondaryContentColor.hashCode()
912 result = 31 * result + disabledUncheckedIconColor.hashCode()
913 return result
914 }
915 }
916
917 /** Default [SplitToggleChipColors] implementation. */
918 @Immutable
919 private class DefaultSplitToggleChipColors(
920 private val backgroundColor: Color,
921 private val contentColor: Color,
922 private val secondaryContentColor: Color,
923 private val checkedIconColor: Color,
924 private val checkedSplitBackgroundOverlay: Color,
925 private val disabledBackgroundColor: Color,
926 private val disabledContentColor: Color,
927 private val disabledSecondaryContentColor: Color,
928 private val disabledCheckedIconColor: Color,
929 private val disabledCheckedSplitBackgroundOverlay: Color,
930 private val uncheckedIconColor: Color,
931 private val uncheckedSplitBackgroundOverlay: Color,
932 private val disabledUncheckedIconColor: Color,
933 private val disabledUncheckedSplitBackgroundOverlay: Color,
934 ) : SplitToggleChipColors {
935
936 @Composable
backgroundColornull937 override fun backgroundColor(enabled: Boolean): State<Color> {
938 return rememberUpdatedState(if (enabled) backgroundColor else disabledBackgroundColor)
939 }
940
941 @Composable
contentColornull942 override fun contentColor(enabled: Boolean): State<Color> {
943 return rememberUpdatedState(if (enabled) contentColor else disabledContentColor)
944 }
945
946 @Composable
secondaryContentColornull947 override fun secondaryContentColor(enabled: Boolean): State<Color> {
948 return rememberUpdatedState(
949 if (enabled) secondaryContentColor else disabledSecondaryContentColor
950 )
951 }
952
953 @Composable
toggleControlColornull954 override fun toggleControlColor(enabled: Boolean, checked: Boolean): State<Color> {
955 return rememberUpdatedState(
956 if (enabled) {
957 if (checked) checkedIconColor else uncheckedIconColor
958 } else {
959 if (checked) disabledCheckedIconColor else disabledUncheckedIconColor
960 }
961 )
962 }
963
964 @Composable
splitBackgroundOverlaynull965 override fun splitBackgroundOverlay(enabled: Boolean, checked: Boolean): State<Color> {
966 return rememberUpdatedState(
967 if (enabled) {
968 if (checked) checkedSplitBackgroundOverlay else uncheckedSplitBackgroundOverlay
969 } else {
970 if (checked) disabledCheckedSplitBackgroundOverlay
971 else disabledUncheckedSplitBackgroundOverlay
972 }
973 )
974 }
975
equalsnull976 override fun equals(other: Any?): Boolean {
977 if (this === other) return true
978 if (other == null) return false
979 if (this::class != other::class) return false
980
981 other as DefaultSplitToggleChipColors
982
983 if (backgroundColor != other.backgroundColor) return false
984 if (contentColor != other.contentColor) return false
985 if (checkedIconColor != other.checkedIconColor) return false
986 if (checkedSplitBackgroundOverlay != other.checkedSplitBackgroundOverlay) return false
987 if (uncheckedIconColor != other.uncheckedIconColor) return false
988 if (uncheckedSplitBackgroundOverlay != other.uncheckedSplitBackgroundOverlay) return false
989 if (disabledBackgroundColor != other.disabledBackgroundColor) return false
990 if (disabledContentColor != other.disabledContentColor) return false
991 if (disabledCheckedIconColor != other.disabledCheckedIconColor) return false
992 if (disabledSecondaryContentColor != other.disabledSecondaryContentColor) return false
993 if (disabledCheckedSplitBackgroundOverlay != other.disabledCheckedSplitBackgroundOverlay)
994 return false
995 if (disabledUncheckedIconColor != other.disabledUncheckedIconColor) return false
996 if (
997 disabledUncheckedSplitBackgroundOverlay != other.disabledUncheckedSplitBackgroundOverlay
998 )
999 return false
1000
1001 return true
1002 }
1003
hashCodenull1004 override fun hashCode(): Int {
1005 var result = backgroundColor.hashCode()
1006 result = 31 * result + contentColor.hashCode()
1007 result = 31 * result + secondaryContentColor.hashCode()
1008 result = 31 * result + checkedIconColor.hashCode()
1009 result = 31 * result + checkedSplitBackgroundOverlay.hashCode()
1010 result = 31 * result + uncheckedIconColor.hashCode()
1011 result = 31 * result + uncheckedSplitBackgroundOverlay.hashCode()
1012 result = 31 * result + disabledBackgroundColor.hashCode()
1013 result = 31 * result + disabledContentColor.hashCode()
1014 result = 31 * result + disabledSecondaryContentColor.hashCode()
1015 result = 31 * result + disabledCheckedIconColor.hashCode()
1016 result = 31 * result + disabledCheckedSplitBackgroundOverlay.hashCode()
1017 result = 31 * result + disabledUncheckedIconColor.hashCode()
1018 result = 31 * result + disabledUncheckedSplitBackgroundOverlay.hashCode()
1019 return result
1020 }
1021 }
1022
1023 private val TOGGLE_CONTROL_HEIGHT = 24.dp
1024 private val TOGGLE_CONTROL_WIDTH = 24.dp
1025 private val TOGGLE_CONTROL_SPACING = 4.dp
1026 private val ICON_SPACING = 6.dp
1027