react-native-highlight-text-view 0.1.32 → 0.1.33

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.
@@ -14,6 +14,7 @@ import android.util.TypedValue
14
14
  import android.view.Gravity
15
15
  import androidx.appcompat.widget.AppCompatEditText
16
16
  import com.facebook.react.common.assets.ReactFontManager
17
+ import kotlin.math.abs
17
18
 
18
19
  /**
19
20
  * Custom EditText that mimics the iOS implementation by drawing per-character
@@ -141,6 +142,12 @@ class HighlightTextView : AppCompatEditText {
141
142
  val paint = paint
142
143
  val radius = if (highlightBorderRadius > 0f) highlightBorderRadius else cornerRadius
143
144
 
145
+ // Precompute horizontal alignment flags once per draw pass
146
+ val horizontalGravity = gravity and Gravity.HORIZONTAL_GRAVITY_MASK
147
+ val isLeftAlignedView = horizontalGravity == Gravity.START || horizontalGravity == Gravity.LEFT
148
+ val isRightAlignedView = horizontalGravity == Gravity.END || horizontalGravity == Gravity.RIGHT
149
+ val isCenterAlignedView = horizontalGravity == Gravity.CENTER_HORIZONTAL
150
+
144
151
  val length = text.length
145
152
  if (length == 0) return
146
153
 
@@ -194,14 +201,22 @@ class HighlightTextView : AppCompatEditText {
194
201
  xStart + paint.measureText(text, i, i + 1)
195
202
  }
196
203
 
197
- // Vertical bounds based on line box (includes line spacing)
198
- val lineTop = layout.getLineTop(line).toFloat()
199
- val lineBottom = layout.getLineBottom(line).toFloat()
204
+ // Vertical bounds based on font metrics around the baseline, so
205
+ // they are independent from Android's line spacing mechanics.
206
+ val baseline = layout.getLineBaseline(line).toFloat()
207
+ val fm = paint.fontMetrics
200
208
 
201
209
  var left = xStart
202
210
  var right = xEnd
203
- var top = lineTop
204
- var bottom = lineBottom
211
+ var top = baseline + fm.ascent
212
+ var bottom = baseline + fm.descent
213
+
214
+ // For right-aligned text, ensure the outermost character on each line
215
+ // snaps to the line's visual right edge so the highlight's right side
216
+ // forms a clean vertical column across wrapped lines.
217
+ if (isRightAlignedView && !hasRightNeighbor) {
218
+ right = layout.getLineRight(line)
219
+ }
205
220
 
206
221
  // First shrink by background insets (from the line box)
207
222
  top += backgroundInsetTop
@@ -215,25 +230,6 @@ class HighlightTextView : AppCompatEditText {
215
230
  top -= charPaddingTop
216
231
  bottom += charPaddingBottom
217
232
 
218
- if (customLineSpacing < 0f) {
219
- val originalLineTop = layout.getLineTop(line).toFloat()
220
- val originalLineBottom = layout.getLineBottom(line).toFloat()
221
-
222
- if (line > 0 && top < originalLineTop) {
223
- val prevLineBottom = layout.getLineBottom(line - 1).toFloat()
224
- if (top < prevLineBottom) {
225
- top = prevLineBottom
226
- }
227
- }
228
-
229
- if (line < layout.lineCount - 1 && bottom > originalLineBottom) {
230
- val nextLineTop = layout.getLineTop(line + 1).toFloat()
231
- if (bottom > nextLineTop) {
232
- bottom = nextLineTop
233
- }
234
- }
235
- }
236
-
237
233
  if (right <= left || bottom <= top) continue
238
234
 
239
235
  backgroundRect.set(left, top, right, bottom)
@@ -242,11 +238,10 @@ class HighlightTextView : AppCompatEditText {
242
238
  val isFirstLineOfParagraph = line == 0 || isLineEmpty(text, layout, line - 1)
243
239
  val isLastLineOfParagraph = line == layout.lineCount - 1 || isLineEmpty(text, layout, line + 1)
244
240
 
245
- // Detect text alignment from view's gravity
246
- val horizontalGravity = gravity and Gravity.HORIZONTAL_GRAVITY_MASK
247
- val isLeftAligned = horizontalGravity == Gravity.START || horizontalGravity == Gravity.LEFT
248
- val isRightAligned = horizontalGravity == Gravity.END || horizontalGravity == Gravity.RIGHT
249
- val isCenterAligned = horizontalGravity == Gravity.CENTER_HORIZONTAL
241
+ // Use precomputed text alignment flags
242
+ val isLeftAligned = isLeftAlignedView
243
+ val isRightAligned = isRightAlignedView
244
+ val isCenterAligned = isCenterAlignedView
250
245
 
251
246
  var tl = 0f
252
247
  var tr = 0f
@@ -273,8 +268,10 @@ class HighlightTextView : AppCompatEditText {
273
268
  tr = radius
274
269
  } else {
275
270
  val prevLineWidth = layout.getLineMax(line - 1)
276
- // Round Top-Right if we stick out further than the line above
277
- tr = if (currentLineWidth > prevLineWidth) radius else 0f
271
+ // Round Top-Right only if this line extends further than the line above
272
+ tr = if (!lineWidthsEqual(currentLineWidth, prevLineWidth) &&
273
+ currentLineWidth > prevLineWidth
274
+ ) radius else 0f
278
275
  }
279
276
 
280
277
  // Bottom-Right
@@ -282,8 +279,10 @@ class HighlightTextView : AppCompatEditText {
282
279
  br = radius
283
280
  } else {
284
281
  val nextLineWidth = layout.getLineMax(line + 1)
285
- // Round Bottom-Right if we overhang the line below
286
- br = if (currentLineWidth > nextLineWidth) radius else 0f
282
+ // Round Bottom-Right only if this line extends further than the line below
283
+ br = if (!lineWidthsEqual(currentLineWidth, nextLineWidth) &&
284
+ currentLineWidth > nextLineWidth
285
+ ) radius else 0f
287
286
  }
288
287
  }
289
288
  }
@@ -307,8 +306,10 @@ class HighlightTextView : AppCompatEditText {
307
306
  tl = radius
308
307
  } else {
309
308
  val prevLineWidth = layout.getLineMax(line - 1)
310
- // Round Top-Left if we stick out further than the line above
311
- tl = if (currentLineWidth > prevLineWidth) radius else 0f
309
+ // Round Top-Left only if this line extends further than the line above
310
+ tl = if (!lineWidthsEqual(currentLineWidth, prevLineWidth) &&
311
+ currentLineWidth > prevLineWidth
312
+ ) radius else 0f
312
313
  }
313
314
 
314
315
  // Bottom-Left
@@ -316,8 +317,10 @@ class HighlightTextView : AppCompatEditText {
316
317
  bl = radius
317
318
  } else {
318
319
  val nextLineWidth = layout.getLineMax(line + 1)
319
- // Round Bottom-Left if we overhang the line below
320
- bl = if (currentLineWidth > nextLineWidth) radius else 0f
320
+ // Round Bottom-Left only if this line extends further than the line below
321
+ bl = if (!lineWidthsEqual(currentLineWidth, nextLineWidth) &&
322
+ currentLineWidth > nextLineWidth
323
+ ) radius else 0f
321
324
  }
322
325
  }
323
326
  }
@@ -333,8 +336,10 @@ class HighlightTextView : AppCompatEditText {
333
336
  tl = radius
334
337
  } else {
335
338
  val prevLineWidth = layout.getLineMax(line - 1)
336
- // Round Top-Left if we stick out further than the line above
337
- tl = if (currentLineWidth > prevLineWidth) radius else 0f
339
+ // Round Top-Left only if this line extends further than the line above
340
+ tl = if (!lineWidthsEqual(currentLineWidth, prevLineWidth) &&
341
+ currentLineWidth > prevLineWidth
342
+ ) radius else 0f
338
343
  }
339
344
 
340
345
  // Bottom-Left
@@ -342,8 +347,10 @@ class HighlightTextView : AppCompatEditText {
342
347
  bl = radius
343
348
  } else {
344
349
  val nextLineWidth = layout.getLineMax(line + 1)
345
- // Round Bottom-Left if we overhang the line below
346
- bl = if (currentLineWidth > nextLineWidth) radius else 0f
350
+ // Round Bottom-Left only if this line extends further than the line below
351
+ bl = if (!lineWidthsEqual(currentLineWidth, nextLineWidth) &&
352
+ currentLineWidth > nextLineWidth
353
+ ) radius else 0f
347
354
  }
348
355
  }
349
356
 
@@ -354,8 +361,10 @@ class HighlightTextView : AppCompatEditText {
354
361
  tr = radius
355
362
  } else {
356
363
  val prevLineWidth = layout.getLineMax(line - 1)
357
- // Round Top-Right if we stick out further than the line above
358
- tr = if (currentLineWidth > prevLineWidth) radius else 0f
364
+ // Round Top-Right only if this line extends further than the line above
365
+ tr = if (!lineWidthsEqual(currentLineWidth, prevLineWidth) &&
366
+ currentLineWidth > prevLineWidth
367
+ ) radius else 0f
359
368
  }
360
369
 
361
370
  // Bottom-Right
@@ -363,8 +372,10 @@ class HighlightTextView : AppCompatEditText {
363
372
  br = radius
364
373
  } else {
365
374
  val nextLineWidth = layout.getLineMax(line + 1)
366
- // Round Bottom-Right if we overhang the line below
367
- br = if (currentLineWidth > nextLineWidth) radius else 0f
375
+ // Round Bottom-Right only if this line extends further than the line below
376
+ br = if (!lineWidthsEqual(currentLineWidth, nextLineWidth) &&
377
+ currentLineWidth > nextLineWidth
378
+ ) radius else 0f
368
379
  }
369
380
  }
370
381
  }
@@ -382,6 +393,11 @@ class HighlightTextView : AppCompatEditText {
382
393
  }
383
394
  }
384
395
 
396
+ private fun lineWidthsEqual(w1: Float, w2: Float): Boolean {
397
+ // Small tolerance so lines that should visually match are treated as equal
398
+ return abs(w1 - w2) < 0.5f
399
+ }
400
+
385
401
  private fun isLineEmpty(text: CharSequence, layout: android.text.Layout, line: Int): Boolean {
386
402
  if (line < 0 || line >= layout.lineCount) return false
387
403
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-highlight-text-view",
3
- "version": "0.1.32",
3
+ "version": "0.1.33",
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",