/*
* 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.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.takeOrElse
import androidx.wear.compose.material3.tokens.ColorSchemeKeyTokens
import androidx.wear.compose.material3.tokens.ColorTokens
/**
* A [ColorScheme] holds all the named color parameters for a [MaterialTheme].
*
* Color schemes are designed to be harmonious, ensure accessible text, and distinguish UI
* elements and surfaces from one another.
*
* The Material color system and custom schemes provide default values for color as a starting point
* for customization.
*
* To learn more about color schemes,
* see [Material Design Color System](https://m3.material.io/styles/color/the-color-system/color-roles).
*
* @property primary The primary color is the color displayed most frequently across your app’s
* screens and components.
* @property primaryDim is less prominent than [primary] for component backgrounds
* @property primaryContainer is a standout container color for key components.
* @property onPrimary Color used for text and icons displayed on top of the primary color.
* @property onPrimaryContainer The color (and state variants) that should be used for content on
* top of [primaryContainer].
* @property secondary The secondary color provides more ways to accent and distinguish your
* product.
* @property secondaryDim is less prominent than [secondary] for component backgrounds.
* @property secondaryContainer A tonal color to be used in containers.
* @property onSecondary Color used for text and icons displayed on top of the secondary color.
* @property onSecondaryContainer The color (and state variants) that should be used for content on
* top of [secondaryContainer].
* @property tertiary The tertiary color that can be used to balance primary and secondary
* colors, or bring heightened attention to an element.
* @property tertiaryDim A less prominent tertiary color that can be used to balance
* primary and secondary colors, or bring heightened attention to an element.
* @property tertiaryContainer A tonal color to be used in containers.
* @property onTertiary Color used for text and icons displayed on top of the tertiary color.
* @property onTertiaryContainer The color (and state variants) that should be used for content on
* top of [tertiaryContainer].
* @property surfaceDim A surface color used for large containment components
* such as Card and Button with low prominence.
* @property surface The main surface color that affect surfaces of components with large
* containment areas, such as Card and Button.
* @property surfaceBright A surface color used for large containment components
* such Card and Button with high prominence.
* @property onSurface Color used for text and icons displayed on top of the surface color.
* @property onSurfaceVariant The color for secondary text and icons on top of
* [surface].
* @property outline The main color for primary outline components.
* The outline color role adds contrast for accessibility purposes.
* @property outlineVariant The secondary color for secondary outline components.
* @property background The background color that appears behind other content.
* @property onBackground Color used for text and icons displayed on top of the background color.
* @property error The error color is used to indicate errors.
* @property onError Color used for text and icons displayed on top of the error color.
*/@Stable
public class ColorScheme(
primary: Color = ColorTokens.Primary,
primaryDim: Color = ColorTokens.PrimaryDim,
primaryContainer: Color = ColorTokens.PrimaryContainer,
onPrimary: Color = ColorTokens.OnPrimary,
onPrimaryContainer: Color = ColorTokens.OnPrimaryContainer,
secondary: Color = ColorTokens.Secondary,
secondaryDim: Color = ColorTokens.SecondaryDim,
secondaryContainer: Color = ColorTokens.SecondaryContainer,
onSecondary: Color = ColorTokens.OnSecondary,
onSecondaryContainer: Color = ColorTokens.OnSecondaryContainer,
tertiary: Color = ColorTokens.Tertiary,
tertiaryDim: Color = ColorTokens.TertiaryDim,
tertiaryContainer: Color = ColorTokens.TertiaryContainer,
onTertiary: Color = ColorTokens.OnTertiary,
onTertiaryContainer: Color = ColorTokens.OnTertiaryContainer,
surfaceDim: Color = ColorTokens.SurfaceDim,
surface: Color = ColorTokens.Surface,
surfaceBright: Color = ColorTokens.SurfaceBright,
onSurface: Color = ColorTokens.OnSurface,
onSurfaceVariant: Color = ColorTokens.OnSurfaceVariant,
outline: Color = ColorTokens.Outline,
outlineVariant: Color = ColorTokens.OutlineVariant,
background: Color = ColorTokens.Background,
onBackground: Color = ColorTokens.OnBackground,
error: Color = ColorTokens.Error,
onError: Color = ColorTokens.OnError,
) {
/**
* [primary] is the main color used across screens and components
*/
public var primary: Color by mutableStateOf(primary)
internal set
/**
* [primaryDim] is less prominent than [primary] for component backgrounds
*/
public var primaryDim: Color by mutableStateOf(primaryDim)
internal set
/**
* [primaryContainer] is a standout container color for key components
*/
public var primaryContainer: Color by mutableStateOf(primaryContainer)
internal set
/**
* [onPrimary] is for text and icons shown against the Primary and primaryDim colors
*/
public var onPrimary: Color by mutableStateOf(onPrimary)
internal set
/**
* [onPrimaryContainer] is a contrast-passing color shown against the primaryContainer
*/
public var onPrimaryContainer: Color by mutableStateOf(onPrimaryContainer)
internal set
/**
* [secondary] is an accent color used across screens and components
*/
public var secondary: Color by mutableStateOf(secondary)
internal set
/**
* [secondaryDim] is less prominent than [secondary] for component backgrounds
*/
public var secondaryDim: Color by mutableStateOf(secondaryDim)
/**
* [secondaryContainer] is a less prominent container color than [primaryContainer],
* for components like tonal buttons
*/
public var secondaryContainer: Color by mutableStateOf(secondaryContainer)
internal set
/**
* [onSecondary] is for text and icons shown against the Secondary and SecondaryDim colors
*/
public var onSecondary: Color by mutableStateOf(onSecondary)
internal set
/**
* [onSecondaryContainer] is a contrast-passing color shown against the secondaryContainer
*/
public var onSecondaryContainer: Color by mutableStateOf(onSecondaryContainer)
internal set
/**
* [tertiary] is a complementary color to create contrast and draw attention to elements
*/
public var tertiary: Color by mutableStateOf(tertiary)
internal set
/**
* [tertiaryDim] is less prominent than [tertiary] for component backgrounds
*/
public var tertiaryDim: Color by mutableStateOf(tertiaryDim)
internal set
/**
* [tertiaryContainer] is a contrasting container color for components
*/
public var tertiaryContainer: Color by mutableStateOf(tertiaryContainer)
internal set
/**
* [onTertiary] is for text and icons shown against the Tertiary and tertiaryDim colors
*/
public var onTertiary: Color by mutableStateOf(onTertiary)
internal set
/**
* [onTertiaryContainer] is a contrast-passing color shown against the tertiaryContainer
*/
public var onTertiaryContainer: Color by mutableStateOf(onTertiaryContainer)
internal set
/**
* [surfaceDim] is a surface color used for large containment components, with the
* lowest prominence behind Surface and surfaceBright
*/
public var surfaceDim: Color by mutableStateOf(surfaceDim)
internal set
/**
* [surface] is the main color for large containment components like card and button backgrounds
*/
public var surface: Color by mutableStateOf(surface)
internal set
/**
* [surfaceBright] is a surface color used for large containment components, with
* the highest prominence, ahead of Surface and surfaceDim
*/
public var surfaceBright: Color by mutableStateOf(surfaceBright)
internal set
/**
* [onSurface] for primary text and icons shown against the
* [surface], [surfaceDim] and [surfaceBright]
*/
public var onSurface: Color by mutableStateOf(onSurface)
internal set
/**
* [onSurfaceVariant] for secondary text and icons on
* [surface], [surfaceDim] and [surfaceBright]
*/
public var onSurfaceVariant: Color by mutableStateOf(onSurfaceVariant)
internal set
/**
* [outline] is the main color for primary outline components
*/
public var outline: Color by mutableStateOf(outline)
internal set
/**
* [outlineVariant] is the secondary color for secondary outline components
*/
public var outlineVariant: Color by mutableStateOf(outlineVariant)
internal set
/**
* [background] is the static color used behind all texts and components
*/
public var background: Color by mutableStateOf(background)
internal set
/**
* [onBackground] is used for text and icons shown against the background color
*/
public var onBackground: Color by mutableStateOf(onBackground)
internal set
/**
* [error] indicates errors and emergency states
*/
public var error: Color by mutableStateOf(error)
internal set
/**
* [onError] is used for text and icons on the error color
*/
public var onError: Color by mutableStateOf(onError)
internal set
/**
* Returns a copy of this Colors, optionally overriding some of the values.
*/
public fun copy(
primary: Color = this.primary,
primaryDim: Color = this.primaryDim,
primaryContainer: Color = this.primaryContainer,
onPrimary: Color = this.onPrimary,
onPrimaryContainer: Color = this.onPrimaryContainer,
secondary: Color = this.secondary,
secondaryDim: Color = this.secondaryDim,
secondaryContainer: Color = this.secondaryContainer,
onSecondary: Color = this.onSecondary,
onSecondaryContainer: Color = this.onSecondaryContainer,
tertiary: Color = this.tertiary,
tertiaryDim: Color = this.tertiaryDim,
tertiaryContainer: Color = this.tertiaryContainer,
onTertiary: Color = this.onTertiary,
onTertiaryContainer: Color = this.onTertiaryContainer,
surfaceDim: Color = this.surfaceDim,
surface: Color = this.surface,
surfaceBright: Color = this.surfaceBright,
onSurface: Color = this.onSurface,
onSurfaceVariant: Color = this.onSurfaceVariant,
outline: Color = this.outline,
outlineVariant: Color = this.outlineVariant,
background: Color = this.background,
onBackground: Color = this.onBackground,
error: Color = this.error,
onError: Color = this.onError
): ColorScheme = ColorScheme(
primary = primary,
primaryDim = primaryDim,
primaryContainer = primaryContainer,
onPrimary = onPrimary,
onPrimaryContainer = onPrimaryContainer,
secondary = secondary,
secondaryDim = secondaryDim,
secondaryContainer = secondaryContainer,
onSecondary = onSecondary,
onSecondaryContainer = onSecondaryContainer,
tertiary = tertiary,
tertiaryDim = tertiaryDim,
tertiaryContainer = tertiaryContainer,
onTertiary = onTertiary,
onTertiaryContainer = onTertiaryContainer,
surfaceDim = surfaceDim,
surface = surface,
surfaceBright = surfaceBright,
onSurface = onSurface,
onSurfaceVariant = onSurfaceVariant,
outline = outline,
outlineVariant = outlineVariant,
background = background,
onBackground = onBackground,
error = error,
onError = onError
)
override fun toString(): String {
return "Colors(" +
"primary=$primary, " +
"primaryDim=$primaryDim, " +
"primaryContainer=$primaryContainer, " +
"onPrimary=$onPrimary, " +
"onPrimaryContainer=$onPrimaryContainer, " +
"secondary=$secondary, " +
"secondaryDim=$secondaryDim, " +
"secondaryContainer=$secondaryContainer, " +
"onSecondary=$onSecondary, " +
"onSecondaryContainer=$onSecondaryContainer, " +
"tertiary=$tertiary, " +
"tertiaryDim=$tertiaryDim, " +
"tertiaryContainer=$tertiaryContainer, " +
"onTertiary=$onTertiary, " +
"onTertiaryContainer=$onTertiaryContainer, " +
"surfaceDim=$surfaceDim, " +
"surface=$surface, " +
"surfaceBright=$surfaceBright, " +
"onSurface=$onSurface, " +
"onSurfaceVariant=$onSurfaceVariant, " +
"outline=$outline, " +
"outlineVariant=$outlineVariant, " +
"background=$background, " +
"onBackground=$onBackground, " +
"error=$error, " +
"onError=$onError" +
")"
}
}
/**
* The Material color system contains pairs of colors that are typically used for the background
* and content color inside a component. For example, a Button typically uses `primary` for its
* background, and `onPrimary` for the color of its content (usually text or iconography).
*
* This function tries to match the provided [backgroundColor] to a 'background' color in this
* [ColorScheme], and then will return the corresponding color used for content. For example, when
* [backgroundColor] is [ColorScheme.primary], this will return [ColorScheme.onPrimary].
*
* If [backgroundColor] does not match a background color in the theme, this will return
* [Color.Unspecified].
*
* @return the matching content color for [backgroundColor]. If [backgroundColor] is not present in
* the theme's [ColorScheme], then returns [Color.Unspecified].
*
* @see contentColorFor
*/
public fun ColorScheme.contentColorFor(backgroundColor: Color): Color {
return when (backgroundColor) {
primary, primaryDim -> onPrimary
primaryContainer -> onPrimaryContainer
secondary, secondaryDim -> onSecondary
secondaryContainer -> onSecondaryContainer
tertiary, tertiaryDim -> onTertiary
tertiaryContainer -> onTertiaryContainer
surface, surfaceDim, surfaceBright -> onSurface
background -> onBackground
error -> onError
else -> Color.Unspecified
}
}
/**
* The Material color system contains pairs of colors that are typically used for the background
* and content color inside a component. For example, a Button typically uses `primary` for its
* background, and `onPrimary` for the color of its content (usually text or iconography).
*
* This function tries to match the provided [backgroundColor] to a 'background' color in this
* [ColorScheme], and then will return the corresponding color used for content. For example, when
* [backgroundColor] is [ColorScheme.primary], this will return [ColorScheme.onPrimary].
*
* If [backgroundColor] does not match a background color in the theme, this will return
* the current value of [LocalContentColor] as a best-effort color.
*
* @return the matching content color for [backgroundColor]. If [backgroundColor] is not present in
* the theme's [ColorScheme], then returns the current value of [LocalContentColor].
*
* @see ColorScheme.contentColorFor
*/
@Composable
@ReadOnlyComposable
public fun contentColorFor(backgroundColor: Color): Color =
MaterialTheme.colorScheme
.contentColorFor(backgroundColor)
.takeOrElse { LocalContentColor.current }
/**
* Updates the internal values of the given [ColorScheme] with values from the [other] [ColorScheme]. This
* allows efficiently updating a subset of [ColorScheme], without recomposing every composable that
* consumes values from [LocalColorScheme].
*
* Because [ColorScheme] is very wide-reaching, and used by many expensive composables in the
* hierarchy, providing a new value to [LocalColorScheme] causes every composable consuming
* [LocalColorScheme] to recompose, which is prohibitively expensive in cases such as animating one
* color in the theme. Instead, [ColorScheme] is internally backed by [mutableStateOf], and this
* function mutates the internal state of [this] to match values in [other]. This means that any
* changes will mutate the internal state of [this], and only cause composables that are reading
* the specific changed value to recompose.
*/
internal fun ColorScheme.updateColorSchemeFrom(other: ColorScheme) {
primary = other.primary
primaryDim = other.primaryDim
primaryContainer = other.primaryContainer
onPrimary = other.onPrimary
onPrimaryContainer = other.onPrimaryContainer
secondary = other.secondary
secondaryDim = other.secondaryDim
secondaryContainer = other.secondaryContainer
onSecondary = other.onSecondary
onSecondaryContainer = other.onSecondaryContainer
tertiary = other.tertiary
tertiaryDim = other.tertiaryDim
tertiaryContainer = other.tertiaryContainer
onTertiary = other.onTertiary
onTertiaryContainer = other.onTertiaryContainer
surfaceDim = other.surfaceDim
surface = other.surface
surfaceBright = other.surfaceBright
onSurface = other.onSurface
onSurfaceVariant = other.onSurfaceVariant
outline = other.outline
outlineVariant = other.outlineVariant
background = other.background
onBackground = other.onBackground
error = other.error
onError = other.onError
}
/**
* Helper function for component color tokens. Here is an example on how to use component color
* tokens:
* ``MaterialTheme.colorScheme.fromToken(FilledButtonTokens.ContainerColor)``
*/
internal fun ColorScheme.fromToken(value: ColorSchemeKeyTokens): Color {
return when (value) {
ColorSchemeKeyTokens.Primary -> primary
ColorSchemeKeyTokens.PrimaryDim -> primaryDim
ColorSchemeKeyTokens.PrimaryContainer -> primaryContainer
ColorSchemeKeyTokens.OnPrimary -> onPrimary
ColorSchemeKeyTokens.OnPrimaryContainer -> onPrimaryContainer
ColorSchemeKeyTokens.Secondary -> secondary
ColorSchemeKeyTokens.SecondaryDim -> secondaryDim
ColorSchemeKeyTokens.SecondaryContainer -> secondaryContainer
ColorSchemeKeyTokens.OnSecondary -> onSecondary
ColorSchemeKeyTokens.OnSecondaryContainer -> onSecondaryContainer
ColorSchemeKeyTokens.Tertiary -> tertiary
ColorSchemeKeyTokens.TertiaryDim -> tertiaryDim
ColorSchemeKeyTokens.TertiaryContainer -> tertiaryContainer
ColorSchemeKeyTokens.OnTertiary -> onTertiary
ColorSchemeKeyTokens.OnTertiaryContainer -> onTertiaryContainer
ColorSchemeKeyTokens.SurfaceDim -> surfaceDim
ColorSchemeKeyTokens.Surface -> surface
ColorSchemeKeyTokens.SurfaceBright -> surfaceBright
ColorSchemeKeyTokens.OnSurface -> onSurface
ColorSchemeKeyTokens.OnSurfaceVariant -> onSurfaceVariant
ColorSchemeKeyTokens.Outline -> outline
ColorSchemeKeyTokens.OutlineVariant -> outlineVariant
ColorSchemeKeyTokens.Background -> background
ColorSchemeKeyTokens.OnBackground -> onBackground
ColorSchemeKeyTokens.Error -> error
ColorSchemeKeyTokens.OnError -> onError
}
}
internal val LocalColorScheme = staticCompositionLocalOf<ColorScheme> { ColorScheme() }
/**
* Convert given color to disabled color.
* @param disabledAlpha Alpha used to represent disabled colors.
*/
@Composable
internal fun Color.toDisabledColor(disabledAlpha: Float = ContentAlpha.disabled) =
this.copy(alpha = this.alpha * disabledAlpha)
/**
* Converts a color token key to the local color scheme provided by the theme.
* The color references the [LocalColorScheme].
*/
internal val ColorSchemeKeyTokens.value: Color
@ReadOnlyComposable
@Composable
get() = MaterialTheme.colorScheme.fromToken(this)