1 /*
2  * Copyright (C) 2022 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 com.android.compose.modifiers
18 
19 import androidx.compose.ui.Modifier
20 import androidx.compose.ui.draw.DrawModifier
21 import androidx.compose.ui.geometry.Size
22 import androidx.compose.ui.graphics.Brush
23 import androidx.compose.ui.graphics.Color
24 import androidx.compose.ui.graphics.Outline
25 import androidx.compose.ui.graphics.RectangleShape
26 import androidx.compose.ui.graphics.Shape
27 import androidx.compose.ui.graphics.SolidColor
28 import androidx.compose.ui.graphics.drawOutline
29 import androidx.compose.ui.graphics.drawscope.ContentDrawScope
30 import androidx.compose.ui.platform.InspectorInfo
31 import androidx.compose.ui.platform.InspectorValueInfo
32 import androidx.compose.ui.platform.debugInspectorInfo
33 import androidx.compose.ui.unit.LayoutDirection
34 
35 /**
36  * Draws a fading [shape] with a solid [color] and [alpha] behind the content.
37  *
38  * @param color color to paint background with
39  * @param alpha alpha of the background
40  * @param shape desired shape of the background
41  */
fadingBackgroundnull42 fun Modifier.fadingBackground(color: Color, alpha: () -> Float, shape: Shape = RectangleShape) =
43     this.then(
44         FadingBackground(
45             brush = SolidColor(color),
46             alpha = alpha,
47             shape = shape,
48             inspectorInfo =
49                 debugInspectorInfo {
50                     name = "background"
51                     value = color
52                     properties["color"] = color
53                     properties["alpha"] = alpha
54                     properties["shape"] = shape
55                 },
56         )
57     )
58 
59 private class FadingBackground
60 constructor(
61     private val brush: Brush,
62     private val shape: Shape,
63     private val alpha: () -> Float,
64     inspectorInfo: InspectorInfo.() -> Unit,
65 ) : DrawModifier, InspectorValueInfo(inspectorInfo) {
66     // naive cache outline calculation if size is the same
67     private var lastSize: Size? = null
68     private var lastLayoutDirection: LayoutDirection? = null
69     private var lastOutline: Outline? = null
70 
drawnull71     override fun ContentDrawScope.draw() {
72         if (shape === RectangleShape) {
73             // shortcut to avoid Outline calculation and allocation
74             drawRect()
75         } else {
76             drawOutline()
77         }
78         drawContent()
79     }
80 
ContentDrawScopenull81     private fun ContentDrawScope.drawRect() {
82         drawRect(brush, alpha = alpha())
83     }
84 
drawOutlinenull85     private fun ContentDrawScope.drawOutline() {
86         val outline =
87             if (size == lastSize && layoutDirection == lastLayoutDirection) {
88                 lastOutline!!
89             } else {
90                 shape.createOutline(size, layoutDirection, this)
91             }
92         drawOutline(outline, brush = brush, alpha = alpha())
93         lastOutline = outline
94         lastSize = size
95         lastLayoutDirection = layoutDirection
96     }
97 
hashCodenull98     override fun hashCode(): Int {
99         var result = brush.hashCode()
100         result = 31 * result + alpha.hashCode()
101         result = 31 * result + shape.hashCode()
102         return result
103     }
104 
equalsnull105     override fun equals(other: Any?): Boolean {
106         val otherModifier = other as? FadingBackground ?: return false
107         return brush == otherModifier.brush &&
108             alpha == otherModifier.alpha &&
109             shape == otherModifier.shape
110     }
111 
toStringnull112     override fun toString(): String = "FadingBackground(brush=$brush, alpha = $alpha, shape=$shape)"
113 }
114