1 /*
2  * Copyright 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.compose.material
18 
19 import androidx.compose.runtime.Composable
20 import androidx.compose.runtime.ProvidableCompositionLocal
21 import androidx.compose.runtime.ReadOnlyComposable
22 import androidx.compose.runtime.compositionLocalOf
23 import androidx.compose.runtime.staticCompositionLocalOf
24 import androidx.compose.ui.graphics.Color
25 import androidx.compose.ui.graphics.compositeOver
26 import androidx.compose.ui.unit.Dp
27 import androidx.compose.ui.unit.dp
28 import kotlin.math.ln
29 
30 /**
31  * CompositionLocal containing the [ElevationOverlay] used by [Surface] components. Provide `null`
32  * to turn off [ElevationOverlay]s for the children within this CompositionLocal..
33  *
34  * @see ElevationOverlay
35  */
36 val LocalElevationOverlay: ProvidableCompositionLocal<ElevationOverlay?> =
<lambda>null37     staticCompositionLocalOf {
38         DefaultElevationOverlay
39     }
40 
41 // TODO: make this a fun interface
42 /**
43  * An ElevationOverlay is an overlay applied to the background color of [Surface] components, used
44  * to emphasize elevation in dark theme, where shadows are not as visible. An ElevationOverlay does
45  * not replace the shadows drawn by a [Surface], but is used as an additional representation of
46  * elevation.
47  *
48  * The default ElevationOverlay only applies in dark theme (![Colors.isLight]), in accordance with
49  * the Material specification for
50  * [Dark Theme](https://material.io/design/color/dark-theme.html#properties).
51  *
52  * See [LocalElevationOverlay] to provide your own [ElevationOverlay]. You can provide `null` to
53  * have no ElevationOverlay applied.
54  */
55 interface ElevationOverlay {
56     /**
57      * Returns the new background [Color] to use, representing the original background [color] with
58      * an overlay corresponding to [elevation] applied. Typically this should only be applied to
59      * [Colors.surface].
60      */
applynull61     @Composable fun apply(color: Color, elevation: Dp): Color
62 }
63 
64 /** The default [ElevationOverlay] implementation. */
65 private object DefaultElevationOverlay : ElevationOverlay {
66     @ReadOnlyComposable
67     @Composable
68     override fun apply(color: Color, elevation: Dp): Color {
69         val colors = MaterialTheme.colors
70         return if (elevation > 0.dp && !colors.isLight) {
71             val foregroundColor = calculateForegroundColor(color, elevation)
72             foregroundColor.compositeOver(color)
73         } else {
74             color
75         }
76     }
77 }
78 
79 /**
80  * @return the alpha-modified foreground color to overlay on top of the surface color to produce the
81  *   resultant color. This color is the [contentColorFor] the [backgroundColor], with alpha applied
82  *   depending on the value of [elevation].
83  */
84 @ReadOnlyComposable
85 @Composable
calculateForegroundColornull86 private fun calculateForegroundColor(backgroundColor: Color, elevation: Dp): Color {
87     val alpha = ((4.5f * ln(elevation.value + 1)) + 2f) / 100f
88     val baseForegroundColor = contentColorFor(backgroundColor)
89     return baseForegroundColor.copy(alpha = alpha)
90 }
91 
92 /**
93  * CompositionLocal containing the current absolute elevation provided by [Surface] components. This
94  * absolute elevation is a sum of all the previous elevations. Absolute elevation is only used for
95  * calculating elevation overlays in dark theme, and is *not* used for drawing the shadow in a
96  * [Surface]. See [ElevationOverlay] for more information on elevation overlays.
97  *
98  * @sample androidx.compose.material.samples.AbsoluteElevationSample
99  */
<lambda>null100 val LocalAbsoluteElevation = compositionLocalOf { 0.dp }
101