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  */
backgroundnull42 fun Modifier.background(
43     color: Color,
44     alpha: () -> Float,
45     shape: Shape = RectangleShape,
46 ) =
47     this.then(
48         FadingBackground(
49             brush = SolidColor(color),
50             alpha = alpha,
51             shape = shape,
52             inspectorInfo =
53                 debugInspectorInfo {
54                     name = "background"
55                     value = color
56                     properties["color"] = color
57                     properties["alpha"] = alpha
58                     properties["shape"] = shape
59                 }
60         )
61     )
62 
63 private class FadingBackground
64 constructor(
65     private val brush: Brush,
66     private val shape: Shape,
67     private val alpha: () -> Float,
68     inspectorInfo: InspectorInfo.() -> Unit
69 ) : DrawModifier, InspectorValueInfo(inspectorInfo) {
70     // naive cache outline calculation if size is the same
71     private var lastSize: Size? = null
72     private var lastLayoutDirection: LayoutDirection? = null
73     private var lastOutline: Outline? = null
74 
drawnull75     override fun ContentDrawScope.draw() {
76         if (shape === RectangleShape) {
77             // shortcut to avoid Outline calculation and allocation
78             drawRect()
79         } else {
80             drawOutline()
81         }
82         drawContent()
83     }
84 
ContentDrawScopenull85     private fun ContentDrawScope.drawRect() {
86         drawRect(brush, alpha = alpha())
87     }
88 
drawOutlinenull89     private fun ContentDrawScope.drawOutline() {
90         val outline =
91             if (size == lastSize && layoutDirection == lastLayoutDirection) {
92                 lastOutline!!
93             } else {
94                 shape.createOutline(size, layoutDirection, this)
95             }
96         drawOutline(outline, brush = brush, alpha = alpha())
97         lastOutline = outline
98         lastSize = size
99         lastLayoutDirection = layoutDirection
100     }
101 
hashCodenull102     override fun hashCode(): Int {
103         var result = brush.hashCode()
104         result = 31 * result + alpha.hashCode()
105         result = 31 * result + shape.hashCode()
106         return result
107     }
108 
equalsnull109     override fun equals(other: Any?): Boolean {
110         val otherModifier = other as? FadingBackground ?: return false
111         return brush == otherModifier.brush &&
112             alpha == otherModifier.alpha &&
113             shape == otherModifier.shape
114     }
115 
toStringnull116     override fun toString(): String = "FadingBackground(brush=$brush, alpha = $alpha, shape=$shape)"
117 }
118