@@ -7,28 +7,26 @@ import android.text.SpannableStringBuilder
77import android.text.TextUtils
88import android.text.style.AbsoluteSizeSpan
99import android.text.style.BackgroundColorSpan
10- import android.text.style.ClickableSpan
1110import android.text.style.ForegroundColorSpan
1211import android.text.style.StrikethroughSpan
1312import android.text.style.StyleSpan
1413import android.text.style.TypefaceSpan
1514import android.text.style.UnderlineSpan
1615import android.view.Gravity
17- import android.text.method.ArrowKeyMovementMethod
18- import android.text.method.LinkMovementMethod
1916import androidx.appcompat.widget.AppCompatTextView
2017import com.facebook.react.uimanager.PixelUtil
2118import com.margelo.nitro.nitrotext.*
2219import androidx.core.graphics.toColorInt
23- import com.nitrotext.renderers.NitroHtmlRenderer
2420import com.nitrotext.spans.NitroLineHeightSpan
21+ import com.nitrotext.spans.UrlSpanNoUnderline
22+ import android.text.method.LinkMovementMethod
23+ import android.text.method.ArrowKeyMovementMethod
24+ import com.nitrotext.HtmlLinkMovementMethod
2525
2626class NitroTextImpl (private val view : AppCompatTextView ) {
2727 // Stored props
2828 private var fragments: Array <Fragment >? = null
2929 private var text: String? = null
30- private var renderer: NitroRenderer = NitroRenderer .PLAINTEXT
31- private var richTextStyleRules: Array <RichTextStyleRule >? = null
3230
3331 private var selectable: Boolean? = null
3432 private var selectionColor: String? = null
@@ -63,25 +61,23 @@ class NitroTextImpl(private val view: AppCompatTextView) {
6361 applyAlignment()
6462
6563 val frags = fragments
66- val content = text
67-
68- if (renderer == NitroRenderer .HTML && ! content.isNullOrEmpty()) {
69- applyHtml(content)
70- } else if (! frags.isNullOrEmpty()) {
64+
65+ if (! frags.isNullOrEmpty()) {
7166 applyFragments(frags)
7267 } else {
7368 applySimpleText()
7469 }
7570
7671 applyLetterSpacing()
72+
73+ // Request layout update so container can resize based on content
74+ // This will trigger onMeasure which will calculate the correct height
75+ view.requestLayout()
7776 }
7877
7978 // Setters
8079 fun setFragments (value : Array <Fragment >? ) { fragments = value }
8180 fun setText (value : String? ) { text = value }
82- fun setRenderer (value : NitroRenderer ? ) { renderer = value ? : NitroRenderer .PLAINTEXT }
83- fun setRichTextStyleRules (value : Array <RichTextStyleRule >? ) { richTextStyleRules = value }
84-
8581 fun setSelectable (value : Boolean? ) { selectable = value }
8682 fun setSelectionColor (value : String? ) { selectionColor = value }
8783 fun setNumberOfLines (value : Double? ) { numberOfLines = value }
@@ -190,6 +186,7 @@ class NitroTextImpl(private val view: AppCompatTextView) {
190186 private fun applyFragments (fragments : Array <Fragment >) {
191187 val builder = SpannableStringBuilder ()
192188 val containerLineHeightPx = resolveLineHeight(lineHeight)
189+ var hasLinks = false
193190 var start: Int
194191 for (frag in fragments) {
195192 val fragText = transformText(frag.text, frag.textTransform) ? : " "
@@ -222,12 +219,36 @@ class NitroTextImpl(private val view: AppCompatTextView) {
222219 frag.lineHeight?.let { lh ->
223220 resolveLineHeight(lh)?.let { applyLineHeightSpan(builder, start, end, it) }
224221 }
222+ // Links
223+ frag.linkUrl?.let { url ->
224+ if (url.isNotEmpty()) {
225+ builder.setSpan(UrlSpanNoUnderline (url), start, end, Spannable .SPAN_EXCLUSIVE_EXCLUSIVE )
226+ hasLinks = true
227+ // Apply default link color if fragment doesn't have explicit color
228+ if (frag.fontColor == null ) {
229+ // Use system link color (typically blue)
230+ val linkColor = android.graphics.Color .parseColor(" #007AFF" ) // iOS-like blue
231+ builder.setSpan(ForegroundColorSpan (linkColor), start, end, Spannable .SPAN_EXCLUSIVE_EXCLUSIVE )
232+ }
233+ }
234+ }
225235 }
226236 containerLineHeightPx?.let { applyLineHeightSpan(builder, 0 , builder.length, it) }
227237 view.text = builder
228238
229239 // Apply default text color for runs without explicit color
230240 view.setTextColor(resolvedFontColor())
241+
242+ // Set up movement method for links
243+ val isSelectable = selectable == true
244+ view.linksClickable = hasLinks
245+ view.movementMethod = when {
246+ hasLinks && isSelectable -> LinkMovementMethod .getInstance()
247+ hasLinks -> HtmlLinkMovementMethod
248+ isSelectable -> ArrowKeyMovementMethod .getInstance()
249+ else -> null
250+ }
251+ view.setTextIsSelectable(isSelectable)
231252 }
232253
233254 private fun applyDecorationSpans (builder : SpannableStringBuilder , start : Int , end : Int , line : TextDecorationLine ? ) {
@@ -278,50 +299,6 @@ class NitroTextImpl(private val view: AppCompatTextView) {
278299 return parsed ? : Color .BLACK
279300 }
280301
281- private fun baseRichTextStyle (): RichTextStyle {
282- return RichTextStyle (
283- fontColor = fontColor,
284- fragmentBackgroundColor = fragmentBackgroundColor,
285- fontSize = fontSize,
286- fontWeight = fontWeight,
287- fontStyle = fontStyle,
288- fontFamily = fontFamily,
289- lineHeight = lineHeight,
290- letterSpacing = letterSpacing,
291- textAlign = textAlign,
292- textTransform = textTransform,
293- textDecorationLine = textDecorationLine,
294- textDecorationColor = textDecorationColor,
295- textDecorationStyle = textDecorationStyle,
296- marginTop = null ,
297- marginBottom = null ,
298- marginLeft = null ,
299- marginRight = null ,
300- )
301- }
302-
303- private fun applyHtml (html : String ) {
304- val renderer = NitroHtmlRenderer (
305- context = view.context,
306- defaultTextSizePx = view.textSize,
307- allowFontScaling = allowFontScaling,
308- maxFontSizeMultiplier = maxFontSizeMultiplier,
309- )
310- val spannable = renderer.render(html, baseRichTextStyle(), richTextStyleRules)
311- trimTrailingNewlines(spannable)
312- view.text = spannable
313- val hasLinks = spannable.getSpans(0 , spannable.length, ClickableSpan ::class .java).isNotEmpty()
314- view.linksClickable = hasLinks
315- val isSelectable = selectable == true
316- view.movementMethod = when {
317- hasLinks && isSelectable -> LinkMovementMethod .getInstance()
318- hasLinks -> HtmlLinkMovementMethod
319- isSelectable -> ArrowKeyMovementMethod .getInstance()
320- else -> null
321- }
322- view.setTextIsSelectable(isSelectable)
323- view.setTextColor(resolvedFontColor())
324- }
325302
326303 private fun trimTrailingNewlines (builder : SpannableStringBuilder ) {
327304 var length = builder.length
0 commit comments