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