diff --git a/composeApp/src/commonMain/kotlin/CorrectBracketSequence.kt b/composeApp/src/commonMain/kotlin/CorrectBracketSequence.kt index a485d02..fcaf964 100644 --- a/composeApp/src/commonMain/kotlin/CorrectBracketSequence.kt +++ b/composeApp/src/commonMain/kotlin/CorrectBracketSequence.kt @@ -112,14 +112,14 @@ fun CorrectBracketSequence() { val coroutineScope = rememberCoroutineScope() val externalScrollToFlow = remember { MutableSharedFlow() } val showLineNumbers by remember { mutableStateOf(true) } - val pinLines by remember { mutableStateOf(true) } + val stickyHeader by remember { mutableStateOf(true) } val showIndentation by remember { mutableStateOf(true) } val textSize = measureText(textStyle) val density = LocalDensity.current - val pinLinesChooser: (Bracket) -> IntRange? = { bracket -> + val stickyHeaderLinesChooser: (Bracket) -> IntRange? = { bracket -> if (bracket.bracket in "{}") codeTextFieldState.tokenLines[bracket] else null } - var maximumPinnedLinesHeight: Dp by remember { mutableStateOf(0.dp) } + var maximumStickyHeaderHeight: Dp by remember { mutableStateOf(0.dp) } val lineNumbersColor = Color.DarkGray BasicSourceCodeTextField( @@ -140,8 +140,8 @@ fun CorrectBracketSequence() { manualScrollToPosition = externalScrollToFlow, additionalOuterComposable = { _, inner -> inner() - AnimatedVisibility(pinLines) { - PinnedLines( + AnimatedVisibility(stickyHeader) { + StickyHeader( state = codeTextFieldState, textStyle = textStyle, lineNumbersColor = lineNumbersColor, @@ -149,8 +149,8 @@ fun CorrectBracketSequence() { scrollState = verticalState, showLineNumbers = showLineNumbers, matchedBrackets = matchedBrackets, - pinLinesChooser = pinLinesChooser, - maximumPinnedLinesHeight = (maxHeight / 3).also { maximumPinnedLinesHeight = it }, + stickyHeaderLinesChooser = stickyHeaderLinesChooser, + maximumStickyHeaderHeight = (maxHeight / 3).also { maximumStickyHeaderHeight = it }, onClick = { coroutineScope.launch { externalScrollToFlow.emit(SourceCodePosition(it, 0)) } }, divider = { HorizontalDivider(thickness = 1.dp) }, additionalInnerComposable = { linesToWrite, _ -> @@ -180,8 +180,8 @@ fun CorrectBracketSequence() { state = codeTextFieldState, matchedBrackets = matchedBrackets, dividerThickness = 0.dp, // do not include divider thickness in the calculation - maximumPinnedLinesHeight = maximumPinnedLinesHeight, - pinLinesChooser = pinLinesChooser, + maximumStickyHeaderHeight = maximumStickyHeaderHeight, + stickyHeaderLinesChooser = stickyHeaderLinesChooser, ) ) }, diff --git a/editor/src/commonMain/kotlin/editor/basic/Decorators.kt b/editor/src/commonMain/kotlin/editor/basic/Decorators.kt index 9d0eee0..03e3d38 100644 --- a/editor/src/commonMain/kotlin/editor/basic/Decorators.kt +++ b/editor/src/commonMain/kotlin/editor/basic/Decorators.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Dp @@ -74,11 +75,11 @@ public fun BoxScope.IndentationLines( } @PublishedApi -internal inline fun getPinnedLines( +internal inline fun getStickyHeaderLines( line: Int, state: BasicSourceCodeTextFieldState, matchedBrackets: Map, - crossinline pinLinesChooser: (Bracket) -> IntRange? = { bracket -> state.tokenLines[bracket as T] }, + crossinline stickyHeaderLinesChooser: (Bracket) -> IntRange? = { bracket -> state.tokenLines[bracket as T] }, ): Set { val lineUsages = IntArray(state.offsets.size) var topLine = line @@ -125,7 +126,7 @@ internal inline fun getPinnedL } } return openedBracketLines.flatMapTo(mutableSetOf()) { bracket -> - pinLinesChooser(bracket) ?: IntRange.EMPTY + stickyHeaderLinesChooser(bracket) ?: IntRange.EMPTY }.sorted().toSet() } @@ -136,19 +137,19 @@ public inline fun getOffsetFor state: BasicSourceCodeTextFieldState, matchedBrackets: Map, dividerThickness: Dp, - maximumPinnedLinesHeight: Dp, - crossinline pinLinesChooser: (Bracket) -> IntRange? = { bracket -> state.tokenLines[bracket as T] } + maximumStickyHeaderHeight: Dp, + crossinline stickyHeaderLinesChooser: (Bracket) -> IntRange? = { bracket -> state.tokenLines[bracket as T] } ): Int { val resultLine = (line downTo 0).firstOrNull { attemptLine -> - val height = getPinnedLinesHeight( + val height = getStickyHeaderHeight( attemptLine, textSize, density, state, matchedBrackets, dividerThickness, - maximumPinnedLinesHeight, - pinLinesChooser + maximumStickyHeaderHeight, + stickyHeaderLinesChooser ) (line - attemptLine) * textSize.height >= height } ?: 0 @@ -157,25 +158,25 @@ public inline fun getOffsetFor @PublishedApi -internal inline fun getPinnedLinesHeight( +internal inline fun getStickyHeaderHeight( line: Int, textSize: Size, density: Density, state: BasicSourceCodeTextFieldState, matchedBrackets: Map, dividerThickness: Dp, - maximumPinnedLinesHeight: Dp, - crossinline pinLinesChooser: (Bracket) -> IntRange? = { bracket -> state.tokenLines[bracket as T] } + maximumStickyHeaderHeight: Dp, + crossinline stickyHeaderLinesChooser: (Bracket) -> IntRange? = { bracket -> state.tokenLines[bracket as T] } ): Int { - val pinnedLines = getPinnedLines(line, state, matchedBrackets, pinLinesChooser) - if (pinnedLines.isEmpty()) return 0 + val stickyHeaderLines = getStickyHeaderLines(line, state, matchedBrackets, stickyHeaderLinesChooser) + if (stickyHeaderLines.isEmpty()) return 0 return with(density) { - minOf(pinnedLines.size * textSize.height + dividerThickness.toPx(), maximumPinnedLinesHeight.toPx()) + minOf(stickyHeaderLines.size * textSize.height + dividerThickness.toPx(), maximumStickyHeaderHeight.toPx()) }.roundToInt() } @Composable -public inline fun BoxWithConstraintsScope.PinnedLines( +public inline fun BoxWithConstraintsScope.StickyHeader( state: BasicSourceCodeTextFieldState, textStyle: TextStyle, lineNumbersColor: Color, @@ -184,10 +185,11 @@ public inline fun BoxWithConst showLineNumbers: Boolean, matchedBrackets: Map, divider: @Composable () -> Unit, - maximumPinnedLinesHeight: Dp = maxHeight / 3, + maximumStickyHeaderHeight: Dp = maxHeight / 3, lineNumberModifier: Modifier = defaultLineNumberModifier, lineStringModifier: Modifier = Modifier, - crossinline pinLinesChooser: (Bracket) -> IntRange? = { bracket -> state.tokenLines[bracket as T] }, + visualTransformation: VisualTransformation = VisualTransformation.None, + crossinline stickyHeaderLinesChooser: (Bracket) -> IntRange? = { bracket -> state.tokenLines[bracket as T] }, crossinline onClick: (lineNumber: Int) -> Unit = {}, crossinline onHoveredSourceCodePositionChange: (position: SourceCodePosition) -> Unit = {}, crossinline additionalInnerComposable: @Composable BoxWithConstraintsScope.(linesToWrite: Map, inner: @Composable () -> Unit) -> Unit = { _, _ -> }, @@ -196,13 +198,13 @@ public inline fun BoxWithConst val textHeightDp = with(LocalDensity.current) { measuredText.height.toDp() } if (scrollState.value == 0) return val topVisibleRow = (scrollState.value / measuredText.height).toInt() - val requestedLinesSet = getPinnedLines(topVisibleRow, state, matchedBrackets, pinLinesChooser) + val requestedLinesSet = getStickyHeaderLines(topVisibleRow, state, matchedBrackets, stickyHeaderLinesChooser) if (requestedLinesSet.isEmpty()) return Column { - Column(Modifier.heightIn(max = maximumPinnedLinesHeight)) { + Column(Modifier.heightIn(max = maximumStickyHeaderHeight)) { Row( modifier = Modifier - .width(this@PinnedLines.maxWidth) + .width(this@StickyHeader.maxWidth) .verticalScroll(rememberScrollState()) .background(backgroundColor) ) { @@ -210,10 +212,11 @@ public inline fun BoxWithConst val linesToWrite = requestedLinesSet.associateWith { lineNumber -> val lineOffsets = state.offsets[lineNumber].takeIf { it.isNotEmpty() } ?: return@associateWith AnnotatedString("") + val annotatedString = visualTransformation.filter(state.annotatedString).text val lastOffset = - if (lineOffsets.last() == state.text.lastIndex) state.text.length else lineOffsets.last() + if (lineOffsets.last() == annotatedString.lastIndex) annotatedString.length else lineOffsets.last() buildAnnotatedString { - append(state.annotatedString, lineOffsets.first(), lastOffset) + append(annotatedString, lineOffsets.first(), lastOffset) } } AnimatedVisibility(showLineNumbers) { diff --git a/gradle.properties b/gradle.properties index dc9ad68..20b42f4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,4 +20,4 @@ development=true #Publication group=com.zhelenskiy -version=0.0.5 +version=0.0.7