Shapes.kt
/*
* 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.shape.CornerBasedShape
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Shape
import androidx.wear.compose.material3.tokens.ShapeKeyTokens
import androidx.wear.compose.material3.tokens.ShapeTokens
/**
* Material surfaces can be displayed in different shapes. Shapes direct attention, identify
* components, communicate state, and express brand.
*
* The shape scale defines the style of container, offering a range of
* curved shapes (mostly polygonal). The default [Shapes] theme for Material3 is rounded rectangles,
* with various degrees of corner roundness:
*
* - Extra Small
* - Small
* - Medium
* - Large
* - Extra Large
*
* You can customize the shape system for all components in the [MaterialTheme] or you can do it
* on a per component basis by overriding the shape parameter for that
* component. For example, by default, buttons use the shape style "large". If your product requires
* a smaller amount of roundness, you can override the shape parameter with a different shape
* value like [Shapes.small].
*
* TODO(b/273226734) Review documentation with references to components that use the shape themes.
*
* @param extraSmall By default, provides [ShapeDefaults.ExtraSmall], a [RoundedCornerShape]
* with 4dp [CornerSize] (used by bundled Cards).
* @param small By default, provides [ShapeDefaults.Small], a [RoundedCornerShape]
* with 8dp [CornerSize].
* @param medium By default, provides [ShapeDefaults.Medium], a [RoundedCornerShape] with
* 16dp [CornerSize] (used by shape-shifting Buttons and rounded rectangle buttons).
* @param large By default, provides [ShapeDefaults.Large], a [RoundedCornerShape]
* with 24dp [CornerSize] (used by Cards).
* @param extraLarge By default, provides [ShapeDefaults.ExtraLarge], a
* [RoundedCornerShape] with 32dp [CornerSize].
*/
@Immutable
class Shapes(
val extraSmall: CornerBasedShape = ShapeDefaults.ExtraSmall,
val small: CornerBasedShape = ShapeDefaults.Small,
val medium: CornerBasedShape = ShapeDefaults.Medium,
val large: CornerBasedShape = ShapeDefaults.Large,
val extraLarge: CornerBasedShape = ShapeDefaults.ExtraLarge,
) {
/** Returns a copy of this Shapes, optionally overriding some of the values. */
fun copy(
extraSmall: CornerBasedShape = this.extraSmall,
small: CornerBasedShape = this.small,
medium: CornerBasedShape = this.medium,
large: CornerBasedShape = this.large,
extraLarge: CornerBasedShape = this.extraLarge,
): Shapes = Shapes(
extraSmall = extraSmall,
small = small,
medium = medium,
large = large,
extraLarge = extraLarge,
)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Shapes) return false
if (extraSmall != other.extraSmall) return false
if (small != other.small) return false
if (medium != other.medium) return false
if (large != other.large) return false
if (extraLarge != other.extraLarge) return false
return true
}
override fun hashCode(): Int {
var result = extraSmall.hashCode()
result = 31 * result + small.hashCode()
result = 31 * result + medium.hashCode()
result = 31 * result + large.hashCode()
result = 31 * result + extraLarge.hashCode()
return result
}
override fun toString(): String {
return "Shapes(" +
"extraSmall=$extraSmall, " +
"small=$small, " +
"medium=$medium, " +
"large=$large, " +
"extraLarge=$extraLarge)"
}
}
/**
* Contains the default values used by [Shapes]
*/
object ShapeDefaults {
/** Extra small sized corner shape */
val ExtraSmall = ShapeTokens.CornerExtraSmall
/** Small sized corner shape */
val Small = ShapeTokens.CornerSmall
/** Medium sized corner shape */
val Medium = ShapeTokens.CornerMedium
/** Large sized corner shape */
val Large = ShapeTokens.CornerLarge
/** Extra large sized corner shape */
val ExtraLarge = ShapeTokens.CornerExtraLarge
}
/**
* Helper function for component shape tokens. Here is an example on how to use component color
* tokens:
* ``MaterialTheme.shapes.fromToken(FabPrimarySmallTokens.ContainerShape)``
*/
internal fun Shapes.fromToken(value: ShapeKeyTokens): Shape {
return when (value) {
ShapeKeyTokens.CornerExtraSmall -> extraSmall
ShapeKeyTokens.CornerSmall -> small
ShapeKeyTokens.CornerMedium -> medium
ShapeKeyTokens.CornerLarge -> large
ShapeKeyTokens.CornerExtraLarge -> extraLarge
ShapeKeyTokens.CornerFull -> ShapeTokens.CornerFull
ShapeKeyTokens.CornerNone -> ShapeTokens.CornerNone
}
}
/**
* Converts a shape token key to the local shape provided by the theme
* The shape references the [LocalShapes].
*/
internal val ShapeKeyTokens.value: Shape
@Composable
@ReadOnlyComposable
get() = MaterialTheme.shapes.fromToken(this)
/** CompositionLocal used to specify the default shapes for the surfaces. */
internal val LocalShapes = staticCompositionLocalOf { Shapes() }