From a232f32ea46c99417c21669167c549160a5d9a94 Mon Sep 17 00:00:00 2001 From: jahorton Date: Fri, 26 Feb 2021 10:32:48 +0700 Subject: [PATCH] chore: reverts right-delete changes --- .../java/com/tavultesoft/kmea/KMManager.java | 27 +-- .../worker/model-compositor.ts | 13 -- .../Classes/InputViewController.swift | 176 ++++-------------- .../Classes/KeymanWebDelegate.swift | 2 +- .../Classes/KeymanWebViewController.swift | 8 +- .../KMEI/KeymanEngine/Classes/TextView.swift | 7 + 6 files changed, 51 insertions(+), 182 deletions(-) diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java index 6a0eecac0c..dce7a688ef 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/KMManager.java @@ -36,7 +36,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; -import android.text.Editable; import android.text.InputType; import android.util.Log; import android.view.KeyEvent; @@ -2350,6 +2349,10 @@ public final class KMManager { // This annotation is required in Jelly Bean and later: @JavascriptInterface public void insertText(final int dn, final String s, final int dr) { + if(dr != 0) { + Log.d(TAG, "Right deletions requested but are not presently supported by the in-app keyboard."); + } + Handler mainLoop = new Handler(Looper.getMainLooper()); mainLoop.post(new Runnable() { public void run() { @@ -2381,28 +2384,6 @@ public final class KMManager { start = temp; } - // As we depend upon the caret's current position for the in-app implementation, - // we need to perform right-deletions BEFORE left-deletions & text insertion. - if(dr > 0) { - for (int i = 0; i < dr; i++) { - Editable context = textView.getText(); - CharSequence chars = context.subSequence(end, context.length()); - if (chars != null && chars.length() > 0) { - char c = chars.charAt(0); - InAppKeyboardShouldIgnoreTextChange = true; - InAppKeyboardShouldIgnoreSelectionChange = true; - if (Character.isHighSurrogate(c)) { - textView.getText().delete(start, end+2); - } else { - textView.getText().delete(start, end+1); - } - - start = textView.getSelectionStart(); - end = textView.getSelectionEnd(); - } - } - } - if (dn <= 0) { if (start == end) { if (!s.isEmpty() && s.charAt(0) == '\n') { diff --git a/common/predictive-text/worker/model-compositor.ts b/common/predictive-text/worker/model-compositor.ts index 213164a014..ef03d5b706 100644 --- a/common/predictive-text/worker/model-compositor.ts +++ b/common/predictive-text/worker/model-compositor.ts @@ -341,12 +341,6 @@ class ModelCompositor { // A predictive text default (on iOS, at least) - immediately wordbreak // on suggestions accepted mid-word. suggestion.transform.insert += punctuation.insertAfterWord; - - // // Unfortunately, we can't quite enable the next two lines in iOS yet; there's an - // // odd issue with its textDocumentProxy that gives us the wrong results. - // - // let righthandSplit = tokenization.right[0]; - // suggestion.transform.deleteRight = (suggestion.transform.deleteRight || 0) + righthandSplit.kmwLength(); } } @@ -458,7 +452,6 @@ class ModelCompositor { // Step 1: generate and save the reversion's Transform. let sourceTransform = suggestion.transform; let deletedLeftChars = context.left.kmwSubstr(-sourceTransform.deleteLeft, sourceTransform.deleteLeft); - let deletedRightChars = context.right ? context.right.kmwSubstr(0, sourceTransform.deleteRight || 0) : ''; let insertedLength = sourceTransform.insert.kmwLength(); let reversionTransform: Transform = { @@ -466,18 +459,12 @@ class ModelCompositor { deleteLeft: insertedLength }; - let postCaretReversionTransform: Transform = { - insert: deletedRightChars, - deleteLeft: 0 - } - // Step 2: building the proper 'displayAs' string for the Reversion let postContext = context; if(postTransform) { // The code above restores the state to the context at the time the `Suggestion` was created. // `postTransform` handles any missing context that came later. reversionTransform = models.buildMergedTransform(reversionTransform, postTransform); - reversionTransform = models.buildMergedTransform(reversionTransform, postCaretReversionTransform); // Now that we've built the reversion based upon the Suggestion's original context, // we manipulate it in order to get a proper 'displayAs' string. diff --git a/ios/engine/KMEI/KeymanEngine/Classes/InputViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/InputViewController.swift index cd4092140f..35cabcb4b3 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/InputViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/InputViewController.swift @@ -168,7 +168,6 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { private var keymanWeb: KeymanWebViewController private var swallowBackspaceTextChange: Bool = false - private var swallowContextChangeCount: Int = 0 open class var isPortrait: Bool { return UIScreen.main.bounds.width < UIScreen.main.bounds.height @@ -322,13 +321,6 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { self.swallowBackspaceTextChange = false return } - - // Currently motivated by the need to shift caret position to perform - // right-deletions. - if self.swallowContextChangeCount > 0 { - self.swallowContextChangeCount -= 1 - return - } let contextBeforeInput = textDocumentProxy.documentContextBeforeInput ?? "" let contextAfterInput = textDocumentProxy.documentContextAfterInput ?? "" @@ -346,7 +338,7 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { // We should NOT call .resetContext() here for this reason. } - func insertText(_ keymanWeb: KeymanWebViewController, numCharsToLeftDelete: Int, newText: String, numCharsToRightDelete: Int) { + func insertText(_ keymanWeb: KeymanWebViewController, numCharsToDelete: Int, newText: String) { if keymanWeb.isSubKeysMenuVisible { return } @@ -359,7 +351,9 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { perform(#selector(self.enableInputClickSound), with: nil, afterDelay: 0.1) } - if numCharsToLeftDelete <= 0 { + if numCharsToDelete <= 0 { + textDocumentProxy.insertText(newText) + // A full-context deletion will report numCharsToDelete == 0 and won't // otherwise delete selected text. if #available(iOSApplicationExtension 11.0, *) { @@ -369,143 +363,43 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { } } } - } else { - for _ in 0.. 1 { - if !InputViewController.isSurrogate(oldContext.utf16.last!) { - let lowerIndex = oldContext.utf16.index(oldContext.utf16.startIndex, - offsetBy: newContext.utf16.count) - let upperIndex = oldContext.utf16.index(lowerIndex, offsetBy: unitsDeleted - 1) - let remnant = String(oldContext.utf16[lowerIndex.. 1 { + if !InputViewController.isSurrogate(oldContext.utf16.last!) { + let lowerIndex = oldContext.utf16.index(oldContext.utf16.startIndex, + offsetBy: newContext.utf16.count) + let upperIndex = oldContext.utf16.index(lowerIndex, offsetBy: unitsDeleted - 1) + textDocumentProxy.insertText(String(oldContext[lowerIndex.. 0 { - // Should never change throughout the next loop; right-deletions only! - let oldLeftContext = textDocumentProxy.documentContextBeforeInput ?? "" - var pointsToDelete = numCharsToRightDelete - while pointsToDelete > 0 { - let oldContext = textDocumentProxy.documentContextAfterInput ?? "" - self.swallowContextChangeCount += 1 - // Asynchronously triggers a context change. - textDocumentProxy.adjustTextPosition(byCharacterOffset: 1) - let newContext = textDocumentProxy.documentContextAfterInput ?? "" - - var failsafeCount = 0 - while (textDocumentProxy.documentContextBeforeInput ?? "" != oldLeftContext) - && failsafeCount < numCharsToRightDelete { // mild breakage > locked kbd - // While adjustTextPosition makes cluster-based jumps... - // deleteBackward does not. So, we 'force' it. - textDocumentProxy.deleteBackward() - failsafeCount += 1 - } - - // Determine the removed codepoint count. Also, find the index - // of the first character that shouldn't be removed, if possible. - let unitsDeleted = oldContext.utf16.count - newContext.utf16.count - var pointsDeleted = 0 - - // Our current index within the deleted context. - var deletedIndex = oldContext.utf16.startIndex - // Marks the first index we wish to NOT remove. - var remnantIndex: String.Index? = nil - // Marks the first index that was NOT removed by deleteBackward. - let undeletedStartIndex = oldContext.utf16.index(deletedIndex, offsetBy: unitsDeleted) - - while deletedIndex < undeletedStartIndex { - if(InputViewController.isSurrogate(oldContext.utf16[deletedIndex])) { - // Check - is it truly a surrogate pair? - let pairedIndex = oldContext.utf16.index(after: deletedIndex) - if(InputViewController.isSurrogate(oldContext.utf16[pairedIndex])) { - // If so, pre-emptively increase the index - the pair will count - // as a single character as a result. - deletedIndex = pairedIndex - } - } - pointsDeleted += 1 - deletedIndex = oldContext.utf16.index(after: deletedIndex) - - // Intended end of deletion found! - if pointsDeleted == pointsToDelete { - remnantIndex = deletedIndex - break // There's nothing to be gained by further loop iterations. - } - } - - if let remnantIndex = remnantIndex, remnantIndex < undeletedStartIndex { - // We need to restore some of the deleted text! - let remnant = String(oldContext.utf16[remnantIndex.. oldLeftContext.count) { - textDocumentProxy.adjustTextPosition(byCharacterOffset: -remnant.count) - leftContext = textDocumentProxy.documentContextBeforeInput ?? "" - } - - // FAILSAFE, non-ideal behavior below. - // - // It's probably (?) better to keep the caret at the end of a cluster that - // previously started before the caret, even if right-deletions add new - // characters to the cluster. I think. - // - // Khmer example: if right-deletes leave a joeung-S that can attach - // to a main consonant to the left of my cursor, it will 'snap' into - // a cluster. It'd be more natural to have that cluster on the left, - // so I can add more chars to the cluster if desired. - // - // It's also easier to immediately backspace the extra chars if needed - // this way, rather than forcing a caret reposition. - if(leftContext.count < oldLeftContext.count) { - log.debug("Could not reposition caret perfectly after a right-delete!") - textDocumentProxy.adjustTextPosition(byCharacterOffset: 1) - } - } - - // We've handled a set of code points; make sure we mark our progress! - pointsToDelete -= pointsDeleted - } - } } func menuKeyUp(_ keymanWeb: KeymanWebViewController) { @@ -657,9 +551,7 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { } func resetContext() { - if self.swallowContextChangeCount <= 0 { - keymanWeb.resetContext() - } + keymanWeb.resetContext() } internal func setSentryState(enabled: Bool) { diff --git a/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebDelegate.swift b/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebDelegate.swift index f931b25bfa..a176a2d1ed 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebDelegate.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebDelegate.swift @@ -16,7 +16,7 @@ protocol KeymanWebDelegate: class { /// - Parameters: /// - numCharsToDelete: The number of UTF-16 code units to delete before inserting the new text. /// - newText: The string to insert. - func insertText(_ keymanWeb: KeymanWebViewController, numCharsToLeftDelete: Int, newText: String, numCharsToRightDelete: Int) + func insertText(_ keymanWeb: KeymanWebViewController, numCharsToDelete: Int, newText: String) /// - Parameters: func beep(_ keymanWeb: KeymanWebViewController) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebViewController.swift index 113dc6c861..e20c4f7112 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/KeymanWebViewController.swift @@ -388,14 +388,16 @@ extension KeymanWebViewController: WKScriptMessageHandler { let dn = Int(fragment[dnRange.upperBound..