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