react-native-advanced-text 0.2.14 → 0.2.15
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.
|
@@ -3,9 +3,12 @@ package com.advancedtext
|
|
|
3
3
|
import android.content.Context
|
|
4
4
|
import android.graphics.Canvas
|
|
5
5
|
import android.graphics.Color
|
|
6
|
+
import android.graphics.Paint
|
|
7
|
+
import android.graphics.Path
|
|
6
8
|
import android.graphics.Point
|
|
7
9
|
import android.graphics.RectF
|
|
8
10
|
import android.os.Build
|
|
11
|
+
import android.text.Layout
|
|
9
12
|
import android.text.SpannableString
|
|
10
13
|
import android.text.Spannable
|
|
11
14
|
import android.text.Spanned
|
|
@@ -58,6 +61,12 @@ class AdvancedTextView : TextView {
|
|
|
58
61
|
// text. setText() would clear any selection the user is making.
|
|
59
62
|
private var wordColorSpans: Array<ForegroundColorSpan?> = emptyArray()
|
|
60
63
|
|
|
64
|
+
// Reused on every draw for the highlight backgrounds.
|
|
65
|
+
private val highlightPaint = Paint(Paint.ANTI_ALIAS_FLAG)
|
|
66
|
+
private val rangePath = Path()
|
|
67
|
+
private val lineClipPath = Path()
|
|
68
|
+
private val rangeBounds = RectF()
|
|
69
|
+
|
|
61
70
|
constructor(context: Context?) : super(context) { init() }
|
|
62
71
|
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) { init() }
|
|
63
72
|
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { init() }
|
|
@@ -382,87 +391,78 @@ class AdvancedTextView : TextView {
|
|
|
382
391
|
private fun drawHighlightedWordBackgrounds(canvas: Canvas) {
|
|
383
392
|
val currentLayout = layout ?: return
|
|
384
393
|
val density = resources.displayMetrics.density
|
|
385
|
-
val originalColor = paint.color
|
|
386
394
|
|
|
387
395
|
highlightedWords.forEach { highlightedWord ->
|
|
388
396
|
val wordPos = wordPositions.find { it.index == highlightedWord.index } ?: return@forEach
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
paint.color = parseColor(highlightedWord.highlightColor)
|
|
399
|
-
|
|
400
|
-
for (line in startLine..endLine) {
|
|
401
|
-
val lineStartOffset = maxOf(start, currentLayout.getLineStart(line))
|
|
402
|
-
val lineEndOffset = minOf(end, currentLayout.getLineEnd(line))
|
|
403
|
-
if (lineStartOffset >= lineEndOffset) continue
|
|
404
|
-
|
|
405
|
-
val left = currentLayout.getPrimaryHorizontal(lineStartOffset)
|
|
406
|
-
val isLastWordOnLine = lineEndOffset >= currentLayout.getLineEnd(line)
|
|
407
|
-
val right = if (isLastWordOnLine) {
|
|
408
|
-
currentLayout.getLineRight(line)
|
|
409
|
-
} else {
|
|
410
|
-
currentLayout.getPrimaryHorizontal(lineEndOffset)
|
|
411
|
-
}
|
|
412
|
-
val top = currentLayout.getLineTop(line).toFloat()
|
|
413
|
-
val bottom = currentLayout.getLineBottom(line).toFloat()
|
|
414
|
-
val rect = RectF(
|
|
415
|
-
totalPaddingLeft + left,
|
|
416
|
-
totalPaddingTop + top,
|
|
417
|
-
totalPaddingLeft + right,
|
|
418
|
-
totalPaddingTop + bottom
|
|
419
|
-
)
|
|
420
|
-
val radius = highlightedWord.borderRadius * density
|
|
421
|
-
canvas.drawRoundRect(rect, radius, radius, paint)
|
|
422
|
-
}
|
|
397
|
+
drawRangeBackground(
|
|
398
|
+
canvas,
|
|
399
|
+
currentLayout,
|
|
400
|
+
wordPos.start,
|
|
401
|
+
wordPos.extendedEnd.coerceAtMost(currentText.length),
|
|
402
|
+
parseColor(highlightedWord.highlightColor),
|
|
403
|
+
highlightedWord.borderRadius * density
|
|
404
|
+
)
|
|
423
405
|
}
|
|
424
406
|
|
|
425
407
|
// Draw indicator background only in Highlight mode
|
|
426
408
|
if (indicatorMode != "fill" && indicatorWordIndex >= 0 && indicatorColor.isNotEmpty()) {
|
|
427
409
|
val wordPos = wordPositions.find { it.index == indicatorWordIndex }
|
|
428
410
|
if (wordPos != null) {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
paint.color = parseColor(indicatorColor)
|
|
438
|
-
|
|
439
|
-
for (line in startLine..endLine) {
|
|
440
|
-
val lineStartOffset = maxOf(start, currentLayout.getLineStart(line))
|
|
441
|
-
val lineEndOffset = minOf(end, currentLayout.getLineEnd(line))
|
|
442
|
-
if (lineStartOffset >= lineEndOffset) continue
|
|
443
|
-
|
|
444
|
-
val left = currentLayout.getPrimaryHorizontal(lineStartOffset)
|
|
445
|
-
val isLastWordOnLine = lineEndOffset >= currentLayout.getLineEnd(line)
|
|
446
|
-
val right = if (isLastWordOnLine) {
|
|
447
|
-
currentLayout.getLineRight(line)
|
|
448
|
-
} else {
|
|
449
|
-
currentLayout.getPrimaryHorizontal(lineEndOffset)
|
|
450
|
-
}
|
|
451
|
-
val top = currentLayout.getLineTop(line).toFloat()
|
|
452
|
-
val bottom = currentLayout.getLineBottom(line).toFloat()
|
|
453
|
-
val rect = RectF(
|
|
454
|
-
totalPaddingLeft + left,
|
|
455
|
-
totalPaddingTop + top,
|
|
456
|
-
totalPaddingLeft + right,
|
|
457
|
-
totalPaddingTop + bottom
|
|
458
|
-
)
|
|
459
|
-
canvas.drawRoundRect(rect, 0f, 0f, paint)
|
|
460
|
-
}
|
|
461
|
-
}
|
|
411
|
+
drawRangeBackground(
|
|
412
|
+
canvas,
|
|
413
|
+
currentLayout,
|
|
414
|
+
wordPos.start,
|
|
415
|
+
wordPos.end.coerceAtMost(currentText.length),
|
|
416
|
+
parseColor(indicatorColor),
|
|
417
|
+
0f
|
|
418
|
+
)
|
|
462
419
|
}
|
|
463
420
|
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Fills the background of text[start, end), one box per line. The edges
|
|
424
|
+
// come from Layout.getSelectionPath, which follows the text's direction:
|
|
425
|
+
// a right-to-left word (Arabic) starts at its right edge, so treating
|
|
426
|
+
// getPrimaryHorizontal(start) as the left edge drew boxes in the wrong place.
|
|
427
|
+
private fun drawRangeBackground(
|
|
428
|
+
canvas: Canvas,
|
|
429
|
+
layout: Layout,
|
|
430
|
+
start: Int,
|
|
431
|
+
end: Int,
|
|
432
|
+
color: Int,
|
|
433
|
+
radius: Float
|
|
434
|
+
) {
|
|
435
|
+
if (start >= end) return
|
|
436
|
+
highlightPaint.color = color
|
|
437
|
+
|
|
438
|
+
val startLine = layout.getLineForOffset(start)
|
|
439
|
+
val endLine = layout.getLineForOffset(end - 1)
|
|
440
|
+
for (line in startLine..endLine) {
|
|
441
|
+
val segmentStart = maxOf(start, layout.getLineStart(line))
|
|
442
|
+
val segmentEnd = minOf(end, layout.getLineEnd(line))
|
|
443
|
+
if (segmentStart >= segmentEnd) continue
|
|
444
|
+
|
|
445
|
+
rangePath.reset()
|
|
446
|
+
layout.getSelectionPath(segmentStart, segmentEnd, rangePath)
|
|
447
|
+
|
|
448
|
+
// A range reaching the end of a line counts as running onto the
|
|
449
|
+
// next one, so the selection path fills out to the view's edge.
|
|
450
|
+
// Clip to this line's text to stop the box at the last word.
|
|
451
|
+
lineClipPath.reset()
|
|
452
|
+
lineClipPath.addRect(
|
|
453
|
+
layout.getLineLeft(line),
|
|
454
|
+
layout.getLineTop(line).toFloat(),
|
|
455
|
+
layout.getLineRight(line),
|
|
456
|
+
layout.getLineBottom(line).toFloat(),
|
|
457
|
+
Path.Direction.CW
|
|
458
|
+
)
|
|
459
|
+
rangePath.op(lineClipPath, Path.Op.INTERSECT)
|
|
460
|
+
rangePath.computeBounds(rangeBounds, true)
|
|
461
|
+
if (rangeBounds.isEmpty) continue
|
|
464
462
|
|
|
465
|
-
|
|
463
|
+
rangeBounds.offset(totalPaddingLeft.toFloat(), totalPaddingTop.toFloat())
|
|
464
|
+
canvas.drawRoundRect(rangeBounds, radius, radius, highlightPaint)
|
|
465
|
+
}
|
|
466
466
|
}
|
|
467
467
|
|
|
468
468
|
private fun parseColor(colorString: String): Int {
|
package/package.json
CHANGED