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.foundation.BorderStroke
20 import androidx.compose.foundation.background
21 import androidx.compose.foundation.border
22 import androidx.compose.foundation.clickable
23 import androidx.compose.foundation.interaction.Interaction
24 import androidx.compose.foundation.interaction.MutableInteractionSource
25 import androidx.compose.foundation.layout.Box
26 import androidx.compose.foundation.selection.selectable
27 import androidx.compose.foundation.selection.toggleable
28 import androidx.compose.runtime.Composable
29 import androidx.compose.runtime.CompositionLocalProvider
30 import androidx.compose.ui.Modifier
31 import androidx.compose.ui.draw.clip
32 import androidx.compose.ui.draw.shadow
33 import androidx.compose.ui.graphics.Color
34 import androidx.compose.ui.graphics.RectangleShape
35 import androidx.compose.ui.graphics.Shape
36 import androidx.compose.ui.input.pointer.pointerInput
37 import androidx.compose.ui.semantics.isContainer
38 import androidx.compose.ui.semantics.semantics
39 import androidx.compose.ui.text.TextStyle
40 import androidx.compose.ui.unit.Dp
41 import androidx.compose.ui.unit.dp
42
43 /**
44 * [Material Design surface](https://material.io/design/environment/surfaces.html)
45 *
46 * Material surface is the central metaphor in material design. Each surface exists at a given
47 * elevation, which influences how that piece of surface visually relates to other surfaces and how
48 * that surface casts shadows.
49 *
50 * See the other overloads for clickable, selectable, and toggleable surfaces.
51 *
52 * The Surface is responsible for:
53 * 1) Clipping: Surface clips its children to the shape specified by [shape]
54 * 2) Elevation: Surface draws a shadow to represent depth, where [elevation] represents the depth
55 * of this surface. If the passed [shape] is concave the shadow will not be drawn on Android
56 * versions less than 10.
57 * 3) Borders: If [shape] has a border, then it will also be drawn.
58 * 4) Background: Surface fills the shape specified by [shape] with the [color]. If [color] is
59 * [Colors.surface], the [ElevationOverlay] from [LocalElevationOverlay] will be used to apply an
60 * overlay - by default this will only occur in dark theme. The color of the overlay depends on
61 * the [elevation] of this Surface, and the [LocalAbsoluteElevation] set by any parent surfaces.
62 * This ensures that a Surface never appears to have a lower elevation overlay than its
63 * ancestors, by summing the elevation of all previous Surfaces.
64 * 5) Content color: Surface uses [contentColor] to specify a preferred color for the content of
65 * this surface - this is used by the [Text] and [Icon] components as a default color.
66 * 6) Blocking touch propagation behind the surface.
67 *
68 * If no [contentColor] is set, this surface will try and match its background color to a color
69 * defined in the theme [Colors], and return the corresponding content color. For example, if the
70 * [color] of this surface is [Colors.surface], [contentColor] will be set to [Colors.onSurface]. If
71 * [color] is not part of the theme palette, [contentColor] will keep the same value set above this
72 * Surface.
73 *
74 * @sample androidx.compose.material.samples.SurfaceSample
75 *
76 * To modify these default style values used by text, use [ProvideTextStyle] or explicitly pass a
77 * new [TextStyle] to your text.
78 *
79 * To manually retrieve the content color inside a surface, use [LocalContentColor].
80 *
81 * @param modifier Modifier to be applied to the layout corresponding to the surface
82 * @param shape Defines the surface's shape as well its shadow. A shadow is only displayed if the
83 * [elevation] is greater than zero.
84 * @param color The background color. Use [Color.Transparent] to have no color.
85 * @param contentColor The preferred content color provided by this Surface to its children.
86 * Defaults to either the matching content color for [color], or if [color] is not a color from
87 * the theme, this will keep the same value set above this Surface.
88 * @param border Optional border to draw on top of the surface
89 * @param elevation The size of the shadow below the surface. Note that It will not affect z index
90 * of the Surface. If you want to change the drawing order you can use `Modifier.zIndex`.
91 * @param content The content to be displayed on this Surface
92 */
93 @Composable
Surfacenull94 fun Surface(
95 modifier: Modifier = Modifier,
96 shape: Shape = RectangleShape,
97 color: Color = MaterialTheme.colors.surface,
98 contentColor: Color = contentColorFor(color),
99 border: BorderStroke? = null,
100 elevation: Dp = 0.dp,
101 content: @Composable () -> Unit
102 ) {
103 val absoluteElevation = LocalAbsoluteElevation.current + elevation
104 CompositionLocalProvider(
105 LocalContentColor provides contentColor,
106 LocalAbsoluteElevation provides absoluteElevation
107 ) {
108 Box(
109 modifier =
110 modifier
111 .surface(
112 shape = shape,
113 backgroundColor =
114 surfaceColorAtElevation(
115 color = color,
116 elevationOverlay = LocalElevationOverlay.current,
117 absoluteElevation = absoluteElevation
118 ),
119 border = border,
120 elevation = elevation
121 )
122 .semantics(mergeDescendants = false) {
123 @Suppress("DEPRECATION")
124 isContainer = true
125 }
126 .pointerInput(Unit) {},
127 propagateMinConstraints = true
128 ) {
129 content()
130 }
131 }
132 }
133
134 /**
135 * Material surface is the central metaphor in material design. Each surface exists at a given
136 * elevation, which influences how that piece of surface visually relates to other surfaces and how
137 * that surface casts shadows.
138 *
139 * This version of Surface is responsible for a click handling as well al everything else that a
140 * regular Surface does:
141 *
142 * This clickable Surface is responsible for:
143 * 1) Clipping: Surface clips its children to the shape specified by [shape]
144 * 2) Elevation: Surface draws a shadow to represent depth, where [elevation] represents the depth
145 * of this surface. If the passed [shape] is convex the shadow will not be drawn on Android
146 * versions less than 10.
147 * 3) Borders: If [shape] has a border, then it will also be drawn.
148 * 4) Background: Surface fills the shape specified by [shape] with the [color]. If [color] is
149 * [Colors.surface], the [ElevationOverlay] from [LocalElevationOverlay] will be used to apply an
150 * overlay - by default this will only occur in dark theme. The color of the overlay depends on
151 * the [elevation] of this Surface, and the [LocalAbsoluteElevation] set by any parent surfaces.
152 * This ensures that a Surface never appears to have a lower elevation overlay than its
153 * ancestors, by summing the elevation of all previous Surfaces.
154 * 5) Content color: Surface uses [contentColor] to specify a preferred color for the content of
155 * this surface - this is used by the [Text] and [Icon] components as a default color. If no
156 * [contentColor] is set, this surface will try and match its background color to a color defined
157 * in the theme [Colors], and return the corresponding content color. For example, if the [color]
158 * of this surface is [Colors.surface], [contentColor] will be set to [Colors.onSurface]. If
159 * [color] is not part of the theme palette, [contentColor] will keep the same value set above
160 * this Surface.
161 * 6) Click handling. This version of surface will react to the clicks, calling [onClick] lambda,
162 * updating the [interactionSource] when [PressInteraction] occurs, and showing ripple indication
163 * in response to press events. If you don't need click handling, consider using the Surface
164 * function that doesn't require [onClick] param.
165 * 7) Semantics for clicks. Just like with [Modifier.clickable], clickable version of Surface will
166 * produce semantics to indicate that it is clicked. No semantic role is set by default, you may
167 * specify one by passing a desired [Role] with a [Modifier.semantics].
168 *
169 * @sample androidx.compose.material.samples.ClickableSurfaceSample
170 *
171 * To modify these default style values used by text, use [ProvideTextStyle] or explicitly pass a
172 * new [TextStyle] to your text.
173 *
174 * To manually retrieve the content color inside a surface, use [LocalContentColor].
175 *
176 * @param onClick callback to be called when the surface is clicked
177 * @param modifier Modifier to be applied to the layout corresponding to the surface
178 * @param enabled Controls the enabled state of the surface. When `false`, this surface will not be
179 * clickable
180 * @param shape Defines the surface's shape as well its shadow. A shadow is only displayed if the
181 * [elevation] is greater than zero.
182 * @param color The background color. Use [Color.Transparent] to have no color.
183 * @param contentColor The preferred content color provided by this Surface to its children.
184 * Defaults to either the matching content color for [color], or if [color] is not a color from
185 * the theme, this will keep the same value set above this Surface.
186 * @param border Optional border to draw on top of the surface
187 * @param elevation The size of the shadow below the surface. Note that It will not affect z index
188 * of the Surface. If you want to change the drawing order you can use `Modifier.zIndex`.
189 * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
190 * emitting [Interaction]s for this surface. You can use this to change the surface's appearance
191 * or preview the surface in different states. Note that if `null` is provided, interactions will
192 * still happen internally.
193 * @param content The content to be displayed on this Surface
194 */
195 @ExperimentalMaterialApi
196 @Composable
Surfacenull197 fun Surface(
198 onClick: () -> Unit,
199 modifier: Modifier = Modifier,
200 enabled: Boolean = true,
201 shape: Shape = RectangleShape,
202 color: Color = MaterialTheme.colors.surface,
203 contentColor: Color = contentColorFor(color),
204 border: BorderStroke? = null,
205 elevation: Dp = 0.dp,
206 interactionSource: MutableInteractionSource? = null,
207 content: @Composable () -> Unit
208 ) {
209 val absoluteElevation = LocalAbsoluteElevation.current + elevation
210 CompositionLocalProvider(
211 LocalContentColor provides contentColor,
212 LocalAbsoluteElevation provides absoluteElevation
213 ) {
214 Box(
215 modifier =
216 modifier
217 .minimumInteractiveComponentSize()
218 .surface(
219 shape = shape,
220 backgroundColor =
221 surfaceColorAtElevation(
222 color = color,
223 elevationOverlay = LocalElevationOverlay.current,
224 absoluteElevation = absoluteElevation
225 ),
226 border = border,
227 elevation = elevation
228 )
229 .clickable(
230 interactionSource = interactionSource,
231 indication = ripple(),
232 enabled = enabled,
233 onClick = onClick
234 ),
235 propagateMinConstraints = true
236 ) {
237 content()
238 }
239 }
240 }
241
242 /**
243 * Material surface is the central metaphor in material design. Each surface exists at a given
244 * elevation, which influences how that piece of surface visually relates to other surfaces and how
245 * that surface casts shadows.
246 *
247 * This version of Surface is responsible for a selection handling as well as everything else that a
248 * regular Surface does:
249 *
250 * This selectable Surface is responsible for:
251 * 1) Clipping: Surface clips its children to the shape specified by [shape]
252 * 2) Elevation: Surface draws a shadow to represent depth, where [elevation] represents the depth
253 * of this surface. If the passed [shape] is convex the shadow will not be drawn on Android
254 * versions less than 10.
255 * 3) Borders: If [shape] has a border, then it will also be drawn.
256 * 4) Background: Surface fills the shape specified by [shape] with the [color]. If [color] is
257 * [Colors.surface], the [ElevationOverlay] from [LocalElevationOverlay] will be used to apply an
258 * overlay - by default this will only occur in dark theme. The color of the overlay depends on
259 * the [elevation] of this Surface, and the [LocalAbsoluteElevation] set by any parent surfaces.
260 * This ensures that a Surface never appears to have a lower elevation overlay than its
261 * ancestors, by summing the elevation of all previous Surfaces.
262 * 5) Content color: Surface uses [contentColor] to specify a preferred color for the content of
263 * this surface - this is used by the [Text] and [Icon] components as a default color. If no
264 * [contentColor] is set, this surface will try and match its background color to a color defined
265 * in the theme [Colors], and return the corresponding content color. For example, if the [color]
266 * of this surface is [Colors.surface], [contentColor] will be set to [Colors.onSurface]. If
267 * [color] is not part of the theme palette, [contentColor] will keep the same value set above
268 * this Surface.
269 * 6) Click handling. This version of surface will react to the clicks, calling [onClick] lambda,
270 * updating the [interactionSource] when [PressInteraction] occurs, and showing ripple indication
271 * in response to press events. If you don't need click handling, consider using the Surface
272 * function that doesn't require [onClick] param.
273 * 7) Semantics for selection. Just like with [Modifier.selectable], selectable version of Surface
274 * will produce semantics to indicate that it is selected. No semantic role is set by default,
275 * you may specify one by passing a desired [Role] with a [Modifier.semantics].
276 *
277 * @sample androidx.compose.material.samples.SelectableSurfaceSample
278 *
279 * To modify these default style values used by text, use [ProvideTextStyle] or explicitly pass a
280 * new [TextStyle] to your text.
281 *
282 * To manually retrieve the content color inside a surface, use [LocalContentColor].
283 *
284 * @param selected whether this Surface is selected
285 * @param onClick callback to be called when the surface is clicked
286 * @param modifier Modifier to be applied to the layout corresponding to the surface
287 * @param enabled Controls the enabled state of the surface. When `false`, this surface will not be
288 * selectable
289 * @param shape Defines the surface's shape as well its shadow. A shadow is only displayed if the
290 * [elevation] is greater than zero.
291 * @param color The background color. Use [Color.Transparent] to have no color.
292 * @param contentColor The preferred content color provided by this Surface to its children.
293 * Defaults to either the matching content color for [color], or if [color] is not a color from
294 * the theme, this will keep the same value set above this Surface.
295 * @param border Optional border to draw on top of the surface
296 * @param elevation The size of the shadow below the surface. Note that It will not affect z index
297 * of the Surface. If you want to change the drawing order you can use `Modifier.zIndex`.
298 * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
299 * emitting [Interaction]s for this surface. You can use this to change the surface's appearance
300 * or preview the surface in different states. Note that if `null` is provided, interactions will
301 * still happen internally.
302 * @param content The content to be displayed on this Surface
303 */
304 @ExperimentalMaterialApi
305 @Composable
Surfacenull306 fun Surface(
307 selected: Boolean,
308 onClick: () -> Unit,
309 modifier: Modifier = Modifier,
310 enabled: Boolean = true,
311 shape: Shape = RectangleShape,
312 color: Color = MaterialTheme.colors.surface,
313 contentColor: Color = contentColorFor(color),
314 border: BorderStroke? = null,
315 elevation: Dp = 0.dp,
316 interactionSource: MutableInteractionSource? = null,
317 content: @Composable () -> Unit
318 ) {
319 val absoluteElevation = LocalAbsoluteElevation.current + elevation
320 CompositionLocalProvider(
321 LocalContentColor provides contentColor,
322 LocalAbsoluteElevation provides absoluteElevation
323 ) {
324 Box(
325 modifier =
326 modifier
327 .minimumInteractiveComponentSize()
328 .surface(
329 shape = shape,
330 backgroundColor =
331 surfaceColorAtElevation(
332 color = color,
333 elevationOverlay = LocalElevationOverlay.current,
334 absoluteElevation = absoluteElevation
335 ),
336 border = border,
337 elevation = elevation
338 )
339 .selectable(
340 selected = selected,
341 interactionSource = interactionSource,
342 indication = ripple(),
343 enabled = enabled,
344 onClick = onClick
345 ),
346 propagateMinConstraints = true
347 ) {
348 content()
349 }
350 }
351 }
352
353 /**
354 * Material surface is the central metaphor in material design. Each surface exists at a given
355 * elevation, which influences how that piece of surface visually relates to other surfaces and how
356 * that surface casts shadows.
357 *
358 * This version of Surface is responsible for a toggling its checked state as well as everything
359 * else that a regular Surface does:
360 *
361 * This toggleable Surface is responsible for:
362 * 1) Clipping: Surface clips its children to the shape specified by [shape]
363 * 2) Elevation: Surface draws a shadow to represent depth, where [elevation] represents the depth
364 * of this surface. If the passed [shape] is convex the shadow will not be drawn on Android
365 * versions less than 10.
366 * 3) Borders: If [shape] has a border, then it will also be drawn.
367 * 4) Background: Surface fills the shape specified by [shape] with the [color]. If [color] is
368 * [Colors.surface], the [ElevationOverlay] from [LocalElevationOverlay] will be used to apply an
369 * overlay - by default this will only occur in dark theme. The color of the overlay depends on
370 * the [elevation] of this Surface, and the [LocalAbsoluteElevation] set by any parent surfaces.
371 * This ensures that a Surface never appears to have a lower elevation overlay than its
372 * ancestors, by summing the elevation of all previous Surfaces.
373 * 5) Content color: Surface uses [contentColor] to specify a preferred color for the content of
374 * this surface - this is used by the [Text] and [Icon] components as a default color. If no
375 * [contentColor] is set, this surface will try and match its background color to a color defined
376 * in the theme [Colors], and return the corresponding content color. For example, if the [color]
377 * of this surface is [Colors.surface], [contentColor] will be set to [Colors.onSurface]. If
378 * [color] is not part of the theme palette, [contentColor] will keep the same value set above
379 * this Surface.
380 * 6) Click handling. This version of surface will react to the check toggles, calling
381 * [onCheckedChange] lambda, updating the [interactionSource] when [PressInteraction] occurs, and
382 * showing ripple indication in response to press events. If you don't need check handling,
383 * consider using a Surface function that doesn't require [onCheckedChange] param.
384 * 7) Semantics for toggle. Just like with [Modifier.toggleable], toggleable version of Surface will
385 * produce semantics to indicate that it is checked. No semantic role is set by default, you may
386 * specify one by passing a desired [Role] with a [Modifier.semantics].
387 *
388 * @sample androidx.compose.material.samples.ToggleableSurfaceSample
389 *
390 * To modify these default style values used by text, use [ProvideTextStyle] or explicitly pass a
391 * new [TextStyle] to your text.
392 *
393 * To manually retrieve the content color inside a surface, use [LocalContentColor].
394 *
395 * @param checked whether or not this Surface is toggled on or off
396 * @param onCheckedChange callback to be invoked when the toggleable Surface is clicked
397 * @param modifier Modifier to be applied to the layout corresponding to the surface
398 * @param enabled Controls the enabled state of the surface. When `false`, this surface will not be
399 * selectable
400 * @param shape Defines the surface's shape as well its shadow. A shadow is only displayed if the
401 * [elevation] is greater than zero.
402 * @param color The background color. Use [Color.Transparent] to have no color.
403 * @param contentColor The preferred content color provided by this Surface to its children.
404 * Defaults to either the matching content color for [color], or if [color] is not a color from
405 * the theme, this will keep the same value set above this Surface.
406 * @param border Optional border to draw on top of the surface
407 * @param elevation The size of the shadow below the surface. Note that It will not affect z index
408 * of the Surface. If you want to change the drawing order you can use `Modifier.zIndex`.
409 * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
410 * emitting [Interaction]s for this surface. You can use this to change the surface's appearance
411 * or preview the surface in different states. Note that if `null` is provided, interactions will
412 * still happen internally.
413 * @param content The content to be displayed on this Surface
414 */
415 @ExperimentalMaterialApi
416 @Composable
Surfacenull417 fun Surface(
418 checked: Boolean,
419 onCheckedChange: (Boolean) -> Unit,
420 modifier: Modifier = Modifier,
421 enabled: Boolean = true,
422 shape: Shape = RectangleShape,
423 color: Color = MaterialTheme.colors.surface,
424 contentColor: Color = contentColorFor(color),
425 border: BorderStroke? = null,
426 elevation: Dp = 0.dp,
427 interactionSource: MutableInteractionSource? = null,
428 content: @Composable () -> Unit
429 ) {
430 val absoluteElevation = LocalAbsoluteElevation.current + elevation
431 CompositionLocalProvider(
432 LocalContentColor provides contentColor,
433 LocalAbsoluteElevation provides absoluteElevation
434 ) {
435 Box(
436 modifier =
437 modifier
438 .minimumInteractiveComponentSize()
439 .surface(
440 shape = shape,
441 backgroundColor =
442 surfaceColorAtElevation(
443 color = color,
444 elevationOverlay = LocalElevationOverlay.current,
445 absoluteElevation = absoluteElevation
446 ),
447 border = border,
448 elevation = elevation
449 )
450 .toggleable(
451 value = checked,
452 interactionSource = interactionSource,
453 indication = ripple(),
454 enabled = enabled,
455 onValueChange = onCheckedChange
456 ),
457 propagateMinConstraints = true
458 ) {
459 content()
460 }
461 }
462 }
463
surfacenull464 private fun Modifier.surface(
465 shape: Shape,
466 backgroundColor: Color,
467 border: BorderStroke?,
468 elevation: Dp
469 ) =
470 this.shadow(elevation, shape, clip = false)
471 .then(if (border != null) Modifier.border(border, shape) else Modifier)
472 .background(color = backgroundColor, shape = shape)
473 .clip(shape)
474
475 @Composable
476 private fun surfaceColorAtElevation(
477 color: Color,
478 elevationOverlay: ElevationOverlay?,
479 absoluteElevation: Dp
480 ): Color {
481 return if (color == MaterialTheme.colors.surface && elevationOverlay != null) {
482 elevationOverlay.apply(color, absoluteElevation)
483 } else {
484 color
485 }
486 }
487