1 /*
<lambda>null2  * Copyright 2023 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.systemui.ribbon.ui.composable
18 
19 import androidx.compose.foundation.background
20 import androidx.compose.foundation.layout.Box
21 import androidx.compose.runtime.Composable
22 import androidx.compose.ui.Modifier
23 import androidx.compose.ui.graphics.Color
24 import androidx.compose.ui.graphics.graphicsLayer
25 import androidx.compose.ui.layout.layout
26 import com.android.compose.modifiers.thenIf
27 import kotlin.math.PI
28 import kotlin.math.cos
29 import kotlin.math.roundToInt
30 import kotlin.math.sin
31 import kotlin.math.tan
32 
33 /**
34  * Renders a "ribbon" at the bottom right corner of its container.
35  *
36  * The [content] is rendered leaning at an angle of [degrees] degrees (between `1` and `89`,
37  * inclusive), with an alpha of [alpha] (between `0f` and `1f`, inclusive).
38  *
39  * The background color of the strip can be modified by passing a value to the [backgroundColor] or
40  * `null` to remove the strip background.
41  *
42  * Note: this function assumes that it's been placed at the bottom right of its parent by its
43  * caller. It's the caller's responsibility to meet that assumption by actually placing this
44  * composable element at the bottom right.
45  */
46 @Composable
47 fun BottomRightCornerRibbon(
48     content: @Composable () -> Unit,
49     modifier: Modifier = Modifier,
50     degrees: Int = 45,
51     alpha: Float = 0.6f,
52     backgroundColor: Color? = Color.Red,
53 ) {
54     check(degrees in 1..89)
55     check(alpha in 0f..1f)
56 
57     val radians = degrees * (PI / 180)
58 
59     Box(
60         content = { content() },
61         modifier =
62             modifier
63                 .graphicsLayer {
64                     this.alpha = alpha
65 
66                     val w = size.width
67                     val h = size.height
68 
69                     val sine = sin(radians).toFloat()
70                     val cosine = cos(radians).toFloat()
71 
72                     translationX = (w - w * cosine + h * sine) / 2f
73                     translationY = (h - w * sine + h * cosine) / 2f
74                     rotationZ = 360f - degrees
75                 }
76                 .thenIf(backgroundColor != null) { Modifier.background(backgroundColor!!) }
77                 .layout { measurable, constraints ->
78                     val placeable = measurable.measure(constraints)
79 
80                     val tangent = tan(radians)
81                     val leftPadding = (placeable.measuredHeight / tangent).roundToInt()
82                     val rightPadding = (placeable.measuredHeight * tangent).roundToInt()
83 
84                     layout(
85                         width = placeable.measuredWidth + leftPadding + rightPadding,
86                         height = placeable.measuredHeight,
87                     ) {
88                         placeable.place(leftPadding, 0)
89                     }
90                 }
91     )
92 }
93