mirror of
https://github.com/keymanapp/keyman.git
synced 2026-10-16 19:07:33 +00:00
feat(ios): add per-language autocorrect toggle
Does not actually pass the toggle's setting to KMW yet. Build-bot: skip build:ios
This commit is contained in:
parent
e02404b365
commit
a7371ea3ec
7 changed files with 138 additions and 50 deletions
|
|
@ -33,6 +33,7 @@ public enum Key {
|
|||
/// Dictionary of prediction/correction toggle settings keyed by language id in UserDefaults
|
||||
static let userPredictSettings = "UserPredictionEnablementSettings"
|
||||
static let userCorrectSettings = "UserCorrectionEnablementSettings"
|
||||
static let userAutocorrectSettings = "UserAutocorrectionEnablementSettings"
|
||||
|
||||
// Internal user defaults keys
|
||||
static let engineVersion = "KeymanEngineVersion"
|
||||
|
|
|
|||
|
|
@ -331,6 +331,17 @@ public extension UserDefaults {
|
|||
set(prefs, forKey: Key.userCorrectSettings)
|
||||
}
|
||||
}
|
||||
|
||||
// stores a dictionary of autocorrection-enablement settings keyed to language ids, i.e., [langID: Bool]
|
||||
var autocorrectionEnablements: [String: Bool]? {
|
||||
get {
|
||||
return dictionary(forKey: Key.userAutocorrectSettings) as? [String : Bool]
|
||||
}
|
||||
|
||||
set(prefs) {
|
||||
set(prefs, forKey: Key.userAutocorrectSettings)
|
||||
}
|
||||
}
|
||||
|
||||
var portraitKeyboardHeight: Double {
|
||||
get {
|
||||
|
|
@ -387,4 +398,22 @@ public extension UserDefaults {
|
|||
prefs?[forLanguageID] = correctSetting
|
||||
correctionEnablements = prefs
|
||||
}
|
||||
|
||||
func autocorrectSettingForLanguage(languageID: String) -> Bool {
|
||||
if let dict = autocorrectionEnablements {
|
||||
return dict[languageID] ?? true
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func set(autocorrectSetting: Bool, forLanguageID: String) {
|
||||
var prefs: [String: Bool]?
|
||||
prefs = autocorrectionEnablements
|
||||
if prefs == nil {
|
||||
prefs = [String: Bool]()
|
||||
}
|
||||
prefs?[forLanguageID] = autocorrectSetting
|
||||
autocorrectionEnablements = prefs
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ extension KeymanWebViewController {
|
|||
}
|
||||
view = nil
|
||||
}
|
||||
|
||||
|
||||
func languageMenuPosition(_ completion: @escaping (CGRect) -> Void) {
|
||||
webView!.evaluateJavaScript("langMenuPos();") { result, _ in
|
||||
guard let result = result as? String, !result.isEmpty else {
|
||||
|
|
@ -422,10 +422,11 @@ extension KeymanWebViewController {
|
|||
|
||||
let predict = userDefaults.predictSettingForLanguage(languageID: lexicalModel.languageID)
|
||||
let correct = userDefaults.correctSettingForLanguage(languageID: lexicalModel.languageID)
|
||||
let autocorrect = userDefaults.autocorrectSettingForLanguage(languageID: lexicalModel.languageID)
|
||||
|
||||
// Pass these off to KMW!
|
||||
// We do these first so that they're automatically set for the to-be-registered model in advance.
|
||||
webView!.evaluateJavaScript("enableSuggestions(\(stubString), \(predict), \(correct))")
|
||||
webView!.evaluateJavaScript("enableSuggestions(\(stubString), \(predict), \(correct), \(autocorrect))")
|
||||
self.activeModel = predict
|
||||
} else { // We're registering a model in the background - don't change settings.
|
||||
webView!.evaluateJavaScript("keyman.addModel(\(stubString));", completionHandler: nil)
|
||||
|
|
|
|||
|
|
@ -18,8 +18,11 @@ class LanguageSettingsViewController: UITableViewController {
|
|||
|
||||
private var doPredictionsSwitch: UISwitch?
|
||||
private var doCorrectionsSwitch: UISwitch?
|
||||
private var doAutocorrectionsSwitch: UISwitch?
|
||||
private var doCorrectionsLabel: UILabel?
|
||||
private var doAutocorrectionsLabel: UILabel?
|
||||
private var correctionsCell: UITableViewCell?
|
||||
private var autocorrectionsCell: UITableViewCell?
|
||||
|
||||
public init(_ inLanguage: Language) {
|
||||
language = inLanguage
|
||||
|
|
@ -87,17 +90,8 @@ class LanguageSettingsViewController: UITableViewController {
|
|||
return switchFrame
|
||||
}
|
||||
|
||||
@objc
|
||||
func predictionSwitchValueChanged(source: UISwitch) {
|
||||
let value = source.isOn;
|
||||
func refreshModelIfNeeded() {
|
||||
let userDefaults = Storage.active.userDefaults
|
||||
userDefaults.set(predictSetting: value, forLanguageID: self.language.id)
|
||||
|
||||
// Reactively set the corrections switch interactivity state.
|
||||
self.doCorrectionsSwitch?.isHidden = !value
|
||||
self.doCorrectionsLabel?.isEnabled = value
|
||||
self.correctionsCell?.isUserInteractionEnabled = value
|
||||
|
||||
if let lm = Manager.shared.preferredLexicalModel(userDefaults, forLanguage: self.language.id) {
|
||||
if Manager.shared.currentKeyboardID?.languageID == self.language.id {
|
||||
// re-register the model - that'll enact the settings.
|
||||
|
|
@ -111,23 +105,59 @@ class LanguageSettingsViewController: UITableViewController {
|
|||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
func predictionSwitchValueChanged(source: UISwitch) {
|
||||
let value = source.isOn;
|
||||
let userDefaults = Storage.active.userDefaults
|
||||
userDefaults.set(predictSetting: value, forLanguageID: self.language.id)
|
||||
|
||||
// Reactively set the corrections switch interactivity state.
|
||||
self.doCorrectionsSwitch?.isHidden = !value
|
||||
self.doCorrectionsLabel?.isEnabled = value
|
||||
|
||||
let mayCorrect = userDefaults.autocorrectSettingForLanguage(languageID: self.language.id)
|
||||
self.doAutocorrectionsSwitch?.isHidden = !(value && mayCorrect)
|
||||
self.doAutocorrectionsLabel?.isEnabled = value && mayCorrect
|
||||
self.correctionsCell?.isUserInteractionEnabled = value
|
||||
}
|
||||
|
||||
@objc
|
||||
func correctionSwitchValueChanged(source: UISwitch) {
|
||||
let value = source.isOn;
|
||||
let userDefaults = Storage.active.userDefaults
|
||||
userDefaults.set(correctSetting: value, forLanguageID: self.language.id)
|
||||
|
||||
if let lm = Manager.shared.preferredLexicalModel(userDefaults, forLanguage: self.language.id) {
|
||||
if Manager.shared.currentKeyboardID?.languageID == self.language.id {
|
||||
// re-register the model - that'll enact the settings.
|
||||
_ = Manager.shared.registerLexicalModel(lm)
|
||||
}
|
||||
// Based on how Manager chooses the model in setKeyboard.
|
||||
} else if let lm = userDefaults.userLexicalModels?.first(where: { $0.languageID == self.language.id }) {
|
||||
if Manager.shared.currentKeyboardID?.languageID == self.language.id {
|
||||
_ = Manager.shared.registerLexicalModel(lm)
|
||||
}
|
||||
}
|
||||
// This may only be triggered if the predict toggle is on,
|
||||
// so we can rely on just the input value.
|
||||
self.doAutocorrectionsSwitch?.isHidden = !value
|
||||
self.doAutocorrectionsLabel?.isEnabled = value
|
||||
|
||||
refreshModelIfNeeded()
|
||||
}
|
||||
|
||||
@objc
|
||||
func autocorrectionSwitchValueChanged(source: UISwitch) {
|
||||
let value = source.isOn;
|
||||
let userDefaults = Storage.active.userDefaults
|
||||
userDefaults.set(autocorrectSetting: value, forLanguageID: self.language.id)
|
||||
|
||||
refreshModelIfNeeded()
|
||||
}
|
||||
|
||||
func addSwitchToTableCell(_ toggle: UISwitch, cell: UITableViewCell, isOn: Bool, selector: Selector) {
|
||||
cell.accessoryType = .none
|
||||
toggle.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
let switchFrame = frameAtRightOfCell(cell: cell.frame, controlSize: toggle.frame.size)
|
||||
toggle.frame = switchFrame
|
||||
|
||||
toggle.isOn = isOn
|
||||
toggle.addTarget(self, action: selector, for: .valueChanged)
|
||||
cell.addSubview(toggle)
|
||||
cell.contentView.isUserInteractionEnabled = false
|
||||
|
||||
toggle.rightAnchor.constraint(equalTo: cell.layoutMarginsGuide.rightAnchor).isActive = true
|
||||
toggle.centerYAnchor.constraint(equalTo: cell.layoutMarginsGuide.centerYAnchor).isActive = true
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
|
|
@ -143,40 +173,44 @@ class LanguageSettingsViewController: UITableViewController {
|
|||
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: cellIdentifier)
|
||||
if 1 == indexPath.section {
|
||||
if 0 == indexPath.row {
|
||||
cell.accessoryType = .none
|
||||
doPredictionsSwitch = UISwitch()
|
||||
doPredictionsSwitch!.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
let switchFrame = frameAtRightOfCell(cell: cell.frame, controlSize: doPredictionsSwitch!.frame.size)
|
||||
doPredictionsSwitch!.frame = switchFrame
|
||||
|
||||
doPredictionsSwitch!.isOn = userDefaults.predictSettingForLanguage(languageID: self.language.id)
|
||||
doPredictionsSwitch!.addTarget(self, action: #selector(self.predictionSwitchValueChanged), for: .valueChanged)
|
||||
cell.addSubview(doPredictionsSwitch!)
|
||||
cell.contentView.isUserInteractionEnabled = false
|
||||
|
||||
doPredictionsSwitch!.rightAnchor.constraint(equalTo: cell.layoutMarginsGuide.rightAnchor).isActive = true
|
||||
doPredictionsSwitch!.centerYAnchor.constraint(equalTo: cell.layoutMarginsGuide.centerYAnchor).isActive = true
|
||||
self.addSwitchToTableCell(
|
||||
doPredictionsSwitch!,
|
||||
cell: cell,
|
||||
isOn: userDefaults.predictSettingForLanguage(languageID: self.language.id),
|
||||
selector: #selector(self.predictionSwitchValueChanged)
|
||||
)
|
||||
} else if 1 == indexPath.row {
|
||||
correctionsCell = cell
|
||||
cell.accessoryType = .none
|
||||
doCorrectionsSwitch = UISwitch()
|
||||
doCorrectionsSwitch!.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
let switchFrame = frameAtRightOfCell(cell: cell.frame, controlSize: doCorrectionsSwitch!.frame.size)
|
||||
doCorrectionsSwitch!.frame = switchFrame
|
||||
|
||||
doCorrectionsSwitch!.isOn = userDefaults.correctSettingForLanguage(languageID: self.language.id)
|
||||
doCorrectionsSwitch!.addTarget(self, action: #selector(self.correctionSwitchValueChanged), for: .valueChanged)
|
||||
cell.addSubview(doCorrectionsSwitch!)
|
||||
cell.contentView.isUserInteractionEnabled = false
|
||||
|
||||
doCorrectionsSwitch!.rightAnchor.constraint(equalTo: cell.layoutMarginsGuide.rightAnchor).isActive = true
|
||||
doCorrectionsSwitch!.centerYAnchor.constraint(equalTo: cell.layoutMarginsGuide.centerYAnchor).isActive = true
|
||||
self.addSwitchToTableCell(
|
||||
doCorrectionsSwitch!,
|
||||
cell: cell,
|
||||
isOn: userDefaults.correctSettingForLanguage(languageID: self.language.id),
|
||||
selector: #selector(self.correctionSwitchValueChanged)
|
||||
)
|
||||
|
||||
// Disable interactivity if the prediction toggle is set to 'off'.
|
||||
doCorrectionsSwitch!.isHidden = !userDefaults.predictSettingForLanguage(languageID: self.language.id)
|
||||
cell.isUserInteractionEnabled = userDefaults.predictSettingForLanguage(languageID: self.language.id)
|
||||
} else if 2 == indexPath.row {
|
||||
autocorrectionsCell = cell
|
||||
doAutocorrectionsSwitch = UISwitch()
|
||||
|
||||
self.addSwitchToTableCell(
|
||||
doAutocorrectionsSwitch!,
|
||||
cell: cell,
|
||||
isOn: userDefaults.autocorrectSettingForLanguage(languageID: self.language.id),
|
||||
selector: #selector(self.autocorrectionSwitchValueChanged)
|
||||
)
|
||||
|
||||
// Disable interactivity if the prediction or correction toggle is set to 'off'.
|
||||
let mayPredict = userDefaults.predictSettingForLanguage(languageID: self.language.id)
|
||||
let mayCorrect = userDefaults.correctSettingForLanguage(languageID: self.language.id)
|
||||
doAutocorrectionsSwitch!.isHidden = !(mayPredict && mayCorrect)
|
||||
cell.isUserInteractionEnabled = mayPredict && mayCorrect
|
||||
} else { // rows 3 and 4
|
||||
cell.accessoryType = .disclosureIndicator
|
||||
}
|
||||
|
|
@ -245,6 +279,10 @@ class LanguageSettingsViewController: UITableViewController {
|
|||
cell.textLabel?.text = NSLocalizedString("menu-langsettings-toggle-correct", bundle: engineBundle, comment: "")
|
||||
cell.textLabel?.isEnabled = !(doCorrectionsSwitch?.isHidden ?? false)
|
||||
case 2:
|
||||
doAutocorrectionsLabel = cell.textLabel
|
||||
cell.textLabel?.text = NSLocalizedString("menu-langsettings-toggle-autocorrect", bundle: engineBundle, comment: "")
|
||||
cell.textLabel?.isEnabled = !(doAutocorrectionsSwitch?.isHidden ?? false)
|
||||
case 3:
|
||||
cell.textLabel?.text = NSLocalizedString("menu-langsettings-label-lexical-models", bundle: engineBundle, comment: "")
|
||||
cell.accessoryType = .disclosureIndicator
|
||||
let modelCt = language.lexicalModels?.count ?? 0
|
||||
|
|
@ -332,8 +370,8 @@ class LanguageSettingsViewController: UITableViewController {
|
|||
showKeyboardInfoView(kb: (language.keyboards?[safe: indexPath.row])!)
|
||||
case 1:
|
||||
switch indexPath.row {
|
||||
// case 0, 1: the toggles - but a general 'click' not on the toggle itself.
|
||||
case 2:
|
||||
// case 0, 1, 2: the toggles - but a general 'click' not on the toggle itself.
|
||||
case 3:
|
||||
showLexicalModelsView()
|
||||
default:
|
||||
break
|
||||
|
|
|
|||
|
|
@ -154,6 +154,9 @@
|
|||
/* Label for the toggle that enables corrections that is displayed within a language-specific settings menu */
|
||||
"menu-langsettings-toggle-correct" = "Enable corrections";
|
||||
|
||||
/* Label for the toggle that enables auto-corrections that is displayed within a language-specific settings menu */
|
||||
"menu-langsettings-toggle-autocorrect" = "Enable autocorrections";
|
||||
|
||||
/* Label for the toggle that enables predictions that is displayed within a language-specific settings menu */
|
||||
"menu-langsettings-toggle-predict" = "Enable predictions";
|
||||
|
||||
|
|
|
|||
|
|
@ -326,11 +326,12 @@ function toHex(theString) {
|
|||
return hexString.substr(0, hexString.length-1);
|
||||
}
|
||||
|
||||
function enableSuggestions(model, mayPredict, mayCorrect) {
|
||||
function enableSuggestions(model, mayPredict, mayCorrect, mayAutocorrect) {
|
||||
// Set the options first so that KMW's ModelCache can properly handle model enablement states
|
||||
// the moment we actually register the new model.
|
||||
keyman.core.languageProcessor.mayPredict = mayPredict;
|
||||
keyman.core.languageProcessor.mayCorrect = mayCorrect;
|
||||
keyman.core.languageProcessor.mayAutocorrect = mayAutocorrect;
|
||||
|
||||
keyman.addModel(model);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -166,6 +166,12 @@ export class LanguageProcessor extends EventEmitter<LanguageProcessorEventMap> {
|
|||
return !!this.recentTranscriptions.get(transitionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* When called, this requests the worker to generate predictions for the transcribed
|
||||
* context transition.
|
||||
* @param transcription
|
||||
* @param layerId
|
||||
*/
|
||||
public predict(transcription: Transcription, layerId: string): Promise<Suggestion[]> {
|
||||
if(!this.isActive) {
|
||||
return null;
|
||||
|
|
@ -373,6 +379,9 @@ export class LanguageProcessor extends EventEmitter<LanguageProcessorEventMap> {
|
|||
this.lmEngine.resetContext(context, transcription.token);
|
||||
}
|
||||
|
||||
// The "may correct" setting is enforced here, in the main Web engine - not
|
||||
// in the worker. As the desired setting may vary language-to-language,
|
||||
// we'd rather do this than reconfigure the worker constantly.
|
||||
let alternates = transcription.alternates;
|
||||
if(!this.mayCorrect || !alternates || alternates.length == 0) {
|
||||
alternates = [{
|
||||
|
|
@ -386,6 +395,12 @@ export class LanguageProcessor extends EventEmitter<LanguageProcessorEventMap> {
|
|||
|
||||
return promise.then((suggestions: Suggestion[]) => {
|
||||
if(promise == this.currentPromise) {
|
||||
if(!this.mayAutoCorrect) {
|
||||
// We disable the auto-accept flag here, rather than in the worker -
|
||||
// that way, we don't need to reconfigure the worker when settings
|
||||
// change.
|
||||
suggestions.forEach((s) => delete s.autoAccept);
|
||||
}
|
||||
const result = new ReadySuggestions(suggestions, transform.id);
|
||||
this.emit("suggestionsready", result);
|
||||
this.currentPromise = null;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue