From f0e9346060d4913eb26cd531bb8c9a4d2826f1fc Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Mon, 17 Jan 2022 21:22:58 +0800 Subject: [PATCH] Zonble: Flexible candidate amount per page. --- Source/PreferencesModule.swift | 89 +++++++++++++++--------- Source/PreferencesWindowController.swift | 34 ++++++--- Source/en.lproj/Localizable.strings | 6 ++ Source/zh-Hans.lproj/Localizable.strings | 6 ++ Source/zh-Hant.lproj/Localizable.strings | 6 ++ 5 files changed, 97 insertions(+), 44 deletions(-) diff --git a/Source/PreferencesModule.swift b/Source/PreferencesModule.swift index 39ea0b4d..6f8e2a47 100644 --- a/Source/PreferencesModule.swift +++ b/Source/PreferencesModule.swift @@ -144,36 +144,6 @@ struct ComposingBufferSize { } } -@propertyWrapper -struct ComposingKeys { - let key: String - let defaultValue: String? = kCandidateKeys - lazy var container: UserDefault = { - UserDefault(key: key, defaultValue: defaultValue) }() - - var wrappedValue: String? { - mutating get { - let value = container.wrappedValue - if let value = value { - if value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { - return nil - } - } - return value - } - set { - let value = newValue - if let value = value { - if value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { - container.wrappedValue = nil - return - } - } - container.wrappedValue = value - } - } -} - // MARK: - @objc enum KeyboardLayout: Int { case standard @@ -284,12 +254,65 @@ struct ComposingKeys { @UserDefault(key: kCandidateKeyLabelFontName, defaultValue: nil) @objc static var candidateKeyLabelFontName: String? - @ComposingKeys(key: kCandidateKeys) - @objc static var candidateKeys: String? + @UserDefault(key: kCandidateKeys, defaultValue: kDefaultKeys) + @objc static var candidateKeys: String - @objc static var defaultKeys: String { + @objc static var defaultCandidateKeys: String { kDefaultKeys } + @objc static var suggestedCandidateKeys: [String] { + [kDefaultKeys, "234567890", "QWERTYUIO", "QWERTASDF", "ASDFGHJKL", "ASDFZXCVB"] + } + + static func validate(candidateKeys: String) throws { + let trimmed = candidateKeys.trimmingCharacters(in: .whitespacesAndNewlines) + if trimmed.isEmpty { + throw CandidateKeyError.empty + } + if !trimmed.canBeConverted(to: .ascii) { + throw CandidateKeyError.invalidCharacters + } + if trimmed.contains(" ") { + throw CandidateKeyError.containSpace + } + if trimmed.count < 4 { + throw CandidateKeyError.tooShort + } + if trimmed.count > 15 { + throw CandidateKeyError.tooLong + } + let set = Set(Array(trimmed)) + if set.count != trimmed.count { + throw CandidateKeyError.duplicatedCharacters + } + } + + enum CandidateKeyError: Error, LocalizedError { + case empty + case invalidCharacters + case containSpace + case duplicatedCharacters + case tooShort + case tooLong + + var errorDescription: String? { + switch self { + case .empty: + return NSLocalizedString("Candidates keys cannot be empty.", comment: "") + case .invalidCharacters: + return NSLocalizedString("Candidate keys can only contain ASCII characters like alphanumerals.", comment: "") + case .containSpace: + return NSLocalizedString("Candidate keys cannot contain space.", comment: "") + case .duplicatedCharacters: + return NSLocalizedString("There should not be duplicated keys.", comment: "") + case .tooShort: + return NSLocalizedString("The length of your candidate keys can not be less than 4 characters.", comment: "") + case .tooLong: + return NSLocalizedString("The length of your candidate keys can not be larger than 15 characters.", comment: "") + } + } + + } @UserDefault(key: kChineseConversionEngineKey, defaultValue: 0) @objc static var chineseConversionEngine: Int diff --git a/Source/PreferencesWindowController.swift b/Source/PreferencesWindowController.swift index d05289bf..39c6c918 100644 --- a/Source/PreferencesWindowController.swift +++ b/Source/PreferencesWindowController.swift @@ -136,11 +136,11 @@ extension RangeReplaceableCollection where Element: Hashable { basisKeyboardLayoutButton.select(chosenItem ?? usKeyboardLayoutItem) selectionKeyComboBox.usesDataSource = false selectionKeyComboBox.removeAllItems() - selectionKeyComboBox.addItems(withObjectValues: [Preferences.defaultKeys, "234567890", "QWERTYUIO", "QWERTASDF", "ASDFGHJKL", "ASDFZXCVB"]) + selectionKeyComboBox.addItems(withObjectValues: Preferences.suggestedCandidateKeys) - var candidateSelectionKeys = Preferences.candidateKeys ?? Preferences.defaultKeys + var candidateSelectionKeys = Preferences.candidateKeys if candidateSelectionKeys.isEmpty { - candidateSelectionKeys = Preferences.defaultKeys + candidateSelectionKeys = Preferences.defaultCandidateKeys } selectionKeyComboBox.stringValue = candidateSelectionKeys @@ -157,14 +157,26 @@ extension RangeReplaceableCollection where Element: Hashable { } @IBAction func changeSelectionKeyAction(_ sender: Any) { - let keys = (sender as AnyObject).stringValue.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).charDeDuplicate - if keys.count != 9 || !keys.canBeConverted(to: .ascii) { - selectionKeyComboBox.stringValue = Preferences.defaultKeys - Preferences.candidateKeys = Preferences.defaultKeys // 修正記錄:這裡千萬不能是 nil,否則會鬼打牆。 - clsSFX.beep() - return - } - + guard let keys = (sender as AnyObject).stringValue?.trimmingCharacters(in: .whitespacesAndNewlines).charDeDuplicate else { + return + } + do { + try Preferences.validate(candidateKeys: keys) + Preferences.candidateKeys = keys + } + catch Preferences.CandidateKeyError.empty { + selectionKeyComboBox.stringValue = Preferences.candidateKeys + } + catch { + if let window = window { + let alert = NSAlert(error: error) + alert.beginSheetModal(for: window) { response in + self.selectionKeyComboBox.stringValue = Preferences.candidateKeys + } + clsSFX.beep() + } + } + selectionKeyComboBox.stringValue = keys Preferences.candidateKeys = keys } diff --git a/Source/en.lproj/Localizable.strings b/Source/en.lproj/Localizable.strings index 426b919f..0504f283 100644 --- a/Source/en.lproj/Localizable.strings +++ b/Source/en.lproj/Localizable.strings @@ -29,3 +29,9 @@ "You are now selecting \"%@\". Press enter to add a new phrase." = "You are now selecting \"%@\". Press enter to add a new phrase."; "Edit Phrase Replacement Table" = "Edit Phrase Replacement Table"; "Use Phrase Replacement" = "Use Phrase Replacement"; +"Candidates keys cannot be empty." = "Candidates keys cannot be empty."; +"Candidate keys can only contain ASCII characters like alphanumerals." = "Candidate keys can only contain ASCII characters like alphanumerals."; +"Candidate keys cannot contain space." = "Candidate keys cannot contain space."; +"There should not be duplicated keys." = "There should not be duplicated keys."; +"The length of your candidate keys can not be less than 4 characters." = "The length of your candidate keys can not be less than 4 characters."; +"The length of your candidate keys can not be larger than 15 characters." = "The length of your candidate keys can not be larger than 15 characters."; diff --git a/Source/zh-Hans.lproj/Localizable.strings b/Source/zh-Hans.lproj/Localizable.strings index a3710edb..66ee7996 100644 --- a/Source/zh-Hans.lproj/Localizable.strings +++ b/Source/zh-Hans.lproj/Localizable.strings @@ -29,3 +29,9 @@ "You are now selecting \"%@\". Press enter to add a new phrase." = "您目前选择了「%@」。按下 Enter 就可以加入到自订语汇中。"; "Edit Phrase Replacement Table" = "编辑语汇置换表"; "Use Phrase Replacement" = "使用语汇置换"; +"Candidates keys cannot be empty." = "您必须指定选字键。"; +"Candidate keys can only contain ASCII characters like alphanumerals." = "选字键只能是英数等 ASCII 字符。"; +"Candidate keys cannot contain space." = "选字键不得包含空格。"; +"There should not be duplicated keys." = "选字键不得重复。"; +"The length of your candidate keys can not be less than 4 characters." = "请至少指定四个选字键。"; +"The length of your candidate keys can not be larger than 15 characters." = "选字键最多只能指定十五个。"; diff --git a/Source/zh-Hant.lproj/Localizable.strings b/Source/zh-Hant.lproj/Localizable.strings index eec7b514..b25c2356 100644 --- a/Source/zh-Hant.lproj/Localizable.strings +++ b/Source/zh-Hant.lproj/Localizable.strings @@ -29,3 +29,9 @@ "You are now selecting \"%@\". Press enter to add a new phrase." = "您目前選擇了「%@」。按下 Enter 就可以加入到自訂語彙中。"; "Edit Phrase Replacement Table" = "編輯語彙置換表"; "Use Phrase Replacement" = "使用語彙置換"; +"Candidates keys cannot be empty." = "您必須指定選字鍵。"; +"Candidate keys can only contain ASCII characters like alphanumerals." = "選字鍵只能是英數等 ASCII 字符。"; +"Candidate keys cannot contain space." = "選字鍵不得包含空格。"; +"There should not be duplicated keys." = "選字鍵不得重複。"; +"The length of your candidate keys can not be less than 4 characters." = "請至少指定四個選字鍵。"; +"The length of your candidate keys can not be larger than 15 characters." = "選字鍵最多只能指定十五個。";