1 /*
2 * Copyright (C) 2024 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.qs.ui.compose
18
19 import androidx.compose.foundation.shape.CornerSize
20 import androidx.compose.runtime.getValue
21 import androidx.compose.runtime.mutableStateOf
22 import androidx.compose.runtime.setValue
23 import androidx.compose.ui.Modifier
24 import androidx.compose.ui.focus.FocusEventModifierNode
25 import androidx.compose.ui.focus.FocusState
26 import androidx.compose.ui.geometry.CornerRadius
27 import androidx.compose.ui.geometry.Offset
28 import androidx.compose.ui.geometry.Rect
29 import androidx.compose.ui.graphics.Color
30 import androidx.compose.ui.graphics.drawscope.ContentDrawScope
31 import androidx.compose.ui.graphics.drawscope.Stroke
32 import androidx.compose.ui.node.DrawModifierNode
33 import androidx.compose.ui.node.ModifierNodeElement
34 import androidx.compose.ui.platform.InspectorInfo
35 import androidx.compose.ui.unit.Dp
36 import androidx.compose.ui.unit.dp
37
38 /**
39 * Provides a rounded rect border when the element is focused.
40 *
41 * This should be used for elements that are themselves rounded rects.
42 */
borderOnFocusnull43 fun Modifier.borderOnFocus(
44 color: Color,
45 cornerSize: CornerSize,
46 strokeWidth: Dp = 3.dp,
47 padding: Dp = 2.dp,
48 ) = this then BorderOnFocusElement(color, cornerSize, strokeWidth, padding)
49
50 private class BorderOnFocusNode(
51 var color: Color,
52 var cornerSize: CornerSize,
53 var strokeWidth: Dp,
54 var padding: Dp,
55 ) : FocusEventModifierNode, DrawModifierNode, Modifier.Node() {
56
57 private var focused by mutableStateOf(false)
58
59 override fun onFocusEvent(focusState: FocusState) {
60 focused = focusState.isFocused
61 }
62
63 override fun ContentDrawScope.draw() {
64 drawContent()
65 val focusOutline = Rect(Offset.Zero, size).inflate(padding.toPx())
66 if (focused) {
67 drawRoundRect(
68 color = color,
69 topLeft = focusOutline.topLeft,
70 size = focusOutline.size,
71 cornerRadius = CornerRadius(cornerSize.toPx(focusOutline.size, this)),
72 style = Stroke(strokeWidth.toPx()),
73 )
74 }
75 }
76 }
77
78 private data class BorderOnFocusElement(
79 val color: Color,
80 val cornerSize: CornerSize,
81 val strokeWidth: Dp,
82 val padding: Dp,
83 ) : ModifierNodeElement<BorderOnFocusNode>() {
createnull84 override fun create(): BorderOnFocusNode {
85 return BorderOnFocusNode(color, cornerSize, strokeWidth, padding)
86 }
87
updatenull88 override fun update(node: BorderOnFocusNode) {
89 node.color = color
90 node.cornerSize = cornerSize
91 node.strokeWidth = strokeWidth
92 node.padding = padding
93 }
94
inspectablePropertiesnull95 override fun InspectorInfo.inspectableProperties() {
96 name = "borderOnFocus"
97 properties["color"] = color
98 properties["cornerSize"] = cornerSize
99 properties["strokeWidth"] = strokeWidth
100 properties["padding"] = padding
101 }
102 }
103