From 82dc17cd9ce03db2be38cc600183c2478d9f814e Mon Sep 17 00:00:00 2001 From: khanhduytran0 Date: Tue, 11 Jan 2022 13:58:16 +0700 Subject: [PATCH] Support for input cursor movement from keyboard New class: TrackedTextField. This class keeps track of the pointer in the text field. The hidden text field now has 40 placeholder spaces and the pointer is placed at the middle. When typing, placeholder spaces won't get removed as before. Autocorrect seem to be broken a bit for now. Additional changes: - Added missing GLFW functions (array version of other functions) - Hotbar handling: doubled barWidth and barHeight Bug fix: - Touching onto the hotbar breaks the handling of on-screen tap/long-press gestures --- .../src/main/java/org/lwjgl/glfw/GLFW.java | 196 +++++++++++++----- Natives/SurfaceViewController.m | 149 +++++++------ Natives/input_bridge_v3.m | 4 +- 3 files changed, 240 insertions(+), 109 deletions(-) diff --git a/JavaApp/src/main/java/org/lwjgl/glfw/GLFW.java b/JavaApp/src/main/java/org/lwjgl/glfw/GLFW.java index b0a8b00b..4d780f67 100644 --- a/JavaApp/src/main/java/org/lwjgl/glfw/GLFW.java +++ b/JavaApp/src/main/java/org/lwjgl/glfw/GLFW.java @@ -835,29 +835,18 @@ public class GLFW } width.put(internalGetWindow(window).width); height.put(internalGetWindow(window).height); - } - - public static void glfwGetFramebufferSize(@NativeType("GLFWwindow *") long window, @Nullable @NativeType("int *") int[] width, @Nullable @NativeType("int *") int[] height) { - if (CHECKS) { - // check(window); - checkSafe(width, 1); - checkSafe(height, 1); - } - - width[0] = internalGetWindow(window).width; - height[0] = internalGetWindow(window).height; } - @Nullable + @Nullable @NativeType("GLFWmonitor **") public static PointerBuffer glfwGetMonitors() { PointerBuffer pBuffer = PointerBuffer.allocateDirect(1); - pBuffer.put(glfwGetPrimaryMonitor()); - return pBuffer; + pBuffer.put(glfwGetPrimaryMonitor()); + return pBuffer; } public static long glfwGetPrimaryMonitor() { - // Prevent NULL check + // Prevent NULL check return 1L; } @@ -885,44 +874,17 @@ public class GLFW height.put(mGLFWWindowHeight); } - public static void glfwGetMonitorPos(@NativeType("GLFWmonitor *") long monitor, @Nullable @NativeType("int *") int[] xpos, @Nullable @NativeType("int *") int[] ypos) { - if (CHECKS) { - // check(monitor); - checkSafe(xpos, 1); - checkSafe(ypos, 1); - } - - xpos[0] = 0; - ypos[0] = 0; - } - - /** Array version of: {@link #glfwGetMonitorWorkarea GetMonitorWorkarea} */ - public static void glfwGetMonitorWorkarea(@NativeType("GLFWmonitor *") long monitor, @Nullable @NativeType("int *") int[] xpos, @Nullable @NativeType("int *") int[] ypos, @Nullable @NativeType("int *") int[] width, @Nullable @NativeType("int *") int[] height) { - if (CHECKS) { - // check(monitor); - checkSafe(xpos, 1); - checkSafe(ypos, 1); - checkSafe(width, 1); - checkSafe(height, 1); - } - - xpos[0] = 0; - ypos[0] = 0; - width[0] = mGLFWWindowWidth; - height[0] = mGLFWWindowHeight; - } - @NativeType("GLFWmonitor *") public static long glfwGetWindowMonitor(@NativeType("GLFWwindow *") long window) { return internalGetWindow(window).monitor; } - - public static void glfwSetWindowMonitor(@NativeType("GLFWwindow *") long window, @NativeType("GLFWmonitor *") long monitor, int xpos, int ypos, int width, int height, int refreshRate) { - GLFWWindowProperties properties = internalGetWindow(window); - properties.monitor = monitor; - properties.x = xpos; - properties.y = ypos; - internalChangeMonitorSize(width, height); + + public static void glfwSetWindowMonitor(@NativeType("GLFWwindow *") long window, @NativeType("GLFWmonitor *") long monitor, int xpos, int ypos, int width, int height, int refreshRate) { + GLFWWindowProperties properties = internalGetWindow(window); + properties.monitor = monitor; + properties.x = xpos; + properties.y = ypos; + internalChangeMonitorSize(width, height); } public static int glfwGetWindowAttrib(@NativeType("GLFWwindow *") long window, int attrib) { @@ -1317,4 +1279,140 @@ return false; public static boolean glfwGetGamepadState(int jid, GLFWGamepadState state) { return false; } + + /** Array version of: {@link #glfwGetVersion GetVersion} */ + public static void glfwGetVersion(@Nullable @NativeType("int *") int[] major, @Nullable @NativeType("int *") int[] minor, @Nullable @NativeType("int *") int[] rev) { + if (CHECKS) { + checkSafe(major, 1); + checkSafe(minor, 1); + checkSafe(rev, 1); + } + major[0] = GLFW_VERSION_MAJOR; + minor[0] = GLFW_VERSION_MINOR; + rev[0] = GLFW_VERSION_REVISION; + } + + /** Array version of: {@link #glfwGetMonitorPos GetMonitorPos} */ + public static void glfwGetMonitorPos(@NativeType("GLFWmonitor *") long monitor, @Nullable @NativeType("int *") int[] xpos, @Nullable @NativeType("int *") int[] ypos) { + if (CHECKS) { + // check(monitor); + checkSafe(xpos, 1); + checkSafe(ypos, 1); + } + xpos[0] = 0; + ypos[0] = 0; + } + + /** Array version of: {@link #glfwGetMonitorWorkarea GetMonitorWorkarea} */ + public static void glfwGetMonitorWorkarea(@NativeType("GLFWmonitor *") long monitor, @Nullable @NativeType("int *") int[] xpos, @Nullable @NativeType("int *") int[] ypos, @Nullable @NativeType("int *") int[] width, @Nullable @NativeType("int *") int[] height) { + if (CHECKS) { + // check(monitor); + checkSafe(xpos, 1); + checkSafe(ypos, 1); + checkSafe(width, 1); + checkSafe(height, 1); + } + + xpos[0] = 0; + ypos[0] = 0; + width[0] = mGLFWWindowWidth; + height[0] = mGLFWWindowHeight; + } + + /** Array version of: {@link #glfwGetMonitorPhysicalSize GetMonitorPhysicalSize} */ +/* + public static void glfwGetMonitorPhysicalSize(@NativeType("GLFWmonitor *") long monitor, @Nullable @NativeType("int *") int[] widthMM, @Nullable @NativeType("int *") int[] heightMM) { + long __functionAddress = Functions.GetMonitorPhysicalSize; + if (CHECKS) { + check(monitor); + checkSafe(widthMM, 1); + checkSafe(heightMM, 1); + } + invokePPPV(monitor, widthMM, heightMM, __functionAddress); + } +*/ + + /** Array version of: {@link #glfwGetMonitorContentScale GetMonitorContentScale} */ +/* + public static void glfwGetMonitorContentScale(@NativeType("GLFWmonitor *") long monitor, @Nullable @NativeType("float *") float[] xscale, @Nullable @NativeType("float *") float[] yscale) { + long __functionAddress = Functions.GetMonitorContentScale; + if (CHECKS) { + check(monitor); + checkSafe(xscale, 1); + checkSafe(yscale, 1); + } + invokePPPV(monitor, xscale, yscale, __functionAddress); + } +*/ + + /** Array version of: {@link #glfwGetWindowPos GetWindowPos} */ + public static void glfwGetWindowPos(@NativeType("GLFWwindow *") long window, @Nullable @NativeType("int *") int[] xpos, @Nullable @NativeType("int *") int[] ypos) { + if (CHECKS) { + // check(window); + checkSafe(xpos, 1); + checkSafe(ypos, 1); + } + xpos[0] = 0; + ypos[0] = 0; + } + + /** Array version of: {@link #glfwGetWindowSize GetWindowSize} */ + public static void glfwGetWindowSize(@NativeType("GLFWwindow *") long window, @Nullable @NativeType("int *") int[] width, @Nullable @NativeType("int *") int[] height) { + if (CHECKS) { + // check(window); + checkSafe(width, 1); + checkSafe(height, 1); + } + width[0] = internalGetWindow(window).width; + height[0] = internalGetWindow(window).height; + } + + /** Array version of: {@link #glfwGetFramebufferSize GetFramebufferSize} */ + public static void glfwGetFramebufferSize(@NativeType("GLFWwindow *") long window, @Nullable @NativeType("int *") int[] width, @Nullable @NativeType("int *") int[] height) { + if (CHECKS) { + // check(window); + checkSafe(width, 1); + checkSafe(height, 1); + } + width[0] = internalGetWindow(window).width; + height[0] = internalGetWindow(window).height; + } + + /** Array version of: {@link #glfwGetWindowFrameSize GetWindowFrameSize} */ + public static void glfwGetWindowFrameSize(@NativeType("GLFWwindow *") long window, @Nullable @NativeType("int *") int[] left, @Nullable @NativeType("int *") int[] top, @Nullable @NativeType("int *") int[] right, @Nullable @NativeType("int *") int[] bottom) { + if (CHECKS) { + // check(window); + checkSafe(left, 1); + checkSafe(top, 1); + checkSafe(right, 1); + checkSafe(bottom, 1); + } + left[0] = top[0] = 0; + right[0] = internalGetWindow(window).width; + bottom[0] = internalGetWindow(window).height; + } + + /** Array version of: {@link #glfwGetWindowContentScale GetWindowContentScale} */ +/* + public static void glfwGetWindowContentScale(@NativeType("GLFWwindow *") long window, @Nullable @NativeType("float *") float[] xscale, @Nullable @NativeType("float *") float[] yscale) { + long __functionAddress = Functions.GetWindowContentScale; + if (CHECKS) { + check(window); + checkSafe(xscale, 1); + checkSafe(yscale, 1); + } + invokePPPV(window, xscale, yscale, __functionAddress); + } +*/ + + /** Array version of: {@link #glfwGetCursorPos GetCursorPos} */ + public static void glfwGetCursorPos(@NativeType("GLFWwindow *") long window, @Nullable @NativeType("double *") double[] xpos, @Nullable @NativeType("double *") double[] ypos) { + if (CHECKS) { + // check(window); + checkSafe(xpos, 1); + checkSafe(ypos, 1); + } + xpos[0] = mGLFWCursorX; + ypos[0] = mGLFWCursorY; + } } diff --git a/Natives/SurfaceViewController.m b/Natives/SurfaceViewController.m index 5926cb85..1fa35baf 100644 --- a/Natives/SurfaceViewController.m +++ b/Natives/SurfaceViewController.m @@ -24,13 +24,13 @@ // #define DEBUG_VISIBLE_TEXT_FIELD // #define DEBUG_VISIBLE_TOUCH -#define INPUT_SPACE_CHAR @" " +#define INPUT_SPACE_CHAR @" " +#define INPUT_FULL_LENGTH 40 #define INPUT_SPACE_LENGTH 20 #ifdef DEBUG_VISIBLE_TEXT_FIELD UILabel *inputLengthView; #endif -UITextField *inputView; int inputTextLength; BOOL shouldTriggerClick = NO; @@ -41,6 +41,7 @@ BOOL slideableHotbar; // TODO: key modifiers impl +#pragma mark Class GameSurfaceView @implementation GameSurfaceView const void * _CGDataProviderGetBytePointerCallbackOSMESA(void *info) { return gbuffer; @@ -84,9 +85,39 @@ const void * _CGDataProviderGetBytePointerCallbackOSMESA(void *info) { @end + +#pragma mark Class TrackedTextField +@interface TrackedTextField : UITextField +@property int lastPosition; +@property UITextPosition* lockPos; +@end + +@implementation TrackedTextField +- (UITextPosition *)closestPositionToPoint:(CGPoint)point { + UITextPosition *position = [super closestPositionToPoint:point]; + int start = [self offsetFromPosition:self.beginningOfDocument toPosition:position]; + if (start - self.lastPosition != 0) { + int key = (start - self.lastPosition > 0) ? GLFW_KEY_DPAD_RIGHT : GLFW_KEY_DPAD_LEFT; + CallbackBridge_nativeSendKey(key, 0, 1, 0); + CallbackBridge_nativeSendKey(key, 0, 0, 0); + } + self.lastPosition = start; + return [self positionFromPosition:self.beginningOfDocument offset:clamp(start, 20, self.text.length - 20)]; +} + +- (void)setText:(NSString *)text { + [super setText:text]; + self.lockPos = [self positionFromPosition:self.beginningOfDocument offset:20]; + self.selectedTextRange = [self textRangeFromPosition:self.lockPos toPosition:self.lockPos]; +} +@end + + +#pragma mark Class SurfaceViewController @interface SurfaceViewController () { } +@property TrackedTextField *inputView; @property(nonatomic, strong) NSMutableDictionary* cc_dictionary; @property(nonatomic, strong) NSMutableArray* swipeableButtons; @property(nonatomic, strong) NSMutableArray* togglableVisibleButtons; @@ -193,21 +224,19 @@ const void * _CGDataProviderGetBytePointerCallbackOSMESA(void *info) { [self.view addSubview:self.mousePointerView]; #ifndef DEBUG_VISIBLE_TEXT_FIELD - inputView = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 1, 1)]; - inputView.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.0f]; + self.inputView = [[TrackedTextField alloc] initWithFrame:CGRectMake(0, -1, 1, 1)]; #else - inputView = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 1, 1)]; - inputView.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.5f]; + self.inputView = [[TrackedTextField alloc] initWithFrame:CGRectMake(5 * 2 + 160.0, 5 * 2 + 30.0, 200.0, 30.0)]; + self.inputView.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.5f]; inputLengthView = [[UILabel alloc] initWithFrame:CGRectMake(5 * 2 + 80.0, 5 * 2 + 30.0, 80.0, 30.0)]; inputLengthView.text = @"length=?"; inputLengthView.backgroundColor = [UIColor colorWithWhite:1.0f alpha:0.6f]; [self.view addSubview:inputLengthView]; #endif - inputView.delegate = self; - [inputView addTarget:self action:@selector(inputViewDidChange) forControlEvents:UIControlEventEditingChanged]; - [inputView addTarget:self action:@selector(inputViewDidClick) forControlEvents:UIControlEventTouchDown]; - + self.inputView.delegate = self; + self.inputView.lastPosition = 20; + [self.inputView addTarget:self action:@selector(inputViewDidChange) forControlEvents:UIControlEventEditingChanged]; NSString *controlFilePath = [NSString stringWithFormat:@"%s/%@", getenv("POJAV_PATH_CONTROL"), (NSString *)getPreference(@"default_ctrl")]; @@ -233,9 +262,6 @@ const void * _CGDataProviderGetBytePointerCallbackOSMESA(void *info) { BOOL isToggleCtrlBtn = NO; for (int i = 0; i < 4; i++) { int keycodeInt = [button.properties[@"keycodes"][i] intValue]; - if (keycodeInt == SPECIALBTN_KEYBOARD) { - inputView.frame = button.frame; - } if (keycodeInt == SPECIALBTN_TOGGLECTRL) { isToggleCtrlBtn = YES; } @@ -281,7 +307,7 @@ const void * _CGDataProviderGetBytePointerCallbackOSMESA(void *info) { } } - [self.view addSubview:inputView]; + [self.view addSubview:self.inputView]; [self executebtn_special_togglebtn:0]; @@ -445,7 +471,7 @@ const void * _CGDataProviderGetBytePointerCallbackOSMESA(void *info) { if (shouldTriggerClick == NO) return; if (sender.state == UIGestureRecognizerStateRecognized) { if (currentHotbarSlot == -1) { - inputView.text = INPUT_SPACE_CHAR; + self.inputView.text = INPUT_SPACE_CHAR; inputTextLength = 0; CallbackBridge_nativeSendMouseButton( @@ -503,7 +529,7 @@ const void * _CGDataProviderGetBytePointerCallbackOSMESA(void *info) { } if (sender.state == UIGestureRecognizerStateBegan) { if (currentHotbarSlot == -1) { - inputView.text = INPUT_SPACE_CHAR; + self.inputView.text = INPUT_SPACE_CHAR; inputTextLength = 0; CallbackBridge_nativeSendMouseButton(GLFW_MOUSE_BUTTON_LEFT, 1, 0); @@ -518,7 +544,7 @@ const void * _CGDataProviderGetBytePointerCallbackOSMESA(void *info) { || sender.state == UIGestureRecognizerStateEnded) { if (currentHotbarSlot == -1) { - inputView.text = INPUT_SPACE_CHAR; + self.inputView.text = INPUT_SPACE_CHAR; inputTextLength = 0; CallbackBridge_nativeSendMouseButton(GLFW_MOUSE_BUTTON_LEFT, 0, 0); @@ -556,22 +582,28 @@ CallbackBridge_nativeSendKey(GLFW_KEY_Q, 0, 0, 0); #pragma mark - Input view stuff NSString* inputStringBefore; +int inputStringLength = INPUT_FULL_LENGTH; -(void)inputViewDidChange { - if ([inputView.text length] < INPUT_SPACE_LENGTH) { - CallbackBridge_nativeSendKey(GLFW_KEY_BACKSPACE, 0, 1, 0); - CallbackBridge_nativeSendKey(GLFW_KEY_BACKSPACE, 0, 0, 0); - inputView.text = [@" " stringByAppendingString:inputView.text]; - - if (inputTextLength > 0) { - --inputTextLength; + int typedLength = (int)self.inputView.text.length - inputStringLength; + if (typedLength < 0) { + for (int i = 0; i < -typedLength; i++) { + if (self.inputView.text.length < INPUT_FULL_LENGTH) { + self.inputView.text = [@" " stringByAppendingString:self.inputView.text]; + } + CallbackBridge_nativeSendKey(GLFW_KEY_BACKSPACE, 0, 1, 0); + CallbackBridge_nativeSendKey(GLFW_KEY_BACKSPACE, 0, 0, 0); + if (inputTextLength > 0) { + --inputTextLength; + } } #ifdef DEBUG_VISIBLE_TEXT_FIELD - inputLengthView.text = [@"length=" stringByAppendingFormat:@"%i", + inputLengthView.text = [@"length=" stringByAppendingFormat:@"%i", inputTextLength]; #endif - } else if ([inputView.text length] > INPUT_SPACE_LENGTH) { - NSString *newText = [inputView.text substringFromIndex:INPUT_SPACE_LENGTH]; + } else if (typedLength > 0) { + int index = [self.inputView offsetFromPosition:self.inputView.beginningOfDocument toPosition:self.inputView.selectedTextRange.start]; + NSString *newText = [self.inputView.text substringWithRange:NSMakeRange(index - typedLength, typedLength)]; int charLength = (int) [newText length]; for (int i = 0; i < charLength; i++) { // Directly convert unichar to jchar since both are in UTF-16 encoding. @@ -583,30 +615,27 @@ NSString* inputStringBefore; CallbackBridge_nativeSendChar(theChar); } - inputView.text = [inputView.text substringFromIndex:1]; - if (inputTextLength < INPUT_SPACE_LENGTH) { - ++inputTextLength; - } + ++inputTextLength; #ifdef DEBUG_VISIBLE_TEXT_FIELD inputLengthView.text = [@"length=" stringByAppendingFormat:@"%i", inputTextLength]; #endif } - inputStringBefore = inputView.text; - // [inputView.text substringFromIndex:inputTextLength - 1]; + inputStringBefore = self.inputView.text; + // [self.inputView.text substringFromIndex:inputTextLength - 1]; } else { #ifdef DEBUG_VISIBLE_TEXT_FIELD - NSLog(@"Compare \"%@\" vs \"%@\"", inputView.text, inputStringBefore); + NSLog(@"Compare \"%@\" vs \"%@\"", self.inputView.text, inputStringBefore); #endif - for (int i = 0; i < INPUT_SPACE_LENGTH; i++) { - if ([inputView.text characterAtIndex:i] != [inputStringBefore characterAtIndex:i]) { - NSString *inputStringNow = [inputView.text substringFromIndex:i]; + for (int i = 0; i < INPUT_FULL_LENGTH; i++) { + if ([self.inputView.text characterAtIndex:i] != [inputStringBefore characterAtIndex:i]) { + NSString *inputStringNow = [self.inputView.text substringFromIndex:i]; /* - inputView.text = [inputView.text substringToIndex:i]; + self.inputView.text = [self.inputView.text substringToIndex:i]; // self notify [self inputViewDidChange]; - inputView.text = [inputView.text stringByAppendingString:inputStringNow]; + self.inputView.text = [self.inputView.text stringByAppendingString:inputStringNow]; // self notify [self inputViewDidChange]; */ @@ -627,21 +656,13 @@ NSString* inputStringBefore; #ifdef DEBUG_VISIBLE_TEXT_FIELD inputLengthView.text = @"length ="; #endif - inputStringBefore = inputView.text; - // [inputView.text substringFromIndex:inputTextLength - 1]; + inputStringBefore = self.inputView.text; } - // Reset to default value - // inputView.text = INPUT_SPACE_CHAR; -} + inputStringLength = (int)self.inputView.text.length; --(void)inputViewDidClick { - // Zero the input field so user will no longer able to select text inside. -#ifndef DEBUG_VISIBLE_TEXT_FIELD - inputView.alpha = 0.0f; -#endif - inputView.text = INPUT_SPACE_CHAR; - inputTextLength = 0; + // Reset to default value + // self.inputView.text = INPUT_SPACE_CHAR; } -(BOOL)textFieldShouldReturn:(UITextField *)textField { @@ -662,9 +683,21 @@ int currentVisibility = 1; switch (keycode) { case SPECIALBTN_KEYBOARD: if (held == 0) { - [inputView resignFirstResponder]; - inputView.alpha = 1.0f; - inputView.text = @""; + if (self.inputView.isFirstResponder) { + [self.inputView resignFirstResponder]; + self.inputView.alpha = 1.0f; + self.inputView.text = @""; + } else { + [self.inputView becomeFirstResponder]; + // Empty the input field so that user will no longer able to select text inside. + self.inputView.text = INPUT_SPACE_CHAR; + // Trigger a text field cursor lock + +#ifndef DEBUG_VISIBLE_TEXT_FIELD + self.inputView.alpha = 0.0f; +#endif + inputTextLength = 0; + } } break; @@ -766,7 +799,7 @@ CallbackBridge_nativeSendKey(keycode, 0, held, 0); } #ifndef DEBUG_VISIBLE_TEXT_FIELD - inputView.hidden = currentVisibility; + self.inputView.hidden = currentVisibility; #endif } } @@ -787,11 +820,11 @@ int touchesMovedCount; } CGPoint locationInView = [touch locationInView:self.view]; CGFloat screenScale = [[UIScreen mainScreen] scale]; - int slot = callback_SurfaceViewController_touchHotbar(locationInView.x * screenScale, locationInView.y * screenScale); - if ([self isTouchInactive:self.hotbarTouch] && slot != -1) { + currentHotbarSlot = callback_SurfaceViewController_touchHotbar(locationInView.x * screenScale, locationInView.y * screenScale); + if ([self isTouchInactive:self.hotbarTouch] && currentHotbarSlot != -1) { self.hotbarTouch = touch; } - if ([self isTouchInactive:self.primaryTouch] && slot == -1) { + if ([self isTouchInactive:self.primaryTouch] && currentHotbarSlot == -1) { self.primaryTouch = touch; } [self sendTouchEvent:touch withUIEvent:event withEvent:ACTION_DOWN]; diff --git a/Natives/input_bridge_v3.m b/Natives/input_bridge_v3.m index 72923977..3638557f 100644 --- a/Natives/input_bridge_v3.m +++ b/Natives/input_bridge_v3.m @@ -209,8 +209,8 @@ int callback_SurfaceViewController_touchHotbar(CGFloat x, CGFloat y) { return -1; } - int barHeight = mcscale(20); - int barWidth = mcscale(180); + int barHeight = mcscale(40); // 20 + int barWidth = mcscale(360); // 180 int barX = (savedWidth / 2) - (barWidth / 2); int barY = savedHeight - barHeight; if (x < barX || x >= barX + barWidth || y < barY || y >= barY + barHeight) {