1 /*
2 * Copyright 2020 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.compose.material
18
19 import androidx.compose.foundation.text.BasicText
20 import androidx.compose.foundation.text.InlineTextContent
21 import androidx.compose.runtime.Composable
22 import androidx.compose.runtime.CompositionLocalProvider
23 import androidx.compose.runtime.compositionLocalOf
24 import androidx.compose.runtime.structuralEqualityPolicy
25 import androidx.compose.ui.Modifier
26 import androidx.compose.ui.graphics.Color
27 import androidx.compose.ui.graphics.isSpecified
28 import androidx.compose.ui.text.AnnotatedString
29 import androidx.compose.ui.text.Paragraph
30 import androidx.compose.ui.text.TextLayoutResult
31 import androidx.compose.ui.text.TextStyle
32 import androidx.compose.ui.text.font.FontFamily
33 import androidx.compose.ui.text.font.FontStyle
34 import androidx.compose.ui.text.font.FontWeight
35 import androidx.compose.ui.text.style.TextAlign
36 import androidx.compose.ui.text.style.TextDecoration
37 import androidx.compose.ui.text.style.TextOverflow
38 import androidx.compose.ui.unit.TextUnit
39
40 /**
41 * High level element that displays text and provides semantics / accessibility information.
42 *
43 * The default [style] uses the [LocalTextStyle] provided by the [MaterialTheme] / components. If
44 * you are setting your own style, you may want to consider first retrieving [LocalTextStyle], and
45 * using [TextStyle.copy] to keep any theme defined attributes, only modifying the specific
46 * attributes you want to override.
47 *
48 * For ease of use, commonly used parameters from [TextStyle] are also present here. The order of
49 * precedence is as follows:
50 * - If a parameter is explicitly set here (i.e, it is _not_ `null` or [TextUnit.Unspecified]), then
51 * this parameter will always be used.
52 * - If a parameter is _not_ set, (`null` or [TextUnit.Unspecified]), then the corresponding value
53 * from [style] will be used instead.
54 *
55 * Additionally, for [color], if [color] is not set, and [style] does not have a color, then
56 * [LocalContentColor] will be used with an alpha of [LocalContentAlpha]- this allows this [Text] or
57 * element containing this [Text] to adapt to different background colors and still maintain
58 * contrast and accessibility.
59 *
60 * @param text The text to be displayed.
61 * @param modifier [Modifier] to apply to this layout node.
62 * @param color [Color] to apply to the text. If [Color.Unspecified], and [style] has no color set,
63 * this will be [LocalContentColor].
64 * @param fontSize The size of glyphs to use when painting the text. See [TextStyle.fontSize].
65 * @param fontStyle The typeface variant to use when drawing the letters (e.g., italic). See
66 * [TextStyle.fontStyle].
67 * @param fontWeight The typeface thickness to use when painting the text (e.g., [FontWeight.Bold]).
68 * @param fontFamily The font family to be used when rendering the text. See [TextStyle.fontFamily].
69 * @param letterSpacing The amount of space to add between each letter. See
70 * [TextStyle.letterSpacing].
71 * @param textDecoration The decorations to paint on the text (e.g., an underline). See
72 * [TextStyle.textDecoration].
73 * @param textAlign The alignment of the text within the lines of the paragraph. See
74 * [TextStyle.textAlign].
75 * @param lineHeight Line height for the [Paragraph] in [TextUnit] unit, e.g. SP or EM. See
76 * [TextStyle.lineHeight].
77 * @param overflow How visual overflow should be handled.
78 * @param softWrap Whether the text should break at soft line breaks. If false, the glyphs in the
79 * text will be positioned as if there was unlimited horizontal space. If [softWrap] is false,
80 * [overflow] and TextAlign may have unexpected effects.
81 * @param maxLines An optional maximum number of lines for the text to span, wrapping if necessary.
82 * If the text exceeds the given number of lines, it will be truncated according to [overflow] and
83 * [softWrap]. It is required that 1 <= [minLines] <= [maxLines].
84 * @param minLines The minimum height in terms of minimum number of visible lines. It is required
85 * that 1 <= [minLines] <= [maxLines].
86 * @param onTextLayout Callback that is executed when a new text layout is calculated. A
87 * [TextLayoutResult] object that callback provides contains paragraph information, size of the
88 * text, baselines and other details. The callback can be used to add additional decoration or
89 * functionality to the text. For example, to draw selection around the text.
90 * @param style Style configuration for the text such as color, font, line height etc.
91 */
92 @Composable
Textnull93 fun Text(
94 text: String,
95 modifier: Modifier = Modifier,
96 color: Color = Color.Unspecified,
97 fontSize: TextUnit = TextUnit.Unspecified,
98 fontStyle: FontStyle? = null,
99 fontWeight: FontWeight? = null,
100 fontFamily: FontFamily? = null,
101 letterSpacing: TextUnit = TextUnit.Unspecified,
102 textDecoration: TextDecoration? = null,
103 textAlign: TextAlign? = null,
104 lineHeight: TextUnit = TextUnit.Unspecified,
105 overflow: TextOverflow = TextOverflow.Clip,
106 softWrap: Boolean = true,
107 maxLines: Int = Int.MAX_VALUE,
108 minLines: Int = 1,
109 onTextLayout: ((TextLayoutResult) -> Unit)? = null,
110 style: TextStyle = LocalTextStyle.current
111 ) {
112 // TL:DR: profile before you change any line of code in this method
113 //
114 // The call to LocalContentAlpha.current looks like it can be avoided by only calling it in the
115 // last else block but, in 1.5, this causes a control flow group to be created because it would
116 // be a conditional call to a composable function. The call is currently made unconditionally
117 // since the call to LocalContentAlpha.current does not create a group (it is a read-only
118 // composable) and looking up the value in the composition locals map is currently faster than
119 // creating a group to avoid it.
120 //
121 // Similar notes regarding lambda allocations. It appears there's a path to optimize for
122 // zero-allocations in the style-provided color route, but this either introduces a group or a
123 // box depending on how it's coded. It's also possible that allocating a final ColorProducer
124 // subclass with no capture may be a successful optimization, but it appeared slower in initial
125 // profiling.
126 //
127 // If changing ANY LINE OF CODE, please confirm that it's faster or the same speed using
128 // profilers and benchmarks.
129 val localContentColor = LocalContentColor.current
130 val localContentAlpha = LocalContentAlpha.current
131 val overrideColorOrUnspecified: Color =
132 if (color.isSpecified) {
133 color
134 } else if (style.color.isSpecified) {
135 style.color
136 } else {
137 localContentColor.copy(localContentAlpha)
138 }
139
140 BasicText(
141 text = text,
142 modifier = modifier,
143 style =
144 style.merge(
145 fontSize = fontSize,
146 fontWeight = fontWeight,
147 textAlign = textAlign ?: TextAlign.Unspecified,
148 lineHeight = lineHeight,
149 fontFamily = fontFamily,
150 textDecoration = textDecoration,
151 fontStyle = fontStyle,
152 letterSpacing = letterSpacing
153 ),
154 onTextLayout = onTextLayout,
155 overflow = overflow,
156 softWrap = softWrap,
157 maxLines = maxLines,
158 minLines = minLines,
159 color = { overrideColorOrUnspecified }
160 )
161 }
162
163 @Deprecated(
164 "Maintained for binary compatibility. Use version with minLines instead",
165 level = DeprecationLevel.HIDDEN
166 )
167 @Composable
Textnull168 fun Text(
169 text: String,
170 modifier: Modifier = Modifier,
171 color: Color = Color.Unspecified,
172 fontSize: TextUnit = TextUnit.Unspecified,
173 fontStyle: FontStyle? = null,
174 fontWeight: FontWeight? = null,
175 fontFamily: FontFamily? = null,
176 letterSpacing: TextUnit = TextUnit.Unspecified,
177 textDecoration: TextDecoration? = null,
178 textAlign: TextAlign? = null,
179 lineHeight: TextUnit = TextUnit.Unspecified,
180 overflow: TextOverflow = TextOverflow.Clip,
181 softWrap: Boolean = true,
182 maxLines: Int = Int.MAX_VALUE,
183 onTextLayout: (TextLayoutResult) -> Unit = {},
184 style: TextStyle = LocalTextStyle.current
185 ) {
186 Text(
187 text,
188 modifier,
189 color,
190 fontSize,
191 fontStyle,
192 fontWeight,
193 fontFamily,
194 letterSpacing,
195 textDecoration,
196 textAlign,
197 lineHeight,
198 overflow,
199 softWrap,
200 maxLines,
201 1,
202 onTextLayout,
203 style
204 )
205 }
206
207 /**
208 * High level element that displays text and provides semantics / accessibility information.
209 *
210 * The default [style] uses the [LocalTextStyle] provided by the [MaterialTheme] / components. If
211 * you are setting your own style, you may want to consider first retrieving [LocalTextStyle], and
212 * using [TextStyle.copy] to keep any theme defined attributes, only modifying the specific
213 * attributes you want to override.
214 *
215 * For ease of use, commonly used parameters from [TextStyle] are also present here. The order of
216 * precedence is as follows:
217 * - If a parameter is explicitly set here (i.e, it is _not_ `null` or [TextUnit.Unspecified]), then
218 * this parameter will always be used.
219 * - If a parameter is _not_ set, (`null` or [TextUnit.Unspecified]), then the corresponding value
220 * from [style] will be used instead.
221 *
222 * Additionally, for [color], if [color] is not set, and [style] does not have a color, then
223 * [LocalContentColor] will be used with an alpha of [LocalContentAlpha]- this allows this [Text] or
224 * element containing this [Text] to adapt to different background colors and still maintain
225 * contrast and accessibility.
226 *
227 * See an example of displaying text with links where links apply the styling from the theme:
228 *
229 * @sample androidx.compose.material.samples.TextWithLinks
230 * @param text The text to be displayed.
231 * @param modifier [Modifier] to apply to this layout node.
232 * @param color [Color] to apply to the text. If [Color.Unspecified], and [style] has no color set,
233 * this will be [LocalContentColor].
234 * @param fontSize The size of glyphs to use when painting the text. See [TextStyle.fontSize].
235 * @param fontStyle The typeface variant to use when drawing the letters (e.g., italic). See
236 * [TextStyle.fontStyle].
237 * @param fontWeight The typeface thickness to use when painting the text (e.g., [FontWeight.Bold]).
238 * @param fontFamily The font family to be used when rendering the text. See [TextStyle.fontFamily].
239 * @param letterSpacing The amount of space to add between each letter. See
240 * [TextStyle.letterSpacing].
241 * @param textDecoration The decorations to paint on the text (e.g., an underline). See
242 * [TextStyle.textDecoration].
243 * @param textAlign The alignment of the text within the lines of the paragraph. See
244 * [TextStyle.textAlign].
245 * @param lineHeight Line height for the [Paragraph] in [TextUnit] unit, e.g. SP or EM. See
246 * [TextStyle.lineHeight].
247 * @param overflow How visual overflow should be handled.
248 * @param softWrap Whether the text should break at soft line breaks. If false, the glyphs in the
249 * text will be positioned as if there was unlimited horizontal space. If [softWrap] is false,
250 * [overflow] and TextAlign may have unexpected effects.
251 * @param maxLines An optional maximum number of lines for the text to span, wrapping if necessary.
252 * If the text exceeds the given number of lines, it will be truncated according to [overflow] and
253 * [softWrap]. It is required that 1 <= [minLines] <= [maxLines].
254 * @param minLines The minimum height in terms of minimum number of visible lines. It is required
255 * that 1 <= [minLines] <= [maxLines].
256 * @param inlineContent A map store composables that replaces certain ranges of the text. It's used
257 * to insert composables into text layout. Check [InlineTextContent] for more information.
258 * @param onTextLayout Callback that is executed when a new text layout is calculated. A
259 * [TextLayoutResult] object that callback provides contains paragraph information, size of the
260 * text, baselines and other details. The callback can be used to add additional decoration or
261 * functionality to the text. For example, to draw selection around the text.
262 * @param style Style configuration for the text such as color, font, line height etc.
263 */
264 @Composable
Textnull265 fun Text(
266 text: AnnotatedString,
267 modifier: Modifier = Modifier,
268 color: Color = Color.Unspecified,
269 fontSize: TextUnit = TextUnit.Unspecified,
270 fontStyle: FontStyle? = null,
271 fontWeight: FontWeight? = null,
272 fontFamily: FontFamily? = null,
273 letterSpacing: TextUnit = TextUnit.Unspecified,
274 textDecoration: TextDecoration? = null,
275 textAlign: TextAlign? = null,
276 lineHeight: TextUnit = TextUnit.Unspecified,
277 overflow: TextOverflow = TextOverflow.Clip,
278 softWrap: Boolean = true,
279 maxLines: Int = Int.MAX_VALUE,
280 minLines: Int = 1,
281 inlineContent: Map<String, InlineTextContent> = mapOf(),
282 onTextLayout: (TextLayoutResult) -> Unit = {},
283 style: TextStyle = LocalTextStyle.current
284 ) {
285 // TL:DR: profile before you change any line of code in this method
286 //
287 // The call to LocalContentAlpha.current looks like it can be avoided by only calling it in the
288 // last else block but, in 1.5, this causes a control flow group to be created because it would
289 // be a conditional call to a composable function. The call is currently made unconditionally
290 // since the call to LocalContentAlpha.current does not create a group (it is a read-only
291 // composable) and looking up the value in the composition locals map is currently faster than
292 // creating a group to avoid it.
293 //
294 // Similar notes regarding lambda allocations. It appears there's a path to optimize for
295 // zero-allocations in the style-provided color route, but this either introduces a group or a
296 // box depending on how it's coded. It's also possible that allocating a final ColorProducer
297 // subclass with no capture may be a successful optimization, but it appeared slower in initial
298 // profiling.
299 //
300 // If changing ANY LINE OF CODE, please confirm that it's faster or the same speed using
301 // profilers and benchmarks.
302 val localContentColor = LocalContentColor.current
303 val localContentAlpha = LocalContentAlpha.current
304 val overrideColorOrUnspecified =
305 if (color.isSpecified) {
306 color
307 } else if (style.color.isSpecified) {
308 style.color
309 } else {
310 localContentColor.copy(localContentAlpha)
311 }
312
313 BasicText(
314 text = text,
315 modifier = modifier,
316 style =
317 style.merge(
318 fontSize = fontSize,
319 fontWeight = fontWeight,
320 textAlign = textAlign ?: TextAlign.Unspecified,
321 lineHeight = lineHeight,
322 fontFamily = fontFamily,
323 textDecoration = textDecoration,
324 fontStyle = fontStyle,
325 letterSpacing = letterSpacing
326 ),
327 onTextLayout = onTextLayout,
328 overflow = overflow,
329 softWrap = softWrap,
330 maxLines = maxLines,
331 minLines = minLines,
332 inlineContent = inlineContent,
<lambda>null333 color = { overrideColorOrUnspecified }
334 )
335 }
336
337 @Deprecated(
338 "Maintained for binary compatibility. Use version with minLines instead",
339 level = DeprecationLevel.HIDDEN
340 )
341 @Composable
Textnull342 fun Text(
343 text: AnnotatedString,
344 modifier: Modifier = Modifier,
345 color: Color = Color.Unspecified,
346 fontSize: TextUnit = TextUnit.Unspecified,
347 fontStyle: FontStyle? = null,
348 fontWeight: FontWeight? = null,
349 fontFamily: FontFamily? = null,
350 letterSpacing: TextUnit = TextUnit.Unspecified,
351 textDecoration: TextDecoration? = null,
352 textAlign: TextAlign? = null,
353 lineHeight: TextUnit = TextUnit.Unspecified,
354 overflow: TextOverflow = TextOverflow.Clip,
355 softWrap: Boolean = true,
356 maxLines: Int = Int.MAX_VALUE,
357 inlineContent: Map<String, InlineTextContent> = mapOf(),
358 onTextLayout: (TextLayoutResult) -> Unit = {},
359 style: TextStyle = LocalTextStyle.current
360 ) {
361 Text(
362 text,
363 modifier,
364 color,
365 fontSize,
366 fontStyle,
367 fontWeight,
368 fontFamily,
369 letterSpacing,
370 textDecoration,
371 textAlign,
372 lineHeight,
373 overflow,
374 softWrap,
375 maxLines,
376 1,
377 inlineContent,
378 onTextLayout,
379 style
380 )
381 }
382
383 /**
384 * CompositionLocal containing the preferred [TextStyle] that will be used by [Text] components by
385 * default. To set the value for this CompositionLocal, see [ProvideTextStyle] which will merge any
386 * missing [TextStyle] properties with the existing [TextStyle] set in this CompositionLocal.
387 *
388 * @see ProvideTextStyle
389 */
<lambda>null390 val LocalTextStyle = compositionLocalOf(structuralEqualityPolicy()) { DefaultTextStyle }
391
392 // TODO: b/156598010 remove this and replace with fold definition on the backing CompositionLocal
393 /**
394 * This function is used to set the current value of [LocalTextStyle], merging the given style with
395 * the current style values for any missing attributes. Any [Text] components included in this
396 * component's [content] will be styled with this style unless styled explicitly.
397 *
398 * @see LocalTextStyle
399 */
400 @Composable
ProvideTextStylenull401 fun ProvideTextStyle(value: TextStyle, content: @Composable () -> Unit) {
402 val mergedStyle = LocalTextStyle.current.merge(value)
403 CompositionLocalProvider(LocalTextStyle provides mergedStyle, content = content)
404 }
405