react-native-highlight-text-view 0.1.22 → 0.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -33,7 +33,9 @@ class RoundedBackgroundSpan(
33
33
  internal val backgroundInsetRight: Float,
34
34
  internal val cornerRadius: Float,
35
35
  internal val isLineStart: Boolean = false,
36
- internal val isLineEnd: Boolean = false
36
+ internal val isLineEnd: Boolean = false,
37
+ internal val isFirstLine: Boolean = false,
38
+ internal val isLastLine: Boolean = false
37
39
  ) : ReplacementSpan() {
38
40
 
39
41
  override fun getSize(
@@ -102,9 +104,9 @@ class RoundedBackgroundSpan(
102
104
  0f
103
105
  }
104
106
 
105
- // Vertical overlap to eliminate gaps
106
- val topExtend = 4f
107
- val bottomExtend = 4f
107
+ // Vertical overlap to eliminate gaps (reduced to prevent descender clipping)
108
+ val topExtend = 0f
109
+ val bottomExtend = 0f
108
110
 
109
111
  // Calculate background rect
110
112
  // NOTE: Since this is a ReplacementSpan, 'x' is the start of the span (including padding).
@@ -123,37 +125,37 @@ class RoundedBackgroundSpan(
123
125
  canvas.drawRoundRect(rect, cornerRadius, cornerRadius, bgPaint)
124
126
  }
125
127
  isLineStart -> {
126
- // Line START - round LEFT corners only
128
+ // Line START - round LEFT corners (top and bottom)
127
129
  val path = android.graphics.Path()
128
130
  path.addRoundRect(
129
131
  rect,
130
132
  floatArrayOf(
131
- cornerRadius, cornerRadius, // top-left
132
- 0f, 0f, // top-right
133
- 0f, 0f, // bottom-right
134
- cornerRadius, cornerRadius // bottom-left
133
+ cornerRadius, cornerRadius, // top-left
134
+ 0f, 0f, // top-right
135
+ 0f, 0f, // bottom-right
136
+ cornerRadius, cornerRadius // bottom-left
135
137
  ),
136
138
  android.graphics.Path.Direction.CW
137
139
  )
138
140
  canvas.drawPath(path, bgPaint)
139
141
  }
140
142
  isLineEnd -> {
141
- // Line END - round RIGHT corners only
143
+ // Line END - round RIGHT corners (top and bottom)
142
144
  val path = android.graphics.Path()
143
145
  path.addRoundRect(
144
146
  rect,
145
147
  floatArrayOf(
146
- 0f, 0f, // top-left
147
- cornerRadius, cornerRadius, // top-right
148
- cornerRadius, cornerRadius, // bottom-right
149
- 0f, 0f // bottom-left
148
+ 0f, 0f, // top-left
149
+ cornerRadius, cornerRadius, // top-right
150
+ cornerRadius, cornerRadius, // bottom-right
151
+ 0f, 0f // bottom-left
150
152
  ),
151
153
  android.graphics.Path.Direction.CW
152
154
  )
153
155
  canvas.drawPath(path, bgPaint)
154
156
  }
155
157
  else -> {
156
- // Middle - NO rounded corners (Square)
158
+ // Middle characters - NO rounded corners (square) for smooth edges
157
159
  canvas.drawRect(rect, bgPaint)
158
160
  }
159
161
  }
@@ -486,12 +488,19 @@ class HighlightTextView : AppCompatEditText {
486
488
  var isAtLineStart = i == 0 || hasNewlineBefore
487
489
  var isAtLineEnd = i == textStr.length - 1 || hasNewlineAfter
488
490
 
491
+ // Determine if this is the first or last line
492
+ var isOnFirstLine = i == 0 || hasNewlineBefore
493
+ var isOnLastLine = i == textStr.length - 1 || hasNewlineAfter
494
+
489
495
  // Only use layout for auto-wrapped lines (not manual newlines)
490
496
  if (!hasNewlineBefore && !hasNewlineAfter && layout != null && i < textStr.length) {
491
497
  try {
492
498
  val line = layout.getLineForOffset(i)
493
499
  val lineStart = layout.getLineStart(line)
494
500
  val lineEnd = layout.getLineEnd(line)
501
+ // Check if this is the first or last line
502
+ isOnFirstLine = line == 0
503
+ isOnLastLine = line == layout.lineCount - 1
495
504
  // Only override if this is an auto-wrapped boundary
496
505
  if (i == lineStart && textStr.getOrNull(i - 1) != '\n') {
497
506
  isAtLineStart = true
@@ -517,7 +526,9 @@ class HighlightTextView : AppCompatEditText {
517
526
  backgroundInsetRight,
518
527
  radius,
519
528
  isAtLineStart,
520
- isAtLineEnd
529
+ isAtLineEnd,
530
+ isOnFirstLine,
531
+ isOnLastLine
521
532
  )
522
533
  editable.setSpan(span, i, i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
523
534
  }
@@ -547,10 +558,14 @@ class HighlightTextView : AppCompatEditText {
547
558
  val textStr = editable.toString()
548
559
  if (textStr.isEmpty()) return
549
560
 
550
- // Apply line height if specified
561
+ // Apply line height if specified, or add spacing for padding
551
562
  if (customLineHeight > 0) {
552
563
  val lineSpacingMultiplier = customLineHeight / textSize
553
564
  setLineSpacing(0f, lineSpacingMultiplier)
565
+ } else {
566
+ // Add line spacing to accommodate vertical padding and prevent overlap
567
+ val extraSpacing = charPaddingTop + charPaddingBottom
568
+ setLineSpacing(extraSpacing, 1.0f)
554
569
  }
555
570
 
556
571
  isUpdatingText = true
@@ -586,12 +601,19 @@ class HighlightTextView : AppCompatEditText {
586
601
  var isAtLineStart = i == 0 || hasNewlineBefore
587
602
  var isAtLineEnd = i == textStr.length - 1 || hasNewlineAfter
588
603
 
604
+ // Determine if this is the first or last line
605
+ var isOnFirstLine = i == 0 || hasNewlineBefore
606
+ var isOnLastLine = i == textStr.length - 1 || hasNewlineAfter
607
+
589
608
  // Only use layout for auto-wrapped lines (not manual newlines)
590
609
  if (!hasNewlineBefore && !hasNewlineAfter && layoutObj != null && i < textStr.length) {
591
610
  try {
592
611
  val line = layoutObj.getLineForOffset(i)
593
612
  val lineStart = layoutObj.getLineStart(line)
594
613
  val lineEnd = layoutObj.getLineEnd(line)
614
+ // Check if this is the first or last line
615
+ isOnFirstLine = line == 0
616
+ isOnLastLine = line == layoutObj.lineCount - 1
595
617
  // Only override if this is an auto-wrapped boundary
596
618
  if (i == lineStart && textStr.getOrNull(i - 1) != '\n') {
597
619
  isAtLineStart = true
@@ -617,13 +639,23 @@ class HighlightTextView : AppCompatEditText {
617
639
  backgroundInsetRight,
618
640
  radius,
619
641
  isAtLineStart,
620
- isAtLineEnd
642
+ isAtLineEnd,
643
+ isOnFirstLine,
644
+ isOnLastLine
621
645
  )
622
646
  editable.setSpan(span, i, i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
623
647
  }
624
648
  }
625
649
 
626
650
  isUpdatingText = false
651
+
652
+ // Schedule a post-layout update to ensure corner rounding is correct
653
+ // This is needed because layout might not be ready when this method is called
654
+ post {
655
+ if (!isUpdatingText) {
656
+ updateAutoWrappedLineBoundaries()
657
+ }
658
+ }
627
659
  }
628
660
 
629
661
  /**
@@ -664,12 +696,17 @@ class HighlightTextView : AppCompatEditText {
664
696
  val isAtLineStart = spanStart == lineStart
665
697
  val isAtLineEnd = spanEnd == lineEnd
666
698
 
699
+ // Check if this is the first or last line
700
+ val isOnFirstLine = line == 0
701
+ val isOnLastLine = line == layout.lineCount - 1
702
+
667
703
  // Only update if this is an auto-wrapped line boundary (not a newline boundary)
668
704
  val isNewlineBoundary = (spanStart > 0 && textStr[spanStart - 1] == '\n') ||
669
705
  (spanEnd < textStr.length && textStr[spanEnd] == '\n')
670
706
 
671
707
  // Only recreate span if it's at an auto-wrapped boundary and flags are wrong
672
- if (!isNewlineBoundary && (isAtLineStart != span.isLineStart || isAtLineEnd != span.isLineEnd)) {
708
+ if (!isNewlineBoundary && (isAtLineStart != span.isLineStart || isAtLineEnd != span.isLineEnd ||
709
+ isOnFirstLine != span.isFirstLine || isOnLastLine != span.isLastLine)) {
673
710
  val newSpan = RoundedBackgroundSpan(
674
711
  span.backgroundColor,
675
712
  span.textColor,
@@ -683,7 +720,9 @@ class HighlightTextView : AppCompatEditText {
683
720
  span.backgroundInsetRight,
684
721
  span.cornerRadius,
685
722
  isAtLineStart,
686
- isAtLineEnd
723
+ isAtLineEnd,
724
+ isOnFirstLine,
725
+ isOnLastLine
687
726
  )
688
727
  editable.removeSpan(span)
689
728
  editable.setSpan(newSpan, spanStart, spanEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-highlight-text-view",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "description": "A native text input for React Native that supports inline text highlighting",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",