🌈 Create new beautiful Compose Gradients with our new wesite ->
Class

TrackedRange

A style applied on the text that is tracked by TextFieldBuffer, returned by TextFieldBuffer.addStyle.

BasicTextFieldTrackedRangeSample

@Sampled
@Composable
fun BasicTextFieldTrackedRangeSample() {
    // This sample demonstrates how to use the `TrackedRange` API to track and modify text ranges
    // dynamically. It implements a basic Markdown-like behavior where text typed inside double
    // asterisks (e.g., **bold**) is automatically bolded, and the asterisks are removed.
    val state = rememberTextFieldState("")

BasicTextFieldTrackedRangeToggleBoldSample

@Sampled
@Composable
fun BasicTextFieldTrackedRangeToggleBoldSample() {
    // 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
            }
        }
    }

BasicTextFieldTrackedRangeTextRangeSetterSample

@Sampled
@Composable
fun BasicTextFieldTrackedRangeTextRangeSetterSample() {
    // Wipe the bold style on a given range using the TrackedRange.textRange API
    val state = TextFieldState("Hello World")

    state.edit {
        // Assume we want to "wipe" all bold styles from the first 5 characters.
        val rangeToWipe = TextRange(0, 5)

        // Get all span styles that intersect with the wipe range.
        getSpanStyles(rangeToWipe).forEach { trackedRange ->
            if (trackedRange.spanStyle.fontWeight == FontWeight.Bold) {
                val current = trackedRange.textRange

                if (rangeToWipe.start <= current.start && current.end <= rangeToWipe.end) {
                    // Case 1: The bold style is entirely within the wipe range, remove it.
                    removeStyle(trackedRange)
                } else if (current.start < rangeToWipe.start && rangeToWipe.end < current.end) {
                    // Case 2: The wipe range is in the middle: split the style into two parts.
                    val oldEnd = current.end
                    // Truncate the original style to end at the start of the wipe range.
                    trackedRange.textRange = TextRange(current.start, rangeToWipe.start)
                    // Add a new bold style starting after the wipe range.
                    addStyle(trackedRange.spanStyle, rangeToWipe.end, oldEnd)
                } else if (current.start < rangeToWipe.start) {
                    // Case 3: Overlap at the start of wipe: truncate the style's end.
                    trackedRange.textRange = TextRange(current.start, rangeToWipe.start)
                } else {
                    // Case 4: Overlap at the end of wipe: truncate the style's start.
                    trackedRange.textRange = TextRange(rangeToWipe.end, current.end)
                }
            }
        }
    }
}

Content updated: