| /* |
| * Copyright 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package androidx.compose.foundation.demos.text |
| |
| import androidx.compose.animation.animateColor |
| import androidx.compose.animation.core.RepeatMode |
| import androidx.compose.animation.core.infiniteRepeatable |
| import androidx.compose.animation.core.rememberInfiniteTransition |
| import androidx.compose.animation.core.tween |
| import androidx.compose.foundation.Canvas |
| import androidx.compose.foundation.background |
| import androidx.compose.foundation.layout.Arrangement |
| import androidx.compose.foundation.layout.Box |
| import androidx.compose.foundation.layout.Column |
| import androidx.compose.foundation.layout.ColumnScope |
| import androidx.compose.foundation.layout.Row |
| import androidx.compose.foundation.layout.Spacer |
| import androidx.compose.foundation.layout.fillMaxHeight |
| import androidx.compose.foundation.layout.fillMaxSize |
| import androidx.compose.foundation.layout.fillMaxWidth |
| import androidx.compose.foundation.layout.heightIn |
| import androidx.compose.foundation.layout.size |
| import androidx.compose.foundation.layout.width |
| import androidx.compose.foundation.lazy.LazyColumn |
| import androidx.compose.foundation.selection.selectable |
| import androidx.compose.foundation.selection.selectableGroup |
| import androidx.compose.foundation.text.InlineTextContent |
| import androidx.compose.foundation.text.appendInlineContent |
| import androidx.compose.material.ExperimentalMaterialApi |
| import androidx.compose.material.Icon |
| import androidx.compose.material.IconButton |
| import androidx.compose.material.ListItem |
| import androidx.compose.material.RadioButton |
| import androidx.compose.material.Slider |
| import androidx.compose.material.Switch |
| import androidx.compose.material.Text |
| import androidx.compose.material.icons.Icons |
| import androidx.compose.material.icons.filled.KeyboardArrowDown |
| import androidx.compose.material.icons.filled.KeyboardArrowUp |
| import androidx.compose.runtime.Composable |
| import androidx.compose.runtime.CompositionLocalProvider |
| import androidx.compose.runtime.getValue |
| import androidx.compose.runtime.mutableFloatStateOf |
| import androidx.compose.runtime.mutableStateOf |
| import androidx.compose.runtime.remember |
| import androidx.compose.runtime.setValue |
| import androidx.compose.ui.Alignment |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.geometry.Offset |
| import androidx.compose.ui.graphics.Color |
| import androidx.compose.ui.graphics.Shadow |
| import androidx.compose.ui.graphics.graphicsLayer |
| import androidx.compose.ui.platform.LocalLayoutDirection |
| import androidx.compose.ui.semantics.Role |
| import androidx.compose.ui.text.AnnotatedString |
| import androidx.compose.ui.text.ExperimentalTextApi |
| import androidx.compose.ui.text.ParagraphStyle |
| import androidx.compose.ui.text.Placeholder |
| import androidx.compose.ui.text.PlaceholderVerticalAlign |
| import androidx.compose.ui.text.SpanStyle |
| import androidx.compose.ui.text.TextStyle |
| import androidx.compose.ui.text.buildAnnotatedString |
| import androidx.compose.ui.text.drawText |
| import androidx.compose.ui.text.font.FontFamily |
| import androidx.compose.ui.text.font.FontStyle |
| import androidx.compose.ui.text.font.FontWeight |
| import androidx.compose.ui.text.intl.LocaleList |
| import androidx.compose.ui.text.rememberTextMeasurer |
| import androidx.compose.ui.text.samples.BaselineShiftSample |
| import androidx.compose.ui.text.samples.FontFamilyCursiveSample |
| import androidx.compose.ui.text.samples.FontFamilyMonospaceSample |
| import androidx.compose.ui.text.samples.FontFamilySansSerifSample |
| import androidx.compose.ui.text.samples.FontFamilySerifSample |
| import androidx.compose.ui.text.samples.ParagraphStyleAnnotatedStringsSample |
| import androidx.compose.ui.text.samples.ParagraphStyleSample |
| import androidx.compose.ui.text.samples.TextDecorationCombinedSample |
| import androidx.compose.ui.text.samples.TextDecorationLineThroughSample |
| import androidx.compose.ui.text.samples.TextDecorationUnderlineSample |
| import androidx.compose.ui.text.samples.TextOverflowClipSample |
| import androidx.compose.ui.text.samples.TextOverflowEllipsisSample |
| import androidx.compose.ui.text.samples.TextOverflowVisibleFixedSizeSample |
| import androidx.compose.ui.text.samples.TextOverflowVisibleMinHeightSample |
| import androidx.compose.ui.text.samples.TextStyleSample |
| import androidx.compose.ui.text.style.Hyphens |
| import androidx.compose.ui.text.style.TextAlign |
| import androidx.compose.ui.text.style.TextOverflow |
| import androidx.compose.ui.text.withStyle |
| import androidx.compose.ui.tooling.preview.Preview |
| import androidx.compose.ui.unit.LayoutDirection |
| import androidx.compose.ui.unit.dp |
| import androidx.compose.ui.unit.em |
| import androidx.compose.ui.unit.sp |
| import androidx.compose.ui.window.Popup |
| import androidx.compose.ui.window.PopupProperties |
| |
| private const val longText = "This is a very-very long string that wraps into a few lines " + |
| "given the width restrictions." |
| const val displayText = "Text Demo" |
| const val displayTextChinese = "文本演示" |
| const val displayTextArabic = "\u0639\u0631\u0636\u0020\u0627\u0644\u0646\u0635" |
| const val displayTextHindi = "पाठ डेमो" |
| const val displayTextBidi = "Text \u0639\u0631\u0636" |
| |
| val fontSize4 = 16.sp |
| val fontSize6 = 20.sp |
| val fontSize8 = 25.sp |
| val fontSize10 = 30.sp |
| |
| private val overflowOptions = listOf(TextOverflow.Visible, TextOverflow.Ellipsis, TextOverflow.Clip) |
| private val paragraphOptions = listOf(true, false) |
| |
| @Preview |
| @Composable |
| fun TextDemo() { |
| LazyColumn { |
| item { |
| TagLine(tag = "color, fontSize, fontWeight and fontStyle") |
| TextDemoBasic() |
| } |
| item { |
| TagLine( |
| tag = "color, fontSize, fontWeight, fontFamily, fontStyle, letterSpacing, " + |
| "background, decoration" |
| ) |
| TextDemoComplexStyling() |
| } |
| item { |
| TagLine(tag = "Chinese, Arabic, and Hindi") |
| TextDemoLanguage() |
| } |
| item { |
| TagLine(tag = "FontFamily generic names") |
| TextDemoFontFamily() |
| } |
| item { |
| TagLine(tag = "FontFamily default values") |
| TextDemoFontFamilyDefaultValues() |
| } |
| item { |
| TagLine(tag = "decoration, decorationColor and decorationStyle") |
| TextDemoTextDecoration() |
| } |
| item { |
| TagLine(tag = "letterSpacing") |
| TextDemoLetterSpacing() |
| } |
| item { |
| TagLine(tag = "baselineShift") |
| TextDemoBaselineShift() |
| } |
| item { |
| TagLine(tag = "lineHeight") |
| TextDemoHeight() |
| } |
| item { |
| TagLine(tag = "background") |
| TextDemoBackground() |
| } |
| item { |
| TagLine(tag = "Locale: Japanese, Simplified and Traditional Chinese") |
| TextDemoLocale() |
| } |
| item { |
| TagLine(tag = "textAlign and textDirection") |
| TextDemoTextAlign() |
| } |
| item { |
| TagLine(tag = "softWrap: on and off") |
| TextDemoSoftWrap() |
| } |
| item { |
| TagLine(tag = "shadow") |
| TextDemoShadowEffect() |
| } |
| item { |
| TagLine(tag = "fontSizeScale") |
| TextDemoFontSizeScale() |
| } |
| item { |
| TagLine(tag = "complex paragraph styling") |
| TextDemoParagraphStyling() |
| } |
| |
| item { |
| TagLine(tag = "textOverflow: Clip, Ellipsis, Visible") |
| TextDemoTextOverflow() |
| } |
| item { |
| TagLine(tag = "inline content") |
| TextDemoInlineContent() |
| } |
| } |
| } |
| |
| @Composable |
| fun TagLine(tag: String) { |
| Text( |
| style = TextStyle(fontSize = fontSize8), |
| text = buildAnnotatedString { |
| append("\n") |
| withStyle( |
| style = SpanStyle( |
| color = Color(0xFFAAAAAA), |
| fontSize = fontSize6 |
| ) |
| ) { |
| append(tag) |
| } |
| } |
| ) |
| } |
| |
| @Composable |
| fun SecondTagLine(tag: String) { |
| Text( |
| text = buildAnnotatedString { |
| withStyle( |
| style = SpanStyle( |
| color = Color(0xFFAAAAAA), |
| fontSize = fontSize4 |
| ) |
| ) { |
| append(tag) |
| } |
| } |
| ) |
| } |
| |
| @Composable |
| fun TextDemoBasic() { |
| // This group of text composables show different color, fontSize, fontWeight and fontStyle in |
| // English. |
| Text( |
| text = buildAnnotatedString { |
| withStyle( |
| SpanStyle( |
| color = Color(0xFFFF0000), |
| fontSize = fontSize6, |
| fontWeight = FontWeight.W200, |
| fontStyle = FontStyle.Italic |
| ) |
| ) { |
| append("$displayText ") |
| } |
| |
| withStyle( |
| SpanStyle( |
| color = Color(0xFF00FF00), |
| fontSize = fontSize8, |
| fontWeight = FontWeight.W500, |
| fontStyle = FontStyle.Normal |
| ) |
| ) { |
| append("$displayText ") |
| } |
| |
| withStyle( |
| SpanStyle( |
| color = Color(0xFF0000FF), |
| fontSize = fontSize10, |
| fontWeight = FontWeight.W800, |
| fontStyle = FontStyle.Normal |
| ) |
| ) { |
| append(displayText) |
| } |
| } |
| ) |
| } |
| |
| @Composable |
| fun TextDemoComplexStyling() { |
| TextStyleSample() |
| } |
| |
| @Composable |
| fun TextDemoLanguage() { |
| // This group of text composables show different color, fontSize, fontWeight and fontStyle in |
| // Chinese, Arabic, and Hindi. |
| Text( |
| text = buildAnnotatedString { |
| withStyle( |
| style = SpanStyle( |
| color = Color(0xFFFF0000), |
| fontSize = fontSize6, |
| fontWeight = FontWeight.W200, |
| fontStyle = FontStyle.Italic |
| ) |
| ) { |
| append("$displayTextChinese ") |
| } |
| |
| withStyle( |
| style = SpanStyle( |
| color = Color(0xFF00FF00), |
| fontSize = fontSize8, |
| fontWeight = FontWeight.W500, |
| fontStyle = FontStyle.Normal |
| ) |
| ) { |
| append("$displayTextArabic ") |
| } |
| |
| withStyle( |
| style = SpanStyle( |
| color = Color(0xFF0000FF), |
| fontSize = fontSize10, |
| fontWeight = FontWeight.W800, |
| fontStyle = FontStyle.Normal |
| ) |
| ) { |
| append(displayTextHindi) |
| } |
| } |
| ) |
| } |
| |
| @Composable |
| fun TextDemoFontFamily() { |
| // This group of text composables show different fontFamilies in English. |
| Text( |
| buildAnnotatedString { |
| withStyle( |
| style = SpanStyle( |
| fontSize = fontSize8, |
| fontFamily = FontFamily.SansSerif |
| ) |
| ) { |
| append("$displayText sans-serif\n") |
| } |
| |
| withStyle( |
| style = SpanStyle( |
| fontSize = fontSize8, |
| fontFamily = FontFamily.Serif |
| ) |
| ) { |
| append("$displayText serif\n") |
| } |
| |
| withStyle( |
| style = SpanStyle( |
| fontSize = fontSize8, |
| fontFamily = FontFamily.Monospace |
| ) |
| ) { |
| append("$displayText monospace") |
| } |
| } |
| ) |
| } |
| |
| @Composable |
| fun TextDemoFontFamilyDefaultValues() { |
| // This group of text composables show the default font families in English. |
| FontFamilySerifSample() |
| FontFamilySansSerifSample() |
| FontFamilyMonospaceSample() |
| FontFamilyCursiveSample() |
| } |
| |
| @Composable |
| fun TextDemoTextDecoration() { |
| // This group of text composables show different decoration, decorationColor and decorationStyle. |
| TextDecorationLineThroughSample() |
| TextDecorationUnderlineSample() |
| TextDecorationCombinedSample() |
| } |
| |
| @Composable |
| fun TextDemoLetterSpacing() { |
| // This group of text composables show different letterSpacing. |
| Text( |
| text = buildAnnotatedString { |
| withStyle(style = SpanStyle(fontSize = fontSize8)) { |
| append("$displayText ") |
| } |
| withStyle( |
| style = SpanStyle( |
| fontSize = fontSize8, |
| letterSpacing = 0.5.em |
| ) |
| ) { |
| append(displayText) |
| } |
| } |
| ) |
| } |
| |
| @Composable |
| fun TextDemoBaselineShift() { |
| BaselineShiftSample() |
| } |
| |
| @Composable |
| fun TextDemoHeight() { |
| // This group of text composables show different height. |
| Row(Modifier.fillMaxWidth()) { |
| Text( |
| text = "$displayText\n$displayText ", |
| style = TextStyle(fontSize = fontSize8) |
| ) |
| Text( |
| text = "$displayText\n$displayText ", |
| style = TextStyle(fontSize = fontSize8, lineHeight = 50.sp) |
| ) |
| } |
| } |
| |
| @Composable |
| fun TextDemoBackground() { |
| // This group of text composables show different background. |
| Text( |
| text = buildAnnotatedString { |
| withStyle(style = SpanStyle(background = Color(0xFFFF0000))) { |
| append("$displayText ") |
| } |
| |
| withStyle(style = SpanStyle(background = Color(0xFF00FF00))) { |
| append("$displayText ") |
| } |
| |
| withStyle(style = SpanStyle(background = Color(0xFF0000FF))) { |
| append(displayText) |
| } |
| }, |
| style = TextStyle(fontSize = fontSize8) |
| ) |
| } |
| |
| @Composable |
| fun TextDemoLocale() { |
| // This group of text composables show different Locales of the same Unicode codepoint. |
| val text = "\u82B1" |
| Text( |
| text = buildAnnotatedString { |
| withStyle(style = SpanStyle(localeList = LocaleList("ja-JP"))) { |
| append("$text ") |
| } |
| |
| withStyle(style = SpanStyle(localeList = LocaleList("zh-CN"))) { |
| append("$text ") |
| } |
| |
| withStyle(style = SpanStyle(localeList = LocaleList("zh-TW"))) { |
| append(text) |
| } |
| }, |
| style = TextStyle(fontSize = fontSize8) |
| ) |
| } |
| |
| @Composable |
| fun TextDemoTextAlign() { |
| // This group of text composables show different TextAligns: LEFT, RIGHT, CENTER, JUSTIFY, START for |
| // LTR and RTL, END for LTR and RTL. |
| var text = "" |
| for (i in 1..10) { |
| text = "$text$displayText " |
| } |
| Column(Modifier.fillMaxHeight()) { |
| SecondTagLine(tag = "textAlign = TextAlign.Left") |
| Text( |
| modifier = Modifier.fillMaxWidth(), |
| text = displayText, |
| style = TextStyle(fontSize = fontSize8, textAlign = TextAlign.Left) |
| ) |
| |
| SecondTagLine(tag = "textAlign = TextAlign.Right") |
| Text( |
| modifier = Modifier.fillMaxWidth(), |
| text = displayText, |
| style = TextStyle(fontSize = fontSize8, textAlign = TextAlign.Right) |
| ) |
| |
| SecondTagLine(tag = "textAlign = TextAlign.Center") |
| Text( |
| modifier = Modifier.fillMaxWidth(), |
| text = displayText, |
| style = TextStyle(fontSize = fontSize8, textAlign = TextAlign.Center) |
| ) |
| |
| SecondTagLine(tag = "textAlign = default and TextAlign.Justify") |
| Text( |
| modifier = Modifier.fillMaxWidth(), |
| text = text, |
| style = TextStyle( |
| fontSize = fontSize8, |
| color = Color(0xFFFF0000) |
| ) |
| ) |
| Text( |
| modifier = Modifier.fillMaxWidth(), |
| text = text, |
| style = TextStyle( |
| fontSize = fontSize8, |
| color = Color(0xFF0000FF), |
| textAlign = TextAlign.Justify |
| ) |
| ) |
| |
| SecondTagLine(tag = "textAlign = TextAlign.Start for Ltr") |
| Text( |
| modifier = Modifier.fillMaxWidth(), |
| text = displayText, |
| style = TextStyle(fontSize = fontSize8, textAlign = TextAlign.Start) |
| ) |
| SecondTagLine(tag = "textAlign = TextAlign.Start for Rtl") |
| Text( |
| modifier = Modifier.fillMaxWidth(), |
| text = displayTextArabic, |
| style = TextStyle(fontSize = fontSize8, textAlign = TextAlign.Start) |
| ) |
| SecondTagLine(tag = "textAlign = TextAlign.End for Ltr") |
| Text( |
| modifier = Modifier.fillMaxWidth(), |
| text = displayText, |
| style = TextStyle(fontSize = fontSize8, textAlign = TextAlign.End) |
| ) |
| SecondTagLine(tag = "textAlign = TextAlign.End for Rtl") |
| Text( |
| modifier = Modifier.fillMaxWidth(), |
| text = displayTextArabic, |
| style = TextStyle(fontSize = fontSize8, textAlign = TextAlign.End) |
| ) |
| } |
| } |
| |
| @Composable |
| fun TextDemoSoftWrap() { |
| // This group of text composables show difference between softWrap is true and false. |
| var text = "" |
| for (i in 1..10) { |
| text = "$text$displayText" |
| } |
| val textStyle = TextStyle(fontSize = fontSize8, color = Color(0xFFFF0000)) |
| |
| Column(Modifier.fillMaxHeight()) { |
| Text(text = text, style = textStyle) |
| Text(text = text, style = textStyle, softWrap = false) |
| } |
| } |
| |
| @Composable |
| fun TextDemoHyphens() { |
| val text = "Transformation" |
| val textStyleHyphensOn = TextStyle(fontSize = fontSize8, color = Color.Red, |
| hyphens = Hyphens.Auto) |
| val textStyleHyphensOff = TextStyle(fontSize = fontSize8, color = Color.Blue, |
| hyphens = Hyphens.None) |
| Column { |
| var width by remember { mutableFloatStateOf(30f) } |
| Slider( |
| value = width, |
| onValueChange = { width = it }, |
| valueRange = 20f..400f |
| ) |
| Column(Modifier.width(width.dp)) { |
| Text(text = text, style = textStyleHyphensOn) |
| Text(text = text, style = textStyleHyphensOff) |
| } |
| } |
| } |
| |
| @Composable |
| fun TextDemoShadowEffect() { |
| val shadow = Shadow( |
| Color(0xFFE0A0A0), |
| Offset(5f, 5f), |
| blurRadius = 5.0f |
| ) |
| Text( |
| style = TextStyle(fontSize = fontSize8), |
| text = buildAnnotatedString { |
| append("text with ") |
| withStyle(style = SpanStyle(shadow = shadow)) { |
| append("shadow!") |
| } |
| } |
| ) |
| } |
| |
| @Composable |
| fun TextDemoFontSizeScale() { |
| Text( |
| style = TextStyle(fontSize = fontSize8), |
| text = buildAnnotatedString { |
| for (i in 4..12 step 4) { |
| val scale = i * 0.1f |
| withStyle(style = SpanStyle(fontSize = scale.em)) { |
| append("fontSizeScale=$scale\n") |
| } |
| } |
| } |
| ) |
| } |
| |
| @Composable |
| fun TextDemoParagraphStyling() { |
| ParagraphStyleSample() |
| ParagraphStyleAnnotatedStringsSample() |
| } |
| |
| @Composable |
| fun TextDemoTextOverflow() { |
| SecondTagLine(tag = "overflow = TextOverflow.Clip") |
| TextOverflowClipSample() |
| SecondTagLine(tag = "overflow = TextOverflow.Ellipsis") |
| TextOverflowEllipsisSample() |
| SecondTagLine(tag = "overflow = TextOverflow.Visible with fixed size") |
| TextOverflowVisibleFixedSizeSample() |
| Spacer(modifier = Modifier.size(30.dp)) |
| SecondTagLine(tag = "overflow = TextOverflow.Visible with fixed width and min height") |
| TextOverflowVisibleMinHeightSample() |
| } |
| |
| @Composable |
| fun TextOverflowVisibleInPopupDemo() { |
| Popup( |
| alignment = Alignment.Center, |
| properties = PopupProperties(clippingEnabled = false) |
| ) { |
| val text = "Line\n".repeat(10) |
| Box(Modifier.background(Color.Magenta).size(100.dp)) { |
| Text(text, fontSize = fontSize6, overflow = TextOverflow.Visible) |
| } |
| } |
| } |
| |
| @OptIn(ExperimentalTextApi::class) |
| @Composable |
| fun TextOverflowVisibleInDrawText() { |
| val textMeasurer = rememberTextMeasurer() |
| val text = "Line\n".repeat(10) |
| Box(Modifier.fillMaxSize()) { |
| Canvas(Modifier |
| .graphicsLayer() |
| .align(Alignment.Center) |
| .background(Color.Green) |
| .size(100.dp) |
| ) { |
| drawText( |
| textMeasurer = textMeasurer, |
| text = text, |
| style = TextStyle(fontSize = fontSize6), |
| overflow = TextOverflow.Visible |
| ) |
| } |
| } |
| } |
| |
| @Composable |
| fun TextOverflowDemo() { |
| Column { |
| var singleParagraph by remember { mutableStateOf(paragraphOptions[0]) } |
| var selectedOverflow by remember { mutableStateOf(overflowOptions[0]) } |
| Row(Modifier.fillMaxWidth()) { |
| Column(Modifier.selectableGroup().weight(1f)) { |
| Text("TextOverflow", fontWeight = FontWeight.Bold) |
| overflowOptions.forEach { |
| Row( |
| Modifier |
| .fillMaxWidth() |
| .selectable( |
| selected = (it == selectedOverflow), |
| onClick = { selectedOverflow = it }, |
| role = Role.RadioButton |
| ), |
| verticalAlignment = Alignment.CenterVertically |
| ) { |
| RadioButton( |
| selected = (it == selectedOverflow), |
| onClick = null // null recommended for accessibility with screenreaders |
| ) |
| Text(text = it.toString()) |
| } |
| } |
| } |
| Column(Modifier.selectableGroup().weight(1f)) { |
| Text("Paragraph", fontWeight = FontWeight.Bold) |
| paragraphOptions.forEach { |
| Row( |
| Modifier |
| .fillMaxWidth() |
| .selectable( |
| selected = (it == singleParagraph), |
| onClick = { singleParagraph = it }, |
| role = Role.RadioButton |
| ), |
| verticalAlignment = Alignment.CenterVertically |
| ) { |
| RadioButton( |
| selected = (it == singleParagraph), |
| onClick = null // null recommended for accessibility with screenreaders |
| ) |
| Text(text = if (it) "Single" else "Multi") |
| } |
| } |
| } |
| } |
| |
| TextOverflowDemo(singleParagraph, selectedOverflow) |
| } |
| } |
| |
| @Composable |
| private fun ColumnScope.TextOverflowDemo(singleParagraph: Boolean, textOverflow: TextOverflow) { |
| Box(Modifier.weight(1f).fillMaxWidth()) { |
| val text = if (singleParagraph) { |
| AnnotatedString(longText) |
| } else { |
| buildAnnotatedString { |
| append(longText) |
| withStyle(ParagraphStyle(textAlign = TextAlign.End)) { |
| append("This is a second paragraph.") |
| } |
| } |
| } |
| Text( |
| text = text, |
| modifier = Modifier.align(Alignment.Center).background(Color.Magenta).size(100.dp), |
| fontSize = fontSize6, |
| overflow = textOverflow |
| ) |
| } |
| } |
| |
| @Composable |
| fun TextDemoInlineContent() { |
| val inlineContentId = "box" |
| val inlineTextContent = InlineTextContent( |
| placeholder = Placeholder( |
| width = 5.em, |
| height = 1.em, |
| placeholderVerticalAlign = PlaceholderVerticalAlign.AboveBaseline |
| ) |
| ) { |
| val colorAnimation = rememberInfiniteTransition() |
| val color by colorAnimation.animateColor( |
| initialValue = Color.Red, |
| targetValue = Color.Blue, |
| animationSpec = infiniteRepeatable(tween(1000), RepeatMode.Reverse) |
| ) |
| Box(modifier = Modifier |
| .fillMaxSize() |
| .background(color)) |
| } |
| |
| Text( |
| text = buildAnnotatedString { |
| append("Here is a wide inline composable ") |
| appendInlineContent(inlineContentId) |
| append(" that is repeatedly changing its color.") |
| }, |
| inlineContent = mapOf(inlineContentId to inlineTextContent), |
| modifier = Modifier.fillMaxWidth() |
| ) |
| |
| SecondTagLine(tag = "RTL Layout") |
| CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) { |
| Text( |
| text = buildAnnotatedString { |
| append("Here is a wide inline composable ") |
| appendInlineContent(inlineContentId) |
| append(" that is repeatedly changing its color.") |
| }, |
| inlineContent = mapOf(inlineContentId to inlineTextContent), |
| modifier = Modifier.fillMaxWidth() |
| ) |
| } |
| |
| SecondTagLine(tag = "Bidi Text - LTR/RTL") |
| Text( |
| text = buildAnnotatedString { |
| append("$displayText ") |
| appendInlineContent(inlineContentId) |
| append("$displayTextArabic ") |
| }, |
| inlineContent = mapOf(inlineContentId to inlineTextContent), |
| modifier = Modifier.fillMaxWidth() |
| ) |
| |
| SecondTagLine(tag = "Bidi Text - RTL/LTR") |
| Text( |
| text = buildAnnotatedString { |
| append("$displayTextArabic ") |
| appendInlineContent(inlineContentId) |
| append("$displayText ") |
| }, |
| inlineContent = mapOf(inlineContentId to inlineTextContent), |
| modifier = Modifier.fillMaxWidth() |
| ) |
| } |
| |
| @OptIn(ExperimentalMaterialApi::class) |
| @Composable |
| fun EllipsizeDemo() { |
| var softWrap by remember { mutableStateOf(true) } |
| var ellipsis by remember { mutableStateOf(true) } |
| var withSpans by remember { mutableStateOf(false) } |
| val lineHeight = remember { mutableStateOf(16.sp) } |
| val heightRestriction = remember { mutableStateOf(45.dp) } |
| |
| Column { |
| ListItem( |
| Modifier.selectable(softWrap) { softWrap = !softWrap }, |
| trailing = { Switch(softWrap, null) } |
| ) { |
| Text("Soft wrap") |
| } |
| ListItem( |
| Modifier.selectable(ellipsis) { ellipsis = !ellipsis }, |
| trailing = { Switch(ellipsis, null) } |
| ) { |
| Text("Ellipsis") |
| } |
| ListItem( |
| Modifier.selectable(withSpans) { withSpans = !withSpans }, |
| trailing = { Switch(withSpans, null) }, |
| secondaryText = { Text("Text with spans") } |
| ) { |
| Text("Spans") |
| } |
| |
| Row(horizontalArrangement = Arrangement.SpaceAround, modifier = Modifier.fillMaxWidth()) { |
| Column(horizontalAlignment = Alignment.CenterHorizontally) { |
| IconButton({ |
| heightRestriction.value = (heightRestriction.value + 5.dp).coerceAtMost(300.dp) |
| }) { |
| Icon(Icons.Default.KeyboardArrowUp, "Increase height") |
| } |
| Text("Max height ${heightRestriction.value}") |
| IconButton({ |
| heightRestriction.value = (heightRestriction.value - 5.dp).coerceAtLeast(0.dp) |
| }) { |
| Icon(Icons.Default.KeyboardArrowDown, "Decrease height") |
| } |
| } |
| |
| Column(horizontalAlignment = Alignment.CenterHorizontally) { |
| IconButton({ |
| lineHeight.value = ((lineHeight.value.value + 2f)).coerceAtMost(100f).sp |
| }) { |
| Icon(Icons.Default.KeyboardArrowUp, "Increase line height") |
| } |
| Text("Line height ${lineHeight.value.value.toInt().sp}") |
| IconButton({ |
| lineHeight.value = ((lineHeight.value.value - 2f)).coerceAtLeast(5f).sp |
| }) { |
| Icon(Icons.Default.KeyboardArrowDown, "Decrease line height") |
| } |
| } |
| } |
| |
| val fontSize = 16.sp |
| val text = "This is a very-very " + |
| "long text that has a limited height and width to test how it's ellipsized." + |
| " This is a second sentence of the text." |
| val textWithSpans = buildAnnotatedString { |
| withStyle(SpanStyle(fontSize = fontSize / 2)) { |
| append("This is a very-very long text that has ") |
| } |
| withStyle(SpanStyle(fontSize = fontSize * 2)) { |
| append("a limited height") |
| } |
| append(" and width to test how it's ellipsized. This is a second sentence of the text.") |
| } |
| Text( |
| text = if (withSpans) textWithSpans else AnnotatedString(text), |
| fontSize = fontSize, |
| lineHeight = lineHeight.value, |
| modifier = Modifier |
| .background(Color.Magenta) |
| .width(200.dp) |
| .heightIn(max = heightRestriction.value), |
| softWrap = softWrap, |
| overflow = if (ellipsis) TextOverflow.Ellipsis else TextOverflow.Clip |
| ) |
| } |
| } |