From c683872e26c49991676ac1235796d30d26344a71 Mon Sep 17 00:00:00 2001 From: Andrew Tavis McAllister Date: Mon, 5 Sep 2022 00:15:20 +0200 Subject: [PATCH] #188 #194 autocomplete bug fix and autosuggest placeholders --- Keyboards/KeyboardsBase/Extensions.swift | 6 +- .../KeyboardsBase/InterfaceVariables.swift | 8 ++ .../KeyboardViewController.swift | 96 +++++++++++++------ .../CommandVariables.swift | 3 +- .../French/FRInterfaceVariables.swift | 1 + .../German/DEInterfaceVariables.swift | 1 + .../Italian/ITInterfaceVariables.swift | 1 + .../Portuguese/PTInterfaceVariables.swift | 1 + .../Russian/RUInterfaceVariables.swift | 1 + .../Spanish/ESInterfaceVariables.swift | 1 + .../Swedish/SVInterfaceVariables.swift | 1 + 11 files changed, 87 insertions(+), 33 deletions(-) diff --git a/Keyboards/KeyboardsBase/Extensions.swift b/Keyboards/KeyboardsBase/Extensions.swift index 4010ad26..52974484 100644 --- a/Keyboards/KeyboardsBase/Extensions.swift +++ b/Keyboards/KeyboardsBase/Extensions.swift @@ -55,13 +55,17 @@ extension String { return self == self.uppercased() } + var isCaptalized: Bool { + return self == prefix(1).uppercased() + self.lowercased().dropFirst() + } + func count(of char: Character) -> Int { return reduce(0) { $1 == char ? $0 + 1 : $0 } } - func capitalizingFirstLetter() -> String { + func capitalize() -> String { return prefix(1).uppercased() + self.lowercased().dropFirst() } } diff --git a/Keyboards/KeyboardsBase/InterfaceVariables.swift b/Keyboards/KeyboardsBase/InterfaceVariables.swift index c398356a..f6efa346 100644 --- a/Keyboards/KeyboardsBase/InterfaceVariables.swift +++ b/Keyboards/KeyboardsBase/InterfaceVariables.swift @@ -60,10 +60,17 @@ enum CommandState { case invalid } +/// States of the keyboard corresponding to which auto actions should be presented. +enum AutoActionState { + case complete + case suggest +} + // Baseline state variables. var keyboardState: KeyboardState = .letters var shiftButtonState: ShiftButtonState = .normal var commandState: CommandState = .idle +var autoActionState: AutoActionState = .suggest // Variables and functions to determine display parameters. struct DeviceType { @@ -269,6 +276,7 @@ func setENKeyboardLayout() { currencySymbolAlternates = dollarAlternateKeys spaceBar = "space" invalidCommandMsg = "Not in Wikidata" + baseAutosuggestions = ["I", "I'm", "we"] translateKeyLbl = "Translate" translatePrompt = commandPromptSpacing + "en -› \(getControllerLanguageAbbr()): " diff --git a/Keyboards/KeyboardsBase/KeyboardViewController.swift b/Keyboards/KeyboardsBase/KeyboardViewController.swift index 0946c8a1..8a196c02 100644 --- a/Keyboards/KeyboardsBase/KeyboardViewController.swift +++ b/Keyboards/KeyboardsBase/KeyboardViewController.swift @@ -200,6 +200,13 @@ class KeyboardViewController: UIInputViewController { } } + /// Hides the partitions for autocomplete and autosuggest. + /// Note: this function is called during command mode when the commandBar is viewable and the Scribe key state. + func hideAutoActionPartitions() { + leftAutoPartition.backgroundColor = .clear + rightAutoPartition.backgroundColor = .clear + } + /// Generates the array for the three autocomplete words. func getAutocompleteWords() { completionWords = [" ", " ", " "] @@ -207,6 +214,12 @@ class KeyboardViewController: UIInputViewController { if let inString = proxy.documentContextBeforeInput { // To only focus on the current word as prefix in autocomplete. currentPrefix = inString.replacingOccurrences(of: pastStringInTextProxy, with: "") + + // Post commands pastStringInTextProxy is "", so take last word. + if currentPrefix.contains(" ") { + currentPrefix = currentPrefix.components(separatedBy: " ").last ?? "" + } + let stringOptions = autocompleteWords.filter { item in return item.lowercased().hasPrefix(currentPrefix.lowercased()) } @@ -215,43 +228,49 @@ class KeyboardViewController: UIInputViewController { if stringOptions.count <= 3 { while i < stringOptions.count { if shiftButtonState == .caps { - // Capital autocomplete if the user starts typing capitalized. completionWords[i] = stringOptions[i].uppercased() + } else if currentPrefix.isCaptalized { + completionWords[i] = stringOptions[i].capitalize() } else { - completionWords[i] = currentPrefix == currentPrefix.capitalized ? stringOptions[i].capitalizingFirstLetter() : stringOptions[i] + completionWords[i] = stringOptions[i] } i += 1 } } else { - while i < 3 { - if shiftButtonState == .caps { - // Capital autocomplete if the user starts typing capitalized. - completionWords[i] = stringOptions[i].uppercased() - } else { - completionWords[i] = currentPrefix == currentPrefix.capitalized ? stringOptions[i].capitalizingFirstLetter() : stringOptions[i] - } - i += 1 + while i < 3 { + if shiftButtonState == .caps { + completionWords[i] = stringOptions[i].uppercased() + } else if currentPrefix.isCaptalized { + completionWords[i] = stringOptions[i].capitalize() + } else { + completionWords[i] = stringOptions[i] } + i += 1 + } } } else { - getDefaultAutoCompleteWords(autocompleteWords) + getDefaultAutoSuggestions() } } else { - // For getting words on launch. When the user has not typed anything in the proxy. - getDefaultAutoCompleteWords(autocompleteWords) + // For getting words on launch when the user hasn't typed anything in the proxy. + getDefaultAutoSuggestions() } } /// Suggests words on launch before the user starts typing. - /// Note: replace this section when we add the most common used words. - func getDefaultAutoCompleteWords(_ keys: [String]) { + func getDefaultAutoSuggestions() { var i = 0 - var threeWords = [String]() + completionWords = [String]() while i < 3 { - threeWords.append(keys[i]) - i += 1 + if shiftButtonState == .shift { + completionWords.append(baseAutosuggestions[i].capitalize()) + } else if shiftButtonState == .caps { + completionWords.append(baseAutosuggestions[i].uppercased()) + } else { + completionWords.append(baseAutosuggestions[i]) + } + i += 1 } - completionWords = threeWords } /// Clears the text proxy when inserting using an auto action. @@ -287,13 +306,6 @@ class KeyboardViewController: UIInputViewController { } } - /// Hides the partitions for autocomplete and autosuggest. - /// Note: this function is called during command mode when the commandBar is viewable and the Scribe key state. - func hideAutoActionPartitions() { - leftAutoPartition.backgroundColor = .clear - rightAutoPartition.backgroundColor = .clear - } - // The background for the Scribe command elements. @IBOutlet var commandBackground: UILabel! func setCommandBackground() { @@ -365,7 +377,11 @@ class KeyboardViewController: UIInputViewController { /// Sets up command buttons to execute autocomplete and autosuggest. func conditionallySetAutoActionBtns() { - getAutocompleteWords() + if autoActionState == .suggest { + getDefaultAutoSuggestions() + } else { + getAutocompleteWords() + } if commandState == .idle { deactivateBtn(btn: translateKey) deactivateBtn(btn: conjugateKey) @@ -951,7 +967,10 @@ class KeyboardViewController: UIInputViewController { commandBar.text = "" commandBar.hide() - conditionallySetAutoActionBtns() + // Set autosuggestions on keyboard's first load. + if keyboardLoad == true { + conditionallySetAutoActionBtns() + } } } @@ -1212,9 +1231,6 @@ class KeyboardViewController: UIInputViewController { doubleSpacePeriodPossible = false } - // Reset the Russian verbs view after a selection. - ruConjugationState = .present - switch originalKey { case "Scribe": if proxy.selectedText != nil && [.idle, .select, .alreadyPlural, .invalid].contains(commandState) { // annotate word @@ -1313,45 +1329,56 @@ class KeyboardViewController: UIInputViewController { case "firstPersonSingular": returnConjugation(keyPressed: sender, requestedTense: tenseFPS) + autoActionState = .suggest loadKeys() case "secondPersonSingular": returnConjugation(keyPressed: sender, requestedTense: tenseSPS) + autoActionState = .suggest loadKeys() case "thirdPersonSingular": returnConjugation(keyPressed: sender, requestedTense: tenseTPS) + autoActionState = .suggest loadKeys() case "firstPersonPlural": returnConjugation(keyPressed: sender, requestedTense: tenseFPP) + autoActionState = .suggest loadKeys() case "secondPersonPlural": returnConjugation(keyPressed: sender, requestedTense: tenseSPP) + autoActionState = .suggest loadKeys() case "thirdPersonPlural": returnConjugation(keyPressed: sender, requestedTense: tenseTPP) + autoActionState = .suggest loadKeys() case "conjugateTopLeft": returnConjugation(keyPressed: sender, requestedTense: tenseTopLeft) + autoActionState = .suggest loadKeys() case "conjugateTopRight": returnConjugation(keyPressed: sender, requestedTense: tenseTopRight) + autoActionState = .suggest loadKeys() case "conjugateBottomLeft": returnConjugation(keyPressed: sender, requestedTense: tenseBottomLeft) + autoActionState = .suggest loadKeys() case "conjugateBottomRight": returnConjugation(keyPressed: sender, requestedTense: tenseBottomRight) + autoActionState = .suggest loadKeys() case "AutoAction1": + autoActionState = .suggest clearPrefixFromTextFieldProxy() proxy.insertText(translateKey.titleLabel?.text ?? "") proxy.insertText(" ") @@ -1374,6 +1401,7 @@ class KeyboardViewController: UIInputViewController { nounAnnotationsToDisplay = 0 case "AutoAction2": + autoActionState = .suggest clearPrefixFromTextFieldProxy() proxy.insertText(conjugateKey.titleLabel?.text ?? "") proxy.insertText(" ") @@ -1396,6 +1424,7 @@ class KeyboardViewController: UIInputViewController { nounAnnotationsToDisplay = 0 case "AutoAction3": + autoActionState = .suggest clearPrefixFromTextFieldProxy() proxy.insertText(pluralKey.titleLabel?.text ?? "") proxy.insertText(" ") @@ -1443,10 +1472,12 @@ class KeyboardViewController: UIInputViewController { clearCommandBar() // Inserting the placeholder when commandBar text is deleted. + autoActionState = .complete commandBar.conditionallyAddPlaceholder() conditionallySetAutoActionBtns() case spaceBar: + autoActionState = .suggest commandBar.conditionallyRemovePlaceholder() if ![.translate, .conjugate, .plural].contains(commandState) { proxy.insertText(" ") @@ -1537,6 +1568,7 @@ class KeyboardViewController: UIInputViewController { commandBar.textColor = keyCharColor return } else if [.translate, .plural].contains(commandState) { // functional commands above + autoActionState = .suggest commandState = .idle clearCommandBar() autoCapAtStartOfProxy() @@ -1596,6 +1628,7 @@ class KeyboardViewController: UIInputViewController { capsLockPossible = true default: + autoActionState = .complete commandBar.conditionallyRemovePlaceholder() if shiftButtonState == .shift { shiftButtonState = .normal @@ -1664,6 +1697,7 @@ class KeyboardViewController: UIInputViewController { shiftButtonState = .caps loadKeys() clearCommandBar() + conditionallySetAutoActionBtns() } // To make sure that the user can still use the double space period shortcut after numbers and symbols. diff --git a/Keyboards/KeyboardsBase/ScribeFunctionality/CommandVariables.swift b/Keyboards/KeyboardsBase/ScribeFunctionality/CommandVariables.swift index fae91cbb..fedbf488 100644 --- a/Keyboards/KeyboardsBase/ScribeFunctionality/CommandVariables.swift +++ b/Keyboards/KeyboardsBase/ScribeFunctionality/CommandVariables.swift @@ -21,7 +21,8 @@ let prepositions = loadJSONToDict(filename: "prepositions") // Words that should not be included in autocomplete should be added to the string below. let autocompleteWords = nouns!.keys.filter( { $0.rangeOfCharacter(from: CharacterSet(charactersIn: "1234567890-")) == nil } -).sorted() +).sorted{$0.caseInsensitiveCompare($1) == .orderedAscending} +var baseAutosuggestions = [String]() var currentPrefix: String = "" var pastStringInTextProxy: String = "" diff --git a/Keyboards/LanguageKeyboards/French/FRInterfaceVariables.swift b/Keyboards/LanguageKeyboards/French/FRInterfaceVariables.swift index 0256b683..5c4e4de1 100644 --- a/Keyboards/LanguageKeyboards/French/FRInterfaceVariables.swift +++ b/Keyboards/LanguageKeyboards/French/FRInterfaceVariables.swift @@ -109,6 +109,7 @@ func setFRKeyboardLayout() { currencySymbolAlternates = euroAlternateKeys spaceBar = "espace" invalidCommandMsg = "Pas dans Wikidata" + baseAutosuggestions = ["je", "il", "le"] translateKeyLbl = "Traduire" translatePlaceholder = "Entrez un mot" diff --git a/Keyboards/LanguageKeyboards/German/DEInterfaceVariables.swift b/Keyboards/LanguageKeyboards/German/DEInterfaceVariables.swift index d971fbe3..24861dad 100644 --- a/Keyboards/LanguageKeyboards/German/DEInterfaceVariables.swift +++ b/Keyboards/LanguageKeyboards/German/DEInterfaceVariables.swift @@ -115,6 +115,7 @@ func setDEKeyboardLayout() { currencySymbolAlternates = euroAlternateKeys spaceBar = "Leerzeichen" invalidCommandMsg = "Nicht in Wikidata" + baseAutosuggestions = ["ich", "die", "das"] translateKeyLbl = "Übersetzen" translatePlaceholder = "Wort eingeben" diff --git a/Keyboards/LanguageKeyboards/Italian/ITInterfaceVariables.swift b/Keyboards/LanguageKeyboards/Italian/ITInterfaceVariables.swift index 3c818156..b06e4c15 100644 --- a/Keyboards/LanguageKeyboards/Italian/ITInterfaceVariables.swift +++ b/Keyboards/LanguageKeyboards/Italian/ITInterfaceVariables.swift @@ -109,6 +109,7 @@ func setITKeyboardLayout() { currencySymbolAlternates = euroAlternateKeys spaceBar = "spazio" invalidCommandMsg = "Non in Wikidata" + baseAutosuggestions = ["ho", "non", "ma"] translateKeyLbl = "Tradurre" translatePlaceholder = "Inserisci una parola" diff --git a/Keyboards/LanguageKeyboards/Portuguese/PTInterfaceVariables.swift b/Keyboards/LanguageKeyboards/Portuguese/PTInterfaceVariables.swift index d848ce97..d405fb17 100644 --- a/Keyboards/LanguageKeyboards/Portuguese/PTInterfaceVariables.swift +++ b/Keyboards/LanguageKeyboards/Portuguese/PTInterfaceVariables.swift @@ -107,6 +107,7 @@ func setPTKeyboardLayout() { currencySymbolAlternates = dollarAlternateKeys spaceBar = "espaço" invalidCommandMsg = "Não está no Wikidata" + baseAutosuggestions = ["o", "a", "eu"] translateKeyLbl = "Traduzir" translatePlaceholder = "Digite uma palavra" diff --git a/Keyboards/LanguageKeyboards/Russian/RUInterfaceVariables.swift b/Keyboards/LanguageKeyboards/Russian/RUInterfaceVariables.swift index d8414df6..db14a723 100644 --- a/Keyboards/LanguageKeyboards/Russian/RUInterfaceVariables.swift +++ b/Keyboards/LanguageKeyboards/Russian/RUInterfaceVariables.swift @@ -97,6 +97,7 @@ func setRUKeyboardLayout() { currencySymbolAlternates = roubleAlternateKeys spaceBar = "Пробел" invalidCommandMsg = "Нет в Викиданных" + baseAutosuggestions = ["я", "а", "в"] translateKeyLbl = "Перевести" translatePlaceholder = "Введите слово" diff --git a/Keyboards/LanguageKeyboards/Spanish/ESInterfaceVariables.swift b/Keyboards/LanguageKeyboards/Spanish/ESInterfaceVariables.swift index 7b30b08e..332d9856 100644 --- a/Keyboards/LanguageKeyboards/Spanish/ESInterfaceVariables.swift +++ b/Keyboards/LanguageKeyboards/Spanish/ESInterfaceVariables.swift @@ -111,6 +111,7 @@ func setESKeyboardLayout() { currencySymbolAlternates = dollarAlternateKeys spaceBar = "espacio" invalidCommandMsg = "No en Wikidata" + baseAutosuggestions = ["el", "la", "no"] translateKeyLbl = "Traducir" translatePlaceholder = "Ingrese una palabra" diff --git a/Keyboards/LanguageKeyboards/Swedish/SVInterfaceVariables.swift b/Keyboards/LanguageKeyboards/Swedish/SVInterfaceVariables.swift index 556b2bf5..7f3869b9 100644 --- a/Keyboards/LanguageKeyboards/Swedish/SVInterfaceVariables.swift +++ b/Keyboards/LanguageKeyboards/Swedish/SVInterfaceVariables.swift @@ -113,6 +113,7 @@ func setSVKeyboardLayout() { currencySymbolAlternates = kronaAlternateKeys spaceBar = "mellanslag" invalidCommandMsg = "Inte i Wikidata" + baseAutosuggestions = ["jag", "det", "men"] translateKeyLbl = "Översätt" translatePlaceholder = "Ange ett ord"