| /* |
| * Copyright 2023 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package androidx.wear.compose.material3 |
| |
| import androidx.compose.foundation.BorderStroke |
| import androidx.compose.foundation.interaction.Interaction |
| import androidx.compose.foundation.interaction.MutableInteractionSource |
| import androidx.compose.foundation.layout.BoxScope |
| import androidx.compose.runtime.Composable |
| import androidx.compose.runtime.Immutable |
| import androidx.compose.runtime.State |
| import androidx.compose.runtime.remember |
| import androidx.compose.runtime.rememberUpdatedState |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.graphics.Color |
| import androidx.compose.ui.graphics.Shape |
| import androidx.wear.compose.material3.tokens.FilledTextButtonTokens |
| import androidx.wear.compose.material3.tokens.FilledTonalTextButtonTokens |
| import androidx.wear.compose.material3.tokens.OutlinedTextButtonTokens |
| import androidx.wear.compose.material3.tokens.TextButtonTokens |
| import androidx.wear.compose.material3.tokens.TextToggleButtonTokens |
| |
| /** |
| * Wear Material [TextButton] is a circular, text-only button with transparent background and |
| * no border. It offers a single slot for text. |
| * |
| * Set the size of the [TextButton] with [Modifier.touchTargetAwareSize] |
| * to ensure that the recommended minimum touch target size is available. The recommended |
| * [TextButton] sizes are [TextButtonDefaults.DefaultButtonSize], |
| * [TextButtonDefaults.LargeButtonSize] and [TextButtonDefaults.SmallButtonSize]. |
| * [TextButton] uses [Typography.labelMedium] by default and this should be |
| * overridden to [Typography.labelLarge] when using [TextButtonDefaults.LargeButtonSize]. |
| * |
| * The default [TextButton] has no border and a transparent background for low emphasis actions. |
| * For actions that require high emphasis, set [colors] to |
| * [TextButtonDefaults.filledTextButtonColors]. |
| * For a medium-emphasis, outlined [TextButton], set [border] to |
| * [ButtonDefaults.outlinedButtonBorder]. |
| * For a middle ground between outlined and filled, set [colors] to |
| * [TextButtonDefaults.filledTonalTextButtonColors]. |
| * |
| * [TextButton] can be enabled or disabled. A disabled button will not respond to click events. |
| * |
| * TODO(b/261838497) Add Material3 UX guidance links |
| * |
| * Example of a [TextButton]: |
| * @sample androidx.wear.compose.material3.samples.TextButtonSample |
| * |
| * Example of a large, filled tonal [TextButton]: |
| * @sample androidx.wear.compose.material3.samples.LargeFilledTonalTextButtonSample |
| * |
| * @param onClick Will be called when the user clicks the button. |
| * @param modifier Modifier to be applied to the button. |
| * @param enabled Controls the enabled state of the button. When `false`, this button will not |
| * be clickable. |
| * @param shape Defines the text button's shape. It is strongly recommended to use the default |
| * as this shape is a key characteristic of the Wear Material3 Theme. |
| * @param colors [TextButtonColors] that will be used to resolve the background and content color |
| * for this button in different states. |
| * @param border Optional [BorderStroke] that will be used to resolve the text button border in |
| * different states. See [ButtonDefaults.outlinedButtonBorder]. |
| * @param interactionSource The [MutableInteractionSource] representing the stream of |
| * [Interaction]s for this Button. You can create and pass in your own remembered |
| * [MutableInteractionSource] if you want to observe [Interaction]s and customize the |
| * appearance / behavior of this Button in different [Interaction]s. |
| * @param content The content displayed on the text button, expected to be text or image. |
| */ |
| @Composable |
| fun TextButton( |
| onClick: () -> Unit, |
| modifier: Modifier = Modifier, |
| enabled: Boolean = true, |
| shape: Shape = TextButtonDefaults.shape, |
| colors: TextButtonColors = TextButtonDefaults.textButtonColors(), |
| border: BorderStroke? = null, |
| interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, |
| content: @Composable BoxScope.() -> Unit, |
| ) { |
| androidx.wear.compose.materialcore.Button( |
| onClick = onClick, |
| modifier.minimumInteractiveComponentSize(), |
| enabled = enabled, |
| backgroundColor = { colors.containerColor(enabled = it) }, |
| interactionSource = interactionSource, |
| shape = shape, |
| border = { rememberUpdatedState(border) }, |
| buttonSize = TextButtonDefaults.DefaultButtonSize, |
| ripple = rippleOrFallbackImplementation(), |
| content = provideScopeContent( |
| colors.contentColor(enabled = enabled), |
| TextButtonTokens.ContentFont.value, |
| content |
| ) |
| ) |
| } |
| |
| /** |
| * Wear Material [TextToggleButton] is a filled text toggle button which switches between primary |
| * colors and tonal colors depending on [checked] value, and offers a single slot for |
| * text. |
| * |
| * Set the size of the [TextToggleButton] with Modifier.[touchTargetAwareSize] |
| * to ensure that the background padding will correctly reach the edge of the minimum touch target. |
| * The recommended [TextToggleButton] sizes are [TextButtonDefaults.DefaultButtonSize], |
| * [TextButtonDefaults.LargeButtonSize] and [TextButtonDefaults.SmallButtonSize]. |
| * [TextToggleButton] uses [Typography.labelMedium] by default and this should be overridden to |
| * [Typography.labelLarge] when using [TextButtonDefaults.LargeButtonSize]. |
| * |
| * [TextToggleButton] can be enabled or disabled. A disabled button will not respond to |
| * click events. When enabled, the checked and unchecked events are propagated by [onCheckedChange]. |
| * |
| * A simple text toggle button using the default colors: |
| * @sample androidx.wear.compose.material3.samples.TextToggleButtonSample |
| * |
| * Example of a large text toggle button: |
| * @sample androidx.wear.compose.material3.samples.LargeTextToggleButtonSample |
| * |
| * @param checked Boolean flag indicating whether this toggle button is currently checked. |
| * @param onCheckedChange Callback to be invoked when this toggle button is clicked. |
| * @param modifier Modifier to be applied to the toggle button. |
| * @param enabled Controls the enabled state of the toggle button. When `false`, |
| * this toggle button will not be clickable. |
| * @param colors [ToggleButtonColors] that will be used to resolve the container and |
| * content color for this toggle button. |
| * @param interactionSource The [MutableInteractionSource] representing the stream of |
| * [Interaction]s for this toggle button. You can create and pass in your own remembered |
| * [MutableInteractionSource] if you want to observe [Interaction]s and customize the |
| * appearance / behavior of this ToggleButton in different [Interaction]s. |
| * @param shape Defines the shape for this toggle button. It is strongly recommended to use the |
| * default as this shape is a key characteristic of the Wear Material 3 Theme. |
| * @param border Optional [BorderStroke] for the [TextToggleButton]. |
| * @param content The text to be drawn inside the toggle button. |
| */ |
| @Composable |
| fun TextToggleButton( |
| checked: Boolean, |
| onCheckedChange: (Boolean) -> Unit, |
| modifier: Modifier = Modifier, |
| enabled: Boolean = true, |
| colors: ToggleButtonColors = TextButtonDefaults.textToggleButtonColors(), |
| interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, |
| shape: Shape = TextButtonDefaults.shape, |
| border: BorderStroke? = null, |
| content: @Composable BoxScope.() -> Unit, |
| ) { |
| androidx.wear.compose.materialcore.ToggleButton( |
| checked = checked, |
| onCheckedChange = onCheckedChange, |
| modifier = modifier.minimumInteractiveComponentSize(), |
| enabled = enabled, |
| backgroundColor = { isEnabled, isChecked -> |
| colors.containerColor(enabled = isEnabled, checked = isChecked) |
| }, |
| border = { _, _ -> rememberUpdatedState(border) }, |
| toggleButtonSize = TextButtonDefaults.DefaultButtonSize, |
| interactionSource = interactionSource, |
| shape = shape, |
| ripple = rippleOrFallbackImplementation(), |
| content = provideScopeContent( |
| colors.contentColor(enabled = enabled, checked = checked), |
| TextToggleButtonTokens.ContentFont.value, |
| content |
| ) |
| ) |
| } |
| |
| /** |
| * Contains the default values used by [TextButton]. |
| */ |
| object TextButtonDefaults { |
| /** |
| * Recommended [Shape] for [TextButton]. |
| */ |
| val shape: Shape @Composable get() = TextButtonTokens.ContainerShape.value |
| /** |
| * Creates a [TextButtonColors] with the colors for a filled [TextButton]- by default, a |
| * colored background with a contrasting content color. |
| * If the text button is disabled then the colors will default to [ColorScheme.onSurface] with |
| * suitable alpha values applied. |
| * |
| * Example of [TextButton] with [filledTextButtonColors]: |
| * @sample androidx.wear.compose.material3.samples.FilledTextButtonSample |
| * |
| * @param containerColor The background color of this text button when enabled |
| * @param contentColor The content color of this text button when enabled |
| * @param disabledContainerColor the background color of this text button when not enabled |
| * @param disabledContentColor the content color of this text button when not enabled |
| */ |
| @Composable |
| fun filledTextButtonColors( |
| containerColor: Color = FilledTextButtonTokens.ContainerColor.value, |
| contentColor: Color = FilledTextButtonTokens.ContentColor.value, |
| disabledContainerColor: Color = |
| FilledTextButtonTokens.DisabledContainerColor.value.toDisabledColor( |
| disabledAlpha = FilledTextButtonTokens.DisabledContainerOpacity |
| ), |
| disabledContentColor: Color = |
| FilledTextButtonTokens.DisabledContentColor.value.toDisabledColor( |
| disabledAlpha = FilledTextButtonTokens.DisabledContentOpacity |
| ), |
| ): TextButtonColors { |
| return textButtonColors( |
| containerColor = containerColor, |
| contentColor = contentColor, |
| disabledContainerColor = disabledContainerColor, |
| disabledContentColor = disabledContentColor |
| ) |
| } |
| |
| /** |
| * Creates a [TextButtonColors] with the colors for a filled, tonal [TextButton]- by default, |
| * a muted colored background with a contrasting content color. |
| * If the text button is disabled then the colors will default to [ColorScheme.onSurface] with |
| * suitable alpha values applied. |
| * |
| * Example of [TextButton] with [filledTonalTextButtonColors]: |
| * @sample androidx.wear.compose.material3.samples.FilledTonalTextButtonSample |
| * |
| * @param containerColor The background color of this text button when enabled |
| * @param contentColor The content color of this text button when enabled |
| * @param disabledContainerColor the background color of this text button when not enabled |
| * @param disabledContentColor the content color of this text button when not enabled |
| */ |
| @Composable |
| fun filledTonalTextButtonColors( |
| containerColor: Color = FilledTonalTextButtonTokens.ContainerColor.value, |
| contentColor: Color = FilledTonalTextButtonTokens.ContentColor.value, |
| disabledContainerColor: Color = |
| FilledTonalTextButtonTokens.DisabledContainerColor.value.toDisabledColor( |
| disabledAlpha = FilledTonalTextButtonTokens.DisabledContainerOpacity |
| ), |
| disabledContentColor: Color = |
| FilledTonalTextButtonTokens.DisabledContentColor.value.toDisabledColor( |
| disabledAlpha = FilledTonalTextButtonTokens.DisabledContentOpacity |
| ), |
| ): TextButtonColors { |
| return textButtonColors( |
| containerColor = containerColor, |
| contentColor = contentColor, |
| disabledContainerColor = disabledContainerColor, |
| disabledContentColor = disabledContentColor |
| ) |
| } |
| |
| /** |
| * Creates a [TextButtonColors] with the colors for an outlined [TextButton]- by default, |
| * a transparent background with contrasting content color. If the button is disabled, |
| * then the colors will default to [ColorScheme.onSurface] with suitable alpha values applied. |
| * |
| * Example of [TextButton] with [outlinedTextButtonColors] and |
| * [ButtonDefaults.outlinedButtonBorder]: |
| * @sample androidx.wear.compose.material3.samples.OutlinedTextButtonSample |
| * |
| * @param contentColor The content color of this text button when enabled |
| * @param disabledContentColor The content color of this text button when not enabled |
| */ |
| @Composable |
| fun outlinedTextButtonColors( |
| contentColor: Color = OutlinedTextButtonTokens.ContentColor.value, |
| disabledContentColor: Color = |
| OutlinedTextButtonTokens.DisabledContentColor.value.toDisabledColor( |
| disabledAlpha = OutlinedTextButtonTokens.DisabledContentOpacity |
| ), |
| ): TextButtonColors { |
| return textButtonColors( |
| containerColor = Color.Transparent, |
| contentColor = contentColor, |
| disabledContentColor = disabledContentColor |
| ) |
| } |
| |
| /** |
| * Creates a [TextButtonColors] for a text button - by default, a transparent |
| * background with contrasting content color. If the button is disabled |
| * then the colors default to [ColorScheme.onSurface] with suitable alpha values applied. |
| * |
| * @param containerColor the background color of this text button when enabled |
| * @param contentColor the content color of this text button when enabled |
| * @param disabledContainerColor the background color of this text button when not enabled |
| * @param disabledContentColor the content color of this text button when not enabled |
| */ |
| @Composable |
| fun textButtonColors( |
| containerColor: Color = Color.Transparent, |
| contentColor: Color = TextButtonTokens.ContentColor.value, |
| disabledContainerColor: Color = Color.Transparent, |
| disabledContentColor: Color = |
| TextButtonTokens.DisabledContentColor.value.toDisabledColor( |
| disabledAlpha = TextButtonTokens.DisabledContentOpacity |
| ), |
| ): TextButtonColors = TextButtonColors( |
| containerColor = containerColor, |
| contentColor = contentColor, |
| disabledContainerColor = disabledContainerColor, |
| disabledContentColor = disabledContentColor, |
| ) |
| |
| /** |
| * Creates a [ToggleButtonColors] for a [TextToggleButton] |
| * - by default, a colored background with a contrasting content color. |
| * If the button is disabled, then the |
| * colors will have an alpha ([ContentAlpha.disabled]) value applied. |
| * |
| * @param checkedContainerColor the container color of this [TextToggleButton] when enabled |
| * and checked |
| * @param checkedContentColor the content color of this [TextToggleButton] when enabled and |
| * checked |
| * @param uncheckedContainerColor the container color of this [TextToggleButton] when enabled |
| * and unchecked |
| * @param uncheckedContentColor the content color of this [TextToggleButton] when enabled and |
| * unchecked |
| * @param disabledCheckedContainerColor the container color of this [TextToggleButton] when |
| * checked and not enabled |
| * @param disabledCheckedContentColor the content color of this [TextToggleButton] when checked |
| * and not enabled |
| * @param disabledUncheckedContainerColor the container color of this [TextToggleButton] when |
| * unchecked and not enabled |
| * @param disabledUncheckedContentColor the content color of this [TextToggleButton] when |
| * unchecked and not enabled |
| */ |
| @Composable |
| fun textToggleButtonColors( |
| checkedContainerColor: Color = TextToggleButtonTokens.CheckedContainerColor.value, |
| checkedContentColor: Color = TextToggleButtonTokens.CheckedContentColor.value, |
| uncheckedContainerColor: Color = TextToggleButtonTokens.UncheckedContainerColor.value, |
| uncheckedContentColor: Color = TextToggleButtonTokens.UncheckedContentColor.value, |
| disabledCheckedContainerColor: Color = |
| TextToggleButtonTokens.DisabledCheckedContainerColor.value.toDisabledColor( |
| disabledAlpha = TextToggleButtonTokens.DisabledCheckedContainerOpacity |
| ), |
| disabledCheckedContentColor: Color = |
| TextToggleButtonTokens.DisabledCheckedContentColor.value.toDisabledColor( |
| disabledAlpha = TextToggleButtonTokens.DisabledCheckedContentOpacity |
| ), |
| disabledUncheckedContainerColor: Color = |
| TextToggleButtonTokens.DisabledUncheckedContainerColor.value.toDisabledColor( |
| disabledAlpha = TextToggleButtonTokens.DisabledUncheckedContainerOpacity |
| ), |
| disabledUncheckedContentColor: Color = |
| TextToggleButtonTokens.DisabledUncheckedContentColor.value.toDisabledColor( |
| disabledAlpha = TextToggleButtonTokens.DisabledUncheckedContentOpacity |
| ), |
| ): ToggleButtonColors { |
| return ToggleButtonColors( |
| checkedContainerColor = checkedContainerColor, |
| checkedContentColor = checkedContentColor, |
| uncheckedContainerColor = uncheckedContainerColor, |
| uncheckedContentColor = uncheckedContentColor, |
| disabledCheckedContainerColor = disabledCheckedContainerColor, |
| disabledCheckedContentColor = disabledCheckedContentColor, |
| disabledUncheckedContainerColor = disabledUncheckedContainerColor, |
| disabledUncheckedContentColor = disabledUncheckedContentColor, |
| ) |
| } |
| |
| /** |
| * The recommended size for a small button - for this size, it is recommended to set |
| * the text style to [Typography.labelMedium]. It is recommended to apply this size |
| * using [Modifier.touchTargetAwareSize]. |
| */ |
| val SmallButtonSize = TextButtonTokens.ContainerSmallSize |
| |
| /** |
| * The default size applied for buttons. |
| * It is recommended to apply this size using [Modifier.touchTargetAwareSize]. |
| */ |
| val DefaultButtonSize = TextButtonTokens.ContainerDefaultSize |
| |
| /** |
| * The recommended size for a large button. |
| * It is recommended to apply this size using [Modifier.touchTargetAwareSize]. |
| */ |
| val LargeButtonSize = TextButtonTokens.ContainerLargeSize |
| } |
| |
| /** |
| * Represents the container and content colors used in a text button in different states. |
| * |
| * See [TextButtonDefaults.filledTextButtonColors], |
| * [TextButtonDefaults.filledTonalTextButtonColors], [TextButtonDefaults.textButtonColors] and |
| * [TextButtonDefaults.outlinedTextButtonColors] for [TextButtonColors] with different levels |
| * of emphasis. |
| * |
| * @param containerColor the background color of this text button when enabled. |
| * @param contentColor the content color of this text button when enabled. |
| * @param disabledContainerColor the background color of this text button when not enabled. |
| * @param disabledContentColor the content color of this text button when not enabled. |
| */ |
| @Immutable |
| class TextButtonColors( |
| val containerColor: Color, |
| val contentColor: Color, |
| val disabledContainerColor: Color, |
| val disabledContentColor: Color, |
| ) { |
| /** |
| * Represents the container color for this text button, depending on [enabled]. |
| * |
| * @param enabled whether the text button is enabled |
| */ |
| @Composable |
| internal fun containerColor(enabled: Boolean): State<Color> { |
| return rememberUpdatedState(if (enabled) containerColor else disabledContainerColor) |
| } |
| |
| /** |
| * Represents the content color for this text button, depending on [enabled]. |
| * |
| * @param enabled whether the text button is enabled |
| */ |
| @Composable |
| internal fun contentColor(enabled: Boolean): State<Color> { |
| return rememberUpdatedState(if (enabled) contentColor else disabledContentColor) |
| } |
| |
| override fun equals(other: Any?): Boolean { |
| if (this === other) return true |
| if (other == null || other !is TextButtonColors) return false |
| |
| if (containerColor != other.containerColor) return false |
| if (contentColor != other.contentColor) return false |
| if (disabledContainerColor != other.disabledContainerColor) return false |
| if (disabledContentColor != other.disabledContentColor) return false |
| |
| return true |
| } |
| |
| override fun hashCode(): Int { |
| var result = containerColor.hashCode() |
| result = 31 * result + contentColor.hashCode() |
| result = 31 * result + disabledContainerColor.hashCode() |
| result = 31 * result + disabledContentColor.hashCode() |
| |
| return result |
| } |
| } |