react-native-highlight-text-view 0.1.16 → 0.1.18
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.
|
@@ -206,6 +206,13 @@ class HighlightTextView : AppCompatEditText {
|
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
})
|
|
209
|
+
|
|
210
|
+
// Set cursor at end when view gains focus
|
|
211
|
+
setOnFocusChangeListener { _, hasFocus ->
|
|
212
|
+
if (hasFocus) {
|
|
213
|
+
text?.length?.let { setSelection(it) }
|
|
214
|
+
}
|
|
215
|
+
}
|
|
209
216
|
}
|
|
210
217
|
|
|
211
218
|
fun setCharacterBackgroundColor(color: Int) {
|
|
@@ -308,8 +315,41 @@ class HighlightTextView : AppCompatEditText {
|
|
|
308
315
|
Typeface.create(baseTypeface, style)
|
|
309
316
|
}
|
|
310
317
|
|
|
318
|
+
// Save current text to force complete rebuild
|
|
319
|
+
val currentText = text?.toString() ?: ""
|
|
320
|
+
val currentSelection = selectionStart
|
|
321
|
+
|
|
311
322
|
this.typeface = typeface
|
|
312
|
-
|
|
323
|
+
|
|
324
|
+
// Force complete text rebuild with new font metrics
|
|
325
|
+
// This is crucial for fonts with different ascender/descender values like Eczar
|
|
326
|
+
post {
|
|
327
|
+
// Clear text first to force layout recalculation
|
|
328
|
+
isUpdatingText = true
|
|
329
|
+
setText("")
|
|
330
|
+
|
|
331
|
+
// Force measure and layout with empty text
|
|
332
|
+
measure(
|
|
333
|
+
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
|
334
|
+
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
// Restore text - this triggers complete rebuild with new font metrics
|
|
338
|
+
setText(currentText)
|
|
339
|
+
|
|
340
|
+
// Reapply all character backgrounds with new font metrics
|
|
341
|
+
applyCharacterBackgrounds()
|
|
342
|
+
|
|
343
|
+
// Restore cursor position
|
|
344
|
+
val safePosition = currentSelection.coerceIn(0, currentText.length)
|
|
345
|
+
setSelection(safePosition)
|
|
346
|
+
|
|
347
|
+
isUpdatingText = false
|
|
348
|
+
|
|
349
|
+
// Force complete layout recalculation
|
|
350
|
+
requestLayout()
|
|
351
|
+
invalidate()
|
|
352
|
+
}
|
|
313
353
|
}
|
|
314
354
|
|
|
315
355
|
fun setVerticalAlign(align: String?) {
|
|
@@ -367,6 +407,8 @@ class HighlightTextView : AppCompatEditText {
|
|
|
367
407
|
if (autoFocus && isFocusable && isFocusableInTouchMode) {
|
|
368
408
|
post {
|
|
369
409
|
requestFocus()
|
|
410
|
+
// Set cursor at the end of text
|
|
411
|
+
text?.length?.let { setSelection(it) }
|
|
370
412
|
val imm = context.getSystemService(android.content.Context.INPUT_METHOD_SERVICE) as? android.view.inputmethod.InputMethodManager
|
|
371
413
|
imm?.showSoftInput(this, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT)
|
|
372
414
|
}
|
|
@@ -377,6 +419,9 @@ class HighlightTextView : AppCompatEditText {
|
|
|
377
419
|
val text = text?.toString() ?: return
|
|
378
420
|
if (text.isEmpty()) return
|
|
379
421
|
|
|
422
|
+
// Save current cursor position
|
|
423
|
+
val currentSelection = selectionStart
|
|
424
|
+
|
|
380
425
|
val spannable = SpannableString(text)
|
|
381
426
|
|
|
382
427
|
// Apply line height if specified
|
|
@@ -430,7 +475,9 @@ class HighlightTextView : AppCompatEditText {
|
|
|
430
475
|
|
|
431
476
|
isUpdatingText = true
|
|
432
477
|
setText(spannable)
|
|
433
|
-
|
|
478
|
+
// Restore cursor position, or set to end if position is invalid
|
|
479
|
+
val safePosition = currentSelection.coerceIn(0, text.length)
|
|
480
|
+
setSelection(safePosition)
|
|
434
481
|
isUpdatingText = false
|
|
435
482
|
|
|
436
483
|
// Detect line wraps after layout is ready
|
package/ios/HighlightTextView.mm
CHANGED
|
@@ -430,6 +430,9 @@ using namespace facebook::react;
|
|
|
430
430
|
if (newViewProps.autoFocus && _textView.isEditable) {
|
|
431
431
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
432
432
|
[self->_textView becomeFirstResponder];
|
|
433
|
+
// Set cursor at the end of text
|
|
434
|
+
NSUInteger textLength = self->_textView.text.length;
|
|
435
|
+
self->_textView.selectedRange = NSMakeRange(textLength, 0);
|
|
433
436
|
});
|
|
434
437
|
}
|
|
435
438
|
}
|
|
@@ -468,6 +471,13 @@ Class<RCTComponentViewProtocol> HighlightTextViewCls(void)
|
|
|
468
471
|
}
|
|
469
472
|
}
|
|
470
473
|
|
|
474
|
+
- (void)textViewDidBeginEditing:(UITextView *)textView
|
|
475
|
+
{
|
|
476
|
+
// Set cursor at the end when editing begins
|
|
477
|
+
NSUInteger textLength = textView.text.length;
|
|
478
|
+
textView.selectedRange = NSMakeRange(textLength, 0);
|
|
479
|
+
}
|
|
480
|
+
|
|
471
481
|
- (void)updateFont
|
|
472
482
|
{
|
|
473
483
|
CGFloat fontSize = _fontSize > 0 ? _fontSize : 32.0;
|
|
@@ -519,7 +529,32 @@ Class<RCTComponentViewProtocol> HighlightTextViewCls(void)
|
|
|
519
529
|
}
|
|
520
530
|
|
|
521
531
|
_textView.font = newFont;
|
|
532
|
+
|
|
533
|
+
// Save current text to force complete rebuild
|
|
534
|
+
NSString *currentText = [_textView.text copy];
|
|
535
|
+
|
|
536
|
+
// Clear text storage to force complete recalculation
|
|
537
|
+
[_textView.textStorage beginEditing];
|
|
538
|
+
[_textView.textStorage replaceCharactersInRange:NSMakeRange(0, _textView.textStorage.length) withString:@""];
|
|
539
|
+
[_textView.textStorage endEditing];
|
|
540
|
+
|
|
541
|
+
// Force layout manager to invalidate everything
|
|
542
|
+
[_layoutManager invalidateLayoutForCharacterRange:NSMakeRange(0, 0) actualCharacterRange:NULL];
|
|
543
|
+
[_layoutManager invalidateDisplayForCharacterRange:NSMakeRange(0, 0)];
|
|
544
|
+
|
|
545
|
+
// Restore text and rebuild with new font
|
|
546
|
+
_textView.text = currentText;
|
|
547
|
+
|
|
548
|
+
// Reapply character backgrounds with new font metrics - this rebuilds attributed text
|
|
522
549
|
[self applyCharacterBackgrounds];
|
|
550
|
+
|
|
551
|
+
// Force complete layout recalculation
|
|
552
|
+
[_textView.layoutManager ensureLayoutForTextContainer:_textView.textContainer];
|
|
553
|
+
|
|
554
|
+
// Force the text view to redraw
|
|
555
|
+
[_textView setNeedsDisplay];
|
|
556
|
+
[_textView setNeedsLayout];
|
|
557
|
+
[_textView layoutIfNeeded];
|
|
523
558
|
}
|
|
524
559
|
|
|
525
560
|
- (void)applyCharacterBackgrounds
|
|
@@ -529,6 +564,9 @@ Class<RCTComponentViewProtocol> HighlightTextViewCls(void)
|
|
|
529
564
|
return;
|
|
530
565
|
}
|
|
531
566
|
|
|
567
|
+
// Save current cursor position
|
|
568
|
+
NSRange currentSelection = _textView.selectedRange;
|
|
569
|
+
|
|
532
570
|
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:text];
|
|
533
571
|
UIColor *bgColor = [self hexStringToColor:_characterBackgroundColor];
|
|
534
572
|
|
|
@@ -577,6 +615,12 @@ Class<RCTComponentViewProtocol> HighlightTextViewCls(void)
|
|
|
577
615
|
}
|
|
578
616
|
|
|
579
617
|
_textView.attributedText = attributedString;
|
|
618
|
+
|
|
619
|
+
// Restore cursor position if valid
|
|
620
|
+
if (currentSelection.location <= text.length) {
|
|
621
|
+
_textView.selectedRange = currentSelection;
|
|
622
|
+
}
|
|
623
|
+
|
|
580
624
|
[_textView setNeedsDisplay];
|
|
581
625
|
}
|
|
582
626
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-highlight-text-view",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18",
|
|
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",
|