1616
1717package com.facebook.rendercore.text
1818
19+ import android.graphics.Rect
1920import androidx.core.util.component1
2021import androidx.core.util.component2
2122import com.facebook.rendercore.RenderCoreConfig
@@ -36,10 +37,12 @@ fun RichTextPrimitive(
3637 id : Long ,
3738 text : CharSequence ,
3839 style : TextStyle ,
39- usePerformantTruncation : Boolean = RenderCoreConfig .usePerformantTruncation
40+ usePerformantTruncation : Boolean = RenderCoreConfig .usePerformantTruncation,
41+ useTruncationCaching : Boolean = RenderCoreConfig .useTruncationCaching,
4042): Primitive {
4143 return Primitive (
42- layoutBehavior = RichTextLayoutBehavior (text, style, usePerformantTruncation),
44+ layoutBehavior =
45+ RichTextLayoutBehavior (text, style, usePerformantTruncation, useTruncationCaching),
4346 mountBehavior =
4447 MountBehavior (
4548 id = id,
@@ -50,8 +53,8 @@ fun RichTextPrimitive(
5053 ) { c ->
5154 RCTextView (c)
5255 }) {
53- bindWithLayoutData<TextLayout >(Unit ) { content, textLayout ->
54- content.mount(textLayout)
56+ bindWithLayoutData<RichTextLayoutData >(Unit ) { content, layoutData ->
57+ content.mount(layoutData. textLayout)
5558 onUnbind { content.unmount() }
5659 }
5760 })
@@ -60,9 +63,26 @@ fun RichTextPrimitive(
6063private class RichTextLayoutBehavior (
6164 val text : CharSequence ,
6265 val style : TextStyle ,
63- val usePerformantTruncation : Boolean
66+ val usePerformantTruncation : Boolean ,
67+ val useTruncationCaching : Boolean ,
6468) : LayoutBehavior {
6569 override fun LayoutScope.layout (sizeConstraints : SizeConstraints ): PrimitiveLayoutResult {
70+ val previousRichTextLayoutData = previousLayoutData as ? RichTextLayoutData
71+ if (useTruncationCaching &&
72+ previousRichTextLayoutData != null &&
73+ canReuseTextLayout(previousRichTextLayoutData, text, style, sizeConstraints)) {
74+ return PrimitiveLayoutResult (
75+ width = max(previousRichTextLayoutData.size.width(), sizeConstraints.minWidth),
76+ height = max(previousRichTextLayoutData.size.height(), sizeConstraints.minHeight),
77+ // TODO: This should copy the textLayout with new clickable spans
78+ layoutData =
79+ RichTextLayoutData (
80+ previousRichTextLayoutData.textLayout,
81+ previousRichTextLayoutData.size,
82+ style,
83+ sizeConstraints))
84+ }
85+
6686 val (size, textLayout) =
6787 layout(
6888 androidContext,
@@ -71,14 +91,55 @@ private class RichTextLayoutBehavior(
7191 text,
7292 style,
7393 usePerformantTruncation)
74-
7594 return PrimitiveLayoutResult (
7695 width = max(size.width(), sizeConstraints.minWidth),
7796 height = max(size.height(), sizeConstraints.minHeight),
78- layoutData = textLayout)
97+ layoutData = RichTextLayoutData (textLayout, size, style, sizeConstraints))
98+ }
99+
100+ private fun canReuseTextLayout (
101+ previousLayoutData : RichTextLayoutData ,
102+ text : CharSequence ,
103+ style : TextStyle ,
104+ sizeConstraints : SizeConstraints
105+ ): Boolean {
106+ return previousLayoutData.textLayout.isExplicitlyTruncated &&
107+ previousLayoutData.sizeConstraints == sizeConstraints &&
108+ hasEquivalentStyle(style, previousLayoutData.style) &&
109+ hasEquivalentTruncatedText(previousLayoutData.textLayout, text, style)
110+ }
111+
112+ // TODO: this should be moved to TextStyle and emcompass all properties
113+ fun hasEquivalentStyle (style1 : TextStyle , style2 : TextStyle ): Boolean {
114+ return style1.textSize == style2.textSize &&
115+ style1.textColor == style2.textColor &&
116+ style1.textDirection == style2.textDirection &&
117+ style1.textStyle == style2.textStyle &&
118+ style1.truncationStyle == style2.truncationStyle
119+ }
120+
121+ private fun hasEquivalentTruncatedText (
122+ textLayout : TextLayout ,
123+ text : CharSequence ,
124+ style : TextStyle
125+ ): Boolean {
126+ if (! textLayout.isExplicitlyTruncated) {
127+ return false
128+ }
129+ val textLayoutText = textLayout.layout.text
130+ val suffix = style.customEllipsisText ? : " "
131+ return textLayoutText.endsWith(suffix) &&
132+ text.startsWith(textLayoutText.subSequence(0 , textLayoutText.length - suffix.length))
79133 }
80134}
81135
136+ private class RichTextLayoutData (
137+ val textLayout : TextLayout ,
138+ val size : Rect ,
139+ val style : TextStyle ,
140+ val sizeConstraints : SizeConstraints
141+ )
142+
82143object RichTextPrimitiveConfig {
83144 val canPreallocation: Boolean = true
84145 val poolSize: Int = 10
0 commit comments