You can get indexes of new line characters '\n' or others and then loop through each of them to get line count.
Result
fun getLine(textFieldValue: TextFieldValue): Int {
val text = textFieldValue.text
val selection = textFieldValue.selection.start
val lineList = mutableListOf< Int>()
text.forEachIndexed { index: Int, c: Char ->
if (c == '\n') {
lineList.add(index)
}
}
if (lineList.isEmpty()) return 1
lineList.forEachIndexed { index, lineEndIndex ->
if (selection <= lineEndIndex){
return index + 1
}
}
return lineList.size + 1
}
If you wish to get not only line but row too you can do it by using a map
fun getLineAndRowOfSelection(textFieldValue: TextFieldValue): Pair<Int, Int> {
val text = textFieldValue.text
val selection = textFieldValue.selection.start
val lineMap = linkedMapOf<Int, Int>()
var lineCount = 1
text.forEachIndexed { index: Int, c: Char ->
if (c == '\n') {
lineMap[index] = lineCount
lineCount++
}
}
if (lineMap.isEmpty()) {
return Pair(1, selection)
} else if (lineMap.keys.last() < selection) {
// Selection is in a row after last new line char
val key = lineMap.keys.last()
val lastLine = lineMap[key] ?: 0
return Pair(lastLine + 1, selection - key - 1)
} else {
// Selection is before last new line char
var previousLineIndex = -1
lineMap.forEach { (lineEndIndex, line) ->
if (selection <= lineEndIndex) {
// First line
return if (previousLineIndex == -1) {
Pair(line, selection)
} else {
Pair(line, selection - previousLineIndex - 1)
}
}
previousLineIndex = lineEndIndex
}
}
return Pair(-1, -1)
}
Demo
@Preview
@Composable
private fun TextSelectionTest() {
Column(
modifier = Modifier.padding(top = 30.dp)
) {
var textFieldValue by remember {
mutableStateOf(TextFieldValue())
}
val lineAndRow = getLineAndRowOfSelection(textFieldValue)
val line = getLine(textFieldValue)
Text(
"Text ${textFieldValue.text}, " +
"selection: ${textFieldValue.selection}\n" +
"line and row: $lineAndRow\n" +
"line: $line"
)
TextField(value = textFieldValue, onValueChange = { textFieldValue = it })
}
}