SDL: Properly backspace over text that was entered when autocorrect updates text with the iPhone on-screen keyboard

From 28572702bfc77f799ccb5eddd3ae5982f402f042 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 30 Sep 2022 17:25:57 -0700
Subject: [PATCH] Properly backspace over text that was entered when
 autocorrect updates text with the iPhone on-screen keyboard

---
 Android.mk                                |  0
 src/video/uikit/SDL_uikitviewcontroller.m | 51 ++++++++++++++---------
 2 files changed, 31 insertions(+), 20 deletions(-)
 mode change 100644 => 100755 Android.mk

diff --git a/Android.mk b/Android.mk
old mode 100644
new mode 100755
diff --git a/src/video/uikit/SDL_uikitviewcontroller.m b/src/video/uikit/SDL_uikitviewcontroller.m
index d0dd863f631d..4aa2d287e8f4 100644
--- a/src/video/uikit/SDL_uikitviewcontroller.m
+++ b/src/video/uikit/SDL_uikitviewcontroller.m
@@ -75,7 +75,7 @@ @implementation SDL_uikitviewcontroller {
     BOOL hardwareKeyboard;
     BOOL showingKeyboard;
     BOOL rotatingOrientation;
-    NSString *changeText;
+    NSString *committedText;
     NSString *obligateForBackspace;
 #endif
 }
@@ -263,12 +263,12 @@ - (BOOL)prefersPointerLocked
 /* Set ourselves up as a UITextFieldDelegate */
 - (void)initKeyboard
 {
-    changeText = nil;
     obligateForBackspace = @"                                                                "; /* 64 space */
     textField = [[UITextField alloc] initWithFrame:CGRectZero];
     textField.delegate = self;
     /* placeholder so there is something to delete! */
     textField.text = obligateForBackspace;
+    committedText = textField.text;
 
     /* set UITextInputTrait properties, mostly to defaults */
     textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
@@ -408,21 +408,39 @@ - (void)keyboardWillHide:(NSNotification *)notification
 
 - (void)textFieldTextDidChange:(NSNotification *)notification
 {
-    if (changeText!=nil && textField.markedTextRange == nil)
-    {
-        NSUInteger len = changeText.length;
-        if (len > 0) {
+    if (textField.markedTextRange == nil) {
+        NSUInteger compareLength = SDL_min(textField.text.length, committedText.length);
+        NSUInteger matchLength;
+
+        /* Backspace over characters that are no longer in the string */
+        for (matchLength = 0; matchLength < compareLength; ++matchLength) {
+            if ([committedText characterAtIndex:matchLength] != [textField.text characterAtIndex:matchLength]) {
+                break;
+            }
+        }
+        if (matchLength < committedText.length) {
+            size_t deleteLength = SDL_utf8strlen([[committedText substringFromIndex:matchLength] UTF8String]);
+            while (deleteLength > 0) {
+                /* Send distinct down and up events for each backspace action */
+                SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_BACKSPACE);
+                SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_BACKSPACE);
+                --deleteLength;
+            }
+        }
+
+        if (matchLength < textField.text.length) {
+            NSString *pendingText = [textField.text substringFromIndex:matchLength];
             if (!SDL_HardwareKeyboardKeyPressed()) {
                 /* Go through all the characters in the string we've been sent and
                  * convert them to key presses */
-                int i;
-                for (i = 0; i < len; i++) {
-                    SDL_SendKeyboardUnicodeKey([changeText characterAtIndex:i]);
+                NSUInteger i;
+                for (i = 0; i < pendingText.length; i++) {
+                    SDL_SendKeyboardUnicodeKey([pendingText characterAtIndex:i]);
                 }
             }
-            SDL_SendKeyboardText([changeText UTF8String]);
+            SDL_SendKeyboardText([pendingText UTF8String]);
         }
-        changeText = nil;
+        committedText = textField.text;
     }
 }
 
@@ -463,18 +481,11 @@ - (void)setKeyboardHeight:(int)height
 /* UITextFieldDelegate method.  Invoked when user types something. */
 - (BOOL)textField:(UITextField *)_textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
 {
-    NSUInteger len = string.length;
-    if (len == 0) {
-        changeText = nil;
-        if (textField.markedTextRange == nil) {
-            /* it wants to replace text with nothing, ie a delete */
-            SDL_SendKeyboardKeyAutoRelease(SDL_SCANCODE_BACKSPACE);
-        }
+    if (textField.markedTextRange == nil) {
         if (textField.text.length < 16) {
             textField.text = obligateForBackspace;
+            committedText = textField.text;
         }
-    } else {
-        changeText = string;
     }
     return YES;
 }