Files
PojavLauncher_iOS/Natives/TrackedTextField.m

157 lines
4.8 KiB
Objective-C

#import "TrackedTextField.h"
#import "ios_uikit_bridge.h"
#import "utils.h"
#include "glfw_keycodes.h"
extern bool isUseStackQueueCall;
// There are private functions that we are unable to find public replacements
// (Both are found by placing breakpoints)
@interface UITextField(private)
- (NSRange)insertFilteredText:(NSString *)text;
- (id) replaceRangeWithTextWithoutClosingTyping:(UITextRange *)range replacementText:(NSString *)text;
@end
@interface TrackedTextField()
@property(nonatomic) int lastTextPos;
@property(nonatomic) CGFloat lastPointX;
@end
@implementation TrackedTextField
- (void)sendMultiBackspaces:(int)times {
for (int i = 0; i < times; i++) {
self.sendKey(GLFW_KEY_BACKSPACE, 0, 1, 0);
self.sendKey(GLFW_KEY_BACKSPACE, 0, 0, 0);
}
}
// workaround pasted text not being caught
- (void)paste:(id)sender {
[super paste:sender];
[self sendText:UIPasteboard.generalPasteboard.string];
}
- (void)sendText:(NSString *)text {
for (int i = 0; i < text.length; i++) {
// Directly convert unichar to jchar since both are in UTF-16 encoding.
unichar theChar = [text characterAtIndex:i];
if (isUseStackQueueCall && self.sendCharMods != nil) {
self.sendCharMods(theChar, 0);
} else {
self.sendChar(theChar);
}
}
}
- (void)beginFloatingCursorAtPoint:(CGPoint)point {
[super beginFloatingCursorAtPoint:point];
self.lastPointX = point.x;
}
// Handle cursor movement in the empty space
- (void)updateFloatingCursorAtPoint:(CGPoint)point {
[super updateFloatingCursorAtPoint:point];
if (self.lastPointX == 0 || (self.lastTextPos > 0 && self.lastTextPos < self.text.length)) {
// This is handled in -[TrackedTextField closestPositionToPoint:]
return;
}
CGFloat diff = point.x - self.lastPointX;
if (ABS(diff) < 8) {
return;
}
self.lastPointX = point.x;
int key = (diff > 0) ? GLFW_KEY_DPAD_RIGHT : GLFW_KEY_DPAD_LEFT;
self.sendKey(key, 0, 1, 0);
self.sendKey(key, 0, 0, 0);
}
- (void)endFloatingCursor {
[super endFloatingCursor];
self.lastPointX = 0;
}
- (UITextPosition *)closestPositionToPoint:(CGPoint)point {
// Handle cursor movement between characters
UITextPosition *position = [super closestPositionToPoint:point];
int start = [self offsetFromPosition:self.beginningOfDocument toPosition:position];
if (start - self.lastTextPos != 0) {
int key = (start - self.lastTextPos > 0) ? GLFW_KEY_DPAD_RIGHT : GLFW_KEY_DPAD_LEFT;
self.sendKey(key, 0, 1, 0);
self.sendKey(key, 0, 0, 0);
}
self.lastTextPos = start;
return position;
}
- (void)deleteBackward {
if (self.text.length > 1) {
// Keep the first character (a space)
[super deleteBackward];
} else {
self.text = @" ";
}
self.lastTextPos = [super offsetFromPosition:self.beginningOfDocument toPosition:self.selectedTextRange.start];
[self sendMultiBackspaces:1];
}
- (BOOL)hasText {
self.lastTextPos = MAX(self.lastTextPos, 1);
return YES;
}
// Old name: insertText
- (NSRange)insertFilteredText:(NSString *)text {
int cursorPos = [super offsetFromPosition:self.beginningOfDocument toPosition:self.selectedTextRange.start];
// This also makes sure that lastTextPos != cursorPos (text should never be empty)
if (self.lastTextPos - cursorPos == text.length) {
// Handle text markup by first deleting N amount of characters equal to the replaced text
[self sendMultiBackspaces:text.length];
}
// What else is done by past-autocomplete (insert a space after autocompletion)
// See -[TrackedTextField replaceRangeWithTextWithoutClosingTyping:replacementText:]
self.lastTextPos = cursorPos + text.length;
[self sendText:text];
NSRange range = [super insertFilteredText:text];
return range;
}
- (id)replaceRangeWithTextWithoutClosingTyping:(UITextRange *)range replacementText:(NSString *)text
{
int length = [super offsetFromPosition:range.start toPosition:range.end];
// Delete the range of needs for autocompletion
[self sendMultiBackspaces:length];
// Insert the autocompleted text
[self sendText:text];
return [super replaceRangeWithTextWithoutClosingTyping:range replacementText:text];
}
- (void)setAttributedMarkedText:(NSAttributedString *)markedText selectedRange:(NSRange)selectedRange {
// Delete the marked range
NSInteger markedLength = [self offsetFromPosition:self.markedTextRange.start toPosition:self.markedTextRange.end];
[self sendMultiBackspaces:markedLength];
[super setAttributedMarkedText:markedText selectedRange:selectedRange];
// Insert the new text
[self sendText:markedText.string];
}
- (void)setText:(NSString *)text {
[super setText:text];
self.lastTextPos = text.length;
}
@end