1 /*
2 * Copyright 2021 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 androidx.glance
18
19 import androidx.annotation.RestrictTo
20 import androidx.compose.runtime.Composable
21 import androidx.glance.action.Action
22 import androidx.glance.action.action
23 import androidx.glance.action.clickable
24 import androidx.glance.text.EmittableText
25 import androidx.glance.text.TextStyle
26 import androidx.glance.unit.ColorProvider
27
28 /**
29 * Adds a button view to the glance view.
30 *
31 * @param text The text that this button will show.
32 * @param onClick The action to be performed when this button is clicked.
33 * @param modifier The modifier to be applied to this button.
34 * @param enabled If false, the button will not be clickable.
35 * @param style The style to be applied to the text in this button.
36 * @param colors The colors to use for the background and content of the button.
37 * @param maxLines An optional maximum number of lines for the text to span, wrapping if necessary.
38 * If the text exceeds the given number of lines, it will be truncated.
39 */
40 @Composable
Buttonnull41 fun Button(
42 text: String,
43 onClick: Action,
44 modifier: GlanceModifier = GlanceModifier,
45 enabled: Boolean = true,
46 style: TextStyle? = null,
47 colors: ButtonColors = ButtonDefaults.buttonColors(),
48 maxLines: Int = Int.MAX_VALUE,
49 ) = ButtonElement(text, onClick, modifier, enabled, style, colors, maxLines)
50
51 /**
52 * Adds a button view to the glance view.
53 *
54 * @param text The text that this button will show.
55 * @param onClick The action to be performed when this button is clicked.
56 * @param modifier The modifier to be applied to this button.
57 * @param enabled If false, the button will not be clickable.
58 * @param style The style to be applied to the text in this button.
59 * @param colors The colors to use for the background and content of the button.
60 * @param maxLines An optional maximum number of lines for the text to span, wrapping if necessary.
61 * If the text exceeds the given number of lines, it will be truncated.
62 */
63 @Composable
64 fun Button(
65 text: String,
66 onClick: () -> Unit,
67 modifier: GlanceModifier = GlanceModifier,
68 enabled: Boolean = true,
69 style: TextStyle? = null,
70 colors: ButtonColors = ButtonDefaults.buttonColors(),
71 maxLines: Int = Int.MAX_VALUE,
72 ) = ButtonElement(text, action(block = onClick), modifier, enabled, style, colors, maxLines)
73
74 /**
75 * Adds a button view to the glance view.
76 *
77 * @param text The text that this button will show.
78 * @param onClick The action to be performed when this button is clicked.
79 * @param modifier The modifier to be applied to this button.
80 * @param enabled If false, the button will not be clickable.
81 * @param style The style to be applied to the text in this button.
82 * @param colors The colors to use for the background and content of the button.
83 * @param maxLines An optional maximum number of lines for the text to span, wrapping if necessary.
84 * If the text exceeds the given number of lines, it will be truncated.
85 * @param key A stable and unique key that identifies the action for this button. This ensures that
86 * the correct action is triggered, especially in cases of items that change order. If not
87 * provided we use the key that is automatically generated by the Compose runtime, which is unique
88 * for every exact code location in the composition tree.
89 */
90 @ExperimentalGlanceApi
91 @Composable
92 fun Button(
93 text: String,
94 onClick: () -> Unit,
95 modifier: GlanceModifier = GlanceModifier,
96 enabled: Boolean = true,
97 style: TextStyle? = null,
98 colors: ButtonColors = ButtonDefaults.buttonColors(),
99 maxLines: Int = Int.MAX_VALUE,
100 key: String? = null
101 ) = ButtonElement(text, action(key, onClick), modifier, enabled, style, colors, maxLines)
102
103 @Composable
104 internal fun ButtonElement(
105 text: String,
106 onClick: Action,
107 modifier: GlanceModifier = GlanceModifier,
108 enabled: Boolean = true,
109 style: TextStyle? = null,
110 colors: ButtonColors = ButtonDefaults.buttonColors(),
111 maxLines: Int = Int.MAX_VALUE,
112 ) {
113 var finalModifier = if (enabled) modifier.clickable(onClick) else modifier
114 finalModifier = finalModifier.background(colors.backgroundColor)
115 val finalStyle =
116 style?.copy(color = colors.contentColor) ?: TextStyle(color = colors.contentColor)
117
118 GlanceNode(
119 factory = ::EmittableButton,
120 update = {
121 this.set(text) { this.text = it }
122 this.set(finalModifier) { this.modifier = it }
123 this.set(finalStyle) { this.style = it }
124 this.set(colors) { this.colors = it }
125 this.set(enabled) { this.enabled = it }
126 this.set(maxLines) { this.maxLines = it }
127 }
128 )
129 }
130
131 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
132 class EmittableButton : EmittableWithText() {
133 override var modifier: GlanceModifier = GlanceModifier
134 var colors: ButtonColors? = null
135 var enabled: Boolean = true
136
copynull137 override fun copy(): Emittable =
138 EmittableButton().also {
139 it.modifier = modifier
140 it.text = text
141 it.style = style
142 it.colors = colors
143 it.enabled = enabled
144 it.maxLines = maxLines
145 }
146
toStringnull147 override fun toString(): String =
148 "EmittableButton('$text', enabled=$enabled, style=$style, " +
149 "colors=$colors modifier=$modifier, maxLines=$maxLines)"
150 }
151
152 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
153 fun EmittableButton.toEmittableText() =
154 EmittableText().also {
155 it.modifier = modifier
156 it.text = text
157 it.style = style
158 it.maxLines = maxLines
159 }
160
161 /** Represents the colors used to style a button, prefer this to using the modifier. */
162 class ButtonColors
163 internal constructor(val backgroundColor: ColorProvider, val contentColor: ColorProvider) {
equalsnull164 override fun equals(other: Any?): Boolean {
165 if (this === other) return true
166 if (javaClass != other?.javaClass) return false
167
168 other as ButtonColors
169
170 if (backgroundColor != other.backgroundColor) return false
171 if (contentColor != other.contentColor) return false
172
173 return true
174 }
175
hashCodenull176 override fun hashCode(): Int {
177 var result = backgroundColor.hashCode()
178 result = 31 * result + contentColor.hashCode()
179 return result
180 }
181 }
182
183 /** Contains the default values used by [Button]. */
184 object ButtonDefaults {
185 @Composable
186 /**
187 * Creates a [ButtonColors] that represents the default background and content colors used in a
188 * [Button].
189 *
190 * @param backgroundColor the background color of this [Button]
191 * @param contentColor the content color of this [Button]
192 */
buttonColorsnull193 fun buttonColors(
194 backgroundColor: ColorProvider = GlanceTheme.colors.primary,
195 contentColor: ColorProvider = GlanceTheme.colors.onPrimary
196 ) = ButtonColors(backgroundColor = backgroundColor, contentColor = contentColor)
197 }
198