Skip to content

Instantly share code, notes, and snippets.

@nirbhayph
Created September 6, 2023 20:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nirbhayph/8600645d403c7acc671f056dfc4fdb95 to your computer and use it in GitHub Desktop.
Save nirbhayph/8600645d403c7acc671f056dfc4fdb95 to your computer and use it in GitHub Desktop.
Composing AnnotatedString -  Poetry, Music, Code, Blogs, Expandables and Beyond
@Composable
fun AbstractArtText(){
val abstractArtText = buildAnnotatedString {
withStyle(style = SpanStyle(fontSize = 42.sp, brush = Brush.horizontalGradient(listOf(Color.Red, Color.Blue, Color.Green)))) {
append("Colors in Motion")
}
append("\n\n")
append("Vivid shades of ")
withStyle(style = SpanStyle(brush = Brush.verticalGradient(listOf(Color.Magenta, Color.Yellow)))) {
append("passion")
}
append(" blend with the ")
withStyle(style = SpanStyle(brush = Brush.horizontalGradient(listOf(Color.Blue, Color.Green)))) {
append("calmness")
}
append(" of an eternal ")
withStyle(style = SpanStyle(brush = Brush.radialGradient(listOf(Color.Red, Color.Magenta)))) {
append("sunrise")
}
append(". A symphony of ")
withStyle(style = SpanStyle(brush = Brush.linearGradient(listOf(Color.Blue, Color.Cyan)))) {
append("colors")
}
append(" dances in the ")
withStyle(style = SpanStyle(brush = Brush.sweepGradient(listOf(Color.Green, Color.Blue, Color.Magenta)))) {
append("sky")
}
append(" like a dream.")
}
Box(
modifier = Modifier.fillMaxSize().padding(20.dp),
contentAlignment = Alignment.TopCenter,
) {
Text(text = abstractArtText)
}
}
@Composable
fun BasicStyles(){
val boldText = buildAnnotatedString {
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) {
append("Bold Text")
}
append("\n\n")
append("Regular Text")
}
val colorfulText = buildAnnotatedString {
withStyle(style = SpanStyle(color = Color.Red)) {
append("Vibrant Text")
}
append("\n\n")
append("Regular Text")
}
val highlightedText = buildAnnotatedString {
withStyle(style = SpanStyle(background = Color.Yellow)) {
append("Highlighted Text")
}
append("\n\n")
append("Regular Text")
}
}
@Composable
fun BlogPost(){
val blogPost = buildAnnotatedString {
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold, fontSize = 28.sp)) {
append("Title of the Blog Post")
}
append("\n\n")
withStyle(style = SpanStyle(fontSize = 24.sp, fontStyle = FontStyle.Italic)) {
append("Introduction")
}
append("\nLorem ipsum dolor sit amet, consectetur adipiscing elit. ")
withStyle(style = SpanStyle(fontStyle = FontStyle.Italic, color = Color.Gray)) {
append("\nPhoto by John Doe")
}
append("\n\n")
withStyle(style = SpanStyle(Color.Red)) {
append("Quote of the Day\n")
}
append("\"The only way to do great work is to love what you do.\" - Steve Jobs")
}
Box(
modifier = Modifier.fillMaxSize().padding(20.dp),
contentAlignment = Alignment.TopCenter,
) {
Text(text = blogPost)
}
}
@Composable
fun LinkedText(){
val annotatedLinkString: AnnotatedString = buildAnnotatedString {
val str = "Let's open google!"
val startIndex = str.indexOf("google")
val endIndex = startIndex + 6
append(str)
addStyle(
style = SpanStyle(
color = Color.Red,
textDecoration = TextDecoration.Underline
), start = startIndex, end = endIndex
)
addUrlAnnotation(
UrlAnnotation("https://google.com"),
start = startIndex,
end = endIndex
)
}
val uriHandler = LocalUriHandler.current
ClickableText(
modifier = Modifier.padding(20.dp).fillMaxWidth(),
text = annotatedLinkString,
onClick = {
annotatedLinkString
.getUrlAnnotations(it, it)
.firstOrNull()?.let { annotation ->
uriHandler.openUri(annotation.item.url)
}
}
)
}
@Composable
fun Code(){
val codeEditorText = buildAnnotatedString {
append("fun ")
withStyle(style = SpanStyle(color = Color.Blue)) {
append("main")
}
append("() {\n")
append("\t")
withStyle(style = SpanStyle(color = Color.Green)) {
append("// Print \"Hello, World!\"")
}
append("\n")
append("\t")
withStyle(style = SpanStyle(color = Color.Blue)) {
append("println")
}
append("(")
withStyle(style = SpanStyle(color = Color.Red)) {
append("\"Hello, World!\"")
}
append(")\n")
append("}")
}
Box(
modifier = Modifier.fillMaxSize().padding(20.dp),
contentAlignment = Alignment.TopCenter,
) {
Text(text = codeEditorText)
}
}
@Composable
fun ExpandableText(){
// Define tags for expanded and minified text
val tagExpanded = "expanded_text"
val tagMinified = "minified_text"
// Maintain the state of text toggling (expanded or minified)
val textToggleState = remember { mutableStateOf(Pair(tagMinified, "Read more ...")) }
// Create the annotated string for expanded and minified text
val expandedTextString: AnnotatedString = buildAnnotatedString {
val toggleString = textToggleState.value.second
// Define the base article snippet with a toggle string
val snippet = "In a groundbreaking discovery, scientists " +
"have identified a new species of marine life " +
"in the deep sea. $toggleString"
// Find the start and end indices of the toggle string
val startIndex = snippet.indexOf(toggleString)
val endIndex = startIndex + toggleString.length
// Apply styling to the entire snippet (font size and semi-bold)
withStyle(style = SpanStyle(fontSize = 24.sp)) {
withStyle(style = SpanStyle(fontWeight = FontWeight.SemiBold)) {
append(snippet)
}
// If the text is expanded, add more article content
if(textToggleState.value.first == tagExpanded) {
append(
"\n\nThis new species, tentatively named " +
"'DeepSea Marvel,' was found at a depth " +
"of 4,000 meters beneath the ocean's surface."
)
}
}
// Apply styling to the specified range (magenta color and underline)
addStyle(
style = SpanStyle(
color = Color.Magenta,
textDecoration = TextDecoration.Underline
), start = startIndex, end = endIndex
)
// Add annotations based on the text state (expanded or minified)
if(textToggleState.value.first == tagExpanded) {
addStringAnnotation(
tagMinified,
"Read again ...",
start = startIndex,
end = endIndex
)
} else {
addStringAnnotation(
tagExpanded,
"Show less ...",
start = startIndex,
end = endIndex
)
}
}
// Create a clickable text composable to display the article text
ClickableText(
modifier = Modifier.padding(16.dp).fillMaxWidth(),
text = expandedTextString,
onClick = {
// Toggle between expanded and minified text based on annotations
expandedTextString
.getStringAnnotations(it, it)
.firstOrNull()?.let { annotation ->
if(annotation.tag == tagExpanded) {
textToggleState.value = Pair(tagExpanded, annotation.item)
} else {
textToggleState.value = Pair(tagMinified, annotation.item)
}
}
}
)
}
@Composable
fun MusicalNotation(){
val musicalNotation = buildAnnotatedString {
withStyle(style = SpanStyle(fontSize = 24.sp, color = Color.Black)) {
append("Sheet Music")
}
append("\n\n")
withStyle(style = SpanStyle(fontSize = 36.sp, color = Color.Black)) {
append("♪")
}
append(" ")
withStyle(style = SpanStyle(fontSize = 20.sp, color = Color.Gray)) {
append("Piano Sonata in C Minor")
}
append("\n")
withStyle(style = SpanStyle(fontSize = 24.sp, color = Color.Black)) {
append("pp")
}
append(" ")
withStyle(style = SpanStyle(fontSize = 18.sp, color = Color.Gray)) {
append("Softly")
}
append(" ")
withStyle(style = SpanStyle(fontSize = 28.sp, color = Color.Black)) {
append("f")
}
append(" ")
withStyle(style = SpanStyle(fontSize = 18.sp, color = Color.Gray)) {
append("Loudly")
}
}
Box(
modifier = Modifier.fillMaxSize().padding(20.dp),
contentAlignment = Alignment.TopCenter,
) {
Text(text = musicalNotation)
}
}
@Composable
fun LegalLetterText(text: String) {
val customLineSpacingText = buildAnnotatedString {
// Apply paragraph style for custom line spacing using withStyle
withStyle(
style = ParagraphStyle(
lineHeight = 30.sp,
textAlign = TextAlign.Justify,
)
) {
append("Contrary to popular belief, Lorem Ipsum is not simply " +
"random text. It has roots in a piece of classical " +
"Latin literature from 45 BC, making it over 2000 years old. " +
"Richard McClintock, a Latin professor at Hampden-Sydney " +
"College in Virginia, looked up one of the more obscure Latin words," +
" consectetur, from a Lorem Ipsum passage, and going through the " +
"cites of the word in classical literature, discovered the" +
" undoubtable source. Lorem Ipsum comes from sections 1.10.32 " +
"and 1.10.33 of de Finibus Bonorum et Malorum " +
"(The Extremes of Good and Evil) by Cicero, written in 45 BC. " +
"This book is a treatise on the theory of ethics, very " +
"popular during the Renaissance. The first line of Lorem Ipsum, " +
"Lorem ipsum dolor sit amet.., comes from a line in section 1.10.32." +
"The standard chunk of Lorem Ipsum used since the 1500s is " +
"reproduced below for those interested. Sections 1.10.32 and 1.10.33 from " +
"de Finibus Bonorum et Malorum by Cicero are also reproduced in their exact" +
" original form, accompanied by English versions from the 1914 translation by H. Rackham.")
}
}
Text(
text = customLineSpacingText,
modifier = Modifier.fillMaxWidth().padding(16.dp),
)
}
@Composable
fun StylizedPoetry(){
val stylizedPoetry = buildAnnotatedString {
withStyle(style = SpanStyle(fontSize = 24.sp)) {
append("The ")
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold, color = Color.Red)) {
append("rose")
}
append(" is ")
withStyle(style = SpanStyle(fontStyle = FontStyle.Italic, color = Color.Green)) {
append("red")
}
append("\n")
withStyle(style = SpanStyle(textDecoration = TextDecoration.LineThrough, color = Color.Blue)) {
append("The ")
}
withStyle(style = SpanStyle(fontStyle = FontStyle.Italic, color = Color.Red)) {
append("violets")
}
append(" are blue")
}
}
Box(
modifier = Modifier.fillMaxSize().padding(20.dp),
contentAlignment = Alignment.TopCenter,
) {
Text(text = stylizedPoetry)
}
}
@Composable
fun TerminalText(){
val terminalText = buildAnnotatedString {
withStyle(style = SpanStyle(color = Color.Green)) {
append("user@android:~$")
}
append(" ls\n")
withStyle(style = SpanStyle(color = Color.Gray)) {
append("file1.txt file2.txt folder1\n")
}
withStyle(style = SpanStyle(color = Color.Green)) {
append("user@android:~$")
}
append(" cd folder1\n")
withStyle(style = SpanStyle(color = Color.Red)) {
append("cd: folder1: permission denied\n")
}
}
Box(
modifier = Modifier.fillMaxSize().padding(20.dp),
contentAlignment = Alignment.TopCenter,
) {
Text(text = terminalText)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment