Skip to content

Commit 64936dc

Browse files
try 2
1 parent 6af5d8e commit 64936dc

File tree

1 file changed

+32
-36
lines changed
  • richtext-ui/src/commonMain/kotlin/com/halilibo/richtext/ui/string

1 file changed

+32
-36
lines changed

richtext-ui/src/commonMain/kotlin/com/halilibo/richtext/ui/string/Text.kt

Lines changed: 32 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import androidx.compose.ui.graphics.StrokeCap
2828
import androidx.compose.ui.graphics.isSpecified
2929
import androidx.compose.ui.layout.layout
3030
import androidx.compose.ui.text.AnnotatedString
31-
import androidx.compose.ui.text.LinkAnnotation
3231
import androidx.compose.ui.text.SpanStyle
3332
import androidx.compose.ui.text.TextLayoutResult
3433
import androidx.compose.ui.text.buildAnnotatedString
@@ -116,7 +115,7 @@ public fun RichTextScope.Text(
116115
val inlineContents = decoratedTextResult.inlineContents
117116
val decoratedLinkRanges = decoratedTextResult.decoratedLinkRanges
118117
var textLayoutResult by remember { mutableStateOf<TextLayoutResult?>(null) }
119-
val animatedText = if (renderOptions.animate && inlineContents.isEmpty()) {
118+
val animatedTextResult = if (renderOptions.animate && inlineContents.isEmpty()) {
120119
rememberAnimatedText(
121120
annotated = decoratedTextResult.annotatedString,
122121
contentColor = contentColor,
@@ -125,44 +124,43 @@ public fun RichTextScope.Text(
125124
sharedAnimationState = sharedAnimationState,
126125
)
127126
} else {
128-
decoratedTextResult.annotatedString
127+
remember(decoratedTextResult.annotatedString) {
128+
AnimatedTextResult(
129+
text = decoratedTextResult.annotatedString,
130+
activeAnimations = emptyList(),
131+
)
132+
}
129133
}
130-
val isPartialText = animatedText.text.length < decoratedTextResult.annotatedString.text.length
131-
val underlineSpecs = remember(
132-
decoratedLinkRanges,
133-
resolvedStyle,
134-
contentColor,
135-
animatedText,
136-
isPartialText,
137-
) {
134+
val animatedText = animatedTextResult.text
135+
val underlineSpecs = remember(decoratedLinkRanges, resolvedStyle, contentColor) {
138136
decoratedLinkRanges.mapNotNull { range ->
139-
if (isPartialText && range.end > animatedText.text.length) return@mapNotNull null
140137
val linkStyle = range.linkStyleOverride
141138
?.invoke(resolvedStyle.linkStyle)
142139
?: resolvedStyle.linkStyle
143140
val underlineColor = range.underlineColor
144141
?: linkStyle?.style?.color
145142
?.takeIf { it.isSpecified }
146143
?: contentColor
147-
val textLength = animatedText.text.length
148-
val clampedStart = range.start.coerceIn(0, textLength)
149-
val clampedEnd = range.end.coerceIn(0, textLength)
150-
if (clampedStart >= clampedEnd) return@mapNotNull null
151-
val hasLinkAnnotation = animatedText
152-
.getLinkAnnotations(clampedStart, clampedEnd)
153-
.isNotEmpty()
154-
if (!hasLinkAnnotation) return@mapNotNull null
155144
UnderlineSpec(
156145
range = range,
157146
color = underlineColor,
158147
)
159148
}
160149
}
161150
val underlineModifier = if (underlineSpecs.isNotEmpty()) {
151+
val activeAnimations = animatedTextResult.activeAnimations
162152
Modifier.drawWithContent {
163153
drawContent()
164154
val layoutResult = textLayoutResult ?: return@drawWithContent
165155
underlineSpecs.fastForEach { spec ->
156+
if (activeAnimations.any { animation ->
157+
animation.startIndex >= spec.range.start &&
158+
animation.startIndex < spec.range.end &&
159+
animation.alpha < 1f
160+
}
161+
) {
162+
return@fastForEach
163+
}
166164
drawUnderline(
167165
layoutResult = layoutResult,
168166
start = spec.range.start,
@@ -319,14 +317,19 @@ public class MarkdownAnimationState {
319317
(lastAnimationStartMs - System.currentTimeMillis()).coerceAtLeast(0).toInt()
320318
}
321319

320+
private data class AnimatedTextResult(
321+
val text: AnnotatedString,
322+
val activeAnimations: Collection<TextAnimation>,
323+
)
324+
322325
@Composable
323326
private fun rememberAnimatedText(
324327
annotated: AnnotatedString,
325328
renderOptions: RichTextRenderOptions,
326329
contentColor: Color,
327330
sharedAnimationState: MarkdownAnimationState,
328331
isLeafText: Boolean,
329-
): AnnotatedString {
332+
): AnimatedTextResult {
330333
val coroutineScope = rememberCoroutineScope()
331334
val animations = remember { mutableStateMapOf<Int, TextAnimation>() }
332335
val textToRender = remember { mutableStateOf(AnnotatedString("")) }
@@ -398,10 +401,13 @@ private fun rememberAnimatedText(
398401
// the text will just be re-drawn, since the animated alpha state was read only inside
399402
// DynamicSolidColor during the draw phase.
400403
derivedStateOf {
401-
textToRender.value.withDynamicColorPhrases(
402-
contentColor = contentColor,
403-
animations = animations.values,
404-
onlyVisible = renderOptions.onlyRenderVisibleText,
404+
AnimatedTextResult(
405+
text = textToRender.value.withDynamicColorPhrases(
406+
contentColor = contentColor,
407+
animations = animations.values,
408+
onlyVisible = renderOptions.onlyRenderVisibleText,
409+
),
410+
activeAnimations = animations.values.toList(),
405411
)
406412
}
407413
}.value
@@ -461,17 +467,7 @@ private fun AnnotatedString.withDynamicColor(color: Color, alpha: () -> Float):
461467
start = 0,
462468
end = length
463469
)
464-
val builder = AnnotatedString.Builder(text)
465-
subStyles.fastForEach { builder.addStyle(it.item, it.start, it.end) }
466-
builder.addStyle(fullStyle.item, fullStyle.start, fullStyle.end)
467-
paragraphStyles.fastForEach { builder.addStyle(it.item, it.start, it.end) }
468-
getLinkAnnotations(0, length).fastForEach { annotation ->
469-
when (val link = annotation.item) {
470-
is LinkAnnotation.Url -> builder.addLink(link, annotation.start, annotation.end)
471-
is LinkAnnotation.Clickable -> builder.addLink(link, annotation.start, annotation.end)
472-
}
473-
}
474-
return builder.toAnnotatedString()
470+
return AnnotatedString(text, subStyles + fullStyle)
475471
}
476472

477473
private fun CharSequence.maybeContainsEmojis(): Boolean {

0 commit comments

Comments
 (0)