# TextFieldTextStyles

Artifact: [androidx.compose.foundation:foundation](https://developer.android.com/jetpack/androidx/releases/compose-foundation) [View Source](https://cs.android.com/search?q=file:androidx/compose/foundation/text/input/TextFieldTextStyles.kt+class:androidx.compose.foundation.text.input.TextFieldTextStyles) Added in [1.12.0](https://developer.android.com/jetpack/androidx/releases/compose-foundation#1.12.0) Common/All
- Cmn

  ```
  interface TextFieldTextStyles
  ```

*** ** * ** ***

Provides access to the styles applied to the text within a `https://developer.android.com/reference/kotlin/androidx/compose/foundation/text/input/TextFieldState`.

This interface provides a style query API similar to `https://developer.android.com/reference/kotlin/androidx/compose/foundation/text/input/TextFieldBuffer`, but returns immutable data.

Use this interface when you only need to read the current text styles (e.g., to update a formatting toolbar UI). If you need to mutate the text styles, use `https://developer.android.com/reference/kotlin/androidx/compose/foundation/text/input/TextFieldState#edit(kotlin.Function1)` and the corresponding methods on `https://developer.android.com/reference/kotlin/androidx/compose/foundation/text/input/TextFieldBuffer`.

Do not use this interface to query styles within a `https://developer.android.com/reference/kotlin/androidx/compose/foundation/text/input/TextFieldBuffer` edit block. The data returned will not reflect any ongoing changes made to the buffer, as it only returns the unupdated state from before the edit block began.

```kotlin
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.input.ExpandPolicy
import androidx.compose.foundation.text.input.TextFieldBuffer
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.material3.Button
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.Text
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp

// This sample demonstrates a realistic rich-text editor scenario using the `TrackedRange` and
// `TextFieldTextStyles` APIs. It implements a "Toggle Bold" formatting function on the current
// selection.

// For simplicity, this sample keeps bold styles non-overlapping and contiguous, assuming they
// are
// applied exclusively through this method.
val state = rememberTextFieldState("Hello World")

// This derived state calculates whether the current selection is completely covered by
// bold text styles. This ensures the "Bold" toggle button accurately reflects the
// state of the selected text.
val isSelection100PercentBold by remember {
    derivedStateOf {
        val selection = state.selection
        if (selection.collapsed) {
            false
        } else {
            val spanStyles = state.textStyles.getSpanStyles(selection)
            var boldCoverage = 0
            for (style in spanStyles) {
                if (style.item.fontWeight == FontWeight.Bold) {
                    val overlapStart = maxOf(style.start, selection.min)
                    val overlapEnd = minOf(style.end, selection.max)
                    if (overlapEnd > overlapStart) {
                        boldCoverage += (overlapEnd - overlapStart)
                    }
                }
            }
            boldCoverage == selection.length
        }
    }
}

fun TextFieldBuffer.unBoldSelection() {
    // Query existing bold styles in the selection
    val intersectingStyles =
        getSpanStyles(selection).filter { it.spanStyle.fontWeight == FontWeight.Bold }
    // We modify or remove existing styles to exclude the selected range
    for (style in intersectingStyles) {
        val range = style.textRange
        if (range.start >= selection.min && range.end <= selection.max) {
            // The style is fully inside the selection. Remove it.
            removeStyle(style)
        } else if (range.start < selection.min && range.end > selection.max) {
            // The style completely covers the selection. We need to split it.
            val oldEnd = range.end
            // Truncate the start part
            style.textRange = TextRange(range.start, selection.min)
            // Add a new style for the end part
            addStyle(
                SpanStyle(fontWeight = FontWeight.Bold),
                TextRange(selection.max, oldEnd),
                ExpandPolicy.AtEnd,
            )
        } else if (range.start < selection.min) {
            // The style overlaps with the start of the selection. Truncate it.
            style.textRange = TextRange(range.start, selection.min)
        } else {
            // The style overlaps with the end of the selection. Truncate it.
            style.textRange = TextRange(selection.max, range.end)
        }
    }
}

fun TextFieldBuffer.boldSelection() {
    // Query existing bold styles in the selection
    val intersectingStyles =
        getSpanStyles(selection).filter { it.spanStyle.fontWeight == FontWeight.Bold }
    // To keep bold styles non-overlapping, we merge any intersecting bold
    // styles with the new selection range into a single contiguous bold style.
    var mergedStart = selection.min
    var mergedEnd = selection.max

    for (style in intersectingStyles) {
        mergedStart = minOf(mergedStart, style.textRange.start)
        mergedEnd = maxOf(mergedEnd, style.textRange.end)
        // Remove the fragmented style
        removeStyle(style)
    }

    addStyle(
        SpanStyle(fontWeight = FontWeight.Bold),
        TextRange(mergedStart, mergedEnd),
        ExpandPolicy.AtEnd,
    )
}

Column {
    Button(
        onClick = {
            state.edit {
                val selection = this.selection
                if (selection.collapsed) return@edit
                if (isSelection100PercentBold) {
                    unBoldSelection()
                } else {
                    boldSelection()
                }
            }
        }
    ) {
        Text(
            "B",
            fontWeight = if (isSelection100PercentBold) FontWeight.Bold else FontWeight.Normal,
        )
    }

    BasicTextField(state = state, textStyle = LocalTextStyle.current)
}
```

| See also |
|---|---|
| `https://developer.android.com/reference/kotlin/androidx/compose/foundation/text/input/TextFieldBuffer` |   |
| `https://developer.android.com/reference/kotlin/androidx/compose/foundation/text/input/TextFieldState#textStyles()` |   |

## Summary

| ### Public functions |
|---|---|---|
| `https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/-list/index.html<https://developer.android.com/reference/kotlin/androidx/compose/ui/text/AnnotatedString.Range<https://developer.android.com/reference/kotlin/androidx/compose/ui/text/ParagraphStyle>>` | `https://developer.android.com/reference/kotlin/androidx/compose/foundation/text/input/TextFieldTextStyles#getParagraphStyles(androidx.compose.ui.text.TextRange)(range: https://developer.android.com/reference/kotlin/androidx/compose/ui/text/TextRange)` Returns a list of `https://developer.android.com/reference/kotlin/androidx/compose/ui/text/AnnotatedString.Range`s representing the `https://developer.android.com/reference/kotlin/androidx/compose/ui/text/ParagraphStyle`s that intersect with the given `https://developer.android.com/reference/kotlin/androidx/compose/foundation/text/input/TextFieldTextStyles#getParagraphStyles(androidx.compose.ui.text.TextRange)`. | Cmn |
| `https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/-list/index.html<https://developer.android.com/reference/kotlin/androidx/compose/ui/text/AnnotatedString.Range<https://developer.android.com/reference/kotlin/androidx/compose/ui/text/SpanStyle>>` | `https://developer.android.com/reference/kotlin/androidx/compose/foundation/text/input/TextFieldTextStyles#getSpanStyles(androidx.compose.ui.text.TextRange)(range: https://developer.android.com/reference/kotlin/androidx/compose/ui/text/TextRange)` Returns a list of `https://developer.android.com/reference/kotlin/androidx/compose/ui/text/AnnotatedString.Range`s representing the `https://developer.android.com/reference/kotlin/androidx/compose/ui/text/SpanStyle`s that intersect with the given `https://developer.android.com/reference/kotlin/androidx/compose/foundation/text/input/TextFieldTextStyles#getSpanStyles(androidx.compose.ui.text.TextRange)`. | Cmn |

## Public functions

### getParagraphStyles

Cmn Added in [1.12.0](https://developer.android.com/jetpack/androidx/releases/compose-foundation#1.12.0)

```
fun getParagraphStyles(range: TextRange): List<AnnotatedString.Range<ParagraphStyle>>
```

Returns a list of `https://developer.android.com/reference/kotlin/androidx/compose/ui/text/AnnotatedString.Range`s representing the `https://developer.android.com/reference/kotlin/androidx/compose/ui/text/ParagraphStyle`s that intersect with the given `https://developer.android.com/reference/kotlin/androidx/compose/foundation/text/input/TextFieldTextStyles#getParagraphStyles(androidx.compose.ui.text.TextRange)`.

Styles are returned in the same order they were originally added to the buffer.

A style intersects with the range if it overlaps with it at any point. For non-empty ranges, this means `style.start < range.max` and `range.min < style.end`.

Example Query Range: `[5, 15)`

```kotlin
0    5    10   15   20   25
|---|---|---|---|---|
     [---)              Query Range [5, 15)

[---)         Style [0, 20) (Contains query) -> Returned
          [---)              Style [8, 12) (Inside query)   -> Returned
[---)                  Style [0, 10) (Overlap start)  -> Returned
[---)                        Style [0, 5)  (Touching start) -> NOT Returned
               [---)   Style [15, 25)(Touching end)   -> NOT Returned
```

Example Collapsed Query: `[10, 10)`

```kotlin
0    5    10   15   20   25
|---|---|---|---|---|
          |                   Query Range [10, 10)

[---)         Style [0, 20) (Contains query) -> Returned
[---)                   Style [0, 10) (Touching end)   -> NOT Returned
          [---)        Style [10, 20)(Touching start) -> Returned
```

| Parameters |
|---|---|
| `range: https://developer.android.com/reference/kotlin/androidx/compose/ui/text/TextRange` | The range to query. |

| Returns |
|---|---|
| `https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/-list/index.html<https://developer.android.com/reference/kotlin/androidx/compose/ui/text/AnnotatedString.Range<https://developer.android.com/reference/kotlin/androidx/compose/ui/text/ParagraphStyle>>` | A list of `https://developer.android.com/reference/kotlin/androidx/compose/ui/text/AnnotatedString.Range`s representing the `https://developer.android.com/reference/kotlin/androidx/compose/ui/text/ParagraphStyle`s overlapping with the queried range. |

### getSpanStyles

Cmn Added in [1.12.0](https://developer.android.com/jetpack/androidx/releases/compose-foundation#1.12.0)

```
fun getSpanStyles(range: TextRange): List<AnnotatedString.Range<SpanStyle>>
```

Returns a list of `https://developer.android.com/reference/kotlin/androidx/compose/ui/text/AnnotatedString.Range`s representing the `https://developer.android.com/reference/kotlin/androidx/compose/ui/text/SpanStyle`s that intersect with the given `https://developer.android.com/reference/kotlin/androidx/compose/foundation/text/input/TextFieldTextStyles#getSpanStyles(androidx.compose.ui.text.TextRange)`.

Styles are returned in the same order they were originally added to the buffer.

A style intersects with the range if it overlaps with it at any point. For non-empty ranges, this means `style.start < range.max` and `range.min < style.end`.

Example Query Range: `[5, 15)`

```kotlin
0    5    10   15   20   25
|---|---|---|---|---|
     [---)              Query Range [5, 15)

[---)         Style [0, 20) (Contains query) -> Returned
          [---)              Style [8, 12) (Inside query)   -> Returned
[---)                  Style [0, 10) (Overlap start)  -> Returned
[---)                        Style [0, 5)  (Touching start) -> NOT Returned
               [---)   Style [15, 25)(Touching end)   -> NOT Returned
```

Example Collapsed Query: `[10, 10)`

```kotlin
0    5    10   15   20   25
|---|---|---|---|---|
          |                   Query Range [10, 10)

[---)         Style [0, 20) (Contains query) -> Returned
[---)                   Style [0, 10) (Touching end)   -> NOT Returned
          [---)        Style [10, 20)(Touching start) -> Returned
```

| Parameters |
|---|---|
| `range: https://developer.android.com/reference/kotlin/androidx/compose/ui/text/TextRange` | The range to query. |

| Returns |
|---|---|
| `https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/-list/index.html<https://developer.android.com/reference/kotlin/androidx/compose/ui/text/AnnotatedString.Range<https://developer.android.com/reference/kotlin/androidx/compose/ui/text/SpanStyle>>` | A list of `https://developer.android.com/reference/kotlin/androidx/compose/ui/text/AnnotatedString.Range`s representing the `https://developer.android.com/reference/kotlin/androidx/compose/ui/text/SpanStyle`s overlapping with the queried range. |