/*
* 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.tv.material3
import androidx.annotation.FloatRange
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.dp
/**
* Contains the default values used by clickable Surface.
*/
@ExperimentalTvMaterial3Api
object ClickableSurfaceDefaults {
internal fun shape(
enabled: Boolean,
focused: Boolean,
pressed: Boolean,
shape: ClickableSurfaceShape
): Shape {
return when {
pressed && enabled -> shape.pressedShape
focused && enabled -> shape.focusedShape
focused && !enabled -> shape.focusedDisabledShape
enabled -> shape.shape
else -> shape.disabledShape
}
}
/**
* Creates a [ClickableSurfaceShape] that represents the default container shapes used in a
* Surface.
*
* @param shape the shape used when the Surface is enabled, and has no other
* [Interaction]s.
* @param focusedShape the shape used when the Surface is enabled and focused.
* @param pressedShape the shape used when the Surface is enabled pressed.
* @param disabledShape the shape used when the Surface is not enabled.
* @param focusedDisabledShape the shape used when the Surface is not enabled and focused.
*/
@ReadOnlyComposable
@Composable
fun shape(
shape: Shape = MaterialTheme.shapes.medium,
focusedShape: Shape = shape,
pressedShape: Shape = shape,
disabledShape: Shape = shape,
focusedDisabledShape: Shape = disabledShape
) = ClickableSurfaceShape(
shape = shape,
focusedShape = focusedShape,
pressedShape = pressedShape,
disabledShape = disabledShape,
focusedDisabledShape = focusedDisabledShape
)
internal fun color(
enabled: Boolean,
focused: Boolean,
pressed: Boolean,
color: ClickableSurfaceColor
): Color {
return when {
pressed && enabled -> color.pressedColor
focused && enabled -> color.focusedColor
enabled -> color.color
else -> color.disabledColor
}
}
/**
* Creates a [ClickableSurfaceColor] that represents the default container colors used in a
* Surface.
*
* @param color the container color of this Surface when enabled
* @param focusedColor the container color of this Surface when enabled and focused
* @param pressedColor the container color of this Surface when enabled and pressed
* @param disabledColor the container color of this Surface when not enabled
*/
@ReadOnlyComposable
@Composable
fun color(
color: Color = MaterialTheme.colorScheme.surface,
focusedColor: Color = MaterialTheme.colorScheme.inverseSurface,
pressedColor: Color = MaterialTheme.colorScheme.inverseSurface,
disabledColor: Color = MaterialTheme.colorScheme.surfaceVariant.copy(
alpha = DisabledBackgroundAlpha
)
) = ClickableSurfaceColor(
color = color,
focusedColor = focusedColor,
pressedColor = pressedColor,
disabledColor = disabledColor
)
/**
* Creates a [ClickableSurfaceColor] that represents the default content colors used in a
* Surface.
*
* @param color the content color of this Surface when enabled
* @param focusedColor the content color of this Surface when enabled and focused
* @param pressedColor the content color of this Surface when enabled and pressed
* @param disabledColor the content color of this Surface when not enabled
*/
@ReadOnlyComposable
@Composable
fun contentColor(
color: Color = MaterialTheme.colorScheme.onSurface,
focusedColor: Color = MaterialTheme.colorScheme.inverseOnSurface,
pressedColor: Color = MaterialTheme.colorScheme.inverseOnSurface,
disabledColor: Color = MaterialTheme.colorScheme.onSurface
) = ClickableSurfaceColor(
color = color,
focusedColor = focusedColor,
pressedColor = pressedColor,
disabledColor = disabledColor
)
internal fun scale(
enabled: Boolean,
focused: Boolean,
pressed: Boolean,
scale: ClickableSurfaceScale
): Float {
return when {
pressed && enabled -> scale.pressedScale
focused && enabled -> scale.focusedScale
focused && !enabled -> scale.focusedDisabledScale
enabled -> scale.scale
else -> scale.disabledScale
}
}
/**
* Creates a [ClickableSurfaceScale] that represents the default scales used in a
* Surface. scales are used to modify the size of a composable in different [Interaction]
* states e.g. 1f (original) in default state, 1.2f (scaled up) in focused state,
* 0.8f (scaled down) in pressed state, etc.
*
* @param scale the scale to be used for this Surface when enabled
* @param focusedScale the scale to be used for this Surface when focused
* @param pressedScale the scale to be used for this Surface when pressed
* @param disabledScale the scale to be used for this Surface when disabled
* @param focusedDisabledScale the scale to be used for this Surface when disabled and
* focused
*/
fun scale(
@FloatRange(from = 0.0) scale: Float = 1f,
@FloatRange(from = 0.0) focusedScale: Float = 1.1f,
@FloatRange(from = 0.0) pressedScale: Float = scale,
@FloatRange(from = 0.0) disabledScale: Float = scale,
@FloatRange(from = 0.0) focusedDisabledScale: Float = disabledScale
) = ClickableSurfaceScale(
scale = scale,
focusedScale = focusedScale,
pressedScale = pressedScale,
disabledScale = disabledScale,
focusedDisabledScale = focusedDisabledScale
)
internal fun border(
enabled: Boolean,
focused: Boolean,
pressed: Boolean,
border: ClickableSurfaceBorder
): Border {
return when {
pressed && enabled -> border.pressedBorder
focused && enabled -> border.focusedBorder
focused && !enabled -> border.focusedDisabledBorder
enabled -> border.border
else -> border.disabledBorder
}
}
/**
* Creates a [ClickableSurfaceBorder] that represents the default [Border]s applied on a
* Surface in different [Interaction] states.
*
* @param border the [Border] to be used for this Surface when enabled
* @param focusedBorder the [Border] to be used for this Surface when focused
* @param pressedBorder the [Border] to be used for this Surface when pressed
* @param disabledBorder the [Border] to be used for this Surface when disabled
* @param focusedDisabledBorder the [Border] to be used for this Surface when disabled and
* focused
*/
@ReadOnlyComposable
@Composable
fun border(
border: Border = Border.None,
focusedBorder: Border = border,
pressedBorder: Border = focusedBorder,
disabledBorder: Border = border,
focusedDisabledBorder: Border = Border(
border = BorderStroke(
width = 2.dp,
color = MaterialTheme.colorScheme.border
),
inset = 0.dp,
shape = ShapeDefaults.Small
)
) = ClickableSurfaceBorder(
border = border,
focusedBorder = focusedBorder,
pressedBorder = pressedBorder,
disabledBorder = disabledBorder,
focusedDisabledBorder = focusedDisabledBorder
)
internal fun glow(
enabled: Boolean,
focused: Boolean,
pressed: Boolean,
glow: ClickableSurfaceGlow
): Glow {
return if (enabled) {
when {
pressed -> glow.pressedGlow
focused -> glow.focusedGlow
else -> glow.glow
}
} else {
Glow.None
}
}
/**
* Creates a [ClickableSurfaceGlow] that represents the default [Glow]s used in a
* Surface.
*
* @param glow the Glow behind this Surface when enabled
* @param focusedGlow the Glow behind this Surface when focused
* @param pressedGlow the Glow behind this Surface when pressed
*/
fun glow(
glow: Glow = Glow.None,
focusedGlow: Glow = glow,
pressedGlow: Glow = glow
) = ClickableSurfaceGlow(
glow = glow,
focusedGlow = focusedGlow,
pressedGlow = pressedGlow
)
}
/**
* Contains the default values used by Toggleable Surface.
*/
@ExperimentalTvMaterial3Api
object ToggleableSurfaceDefaults {
/**
* Creates a [ToggleableSurfaceShape] that represents the default container shapes used in a
* toggleable Surface.
*
* @param shape the shape used when the Surface is enabled, and has no other
* [Interaction]s.
* @param focusedShape the shape used when the Surface is enabled and focused.
* @param pressedShape the shape used when the Surface is enabled and pressed.
* @param selectedShape the shape used when the Surface is enabled and selected.
* @param disabledShape the shape used when the Surface is not enabled.
* @param focusedSelectedShape the shape used when the Surface is enabled, focused and selected.
* @param focusedDisabledShape the shape used when the Surface is not enabled and focused.
* @param pressedSelectedShape the shape used when the Surface is enabled, pressed and selected.
* @param selectedDisabledShape the shape used when the Surface is not enabled and selected.
* @param focusedSelectedDisabledShape the shape used when the Surface is not enabled, focused
* and selected.
*/
@ReadOnlyComposable
@Composable
fun shape(
shape: Shape = MaterialTheme.shapes.medium,
focusedShape: Shape = shape,
pressedShape: Shape = shape,
selectedShape: Shape = shape,
disabledShape: Shape = shape,
focusedSelectedShape: Shape = shape,
focusedDisabledShape: Shape = disabledShape,
pressedSelectedShape: Shape = shape,
selectedDisabledShape: Shape = disabledShape,
focusedSelectedDisabledShape: Shape = disabledShape
) = ToggleableSurfaceShape(
shape = shape,
focusedShape = focusedShape,
pressedShape = pressedShape,
selectedShape = selectedShape,
disabledShape = disabledShape,
focusedSelectedShape = focusedSelectedShape,
focusedDisabledShape = focusedDisabledShape,
pressedSelectedShape = pressedSelectedShape,
selectedDisabledShape = selectedDisabledShape,
focusedSelectedDisabledShape = focusedSelectedDisabledShape
)
/**
* Creates a [ToggleableSurfaceColor] that represents the default container colors used in a
* toggleable Surface.
*
* @param color the color used when the Surface is enabled, and has no other [Interaction]s.
* @param focusedColor the color used when the Surface is enabled and focused.
* @param pressedColor the color used when the Surface is enabled and pressed.
* @param selectedColor the color used when the Surface is enabled and selected.
* @param disabledColor the color used when the Surface is not enabled.
* @param focusedSelectedColor the color used when the Surface is enabled, focused and selected.
* @param pressedSelectedColor the color used when the Surface is enabled, pressed and selected.
*/
@ReadOnlyComposable
@Composable
fun color(
color: Color = MaterialTheme.colorScheme.surface,
focusedColor: Color = MaterialTheme.colorScheme.inverseSurface,
pressedColor: Color = MaterialTheme.colorScheme.inverseSurface,
selectedColor: Color = MaterialTheme.colorScheme.inverseSurface.copy(alpha = 0.5f),
disabledColor: Color = MaterialTheme.colorScheme.surfaceVariant.copy(
alpha = DisabledBackgroundAlpha
),
focusedSelectedColor: Color = MaterialTheme.colorScheme.inverseSurface.copy(alpha = 0.5f),
pressedSelectedColor: Color = MaterialTheme.colorScheme.inverseSurface.copy(alpha = 0.5f)
) = ToggleableSurfaceColor(
color = color,
focusedColor = focusedColor,
pressedColor = pressedColor,
selectedColor = selectedColor,
disabledColor = disabledColor,
focusedSelectedColor = focusedSelectedColor,
pressedSelectedColor = pressedSelectedColor
)
/**
* Creates a [ToggleableSurfaceColor] that represents the default content colors used in a
* toggleable Surface.
*
* @param color the color used when the Surface is enabled, and has no other [Interaction]s.
* @param focusedColor the color used when the Surface is enabled and focused.
* @param pressedColor the color used when the Surface is enabled and pressed.
* @param selectedColor the color used when the Surface is enabled and selected.
* @param disabledColor the color used when the Surface is not enabled.
* @param focusedSelectedColor the color used when the Surface is enabled, focused and selected.
* @param pressedSelectedColor the color used when the Surface is enabled, pressed and selected.
*/
@ReadOnlyComposable
@Composable
fun contentColor(
color: Color = MaterialTheme.colorScheme.onSurface,
focusedColor: Color = MaterialTheme.colorScheme.inverseOnSurface,
pressedColor: Color = MaterialTheme.colorScheme.inverseOnSurface,
selectedColor: Color = MaterialTheme.colorScheme.inverseOnSurface,
disabledColor: Color = MaterialTheme.colorScheme.onSurface,
focusedSelectedColor: Color = MaterialTheme.colorScheme.inverseOnSurface,
pressedSelectedColor: Color = MaterialTheme.colorScheme.inverseOnSurface
) = ToggleableSurfaceColor(
color = color,
focusedColor = focusedColor,
pressedColor = pressedColor,
selectedColor = selectedColor,
disabledColor = disabledColor,
focusedSelectedColor = focusedSelectedColor,
pressedSelectedColor = pressedSelectedColor
)
/**
* Creates a [ToggleableSurfaceScale] that represents the default scales used in a
* toggleable Surface. scales are used to modify the size of a composable in different
* [Interaction] states e.g. 1f (original) in default state, 1.2f (scaled up) in focused state,
* 0.8f (scaled down) in pressed state, etc.
*
* @param scale the scale used when the Surface is enabled, and has no other
* [Interaction]s.
* @param focusedScale the scale used when the Surface is enabled and focused.
* @param pressedScale the scale used when the Surface is enabled and pressed.
* @param selectedScale the scale used when the Surface is enabled and selected.
* @param disabledScale the scale used when the Surface is not enabled.
* @param focusedSelectedScale the scale used when the Surface is enabled, focused and
* selected.
* @param focusedDisabledScale the scale used when the Surface is not enabled and
* focused.
* @param pressedSelectedScale the scale used when the Surface is enabled, pressed and
* selected.
* @param selectedDisabledScale the scale used when the Surface is not enabled and
* selected.
* @param focusedSelectedDisabledScale the scale used when the Surface is not enabled,
* focused and selected.
*/
fun scale(
scale: Float = 1f,
focusedScale: Float = 1.1f,
pressedScale: Float = scale,
selectedScale: Float = scale,
disabledScale: Float = scale,
focusedSelectedScale: Float = focusedScale,
focusedDisabledScale: Float = disabledScale,
pressedSelectedScale: Float = scale,
selectedDisabledScale: Float = disabledScale,
focusedSelectedDisabledScale: Float = disabledScale
) = ToggleableSurfaceScale(
scale = scale,
focusedScale = focusedScale,
pressedScale = pressedScale,
selectedScale = selectedScale,
disabledScale = disabledScale,
focusedSelectedScale = focusedSelectedScale,
focusedDisabledScale = focusedDisabledScale,
pressedSelectedScale = pressedSelectedScale,
selectedDisabledScale = selectedDisabledScale,
focusedSelectedDisabledScale = focusedSelectedDisabledScale
)
/**
* Creates a [ToggleableSurfaceBorder] that represents the default [Border]s applied on a
* toggleable Surface in different [Interaction] states.
*
* @param border the [Border] used when the Surface is enabled, and has no other
* [Interaction]s.
* @param focusedBorder the [Border] used when the Surface is enabled and focused.
* @param pressedBorder the [Border] used when the Surface is enabled and pressed.
* @param selectedBorder the [Border] used when the Surface is enabled and selected.
* @param disabledBorder the [Border] used when the Surface is not enabled.
* @param focusedSelectedBorder the [Border] used when the Surface is enabled, focused and
* selected.
* @param focusedDisabledBorder the [Border] used when the Surface is not enabled and focused.
* @param pressedSelectedBorder the [Border] used when the Surface is enabled, pressed and
* selected.
* @param selectedDisabledBorder the [Border] used when the Surface is not enabled and
* selected.
* @param focusedSelectedDisabledBorder the [Border] used when the Surface is not enabled,
* focused and selected.
*/
fun border(
border: Border = Border.None,
focusedBorder: Border = border,
pressedBorder: Border = focusedBorder,
selectedBorder: Border = border,
disabledBorder: Border = border,
focusedSelectedBorder: Border = focusedBorder,
focusedDisabledBorder: Border = disabledBorder,
pressedSelectedBorder: Border = border,
selectedDisabledBorder: Border = disabledBorder,
focusedSelectedDisabledBorder: Border = disabledBorder
) = ToggleableSurfaceBorder(
border = border,
focusedBorder = focusedBorder,
pressedBorder = pressedBorder,
selectedBorder = selectedBorder,
disabledBorder = disabledBorder,
focusedSelectedBorder = focusedSelectedBorder,
focusedDisabledBorder = focusedDisabledBorder,
pressedSelectedBorder = pressedSelectedBorder,
selectedDisabledBorder = selectedDisabledBorder,
focusedSelectedDisabledBorder = focusedSelectedDisabledBorder
)
/**
* Creates a [ToggleableSurfaceGlow] that represents the default [Glow]s used in a
* toggleable Surface.
*
* @param glow the [Glow] used when the Surface is enabled, and has no other [Interaction]s.
* @param focusedGlow the [Glow] used when the Surface is enabled and focused.
* @param pressedGlow the [Glow] used when the Surface is enabled and pressed.
* @param selectedGlow the [Glow] used when the Surface is enabled and selected.
* @param focusedSelectedGlow the [Glow] used when the Surface is enabled, focused and selected.
* @param pressedSelectedGlow the [Glow] used when the Surface is enabled, pressed and selected.
*/
fun glow(
glow: Glow = Glow.None,
focusedGlow: Glow = glow,
pressedGlow: Glow = glow,
selectedGlow: Glow = glow,
focusedSelectedGlow: Glow = focusedGlow,
pressedSelectedGlow: Glow = glow
) = ToggleableSurfaceGlow(
glow = glow,
focusedGlow = focusedGlow,
pressedGlow = pressedGlow,
selectedGlow = selectedGlow,
focusedSelectedGlow = focusedSelectedGlow,
pressedSelectedGlow = pressedSelectedGlow
)
internal fun shape(
enabled: Boolean,
focused: Boolean,
pressed: Boolean,
selected: Boolean,
shape: ToggleableSurfaceShape
): Shape {
return when {
enabled && selected && pressed -> shape.pressedSelectedShape
enabled && selected && focused -> shape.focusedSelectedShape
enabled && selected -> shape.selectedShape
enabled && pressed -> shape.pressedShape
enabled && focused -> shape.focusedShape
enabled -> shape.shape
!enabled && selected && focused -> shape.focusedSelectedDisabledShape
!enabled && selected -> shape.selectedDisabledShape
!enabled && focused -> shape.focusedDisabledShape
else -> shape.disabledShape
}
}
internal fun color(
enabled: Boolean,
focused: Boolean,
pressed: Boolean,
selected: Boolean,
color: ToggleableSurfaceColor
): Color {
return when {
enabled && selected && pressed -> color.pressedSelectedColor
enabled && selected && focused -> color.focusedSelectedColor
enabled && selected -> color.selectedColor
enabled && pressed -> color.pressedColor
enabled && focused -> color.focusedColor
enabled -> color.color
else -> color.disabledColor
}
}
internal fun scale(
enabled: Boolean,
focused: Boolean,
pressed: Boolean,
selected: Boolean,
scale: ToggleableSurfaceScale
): Float {
return when {
enabled && selected && pressed -> scale.pressedSelectedScale
enabled && selected && focused -> scale.focusedSelectedScale
enabled && selected -> scale.selectedScale
enabled && pressed -> scale.pressedScale
enabled && focused -> scale.focusedScale
enabled -> scale.scale
!enabled && selected && focused -> scale.focusedSelectedDisabledScale
!enabled && selected -> scale.selectedDisabledScale
!enabled && focused -> scale.focusedDisabledScale
else -> scale.disabledScale
}
}
internal fun border(
enabled: Boolean,
focused: Boolean,
pressed: Boolean,
selected: Boolean,
border: ToggleableSurfaceBorder
): Border {
return when {
enabled && selected && pressed -> border.pressedSelectedBorder
enabled && selected && focused -> border.focusedSelectedBorder
enabled && selected -> border.selectedBorder
enabled && pressed -> border.pressedBorder
enabled && focused -> border.focusedBorder
enabled -> border.border
!enabled && selected && focused -> border.focusedSelectedDisabledBorder
!enabled && selected -> border.selectedDisabledBorder
!enabled && focused -> border.focusedDisabledBorder
else -> border.disabledBorder
}
}
internal fun glow(
enabled: Boolean,
focused: Boolean,
pressed: Boolean,
selected: Boolean,
glow: ToggleableSurfaceGlow
): Glow {
return when {
enabled && selected && pressed -> glow.pressedSelectedGlow
enabled && selected && focused -> glow.focusedSelectedGlow
enabled && selected -> glow.selectedGlow
enabled && pressed -> glow.pressedGlow
enabled && focused -> glow.focusedGlow
enabled -> glow.glow
else -> Glow.None
}
}
}
private const val DisabledBackgroundAlpha = 0.4f