[go: nahoru, domu]

blob: d2fc69c26c366d57a16a67e28613d9f08161534d [file] [log] [blame]
/*
* Copyright 2019 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.compose.material
import androidx.compose.Ambient
import androidx.compose.Composable
import androidx.compose.Immutable
import androidx.compose.Providers
import androidx.compose.staticAmbientOf
import androidx.compose.foundation.ContentColorAmbient
import androidx.compose.foundation.contentColor
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.util.annotation.FloatRange
/**
* Emphasis allows certain parts of a component to be accentuated, or shown with lower contrast
* to reflect importance / state inside a component. For example, inside a disabled button, text
* should have an emphasis level of [EmphasisLevels.disabled], to show that the button is
* currently not active / able to be interacted with.
*
* Emphasis works by adjusting the color provided by [contentColor], so that emphasis levels
* cascade through a subtree without requiring components to be aware of their context.
*
* The default implementations convey emphasis by changing the alpha / opacity of [color], to
* increase / reduce contrast for a particular element.
*
* To set emphasis for a particular subtree, see [ProvideEmphasis].
*
* To define the emphasis levels in your application, see [EmphasisLevels] and [EmphasisAmbient]
* - note that this should not typically be customized, as the default values are optimized for
* accessibility and contrast on different surfaces.
*
* For more information on emphasis and ensuring legibility for content, see
* [Text legibility](https://material.io/design/color/text-legibility.html)
*/
@Immutable
interface Emphasis {
/**
* Applies emphasis to the given [color].
*/
fun applyEmphasis(color: Color): Color
}
/**
* EmphasisLevels represents the different levels of [Emphasis] that can be applied to a component.
*
* By default, the [Emphasis] implementation for each level varies depending on the color being
* emphasized (typically [contentColor]). This ensures that the [Emphasis] has the correct
* contrast for the background they are on, as [ColorPalette.primary] surfaces typically require
* higher contrast for the content color than [ColorPalette.surface] surfaces to ensure they are
* accessible.
*
* This typically should not be customized as the default implementation is optimized for
* correct accessibility and contrast on different surfaces.
*
* See [EmphasisAmbient] to retrieve the current [EmphasisLevels]
*/
interface EmphasisLevels {
/**
* Emphasis used to express high emphasis, such as for selected text fields.
*/
@Composable
val high: Emphasis
/**
* Emphasis used to express medium emphasis, such as for placeholder text in a text field.
*/
@Composable
val medium: Emphasis
/**
* Emphasis used to express disabled state, such as for a disabled button.
*/
@Composable
val disabled: Emphasis
}
/**
* Applies [emphasis] to [content], by modifying the value of [contentColor].
*
* See [EmphasisAmbient] to retrieve the levels of emphasis provided in the theme,
* so they can be applied with this function.
*
* @sample androidx.compose.material.samples.EmphasisSample
*/
@Composable
fun ProvideEmphasis(emphasis: Emphasis, content: @Composable () -> Unit) {
val emphasizedColor = emphasis.applyEmphasis(contentColor())
Providers(ContentColorAmbient provides emphasizedColor, children = content)
}
/**
* Ambient containing the current [EmphasisLevels] in this hierarchy.
*/
val EmphasisAmbient: Ambient<EmphasisLevels> = staticAmbientOf { DefaultEmphasisLevels }
private object DefaultEmphasisLevels : EmphasisLevels {
/**
* This default implementation uses separate alpha levels depending on the luminance of the
* incoming color, and whether the theme is light or dark. This is to ensure correct contrast
* and accessibility on all surfaces.
*
* See [HighContrastAlphaLevels] and [ReducedContrastAlphaLevels] for what the levels are
* used for, and under what circumstances.
*/
private class AlphaEmphasis(
private val lightTheme: Boolean,
@FloatRange(from = 0.0, to = 1.0) private val highContrastAlpha: Float,
@FloatRange(from = 0.0, to = 1.0) private val reducedContrastAlpha: Float
) : Emphasis {
override fun applyEmphasis(color: Color): Color {
if (color.alpha != 1f) return color
val alpha = if (lightTheme) {
if (color.luminance() > 0.5) highContrastAlpha else reducedContrastAlpha
} else {
if (color.luminance() < 0.5) highContrastAlpha else reducedContrastAlpha
}
return color.copy(alpha = alpha)
}
}
@Composable
override val high: Emphasis
get() = AlphaEmphasis(
lightTheme = MaterialTheme.colors.isLight,
highContrastAlpha = HighContrastAlphaLevels.high,
reducedContrastAlpha = ReducedContrastAlphaLevels.high
)
@Composable
override val medium: Emphasis
get() = AlphaEmphasis(
lightTheme = MaterialTheme.colors.isLight,
highContrastAlpha = HighContrastAlphaLevels.medium,
reducedContrastAlpha = ReducedContrastAlphaLevels.medium
)
@Composable
override val disabled: Emphasis
get() = AlphaEmphasis(
lightTheme = MaterialTheme.colors.isLight,
highContrastAlpha = HighContrastAlphaLevels.disabled,
reducedContrastAlpha = ReducedContrastAlphaLevels.disabled
)
}
/**
* Alpha levels for high luminance content in light theme, or low luminance content in dark theme.
*
* This content will typically be placed on colored surfaces, so it is important that the
* contrast here is higher to meet accessibility standards, and increase legibility.
*
* These levels are typically used for text / iconography in primary colored tabs /
* bottom navigation / etc.
*/
private object HighContrastAlphaLevels {
const val high: Float = 1.00f
const val medium: Float = 0.74f
const val disabled: Float = 0.38f
}
/**
* Alpha levels for low luminance content in light theme, or high luminance content in dark theme.
*
* This content will typically be placed on grayscale surfaces, so the contrast here can be lower
* without sacrificing accessibility and legibility.
*
* These levels are typically used for body text on the main surface (white in light theme, grey
* in dark theme) and text / iconography in surface colored tabs / bottom navigation / etc.
*/
private object ReducedContrastAlphaLevels {
const val high: Float = 0.87f
const val medium: Float = 0.60f
const val disabled: Float = 0.38f
}