[markdown][compose] IJPL-175966 Provide smoother animation

GitOrigin-RevId: 12880ed9794077b5f6ad185fe38c25113f1d53a2
This commit is contained in:
Alexander Kuznetsov
2025-01-31 16:49:49 +00:00
committed by intellij-monorepo-bot
parent 0458e409ee
commit 22d7628468
4 changed files with 17 additions and 7 deletions
@@ -581,6 +581,7 @@ a:org.jetbrains.jewel.markdown.scrolling.ScrollingSynchronizer
- pa:afterProcessing():V
- pa:beforeProcessing():V
- f:process(kotlin.jvm.functions.Function0):java.lang.Object
- a:scrollToLine(I,kotlin.coroutines.Continuation):java.lang.Object
- a:scrollToLine(I,androidx.compose.animation.core.AnimationSpec,kotlin.coroutines.Continuation):java.lang.Object
- bs:scrollToLine$default(org.jetbrains.jewel.markdown.scrolling.ScrollingSynchronizer,I,androidx.compose.animation.core.AnimationSpec,kotlin.coroutines.Continuation,I,java.lang.Object):java.lang.Object
f:org.jetbrains.jewel.markdown.scrolling.ScrollingSynchronizer$Companion
- f:create(androidx.compose.foundation.gestures.ScrollableState):org.jetbrains.jewel.markdown.scrolling.ScrollingSynchronizer
@@ -2,6 +2,8 @@
// Apache 2.0 license.
package org.jetbrains.jewel.markdown.scrolling
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.SpringSpec
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.gestures.ScrollableState
import androidx.compose.foundation.lazy.LazyListState
@@ -69,7 +71,7 @@ import org.jetbrains.jewel.markdown.processing.MarkdownProcessor
@ExperimentalJewelApi
public abstract class ScrollingSynchronizer {
/** Scroll the preview to the position that match the given [sourceLine] the best. */
public abstract suspend fun scrollToLine(sourceLine: Int)
public abstract suspend fun scrollToLine(sourceLine: Int, animationSpec: AnimationSpec<Float> = SpringSpec())
/**
* Called when [MarkdownProcessor] processes the raw markdown text. The processing itself is passed as an [action].
@@ -146,7 +148,7 @@ public abstract class ScrollingSynchronizer {
// so this map always keeps relevant information.
private val blocks2TextOffsets = mutableMapOf<MarkdownBlock, List<Int>>()
override suspend fun scrollToLine(sourceLine: Int) {
override suspend fun scrollToLine(sourceLine: Int, animationSpec: AnimationSpec<Float>) {
val block = findBestBlockForLine(sourceLine) ?: return
val y = blocks2Top[block] ?: return
if (y < 0) return
@@ -156,7 +158,7 @@ public abstract class ScrollingSynchronizer {
// in this case scroll to the first line of the first block positioned after the line
val lineIndexInBlock = maxOf(0, sourceLine - lineRange.start)
val lineOffset = textOffsets?.get(lineIndexInBlock) ?: 0
scrollState.animateScrollTo(y + lineOffset)
scrollState.animateScrollTo(y + lineOffset, animationSpec)
}
private fun findBestBlockForLine(line: Int): MarkdownBlock? {
@@ -21,6 +21,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import java.util.Arrays
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -59,7 +60,6 @@ import org.jetbrains.jewel.ui.component.styling.TrackClickBehavior
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import java.util.Arrays
@Suppress("LargeClass")
class ScrollingSynchronizerTest {
@@ -1,6 +1,9 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.markdown.compose.preview
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.TweenSpec
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
@@ -95,7 +98,11 @@ internal class MarkdownComposePanel(
@OptIn(FlowPreview::class)
@Suppress("FunctionName")
@Composable
private fun MarkdownPreviewPanel(scrollState: ScrollState, scrollingSynchronizer: ScrollingSynchronizer?, blockRenderer: ScrollSyncMarkdownBlockRenderer) {
private fun MarkdownPreviewPanel(scrollState: ScrollState,
scrollingSynchronizer: ScrollingSynchronizer?,
blockRenderer: ScrollSyncMarkdownBlockRenderer,
animationSpec: AnimationSpec<Float> = TweenSpec(easing = LinearEasing)
) {
val request by updateHandler.requests.collectAsState(null)
(request as? PreviewRequest.Update)?.let {
if (scrollingSynchronizer != null) {
@@ -103,7 +110,7 @@ internal class MarkdownComposePanel(
LaunchedEffect(Unit) {
coroutineScope.launch {
scrollToLineFlow.debounce(16.milliseconds).collect { scrollToLine ->
scrollingSynchronizer.scrollToLine(scrollToLine)
scrollingSynchronizer.scrollToLine(scrollToLine, animationSpec)
}
}
}