From abdddb28bb854822c5ea4bd6364e5fce40363bf2 Mon Sep 17 00:00:00 2001 From: henrikth93 Date: Tue, 2 Dec 2025 21:15:28 +0100 Subject: [PATCH 1/5] Add word-for-word delete function This commit will add word-for-word delete function if you hold the delete button. --- .../KeyboardViewController.swift | 47 ++++++++++++++++++- .../CommandVariables.swift | 1 + Scribe/ParentTableCellModel.swift | 1 + Scribe/SettingsTab/SettingsTableData.swift | 7 +++ .../InfoChildTableViewCell.swift | 14 +++++- 5 files changed, 68 insertions(+), 2 deletions(-) diff --git a/Keyboards/KeyboardsBase/KeyboardViewController.swift b/Keyboards/KeyboardsBase/KeyboardViewController.swift index 2d3f9689..aab89cb0 100644 --- a/Keyboards/KeyboardsBase/KeyboardViewController.swift +++ b/Keyboards/KeyboardsBase/KeyboardViewController.swift @@ -886,7 +886,11 @@ class KeyboardViewController: UIInputViewController { /// Deletes in the proxy or command bar given the current constraints. func handleDeleteButtonPressed() { if [.idle, .selectCommand, .alreadyPlural, .invalid].contains(commandState) { - proxy.deleteBackward() + if wordForWordDeletionIsEnabled() && longPressOnDelete { + deleteWordBackward() + } else { + proxy.deleteBackward() + } } else if [.translate, .conjugate, .plural].contains(commandState) && !(allPrompts.contains(commandBar.text ?? "") || allColoredPrompts.contains(commandBar.attributedText ?? NSAttributedString())) { guard let inputText = commandBar.text, !inputText.isEmpty else { return @@ -897,6 +901,32 @@ class KeyboardViewController: UIInputViewController { backspaceTimer = nil } } + + func deleteWordBackward() { + guard let documentText = proxy.documentContextBeforeInput else { + return + } + + var words = documentText.split(separator: " ").map(String.init) + + guard !words.isEmpty else { + return + } + + words.removeLast() + + let updatedText = words.joined(separator: " ") + + for _ in documentText { + proxy.deleteBackward() + } + + for character in updatedText { + proxy.insertText(String(character)) + } + + proxy.adjustTextPosition(byCharacterOffset: 0) + } // The button used to display Scribe commands and its shadow. @IBOutlet var scribeKey: ScribeKey! @@ -2287,6 +2317,18 @@ class KeyboardViewController: UIInputViewController { return true // return the default value } } + + func wordForWordDeletionIsEnabled() -> Bool { + let langCode = languagesAbbrDict[controllerLanguage] ?? "unknown" + if let userDefaults = UserDefaults(suiteName: "group.be.scri.userDefaultsContainer") { + let dictionaryKey = langCode + "WordForWord" + + return userDefaults.bool(forKey: dictionaryKey) + } else { + return false // return the default value + } + + } // MARK: Button Actions @@ -2993,6 +3035,8 @@ class KeyboardViewController: UIInputViewController { gesture.state = .cancelled commandBar.conditionallyAddPlaceholder() } + //This variable can be used for word-for-word deletion + longPressOnDelete = true // Delete is sped up based on the number of deletes that have been completed. var deleteCount = 0 @@ -3019,6 +3063,7 @@ class KeyboardViewController: UIInputViewController { } else if gesture.state == .ended || gesture.state == .cancelled { backspaceTimer?.invalidate() backspaceTimer = nil + longPressOnDelete = false if let button = gesture.view as? UIButton { button.backgroundColor = specialKeyColor styleDeleteButton(button, isPressed: false) diff --git a/Keyboards/KeyboardsBase/ScribeFunctionality/CommandVariables.swift b/Keyboards/KeyboardsBase/ScribeFunctionality/CommandVariables.swift index 68a95045..7ae6d22d 100644 --- a/Keyboards/KeyboardsBase/ScribeFunctionality/CommandVariables.swift +++ b/Keyboards/KeyboardsBase/ScribeFunctionality/CommandVariables.swift @@ -32,6 +32,7 @@ var previousWord = "" var currentPrefix = "" var pastStringInTextProxy = "" var backspaceTimer: Timer? +var longPressOnDelete: Bool = false var baseAutosuggestions = [String]() var completionWords = [String]() diff --git a/Scribe/ParentTableCellModel.swift b/Scribe/ParentTableCellModel.swift index da4c28ce..3b08a1ca 100644 --- a/Scribe/ParentTableCellModel.swift +++ b/Scribe/ParentTableCellModel.swift @@ -68,6 +68,7 @@ enum UserInteractiveState { case doubleSpacePeriods case autosuggestEmojis case toggleAccentCharacters + case toggleWordForWordDeletion case none } diff --git a/Scribe/SettingsTab/SettingsTableData.swift b/Scribe/SettingsTab/SettingsTableData.swift index 5f361d33..0a8931f5 100644 --- a/Scribe/SettingsTab/SettingsTableData.swift +++ b/Scribe/SettingsTab/SettingsTableData.swift @@ -72,6 +72,13 @@ enum SettingsTableData { hasToggle: true, sectionState: .none(.autosuggestEmojis), shortDescription: NSLocalizedString("app.settings.keyboard.functionality.auto_suggest_emoji_description", value: "Turn on emoji suggestions and completions for more expressive typing.", comment: "") + ), + Section( + sectionTitle: NSLocalizedString("app.word_for_word", value: "Word for word deletion on long press", comment: ""), + hasToggle: true, + sectionState: .none(.toggleWordForWordDeletion), + shortDescription: NSLocalizedString("word for word deletion", + value: "Word for word deletion.", comment: "") ) ], hasDynamicData: nil diff --git a/Scribe/Views/Cells/InfoChildTableViewCell/InfoChildTableViewCell.swift b/Scribe/Views/Cells/InfoChildTableViewCell/InfoChildTableViewCell.swift index de6a7d6d..f91dd728 100644 --- a/Scribe/Views/Cells/InfoChildTableViewCell/InfoChildTableViewCell.swift +++ b/Scribe/Views/Cells/InfoChildTableViewCell/InfoChildTableViewCell.swift @@ -146,7 +146,11 @@ final class InfoChildTableViewCell: UITableViewCell { case .autosuggestEmojis: let dictionaryKey = languageCode + "EmojiAutosuggest" userDefaults.setValue(toggleSwitch.isOn, forKey: dictionaryKey) - + + case .toggleWordForWordDeletion: + let dictionaryKey = languageCode + "WordForWord" + userDefaults.setValue(toggleSwitch.isOn, forKey: dictionaryKey) + case .none: break } @@ -186,6 +190,14 @@ final class InfoChildTableViewCell: UITableViewCell { } else { toggleSwitch.isOn = true // Default value } + + case .toggleWordForWordDeletion: + let dictionaryKey = languageCode + "WordForWord" + if let toggleValue = userDefaults.object(forKey: dictionaryKey) as? Bool { + toggleSwitch.isOn = toggleValue + } else { + toggleSwitch.isOn = false //Default value + } case .none: break } From 781103c278ce20f34ddabc1126f1ecaf244b8e40 Mon Sep 17 00:00:00 2001 From: henrikth93 Date: Tue, 2 Dec 2025 21:28:44 +0100 Subject: [PATCH 2/5] Fix lint issues This commit will fix some lint issues --- Keyboards/KeyboardsBase/KeyboardViewController.swift | 4 ++-- Scribe/SettingsTab/SettingsTableData.swift | 3 +-- .../Cells/InfoChildTableViewCell/InfoChildTableViewCell.swift | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Keyboards/KeyboardsBase/KeyboardViewController.swift b/Keyboards/KeyboardsBase/KeyboardViewController.swift index aab89cb0..591642ae 100644 --- a/Keyboards/KeyboardsBase/KeyboardViewController.swift +++ b/Keyboards/KeyboardsBase/KeyboardViewController.swift @@ -3035,8 +3035,8 @@ class KeyboardViewController: UIInputViewController { gesture.state = .cancelled commandBar.conditionallyAddPlaceholder() } - //This variable can be used for word-for-word deletion - longPressOnDelete = true + + longPressOnDelete = true //This variable can be used for word-for-word deletion // Delete is sped up based on the number of deletes that have been completed. var deleteCount = 0 diff --git a/Scribe/SettingsTab/SettingsTableData.swift b/Scribe/SettingsTab/SettingsTableData.swift index 0a8931f5..b0d63718 100644 --- a/Scribe/SettingsTab/SettingsTableData.swift +++ b/Scribe/SettingsTab/SettingsTableData.swift @@ -77,8 +77,7 @@ enum SettingsTableData { sectionTitle: NSLocalizedString("app.word_for_word", value: "Word for word deletion on long press", comment: ""), hasToggle: true, sectionState: .none(.toggleWordForWordDeletion), - shortDescription: NSLocalizedString("word for word deletion", - value: "Word for word deletion.", comment: "") + shortDescription: NSLocalizedString("word for word deletion", value: "Word for word deletion.", comment: "") ) ], hasDynamicData: nil diff --git a/Scribe/Views/Cells/InfoChildTableViewCell/InfoChildTableViewCell.swift b/Scribe/Views/Cells/InfoChildTableViewCell/InfoChildTableViewCell.swift index f91dd728..d8c8b7d3 100644 --- a/Scribe/Views/Cells/InfoChildTableViewCell/InfoChildTableViewCell.swift +++ b/Scribe/Views/Cells/InfoChildTableViewCell/InfoChildTableViewCell.swift @@ -198,7 +198,7 @@ final class InfoChildTableViewCell: UITableViewCell { } else { toggleSwitch.isOn = false //Default value } - + case .none: break } } From d77bb2211cb437f35a7ae668ff3c5699641b8095 Mon Sep 17 00:00:00 2001 From: henrikth93 Date: Tue, 2 Dec 2025 22:41:11 +0100 Subject: [PATCH 3/5] Fix lint issues This commit will fix lint issues --- .../KeyboardViewController.swift | 22 +++++++++---------- .../InfoChildTableViewCell.swift | 10 ++++----- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Keyboards/KeyboardsBase/KeyboardViewController.swift b/Keyboards/KeyboardsBase/KeyboardViewController.swift index 591642ae..e4383498 100644 --- a/Keyboards/KeyboardsBase/KeyboardViewController.swift +++ b/Keyboards/KeyboardsBase/KeyboardViewController.swift @@ -901,22 +901,22 @@ class KeyboardViewController: UIInputViewController { backspaceTimer = nil } } - + func deleteWordBackward() { guard let documentText = proxy.documentContextBeforeInput else { return } - + var words = documentText.split(separator: " ").map(String.init) - + guard !words.isEmpty else { return } - + words.removeLast() - + let updatedText = words.joined(separator: " ") - + for _ in documentText { proxy.deleteBackward() } @@ -2317,17 +2317,17 @@ class KeyboardViewController: UIInputViewController { return true // return the default value } } - + func wordForWordDeletionIsEnabled() -> Bool { let langCode = languagesAbbrDict[controllerLanguage] ?? "unknown" if let userDefaults = UserDefaults(suiteName: "group.be.scri.userDefaultsContainer") { let dictionaryKey = langCode + "WordForWord" - + return userDefaults.bool(forKey: dictionaryKey) } else { return false // return the default value } - + } // MARK: Button Actions @@ -3035,8 +3035,8 @@ class KeyboardViewController: UIInputViewController { gesture.state = .cancelled commandBar.conditionallyAddPlaceholder() } - - longPressOnDelete = true //This variable can be used for word-for-word deletion + + longPressOnDelete = true // This variable can be used for word-for-word deletion // Delete is sped up based on the number of deletes that have been completed. var deleteCount = 0 diff --git a/Scribe/Views/Cells/InfoChildTableViewCell/InfoChildTableViewCell.swift b/Scribe/Views/Cells/InfoChildTableViewCell/InfoChildTableViewCell.swift index d8c8b7d3..7fa80ba3 100644 --- a/Scribe/Views/Cells/InfoChildTableViewCell/InfoChildTableViewCell.swift +++ b/Scribe/Views/Cells/InfoChildTableViewCell/InfoChildTableViewCell.swift @@ -146,11 +146,11 @@ final class InfoChildTableViewCell: UITableViewCell { case .autosuggestEmojis: let dictionaryKey = languageCode + "EmojiAutosuggest" userDefaults.setValue(toggleSwitch.isOn, forKey: dictionaryKey) - + case .toggleWordForWordDeletion: let dictionaryKey = languageCode + "WordForWord" userDefaults.setValue(toggleSwitch.isOn, forKey: dictionaryKey) - + case .none: break } @@ -190,15 +190,15 @@ final class InfoChildTableViewCell: UITableViewCell { } else { toggleSwitch.isOn = true // Default value } - + case .toggleWordForWordDeletion: let dictionaryKey = languageCode + "WordForWord" if let toggleValue = userDefaults.object(forKey: dictionaryKey) as? Bool { toggleSwitch.isOn = toggleValue } else { - toggleSwitch.isOn = false //Default value + toggleSwitch.isOn = false // Default value } - + case .none: break } } From 081733191a578e992952fe327c38b170c9f22ea1 Mon Sep 17 00:00:00 2001 From: henrikth93 Date: Sat, 6 Dec 2025 15:16:54 +0100 Subject: [PATCH 4/5] Update changelog This commit will update the changelog to include the issue 301 --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bab907c6..d91c27ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,8 @@ Emojis for the following are chosen based on [gitmoji](https://gitmoji.dev/). - Adds an English keyboard ([#7](https://github.com/scribe-org/Scribe-iOS/issues/7)). ### ✨ New Features - +- Users can now delete text word for word instead of character by character by enabling a function in the settings menu. The words are deleted by holding down backspace. +([#301](https://github.com/scribe-org/Scribe-iOS/issues/301)) - The Scribe translation functionality now allows the user to select which language to translate from via a convenient menu selector within the settings of each keyboard ([#255](https://github.com/scribe-org/Scribe-iOS/issues/255), [#307](https://github.com/scribe-org/Scribe-iOS/issues/307), [#469](https://github.com/scribe-org/Scribe-iOS/issues/469), [#470](https://github.com/scribe-org/Scribe-iOS/issues/470)) - The keyboard will switch to the appropriate source keyboard during translation and results will be returned from that language. - Scribe commands can now be triggered directly on a selected word by pressing the Scribe key and then choosing which command to use ([#141](https://github.com/scribe-org/Scribe-iOS/issues/141)). From 7d99bcace092d6f4c238eec6a29e8f342f5b58ec Mon Sep 17 00:00:00 2001 From: Andrew Tavis McAllister Date: Sat, 27 Dec 2025 16:40:58 +0100 Subject: [PATCH 5/5] Rename settings variable suffix and update changelog formatting --- CHANGELOG.md | 4 ++-- Keyboards/KeyboardsBase/KeyboardViewController.swift | 4 ++-- .../Cells/InfoChildTableViewCell/InfoChildTableViewCell.swift | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d91c27ee..ffa20610 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,8 +17,8 @@ Emojis for the following are chosen based on [gitmoji](https://gitmoji.dev/). - Adds an English keyboard ([#7](https://github.com/scribe-org/Scribe-iOS/issues/7)). ### ✨ New Features -- Users can now delete text word for word instead of character by character by enabling a function in the settings menu. The words are deleted by holding down backspace. -([#301](https://github.com/scribe-org/Scribe-iOS/issues/301)) + +- Users can now delete text word for word instead of character by character by enabling a function in the settings menu. The words are deleted by holding down backspace ([#301](https://github.com/scribe-org/Scribe-iOS/issues/301)). - The Scribe translation functionality now allows the user to select which language to translate from via a convenient menu selector within the settings of each keyboard ([#255](https://github.com/scribe-org/Scribe-iOS/issues/255), [#307](https://github.com/scribe-org/Scribe-iOS/issues/307), [#469](https://github.com/scribe-org/Scribe-iOS/issues/469), [#470](https://github.com/scribe-org/Scribe-iOS/issues/470)) - The keyboard will switch to the appropriate source keyboard during translation and results will be returned from that language. - Scribe commands can now be triggered directly on a selected word by pressing the Scribe key and then choosing which command to use ([#141](https://github.com/scribe-org/Scribe-iOS/issues/141)). diff --git a/Keyboards/KeyboardsBase/KeyboardViewController.swift b/Keyboards/KeyboardsBase/KeyboardViewController.swift index e4383498..97bc06b9 100644 --- a/Keyboards/KeyboardsBase/KeyboardViewController.swift +++ b/Keyboards/KeyboardsBase/KeyboardViewController.swift @@ -2321,7 +2321,7 @@ class KeyboardViewController: UIInputViewController { func wordForWordDeletionIsEnabled() -> Bool { let langCode = languagesAbbrDict[controllerLanguage] ?? "unknown" if let userDefaults = UserDefaults(suiteName: "group.be.scri.userDefaultsContainer") { - let dictionaryKey = langCode + "WordForWord" + let dictionaryKey = langCode + "WordForWordDeletion" return userDefaults.bool(forKey: dictionaryKey) } else { @@ -3036,7 +3036,7 @@ class KeyboardViewController: UIInputViewController { commandBar.conditionallyAddPlaceholder() } - longPressOnDelete = true // This variable can be used for word-for-word deletion + longPressOnDelete = true // Delete is sped up based on the number of deletes that have been completed. var deleteCount = 0 diff --git a/Scribe/Views/Cells/InfoChildTableViewCell/InfoChildTableViewCell.swift b/Scribe/Views/Cells/InfoChildTableViewCell/InfoChildTableViewCell.swift index 7fa80ba3..1660ecde 100644 --- a/Scribe/Views/Cells/InfoChildTableViewCell/InfoChildTableViewCell.swift +++ b/Scribe/Views/Cells/InfoChildTableViewCell/InfoChildTableViewCell.swift @@ -148,7 +148,7 @@ final class InfoChildTableViewCell: UITableViewCell { userDefaults.setValue(toggleSwitch.isOn, forKey: dictionaryKey) case .toggleWordForWordDeletion: - let dictionaryKey = languageCode + "WordForWord" + let dictionaryKey = languageCode + "WordForWordDeletion" userDefaults.setValue(toggleSwitch.isOn, forKey: dictionaryKey) case .none: break @@ -192,7 +192,7 @@ final class InfoChildTableViewCell: UITableViewCell { } case .toggleWordForWordDeletion: - let dictionaryKey = languageCode + "WordForWord" + let dictionaryKey = languageCode + "WordForWordDeletion" if let toggleValue = userDefaults.object(forKey: dictionaryKey) as? Bool { toggleSwitch.isOn = toggleValue } else {