Skip to content

Commit 2d5847c

Browse files
authored
Merge pull request #49 from perunt/cursor-position-x-y
Cursor position
2 parents 33ce1f4 + 7abd47e commit 2d5847c

File tree

5 files changed

+61
-8
lines changed

5 files changed

+61
-8
lines changed

packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.m

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,8 @@ - (RCTTextSelection *)selection
184184
initWithStart:[backedTextInputView offsetFromPosition:backedTextInputView.beginningOfDocument
185185
toPosition:selectedTextRange.start]
186186
end:[backedTextInputView offsetFromPosition:backedTextInputView.beginningOfDocument
187-
toPosition:selectedTextRange.end]];
187+
toPosition:selectedTextRange.end]
188+
cursorPosition:[backedTextInputView caretRectForPosition:selectedTextRange.start].origin];
188189
}
189190

190191
- (void)setSelection:(RCTTextSelection *)selection
@@ -519,6 +520,8 @@ - (void)textInputDidChangeSelection
519520
@"selection" : @{
520521
@"start" : @(selection.start),
521522
@"end" : @(selection.end),
523+
@"positionY": @(selectionOrigin.y),
524+
@"positionX": @(selectionOrigin.x),
522525
},
523526
});
524527
}

packages/react-native/Libraries/Text/TextInput/RCTTextSelection.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@
1414

1515
@property (nonatomic, assign, readonly) NSInteger start;
1616
@property (nonatomic, assign, readonly) NSInteger end;
17+
@property (nonatomic, assign, readonly) CGPoint cursorPosition;
1718

18-
- (instancetype)initWithStart:(NSInteger)start end:(NSInteger)end;
19+
- (instancetype)initWithStart:(NSInteger)start end:(NSInteger)end cursorPosition:(CGPoint)cursorPosition;
1920

2021
@end
2122

packages/react-native/Libraries/Text/TextInput/RCTTextSelection.m

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@
99

1010
@implementation RCTTextSelection
1111

12-
- (instancetype)initWithStart:(NSInteger)start end:(NSInteger)end
12+
- (instancetype)initWithStart:(NSInteger)start end:(NSInteger)end cursorPosition:(CGPoint)cursorPosition
1313
{
1414
if (self = [super init]) {
1515
_start = start;
1616
_end = end;
17+
_cursorPosition = cursorPosition;
1718
}
1819
return self;
1920
}
@@ -27,7 +28,13 @@ + (RCTTextSelection *)RCTTextSelection:(id)json
2728
if ([json isKindOfClass:[NSDictionary class]]) {
2829
NSInteger start = [self NSInteger:json[@"start"]];
2930
NSInteger end = [self NSInteger:json[@"end"]];
30-
return [[RCTTextSelection alloc] initWithStart:start end:end];
31+
CGPoint cursorPosition = CGPointMake(
32+
[self CGFloat:json[@"cursorPositionX"]],
33+
[self CGFloat:json[@"cursorPositionY"]]
34+
);
35+
return [[RCTTextSelection alloc] initWithStart:start
36+
end:end
37+
cursorPosition:cursorPosition];
3138
}
3239

3340
return nil;

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
import java.util.LinkedList;
8282
import java.util.Locale;
8383
import java.util.Map;
84+
import android.view.ViewTreeObserver;
8485

8586
/** Manages instances of TextInput. */
8687
@ReactModule(name = ReactTextInputManager.REACT_CLASS)
@@ -1240,6 +1241,26 @@ public ReactSelectionWatcher(ReactEditText editText) {
12401241

12411242
@Override
12421243
public void onSelectionChanged(int start, int end) {
1244+
// Calculate cursor position
1245+
Layout layout = mReactEditText.getLayout();
1246+
1247+
// Wait for the TextInput to mount, if needed
1248+
if (layout == null) {
1249+
mReactEditText.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
1250+
@Override
1251+
public void onGlobalLayout() {
1252+
mReactEditText.getViewTreeObserver().removeOnGlobalLayoutListener(this);
1253+
onSelectionChanged(start, end);
1254+
}
1255+
});
1256+
return;
1257+
}
1258+
1259+
int line = layout.getLineForOffset(start);
1260+
int baseline = layout.getLineBaseline(line);
1261+
int ascent = layout.getLineAscent(line);
1262+
float cursorPositionX = layout.getPrimaryHorizontal(start);
1263+
float cursorPositionY = baseline + ascent;
12431264
// Android will call us back for both the SELECTION_START span and SELECTION_END span in text
12441265
// To prevent double calling back into js we cache the result of the previous call and only
12451266
// forward it on if we have new values
@@ -1252,7 +1273,12 @@ public void onSelectionChanged(int start, int end) {
12521273
if (mPreviousSelectionStart != realStart || mPreviousSelectionEnd != realEnd) {
12531274
mEventDispatcher.dispatchEvent(
12541275
new ReactTextInputSelectionEvent(
1255-
mSurfaceId, mReactEditText.getId(), realStart, realEnd));
1276+
mSurfaceId,
1277+
mReactEditText.getId(),
1278+
realStart,
1279+
realEnd,
1280+
Math.round(PixelUtil.toDIPFromPixel(cursorPositionX)),
1281+
Math.round(PixelUtil.toDIPFromPixel(cursorPositionY))));
12561282

12571283
mPreviousSelectionStart = realStart;
12581284
mPreviousSelectionEnd = realEnd;

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputSelectionEvent.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,31 @@
1919

2020
private int mSelectionStart;
2121
private int mSelectionEnd;
22+
private float mCursorPositionX;
23+
private float mCursorPositionY;
2224

2325
@Deprecated
24-
public ReactTextInputSelectionEvent(int viewId, int selectionStart, int selectionEnd) {
25-
this(-1, viewId, selectionStart, selectionEnd);
26+
public ReactTextInputSelectionEvent(
27+
int viewId,
28+
int selectionStart,
29+
int selectionEnd,
30+
float cursorPositionX,
31+
float cursorPositionY) {
32+
this(-1, viewId, selectionStart, selectionEnd, cursorPositionX, cursorPositionY);
2633
}
2734

2835
public ReactTextInputSelectionEvent(
29-
int surfaceId, int viewId, int selectionStart, int selectionEnd) {
36+
int surfaceId,
37+
int viewId,
38+
int selectionStart,
39+
int selectionEnd,
40+
float cursorPositionX,
41+
float cursorPositionY) {
3042
super(surfaceId, viewId);
3143
mSelectionStart = selectionStart;
3244
mSelectionEnd = selectionEnd;
45+
mCursorPositionX = cursorPositionX;
46+
mCursorPositionY = cursorPositionY;
3347
}
3448

3549
@Override
@@ -45,6 +59,8 @@ protected WritableMap getEventData() {
4559
WritableMap selectionData = Arguments.createMap();
4660
selectionData.putInt("end", mSelectionEnd);
4761
selectionData.putInt("start", mSelectionStart);
62+
selectionData.putDouble("cursorPositionX", mCursorPositionX);
63+
selectionData.putDouble("cursorPositionY", mCursorPositionY);
4864

4965
eventData.putMap("selection", selectionData);
5066
return eventData;

0 commit comments

Comments
 (0)