From 02abb7966833002483300e9f7e6c9ffd2d940a6f Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sat, 30 Jul 2022 23:08:47 +0800 Subject: [PATCH 01/27] KeyHandler // Upgrade certain condition structures. --- .../KeyHandler_HandleCandidate.swift | 4 +- .../ControllerModules/KeyHandler_States.swift | 86 +++++++++---------- 2 files changed, 43 insertions(+), 47 deletions(-) diff --git a/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift b/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift index 83501782..c2cfe773 100644 --- a/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift +++ b/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift @@ -293,14 +293,12 @@ extension KeyHandler { let match: String = (state is InputState.AssociatedPhrases) ? input.inputTextIgnoringModifiers ?? "" : inputText - var j = 0 - while j < ctlCandidateCurrent.keyLabels.count { + for j in 0..注音轉拼音->轉教科書式標調 + tooltipParameterRef[i] = Tekkon.restoreToneOneInZhuyinKey(target: tooltipParameterRef[i]) + tooltipParameterRef[i] = Tekkon.cnvPhonaToHanyuPinyin(target: tooltipParameterRef[i]) + tooltipParameterRef[i] = Tekkon.cnvHanyuPinyinToTextbookStyle(target: tooltipParameterRef[i]) } else { - if readingCursorIndex < compositor.cursor { - composedStringCursorIndex += strNodeValue.utf16.count - readingCursorIndex += spanningLength - readingCursorIndex = min(readingCursorIndex, compositor.cursor) - /// 接下來再處理這麼一種情況: - /// 某些錨點內的當前候選字詞長度與讀音長度不相等。 - /// 但此時游標還是按照每個讀音單位來移動的, - /// 所以需要上下文工具提示來顯示游標的相對位置。 - /// 這裡先計算一下要用在工具提示當中的顯示參數的內容。 - switch compositor.cursor { - case compositor.readings.count...: - // 這裡的 compositor.cursor 數值不可能大於 readings.count,因為會被 Megrez 自動糾正。 - tooltipParameterRef[0] = compositor.readings[compositor.cursor - 1] - case 0: - tooltipParameterRef[1] = compositor.readings[compositor.cursor] - default: - tooltipParameterRef[0] = compositor.readings[compositor.cursor - 1] - tooltipParameterRef[1] = compositor.readings[compositor.cursor] - } - /// 注音轉拼音 - for (i, _) in tooltipParameterRef.enumerated() { - if tooltipParameterRef[i].isEmpty { continue } - if tooltipParameterRef[i].contains("_") { continue } - if mgrPrefs.showHanyuPinyinInCompositionBuffer { // 恢復陰平標記->注音轉拼音->轉教科書式標調 - tooltipParameterRef[i] = Tekkon.restoreToneOneInZhuyinKey(target: tooltipParameterRef[i]) - tooltipParameterRef[i] = Tekkon.cnvPhonaToHanyuPinyin(target: tooltipParameterRef[i]) - tooltipParameterRef[i] = Tekkon.cnvHanyuPinyinToTextbookStyle(target: tooltipParameterRef[i]) - } else { - tooltipParameterRef[i] = Tekkon.cnvZhuyinChainToTextbookReading(target: tooltipParameterRef[i]) - } - } - } + tooltipParameterRef[i] = Tekkon.cnvZhuyinChainToTextbookReading(target: tooltipParameterRef[i]) } } } @@ -126,10 +125,9 @@ extension KeyHandler { // 防止組字區內出現不可列印的字元。 var cleanedComposition = "" for theChar in composedText { - if let charCode = theChar.utf16.first { - if !(theChar.isASCII && !(charCode.isPrintable)) { - cleanedComposition += String(theChar) - } + guard let charCode = theChar.utf16.first else { continue } + if !(theChar.isASCII && !(charCode.isPrintable)) { + cleanedComposition += String(theChar) } } From 8d4530b8c3c83445a09b233ab172487c368a6212 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sat, 30 Jul 2022 23:50:12 +0800 Subject: [PATCH 02/27] KeyHandler // Stop cursor from cutting chars. --- .../ControllerModules/KeyHandler_Core.swift | 2 ++ .../ControllerModules/KeyHandler_States.swift | 20 +++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Source/Modules/ControllerModules/KeyHandler_Core.swift b/Source/Modules/ControllerModules/KeyHandler_Core.swift index ffe181e1..7e63899a 100644 --- a/Source/Modules/ControllerModules/KeyHandler_Core.swift +++ b/Source/Modules/ControllerModules/KeyHandler_Core.swift @@ -49,6 +49,8 @@ protocol KeyHandlerDelegate { class KeyHandler { /// 半衰模組的衰減指數 let kEpsilon: Double = 0.000001 + /// 檢測是否出現游標切斷組字圈內字符的情況 + var isCursorCuttingChar = false /// 規定最大動態爬軌範圍。組字器內超出該範圍的節錨都會被自動標記為「已經手動選字過」,減少爬軌運算負擔。 let kMaxComposingBufferNeedsToWalkSize = Int(max(12, ceil(Double(mgrPrefs.composingBufferSize) / 2))) diff --git a/Source/Modules/ControllerModules/KeyHandler_States.swift b/Source/Modules/ControllerModules/KeyHandler_States.swift index aee36b14..19ea84c0 100644 --- a/Source/Modules/ControllerModules/KeyHandler_States.swift +++ b/Source/Modules/ControllerModules/KeyHandler_States.swift @@ -156,7 +156,11 @@ extension KeyHandler { /// 給工具提示設定提示配色。 if !stateResult.tooltip.isEmpty { + isCursorCuttingChar = true ctlInputMethod.tooltipController.setColor(state: .denialOverflow) + } else { + isCursorCuttingChar = false + ctlInputMethod.tooltipController.setColor(state: .normal) } return stateResult @@ -659,6 +663,7 @@ extension KeyHandler { stateCallback(state) } } else if input.isOptionHold { + isCursorCuttingChar = false if input.isControlHold { return handleEnd(state: state, stateCallback: stateCallback, errorCallback: errorCallback) } @@ -673,7 +678,12 @@ extension KeyHandler { } else { if compositor.cursor < compositor.length { compositor.cursor += 1 - stateCallback(buildInputtingState) + var inputtingState = buildInputtingState + if isCursorCuttingChar == true { + compositor.jumpCursorBySpan(to: .front) + inputtingState = buildInputtingState + } + stateCallback(inputtingState) } else { IME.prtDebugIntel("A96AAD58") errorCallback() @@ -727,6 +737,7 @@ extension KeyHandler { stateCallback(state) } } else if input.isOptionHold { + isCursorCuttingChar = false if input.isControlHold { return handleHome(state: state, stateCallback: stateCallback, errorCallback: errorCallback) } @@ -741,7 +752,12 @@ extension KeyHandler { } else { if compositor.cursor > 0 { compositor.cursor -= 1 - stateCallback(buildInputtingState) + var inputtingState = buildInputtingState + if isCursorCuttingChar == true { + compositor.jumpCursorBySpan(to: .rear) + inputtingState = buildInputtingState + } + stateCallback(inputtingState) } else { IME.prtDebugIntel("7045E6F3") errorCallback() From c2189993f2e932f507c62091f79d709150660ec7 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 31 Jul 2022 22:36:29 +0800 Subject: [PATCH 03/27] KeyHandler // Remove tooltip from buildInputtingState(). --- .../ControllerModules/KeyHandler_States.swift | 48 ++----------------- 1 file changed, 3 insertions(+), 45 deletions(-) diff --git a/Source/Modules/ControllerModules/KeyHandler_States.swift b/Source/Modules/ControllerModules/KeyHandler_States.swift index 19ea84c0..8666bbfe 100644 --- a/Source/Modules/ControllerModules/KeyHandler_States.swift +++ b/Source/Modules/ControllerModules/KeyHandler_States.swift @@ -86,21 +86,10 @@ extension KeyHandler { tooltipParameterRef[0] = compositor.readings[compositor.cursor - 1] tooltipParameterRef[1] = compositor.readings[compositor.cursor] } - /// 注音轉拼音 - guard tooltipParameterRef != ["", ""] else { continue } - for (i, _) in tooltipParameterRef.enumerated() { - if tooltipParameterRef[i].isEmpty { continue } - if tooltipParameterRef[i].contains("_") { continue } - if mgrPrefs.showHanyuPinyinInCompositionBuffer { // 恢復陰平標記->注音轉拼音->轉教科書式標調 - tooltipParameterRef[i] = Tekkon.restoreToneOneInZhuyinKey(target: tooltipParameterRef[i]) - tooltipParameterRef[i] = Tekkon.cnvPhonaToHanyuPinyin(target: tooltipParameterRef[i]) - tooltipParameterRef[i] = Tekkon.cnvHanyuPinyinToTextbookStyle(target: tooltipParameterRef[i]) - } else { - tooltipParameterRef[i] = Tekkon.cnvZhuyinChainToTextbookReading(target: tooltipParameterRef[i]) - } - } } + isCursorCuttingChar = !tooltipParameterRef[0].isEmpty || !tooltipParameterRef[1].isEmpty + /// 再接下來,藉由已經計算成功的「可見游標位置」,咱們計算一下在這個游標之前與之後的 /// 組字區內容,以便之後在這之間插入正在輸入的漢字讀音(藉由鐵恨 composer 注拼槽取得)。 var arrHead = [String.UTF16View.Element]() @@ -132,38 +121,7 @@ extension KeyHandler { } /// 這裡生成準備要拿來回呼的「正在輸入」狀態,但還不能立即使用,因為工具提示仍未完成。 - let stateResult = InputState.Inputting(composingBuffer: cleanedComposition, cursorIndex: cursorIndex) - - /// 根據上文的參數結果來決定生成怎樣的工具提示。 - switch (tooltipParameterRef[0].isEmpty, tooltipParameterRef[1].isEmpty) { - case (true, true): stateResult.tooltip.removeAll() - case (true, false): - stateResult.tooltip = String( - format: NSLocalizedString("Cursor is to the rear of \"%@\".", comment: ""), - tooltipParameterRef[1] - ) - case (false, true): - stateResult.tooltip = String( - format: NSLocalizedString("Cursor is in front of \"%@\".", comment: ""), - tooltipParameterRef[0] - ) - case (false, false): - stateResult.tooltip = String( - format: NSLocalizedString("Cursor is between \"%@\" and \"%@\".", comment: ""), - tooltipParameterRef[0], tooltipParameterRef[1] - ) - } - - /// 給工具提示設定提示配色。 - if !stateResult.tooltip.isEmpty { - isCursorCuttingChar = true - ctlInputMethod.tooltipController.setColor(state: .denialOverflow) - } else { - isCursorCuttingChar = false - ctlInputMethod.tooltipController.setColor(state: .normal) - } - - return stateResult + return InputState.Inputting(composingBuffer: cleanedComposition, cursorIndex: cursorIndex) } // MARK: - 用以生成候選詞陣列及狀態 From 37e408a1b5498863905e3582ba0788461e5a827f Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 31 Jul 2022 00:47:02 +0800 Subject: [PATCH 04/27] InputState // Allow adding user phrases even if reading count mismatch. --- Source/Modules/ControllerModules/InputState.swift | 15 +++++---------- Source/Resources/Base.lproj/Localizable.strings | 2 +- Source/Resources/en.lproj/Localizable.strings | 2 +- Source/Resources/ja.lproj/Localizable.strings | 2 +- .../Resources/zh-Hans.lproj/Localizable.strings | 2 +- .../Resources/zh-Hant.lproj/Localizable.strings | 2 +- 6 files changed, 10 insertions(+), 15 deletions(-) diff --git a/Source/Modules/ControllerModules/InputState.swift b/Source/Modules/ControllerModules/InputState.swift index baf085b8..4059a042 100644 --- a/Source/Modules/ControllerModules/InputState.swift +++ b/Source/Modules/ControllerModules/InputState.swift @@ -264,9 +264,9 @@ enum InputState { private var deleteTargetExists = false var tooltip: String { if composingBuffer.count != readings.count { - ctlInputMethod.tooltipController.setColor(state: .redAlert) + ctlInputMethod.tooltipController.setColor(state: .denialOverflow) return NSLocalizedString( - "⚠︎ Unhandlable: Chars and Readings in buffer doesn't match.", comment: "" + "⚠︎ Beware: Chars and Readings in buffer doesn't match.", comment: "" ) } if mgrPrefs.phraseReplacementEnabled { @@ -376,14 +376,9 @@ enum InputState { } var validToWrite: Bool { - /// The input method allows users to input a string whose length differs - /// from the amount of Bopomofo readings. In this case, the range - /// in the composing buffer and the readings could not match, so - /// we disable the function to write user phrases in this case. - /// 這裡的 deleteTargetExists 是防止使用者排除「詞庫內尚未存在的詞」, - /// 免得使用者誤操作之後靠北「我怎麼敲不了這個詞?」之類的。 - ((composingBuffer.count != readings.count) - || (ctlInputMethod.areWeDeleting && !deleteTargetExists)) + /// 與小麥注音不同,威注音會自動解消「游標插斷字符」的異常狀態,所以允許在字音長度不相符的情況下加詞。 + /// 這裡的 deleteTargetExists 是防止使用者排除「詞庫內尚未存在的詞」。 + (ctlInputMethod.areWeDeleting && !deleteTargetExists) ? false : allowedMarkRange.contains(literalMarkedRange.count) } diff --git a/Source/Resources/Base.lproj/Localizable.strings b/Source/Resources/Base.lproj/Localizable.strings index a1ccc9f1..9b57b255 100644 --- a/Source/Resources/Base.lproj/Localizable.strings +++ b/Source/Resources/Base.lproj/Localizable.strings @@ -37,7 +37,7 @@ "Please specify at least 4 candidate keys." = "Please specify at least 4 candidate keys."; "Maximum 15 candidate keys allowed." = "Maximum 15 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."; +"⚠︎ Beware: Chars and Readings in buffer doesn't match." = "⚠︎ Beware: Chars and Readings in buffer doesn't match."; "Per-Char Select Mode" = "Per-Char Select Mode"; "CNS11643 Mode" = "CNS11643 Mode"; "JIS Shinjitai Output" = "JIS Shinjitai Output"; diff --git a/Source/Resources/en.lproj/Localizable.strings b/Source/Resources/en.lproj/Localizable.strings index a1ccc9f1..9b57b255 100644 --- a/Source/Resources/en.lproj/Localizable.strings +++ b/Source/Resources/en.lproj/Localizable.strings @@ -37,7 +37,7 @@ "Please specify at least 4 candidate keys." = "Please specify at least 4 candidate keys."; "Maximum 15 candidate keys allowed." = "Maximum 15 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."; +"⚠︎ Beware: Chars and Readings in buffer doesn't match." = "⚠︎ Beware: Chars and Readings in buffer doesn't match."; "Per-Char Select Mode" = "Per-Char Select Mode"; "CNS11643 Mode" = "CNS11643 Mode"; "JIS Shinjitai Output" = "JIS Shinjitai Output"; diff --git a/Source/Resources/ja.lproj/Localizable.strings b/Source/Resources/ja.lproj/Localizable.strings index 38e6b238..cfab4637 100644 --- a/Source/Resources/ja.lproj/Localizable.strings +++ b/Source/Resources/ja.lproj/Localizable.strings @@ -37,7 +37,7 @@ "Please specify at least 4 candidate keys." = "言選り用キー陣列に少なくとも4つのキーをご登録ください。"; "Maximum 15 candidate keys allowed." = "言選り用キー陣列には最多15つキー登録できます。"; "⚠︎ Phrase replacement mode enabled, interfering user phrase entry." = "⚠︎ 言葉置換機能稼働中、新添付言葉にも影響。"; -"⚠︎ Unhandlable: Chars and Readings in buffer doesn't match." = "⚠︎ 緩衝列の字数は読みの数と不同等のため、対処不可。"; +"⚠︎ Beware: Chars and Readings in buffer doesn't match." = "⚠︎ 注意:緩衝列の字数は読みの数と不同等。"; "Per-Char Select Mode" = "全候補入力モード"; "CNS11643 Mode" = "全字庫モード"; "JIS Shinjitai Output" = "JIS 新字体モード"; diff --git a/Source/Resources/zh-Hans.lproj/Localizable.strings b/Source/Resources/zh-Hans.lproj/Localizable.strings index 5f2d23fe..2b2136d2 100644 --- a/Source/Resources/zh-Hans.lproj/Localizable.strings +++ b/Source/Resources/zh-Hans.lproj/Localizable.strings @@ -37,7 +37,7 @@ "Please specify at least 4 candidate keys." = "请至少指定四个选字键。"; "Maximum 15 candidate keys allowed." = "选字键最多只能指定十五个。"; "⚠︎ Phrase replacement mode enabled, interfering user phrase entry." = "⚠︎ 语汇置换功能已启用,会波及语汇自订。"; -"⚠︎ Unhandlable: Chars and Readings in buffer doesn't match." = "⚠︎ 无法处理组字区字数与读音数不对应的情形。"; +"⚠︎ Beware: Chars and Readings in buffer doesn't match." = "⚠︎ 注意:组字区字数与读音数不对应。"; "Per-Char Select Mode" = "仿真逐字选字输入"; "CNS11643 Mode" = "全字库模式"; "JIS Shinjitai Output" = "JIS 新字体模式"; diff --git a/Source/Resources/zh-Hant.lproj/Localizable.strings b/Source/Resources/zh-Hant.lproj/Localizable.strings index a2f0fe9c..e6f05920 100644 --- a/Source/Resources/zh-Hant.lproj/Localizable.strings +++ b/Source/Resources/zh-Hant.lproj/Localizable.strings @@ -37,7 +37,7 @@ "Please specify at least 4 candidate keys." = "請至少指定四個選字鍵。"; "Maximum 15 candidate keys allowed." = "選字鍵最多只能指定十五個。"; "⚠︎ Phrase replacement mode enabled, interfering user phrase entry." = "⚠︎ 語彙置換功能已啟用,會波及語彙自訂。"; -"⚠︎ Unhandlable: Chars and Readings in buffer doesn't match." = "⚠︎ 無法處理組字區字數與讀音數不對應的情形。"; +"⚠︎ Beware: Chars and Readings in buffer doesn't match." = "⚠︎ 注意:組字區字數與讀音數不對應。"; "Per-Char Select Mode" = "模擬逐字選字輸入"; "CNS11643 Mode" = "全字庫模式"; "JIS Shinjitai Output" = "JIS 新字體模式"; From 785b67cc5b49c6ff312a5b0b021ed4b87998264c Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 31 Jul 2022 16:00:26 +0800 Subject: [PATCH 05/27] InputState // Nomenclature fix. --- Source/Modules/ControllerModules/InputState.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Modules/ControllerModules/InputState.swift b/Source/Modules/ControllerModules/InputState.swift index 4059a042..795e5e0f 100644 --- a/Source/Modules/ControllerModules/InputState.swift +++ b/Source/Modules/ControllerModules/InputState.swift @@ -38,7 +38,7 @@ enum StateType { case ofNotEmpty case ofInputting case ofMarking - case ofChooseCandidate + case ofChoosingCandidate case ofSymbolTable } @@ -413,7 +413,7 @@ enum InputState { /// .ChoosingCandidate: 叫出選字窗、允許使用者選字。 class ChoosingCandidate: NotEmpty { - override public var type: StateType { .ofChooseCandidate } + override public var type: StateType { .ofChoosingCandidate } private(set) var candidates: [(String, String)] private(set) var isTypingVertical: Bool From bdb7736f4c16e6ccc8fe2e0d4d513a8f9a497dee Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 31 Jul 2022 12:25:41 +0800 Subject: [PATCH 06/27] Repo // Make IMKServer public. --- Source/Modules/main.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Modules/main.swift b/Source/Modules/main.swift index ec01b966..8edeb7b8 100644 --- a/Source/Modules/main.swift +++ b/Source/Modules/main.swift @@ -67,4 +67,6 @@ else { exit(-1) } +public let theServer = server + NSApp.run() From e0762339efa1956b18f59d3b2b10110a7e0269d9 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 31 Jul 2022 12:25:27 +0800 Subject: [PATCH 07/27] Repo // Introducing ctlCandidateProtocol. --- .../ControllerModules/KeyHandler_Core.swift | 4 +- .../KeyHandler_HandleCandidate.swift | 2 +- .../ctlInputMethod_Core.swift | 28 ++++++++++- .../ctlInputMethod_Delegates.swift | 23 +++++++-- .../ctlInputMethod_HandleDisplay.swift | 6 +-- Source/UI/CandidateUI/ctlCandidate.swift | 49 +++++++++++++++---- .../CandidateUI/ctlCandidateUniversal.swift | 7 +-- 7 files changed, 95 insertions(+), 24 deletions(-) diff --git a/Source/Modules/ControllerModules/KeyHandler_Core.swift b/Source/Modules/ControllerModules/KeyHandler_Core.swift index 7e63899a..7e9eedc3 100644 --- a/Source/Modules/ControllerModules/KeyHandler_Core.swift +++ b/Source/Modules/ControllerModules/KeyHandler_Core.swift @@ -34,10 +34,10 @@ import Cocoa /// KeyHandler 委任協定 protocol KeyHandlerDelegate { - func ctlCandidate() -> ctlCandidate + func ctlCandidate() -> ctlCandidateProtocol func keyHandler( _: KeyHandler, didSelectCandidateAt index: Int, - ctlCandidate controller: ctlCandidate + ctlCandidate controller: ctlCandidateProtocol ) func keyHandler(_ keyHandler: KeyHandler, didRequestWriteUserPhraseWith state: InputStateProtocol) -> Bool diff --git a/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift b/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift index c2cfe773..04d31f3d 100644 --- a/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift +++ b/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift @@ -46,7 +46,7 @@ extension KeyHandler { ) -> Bool { let inputText = input.inputText let charCode: UniChar = input.charCode - guard let ctlCandidateCurrent = delegate?.ctlCandidate() else { + guard var ctlCandidateCurrent = delegate?.ctlCandidate() else { IME.prtDebugIntel("06661F6E") errorCallback() return true diff --git a/Source/Modules/ControllerModules/ctlInputMethod_Core.swift b/Source/Modules/ControllerModules/ctlInputMethod_Core.swift index 138df186..0e71c4a8 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_Core.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_Core.swift @@ -41,7 +41,7 @@ class ctlInputMethod: IMKInputController { static var areWeDeleting = false /// 目前在用的的選字窗副本。 - static var ctlCandidateCurrent = ctlCandidateUniversal.init(.horizontal) + static var ctlCandidateCurrent: ctlCandidateProtocol = ctlCandidateUniversal.init(.horizontal) /// 工具提示視窗的副本。 static let tooltipController = TooltipController() @@ -236,4 +236,30 @@ class ctlInputMethod: IMKInputController { _ = sender // 防止格式整理工具毀掉與此對應的參數。 resetKeyHandler() } + + /// 生成 IMK 選字窗專用的候選字串陣列。 + /// - Parameter sender: 呼叫了該函式的客體(無須使用)。 + /// - Returns: IMK 選字窗專用的候選字串陣列。 + override func candidates(_ sender: Any!) -> [Any]! { + _ = sender // 防止格式整理工具毀掉與此對應的參數。 + if let state = state as? InputState.AssociatedPhrases { + return state.candidates.map { theCandidate -> String in + let theConverted = IME.kanjiConversionIfRequired(theCandidate.1) + return (theCandidate.1 == theConverted) ? theCandidate.1 : "\(theConverted)(\(theCandidate.1))" + } + } + if let state = state as? InputState.ChoosingCandidate { + return state.candidates.map { theCandidate -> String in + let theConverted = IME.kanjiConversionIfRequired(theCandidate.1) + return (theCandidate.1 == theConverted) ? theCandidate.1 : "\(theConverted)(\(theCandidate.1))" + } + } + if let state = state as? InputState.SymbolTable { + return state.candidates.map { theCandidate -> String in + let theConverted = IME.kanjiConversionIfRequired(theCandidate.1) + return (theCandidate.1 == theConverted) ? theCandidate.1 : "\(theConverted)(\(theCandidate.1))" + } + } + return .init() + } } diff --git a/Source/Modules/ControllerModules/ctlInputMethod_Delegates.swift b/Source/Modules/ControllerModules/ctlInputMethod_Delegates.swift index c6f19fb3..c97458e4 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_Delegates.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_Delegates.swift @@ -29,11 +29,11 @@ import Foundation // MARK: - KeyHandler Delegate extension ctlInputMethod: KeyHandlerDelegate { - func ctlCandidate() -> ctlCandidate { ctlInputMethod.ctlCandidateCurrent } + func ctlCandidate() -> ctlCandidateProtocol { ctlInputMethod.ctlCandidateCurrent } func keyHandler( _: KeyHandler, didSelectCandidateAt index: Int, - ctlCandidate controller: ctlCandidate + ctlCandidate controller: ctlCandidateProtocol ) { ctlCandidate(controller, didSelectCandidateAtIndex: index) } @@ -70,7 +70,7 @@ extension ctlInputMethod: KeyHandlerDelegate { // MARK: - Candidate Controller Delegate extension ctlInputMethod: ctlCandidateDelegate { - func candidateCountForController(_ controller: ctlCandidate) -> Int { + func candidateCountForController(_ controller: ctlCandidateProtocol) -> Int { _ = controller // 防止格式整理工具毀掉與此對應的參數。 if let state = state as? InputState.ChoosingCandidate { return state.candidates.count @@ -80,7 +80,20 @@ extension ctlInputMethod: ctlCandidateDelegate { return 0 } - func ctlCandidate(_ controller: ctlCandidate, candidateAtIndex index: Int) + /// 直接給出全部的候選字詞的字音配對陣列 + /// - Parameter controller: 對應的控制器。因為有唯一解,所以填錯了也不會有影響。 + /// - Returns: 候選字詞陣列(字音配對)。 + func candidatesForController(_ controller: ctlCandidateProtocol) -> [(String, String)] { + _ = controller // 防止格式整理工具毀掉與此對應的參數。 + if let state = state as? InputState.ChoosingCandidate { + return state.candidates + } else if let state = state as? InputState.AssociatedPhrases { + return state.candidates + } + return .init() + } + + func ctlCandidate(_ controller: ctlCandidateProtocol, candidateAtIndex index: Int) -> (String, String) { _ = controller // 防止格式整理工具毀掉與此對應的參數。 @@ -92,7 +105,7 @@ extension ctlInputMethod: ctlCandidateDelegate { return ("", "") } - func ctlCandidate(_ controller: ctlCandidate, didSelectCandidateAtIndex index: Int) { + func ctlCandidate(_ controller: ctlCandidateProtocol, didSelectCandidateAtIndex index: Int) { _ = controller // 防止格式整理工具毀掉與此對應的參數。 if let state = state as? InputState.SymbolTable, diff --git a/Source/Modules/ControllerModules/ctlInputMethod_HandleDisplay.swift b/Source/Modules/ControllerModules/ctlInputMethod_HandleDisplay.swift index 03bb2c47..b0d8ea9a 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_HandleDisplay.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_HandleDisplay.swift @@ -81,11 +81,11 @@ extension ctlInputMethod { /// 該問題徹底解決的價值並不大,直接等到 macOS 10.x 全線淘汰之後用 SwiftUI 重寫選字窗吧。 if isCandidateWindowVertical { // 縱排輸入時強制使用縱排選字窗 - ctlInputMethod.ctlCandidateCurrent = .init(.vertical) + ctlInputMethod.ctlCandidateCurrent = ctlCandidateUniversal.init(.vertical) } else if mgrPrefs.useHorizontalCandidateList { - ctlInputMethod.ctlCandidateCurrent = .init(.horizontal) + ctlInputMethod.ctlCandidateCurrent = ctlCandidateUniversal.init(.horizontal) } else { - ctlInputMethod.ctlCandidateCurrent = .init(.vertical) + ctlInputMethod.ctlCandidateCurrent = ctlCandidateUniversal.init(.vertical) } // set the attributes for the candidate panel (which uses NSAttributedString) diff --git a/Source/UI/CandidateUI/ctlCandidate.swift b/Source/UI/CandidateUI/ctlCandidate.swift index 4142aff0..53c0c776 100644 --- a/Source/UI/CandidateUI/ctlCandidate.swift +++ b/Source/UI/CandidateUI/ctlCandidate.swift @@ -26,6 +26,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import Cocoa +public enum CandidateLayout { + case horizontal + case vertical +} + public class CandidateKeyLabel: NSObject { public private(set) var key: String public private(set) var displayedText: String @@ -38,21 +43,38 @@ public class CandidateKeyLabel: NSObject { } public protocol ctlCandidateDelegate: AnyObject { - func candidateCountForController(_ controller: ctlCandidate) -> Int - func ctlCandidate(_ controller: ctlCandidate, candidateAtIndex index: Int) + func candidateCountForController(_ controller: ctlCandidateProtocol) -> Int + func candidatesForController(_ controller: ctlCandidateProtocol) -> [(String, String)] + func ctlCandidate(_ controller: ctlCandidateProtocol, candidateAtIndex index: Int) -> (String, String) func ctlCandidate( - _ controller: ctlCandidate, didSelectCandidateAtIndex index: Int + _ controller: ctlCandidateProtocol, didSelectCandidateAtIndex index: Int ) } -public class ctlCandidate: NSWindowController { - public enum Layout { - case horizontal - case vertical - } +public protocol ctlCandidateProtocol { + var currentLayout: CandidateLayout { get set } + var delegate: ctlCandidateDelegate? { get set } + var selectedCandidateIndex: Int { get set } + var visible: Bool { get set } + var windowTopLeftPoint: NSPoint { get set } + var keyLabels: [CandidateKeyLabel] { get set } + var keyLabelFont: NSFont { get set } + var candidateFont: NSFont { get set } + var tooltip: String { get set } - public var currentLayout: Layout = .horizontal + init(_ layout: CandidateLayout) + func reloadData() + func showNextPage() -> Bool + func showPreviousPage() -> Bool + func highlightNextCandidate() -> Bool + func highlightPreviousCandidate() -> Bool + func candidateIndexAtKeyLabelIndex(_: Int) -> Int + func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight height: CGFloat) +} + +public class ctlCandidate: NSWindowController, ctlCandidateProtocol { + public var currentLayout: CandidateLayout = .horizontal public weak var delegate: ctlCandidateDelegate? { didSet { reloadData() @@ -85,6 +107,15 @@ public class ctlCandidate: NSWindowController { } } + required public init(_ layout: CandidateLayout = .horizontal) { + super.init(window: .init()) + visible = false + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + public var keyLabels: [CandidateKeyLabel] = ["1", "2", "3", "4", "5", "6", "7", "8", "9"] .map { CandidateKeyLabel(key: $0, displayedText: $0) diff --git a/Source/UI/CandidateUI/ctlCandidateUniversal.swift b/Source/UI/CandidateUI/ctlCandidateUniversal.swift index 5663fcc8..b96c5f10 100644 --- a/Source/UI/CandidateUI/ctlCandidateUniversal.swift +++ b/Source/UI/CandidateUI/ctlCandidateUniversal.swift @@ -370,7 +370,7 @@ public class ctlCandidateUniversal: ctlCandidate { private var nextPageButton: NSButton private var pageCounterLabel: NSTextField private var currentPageIndex: Int = 0 - override public var currentLayout: Layout { + override public var currentLayout: CandidateLayout { get { candidateView.isVerticalLayout ? .vertical : .horizontal } set { switch newValue { @@ -380,7 +380,7 @@ public class ctlCandidateUniversal: ctlCandidate { } } - public init(_ layout: Layout = .horizontal) { + required public init(_ layout: CandidateLayout = .horizontal) { var contentRect = NSRect(x: 128.0, y: 128.0, width: 0.0, height: 0.0) let styleMask: NSWindow.StyleMask = [.nonactivatingPanel] let panel = NSPanel( @@ -448,7 +448,8 @@ public class ctlCandidateUniversal: ctlCandidate { // MARK: Post-Init() - super.init(window: panel) + super.init(layout) + self.window = panel currentLayout = layout candidateView.target = self From 7d6ac3cd5f51b385cc749585e0ef64ec66536734 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 31 Jul 2022 19:15:09 +0800 Subject: [PATCH 08/27] Repo // Clang-format. --- .../ControllerModules/KeyHandler_HandleComposition.swift | 1 + .../ControllerModules/KeyHandler_HandleInput.swift | 4 ++-- .../Modules/LangModelRelated/SubLMs/lmAssociates.swift | 2 +- Source/Modules/LangModelRelated/SubLMs/lmCoreEX.swift | 2 +- .../Modules/LangModelRelated/SubLMs/lmReplacements.swift | 2 +- Source/UI/CandidateUI/ctlCandidate.swift | 5 +++-- Source/UI/CandidateUI/ctlCandidateUniversal.swift | 4 ++-- Source/WindowControllers/ctlAboutWindow.swift | 4 ++-- Source/WindowControllers/ctlPrefWindow.swift | 9 ++++++--- UserPhraseEditor/ctlAboutWindow.swift | 2 +- vChewingTests/KeyHandlerTestsSCPCCHT.swift | 2 +- 11 files changed, 21 insertions(+), 16 deletions(-) diff --git a/Source/Modules/ControllerModules/KeyHandler_HandleComposition.swift b/Source/Modules/ControllerModules/KeyHandler_HandleComposition.swift index 9cecc0e8..ec5b75d5 100644 --- a/Source/Modules/ControllerModules/KeyHandler_HandleComposition.swift +++ b/Source/Modules/ControllerModules/KeyHandler_HandleComposition.swift @@ -40,6 +40,7 @@ extension KeyHandler { stateCallback: @escaping (InputStateProtocol) -> Void, errorCallback: @escaping () -> Void ) -> Bool? { + guard [.ofInputting, .ofEmpty, .ofEmptyIgnoringPreviousState].contains(state.type) else { return nil } // MARK: 注音按鍵輸入處理 (Handle BPMF Keys) diff --git a/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift b/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift index a0c5756d..073de884 100644 --- a/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift +++ b/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift @@ -159,8 +159,8 @@ extension KeyHandler { // MARK: 注音按鍵輸入處理 (Handle BPMF Keys) if let compositionHandled = handleComposition( - input: input, state: state, stateCallback: stateCallback, errorCallback: errorCallback) - { + input: input, state: state, stateCallback: stateCallback, errorCallback: errorCallback + ) { return compositionHandled } diff --git a/Source/Modules/LangModelRelated/SubLMs/lmAssociates.swift b/Source/Modules/LangModelRelated/SubLMs/lmAssociates.swift index 47f15b8c..47b572c7 100644 --- a/Source/Modules/LangModelRelated/SubLMs/lmAssociates.swift +++ b/Source/Modules/LangModelRelated/SubLMs/lmAssociates.swift @@ -62,7 +62,7 @@ extension vChewing { do { strData = try String(contentsOfFile: path, encoding: .utf8).replacingOccurrences(of: "\t", with: " ") strData = strData.replacingOccurrences(of: "\r", with: "\n") - strData.ranges(splitBy: "\n").filter({ !$0.isEmpty }).forEach { + strData.ranges(splitBy: "\n").filter { !$0.isEmpty }.forEach { let neta = strData[$0].split(separator: " ") if neta.count >= 2 { let theKey = String(neta[0]) diff --git a/Source/Modules/LangModelRelated/SubLMs/lmCoreEX.swift b/Source/Modules/LangModelRelated/SubLMs/lmCoreEX.swift index 19ed60ad..a89337c5 100644 --- a/Source/Modules/LangModelRelated/SubLMs/lmCoreEX.swift +++ b/Source/Modules/LangModelRelated/SubLMs/lmCoreEX.swift @@ -87,7 +87,7 @@ extension vChewing { do { strData = try String(contentsOfFile: path, encoding: .utf8).replacingOccurrences(of: "\t", with: " ") strData = strData.replacingOccurrences(of: "\r", with: "\n") - strData.ranges(splitBy: "\n").filter({ !$0.isEmpty }).forEach { + strData.ranges(splitBy: "\n").filter { !$0.isEmpty }.forEach { let neta = strData[$0].split(separator: " ") if neta.count >= 2, String(neta[0]).first != "#" { if !neta[0].isEmpty, !neta[1].isEmpty { diff --git a/Source/Modules/LangModelRelated/SubLMs/lmReplacements.swift b/Source/Modules/LangModelRelated/SubLMs/lmReplacements.swift index d7df5a93..16d0c0de 100644 --- a/Source/Modules/LangModelRelated/SubLMs/lmReplacements.swift +++ b/Source/Modules/LangModelRelated/SubLMs/lmReplacements.swift @@ -53,7 +53,7 @@ extension vChewing { do { strData = try String(contentsOfFile: path, encoding: .utf8).replacingOccurrences(of: "\t", with: " ") strData = strData.replacingOccurrences(of: "\r", with: "\n") - strData.ranges(splitBy: "\n").filter({ !$0.isEmpty }).forEach { + strData.ranges(splitBy: "\n").filter { !$0.isEmpty }.forEach { let neta = strData[$0].split(separator: " ") if neta.count >= 2 { let theKey = String(neta[0]) diff --git a/Source/UI/CandidateUI/ctlCandidate.swift b/Source/UI/CandidateUI/ctlCandidate.swift index 53c0c776..745f9104 100644 --- a/Source/UI/CandidateUI/ctlCandidate.swift +++ b/Source/UI/CandidateUI/ctlCandidate.swift @@ -107,12 +107,13 @@ public class ctlCandidate: NSWindowController, ctlCandidateProtocol { } } - required public init(_ layout: CandidateLayout = .horizontal) { + public required init(_: CandidateLayout = .horizontal) { super.init(window: .init()) visible = false } - required init?(coder: NSCoder) { + @available(*, unavailable) + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/Source/UI/CandidateUI/ctlCandidateUniversal.swift b/Source/UI/CandidateUI/ctlCandidateUniversal.swift index b96c5f10..9fc78c13 100644 --- a/Source/UI/CandidateUI/ctlCandidateUniversal.swift +++ b/Source/UI/CandidateUI/ctlCandidateUniversal.swift @@ -380,7 +380,7 @@ public class ctlCandidateUniversal: ctlCandidate { } } - required public init(_ layout: CandidateLayout = .horizontal) { + public required init(_ layout: CandidateLayout = .horizontal) { var contentRect = NSRect(x: 128.0, y: 128.0, width: 0.0, height: 0.0) let styleMask: NSWindow.StyleMask = [.nonactivatingPanel] let panel = NSPanel( @@ -449,7 +449,7 @@ public class ctlCandidateUniversal: ctlCandidate { // MARK: Post-Init() super.init(layout) - self.window = panel + window = panel currentLayout = layout candidateView.target = self diff --git a/Source/WindowControllers/ctlAboutWindow.swift b/Source/WindowControllers/ctlAboutWindow.swift index 47b767b0..b359ab0d 100644 --- a/Source/WindowControllers/ctlAboutWindow.swift +++ b/Source/WindowControllers/ctlAboutWindow.swift @@ -57,13 +57,13 @@ class ctlAboutWindow: NSWindowController { ) } - @IBAction func btnBugReport(_ sender: NSButton) { + @IBAction func btnBugReport(_: NSButton) { if let url = URL(string: "https://vchewing.github.io/BUGREPORT.html") { NSWorkspace.shared.open(url) } } - @IBAction func btnWebsite(_ sender: NSButton) { + @IBAction func btnWebsite(_: NSButton) { if let url = URL(string: "https://vchewing.github.io/") { NSWorkspace.shared.open(url) } diff --git a/Source/WindowControllers/ctlPrefWindow.swift b/Source/WindowControllers/ctlPrefWindow.swift index 41791725..3358f148 100644 --- a/Source/WindowControllers/ctlPrefWindow.swift +++ b/Source/WindowControllers/ctlPrefWindow.swift @@ -388,7 +388,8 @@ extension ctlPrefWindow: NSToolbarDelegate { item.label = title if #available(macOS 11.0, *) { item.image = NSImage( - systemSymbolName: "wrench.and.screwdriver.fill", accessibilityDescription: "General Preferences") + systemSymbolName: "wrench.and.screwdriver.fill", accessibilityDescription: "General Preferences" + ) } else { item.image = NSImage(named: NSImage.homeTemplateName) } @@ -399,7 +400,8 @@ extension ctlPrefWindow: NSToolbarDelegate { item.label = title if #available(macOS 11.0, *) { item.image = NSImage( - systemSymbolName: "person.fill.questionmark", accessibilityDescription: "Experiences Preferences") + systemSymbolName: "person.fill.questionmark", accessibilityDescription: "Experiences Preferences" + ) } else { item.image = NSImage(named: NSImage.flowViewTemplateName) } @@ -410,7 +412,8 @@ extension ctlPrefWindow: NSToolbarDelegate { item.label = title if #available(macOS 11.0, *) { item.image = NSImage( - systemSymbolName: "character.book.closed.fill", accessibilityDescription: "Dictionary Preferences") + systemSymbolName: "character.book.closed.fill", accessibilityDescription: "Dictionary Preferences" + ) } else { item.image = NSImage(named: NSImage.bookmarksTemplateName) } diff --git a/UserPhraseEditor/ctlAboutWindow.swift b/UserPhraseEditor/ctlAboutWindow.swift index e8c00e5c..fd437f46 100644 --- a/UserPhraseEditor/ctlAboutWindow.swift +++ b/UserPhraseEditor/ctlAboutWindow.swift @@ -57,7 +57,7 @@ import Cocoa ) } - @IBAction func btnWebsite(_ sender: NSButton) { + @IBAction func btnWebsite(_: NSButton) { if let url = URL(string: "https://vchewing.github.io/") { NSWorkspace.shared.open(url) } diff --git a/vChewingTests/KeyHandlerTestsSCPCCHT.swift b/vChewingTests/KeyHandlerTestsSCPCCHT.swift index edcdbcbc..43ea156b 100644 --- a/vChewingTests/KeyHandlerTestsSCPCCHT.swift +++ b/vChewingTests/KeyHandlerTestsSCPCCHT.swift @@ -357,7 +357,7 @@ extension String { extension vChewing.LMAssociates { public mutating func forceOpenStringInstead(_ strData: String) { - strData.ranges(splitBy: "\n").filter({ !$0.isEmpty }).forEach { + strData.ranges(splitBy: "\n").filter { !$0.isEmpty }.forEach { let neta = strData[$0].split(separator: " ") if neta.count >= 2 { let theKey = String(neta[0]) From 7ea221f7ec89ab585d117c07ee117da4f5dd3382 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 31 Jul 2022 21:40:19 +0800 Subject: [PATCH 09/27] KeyHandler // Suppressing EmptyIgnoringPreviousState(). --- .../KeyHandler_HandleCandidate.swift | 8 ++++--- .../KeyHandler_HandleComposition.swift | 11 +++++---- .../KeyHandler_HandleInput.swift | 2 +- .../ControllerModules/KeyHandler_States.swift | 23 +++++++++++++++---- 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift b/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift index 04d31f3d..3a5e7f73 100644 --- a/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift +++ b/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift @@ -69,6 +69,7 @@ extension KeyHandler { // 所以這裡需要對 compositor.isEmpty 做判定。 clear() stateCallback(InputState.EmptyIgnoringPreviousState()) + stateCallback(InputState.Empty()) } else { stateCallback(buildInputtingState) } @@ -84,6 +85,7 @@ extension KeyHandler { if state is InputState.AssociatedPhrases, !mgrPrefs.alsoConfirmAssociatedCandidatesByEnter { clear() stateCallback(InputState.EmptyIgnoringPreviousState()) + stateCallback(InputState.Empty()) return true } delegate?.keyHandler( @@ -350,10 +352,10 @@ extension KeyHandler { ctlCandidate: ctlCandidateCurrent ) clear() - let empty = InputState.EmptyIgnoringPreviousState() - stateCallback(empty) + stateCallback(InputState.EmptyIgnoringPreviousState()) + stateCallback(InputState.Empty()) return handle( - input: input, state: empty, stateCallback: stateCallback, errorCallback: errorCallback + input: input, state: InputState.Empty(), stateCallback: stateCallback, errorCallback: errorCallback ) } return true diff --git a/Source/Modules/ControllerModules/KeyHandler_HandleComposition.swift b/Source/Modules/ControllerModules/KeyHandler_HandleComposition.swift index ec5b75d5..3c5d631b 100644 --- a/Source/Modules/ControllerModules/KeyHandler_HandleComposition.swift +++ b/Source/Modules/ControllerModules/KeyHandler_HandleComposition.swift @@ -30,18 +30,14 @@ extension KeyHandler { /// 用來處理 KeyHandler.HandleInput() 當中的與組字有關的行為。 /// - Parameters: /// - input: 輸入訊號。 - /// - state: 給定狀態(通常為當前狀態)。 /// - stateCallback: 狀態回呼,交給對應的型別內的專有函式來處理。 /// - errorCallback: 錯誤回呼。 /// - Returns: 告知 IMK「該按鍵是否已經被輸入法攔截處理」。 func handleComposition( input: InputSignal, - state: InputStateProtocol, stateCallback: @escaping (InputStateProtocol) -> Void, errorCallback: @escaping () -> Void ) -> Bool? { - guard [.ofInputting, .ofEmpty, .ofEmptyIgnoringPreviousState].contains(state.type) else { return nil } - // MARK: 注音按鍵輸入處理 (Handle BPMF Keys) var keyConsumedByReading = false @@ -84,7 +80,12 @@ extension KeyHandler { errorCallback() composer.clear() // 根據「組字器是否為空」來判定回呼哪一種狀態。 - stateCallback((compositor.isEmpty) ? InputState.EmptyIgnoringPreviousState() : buildInputtingState) + switch compositor.isEmpty { + case false: stateCallback(buildInputtingState) + case true: + stateCallback(InputState.EmptyIgnoringPreviousState()) + stateCallback(InputState.Empty()) + } return true // 向 IMK 報告說這個按鍵訊號已經被輸入法攔截處理了。 } diff --git a/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift b/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift index 073de884..0574cf46 100644 --- a/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift +++ b/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift @@ -159,7 +159,7 @@ extension KeyHandler { // MARK: 注音按鍵輸入處理 (Handle BPMF Keys) if let compositionHandled = handleComposition( - input: input, state: state, stateCallback: stateCallback, errorCallback: errorCallback + input: input, stateCallback: stateCallback, errorCallback: errorCallback ) { return compositionHandled } diff --git a/Source/Modules/ControllerModules/KeyHandler_States.swift b/Source/Modules/ControllerModules/KeyHandler_States.swift index 8666bbfe..0410e8bd 100644 --- a/Source/Modules/ControllerModules/KeyHandler_States.swift +++ b/Source/Modules/ControllerModules/KeyHandler_States.swift @@ -419,8 +419,12 @@ extension KeyHandler { composer.doBackSpace() } - stateCallback( - composer.isEmpty && compositor.isEmpty ? InputState.EmptyIgnoringPreviousState() : buildInputtingState) + switch composer.isEmpty && compositor.isEmpty { + case false: stateCallback(buildInputtingState) + case true: + stateCallback(InputState.EmptyIgnoringPreviousState()) + stateCallback(InputState.Empty()) + } return true } @@ -457,7 +461,12 @@ extension KeyHandler { walk() let inputting = buildInputtingState // 這裡不用「count > 0」,因為該整數變數只要「!isEmpty」那就必定滿足這個條件。 - stateCallback(inputting.composingBuffer.isEmpty ? InputState.EmptyIgnoringPreviousState() : inputting) + switch inputting.composingBuffer.isEmpty { + case false: stateCallback(inputting) + case true: + stateCallback(InputState.EmptyIgnoringPreviousState()) + stateCallback(InputState.Empty()) + } return true } @@ -569,11 +578,17 @@ extension KeyHandler { /// 此乃 macOS 內建注音輸入法預設之行為,但不太受 Windows 使用者群體之待見。 clear() stateCallback(InputState.EmptyIgnoringPreviousState()) + stateCallback(InputState.Empty()) } else { if composer.isEmpty { return true } /// 如果注拼槽不是空的話,則清空之。 composer.clear() - stateCallback(compositor.isEmpty ? InputState.EmptyIgnoringPreviousState() : buildInputtingState) + switch compositor.isEmpty { + case false: stateCallback(buildInputtingState) + case true: + stateCallback(InputState.EmptyIgnoringPreviousState()) + stateCallback(InputState.Empty()) + } } return true } From 40d33825644a1a5606ab86a5348564c9ab046420 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 31 Jul 2022 21:31:20 +0800 Subject: [PATCH 10/27] Repo // Preparing for IMK Candidate implementation. --- .../ControllerModules/InputState.swift | 6 +-- .../ControllerModules/KeyHandler_Core.swift | 2 +- .../ctlInputMethod_Delegates.swift | 49 ++++++++++++++++++- Source/UI/CandidateUI/ctlCandidate.swift | 1 + 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/Source/Modules/ControllerModules/InputState.swift b/Source/Modules/ControllerModules/InputState.swift index 795e5e0f..50fc85b0 100644 --- a/Source/Modules/ControllerModules/InputState.swift +++ b/Source/Modules/ControllerModules/InputState.swift @@ -29,7 +29,7 @@ import Cocoa // 註:所有 InputState 型別均不適合使用 Struct,因為 Struct 無法相互繼承派生。 // 用以讓每個狀態自描述的 enum。 -enum StateType { +public enum StateType { case ofDeactivated case ofAssociatedPhrases case ofEmpty @@ -43,7 +43,7 @@ enum StateType { } // 所有 InputState 均遵守該協定: -protocol InputStateProtocol { +public protocol InputStateProtocol { var type: StateType { get } } @@ -79,7 +79,7 @@ protocol InputStateProtocol { /// 詞音組合放入語彙濾除清單。 /// - .ChoosingCandidate: 叫出選字窗、允許使用者選字。 /// - .SymbolTable: 波浪鍵符號選單專用的狀態,有自身的特殊處理。 -enum InputState { +public enum InputState { /// .Deactivated: 使用者沒在使用輸入法。 class Deactivated: InputStateProtocol { public var type: StateType { .ofDeactivated } diff --git a/Source/Modules/ControllerModules/KeyHandler_Core.swift b/Source/Modules/ControllerModules/KeyHandler_Core.swift index 7e9eedc3..dc54d872 100644 --- a/Source/Modules/ControllerModules/KeyHandler_Core.swift +++ b/Source/Modules/ControllerModules/KeyHandler_Core.swift @@ -46,7 +46,7 @@ protocol KeyHandlerDelegate { // MARK: - 核心 (Kernel). /// KeyHandler 按鍵調度模組。 -class KeyHandler { +public class KeyHandler { /// 半衰模組的衰減指數 let kEpsilon: Double = 0.000001 /// 檢測是否出現游標切斷組字圈內字符的情況 diff --git a/Source/Modules/ControllerModules/ctlInputMethod_Delegates.swift b/Source/Modules/ControllerModules/ctlInputMethod_Delegates.swift index c97458e4..925bb9c5 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_Delegates.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_Delegates.swift @@ -24,7 +24,7 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import Foundation +import Cocoa // MARK: - KeyHandler Delegate @@ -70,6 +70,53 @@ extension ctlInputMethod: KeyHandlerDelegate { // MARK: - Candidate Controller Delegate extension ctlInputMethod: ctlCandidateDelegate { + func handleDelegateEvent(_ event: NSEvent!) -> Bool { + /// 這裡仍舊需要判斷 flags。之前使輸入法狀態卡住無法敲漢字的問題已在 KeyHandler 內修復。 + /// 這裡不判斷 flags 的話,用方向鍵前後定位光標之後,再次試圖觸發組字區時、反而會在首次按鍵時失敗。 + /// 同時注意:必須在 event.type == .flagsChanged 結尾插入 return false, + /// 否則,每次處理這種判斷時都會觸發 NSInternalInconsistencyException。 + if event.type == .flagsChanged { + return false + } + + // 準備修飾鍵,用來判定是否需要利用就地新增語彙時的 Enter 鍵來砍詞。 + ctlInputMethod.areWeDeleting = event.modifierFlags.contains([.shift, .command]) + + var textFrame = NSRect.zero + + let attributes: [AnyHashable: Any]? = client().attributes( + forCharacterIndex: 0, lineHeightRectangle: &textFrame + ) + + let isTypingVertical = + (attributes?["IMKTextOrientation"] as? NSNumber)?.intValue == 0 || false + + if client().bundleIdentifier() + == "org.atelierInmu.vChewing.vChewingPhraseEditor" + { + IME.areWeUsingOurOwnPhraseEditor = true + } else { + IME.areWeUsingOurOwnPhraseEditor = false + } + + let input = InputSignal(event: event, isVerticalTyping: isTypingVertical) + + // 無法列印的訊號輸入,一概不作處理。 + // 這個過程不能放在 KeyHandler 內,否則不會起作用。 + if !input.charCode.isPrintable { + return false + } + + /// 將按鍵行為與當前輸入法狀態結合起來、交給按鍵調度模組來處理。 + /// 再根據返回的 result bool 數值來告知 IMK「這個按鍵事件是被處理了還是被放行了」。 + let result = keyHandler.handleCandidate(state: state, input: input) { newState in + self.handle(state: newState) + } errorCallback: { + clsSFX.beep() + } + return result + } + func candidateCountForController(_ controller: ctlCandidateProtocol) -> Int { _ = controller // 防止格式整理工具毀掉與此對應的參數。 if let state = state as? InputState.ChoosingCandidate { diff --git a/Source/UI/CandidateUI/ctlCandidate.swift b/Source/UI/CandidateUI/ctlCandidate.swift index 745f9104..017979b6 100644 --- a/Source/UI/CandidateUI/ctlCandidate.swift +++ b/Source/UI/CandidateUI/ctlCandidate.swift @@ -43,6 +43,7 @@ public class CandidateKeyLabel: NSObject { } public protocol ctlCandidateDelegate: AnyObject { + func handleDelegateEvent(_ event: NSEvent!) -> Bool func candidateCountForController(_ controller: ctlCandidateProtocol) -> Int func candidatesForController(_ controller: ctlCandidateProtocol) -> [(String, String)] func ctlCandidate(_ controller: ctlCandidateProtocol, candidateAtIndex index: Int) From ccc033e620bfb15db56ae4a385d1114a706d8b38 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 31 Jul 2022 21:31:30 +0800 Subject: [PATCH 11/27] Repo // Implementing IMKCandidates but disable it. - It's not in a ready-to-use state yet. Still buggy. --- Source/UI/CandidateUI/ctlCandidateIMK.swift | 141 ++++++++++++++++++++ vChewing.xcodeproj/project.pbxproj | 4 + 2 files changed, 145 insertions(+) create mode 100644 Source/UI/CandidateUI/ctlCandidateIMK.swift diff --git a/Source/UI/CandidateUI/ctlCandidateIMK.swift b/Source/UI/CandidateUI/ctlCandidateIMK.swift new file mode 100644 index 00000000..1425a190 --- /dev/null +++ b/Source/UI/CandidateUI/ctlCandidateIMK.swift @@ -0,0 +1,141 @@ +// Copyright (c) 2021 and onwards The vChewing Project (MIT-NTL License). +/* +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +1. The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +2. No trademark license is granted to use the trade names, trademarks, service +marks, or product names of Contributor, except as required to fulfill notice +requirements above. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +import Foundation +import InputMethodKit + +public class ctlCandidateIMK: IMKCandidates, ctlCandidateProtocol { + public var currentLayout: CandidateLayout = .horizontal + + public weak var delegate: ctlCandidateDelegate? { + didSet { + reloadData() + } + } + + public var selectedCandidateIndex: Int = .max + + public var visible: Bool = false { + didSet { + if visible { + show() + } else { + hide() + } + } + } + + public var windowTopLeftPoint: NSPoint = .init(x: 0, y: 0) { + didSet { + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) { + self.set(windowTopLeftPoint: self.windowTopLeftPoint, bottomOutOfScreenAdjustmentHeight: 0) + } + } + } + + public var keyLabels: [CandidateKeyLabel] = ["1", "2", "3", "4", "5", "6", "7", "8", "9"] + .map { + CandidateKeyLabel(key: $0, displayedText: $0) + } + + public var keyLabelFont: NSFont = NSFont.monospacedDigitSystemFont( + ofSize: 14, weight: .medium + ) + public var candidateFont: NSFont = NSFont.systemFont(ofSize: 18) + public var tooltip: String = "" + + var keyCount = 0 + var displayedCandidates = [String]() + + public func specifyLayout(_ layout: CandidateLayout = .horizontal) { + currentLayout = layout + switch currentLayout { + case .horizontal: + setPanelType(kIMKScrollingGridCandidatePanel) + case .vertical: + setPanelType(kIMKSingleColumnScrollingCandidatePanel) + } + setAttributes([IMKCandidatesSendServerKeyEventFirst: false]) + } + + public required init(_ layout: CandidateLayout = .horizontal) { + super.init(server: theServer, panelType: kIMKScrollingGridCandidatePanel) + specifyLayout(layout) + visible = false + } + + @available(*, unavailable) + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public func reloadData() { + guard let delegate = delegate else { return } + let candidates = delegate.candidatesForController(self).map { theCandidate -> String in + let theConverted = IME.kanjiConversionIfRequired(theCandidate.1) + return (theCandidate.1 == theConverted) ? theCandidate.1 : "\(theConverted)(\(theCandidate.1))" + } + setCandidateData(candidates) + keyCount = selectionKeys().count + selectedCandidateIndex = 0 + update() + } + + public func showNextPage() -> Bool { + if selectedCandidateIndex == candidates(self).count - 1 { return false } + selectedCandidateIndex = min(selectedCandidateIndex + keyCount, candidates(self).count - 1) + return selectCandidate(withIdentifier: selectedCandidateIndex) + } + + public func showPreviousPage() -> Bool { + if selectedCandidateIndex == 0 { return true } + selectedCandidateIndex = max(selectedCandidateIndex - keyCount, 0) + return selectCandidate(withIdentifier: selectedCandidateIndex) + } + + public func highlightNextCandidate() -> Bool { + if selectedCandidateIndex == candidates(self).count - 1 { return false } + selectedCandidateIndex = min(selectedCandidateIndex + 1, candidates(self).count - 1) + return selectCandidate(withIdentifier: selectedCandidateIndex) + } + + public func highlightPreviousCandidate() -> Bool { + if selectedCandidateIndex == 0 { return true } + selectedCandidateIndex = max(selectedCandidateIndex - 1, 0) + return selectCandidate(withIdentifier: selectedCandidateIndex) + } + + public func candidateIndexAtKeyLabelIndex(_: Int) -> Int { + selectedCandidateIndex + } + + public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight _: CGFloat = 0) { + setCandidateFrameTopLeft(windowTopLeftPoint) + } + + override public func handle(_ event: NSEvent!, client _: Any!) -> Bool { + guard let delegate = delegate else { return false } + return delegate.handleDelegateEvent(event) + } +} diff --git a/vChewing.xcodeproj/project.pbxproj b/vChewing.xcodeproj/project.pbxproj index 8ce2f6ce..08990e45 100644 --- a/vChewing.xcodeproj/project.pbxproj +++ b/vChewing.xcodeproj/project.pbxproj @@ -116,6 +116,7 @@ 5BF9DA2A28840E6200DBD48E /* template-replacements.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5BF9DA2528840E6200DBD48E /* template-replacements.txt */; }; 5BF9DA2B28840E6200DBD48E /* template-userphrases.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5BF9DA2628840E6200DBD48E /* template-userphrases.txt */; }; 5BF9DA2D288427E000DBD48E /* template-associatedPhrases-cht.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5BF9DA2C2884247800DBD48E /* template-associatedPhrases-cht.txt */; }; + 5BFDF011289635C100417BBC /* ctlCandidateIMK.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BFDF010289635C100417BBC /* ctlCandidateIMK.swift */; }; 6A187E2616004C5900466B2E /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6A187E2816004C5900466B2E /* MainMenu.xib */; }; 6A225A1F23679F2600F685C6 /* NotarizedArchives in Resources */ = {isa = PBXBuildFile; fileRef = 6A225A1E23679F2600F685C6 /* NotarizedArchives */; }; 6A2E40F6253A69DA00D1AE1D /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6A2E40F5253A69DA00D1AE1D /* Images.xcassets */; }; @@ -320,6 +321,7 @@ 5BF9DA2528840E6200DBD48E /* template-replacements.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; lineEnding = 0; path = "template-replacements.txt"; sourceTree = ""; usesTabs = 0; }; 5BF9DA2628840E6200DBD48E /* template-userphrases.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; lineEnding = 0; path = "template-userphrases.txt"; sourceTree = ""; usesTabs = 0; }; 5BF9DA2C2884247800DBD48E /* template-associatedPhrases-cht.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; lineEnding = 0; name = "template-associatedPhrases-cht.txt"; path = "../Data/components/cht/template-associatedPhrases-cht.txt"; sourceTree = ""; }; + 5BFDF010289635C100417BBC /* ctlCandidateIMK.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlCandidateIMK.swift; sourceTree = ""; }; 5BFDF48C27B51867009523B6 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Main.strings"; sourceTree = ""; }; 6A0D4EA215FC0D2D00ABF4B3 /* vChewing.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = vChewing.app; sourceTree = BUILT_PRODUCTS_DIR; }; 6A0D4EF515FC0DA600ABF4B3 /* IME-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "IME-Info.plist"; sourceTree = ""; }; @@ -570,6 +572,7 @@ children = ( 5B62A34027AE7CD900A19448 /* ctlCandidate.swift */, 5B242402284B0D6500520FE4 /* ctlCandidateUniversal.swift */, + 5BFDF010289635C100417BBC /* ctlCandidateIMK.swift */, ); path = CandidateUI; sourceTree = ""; @@ -1203,6 +1206,7 @@ 5BA9FD4727FEF3C9002DE248 /* PreferencesStyleController.swift in Sources */, 5B949BDB2816DDBC00D87B5D /* LMConsolidator.swift in Sources */, 5B38F59F281E2E49007D5F5D /* 3_NodeAnchor.swift in Sources */, + 5BFDF011289635C100417BBC /* ctlCandidateIMK.swift in Sources */, 5B62A34727AE7CD900A19448 /* ctlCandidate.swift in Sources */, 5BA9FD3F27FEF3C8002DE248 /* Pane.swift in Sources */, 5BB802DA27FABA8300CF1C19 /* ctlInputMethod_Menu.swift in Sources */, From cc59bdd1adea70704ecce17770d84ba0a307af4d Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Mon, 1 Aug 2022 11:52:08 +0800 Subject: [PATCH 12/27] Prefs // +keepReadingUponCompositionError. --- Source/Modules/IMEModules/mgrPrefs.swift | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Source/Modules/IMEModules/mgrPrefs.swift b/Source/Modules/IMEModules/mgrPrefs.swift index a48c4e61..336ff596 100644 --- a/Source/Modules/IMEModules/mgrPrefs.swift +++ b/Source/Modules/IMEModules/mgrPrefs.swift @@ -61,6 +61,7 @@ struct UserDef { static let kUseFixecCandidateOrderOnSelection = "UseFixecCandidateOrderOnSelection" static let kAutoCorrectReadingCombination = "AutoCorrectReadingCombination" static let kAlsoConfirmAssociatedCandidatesByEnter = "AlsoConfirmAssociatedCandidatesByEnter" + static let kKeepReadingUponCompositionError = "KeepReadingUponCompositionError" static let kCandidateTextFontName = "CandidateTextFontName" static let kCandidateKeyLabelFontName = "CandidateKeyLabelFontName" @@ -297,6 +298,9 @@ public enum mgrPrefs { UserDefaults.standard.setDefault( mgrPrefs.currencyNumeralsEnabled, forKey: UserDef.kCurrencyNumeralsEnabled ) + UserDefaults.standard.setDefault( + mgrPrefs.keepReadingUponCompositionError, forKey: UserDef.kKeepReadingUponCompositionError + ) UserDefaults.standard.setDefault(mgrPrefs.usingHotKeySCPC, forKey: UserDef.kUsingHotKeySCPC) UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyAssociates, forKey: UserDef.kUsingHotKeyAssociates) @@ -386,6 +390,9 @@ public enum mgrPrefs { @UserDefault(key: UserDef.kAlsoConfirmAssociatedCandidatesByEnter, defaultValue: true) static var alsoConfirmAssociatedCandidatesByEnter: Bool + @UserDefault(key: UserDef.kAlsoConfirmAssociatedCandidatesByEnter, defaultValue: false) + static var keepReadingUponCompositionError: Bool + static var minCandidateLength: Int { mgrPrefs.allowBoostingSingleKanjiAsUserPhrase ? 1 : 2 } @@ -632,7 +639,7 @@ extension mgrPrefs { UserDef.kUsingHotKeyAssociates, UserDef.kUsingHotKeyCNS, UserDef.kUsingHotKeyKangXi, UserDef.kUsingHotKeyJIS, UserDef.kUsingHotKeyHalfWidthASCII, UserDef.kUseFixecCandidateOrderOnSelection, UserDef.kAutoCorrectReadingCombination, UserDef.kAlsoConfirmAssociatedCandidatesByEnter, - UserDef.kCurrencyNumeralsEnabled, UserDef.kUsingHotKeyCurrencyNumerals, + UserDef.kCurrencyNumeralsEnabled, UserDef.kUsingHotKeyCurrencyNumerals, UserDef.kKeepReadingUponCompositionError, ] } From a57bf13f6a4cd0a849e1f3bc924a8f1d9f3e52f5 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Mon, 1 Aug 2022 11:51:58 +0800 Subject: [PATCH 13/27] KeyHandler // +keepReadingUponCompositionError. --- .../KeyHandler_HandleComposition.swift | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Source/Modules/ControllerModules/KeyHandler_HandleComposition.swift b/Source/Modules/ControllerModules/KeyHandler_HandleComposition.swift index 3c5d631b..30a96921 100644 --- a/Source/Modules/ControllerModules/KeyHandler_HandleComposition.swift +++ b/Source/Modules/ControllerModules/KeyHandler_HandleComposition.swift @@ -60,7 +60,7 @@ extension KeyHandler { } } - var composeReading = composer.hasToneMarker() // 這裡不需要做排他性判斷。 + var composeReading = composer.hasToneMarker() && composer.inputValidityCheck(key: input.charCode) // 這裡不需要做排他性判斷。 // 如果當前的按鍵是 Enter 或 Space 的話,這時就可以取出 _composer 內的注音來做檢查了。 // 來看看詞庫內到底有沒有對應的讀音索引。這裡用了類似「|=」的判斷處理方式。 @@ -78,6 +78,12 @@ extension KeyHandler { if !currentLM.hasUnigramsFor(key: readingKey) { IME.prtDebugIntel("B49C0979:語彙庫內無「\(readingKey)」的匹配記錄。") errorCallback() + + if mgrPrefs.keepReadingUponCompositionError { + stateCallback(buildInputtingState) + return true + } + composer.clear() // 根據「組字器是否為空」來判定回呼哪一種狀態。 switch compositor.isEmpty { @@ -143,9 +149,7 @@ extension KeyHandler { return true } - /// 如果此時這個選項是 true 的話,可知當前注拼槽輸入了聲調、且上一次按鍵不是聲調按鍵。 - /// 比方說大千傳統佈局敲「6j」會出現「ˊㄨ」但並不會被認為是「ㄨˊ」,因為先輸入的調號 - /// 並非用來確認這個注音的調號。除非是:「ㄨˊ」「ˊㄨˊ」「ˊㄨˇ」「ˊㄨ 」等。 + /// 是說此時注拼槽並非為空、卻還沒組音。這種情況下只可能是「注拼槽內只有聲調」。 if keyConsumedByReading { // 以回呼組字狀態的方式來執行 updateClientComposingBuffer()。 stateCallback(buildInputtingState) From 496f0ddffa87799c25651f61ce41d11869ccfec5 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Mon, 1 Aug 2022 12:01:37 +0800 Subject: [PATCH 14/27] PrefWindow // +keepReadingUponCompositionError. --- .../KeyHandler_HandleComposition.swift | 1 + Source/WindowNIBs/Base.lproj/frmPrefWindow.xib | 13 +++++++++++++ Source/WindowNIBs/en.lproj/frmPrefWindow.strings | 1 + Source/WindowNIBs/ja.lproj/frmPrefWindow.strings | 1 + .../WindowNIBs/zh-Hans.lproj/frmPrefWindow.strings | 1 + .../WindowNIBs/zh-Hant.lproj/frmPrefWindow.strings | 1 + 6 files changed, 18 insertions(+) diff --git a/Source/Modules/ControllerModules/KeyHandler_HandleComposition.swift b/Source/Modules/ControllerModules/KeyHandler_HandleComposition.swift index 30a96921..d3ba533d 100644 --- a/Source/Modules/ControllerModules/KeyHandler_HandleComposition.swift +++ b/Source/Modules/ControllerModules/KeyHandler_HandleComposition.swift @@ -80,6 +80,7 @@ extension KeyHandler { errorCallback() if mgrPrefs.keepReadingUponCompositionError { + composer.intonation.clear() // 砍掉聲調。 stateCallback(buildInputtingState) return true } diff --git a/Source/WindowNIBs/Base.lproj/frmPrefWindow.xib b/Source/WindowNIBs/Base.lproj/frmPrefWindow.xib index 1d171f73..f59676f7 100644 --- a/Source/WindowNIBs/Base.lproj/frmPrefWindow.xib +++ b/Source/WindowNIBs/Base.lproj/frmPrefWindow.xib @@ -591,12 +591,23 @@ + + @@ -618,6 +629,7 @@ + @@ -630,6 +642,7 @@ + diff --git a/Source/WindowNIBs/en.lproj/frmPrefWindow.strings b/Source/WindowNIBs/en.lproj/frmPrefWindow.strings index 4dcc457d..ab30e302 100644 --- a/Source/WindowNIBs/en.lproj/frmPrefWindow.strings +++ b/Source/WindowNIBs/en.lproj/frmPrefWindow.strings @@ -43,6 +43,7 @@ "chkAlsoConfirmAssociatedCandidatesByEnter.title" = "Allow using Enter key to confirm associated candidate selection"; "chkAutoCorrectReadingCombination.title" = "Automatically correct reading combinations when typing"; "chkFetchSuggestionsFromUserOverrideModel.title" = "Applying typing suggestions from half-life user override model"; +"chkKeepReadingUponCompositionError.title" = "Allow backspace-editing miscomposed readings"; "chkUseFixecCandidateOrderOnSelection.title" = "Always use fixed listing order in candidate window"; "DbW-eq-ZdB.title" = "Starlight"; "dIN-TZ-67g.title" = "Space to +cycle candidates, Shift+Space to +cycle pages"; diff --git a/Source/WindowNIBs/ja.lproj/frmPrefWindow.strings b/Source/WindowNIBs/ja.lproj/frmPrefWindow.strings index 169638b0..b6b74f25 100644 --- a/Source/WindowNIBs/ja.lproj/frmPrefWindow.strings +++ b/Source/WindowNIBs/ja.lproj/frmPrefWindow.strings @@ -43,6 +43,7 @@ "chkAlsoConfirmAssociatedCandidatesByEnter.title" = "Enter キーを連想語彙候補の確認のために使う"; "chkAutoCorrectReadingCombination.title" = "入力中で打ち間違った発音組み合わせを自動的に訂正する"; "chkFetchSuggestionsFromUserOverrideModel.title" = "入力中で臨時記憶モジュールからお薦めの候補を自動的に選ぶ"; +"chkKeepReadingUponCompositionError.title" = "効かぬ音読みを BackSpace で再編集"; "chkUseFixecCandidateOrderOnSelection.title" = "候補文字を固定順番で陳列する"; "DbW-eq-ZdB.title" = "星光"; "dIN-TZ-67g.title" = "Shift+Space で次のページ、Space で次の候補文字を"; diff --git a/Source/WindowNIBs/zh-Hans.lproj/frmPrefWindow.strings b/Source/WindowNIBs/zh-Hans.lproj/frmPrefWindow.strings index 03e1337a..3097603f 100644 --- a/Source/WindowNIBs/zh-Hans.lproj/frmPrefWindow.strings +++ b/Source/WindowNIBs/zh-Hans.lproj/frmPrefWindow.strings @@ -43,6 +43,7 @@ "chkAlsoConfirmAssociatedCandidatesByEnter.title" = "允许使用 Enter 确认当前选中的联想词"; "chkAutoCorrectReadingCombination.title" = "敲字时自动纠正读音组合"; "chkFetchSuggestionsFromUserOverrideModel.title" = "在敲字时自动套用来自半衰记忆模组的建议"; +"chkKeepReadingUponCompositionError.title" = "允许对无效的读音使用 BackSpace 编辑"; "chkUseFixecCandidateOrderOnSelection.title" = "以固定顺序来陈列选字窗内的候选字"; "DbW-eq-ZdB.title" = "星光"; "dIN-TZ-67g.title" = "Shift+Space 换下一页,Space 换选下一个候选字。"; diff --git a/Source/WindowNIBs/zh-Hant.lproj/frmPrefWindow.strings b/Source/WindowNIBs/zh-Hant.lproj/frmPrefWindow.strings index b0019775..ae3ab47a 100644 --- a/Source/WindowNIBs/zh-Hant.lproj/frmPrefWindow.strings +++ b/Source/WindowNIBs/zh-Hant.lproj/frmPrefWindow.strings @@ -43,6 +43,7 @@ "chkAlsoConfirmAssociatedCandidatesByEnter.title" = "允許使用 Enter 確認當前選中的聯想詞"; "chkAutoCorrectReadingCombination.title" = "敲字時自動糾正讀音組合"; "chkFetchSuggestionsFromUserOverrideModel.title" = "在敲字時自動套用來自半衰記憶模組的建議"; +"chkKeepReadingUponCompositionError.title" = "允許對無效的讀音使用 BackSpace 編輯"; "chkUseFixecCandidateOrderOnSelection.title" = "以固定順序來陳列選字窗內的候選字"; "DbW-eq-ZdB.title" = "星光"; "dIN-TZ-67g.title" = "Shift+Space 換下一頁,Space 換選下一個候選字"; From e977694888eae0794b3bed45f0e584c89faa4dd7 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Mon, 1 Aug 2022 12:47:41 +0800 Subject: [PATCH 15/27] PrefUI // +keepReadingUponCompositionError. - Crediting Lukhnos Liu for this change. Also remove intonations on error in order to let it work well with Tekkon. --- Source/Resources/Base.lproj/Localizable.strings | 1 + Source/Resources/en.lproj/Localizable.strings | 1 + Source/Resources/ja.lproj/Localizable.strings | 1 + Source/Resources/zh-Hans.lproj/Localizable.strings | 1 + Source/Resources/zh-Hant.lproj/Localizable.strings | 1 + Source/UI/PrefUI/suiPrefPaneExperience.swift | 8 ++++++++ 6 files changed, 13 insertions(+) diff --git a/Source/Resources/Base.lproj/Localizable.strings b/Source/Resources/Base.lproj/Localizable.strings index 9b57b255..3d21a518 100644 --- a/Source/Resources/Base.lproj/Localizable.strings +++ b/Source/Resources/Base.lproj/Localizable.strings @@ -85,6 +85,7 @@ // SwiftUI Preferences "(Shift+)Space:" = "(Shift+)Space:"; +"Allow backspace-editing miscomposed readings" = "Allow backspace-editing miscomposed readings"; "Allow boosting / excluding a candidate of single kanji" = "Allow boosting / excluding a candidate of single kanji"; "Allow using Enter key to confirm associated candidate selection" = "Allow using Enter key to confirm associated candidate selection"; "Always use fixed listing order in candidate window" = "Always use fixed listing order in candidate window"; diff --git a/Source/Resources/en.lproj/Localizable.strings b/Source/Resources/en.lproj/Localizable.strings index 9b57b255..3d21a518 100644 --- a/Source/Resources/en.lproj/Localizable.strings +++ b/Source/Resources/en.lproj/Localizable.strings @@ -85,6 +85,7 @@ // SwiftUI Preferences "(Shift+)Space:" = "(Shift+)Space:"; +"Allow backspace-editing miscomposed readings" = "Allow backspace-editing miscomposed readings"; "Allow boosting / excluding a candidate of single kanji" = "Allow boosting / excluding a candidate of single kanji"; "Allow using Enter key to confirm associated candidate selection" = "Allow using Enter key to confirm associated candidate selection"; "Always use fixed listing order in candidate window" = "Always use fixed listing order in candidate window"; diff --git a/Source/Resources/ja.lproj/Localizable.strings b/Source/Resources/ja.lproj/Localizable.strings index cfab4637..1f01eb48 100644 --- a/Source/Resources/ja.lproj/Localizable.strings +++ b/Source/Resources/ja.lproj/Localizable.strings @@ -85,6 +85,7 @@ // SwiftUI Preferences "(Shift+)Space:" = "(Shift+)Space:"; +"Allow backspace-editing miscomposed readings" = "効かぬ音読みを BackSpace で再編集"; "Allow boosting / excluding a candidate of single kanji" = "即排除/即最優先にできる候補の文字数の最低限は1字とする"; "Allow using Enter key to confirm associated candidate selection" = "Enter キーを連想語彙候補の確認のために使う"; "Always use fixed listing order in candidate window" = "候補文字を固定順番で陳列する"; diff --git a/Source/Resources/zh-Hans.lproj/Localizable.strings b/Source/Resources/zh-Hans.lproj/Localizable.strings index 2b2136d2..c7a18167 100644 --- a/Source/Resources/zh-Hans.lproj/Localizable.strings +++ b/Source/Resources/zh-Hans.lproj/Localizable.strings @@ -85,6 +85,7 @@ // SwiftUI Preferences "(Shift+)Space:" = "(Shift+)空格键:"; +"Allow backspace-editing miscomposed readings" = "允许对无效的读音使用 BackSpace 编辑"; "Allow boosting / excluding a candidate of single kanji" = "将可以就地升权/排除的候选字词的最短词长设为单个汉字"; "Allow using Enter key to confirm associated candidate selection" = "允许使用 Enter 确认当前选中的联想词"; "Always use fixed listing order in candidate window" = "以固定顺序来陈列选字窗内的候选字"; diff --git a/Source/Resources/zh-Hant.lproj/Localizable.strings b/Source/Resources/zh-Hant.lproj/Localizable.strings index e6f05920..9f20e73e 100644 --- a/Source/Resources/zh-Hant.lproj/Localizable.strings +++ b/Source/Resources/zh-Hant.lproj/Localizable.strings @@ -85,6 +85,7 @@ // SwiftUI Preferences "(Shift+)Space:" = "(Shift+)空格鍵:"; +"Allow backspace-editing miscomposed readings" = "允許對無效的讀音使用 BackSpace 編輯"; "Allow boosting / excluding a candidate of single kanji" = "將可以就地升權/排除的候選字詞的最短詞長設為單個漢字"; "Allow using Enter key to confirm associated candidate selection" = "允許使用 Enter 確認當前選中的聯想詞"; "Always use fixed listing order in candidate window" = "以固定順序來陳列選字窗內的候選字"; diff --git a/Source/UI/PrefUI/suiPrefPaneExperience.swift b/Source/UI/PrefUI/suiPrefPaneExperience.swift index 3271a587..2ba38fed 100644 --- a/Source/UI/PrefUI/suiPrefPaneExperience.swift +++ b/Source/UI/PrefUI/suiPrefPaneExperience.swift @@ -50,6 +50,8 @@ struct suiPrefPaneExperience: View { forKey: UserDef.kAutoCorrectReadingCombination) @State private var selAlsoConfirmAssociatedCandidatesByEnter = UserDefaults.standard.bool( forKey: UserDef.kAlsoConfirmAssociatedCandidatesByEnter) + @State private var selKeepReadingUponCompositionError = UserDefaults.standard.bool( + forKey: UserDef.kKeepReadingUponCompositionError) private let contentWidth: Double = { switch mgrPrefs.appleLanguages[0] { case "ja": @@ -188,6 +190,12 @@ struct suiPrefPaneExperience: View { ).onChange(of: selAlsoConfirmAssociatedCandidatesByEnter) { value in mgrPrefs.alsoConfirmAssociatedCandidatesByEnter = value } + Toggle( + LocalizedStringKey("Allow backspace-editing miscomposed readings"), + isOn: $selKeepReadingUponCompositionError + ).onChange(of: selKeepReadingUponCompositionError) { value in + mgrPrefs.keepReadingUponCompositionError = value + } } } } From ebcc0a0479839e5179b4dbe2e15f559e318181ed Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Mon, 1 Aug 2022 23:32:19 +0800 Subject: [PATCH 16/27] InputState // Let attributedString drawn as node segments when OK. --- .../ControllerModules/InputState.swift | 39 ++++++++++++++----- .../ControllerModules/KeyHandler_States.swift | 9 +++-- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/Source/Modules/ControllerModules/InputState.swift b/Source/Modules/ControllerModules/InputState.swift index 50fc85b0..74026bc6 100644 --- a/Source/Modules/ControllerModules/InputState.swift +++ b/Source/Modules/ControllerModules/InputState.swift @@ -175,6 +175,8 @@ public enum InputState { public var type: StateType { .ofNotEmpty } private(set) var composingBuffer: String private(set) var cursorIndex: Int = 0 { didSet { cursorIndex = max(cursorIndex, 0) } } + private(set) var reading: String = "" + private(set) var nodeValuesArray = [String]() public var composingBufferConverted: String { let converted = IME.kanjiConversionIfRequired(composingBuffer) if converted.utf16.count != composingBuffer.utf16.count @@ -185,21 +187,37 @@ public enum InputState { return converted } - init(composingBuffer: String, cursorIndex: Int) { + init(composingBuffer: String, cursorIndex: Int, reading: String = "", nodeValuesArray: [String] = []) { self.composingBuffer = composingBuffer + self.reading = reading + self.nodeValuesArray = nodeValuesArray defer { self.cursorIndex = cursorIndex } } var attributedString: NSMutableAttributedString { /// 考慮到因為滑鼠點擊等其它行為導致的組字區內容遞交情況, /// 這裡對組字區內容也加上康熙字轉換或者 JIS 漢字轉換處理。 - let attributedString = NSMutableAttributedString( - string: composingBufferConverted, - attributes: [ - .underlineStyle: NSUnderlineStyle.single.rawValue, - .markedClauseSegment: 0, - ] - ) + guard reading.isEmpty else { + let attributedString = NSMutableAttributedString( + string: composingBufferConverted, + attributes: [ + .underlineStyle: NSUnderlineStyle.thick.rawValue, + .markedClauseSegment: 0, + ] + ) + return attributedString + } + let attributedString = NSMutableAttributedString(string: composingBufferConverted) + var newBegin = 0 + for (i, neta) in nodeValuesArray.enumerated() { + attributedString.setAttributes( + [ + .underlineStyle: NSUnderlineStyle.thick.rawValue, + .markedClauseSegment: i, + ], range: NSRange(location: newBegin, length: neta.utf16.count) + ) + newBegin += neta.utf16.count + } return attributedString } @@ -216,8 +234,9 @@ public enum InputState { var textToCommit: String = "" var tooltip: String = "" - override init(composingBuffer: String, cursorIndex: Int) { - super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex) + override init(composingBuffer: String, cursorIndex: Int, reading: String = "", nodeValuesArray: [String] = []) { + super.init( + composingBuffer: composingBuffer, cursorIndex: cursorIndex, reading: reading, nodeValuesArray: nodeValuesArray) } override var description: String { diff --git a/Source/Modules/ControllerModules/KeyHandler_States.swift b/Source/Modules/ControllerModules/KeyHandler_States.swift index 0410e8bd..3c15a79d 100644 --- a/Source/Modules/ControllerModules/KeyHandler_States.swift +++ b/Source/Modules/ControllerModules/KeyHandler_States.swift @@ -38,7 +38,7 @@ extension KeyHandler { /// 「更新內文組字區 (Update the composing buffer)」是指要求客體軟體將組字緩衝區的內容 /// 換成由此處重新生成的組字字串(NSAttributeString,否則會不顯示)。 var tooltipParameterRef: [String] = ["", ""] - var composingBuffer = "" + let nodeValuesArray: [String] = walkedAnchors.map(\.node.currentPair.value) var composedStringCursorIndex = 0 var readingCursorIndex = 0 /// IMK 協定的內文組字區的游標長度與游標位置無法正確統計 UTF8 高萬字(比如 emoji)的長度, @@ -47,7 +47,6 @@ extension KeyHandler { for theAnchor in walkedAnchors { let theNode = theAnchor.node let strNodeValue = theNode.currentPair.value - composingBuffer += strNodeValue let arrSplit: [String] = Array(strNodeValue).map { String($0) } let codepointCount = arrSplit.count /// 藉下述步驟重新將「可見游標位置」對齊至「組字器內的游標所在的讀音位置」。 @@ -95,7 +94,7 @@ extension KeyHandler { var arrHead = [String.UTF16View.Element]() var arrTail = [String.UTF16View.Element]() - for (i, n) in composingBuffer.utf16.enumerated() { + for (i, n) in nodeValuesArray.joined().utf16.enumerated() { if i < composedStringCursorIndex { arrHead.append(n) } else { @@ -121,7 +120,9 @@ extension KeyHandler { } /// 這裡生成準備要拿來回呼的「正在輸入」狀態,但還不能立即使用,因為工具提示仍未完成。 - return InputState.Inputting(composingBuffer: cleanedComposition, cursorIndex: cursorIndex) + return InputState.Inputting( + composingBuffer: cleanedComposition, cursorIndex: cursorIndex, reading: reading, nodeValuesArray: nodeValuesArray + ) } // MARK: - 用以生成候選詞陣列及狀態 From c1ee6924d0114ac96d98eaceca87c579b61ffcc1 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 31 Jul 2022 23:50:58 +0800 Subject: [PATCH 17/27] ctlIME // Change the method of specifying candidate fonts. --- .../ctlInputMethod_HandleDisplay.swift | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/Source/Modules/ControllerModules/ctlInputMethod_HandleDisplay.swift b/Source/Modules/ControllerModules/ctlInputMethod_HandleDisplay.swift index b0d8ea9a..f8aa60fa 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_HandleDisplay.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_HandleDisplay.swift @@ -100,12 +100,22 @@ extension ctlInputMethod { } func candidateFont(name: String?, size: CGFloat) -> NSFont { - let currentMUIFont = - (keyHandler.inputMode == InputMode.imeModeCHS) - ? "Sarasa Term Slab SC" : "Sarasa Term Slab TC" - var finalReturnFont = - NSFont(name: currentMUIFont, size: size) ?? NSFont.systemFont(ofSize: size) - // 對更紗黑體的依賴到 macOS 11 Big Sur 為止。macOS 12 Monterey 開始則依賴系統內建的函式使用蘋方來處理。 + var finalReturnFont: NSFont = + { + switch IME.currentInputMode { + case InputMode.imeModeCHS: + return CTFontCreateUIFontForLanguage(.system, size, "zh-Hans" as CFString) + case InputMode.imeModeCHT: + return (mgrPrefs.shiftJISShinjitaiOutputEnabled || mgrPrefs.chineseConversionEnabled) + ? CTFontCreateUIFontForLanguage(.system, size, "ja" as CFString) + : CTFontCreateUIFontForLanguage(.system, size, "zh-Hant" as CFString) + default: + return CTFontCreateUIFontForLanguage(.system, size, nil) + } + }() + ?? NSFont.systemFont(ofSize: size) + // 上述方法對 macOS 10.11-10.15 有效,但對 macOS 12 Monterey 無效(懷疑是 Bug)。 + // macOS 12 Monterey 開始就用系統內建的函式來處理,相關流程直接寫在 ctlCandidateUniversal 內。 if #available(macOS 12.0, *) { finalReturnFont = NSFont.systemFont(ofSize: size) } if let name = name { return NSFont(name: name, size: size) ?? finalReturnFont From f69455892ba050944282ac6f1ffdc7bb532bf35b Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Tue, 2 Aug 2022 12:03:10 +0800 Subject: [PATCH 18/27] ctlIME // Another attempt to make IME work aftermath. --- Source/Modules/ControllerModules/ctlInputMethod_Core.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Modules/ControllerModules/ctlInputMethod_Core.swift b/Source/Modules/ControllerModules/ctlInputMethod_Core.swift index 0e71c4a8..f188bbb6 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_Core.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_Core.swift @@ -85,6 +85,7 @@ class ctlInputMethod: IMKInputController { keyHandler.delegate = self // 下述兩行很有必要,否則輸入法會在手動重啟之後無法立刻生效。 activateServer(inputClient) + keyHandler.ensureParser() resetKeyHandler() } @@ -99,7 +100,7 @@ class ctlInputMethod: IMKInputController { // 因為偶爾會收到與 activateServer 有關的以「強制拆 nil」為理由的報錯, // 所以這裡添加這句、來試圖應對這種情況。 if keyHandler.delegate == nil { keyHandler.delegate = self } - + setValue(IME.currentInputMode.rawValue, forTag: 114514, client: client()) keyHandler.clear() keyHandler.ensureParser() From 38535768bc6e5b8f90ab627257c9353f34ccf5e9 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Tue, 2 Aug 2022 12:03:59 +0800 Subject: [PATCH 19/27] mgrPrefs // make UserDef cases iterable. --- Source/Modules/IMEModules/mgrPrefs.swift | 344 +++++++++---------- Source/UI/PrefUI/suiPrefPaneDictionary.swift | 14 +- Source/UI/PrefUI/suiPrefPaneExperience.swift | 25 +- Source/UI/PrefUI/suiPrefPaneGeneral.swift | 35 +- Source/UI/PrefUI/suiPrefPaneKeyboard.swift | 19 +- 5 files changed, 218 insertions(+), 219 deletions(-) diff --git a/Source/Modules/IMEModules/mgrPrefs.swift b/Source/Modules/IMEModules/mgrPrefs.swift index 336ff596..a0ba5134 100644 --- a/Source/Modules/IMEModules/mgrPrefs.swift +++ b/Source/Modules/IMEModules/mgrPrefs.swift @@ -26,57 +26,57 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import Cocoa -struct UserDef { - static let kIsDebugModeEnabled = "_DebugMode" - static let kMostRecentInputMode = "MostRecentInputMode" - static let kUserDataFolderSpecified = "UserDataFolderSpecified" - static let kCheckUpdateAutomatically = "CheckUpdateAutomatically" - static let kMandarinParser = "MandarinParser" - static let kBasicKeyboardLayout = "BasicKeyboardLayout" - static let kShowPageButtonsInCandidateWindow = "ShowPageButtonsInCandidateWindow" - static let kCandidateListTextSize = "CandidateListTextSize" - static let kAppleLanguages = "AppleLanguages" - static let kShouldAutoReloadUserDataFiles = "ShouldAutoReloadUserDataFiles" - static let kuseRearCursorMode = "useRearCursorMode" - static let kUseHorizontalCandidateList = "UseHorizontalCandidateList" - static let kComposingBufferSize = "ComposingBufferSize" - static let kChooseCandidateUsingSpace = "ChooseCandidateUsingSpace" - static let kCNS11643Enabled = "CNS11643Enabled" - static let kSymbolInputEnabled = "SymbolInputEnabled" - static let kChineseConversionEnabled = "ChineseConversionEnabled" - static let kShiftJISShinjitaiOutputEnabled = "ShiftJISShinjitaiOutputEnabled" - static let kCurrencyNumeralsEnabled = "CurrencyNumeralsEnabled" - static let kHalfWidthPunctuationEnabled = "HalfWidthPunctuationEnable" - static let kMoveCursorAfterSelectingCandidate = "MoveCursorAfterSelectingCandidate" - static let kEscToCleanInputBuffer = "EscToCleanInputBuffer" - static let kSpecifyShiftTabKeyBehavior = "SpecifyShiftTabKeyBehavior" - static let kSpecifyShiftSpaceKeyBehavior = "SpecifyShiftSpaceKeyBehavior" - static let kAllowBoostingSingleKanjiAsUserPhrase = "AllowBoostingSingleKanjiAsUserPhrase" - static let kUseSCPCTypingMode = "UseSCPCTypingMode" - static let kMaxCandidateLength = "MaxCandidateLength" - static let kShouldNotFartInLieuOfBeep = "ShouldNotFartInLieuOfBeep" - static let kShowHanyuPinyinInCompositionBuffer = "ShowHanyuPinyinInCompositionBuffer" - static let kInlineDumpPinyinInLieuOfZhuyin = "InlineDumpPinyinInLieuOfZhuyin" - static let kFetchSuggestionsFromUserOverrideModel = "FetchSuggestionsFromUserOverrideModel" - static let kUseFixecCandidateOrderOnSelection = "UseFixecCandidateOrderOnSelection" - static let kAutoCorrectReadingCombination = "AutoCorrectReadingCombination" - static let kAlsoConfirmAssociatedCandidatesByEnter = "AlsoConfirmAssociatedCandidatesByEnter" - static let kKeepReadingUponCompositionError = "KeepReadingUponCompositionError" +public enum UserDef: String, CaseIterable { + case kIsDebugModeEnabled = "_DebugMode" + case kMostRecentInputMode = "MostRecentInputMode" + case kUserDataFolderSpecified = "UserDataFolderSpecified" + case kCheckUpdateAutomatically = "CheckUpdateAutomatically" + case kMandarinParser = "MandarinParser" + case kBasicKeyboardLayout = "BasicKeyboardLayout" + case kShowPageButtonsInCandidateWindow = "ShowPageButtonsInCandidateWindow" + case kCandidateListTextSize = "CandidateListTextSize" + case kAppleLanguages = "AppleLanguages" + case kShouldAutoReloadUserDataFiles = "ShouldAutoReloadUserDataFiles" + case kuseRearCursorMode = "useRearCursorMode" + case kUseHorizontalCandidateList = "UseHorizontalCandidateList" + case kComposingBufferSize = "ComposingBufferSize" + case kChooseCandidateUsingSpace = "ChooseCandidateUsingSpace" + case kCNS11643Enabled = "CNS11643Enabled" + case kSymbolInputEnabled = "SymbolInputEnabled" + case kChineseConversionEnabled = "ChineseConversionEnabled" + case kShiftJISShinjitaiOutputEnabled = "ShiftJISShinjitaiOutputEnabled" + case kCurrencyNumeralsEnabled = "CurrencyNumeralsEnabled" + case kHalfWidthPunctuationEnabled = "HalfWidthPunctuationEnable" + case kMoveCursorAfterSelectingCandidate = "MoveCursorAfterSelectingCandidate" + case kEscToCleanInputBuffer = "EscToCleanInputBuffer" + case kSpecifyShiftTabKeyBehavior = "SpecifyShiftTabKeyBehavior" + case kSpecifyShiftSpaceKeyBehavior = "SpecifyShiftSpaceKeyBehavior" + case kAllowBoostingSingleKanjiAsUserPhrase = "AllowBoostingSingleKanjiAsUserPhrase" + case kUseSCPCTypingMode = "UseSCPCTypingMode" + case kMaxCandidateLength = "MaxCandidateLength" + case kShouldNotFartInLieuOfBeep = "ShouldNotFartInLieuOfBeep" + case kShowHanyuPinyinInCompositionBuffer = "ShowHanyuPinyinInCompositionBuffer" + case kInlineDumpPinyinInLieuOfZhuyin = "InlineDumpPinyinInLieuOfZhuyin" + case kFetchSuggestionsFromUserOverrideModel = "FetchSuggestionsFromUserOverrideModel" + case kUseFixecCandidateOrderOnSelection = "UseFixecCandidateOrderOnSelection" + case kAutoCorrectReadingCombination = "AutoCorrectReadingCombination" + case kAlsoConfirmAssociatedCandidatesByEnter = "AlsoConfirmAssociatedCandidatesByEnter" + case kKeepReadingUponCompositionError = "KeepReadingUponCompositionError" - static let kCandidateTextFontName = "CandidateTextFontName" - static let kCandidateKeyLabelFontName = "CandidateKeyLabelFontName" - static let kCandidateKeys = "CandidateKeys" + case kCandidateTextFontName = "CandidateTextFontName" + case kCandidateKeyLabelFontName = "CandidateKeyLabelFontName" + case kCandidateKeys = "CandidateKeys" - static let kAssociatedPhrasesEnabled = "AssociatedPhrasesEnabled" - static let kPhraseReplacementEnabled = "PhraseReplacementEnabled" + case kAssociatedPhrasesEnabled = "AssociatedPhrasesEnabled" + case kPhraseReplacementEnabled = "PhraseReplacementEnabled" - static let kUsingHotKeySCPC = "UsingHotKeySCPC" - static let kUsingHotKeyAssociates = "UsingHotKeyAssociates" - static let kUsingHotKeyCNS = "UsingHotKeyCNS" - static let kUsingHotKeyKangXi = "UsingHotKeyKangXi" - static let kUsingHotKeyJIS = "UsingHotKeyJIS" - static let kUsingHotKeyHalfWidthASCII = "UsingHotKeyHalfWidthASCII" - static let kUsingHotKeyCurrencyNumerals = "UsingHotKeyCurrencyNumerals" + case kUsingHotKeySCPC = "UsingHotKeySCPC" + case kUsingHotKeyAssociates = "UsingHotKeyAssociates" + case kUsingHotKeyCNS = "UsingHotKeyCNS" + case kUsingHotKeyKangXi = "UsingHotKeyKangXi" + case kUsingHotKeyJIS = "UsingHotKeyJIS" + case kUsingHotKeyHalfWidthASCII = "UsingHotKeyHalfWidthASCII" + case kUsingHotKeyCurrencyNumerals = "UsingHotKeyCurrencyNumerals" } private let kDefaultCandidateListTextSize: CGFloat = 18 @@ -238,95 +238,111 @@ enum MandarinParser: Int { public enum mgrPrefs { public static func setMissingDefaults() { - UserDefaults.standard.setDefault(mgrPrefs.isDebugModeEnabled, forKey: UserDef.kIsDebugModeEnabled) - UserDefaults.standard.setDefault(mgrPrefs.mostRecentInputMode, forKey: UserDef.kMostRecentInputMode) - UserDefaults.standard.setDefault(mgrPrefs.checkUpdateAutomatically, forKey: UserDef.kCheckUpdateAutomatically) + UserDefaults.standard.setDefault(mgrPrefs.isDebugModeEnabled, forKey: UserDef.kIsDebugModeEnabled.rawValue) + UserDefaults.standard.setDefault(mgrPrefs.mostRecentInputMode, forKey: UserDef.kMostRecentInputMode.rawValue) UserDefaults.standard.setDefault( - mgrPrefs.showPageButtonsInCandidateWindow, forKey: UserDef.kShowPageButtonsInCandidateWindow - ) - UserDefaults.standard.setDefault(mgrPrefs.symbolInputEnabled, forKey: UserDef.kSymbolInputEnabled) - UserDefaults.standard.setDefault(mgrPrefs.candidateListTextSize, forKey: UserDef.kCandidateListTextSize) - UserDefaults.standard.setDefault(mgrPrefs.chooseCandidateUsingSpace, forKey: UserDef.kChooseCandidateUsingSpace) - UserDefaults.standard.setDefault( - mgrPrefs.shouldAutoReloadUserDataFiles, forKey: UserDef.kShouldAutoReloadUserDataFiles + mgrPrefs.checkUpdateAutomatically, forKey: UserDef.kCheckUpdateAutomatically.rawValue ) UserDefaults.standard.setDefault( - mgrPrefs.specifyShiftTabKeyBehavior, forKey: UserDef.kSpecifyShiftTabKeyBehavior + mgrPrefs.showPageButtonsInCandidateWindow, forKey: UserDef.kShowPageButtonsInCandidateWindow.rawValue + ) + UserDefaults.standard.setDefault(mgrPrefs.symbolInputEnabled, forKey: UserDef.kSymbolInputEnabled.rawValue) + UserDefaults.standard.setDefault(mgrPrefs.candidateListTextSize, forKey: UserDef.kCandidateListTextSize.rawValue) + UserDefaults.standard.setDefault( + mgrPrefs.chooseCandidateUsingSpace, forKey: UserDef.kChooseCandidateUsingSpace.rawValue ) UserDefaults.standard.setDefault( - mgrPrefs.specifyShiftSpaceKeyBehavior, forKey: UserDef.kSpecifyShiftSpaceKeyBehavior - ) - UserDefaults.standard.setDefault(mgrPrefs.useSCPCTypingMode, forKey: UserDef.kUseSCPCTypingMode) - UserDefaults.standard.setDefault(mgrPrefs.associatedPhrasesEnabled, forKey: UserDef.kAssociatedPhrasesEnabled) - UserDefaults.standard.setDefault( - mgrPrefs.useRearCursorMode, forKey: UserDef.kuseRearCursorMode + mgrPrefs.shouldAutoReloadUserDataFiles, forKey: UserDef.kShouldAutoReloadUserDataFiles.rawValue ) UserDefaults.standard.setDefault( - mgrPrefs.moveCursorAfterSelectingCandidate, forKey: UserDef.kMoveCursorAfterSelectingCandidate + mgrPrefs.specifyShiftTabKeyBehavior, forKey: UserDef.kSpecifyShiftTabKeyBehavior.rawValue ) UserDefaults.standard.setDefault( - mgrPrefs.useHorizontalCandidateList, forKey: UserDef.kUseHorizontalCandidateList + mgrPrefs.specifyShiftSpaceKeyBehavior, forKey: UserDef.kSpecifyShiftSpaceKeyBehavior.rawValue ) - UserDefaults.standard.setDefault(mgrPrefs.cns11643Enabled, forKey: UserDef.kCNS11643Enabled) - UserDefaults.standard.setDefault(mgrPrefs.chineseConversionEnabled, forKey: UserDef.kChineseConversionEnabled) + UserDefaults.standard.setDefault(mgrPrefs.useSCPCTypingMode, forKey: UserDef.kUseSCPCTypingMode.rawValue) UserDefaults.standard.setDefault( - mgrPrefs.shiftJISShinjitaiOutputEnabled, forKey: UserDef.kShiftJISShinjitaiOutputEnabled - ) - UserDefaults.standard.setDefault(mgrPrefs.phraseReplacementEnabled, forKey: UserDef.kPhraseReplacementEnabled) - UserDefaults.standard.setDefault(mgrPrefs.shouldNotFartInLieuOfBeep, forKey: UserDef.kShouldNotFartInLieuOfBeep) - UserDefaults.standard.setDefault( - mgrPrefs.showHanyuPinyinInCompositionBuffer, forKey: UserDef.kShowHanyuPinyinInCompositionBuffer + mgrPrefs.associatedPhrasesEnabled, forKey: UserDef.kAssociatedPhrasesEnabled.rawValue ) UserDefaults.standard.setDefault( - mgrPrefs.inlineDumpPinyinInLieuOfZhuyin, forKey: UserDef.kInlineDumpPinyinInLieuOfZhuyin + mgrPrefs.useRearCursorMode, forKey: UserDef.kuseRearCursorMode.rawValue ) UserDefaults.standard.setDefault( - mgrPrefs.allowBoostingSingleKanjiAsUserPhrase, forKey: UserDef.kAllowBoostingSingleKanjiAsUserPhrase + mgrPrefs.moveCursorAfterSelectingCandidate, forKey: UserDef.kMoveCursorAfterSelectingCandidate.rawValue ) UserDefaults.standard.setDefault( - mgrPrefs.fetchSuggestionsFromUserOverrideModel, forKey: UserDef.kFetchSuggestionsFromUserOverrideModel + mgrPrefs.useHorizontalCandidateList, forKey: UserDef.kUseHorizontalCandidateList.rawValue + ) + UserDefaults.standard.setDefault(mgrPrefs.cns11643Enabled, forKey: UserDef.kCNS11643Enabled.rawValue) + UserDefaults.standard.setDefault( + mgrPrefs.chineseConversionEnabled, forKey: UserDef.kChineseConversionEnabled.rawValue ) UserDefaults.standard.setDefault( - mgrPrefs.useFixecCandidateOrderOnSelection, forKey: UserDef.kUseFixecCandidateOrderOnSelection + mgrPrefs.shiftJISShinjitaiOutputEnabled, forKey: UserDef.kShiftJISShinjitaiOutputEnabled.rawValue ) UserDefaults.standard.setDefault( - mgrPrefs.autoCorrectReadingCombination, forKey: UserDef.kAutoCorrectReadingCombination + mgrPrefs.phraseReplacementEnabled, forKey: UserDef.kPhraseReplacementEnabled.rawValue ) UserDefaults.standard.setDefault( - mgrPrefs.alsoConfirmAssociatedCandidatesByEnter, forKey: UserDef.kAlsoConfirmAssociatedCandidatesByEnter + mgrPrefs.shouldNotFartInLieuOfBeep, forKey: UserDef.kShouldNotFartInLieuOfBeep.rawValue ) UserDefaults.standard.setDefault( - mgrPrefs.currencyNumeralsEnabled, forKey: UserDef.kCurrencyNumeralsEnabled + mgrPrefs.showHanyuPinyinInCompositionBuffer, forKey: UserDef.kShowHanyuPinyinInCompositionBuffer.rawValue ) UserDefaults.standard.setDefault( - mgrPrefs.keepReadingUponCompositionError, forKey: UserDef.kKeepReadingUponCompositionError + mgrPrefs.inlineDumpPinyinInLieuOfZhuyin, forKey: UserDef.kInlineDumpPinyinInLieuOfZhuyin.rawValue + ) + UserDefaults.standard.setDefault( + mgrPrefs.allowBoostingSingleKanjiAsUserPhrase, forKey: UserDef.kAllowBoostingSingleKanjiAsUserPhrase.rawValue + ) + UserDefaults.standard.setDefault( + mgrPrefs.fetchSuggestionsFromUserOverrideModel, forKey: UserDef.kFetchSuggestionsFromUserOverrideModel.rawValue + ) + UserDefaults.standard.setDefault( + mgrPrefs.useFixecCandidateOrderOnSelection, forKey: UserDef.kUseFixecCandidateOrderOnSelection.rawValue + ) + UserDefaults.standard.setDefault( + mgrPrefs.autoCorrectReadingCombination, forKey: UserDef.kAutoCorrectReadingCombination.rawValue + ) + UserDefaults.standard.setDefault( + mgrPrefs.alsoConfirmAssociatedCandidatesByEnter, forKey: UserDef.kAlsoConfirmAssociatedCandidatesByEnter.rawValue + ) + UserDefaults.standard.setDefault( + mgrPrefs.currencyNumeralsEnabled, forKey: UserDef.kCurrencyNumeralsEnabled.rawValue + ) + UserDefaults.standard.setDefault( + mgrPrefs.keepReadingUponCompositionError, forKey: UserDef.kKeepReadingUponCompositionError.rawValue ) - UserDefaults.standard.setDefault(mgrPrefs.usingHotKeySCPC, forKey: UserDef.kUsingHotKeySCPC) - UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyAssociates, forKey: UserDef.kUsingHotKeyAssociates) - UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyCNS, forKey: UserDef.kUsingHotKeyCNS) - UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyKangXi, forKey: UserDef.kUsingHotKeyKangXi) - UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyJIS, forKey: UserDef.kUsingHotKeyJIS) - UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyHalfWidthASCII, forKey: UserDef.kUsingHotKeyHalfWidthASCII) - UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyCurrencyNumerals, forKey: UserDef.kUsingHotKeyCurrencyNumerals) + UserDefaults.standard.setDefault(mgrPrefs.usingHotKeySCPC, forKey: UserDef.kUsingHotKeySCPC.rawValue) + UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyAssociates, forKey: UserDef.kUsingHotKeyAssociates.rawValue) + UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyCNS, forKey: UserDef.kUsingHotKeyCNS.rawValue) + UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyKangXi, forKey: UserDef.kUsingHotKeyKangXi.rawValue) + UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyJIS, forKey: UserDef.kUsingHotKeyJIS.rawValue) + UserDefaults.standard.setDefault( + mgrPrefs.usingHotKeyHalfWidthASCII, forKey: UserDef.kUsingHotKeyHalfWidthASCII.rawValue + ) + UserDefaults.standard.setDefault( + mgrPrefs.usingHotKeyCurrencyNumerals, forKey: UserDef.kUsingHotKeyCurrencyNumerals.rawValue + ) UserDefaults.standard.synchronize() } - @UserDefault(key: UserDef.kIsDebugModeEnabled, defaultValue: false) + @UserDefault(key: UserDef.kIsDebugModeEnabled.rawValue, defaultValue: false) static var isDebugModeEnabled: Bool - @UserDefault(key: UserDef.kMostRecentInputMode, defaultValue: "") + @UserDefault(key: UserDef.kMostRecentInputMode.rawValue, defaultValue: "") static var mostRecentInputMode: String - @UserDefault(key: UserDef.kCheckUpdateAutomatically, defaultValue: false) + @UserDefault(key: UserDef.kCheckUpdateAutomatically.rawValue, defaultValue: false) static var checkUpdateAutomatically: Bool - @UserDefault(key: UserDef.kUserDataFolderSpecified, defaultValue: "") + @UserDefault(key: UserDef.kUserDataFolderSpecified.rawValue, defaultValue: "") static var userDataFolderSpecified: String static func ifSpecifiedUserDataPathExistsInPlist() -> Bool { - UserDefaults.standard.object(forKey: UserDef.kUserDataFolderSpecified) != nil + UserDefaults.standard.object(forKey: UserDef.kUserDataFolderSpecified.rawValue) != nil } static func resetSpecifiedUserDataFolder() { @@ -334,10 +350,10 @@ public enum mgrPrefs { IME.initLangModels(userOnly: true) } - @UserDefault(key: UserDef.kAppleLanguages, defaultValue: []) + @UserDefault(key: UserDef.kAppleLanguages.rawValue, defaultValue: []) static var appleLanguages: [String] - @UserDefault(key: UserDef.kMandarinParser, defaultValue: 0) + @UserDefault(key: UserDef.kMandarinParser.rawValue, defaultValue: 0) static var mandarinParser: Int static var mandarinParserName: String { @@ -345,106 +361,106 @@ public enum mgrPrefs { } @UserDefault( - key: UserDef.kBasicKeyboardLayout, defaultValue: "com.apple.keylayout.ZhuyinBopomofo" + key: UserDef.kBasicKeyboardLayout.rawValue, defaultValue: "com.apple.keylayout.ZhuyinBopomofo" ) static var basicKeyboardLayout: String - @UserDefault(key: UserDef.kShowPageButtonsInCandidateWindow, defaultValue: true) + @UserDefault(key: UserDef.kShowPageButtonsInCandidateWindow.rawValue, defaultValue: true) static var showPageButtonsInCandidateWindow: Bool - @CandidateListTextSize(key: UserDef.kCandidateListTextSize) + @CandidateListTextSize(key: UserDef.kCandidateListTextSize.rawValue) static var candidateListTextSize: CGFloat static var minKeyLabelSize: CGFloat { kDefaultMinKeyLabelSize } - @UserDefault(key: UserDef.kShouldAutoReloadUserDataFiles, defaultValue: true) + @UserDefault(key: UserDef.kShouldAutoReloadUserDataFiles.rawValue, defaultValue: true) static var shouldAutoReloadUserDataFiles: Bool - @UserDefault(key: UserDef.kuseRearCursorMode, defaultValue: false) + @UserDefault(key: UserDef.kuseRearCursorMode.rawValue, defaultValue: false) static var useRearCursorMode: Bool - @UserDefault(key: UserDef.kMoveCursorAfterSelectingCandidate, defaultValue: true) + @UserDefault(key: UserDef.kMoveCursorAfterSelectingCandidate.rawValue, defaultValue: true) static var moveCursorAfterSelectingCandidate: Bool - @UserDefault(key: UserDef.kUseHorizontalCandidateList, defaultValue: true) + @UserDefault(key: UserDef.kUseHorizontalCandidateList.rawValue, defaultValue: true) static var useHorizontalCandidateList: Bool - @ComposingBufferSize(key: UserDef.kComposingBufferSize) + @ComposingBufferSize(key: UserDef.kComposingBufferSize.rawValue) static var composingBufferSize: Int - @UserDefault(key: UserDef.kChooseCandidateUsingSpace, defaultValue: true) + @UserDefault(key: UserDef.kChooseCandidateUsingSpace.rawValue, defaultValue: true) static var chooseCandidateUsingSpace: Bool - @UserDefault(key: UserDef.kAllowBoostingSingleKanjiAsUserPhrase, defaultValue: false) + @UserDefault(key: UserDef.kAllowBoostingSingleKanjiAsUserPhrase.rawValue, defaultValue: false) static var allowBoostingSingleKanjiAsUserPhrase: Bool - @UserDefault(key: UserDef.kFetchSuggestionsFromUserOverrideModel, defaultValue: true) + @UserDefault(key: UserDef.kFetchSuggestionsFromUserOverrideModel.rawValue, defaultValue: true) static var fetchSuggestionsFromUserOverrideModel: Bool - @UserDefault(key: UserDef.kUseFixecCandidateOrderOnSelection, defaultValue: false) + @UserDefault(key: UserDef.kUseFixecCandidateOrderOnSelection.rawValue, defaultValue: false) static var useFixecCandidateOrderOnSelection: Bool - @UserDefault(key: UserDef.kAutoCorrectReadingCombination, defaultValue: true) + @UserDefault(key: UserDef.kAutoCorrectReadingCombination.rawValue, defaultValue: true) static var autoCorrectReadingCombination: Bool - @UserDefault(key: UserDef.kAlsoConfirmAssociatedCandidatesByEnter, defaultValue: true) + @UserDefault(key: UserDef.kAlsoConfirmAssociatedCandidatesByEnter.rawValue, defaultValue: true) static var alsoConfirmAssociatedCandidatesByEnter: Bool - @UserDefault(key: UserDef.kAlsoConfirmAssociatedCandidatesByEnter, defaultValue: false) + @UserDefault(key: UserDef.kAlsoConfirmAssociatedCandidatesByEnter.rawValue, defaultValue: false) static var keepReadingUponCompositionError: Bool static var minCandidateLength: Int { mgrPrefs.allowBoostingSingleKanjiAsUserPhrase ? 1 : 2 } - @UserDefault(key: UserDef.kUseSCPCTypingMode, defaultValue: false) + @UserDefault(key: UserDef.kUseSCPCTypingMode.rawValue, defaultValue: false) static var useSCPCTypingMode: Bool static func toggleSCPCTypingModeEnabled() -> Bool { useSCPCTypingMode = !useSCPCTypingMode - UserDefaults.standard.set(useSCPCTypingMode, forKey: UserDef.kUseSCPCTypingMode) + UserDefaults.standard.set(useSCPCTypingMode, forKey: UserDef.kUseSCPCTypingMode.rawValue) return useSCPCTypingMode } - @UserDefault(key: UserDef.kMaxCandidateLength, defaultValue: 10) + @UserDefault(key: UserDef.kMaxCandidateLength.rawValue, defaultValue: 10) static var maxCandidateLength: Int - @UserDefault(key: UserDef.kShouldNotFartInLieuOfBeep, defaultValue: true) + @UserDefault(key: UserDef.kShouldNotFartInLieuOfBeep.rawValue, defaultValue: true) static var shouldNotFartInLieuOfBeep: Bool - @UserDefault(key: UserDef.kShowHanyuPinyinInCompositionBuffer, defaultValue: false) + @UserDefault(key: UserDef.kShowHanyuPinyinInCompositionBuffer.rawValue, defaultValue: false) static var showHanyuPinyinInCompositionBuffer: Bool - @UserDefault(key: UserDef.kInlineDumpPinyinInLieuOfZhuyin, defaultValue: false) + @UserDefault(key: UserDef.kInlineDumpPinyinInLieuOfZhuyin.rawValue, defaultValue: false) static var inlineDumpPinyinInLieuOfZhuyin: Bool static func toggleShouldNotFartInLieuOfBeep() -> Bool { shouldNotFartInLieuOfBeep = !shouldNotFartInLieuOfBeep - UserDefaults.standard.set(shouldNotFartInLieuOfBeep, forKey: UserDef.kShouldNotFartInLieuOfBeep) + UserDefaults.standard.set(shouldNotFartInLieuOfBeep, forKey: UserDef.kShouldNotFartInLieuOfBeep.rawValue) return shouldNotFartInLieuOfBeep } - @UserDefault(key: UserDef.kCNS11643Enabled, defaultValue: false) + @UserDefault(key: UserDef.kCNS11643Enabled.rawValue, defaultValue: false) static var cns11643Enabled: Bool static func toggleCNS11643Enabled() -> Bool { cns11643Enabled = !cns11643Enabled mgrLangModel.setCNSEnabled(cns11643Enabled) // 很重要 - UserDefaults.standard.set(cns11643Enabled, forKey: UserDef.kCNS11643Enabled) + UserDefaults.standard.set(cns11643Enabled, forKey: UserDef.kCNS11643Enabled.rawValue) return cns11643Enabled } - @UserDefault(key: UserDef.kSymbolInputEnabled, defaultValue: true) + @UserDefault(key: UserDef.kSymbolInputEnabled.rawValue, defaultValue: true) static var symbolInputEnabled: Bool static func toggleSymbolInputEnabled() -> Bool { symbolInputEnabled = !symbolInputEnabled mgrLangModel.setSymbolEnabled(symbolInputEnabled) // 很重要 - UserDefaults.standard.set(symbolInputEnabled, forKey: UserDef.kSymbolInputEnabled) + UserDefaults.standard.set(symbolInputEnabled, forKey: UserDef.kSymbolInputEnabled.rawValue) return symbolInputEnabled } - @UserDefault(key: UserDef.kChineseConversionEnabled, defaultValue: false) + @UserDefault(key: UserDef.kChineseConversionEnabled.rawValue, defaultValue: false) static var chineseConversionEnabled: Bool @discardableResult static func toggleChineseConversionEnabled() -> Bool { @@ -453,14 +469,14 @@ public enum mgrPrefs { if chineseConversionEnabled, shiftJISShinjitaiOutputEnabled { toggleShiftJISShinjitaiOutputEnabled() UserDefaults.standard.set( - shiftJISShinjitaiOutputEnabled, forKey: UserDef.kShiftJISShinjitaiOutputEnabled + shiftJISShinjitaiOutputEnabled, forKey: UserDef.kShiftJISShinjitaiOutputEnabled.rawValue ) } - UserDefaults.standard.set(chineseConversionEnabled, forKey: UserDef.kChineseConversionEnabled) + UserDefaults.standard.set(chineseConversionEnabled, forKey: UserDef.kChineseConversionEnabled.rawValue) return chineseConversionEnabled } - @UserDefault(key: UserDef.kShiftJISShinjitaiOutputEnabled, defaultValue: false) + @UserDefault(key: UserDef.kShiftJISShinjitaiOutputEnabled.rawValue, defaultValue: false) static var shiftJISShinjitaiOutputEnabled: Bool @discardableResult static func toggleShiftJISShinjitaiOutputEnabled() -> Bool { @@ -470,12 +486,12 @@ public enum mgrPrefs { toggleChineseConversionEnabled() } UserDefaults.standard.set( - shiftJISShinjitaiOutputEnabled, forKey: UserDef.kShiftJISShinjitaiOutputEnabled + shiftJISShinjitaiOutputEnabled, forKey: UserDef.kShiftJISShinjitaiOutputEnabled.rawValue ) return shiftJISShinjitaiOutputEnabled } - @UserDefault(key: UserDef.kCurrencyNumeralsEnabled, defaultValue: false) + @UserDefault(key: UserDef.kCurrencyNumeralsEnabled.rawValue, defaultValue: false) static var currencyNumeralsEnabled: Bool static func toggleCurrencyNumeralsEnabled() -> Bool { @@ -483,7 +499,7 @@ public enum mgrPrefs { return currencyNumeralsEnabled } - @UserDefault(key: UserDef.kHalfWidthPunctuationEnabled, defaultValue: false) + @UserDefault(key: UserDef.kHalfWidthPunctuationEnabled.rawValue, defaultValue: false) static var halfWidthPunctuationEnabled: Bool static func toggleHalfWidthPunctuationEnabled() -> Bool { @@ -491,24 +507,24 @@ public enum mgrPrefs { return halfWidthPunctuationEnabled } - @UserDefault(key: UserDef.kEscToCleanInputBuffer, defaultValue: true) + @UserDefault(key: UserDef.kEscToCleanInputBuffer.rawValue, defaultValue: true) static var escToCleanInputBuffer: Bool - @UserDefault(key: UserDef.kSpecifyShiftTabKeyBehavior, defaultValue: false) + @UserDefault(key: UserDef.kSpecifyShiftTabKeyBehavior.rawValue, defaultValue: false) static var specifyShiftTabKeyBehavior: Bool - @UserDefault(key: UserDef.kSpecifyShiftSpaceKeyBehavior, defaultValue: false) + @UserDefault(key: UserDef.kSpecifyShiftSpaceKeyBehavior.rawValue, defaultValue: false) static var specifyShiftSpaceKeyBehavior: Bool // MARK: - Optional settings - @UserDefault(key: UserDef.kCandidateTextFontName, defaultValue: nil) + @UserDefault(key: UserDef.kCandidateTextFontName.rawValue, defaultValue: nil) static var candidateTextFontName: String? - @UserDefault(key: UserDef.kCandidateKeyLabelFontName, defaultValue: nil) + @UserDefault(key: UserDef.kCandidateKeyLabelFontName.rawValue, defaultValue: nil) static var candidateKeyLabelFontName: String? - @UserDefault(key: UserDef.kCandidateKeys, defaultValue: kDefaultKeys) + @UserDefault(key: UserDef.kCandidateKeys.rawValue, defaultValue: kDefaultKeys) static var candidateKeys: String static var defaultCandidateKeys: String { @@ -573,46 +589,46 @@ public enum mgrPrefs { } } - @UserDefault(key: UserDef.kPhraseReplacementEnabled, defaultValue: false) + @UserDefault(key: UserDef.kPhraseReplacementEnabled.rawValue, defaultValue: false) static var phraseReplacementEnabled: Bool static func togglePhraseReplacementEnabled() -> Bool { phraseReplacementEnabled = !phraseReplacementEnabled mgrLangModel.setPhraseReplacementEnabled(phraseReplacementEnabled) - UserDefaults.standard.set(phraseReplacementEnabled, forKey: UserDef.kPhraseReplacementEnabled) + UserDefaults.standard.set(phraseReplacementEnabled, forKey: UserDef.kPhraseReplacementEnabled.rawValue) return phraseReplacementEnabled } - @UserDefault(key: UserDef.kAssociatedPhrasesEnabled, defaultValue: false) + @UserDefault(key: UserDef.kAssociatedPhrasesEnabled.rawValue, defaultValue: false) static var associatedPhrasesEnabled: Bool static func toggleAssociatedPhrasesEnabled() -> Bool { associatedPhrasesEnabled = !associatedPhrasesEnabled - UserDefaults.standard.set(associatedPhrasesEnabled, forKey: UserDef.kAssociatedPhrasesEnabled) + UserDefaults.standard.set(associatedPhrasesEnabled, forKey: UserDef.kAssociatedPhrasesEnabled.rawValue) return associatedPhrasesEnabled } // MARK: - Keyboard HotKey Enable / Disable - @UserDefault(key: UserDef.kUsingHotKeySCPC, defaultValue: true) + @UserDefault(key: UserDef.kUsingHotKeySCPC.rawValue, defaultValue: true) static var usingHotKeySCPC: Bool - @UserDefault(key: UserDef.kUsingHotKeyAssociates, defaultValue: true) + @UserDefault(key: UserDef.kUsingHotKeyAssociates.rawValue, defaultValue: true) static var usingHotKeyAssociates: Bool - @UserDefault(key: UserDef.kUsingHotKeyCNS, defaultValue: true) + @UserDefault(key: UserDef.kUsingHotKeyCNS.rawValue, defaultValue: true) static var usingHotKeyCNS: Bool - @UserDefault(key: UserDef.kUsingHotKeyKangXi, defaultValue: true) + @UserDefault(key: UserDef.kUsingHotKeyKangXi.rawValue, defaultValue: true) static var usingHotKeyKangXi: Bool - @UserDefault(key: UserDef.kUsingHotKeyJIS, defaultValue: true) + @UserDefault(key: UserDef.kUsingHotKeyJIS.rawValue, defaultValue: true) static var usingHotKeyJIS: Bool - @UserDefault(key: UserDef.kUsingHotKeyHalfWidthASCII, defaultValue: true) + @UserDefault(key: UserDef.kUsingHotKeyHalfWidthASCII.rawValue, defaultValue: true) static var usingHotKeyHalfWidthASCII: Bool - @UserDefault(key: UserDef.kUsingHotKeyCurrencyNumerals, defaultValue: true) + @UserDefault(key: UserDef.kUsingHotKeyCurrencyNumerals.rawValue, defaultValue: true) static var usingHotKeyCurrencyNumerals: Bool } @@ -621,45 +637,23 @@ public enum mgrPrefs { var snapshot: [String: Any]? extension mgrPrefs { - static var allKeys: [String] { - [ - UserDef.kIsDebugModeEnabled, UserDef.kMostRecentInputMode, UserDef.kUserDataFolderSpecified, - UserDef.kCheckUpdateAutomatically, UserDef.kMandarinParser, UserDef.kBasicKeyboardLayout, - UserDef.kShowPageButtonsInCandidateWindow, UserDef.kCandidateListTextSize, UserDef.kAppleLanguages, - UserDef.kShouldAutoReloadUserDataFiles, UserDef.kuseRearCursorMode, UserDef.kUseHorizontalCandidateList, - UserDef.kComposingBufferSize, UserDef.kChooseCandidateUsingSpace, UserDef.kCNS11643Enabled, - UserDef.kSymbolInputEnabled, UserDef.kChineseConversionEnabled, UserDef.kShiftJISShinjitaiOutputEnabled, - UserDef.kHalfWidthPunctuationEnabled, UserDef.kMoveCursorAfterSelectingCandidate, UserDef.kEscToCleanInputBuffer, - UserDef.kSpecifyShiftTabKeyBehavior, UserDef.kSpecifyShiftSpaceKeyBehavior, - UserDef.kAllowBoostingSingleKanjiAsUserPhrase, UserDef.kUseSCPCTypingMode, UserDef.kMaxCandidateLength, - UserDef.kShouldNotFartInLieuOfBeep, UserDef.kShowHanyuPinyinInCompositionBuffer, - UserDef.kInlineDumpPinyinInLieuOfZhuyin, UserDef.kFetchSuggestionsFromUserOverrideModel, - UserDef.kCandidateTextFontName, UserDef.kCandidateKeyLabelFontName, UserDef.kCandidateKeys, - UserDef.kAssociatedPhrasesEnabled, UserDef.kPhraseReplacementEnabled, UserDef.kUsingHotKeySCPC, - UserDef.kUsingHotKeyAssociates, UserDef.kUsingHotKeyCNS, UserDef.kUsingHotKeyKangXi, UserDef.kUsingHotKeyJIS, - UserDef.kUsingHotKeyHalfWidthASCII, UserDef.kUseFixecCandidateOrderOnSelection, - UserDef.kAutoCorrectReadingCombination, UserDef.kAlsoConfirmAssociatedCandidatesByEnter, - UserDef.kCurrencyNumeralsEnabled, UserDef.kUsingHotKeyCurrencyNumerals, UserDef.kKeepReadingUponCompositionError, - ] - } - func reset() { - mgrPrefs.allKeys.forEach { - UserDefaults.standard.removeObject(forKey: $0) + UserDef.allCases.forEach { + UserDefaults.standard.removeObject(forKey: $0.rawValue) } } func makeSnapshot() -> [String: Any] { var dict = [String: Any]() - mgrPrefs.allKeys.forEach { - dict[$0] = UserDefaults.standard.object(forKey: $0) + UserDef.allCases.forEach { + dict[$0.rawValue] = UserDefaults.standard.object(forKey: $0.rawValue) } return dict } func restore(from snapshot: [String: Any]) { - mgrPrefs.allKeys.forEach { - UserDefaults.standard.set(snapshot[$0], forKey: $0) + UserDef.allCases.forEach { + UserDefaults.standard.set(snapshot[$0.rawValue], forKey: $0.rawValue) } } } diff --git a/Source/UI/PrefUI/suiPrefPaneDictionary.swift b/Source/UI/PrefUI/suiPrefPaneDictionary.swift index 922b77f1..0d9ea835 100644 --- a/Source/UI/PrefUI/suiPrefPaneDictionary.swift +++ b/Source/UI/PrefUI/suiPrefPaneDictionary.swift @@ -28,19 +28,19 @@ import SwiftUI struct suiPrefPaneDictionary: View { private var fdrDefault = mgrLangModel.dataFolderPath(isDefaultFolder: true) @State private var tbxUserDataPathSpecified: String = - UserDefaults.standard.string(forKey: UserDef.kUserDataFolderSpecified) + UserDefaults.standard.string(forKey: UserDef.kUserDataFolderSpecified.rawValue) ?? mgrLangModel.dataFolderPath(isDefaultFolder: true) @State private var selAutoReloadUserData: Bool = UserDefaults.standard.bool( - forKey: UserDef.kShouldAutoReloadUserDataFiles) - @State private var selEnableCNS11643: Bool = UserDefaults.standard.bool(forKey: UserDef.kCNS11643Enabled) + forKey: UserDef.kShouldAutoReloadUserDataFiles.rawValue) + @State private var selEnableCNS11643: Bool = UserDefaults.standard.bool(forKey: UserDef.kCNS11643Enabled.rawValue) @State private var selEnableSymbolInputSupport: Bool = UserDefaults.standard.bool( - forKey: UserDef.kSymbolInputEnabled) + forKey: UserDef.kSymbolInputEnabled.rawValue) @State private var selAllowBoostingSingleKanjiAsUserPhrase: Bool = UserDefaults.standard.bool( - forKey: UserDef.kAllowBoostingSingleKanjiAsUserPhrase) + forKey: UserDef.kAllowBoostingSingleKanjiAsUserPhrase.rawValue) @State private var selFetchSuggestionsFromUserOverrideModel: Bool = UserDefaults.standard.bool( - forKey: UserDef.kFetchSuggestionsFromUserOverrideModel) + forKey: UserDef.kFetchSuggestionsFromUserOverrideModel.rawValue) @State private var selUseFixecCandidateOrderOnSelection: Bool = UserDefaults.standard.bool( - forKey: UserDef.kUseFixecCandidateOrderOnSelection) + forKey: UserDef.kUseFixecCandidateOrderOnSelection.rawValue) private let contentWidth: Double = { switch mgrPrefs.appleLanguages[0] { case "ja": diff --git a/Source/UI/PrefUI/suiPrefPaneExperience.swift b/Source/UI/PrefUI/suiPrefPaneExperience.swift index 2ba38fed..908eebb4 100644 --- a/Source/UI/PrefUI/suiPrefPaneExperience.swift +++ b/Source/UI/PrefUI/suiPrefPaneExperience.swift @@ -29,29 +29,30 @@ import SwiftUI struct suiPrefPaneExperience: View { @State private var selSelectionKeysList = mgrPrefs.suggestedCandidateKeys @State private var selSelectionKeys = - (UserDefaults.standard.string(forKey: UserDef.kCandidateKeys) ?? mgrPrefs.defaultCandidateKeys) as String + (UserDefaults.standard.string(forKey: UserDef.kCandidateKeys.rawValue) ?? mgrPrefs.defaultCandidateKeys) as String @State private var selCursorPosition = UserDefaults.standard.bool( - forKey: UserDef.kuseRearCursorMode) ? 1 : 0 + forKey: UserDef.kuseRearCursorMode.rawValue) ? 1 : 0 @State private var selPushCursorAfterSelection = UserDefaults.standard.bool( - forKey: UserDef.kMoveCursorAfterSelectingCandidate) + forKey: UserDef.kMoveCursorAfterSelectingCandidate.rawValue) @State private var selKeyBehaviorShiftTab = - UserDefaults.standard.bool(forKey: UserDef.kSpecifyShiftTabKeyBehavior) ? 1 : 0 + UserDefaults.standard.bool(forKey: UserDef.kSpecifyShiftTabKeyBehavior.rawValue) ? 1 : 0 @State private var selKeyBehaviorShiftSpace = UserDefaults.standard.bool( - forKey: UserDef.kSpecifyShiftSpaceKeyBehavior) ? 1 : 0 + forKey: UserDef.kSpecifyShiftSpaceKeyBehavior.rawValue) ? 1 : 0 @State private var selKeyBehaviorSpaceForCallingCandidate = UserDefaults.standard.bool( - forKey: UserDef.kChooseCandidateUsingSpace) + forKey: UserDef.kChooseCandidateUsingSpace.rawValue) @State private var selKeyBehaviorESCForClearingTheBuffer = UserDefaults.standard.bool( - forKey: UserDef.kEscToCleanInputBuffer) - @State private var selEnableSCPCTypingMode = UserDefaults.standard.bool(forKey: UserDef.kUseSCPCTypingMode) - @State private var selComposingBufferSize = UserDefaults.standard.integer(forKey: UserDef.kComposingBufferSize) + forKey: UserDef.kEscToCleanInputBuffer.rawValue) + @State private var selEnableSCPCTypingMode = UserDefaults.standard.bool(forKey: UserDef.kUseSCPCTypingMode.rawValue) + @State private var selComposingBufferSize = UserDefaults.standard.integer( + forKey: UserDef.kComposingBufferSize.rawValue) @State private var selAutoCorrectReadingCombination = UserDefaults.standard.bool( - forKey: UserDef.kAutoCorrectReadingCombination) + forKey: UserDef.kAutoCorrectReadingCombination.rawValue) @State private var selAlsoConfirmAssociatedCandidatesByEnter = UserDefaults.standard.bool( - forKey: UserDef.kAlsoConfirmAssociatedCandidatesByEnter) + forKey: UserDef.kAlsoConfirmAssociatedCandidatesByEnter.rawValue) @State private var selKeepReadingUponCompositionError = UserDefaults.standard.bool( - forKey: UserDef.kKeepReadingUponCompositionError) + forKey: UserDef.kKeepReadingUponCompositionError.rawValue) private let contentWidth: Double = { switch mgrPrefs.appleLanguages[0] { case "ja": diff --git a/Source/UI/PrefUI/suiPrefPaneGeneral.swift b/Source/UI/PrefUI/suiPrefPaneGeneral.swift index 093b9ba9..e1231ea2 100644 --- a/Source/UI/PrefUI/suiPrefPaneGeneral.swift +++ b/Source/UI/PrefUI/suiPrefPaneGeneral.swift @@ -27,29 +27,32 @@ import SwiftUI @available(macOS 11.0, *) struct suiPrefPaneGeneral: View { - @State private var selCandidateUIFontSize = UserDefaults.standard.integer(forKey: UserDef.kCandidateListTextSize) + @State private var selCandidateUIFontSize = UserDefaults.standard.integer( + forKey: UserDef.kCandidateListTextSize.rawValue) @State private var selUILanguage: [String] = IME.arrSupportedLocales.contains( - ((UserDefaults.standard.object(forKey: UserDef.kAppleLanguages) == nil) - ? ["auto"] : UserDefaults.standard.array(forKey: UserDef.kAppleLanguages) as? [String] ?? ["auto"])[0]) - ? ((UserDefaults.standard.object(forKey: UserDef.kAppleLanguages) == nil) - ? ["auto"] : UserDefaults.standard.array(forKey: UserDef.kAppleLanguages) as? [String] ?? ["auto"]) + ((UserDefaults.standard.object(forKey: UserDef.kAppleLanguages.rawValue) == nil) + ? ["auto"] : UserDefaults.standard.array(forKey: UserDef.kAppleLanguages.rawValue) as? [String] ?? ["auto"])[0]) + ? ((UserDefaults.standard.object(forKey: UserDef.kAppleLanguages.rawValue) == nil) + ? ["auto"] : UserDefaults.standard.array(forKey: UserDef.kAppleLanguages.rawValue) as? [String] ?? ["auto"]) : ["auto"] @State private var selEnableHorizontalCandidateLayout = UserDefaults.standard.bool( - forKey: UserDef.kUseHorizontalCandidateList) + forKey: UserDef.kUseHorizontalCandidateList.rawValue) @State private var selShowPageButtonsInCandidateUI = UserDefaults.standard.bool( - forKey: UserDef.kShowPageButtonsInCandidateWindow) + forKey: UserDef.kShowPageButtonsInCandidateWindow.rawValue) @State private var selEnableKanjiConvToKangXi = UserDefaults.standard.bool( - forKey: UserDef.kChineseConversionEnabled) + forKey: UserDef.kChineseConversionEnabled.rawValue) @State private var selEnableKanjiConvToJIS = UserDefaults.standard.bool( - forKey: UserDef.kShiftJISShinjitaiOutputEnabled) + forKey: UserDef.kShiftJISShinjitaiOutputEnabled.rawValue) @State private var selShowHanyuPinyinInCompositionBuffer = UserDefaults.standard.bool( - forKey: UserDef.kShowHanyuPinyinInCompositionBuffer) + forKey: UserDef.kShowHanyuPinyinInCompositionBuffer.rawValue) @State private var selInlineDumpPinyinInLieuOfZhuyin = UserDefaults.standard.bool( - forKey: UserDef.kInlineDumpPinyinInLieuOfZhuyin) - @State private var selEnableFartSuppressor = UserDefaults.standard.bool(forKey: UserDef.kShouldNotFartInLieuOfBeep) - @State private var selEnableAutoUpdateCheck = UserDefaults.standard.bool(forKey: UserDef.kCheckUpdateAutomatically) - @State private var selEnableDebugMode = UserDefaults.standard.bool(forKey: UserDef.kIsDebugModeEnabled) + forKey: UserDef.kInlineDumpPinyinInLieuOfZhuyin.rawValue) + @State private var selEnableFartSuppressor = UserDefaults.standard.bool( + forKey: UserDef.kShouldNotFartInLieuOfBeep.rawValue) + @State private var selEnableAutoUpdateCheck = UserDefaults.standard.bool( + forKey: UserDef.kCheckUpdateAutomatically.rawValue) + @State private var selEnableDebugMode = UserDefaults.standard.bool(forKey: UserDef.kIsDebugModeEnabled.rawValue) private let contentWidth: Double = { switch mgrPrefs.appleLanguages[0] { case "ja": @@ -94,14 +97,14 @@ struct suiPrefPaneGeneral: View { IME.prtDebugIntel(value[0]) if selUILanguage == mgrPrefs.appleLanguages || (selUILanguage[0] == "auto" - && UserDefaults.standard.object(forKey: UserDef.kAppleLanguages) == nil) + && UserDefaults.standard.object(forKey: UserDef.kAppleLanguages.rawValue) == nil) { return } if selUILanguage[0] != "auto" { mgrPrefs.appleLanguages = value } else { - UserDefaults.standard.removeObject(forKey: UserDef.kAppleLanguages) + UserDefaults.standard.removeObject(forKey: UserDef.kAppleLanguages.rawValue) } NSLog("vChewing App self-terminated due to UI language change.") NSApplication.shared.terminate(nil) diff --git a/Source/UI/PrefUI/suiPrefPaneKeyboard.swift b/Source/UI/PrefUI/suiPrefPaneKeyboard.swift index e5048412..767785a0 100644 --- a/Source/UI/PrefUI/suiPrefPaneKeyboard.swift +++ b/Source/UI/PrefUI/suiPrefPaneKeyboard.swift @@ -26,19 +26,20 @@ import SwiftUI @available(macOS 11.0, *) struct suiPrefPaneKeyboard: View { - @State private var selMandarinParser = UserDefaults.standard.integer(forKey: UserDef.kMandarinParser) + @State private var selMandarinParser = UserDefaults.standard.integer(forKey: UserDef.kMandarinParser.rawValue) @State private var selBasicKeyboardLayout: String = - UserDefaults.standard.string(forKey: UserDef.kBasicKeyboardLayout) ?? mgrPrefs.basicKeyboardLayout + UserDefaults.standard.string(forKey: UserDef.kBasicKeyboardLayout.rawValue) ?? mgrPrefs.basicKeyboardLayout - @State private var selUsingHotKeySCPC = UserDefaults.standard.bool(forKey: UserDef.kUsingHotKeySCPC) - @State private var selUsingHotKeyAssociates = UserDefaults.standard.bool(forKey: UserDef.kUsingHotKeyAssociates) - @State private var selUsingHotKeyCNS = UserDefaults.standard.bool(forKey: UserDef.kUsingHotKeyCNS) - @State private var selUsingHotKeyKangXi = UserDefaults.standard.bool(forKey: UserDef.kUsingHotKeyKangXi) - @State private var selUsingHotKeyJIS = UserDefaults.standard.bool(forKey: UserDef.kUsingHotKeyJIS) + @State private var selUsingHotKeySCPC = UserDefaults.standard.bool(forKey: UserDef.kUsingHotKeySCPC.rawValue) + @State private var selUsingHotKeyAssociates = UserDefaults.standard.bool( + forKey: UserDef.kUsingHotKeyAssociates.rawValue) + @State private var selUsingHotKeyCNS = UserDefaults.standard.bool(forKey: UserDef.kUsingHotKeyCNS.rawValue) + @State private var selUsingHotKeyKangXi = UserDefaults.standard.bool(forKey: UserDef.kUsingHotKeyKangXi.rawValue) + @State private var selUsingHotKeyJIS = UserDefaults.standard.bool(forKey: UserDef.kUsingHotKeyJIS.rawValue) @State private var selUsingHotKeyHalfWidthASCII = UserDefaults.standard.bool( - forKey: UserDef.kUsingHotKeyHalfWidthASCII) + forKey: UserDef.kUsingHotKeyHalfWidthASCII.rawValue) @State private var selUsingHotKeyCurrencyNumerals = UserDefaults.standard.bool( - forKey: UserDef.kUsingHotKeyCurrencyNumerals) + forKey: UserDef.kUsingHotKeyCurrencyNumerals.rawValue) private let contentWidth: Double = { switch mgrPrefs.appleLanguages[0] { From 4b59c5ff5bce42c23a21e1df30ba9ca85dd89f28 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Tue, 2 Aug 2022 12:59:06 +0800 Subject: [PATCH 20/27] UOM // Let things in saveData() catchable in all cases. --- Source/Modules/LangModelRelated/SubLMs/lmUserOverride.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/Modules/LangModelRelated/SubLMs/lmUserOverride.swift b/Source/Modules/LangModelRelated/SubLMs/lmUserOverride.swift index 96da627d..95f93014 100644 --- a/Source/Modules/LangModelRelated/SubLMs/lmUserOverride.swift +++ b/Source/Modules/LangModelRelated/SubLMs/lmUserOverride.swift @@ -326,9 +326,8 @@ extension vChewing.LMUserOverride { public func saveData(toURL fileURL: URL) { let encoder = JSONEncoder() do { - if let jsonData = try? encoder.encode(mutLRUMap) { - try jsonData.write(to: fileURL, options: .atomic) - } + guard let jsonData = try? encoder.encode(mutLRUMap) else { return } + try jsonData.write(to: fileURL, options: .atomic) } catch { IME.prtDebugIntel("UOM Error: Unable to save data, abort saving. Details: \(error)") return From b211faa6f4512e0f5f1a468bb0ef93e60733aecd Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Tue, 2 Aug 2022 13:18:48 +0800 Subject: [PATCH 21/27] Prefs // +failureFlagForUOMObservation & letter case fix. --- Source/Modules/IMEModules/mgrPrefs.swift | 13 ++++++++++--- Source/UI/PrefUI/suiPrefPaneExperience.swift | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Source/Modules/IMEModules/mgrPrefs.swift b/Source/Modules/IMEModules/mgrPrefs.swift index a0ba5134..0d1372ef 100644 --- a/Source/Modules/IMEModules/mgrPrefs.swift +++ b/Source/Modules/IMEModules/mgrPrefs.swift @@ -28,6 +28,7 @@ import Cocoa public enum UserDef: String, CaseIterable { case kIsDebugModeEnabled = "_DebugMode" + case kFailureFlagForUOMObservation = "_FailureFlag_UOMObservation" case kMostRecentInputMode = "MostRecentInputMode" case kUserDataFolderSpecified = "UserDataFolderSpecified" case kCheckUpdateAutomatically = "CheckUpdateAutomatically" @@ -37,7 +38,7 @@ public enum UserDef: String, CaseIterable { case kCandidateListTextSize = "CandidateListTextSize" case kAppleLanguages = "AppleLanguages" case kShouldAutoReloadUserDataFiles = "ShouldAutoReloadUserDataFiles" - case kuseRearCursorMode = "useRearCursorMode" + case kUseRearCursorMode = "useRearCursorMode" case kUseHorizontalCandidateList = "UseHorizontalCandidateList" case kComposingBufferSize = "ComposingBufferSize" case kChooseCandidateUsingSpace = "ChooseCandidateUsingSpace" @@ -239,6 +240,9 @@ enum MandarinParser: Int { public enum mgrPrefs { public static func setMissingDefaults() { UserDefaults.standard.setDefault(mgrPrefs.isDebugModeEnabled, forKey: UserDef.kIsDebugModeEnabled.rawValue) + UserDefaults.standard.setDefault( + mgrPrefs.failureFlagForUOMObservation, forKey: UserDef.kFailureFlagForUOMObservation.rawValue + ) UserDefaults.standard.setDefault(mgrPrefs.mostRecentInputMode, forKey: UserDef.kMostRecentInputMode.rawValue) UserDefaults.standard.setDefault( mgrPrefs.checkUpdateAutomatically, forKey: UserDef.kCheckUpdateAutomatically.rawValue @@ -265,7 +269,7 @@ public enum mgrPrefs { mgrPrefs.associatedPhrasesEnabled, forKey: UserDef.kAssociatedPhrasesEnabled.rawValue ) UserDefaults.standard.setDefault( - mgrPrefs.useRearCursorMode, forKey: UserDef.kuseRearCursorMode.rawValue + mgrPrefs.useRearCursorMode, forKey: UserDef.kUseRearCursorMode.rawValue ) UserDefaults.standard.setDefault( mgrPrefs.moveCursorAfterSelectingCandidate, forKey: UserDef.kMoveCursorAfterSelectingCandidate.rawValue @@ -332,6 +336,9 @@ public enum mgrPrefs { @UserDefault(key: UserDef.kIsDebugModeEnabled.rawValue, defaultValue: false) static var isDebugModeEnabled: Bool + @UserDefault(key: UserDef.kFailureFlagForUOMObservation.rawValue, defaultValue: false) + static var failureFlagForUOMObservation: Bool + @UserDefault(key: UserDef.kMostRecentInputMode.rawValue, defaultValue: "") static var mostRecentInputMode: String @@ -376,7 +383,7 @@ public enum mgrPrefs { @UserDefault(key: UserDef.kShouldAutoReloadUserDataFiles.rawValue, defaultValue: true) static var shouldAutoReloadUserDataFiles: Bool - @UserDefault(key: UserDef.kuseRearCursorMode.rawValue, defaultValue: false) + @UserDefault(key: UserDef.kUseRearCursorMode.rawValue, defaultValue: false) static var useRearCursorMode: Bool @UserDefault(key: UserDef.kMoveCursorAfterSelectingCandidate.rawValue, defaultValue: true) diff --git a/Source/UI/PrefUI/suiPrefPaneExperience.swift b/Source/UI/PrefUI/suiPrefPaneExperience.swift index 908eebb4..0328c571 100644 --- a/Source/UI/PrefUI/suiPrefPaneExperience.swift +++ b/Source/UI/PrefUI/suiPrefPaneExperience.swift @@ -32,7 +32,7 @@ struct suiPrefPaneExperience: View { (UserDefaults.standard.string(forKey: UserDef.kCandidateKeys.rawValue) ?? mgrPrefs.defaultCandidateKeys) as String @State private var selCursorPosition = UserDefaults.standard.bool( - forKey: UserDef.kuseRearCursorMode.rawValue) ? 1 : 0 + forKey: UserDef.kUseRearCursorMode.rawValue) ? 1 : 0 @State private var selPushCursorAfterSelection = UserDefaults.standard.bool( forKey: UserDef.kMoveCursorAfterSelectingCandidate.rawValue) @State private var selKeyBehaviorShiftTab = From d98f57b6f1b4f8e84ebaf9e8ed77165ef1496f89 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Tue, 2 Aug 2022 13:47:23 +0800 Subject: [PATCH 22/27] KeyHandler // Add disaster rescue marker for observe(). - Also disable UOM observation when fetchSuggestionsFromUserOverrideModel is off. --- Source/Modules/ControllerModules/KeyHandler_Core.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Source/Modules/ControllerModules/KeyHandler_Core.swift b/Source/Modules/ControllerModules/KeyHandler_Core.swift index dc54d872..302586ce 100644 --- a/Source/Modules/ControllerModules/KeyHandler_Core.swift +++ b/Source/Modules/ControllerModules/KeyHandler_Core.swift @@ -184,14 +184,20 @@ public class KeyHandler { addToUserOverrideModel = false } } - if addToUserOverrideModel { + if addToUserOverrideModel, mgrPrefs.fetchSuggestionsFromUserOverrideModel { IME.prtDebugIntel("UOM: Start Observation.") + // 這個過程可能會因為使用者半衰記憶模組內部資料錯亂、而導致輸入法在選字時崩潰。 + // 於是在這裡引入災後狀況察覺專用變數,且先開啟該開關。順利執行完觀察後會關閉。 + // 一旦輸入法崩潰,會在重啟時發現這個開關是開著的,屆時 AppDelegate 會做出應對。 + mgrPrefs.failureFlagForUOMObservation = true // 令半衰記憶模組觀測給定的三元圖。 // 這個過程會讓半衰引擎根據當前上下文生成三元圖索引鍵。 currentUOM.observe( walkedAnchors: walkedAnchors, cursorIndex: adjustedCursor, candidate: theCandidate.value, timestamp: NSDate().timeIntervalSince1970, saveCallback: { mgrLangModel.saveUserOverrideModelData() } ) + // 如果沒有出現崩框的話,那就將這個開關復位。 + mgrPrefs.failureFlagForUOMObservation = false } } From 2dc835af628b6d84ac81f4238834dda3f9243763 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Tue, 2 Aug 2022 15:20:09 +0800 Subject: [PATCH 23/27] Delegate // Handle failureFlagForUOMObservation. --- Source/Modules/AppDelegate.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Modules/AppDelegate.swift b/Source/Modules/AppDelegate.swift index 40cbc575..a63a4e94 100644 --- a/Source/Modules/AppDelegate.swift +++ b/Source/Modules/AppDelegate.swift @@ -64,6 +64,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega } func applicationDidFinishLaunching(_: Notification) { + // 一旦發現與使用者半衰模組的觀察行為有關的崩潰標記被開啟,就清空既有的半衰記憶資料檔案。 + if mgrPrefs.failureFlagForUOMObservation { + mgrLangModel.clearUserOverrideModelData(.imeModeCHS) + mgrLangModel.clearUserOverrideModelData(.imeModeCHT) + mgrPrefs.failureFlagForUOMObservation = false + } DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) { IME.initLangModels(userOnly: false) } From 7e42dca764fef951f4737b70740d7d0da31bfcac Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Tue, 2 Aug 2022 15:20:26 +0800 Subject: [PATCH 24/27] Delegate // Notifying failureFlagForUOMObservation. Co-Authored-By: Hiraku --- Source/Modules/AppDelegate.swift | 14 +++++++++++++- Source/Modules/ControllerModules/InputState.swift | 6 ++++-- Source/Resources/Base.lproj/Localizable.strings | 2 ++ Source/Resources/en.lproj/Localizable.strings | 2 ++ Source/Resources/ja.lproj/Localizable.strings | 2 ++ Source/Resources/zh-Hans.lproj/Localizable.strings | 2 ++ Source/Resources/zh-Hant.lproj/Localizable.strings | 2 ++ 7 files changed, 27 insertions(+), 3 deletions(-) diff --git a/Source/Modules/AppDelegate.swift b/Source/Modules/AppDelegate.swift index a63a4e94..5ea0b4c8 100644 --- a/Source/Modules/AppDelegate.swift +++ b/Source/Modules/AppDelegate.swift @@ -29,7 +29,7 @@ import InputMethodKit @objc(AppDelegate) class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelegate, - FSEventStreamHelperDelegate + FSEventStreamHelperDelegate, NSUserNotificationCenterDelegate { func helper(_: FSEventStreamHelper, didReceive _: [FSEventStreamHelper.Event]) { // 拖 100ms 再重載,畢竟有些有特殊需求的使用者可能會想使用巨型自訂語彙檔案。 @@ -63,13 +63,25 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega fsStreamHelper.delegate = nil } + func userNotificationCenter(_: NSUserNotificationCenter, shouldPresent _: NSUserNotification) -> Bool { + return true + } + func applicationDidFinishLaunching(_: Notification) { + NSUserNotificationCenter.default.delegate = self // 一旦發現與使用者半衰模組的觀察行為有關的崩潰標記被開啟,就清空既有的半衰記憶資料檔案。 if mgrPrefs.failureFlagForUOMObservation { mgrLangModel.clearUserOverrideModelData(.imeModeCHS) mgrLangModel.clearUserOverrideModelData(.imeModeCHT) mgrPrefs.failureFlagForUOMObservation = false + let userNotification = NSUserNotification() + userNotification.title = NSLocalizedString("vChewing", comment: "") + userNotification.informativeText = + "\(NSLocalizedString("vChewing crashed while handling previously loaded UOM observation data. These data files are cleaned now to ensure the usability.", comment: ""))" + userNotification.soundName = NSUserNotificationDefaultSoundName + NSUserNotificationCenter.default.deliver(userNotification) } + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) { IME.initLangModels(userOnly: false) } diff --git a/Source/Modules/ControllerModules/InputState.swift b/Source/Modules/ControllerModules/InputState.swift index 74026bc6..9bf30f6c 100644 --- a/Source/Modules/ControllerModules/InputState.swift +++ b/Source/Modules/ControllerModules/InputState.swift @@ -201,7 +201,8 @@ public enum InputState { let attributedString = NSMutableAttributedString( string: composingBufferConverted, attributes: [ - .underlineStyle: NSUnderlineStyle.thick.rawValue, + /// 不能用 .thick,否則會看不到游標。 + .underlineStyle: NSUnderlineStyle.single.rawValue, .markedClauseSegment: 0, ] ) @@ -212,7 +213,8 @@ public enum InputState { for (i, neta) in nodeValuesArray.enumerated() { attributedString.setAttributes( [ - .underlineStyle: NSUnderlineStyle.thick.rawValue, + /// 不能用 .thick,否則會看不到游標。 + .underlineStyle: NSUnderlineStyle.single.rawValue, .markedClauseSegment: i, ], range: NSRange(location: newBegin, length: neta.utf16.count) ) diff --git a/Source/Resources/Base.lproj/Localizable.strings b/Source/Resources/Base.lproj/Localizable.strings index 3d21a518..7fdaca04 100644 --- a/Source/Resources/Base.lproj/Localizable.strings +++ b/Source/Resources/Base.lproj/Localizable.strings @@ -1,3 +1,5 @@ +"vChewing" = "vChewing"; +"vChewing crashed while handling previously loaded UOM observation data. These data files are cleaned now to ensure the usability." = "vChewing crashed while handling previously loaded UOM observation data. These data files are cleaned now to ensure the usability."; "About vChewing…" = "About vChewing…"; "vChewing Preferences…" = "vChewing Preferences…"; "Uninstallation" = "Uninstallation"; diff --git a/Source/Resources/en.lproj/Localizable.strings b/Source/Resources/en.lproj/Localizable.strings index 3d21a518..7fdaca04 100644 --- a/Source/Resources/en.lproj/Localizable.strings +++ b/Source/Resources/en.lproj/Localizable.strings @@ -1,3 +1,5 @@ +"vChewing" = "vChewing"; +"vChewing crashed while handling previously loaded UOM observation data. These data files are cleaned now to ensure the usability." = "vChewing crashed while handling previously loaded UOM observation data. These data files are cleaned now to ensure the usability."; "About vChewing…" = "About vChewing…"; "vChewing Preferences…" = "vChewing Preferences…"; "Uninstallation" = "Uninstallation"; diff --git a/Source/Resources/ja.lproj/Localizable.strings b/Source/Resources/ja.lproj/Localizable.strings index 1f01eb48..148db8cb 100644 --- a/Source/Resources/ja.lproj/Localizable.strings +++ b/Source/Resources/ja.lproj/Localizable.strings @@ -1,3 +1,5 @@ +"vChewing" = "威注音入力アプリ"; +"vChewing crashed while handling previously loaded UOM observation data. These data files are cleaned now to ensure the usability." = "臨時記憶モジュールの観測行為による威注音入力アプリの意外中止は発生した。威注音入力アプリの無事利用のために、既存臨時記憶データは全てお消しした。"; "About vChewing…" = "威注音について…"; "vChewing Preferences…" = "入力機能設定…"; "Uninstallation" = "入力アプリの卸除(おろしのぞき)"; diff --git a/Source/Resources/zh-Hans.lproj/Localizable.strings b/Source/Resources/zh-Hans.lproj/Localizable.strings index c7a18167..2dc53355 100644 --- a/Source/Resources/zh-Hans.lproj/Localizable.strings +++ b/Source/Resources/zh-Hans.lproj/Localizable.strings @@ -1,3 +1,5 @@ +"vChewing" = "威注音输入法"; +"vChewing crashed while handling previously loaded UOM observation data. These data files are cleaned now to ensure the usability." = "威注音输入法的使用者半衰记忆模组在观测时崩溃,相关半衰记忆资料档案内容已全部清空。"; "About vChewing…" = "关于威注音…"; "vChewing Preferences…" = "威注音偏好设定…"; "Uninstallation" = "卸除输入法"; diff --git a/Source/Resources/zh-Hant.lproj/Localizable.strings b/Source/Resources/zh-Hant.lproj/Localizable.strings index 9f20e73e..09f9c864 100644 --- a/Source/Resources/zh-Hant.lproj/Localizable.strings +++ b/Source/Resources/zh-Hant.lproj/Localizable.strings @@ -1,3 +1,5 @@ +"vChewing" = "威注音輸入法"; +"vChewing crashed while handling previously loaded UOM observation data. These data files are cleaned now to ensure the usability." = "威注音輸入法的使用者半衰記憶模組在觀測時崩潰,相關半衰記憶資料檔案內容已全部清空。"; "About vChewing…" = "關於威注音…"; "vChewing Preferences…" = "威注音偏好設定…"; "Uninstallation" = "卸除輸入法"; From 8e63dd1a25d43258b31b3a2a0d815bd1f6a3cc60 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Tue, 2 Aug 2022 21:46:36 +0800 Subject: [PATCH 25/27] README // Explaining why we use MIT-NTL instead. --- README-CHT.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README-CHT.md b/README-CHT.md index eac8c9fa..0f25747b 100644 --- a/README-CHT.md +++ b/README-CHT.md @@ -97,7 +97,7 @@ - 天權星語彙處理引擎:Shiki Suen (MIT-NTL License)。 - 威注音詞庫由 Shiki Suen 維護,以 3-Clause BSD License 授權釋出。其中的詞頻數據[由 NAER 授權用於非商業用途](https://twitter.com/ShikiSuen/status/1479329302713831424)。 -使用者可自由使用、散播本軟體,惟散播時必須完整保留版權聲明及軟體授權、且一旦經過修改便不可以再繼續使用威注音的產品名稱。 +使用者可自由使用、散播本軟體,惟散播時必須完整保留版權聲明及軟體授權、且「一旦經過修改便不可以再繼續使用威注音的產品名稱」。換言之,這條相對上游 MIT 而言新增的規定就是:你 Fork 可以,但 Fork 成單獨發行的產品名稱時就必須修改產品名稱。這條新增規定對 OpenVanilla 與威注音雙方都有益,免得各自的旗號被盜版下載販子等挪用做意外用途。 ## 資料來源 diff --git a/README.md b/README.md index 0df7851f..eaa7503a 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ - 天权星语汇处理引擎:Shiki Suen (MIT-NTL License)。 - 威注音词库由 Shiki Suen 维护,以 3-Clause BSD License 授权释出。其中的词频数据[由 NAER 授权用于非商业用途](https://twitter.com/ShikiSuen/status/1479329302713831424)。 -使用者可自由使用、散播本软件,惟散播时必须完整保留版权声明及软件授权、且一旦经过修改便不可以再继续使用威注音的产品名称。 +使用者可自由使用、散播本软件,惟散播时必须完整保留版权声明及软件授权、且「一旦经过修改便不可以再继续使用威注音的产品名称」。换言之,这条相对上游 MIT 而言新增的规定就是:你 Fork 可以,但 Fork 成单独发行的产品名称时就必须修改产品名称。这条新增规定对 OpenVanilla 与威注音双方都有益,免得各自的旗号被盗版下载贩子等挪用做意外用途。 ## 资料来源 From e8a1c4c5a9d0c12bdf4b42573a55f9a24ad9259b Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Tue, 2 Aug 2022 21:31:08 +0800 Subject: [PATCH 26/27] Update Data - 20220802 --- Source/Data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Data b/Source/Data index 30a5bc77..144db528 160000 --- a/Source/Data +++ b/Source/Data @@ -1 +1 @@ -Subproject commit 30a5bc773a21d67c3f117eef5346d3b35433bbaa +Subproject commit 144db528de84caff0fa20802b144421bcc58419f From 0c316aa30f4e63f1993d6e6448a12d02cbd5620e Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Tue, 2 Aug 2022 21:31:50 +0800 Subject: [PATCH 27/27] Bump version to 1.8.7 Build 1987 --- Update-Info.plist | 4 ++-- vChewing.pkgproj | 2 +- vChewing.xcodeproj/project.pbxproj | 32 +++++++++++++++--------------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Update-Info.plist b/Update-Info.plist index f58fbea1..fa6db1c2 100644 --- a/Update-Info.plist +++ b/Update-Info.plist @@ -3,9 +3,9 @@ CFBundleShortVersionString - 1.8.6 + 1.8.7 CFBundleVersion - 1986 + 1987 UpdateInfoEndpoint https://gitee.com/vchewing/vChewing-macOS/raw/main/Update-Info.plist UpdateInfoSite diff --git a/vChewing.pkgproj b/vChewing.pkgproj index 706f8b1b..cbbae918 100644 --- a/vChewing.pkgproj +++ b/vChewing.pkgproj @@ -726,7 +726,7 @@ USE_HFS+_COMPRESSION VERSION - 1.8.6 + 1.8.7 TYPE 0 diff --git a/vChewing.xcodeproj/project.pbxproj b/vChewing.xcodeproj/project.pbxproj index 08990e45..175fadd2 100644 --- a/vChewing.xcodeproj/project.pbxproj +++ b/vChewing.xcodeproj/project.pbxproj @@ -1397,7 +1397,7 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1986; + CURRENT_PROJECT_VERSION = 1987; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -1407,7 +1407,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.8.6; + MARKETING_VERSION = 1.8.7; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewingTests; @@ -1436,13 +1436,13 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1986; + CURRENT_PROJECT_VERSION = 1987; ENABLE_NS_ASSERTIONS = NO; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.8.6; + MARKETING_VERSION = 1.8.7; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewingTests; @@ -1473,7 +1473,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1986; + CURRENT_PROJECT_VERSION = 1987; DEAD_CODE_STRIPPING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; @@ -1494,7 +1494,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.8.6; + MARKETING_VERSION = 1.8.7; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor; @@ -1523,7 +1523,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1986; + CURRENT_PROJECT_VERSION = 1987; DEAD_CODE_STRIPPING = YES; ENABLE_NS_ASSERTIONS = NO; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -1540,7 +1540,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.8.6; + MARKETING_VERSION = 1.8.7; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor; @@ -1654,7 +1654,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1986; + CURRENT_PROJECT_VERSION = 1987; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = ""; @@ -1682,7 +1682,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.8.6; + MARKETING_VERSION = 1.8.7; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1709,7 +1709,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1986; + CURRENT_PROJECT_VERSION = 1987; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = ""; @@ -1731,7 +1731,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.8.6; + MARKETING_VERSION = 1.8.7; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1753,7 +1753,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1986; + CURRENT_PROJECT_VERSION = 1987; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -1773,7 +1773,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.8.6; + MARKETING_VERSION = 1.8.7; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = "org.atelierInmu.vChewing.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1795,7 +1795,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1986; + CURRENT_PROJECT_VERSION = 1987; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -1809,7 +1809,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.8.6; + MARKETING_VERSION = 1.8.7; PRODUCT_BUNDLE_IDENTIFIER = "org.atelierInmu.vChewing.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "";