From 22d7628468beac6c514645d7ab7497c52ecce5aa Mon Sep 17 00:00:00 2001 From: Alexander Kuznetsov Date: Fri, 31 Jan 2025 14:03:38 +0100 Subject: [PATCH] [markdown][compose] IJPL-175966 Provide smoother animation GitOrigin-RevId: 12880ed9794077b5f6ad185fe38c25113f1d53a2 --- platform/jewel/markdown/core/api-dump-unreviewed.txt | 3 ++- .../jewel/markdown/scrolling/ScrollingSynchronizer.kt | 8 +++++--- .../markdown/scrolling/ScrollingSynchronizerTest.kt | 2 +- .../markdown/compose/preview/MarkdownComposePanel.kt | 11 +++++++++-- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/platform/jewel/markdown/core/api-dump-unreviewed.txt b/platform/jewel/markdown/core/api-dump-unreviewed.txt index c4d26f118a41..5130d60e19d5 100644 --- a/platform/jewel/markdown/core/api-dump-unreviewed.txt +++ b/platform/jewel/markdown/core/api-dump-unreviewed.txt @@ -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 diff --git a/platform/jewel/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/scrolling/ScrollingSynchronizer.kt b/platform/jewel/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/scrolling/ScrollingSynchronizer.kt index 5bf2c1771095..b80a0fbdcdf6 100644 --- a/platform/jewel/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/scrolling/ScrollingSynchronizer.kt +++ b/platform/jewel/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/scrolling/ScrollingSynchronizer.kt @@ -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 = 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>() - override suspend fun scrollToLine(sourceLine: Int) { + override suspend fun scrollToLine(sourceLine: Int, animationSpec: AnimationSpec) { 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? { diff --git a/platform/jewel/markdown/core/src/test/kotlin/org/jetbrains/jewel/markdown/scrolling/ScrollingSynchronizerTest.kt b/platform/jewel/markdown/core/src/test/kotlin/org/jetbrains/jewel/markdown/scrolling/ScrollingSynchronizerTest.kt index 05f6a8c683cb..94d1eb08a199 100644 --- a/platform/jewel/markdown/core/src/test/kotlin/org/jetbrains/jewel/markdown/scrolling/ScrollingSynchronizerTest.kt +++ b/platform/jewel/markdown/core/src/test/kotlin/org/jetbrains/jewel/markdown/scrolling/ScrollingSynchronizerTest.kt @@ -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 { diff --git a/plugins/markdown/compose/src/main/kotlin/com/intellij/markdown/compose/preview/MarkdownComposePanel.kt b/plugins/markdown/compose/src/main/kotlin/com/intellij/markdown/compose/preview/MarkdownComposePanel.kt index 2fe60ba75f32..b310c64091fc 100644 --- a/plugins/markdown/compose/src/main/kotlin/com/intellij/markdown/compose/preview/MarkdownComposePanel.kt +++ b/plugins/markdown/compose/src/main/kotlin/com/intellij/markdown/compose/preview/MarkdownComposePanel.kt @@ -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 = 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) } } }