react-native-advanced-text 0.1.21 → 0.1.22

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.
@@ -1,4 +1,3 @@
1
- // File: AdvancedTextView.kt
2
1
  package com.advancedtext
3
2
 
4
3
  import android.content.Context
@@ -34,7 +33,6 @@ class AdvancedTextView : TextView {
34
33
  private var currentText: String = ""
35
34
  private var textColor: String = "#000000"
36
35
 
37
- // Cache for word positions to avoid recalculating
38
36
  private var wordPositions: List<WordPosition> = emptyList()
39
37
 
40
38
  constructor(context: Context?) : super(context) { init() }
@@ -42,51 +40,52 @@ class AdvancedTextView : TextView {
42
40
  constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { init() }
43
41
 
44
42
  private fun init() {
45
- Log.d(TAG, "AdvancedTextView initialized")
46
-
47
- textSize = 16f
48
- setPadding(16, 16, 16, 16)
49
-
50
- movementMethod = ClickableMovementMethod.getInstance()
51
- isClickable = false
52
- isLongClickable = false
53
-
54
- setTextIsSelectable(true)
55
-
56
- customSelectionActionModeCallback = object : ActionMode.Callback {
57
- override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
58
- customActionMode = mode
59
- return true
60
- }
61
-
62
- override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
63
- menu?.clear()
64
- val selectionStart = selectionStart
65
- val selectionEnd = selectionEnd
66
-
67
- if (selectionStart >= 0 && selectionEnd >= 0 && selectionStart != selectionEnd) {
68
- lastSelectedText = text.subSequence(selectionStart, selectionEnd).toString()
69
- menuOptions.forEachIndexed { index, option ->
70
- menu?.add(0, index, index, option)
71
- }
72
- sendSelectionEvent(lastSelectedText, "selection")
73
- return true
74
- }
75
- return false
76
- }
77
-
78
- override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
79
- item?.let {
80
- sendSelectionEvent(lastSelectedText, it.title.toString())
81
- mode?.finish()
82
- return true
83
- }
84
- return false
85
- }
86
-
87
- override fun onDestroyActionMode(mode: ActionMode?) {
88
- customActionMode = null
89
- }
43
+ Log.d(TAG, "AdvancedTextView initialized")
44
+
45
+ textSize = 16f
46
+ setPadding(16, 16, 16, 16)
47
+ movementMethod = LinkMovementMethod.getInstance()
48
+ setTextIsSelectable(true)
49
+
50
+
51
+ customSelectionActionModeCallback = object : ActionMode.Callback {
52
+ override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
53
+ customActionMode = mode
54
+ return true
55
+ }
56
+
57
+ override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
58
+ menu?.clear()
59
+
60
+ val selectionStart = selectionStart
61
+ val selectionEnd = selectionEnd
62
+
63
+ if (selectionStart >= 0 && selectionEnd >= 0 && selectionStart != selectionEnd) {
64
+ lastSelectedText = text.subSequence(selectionStart, selectionEnd).toString()
65
+
66
+ menuOptions.forEachIndexed { index, option ->
67
+ menu?.add(0, index, index, option)
68
+ }
69
+
70
+ sendSelectionEvent(lastSelectedText, "selection")
71
+ return true
72
+ }
73
+ return false
74
+ }
75
+
76
+ override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
77
+ item?.let {
78
+ val menuItemText = it.title.toString()
79
+ sendSelectionEvent(lastSelectedText, menuItemText)
80
+ mode?.finish()
81
+ return true
82
+ }
83
+ return false
84
+ }
85
+
86
+ override fun onDestroyActionMode(mode: ActionMode?) {
87
+ customActionMode = null
88
+ }
90
89
  }
91
90
  }
92
91
 
@@ -155,9 +154,7 @@ class AdvancedTextView : TextView {
155
154
 
156
155
  val spannableString = SpannableString(currentText)
157
156
 
158
- // Apply spans efficiently
159
157
  wordPositions.forEach { wordPos ->
160
- // Apply highlights
161
158
  highlightedWords.find { it.index == wordPos.index }?.let { highlightedWord ->
162
159
  val color = parseColor(highlightedWord.highlightColor)
163
160
  spannableString.setSpan(
@@ -168,7 +165,6 @@ class AdvancedTextView : TextView {
168
165
  )
169
166
  }
170
167
 
171
- // Apply indicator color
172
168
  if (wordPos.index == indicatorWordIndex) {
173
169
  spannableString.setSpan(
174
170
  ForegroundColorSpan(Color.parseColor(textColor)),
@@ -178,7 +174,6 @@ class AdvancedTextView : TextView {
178
174
  )
179
175
  }
180
176
 
181
- // Make words clickable
182
177
  spannableString.setSpan(
183
178
  WordClickableSpan(wordPos.index, wordPos.word),
184
179
  wordPos.start,
@@ -187,7 +182,6 @@ class AdvancedTextView : TextView {
187
182
  )
188
183
  }
189
184
 
190
- // Use post to ensure UI thread and avoid layout issues
191
185
  post {
192
186
  setText(spannableString, BufferType.SPANNABLE)
193
187
  Log.d(TAG, "Text updated with ${wordPositions.size} spans")
@@ -224,14 +218,11 @@ class AdvancedTextView : TextView {
224
218
  ) : ClickableSpan() {
225
219
 
226
220
  override fun onClick(widget: View) {
227
- Log.d(TAG, "Word clicked: '$word' (index=$wordIndex)")
228
- val spannable = widget as? TextView
229
- spannable?.text?.let {
230
- if (it is android.text.Spannable) {
231
- Selection.removeSelection(it)
232
- }
221
+ Log.d(TAG, "WordClickableSpan onClick triggered: '$word' (index=$wordIndex)")
222
+
223
+ widget.post {
224
+ sendWordPressEvent(word, wordIndex)
233
225
  }
234
- sendWordPressEvent(word, wordIndex)
235
226
  }
236
227
 
237
228
  override fun updateDrawState(ds: TextPaint) {
@@ -1,14 +1,15 @@
1
+ #ifndef AdvancedTextView_h
2
+ #define AdvancedTextView_h
3
+
1
4
  #import <React/RCTViewComponentView.h>
2
5
  #import <UIKit/UIKit.h>
3
6
 
4
- #ifndef AdvancedTextViewNativeComponent_h
5
- #define AdvancedTextViewNativeComponent_h
6
-
7
7
  NS_ASSUME_NONNULL_BEGIN
8
8
 
9
9
  @interface AdvancedTextView : RCTViewComponentView
10
+
10
11
  @end
11
12
 
12
13
  NS_ASSUME_NONNULL_END
13
14
 
14
- #endif /* AdvancedTextViewNativeComponent_h */
15
+ #endif /* AdvancedTextView_h */
@@ -9,13 +9,17 @@
9
9
 
10
10
  using namespace facebook::react;
11
11
 
12
- @interface AdvancedTextView () <RCTAdvancedTextViewViewProtocol>
12
+ @interface AdvancedTextView () <RCTAdvancedTextViewViewProtocol, UIGestureRecognizerDelegate>
13
+
14
+ @property (nonatomic, strong) UITextView *textView;
15
+ @property (nonatomic, strong) NSMutableArray<NSDictionary *> *wordRanges;
16
+ @property (nonatomic, strong) NSMutableDictionary<NSNumber *, UIColor *> *highlightColors;
17
+ @property (nonatomic, strong) NSArray<NSString *> *menuOptions;
18
+ @property (nonatomic, assign) NSInteger indicatorWordIndex;
13
19
 
14
20
  @end
15
21
 
16
- @implementation AdvancedTextView {
17
- UIView * _view;
18
- }
22
+ @implementation AdvancedTextView
19
23
 
20
24
  + (ComponentDescriptorProvider)componentDescriptorProvider
21
25
  {
@@ -24,16 +28,49 @@ using namespace facebook::react;
24
28
 
25
29
  - (instancetype)initWithFrame:(CGRect)frame
26
30
  {
27
- if (self = [super initWithFrame:frame]) {
28
- static const auto defaultProps = std::make_shared<const AdvancedTextViewProps>();
29
- _props = defaultProps;
31
+ if (self = [super initWithFrame:frame]) {
32
+ static const auto defaultProps = std::make_shared<const AdvancedTextViewProps>();
33
+ _props = defaultProps;
34
+
35
+ _wordRanges = [NSMutableArray array];
36
+ _highlightColors = [NSMutableDictionary dictionary];
37
+ _indicatorWordIndex = -1;
38
+
39
+ [self setupTextView];
40
+ [self setupGestureRecognizers];
41
+ }
42
+
43
+ return self;
44
+ }
45
+
46
+ - (void)setupTextView
47
+ {
48
+ _textView = [[UITextView alloc] initWithFrame:self.bounds];
49
+ _textView.editable = NO;
50
+ _textView.scrollEnabled = YES;
51
+ _textView.backgroundColor = [UIColor clearColor];
52
+ _textView.textContainerInset = UIEdgeInsetsMake(8, 8, 8, 8);
53
+ _textView.font = [UIFont systemFontOfSize:16];
54
+ _textView.textColor = [UIColor labelColor];
30
55
 
31
- _view = [[UIView alloc] init];
56
+ self.contentView = _textView;
57
+ }
32
58
 
33
- self.contentView = _view;
34
- }
59
+ - (void)setupGestureRecognizers
60
+ {
61
+ // Single tap for word selection
62
+ UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]
63
+ initWithTarget:self
64
+ action:@selector(handleTap:)];
65
+ tapGesture.delegate = self;
66
+ [_textView addGestureRecognizer:tapGesture];
35
67
 
36
- return self;
68
+ // Long press for context menu
69
+ UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc]
70
+ initWithTarget:self
71
+ action:@selector(handleLongPress:)];
72
+ longPressGesture.delegate = self;
73
+ [_textView addGestureRecognizer:longPressGesture];
37
74
  }
38
75
 
39
76
  - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
@@ -41,26 +78,287 @@ using namespace facebook::react;
41
78
  const auto &oldViewProps = *std::static_pointer_cast<AdvancedTextViewProps const>(_props);
42
79
  const auto &newViewProps = *std::static_pointer_cast<AdvancedTextViewProps const>(props);
43
80
 
44
- if (oldViewProps.color != newViewProps.color) {
45
- NSString * colorToConvert = [[NSString alloc] initWithUTF8String: newViewProps.color.c_str()];
46
- [_view setBackgroundColor:[self hexStringToColor:colorToConvert]];
81
+ // Update text
82
+ if (oldViewProps.text != newViewProps.text) {
83
+ NSString *text = [NSString stringWithUTF8String:newViewProps.text.c_str()];
84
+ [self updateTextContent:text];
85
+ }
86
+
87
+ // Update highlighted words
88
+ if (oldViewProps.highlightedWords != newViewProps.highlightedWords) {
89
+ [self updateHighlightedWords:newViewProps.highlightedWords];
90
+ }
91
+
92
+ // Update menu options
93
+ if (oldViewProps.menuOptions != newViewProps.menuOptions) {
94
+ [self updateMenuOptions:newViewProps.menuOptions];
95
+ }
96
+
97
+ // Update indicator word index
98
+ if (oldViewProps.indicatorWordIndex != newViewProps.indicatorWordIndex) {
99
+ _indicatorWordIndex = newViewProps.indicatorWordIndex;
100
+ [self updateTextAppearance];
47
101
  }
48
102
 
49
103
  [super updateProps:props oldProps:oldProps];
50
104
  }
51
105
 
52
- Class<RCTComponentViewProtocol> AdvancedTextViewCls(void)
106
+ - (void)updateTextContent:(NSString *)text
53
107
  {
54
- return AdvancedTextView.class;
108
+ // Parse text into words and their ranges
109
+ [_wordRanges removeAllObjects];
110
+
111
+ NSRange searchRange = NSMakeRange(0, text.length);
112
+ NSCharacterSet *whitespaceSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
113
+
114
+ NSInteger wordIndex = 0;
115
+ while (searchRange.location < text.length) {
116
+ // Skip whitespace
117
+ while (searchRange.location < text.length &&
118
+ [whitespaceSet characterIsMember:[text characterAtIndex:searchRange.location]]) {
119
+ searchRange.location++;
120
+ searchRange.length = text.length - searchRange.location;
121
+ }
122
+
123
+ if (searchRange.location >= text.length) break;
124
+
125
+ // Find word end
126
+ NSUInteger wordStart = searchRange.location;
127
+ while (searchRange.location < text.length &&
128
+ ![whitespaceSet characterIsMember:[text characterAtIndex:searchRange.location]]) {
129
+ searchRange.location++;
130
+ }
131
+
132
+ NSRange wordRange = NSMakeRange(wordStart, searchRange.location - wordStart);
133
+ NSString *word = [text substringWithRange:wordRange];
134
+
135
+ [_wordRanges addObject:@{
136
+ @"word": word,
137
+ @"range": [NSValue valueWithRange:wordRange],
138
+ @"index": @(wordIndex)
139
+ }];
140
+
141
+ wordIndex++;
142
+ searchRange.length = text.length - searchRange.location;
143
+ }
144
+
145
+ [self updateTextAppearance];
55
146
  }
56
147
 
57
- - hexStringToColor:(NSString *)stringToConvert
148
+ - (void)updateHighlightedWords:(const std::vector<AdvancedTextViewHighlightedWordsStruct> &)highlightedWords
58
149
  {
150
+ [_highlightColors removeAllObjects];
151
+
152
+ for (const auto &hw : highlightedWords) {
153
+ NSInteger index = hw.index;
154
+ NSString *colorString = [NSString stringWithUTF8String:hw.highlightColor.c_str()];
155
+ UIColor *color = [self hexStringToColor:colorString];
156
+
157
+ if (color) {
158
+ _highlightColors[@(index)] = color;
159
+ }
160
+ }
161
+
162
+ [self updateTextAppearance];
163
+ }
164
+
165
+ - (void)updateMenuOptions:(const std::vector<std::string> &)options
166
+ {
167
+ NSMutableArray *menuArray = [NSMutableArray array];
168
+ for (const auto &option : options) {
169
+ [menuArray addObject:[NSString stringWithUTF8String:option.c_str()]];
170
+ }
171
+ _menuOptions = [menuArray copy];
172
+ }
173
+
174
+ - (void)updateTextAppearance
175
+ {
176
+ if (_wordRanges.count == 0) return;
177
+
178
+ NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc]
179
+ initWithString:_textView.text];
180
+
181
+ // Apply default attributes
182
+ [attributedString addAttribute:NSFontAttributeName
183
+ value:[UIFont systemFontOfSize:16]
184
+ range:NSMakeRange(0, attributedString.length)];
185
+
186
+ [attributedString addAttribute:NSForegroundColorAttributeName
187
+ value:[UIColor labelColor]
188
+ range:NSMakeRange(0, attributedString.length)];
189
+
190
+ // Apply highlights
191
+ for (NSDictionary *wordInfo in _wordRanges) {
192
+ NSNumber *index = wordInfo[@"index"];
193
+ NSValue *rangeValue = wordInfo[@"range"];
194
+ NSRange range = [rangeValue rangeValue];
195
+
196
+ UIColor *highlightColor = _highlightColors[index];
197
+ if (highlightColor) {
198
+ [attributedString addAttribute:NSBackgroundColorAttributeName
199
+ value:highlightColor
200
+ range:range];
201
+ }
202
+
203
+ // Add indicator (underline or special formatting) for indicated word
204
+ if (_indicatorWordIndex >= 0 && [index integerValue] == _indicatorWordIndex) {
205
+ [attributedString addAttribute:NSUnderlineStyleAttributeName
206
+ value:@(NSUnderlineStyleSingle)
207
+ range:range];
208
+ [attributedString addAttribute:NSUnderlineColorAttributeName
209
+ value:[UIColor systemBlueColor]
210
+ range:range];
211
+ }
212
+ }
213
+
214
+ _textView.attributedText = attributedString;
215
+ }
216
+
217
+ - (void)handleTap:(UITapGestureRecognizer *)gesture
218
+ {
219
+ if (gesture.state != UIGestureRecognizerStateEnded) return;
220
+
221
+ CGPoint location = [gesture locationInView:_textView];
222
+ NSInteger wordIndex = [self wordIndexAtPoint:location];
223
+
224
+ if (wordIndex >= 0 && wordIndex < _wordRanges.count) {
225
+ NSDictionary *wordInfo = _wordRanges[wordIndex];
226
+ NSString *word = wordInfo[@"word"];
227
+
228
+ [self emitWordPressEvent:word index:wordIndex];
229
+ }
230
+ }
231
+
232
+ - (void)handleLongPress:(UILongPressGestureRecognizer *)gesture
233
+ {
234
+ if (gesture.state != UIGestureRecognizerStateBegan) return;
235
+
236
+ CGPoint location = [gesture locationInView:_textView];
237
+ NSInteger wordIndex = [self wordIndexAtPoint:location];
238
+
239
+ if (wordIndex >= 0 && wordIndex < _wordRanges.count) {
240
+ NSDictionary *wordInfo = _wordRanges[wordIndex];
241
+ NSString *word = wordInfo[@"word"];
242
+
243
+ [self showContextMenuForWord:word atIndex:wordIndex location:location];
244
+ }
245
+ }
246
+
247
+ - (NSInteger)wordIndexAtPoint:(CGPoint)point
248
+ {
249
+ // Adjust point for text container insets
250
+ point.x -= _textView.textContainerInset.left;
251
+ point.y -= _textView.textContainerInset.top;
252
+
253
+ NSLayoutManager *layoutManager = _textView.layoutManager;
254
+ NSTextContainer *textContainer = _textView.textContainer;
255
+
256
+ NSUInteger characterIndex = [layoutManager characterIndexForPoint:point
257
+ inTextContainer:textContainer
258
+ fractionOfDistanceBetweenInsertionPoints:nil];
259
+
260
+ // Find which word this character belongs to
261
+ for (NSDictionary *wordInfo in _wordRanges) {
262
+ NSValue *rangeValue = wordInfo[@"range"];
263
+ NSRange range = [rangeValue rangeValue];
264
+
265
+ if (NSLocationInRange(characterIndex, range)) {
266
+ return [wordInfo[@"index"] integerValue];
267
+ }
268
+ }
269
+
270
+ return -1;
271
+ }
272
+
273
+ - (void)showContextMenuForWord:(NSString *)word atIndex:(NSInteger)index location:(CGPoint)location
274
+ {
275
+ if (!_menuOptions || _menuOptions.count == 0) return;
276
+
277
+ UIAlertController *alert = [UIAlertController alertControllerWithTitle:word
278
+ message:nil
279
+ preferredStyle:UIAlertControllerStyleActionSheet];
280
+
281
+ for (NSString *option in _menuOptions) {
282
+ UIAlertAction *action = [UIAlertAction actionWithTitle:option
283
+ style:UIAlertActionStyleDefault
284
+ handler:^(UIAlertAction * _Nonnull action) {
285
+ [self emitSelectionEvent:word menuOption:option];
286
+ }];
287
+ [alert addAction:action];
288
+ }
289
+
290
+ UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel"
291
+ style:UIAlertActionStyleCancel
292
+ handler:nil];
293
+ [alert addAction:cancelAction];
294
+
295
+ // Present from the root view controller
296
+ UIViewController *rootVC = [self findViewController];
297
+ if (rootVC) {
298
+ // For iPad, set up popover presentation
299
+ if (alert.popoverPresentationController) {
300
+ alert.popoverPresentationController.sourceView = _textView;
301
+ alert.popoverPresentationController.sourceRect = CGRectMake(location.x, location.y, 1, 1);
302
+ }
303
+
304
+ [rootVC presentViewController:alert animated:YES completion:nil];
305
+ }
306
+ }
307
+
308
+ - (UIViewController *)findViewController
309
+ {
310
+ UIResponder *responder = self;
311
+ while (responder) {
312
+ if ([responder isKindOfClass:[UIViewController class]]) {
313
+ return (UIViewController *)responder;
314
+ }
315
+ responder = [responder nextResponder];
316
+ }
317
+ return nil;
318
+ }
319
+
320
+ - (void)emitWordPressEvent:(NSString *)word index:(NSInteger)index
321
+ {
322
+ if (_eventEmitter) {
323
+ auto emitter = std::static_pointer_cast<const AdvancedTextViewEventEmitter>(_eventEmitter);
324
+
325
+ AdvancedTextViewEventEmitter::OnWordPress event;
326
+ event.word = [word UTF8String];
327
+ event.index = static_cast<int>(index);
328
+
329
+ emitter->onWordPress(event);
330
+ }
331
+ }
332
+
333
+ - (void)emitSelectionEvent:(NSString *)selectedText menuOption:(NSString *)option
334
+ {
335
+ if (_eventEmitter) {
336
+ auto emitter = std::static_pointer_cast<const AdvancedTextViewEventEmitter>(_eventEmitter);
337
+
338
+ AdvancedTextViewEventEmitter::OnSelection event;
339
+ event.selectedText = [selectedText UTF8String];
340
+ event.event = [option UTF8String];
341
+
342
+ emitter->onSelection(event);
343
+ }
344
+ }
345
+
346
+ - (void)layoutSubviews
347
+ {
348
+ [super layoutSubviews];
349
+ _textView.frame = self.bounds;
350
+ }
351
+
352
+ - (UIColor *)hexStringToColor:(NSString *)stringToConvert
353
+ {
354
+ if (!stringToConvert || [stringToConvert length] == 0) return nil;
355
+
59
356
  NSString *noHashString = [stringToConvert stringByReplacingOccurrencesOfString:@"#" withString:@""];
60
357
  NSScanner *stringScanner = [NSScanner scannerWithString:noHashString];
61
358
 
62
359
  unsigned hex;
63
360
  if (![stringScanner scanHexInt:&hex]) return nil;
361
+
64
362
  int r = (hex >> 16) & 0xFF;
65
363
  int g = (hex >> 8) & 0xFF;
66
364
  int b = (hex) & 0xFF;
@@ -68,4 +366,9 @@ Class<RCTComponentViewProtocol> AdvancedTextViewCls(void)
68
366
  return [UIColor colorWithRed:r / 255.0f green:g / 255.0f blue:b / 255.0f alpha:1.0f];
69
367
  }
70
368
 
369
+ Class<RCTComponentViewProtocol> AdvancedTextViewCls(void)
370
+ {
371
+ return AdvancedTextView.class;
372
+ }
373
+
71
374
  @end
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-advanced-text",
3
- "version": "0.1.21",
3
+ "version": "0.1.22",
4
4
  "description": " Advanced text component for React Native with custom select options.",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -1,59 +0,0 @@
1
- package com.advancedtext
2
-
3
- import android.text.Layout
4
- import android.text.Selection
5
- import android.text.Spannable
6
- import android.text.method.BaseMovementMethod
7
- import android.text.style.ClickableSpan
8
- import android.view.MotionEvent
9
- import android.widget.TextView
10
-
11
- class ClickableMovementMethod : BaseMovementMethod() {
12
-
13
- companion object {
14
- private var sInstance: ClickableMovementMethod? = null
15
-
16
- fun getInstance(): ClickableMovementMethod {
17
- if (sInstance == null) {
18
- sInstance = ClickableMovementMethod()
19
- }
20
- return sInstance!!
21
- }
22
- }
23
-
24
- override fun canSelectArbitrarily(): Boolean = false
25
-
26
- override fun onTouchEvent(widget: TextView, buffer: Spannable, event: MotionEvent): Boolean {
27
- val action = event.actionMasked
28
- if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {
29
-
30
- var x = (event.x - widget.totalPaddingLeft + widget.scrollX).toInt()
31
- var y = (event.y - widget.totalPaddingTop + widget.scrollY).toInt()
32
-
33
- val layout: Layout = widget.layout
34
- val links: Array<ClickableSpan>? = if (y < 0 || y > layout.height) {
35
- null
36
- } else {
37
- val line = layout.getLineForVertical(y)
38
- if (x < layout.getLineLeft(line) || x > layout.getLineRight(line)) null
39
- else {
40
- val offset = layout.getOffsetForHorizontal(line, x.toFloat())
41
- buffer.getSpans(offset, offset, ClickableSpan::class.java)
42
- }
43
- }
44
-
45
- if (!links.isNullOrEmpty()) {
46
- if (action == MotionEvent.ACTION_UP) links[0].onClick(widget)
47
- else Selection.setSelection(buffer, buffer.getSpanStart(links[0]), buffer.getSpanEnd(links[0]))
48
- return true
49
- } else {
50
- Selection.removeSelection(buffer)
51
- }
52
- }
53
- return false
54
- }
55
-
56
- override fun initialize(widget: TextView, text: Spannable) {
57
- Selection.removeSelection(text)
58
- }
59
- }