diff --git a/Packages/vChewing_Shared/Package.swift b/Packages/vChewing_Shared/Package.swift index 41a00364..64cd0752 100644 --- a/Packages/vChewing_Shared/Package.swift +++ b/Packages/vChewing_Shared/Package.swift @@ -13,13 +13,15 @@ let package = Package( ) ], dependencies: [ - .package(path: "../vChewing_CocoaExtension") + .package(path: "../vChewing_CocoaExtension"), + .package(path: "../vChewing_SwiftExtension") ], targets: [ .target( name: "Shared", dependencies: [ - .product(name: "CocoaExtension", package: "vChewing_CocoaExtension") + .product(name: "CocoaExtension", package: "vChewing_CocoaExtension"), + .product(name: "SwiftExtension", package: "vChewing_SwiftExtension") ] ) ] diff --git a/Packages/vChewing_Shared/Sources/Shared/Shared.swift b/Packages/vChewing_Shared/Sources/Shared/Shared.swift index d7ba66e8..2d1e0204 100644 --- a/Packages/vChewing_Shared/Sources/Shared/Shared.swift +++ b/Packages/vChewing_Shared/Sources/Shared/Shared.swift @@ -7,6 +7,7 @@ // requirements defined in MIT License. import Foundation +import SwiftExtension // MARK: - UserDef Snapshot Manager @@ -214,10 +215,10 @@ public enum CandidateKey { return NSLocalizedString("There should not be duplicated keys.", comment: "") case .tooShort: return NSLocalizedString( - "Please specify at least 4 candidate keys.", comment: "" + "Please specify at least 6 candidate keys.", comment: "" ) case .tooLong: - return NSLocalizedString("Maximum 15 candidate keys allowed.", comment: "") + return NSLocalizedString("Maximum 9 candidate keys allowed.", comment: "") } } } @@ -233,10 +234,10 @@ public enum CandidateKey { if trimmed.contains(" ") { throw CandidateKey.ErrorType.containSpace } - if trimmed.count < 4 { + if trimmed.count < 6 { throw CandidateKey.ErrorType.tooShort } - if trimmed.count > 15 { + if trimmed.count > 9 { throw CandidateKey.ErrorType.tooLong } let set = Set(Array(trimmed)) diff --git a/Packages/vChewing_SwiftExtension/Sources/SwiftExtension/SwiftExtension.swift b/Packages/vChewing_SwiftExtension/Sources/SwiftExtension/SwiftExtension.swift index 6677a294..f32b21a6 100644 --- a/Packages/vChewing_SwiftExtension/Sources/SwiftExtension/SwiftExtension.swift +++ b/Packages/vChewing_SwiftExtension/Sources/SwiftExtension/SwiftExtension.swift @@ -13,7 +13,7 @@ import Foundation // Extend the RangeReplaceableCollection to allow it clean duplicated characters. // Ref: https://stackoverflow.com/questions/25738817/ extension RangeReplaceableCollection where Element: Hashable { - public var deduplicate: Self { + public var deduplicated: Self { var set = Set() return filter { set.insert($0).inserted } } diff --git a/Source/Modules/KeyHandler_Core.swift b/Source/Modules/KeyHandler_Core.swift index 4145ba92..e5a5ab68 100644 --- a/Source/Modules/KeyHandler_Core.swift +++ b/Source/Modules/KeyHandler_Core.swift @@ -275,7 +275,7 @@ public class KeyHandler { Megrez.Compositor.KeyValuePaired(key: $0.0, value: $0.1.value) } arrCandidates = arrSuggestedCandidates.filter { arrCandidates.contains($0) } + arrCandidates - arrCandidates = arrCandidates.deduplicate + arrCandidates = arrCandidates.deduplicated arrCandidates = arrCandidates.stableSort { $0.key.split(separator: "-").count > $1.key.split(separator: "-").count } return arrCandidates.map { ($0.key, $0.value) } } diff --git a/Source/Modules/PrefMgr_Core.swift b/Source/Modules/PrefMgr_Core.swift index 47051530..e279d741 100644 --- a/Source/Modules/PrefMgr_Core.swift +++ b/Source/Modules/PrefMgr_Core.swift @@ -233,7 +233,10 @@ public class PrefMgr: PrefMgrProtocol { @AppProperty(key: UserDef.kCandidateKeys.rawValue, defaultValue: kDefaultCandidateKeys) public var candidateKeys: String { didSet { - if useIMKCandidateWindow { + if candidateKeys != candidateKeys.deduplicated { + candidateKeys = candidateKeys.deduplicated + } + if !(6...9).contains(candidateKeys.count) { candidateKeys = Self.kDefaultCandidateKeys } } diff --git a/Source/Modules/PrefMgr_Extension.swift b/Source/Modules/PrefMgr_Extension.swift index 5f52af93..9595bf63 100644 --- a/Source/Modules/PrefMgr_Extension.swift +++ b/Source/Modules/PrefMgr_Extension.swift @@ -22,6 +22,8 @@ extension PrefMgr { disableShiftTogglingAlphanumericalMode = false togglingAlphanumericalModeWithLShift = false } + // 自動糾正選字鍵 (利用其 didSet 特性) + candidateKeys = candidateKeys // 客體黑名單自動排序去重複。 clientsIMKTextInputIncapable = Array(Set(clientsIMKTextInputIncapable)).sorted() // 注拼槽注音排列選項糾錯。 diff --git a/Source/Modules/UIModules/PrefUI/suiPrefPaneKeyboard.swift b/Source/Modules/UIModules/PrefUI/suiPrefPaneKeyboard.swift index db5b8442..f0009d3d 100644 --- a/Source/Modules/UIModules/PrefUI/suiPrefPaneKeyboard.swift +++ b/Source/Modules/UIModules/PrefUI/suiPrefPaneKeyboard.swift @@ -56,7 +56,7 @@ struct suiPrefPaneKeyboard: View { items: CandidateKey.suggestions, text: $selSelectionKeys.onChange { let value = selSelectionKeys - let keys: String = value.trimmingCharacters(in: .whitespacesAndNewlines).deduplicate + let keys: String = value.trimmingCharacters(in: .whitespacesAndNewlines).deduplicated do { try CandidateKey.validate(keys: keys) PrefMgr.shared.candidateKeys = keys @@ -64,8 +64,8 @@ struct suiPrefPaneKeyboard: View { } catch CandidateKey.ErrorType.empty { selSelectionKeys = PrefMgr.shared.candidateKeys } catch { - if let window = ctlPrefUI.shared.controller.window { - let alert = NSAlert(error: error) + if let window = ctlPrefUI.shared.controller.window, let error = error as? CandidateKey.ErrorType { + let alert = NSAlert(error: error.errorDescription) alert.beginSheetModal(for: window) { _ in selSelectionKeys = PrefMgr.shared.candidateKeys } diff --git a/Source/Modules/WindowControllers/ctlPrefWindow.swift b/Source/Modules/WindowControllers/ctlPrefWindow.swift index 0272d84e..17afd11f 100644 --- a/Source/Modules/WindowControllers/ctlPrefWindow.swift +++ b/Source/Modules/WindowControllers/ctlPrefWindow.swift @@ -135,7 +135,9 @@ class ctlPrefWindow: NSWindowController { } selectionKeyComboBox.stringValue = candidateSelectionKeys - selectionKeyComboBox.isEnabled = false // 無法與 IMKCandidates 協作,故禁用。 + if PrefMgr.shared.useIMKCandidateWindow { + selectionKeyComboBox.isEnabled = false // 無法與 IMKCandidates 協作,故禁用。 + } } // 這裡有必要加上這段處理,用來確保藉由偏好設定介面動過的 CNS 開關能夠立刻生效。 @@ -198,7 +200,7 @@ class ctlPrefWindow: NSWindowController { let keys = (sender as AnyObject).stringValue?.trimmingCharacters( in: .whitespacesAndNewlines ) - .deduplicate + .deduplicated else { return } @@ -209,8 +211,8 @@ class ctlPrefWindow: NSWindowController { } catch CandidateKey.ErrorType.empty { selectionKeyComboBox.stringValue = PrefMgr.shared.candidateKeys } catch { - if let window = window { - let alert = NSAlert(error: error) + if let window = window, let error = error as? CandidateKey.ErrorType { + let alert = NSAlert(error: error.errorDescription) alert.beginSheetModal(for: window) { _ in self.selectionKeyComboBox.stringValue = PrefMgr.shared.candidateKeys } diff --git a/Source/Resources/Base.lproj/Localizable.strings b/Source/Resources/Base.lproj/Localizable.strings index c31aa78e..808e721c 100644 --- a/Source/Resources/Base.lproj/Localizable.strings +++ b/Source/Resources/Base.lproj/Localizable.strings @@ -51,12 +51,11 @@ "\"%@\" already exists:\n ENTER to boost, SHIFT+COMMAND+ENTER to nerf, \n BackSpace or Delete key to exclude." = "\"%@\" already exists:\n ENTER to boost, SHIFT+COMMAND+ENTER to nerf, \n BackSpace or Delete key to exclude."; "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 alphanumericals." = "Candidate keys can only contain ASCII characters like alphanumericals."; "Candidate keys cannot contain space." = "Candidate keys cannot contain space."; "There should not be duplicated keys." = "There should not be duplicated keys."; -"Please specify at least 4 candidate keys." = "Please specify at least 4 candidate keys."; -"Maximum 15 candidate keys allowed." = "Maximum 15 candidate keys allowed."; +"Please specify at least 6 candidate keys." = "Please specify at least 6 candidate keys."; +"Maximum 9 candidate keys allowed." = "Maximum 9 candidate keys allowed."; "⚠︎ Phrase replacement mode enabled, interfering user phrase entry." = "⚠︎ Phrase replacement mode enabled, interfering user phrase entry."; "⚠︎ Unhandlable: Chars and Readings in buffer doesn't match." = "⚠︎ Unhandlable: Chars and Readings in buffer doesn't match."; "Per-Char Select Mode" = "Per-Char Select Mode"; diff --git a/Source/Resources/en.lproj/Localizable.strings b/Source/Resources/en.lproj/Localizable.strings index c31aa78e..808e721c 100644 --- a/Source/Resources/en.lproj/Localizable.strings +++ b/Source/Resources/en.lproj/Localizable.strings @@ -51,12 +51,11 @@ "\"%@\" already exists:\n ENTER to boost, SHIFT+COMMAND+ENTER to nerf, \n BackSpace or Delete key to exclude." = "\"%@\" already exists:\n ENTER to boost, SHIFT+COMMAND+ENTER to nerf, \n BackSpace or Delete key to exclude."; "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 alphanumericals." = "Candidate keys can only contain ASCII characters like alphanumericals."; "Candidate keys cannot contain space." = "Candidate keys cannot contain space."; "There should not be duplicated keys." = "There should not be duplicated keys."; -"Please specify at least 4 candidate keys." = "Please specify at least 4 candidate keys."; -"Maximum 15 candidate keys allowed." = "Maximum 15 candidate keys allowed."; +"Please specify at least 6 candidate keys." = "Please specify at least 6 candidate keys."; +"Maximum 9 candidate keys allowed." = "Maximum 9 candidate keys allowed."; "⚠︎ Phrase replacement mode enabled, interfering user phrase entry." = "⚠︎ Phrase replacement mode enabled, interfering user phrase entry."; "⚠︎ Unhandlable: Chars and Readings in buffer doesn't match." = "⚠︎ Unhandlable: Chars and Readings in buffer doesn't match."; "Per-Char Select Mode" = "Per-Char Select Mode"; diff --git a/Source/Resources/ja.lproj/Localizable.strings b/Source/Resources/ja.lproj/Localizable.strings index 40cf9a33..2e18414b 100644 --- a/Source/Resources/ja.lproj/Localizable.strings +++ b/Source/Resources/ja.lproj/Localizable.strings @@ -51,12 +51,11 @@ "\"%@\" already exists:\n ENTER to boost, SHIFT+COMMAND+ENTER to nerf, \n BackSpace or Delete key to exclude." = "「%@」は既存語彙:\n ENTER で最優先にし、SHIFT+COMMAND+ENTER で優先順位を下げる;\n BackSpace 或いは Delete で排除。"; "Edit Phrase Replacement Table…" = "言葉置換表を編集…"; "Use Phrase Replacement" = "言葉置換機能"; -"Candidates keys cannot be empty." = "言選り用キー陣列に何かキーをご登録ください。"; "Candidate keys can only contain ASCII characters like alphanumericals." = "言選り用キー陣列にはASCII文字だけをご登録ください(英数など)。"; "Candidate keys cannot contain space." = "言選り用キー陣列にスペースキーは登録できません。"; "There should not be duplicated keys." = "言選り用キー陣列に同じキーの重複登録はできません。"; -"Please specify at least 4 candidate keys." = "言選り用キー陣列に少なくとも4つのキーをご登録ください。"; -"Maximum 15 candidate keys allowed." = "言選り用キー陣列には最多15つキー登録できます。"; +"Please specify at least 6 candidate keys." = "言選り用キー陣列に少なくとも6つのキーをご登録ください。"; +"Maximum 9 candidate keys allowed." = "言選り用キー陣列には最多9つキー登録できます。"; "⚠︎ Phrase replacement mode enabled, interfering user phrase entry." = "⚠︎ 言葉置換機能稼働中、新添付言葉にも影響。"; "⚠︎ Unhandlable: Chars and Readings in buffer doesn't match." = "⚠︎ 対処不可:緩衝列の字数は読みの数と不同等。"; "Per-Char Select Mode" = "全候補入力モード"; diff --git a/Source/Resources/zh-Hans.lproj/Localizable.strings b/Source/Resources/zh-Hans.lproj/Localizable.strings index 6bda81d9..23a94622 100644 --- a/Source/Resources/zh-Hans.lproj/Localizable.strings +++ b/Source/Resources/zh-Hans.lproj/Localizable.strings @@ -51,12 +51,11 @@ "\"%@\" already exists:\n ENTER to boost, SHIFT+COMMAND+ENTER to nerf, \n BackSpace or Delete key to exclude." = "「%@」已存在:\n 敲 Enter 以升权、敲 Shift+Command+Enter 以降权;\n 敲 BackSpace 或 Delete 以排除。"; "Edit Phrase Replacement Table…" = "编辑语汇置换表…"; "Use Phrase Replacement" = "使用语汇置换"; -"Candidates keys cannot be empty." = "您必须指定选字键。"; "Candidate keys can only contain ASCII characters like alphanumericals." = "选字键只能是英数等 ASCII 字符。"; "Candidate keys cannot contain space." = "选字键不得包含空格。"; "There should not be duplicated keys." = "选字键不得重复。"; -"Please specify at least 4 candidate keys." = "请至少指定四个选字键。"; -"Maximum 15 candidate keys allowed." = "选字键最多只能指定十五个。"; +"Please specify at least 6 candidate keys." = "请至少指定 6 个选字键。"; +"Maximum 9 candidate keys allowed." = "选字键最多只能指定 9 个。"; "⚠︎ Phrase replacement mode enabled, interfering user phrase entry." = "⚠︎ 语汇置换功能已启用,会波及语汇自订。"; "⚠︎ Unhandlable: Chars and Readings in buffer doesn't match." = "⚠︎ 无法处理:组字区字数与读音数不对应。"; "Per-Char Select Mode" = "模拟逐字选字输入"; diff --git a/Source/Resources/zh-Hant.lproj/Localizable.strings b/Source/Resources/zh-Hant.lproj/Localizable.strings index c5adcb9e..a7e73c72 100644 --- a/Source/Resources/zh-Hant.lproj/Localizable.strings +++ b/Source/Resources/zh-Hant.lproj/Localizable.strings @@ -51,12 +51,11 @@ "\"%@\" already exists:\n ENTER to boost, SHIFT+COMMAND+ENTER to nerf, \n BackSpace or Delete key to exclude." = "「%@」已存在:\n 敲 Enter 以升權、敲 Shift+Command+Enter 以降權;\n 敲 BackSpace 或 Delete 以排除。"; "Edit Phrase Replacement Table…" = "編輯語彙置換表…"; "Use Phrase Replacement" = "使用語彙置換"; -"Candidates keys cannot be empty." = "您必須指定選字鍵。"; "Candidate keys can only contain ASCII characters like alphanumericals." = "選字鍵只能是英數等 ASCII 字符。"; "Candidate keys cannot contain space." = "選字鍵不得包含空格。"; "There should not be duplicated keys." = "選字鍵不得重複。"; -"Please specify at least 4 candidate keys." = "請至少指定四個選字鍵。"; -"Maximum 15 candidate keys allowed." = "選字鍵最多只能指定十五個。"; +"Please specify at least 6 candidate keys." = "請至少指定 6 個選字鍵。"; +"Maximum 9 candidate keys allowed." = "選字鍵最多只能指定 9 個。"; "⚠︎ Phrase replacement mode enabled, interfering user phrase entry." = "⚠︎ 語彙置換功能已啟用,會波及語彙自訂。"; "⚠︎ Unhandlable: Chars and Readings in buffer doesn't match." = "⚠︎ 無法處理:組字區字數與讀音數不對應。"; "Per-Char Select Mode" = "模擬逐字選字輸入";