diff --git a/Source/3rdParty/HotenkaChineseConverter/HotenkaCCBridge.swift b/Source/3rdParty/HotenkaChineseConverter/HotenkaCCBridge.swift index 525a8ece..e9962b6f 100644 --- a/Source/3rdParty/HotenkaChineseConverter/HotenkaCCBridge.swift +++ b/Source/3rdParty/HotenkaChineseConverter/HotenkaCCBridge.swift @@ -27,6 +27,34 @@ import Foundation public class ChineseConverter { public static let shared = HotenkaChineseConverter.init(plistDir: mgrLangModel.getBundleDataPath("convdict")) + /// 漢字數字大寫轉換專用辭典,順序為:康熙、當代繁體中文、日文、簡體中文。 + private static let currencyNumeralDictTable: [String: (String, String, String, String)] = [ + "一": ("壹", "壹", "壹", "壹"), "二": ("貳", "貳", "弐", "贰"), "三": ("叄", "參", "参", "叁"), + "四": ("肆", "肆", "肆", "肆"), "五": ("伍", "伍", "伍", "伍"), "六": ("陸", "陸", "陸", "陆"), + "七": ("柒", "柒", "柒", "柒"), "八": ("捌", "捌", "捌", "捌"), "九": ("玖", "玖", "玖", "玖"), + "十": ("拾", "拾", "拾", "拾"), "百": ("佰", "佰", "佰", "佰"), "千": ("仟", "仟", "仟", "仟"), + "万": ("萬", "萬", "萬", "万"), "〇": ("零", "零", "零", "零"), + ] + + /// 將指定字串內的小寫漢字數字轉換為大寫,會對轉換對象進行直接修改操作。 + /// - Parameter target: 轉換對象。 + public static func ensureCurrencyNumerals(target: inout String) { + if !mgrPrefs.currencyNumeralsEnabled { return } + for key in currencyNumeralDictTable.keys { + guard let result = currencyNumeralDictTable[key] else { continue } + if IME.currentInputMode == InputMode.imeModeCHS { + target = target.replacingOccurrences(of: key, with: result.3) // Simplified Chinese + continue + } + switch (mgrPrefs.chineseConversionEnabled, mgrPrefs.shiftJISShinjitaiOutputEnabled) { + case (false, true), (true, true): target = target.replacingOccurrences(of: key, with: result.2) // JIS + case (true, false): target = target.replacingOccurrences(of: key, with: result.0) // KangXi + default: target = target.replacingOccurrences(of: key, with: result.1) // Contemporary + } + } + return + } + /// CrossConvert. /// /// - Parameter string: Text in Original Script. diff --git a/Source/Data b/Source/Data index 94b215d4..2cbca5af 160000 --- a/Source/Data +++ b/Source/Data @@ -1 +1 @@ -Subproject commit 94b215d444e1c43e81ad173e9c28e6769f74c330 +Subproject commit 2cbca5af7f281b7932878cda5e63f2dfed6b48b0 diff --git a/Source/Modules/ControllerModules/InputSignal.swift b/Source/Modules/ControllerModules/InputSignal.swift index 621475d4..61aea850 100644 --- a/Source/Modules/ControllerModules/InputSignal.swift +++ b/Source/Modules/ControllerModules/InputSignal.swift @@ -115,6 +115,9 @@ enum KeyCodeBlackListed: UInt16 { /// 注意:第 95 號 Key Code(逗號)為 JIS 佈局特有的數字小鍵盤按鍵。 let arrNumpadKeyCodes: [UInt16] = [65, 67, 69, 71, 75, 78, 81, 82, 83, 84, 85, 86, 87, 88, 89, 91, 92, 95] +/// 主鍵盤區域的數字鍵的 KeyCode。 +let arrMainAreaNumKey: [UInt16] = [18, 19, 20, 21, 22, 23, 25, 26, 28, 29] + // CharCodes: https://theasciicode.com.ar/ascii-control-characters/horizontal-tab-ascii-code-9.html enum CharCode: UInt16 { case yajuusenpaiA = 114 @@ -128,27 +131,25 @@ enum CharCode: UInt16 { struct InputSignal: CustomStringConvertible { private(set) var isTypingVertical: Bool - private(set) var inputText: String? + private(set) var inputText: String private(set) var inputTextIgnoringModifiers: String? private(set) var charCode: UInt16 private(set) var keyCode: UInt16 private var isFlagChanged: Bool private var flags: NSEvent.ModifierFlags - private var cursorForwardKey: KeyCode = .kNone - private var cursorBackwardKey: KeyCode = .kNone - private var extraChooseCandidateKey: KeyCode = .kNone - private var extraChooseCandidateKeyReverse: KeyCode = .kNone - private var absorbedArrowKey: KeyCode = .kNone - private var verticalTypingOnlyChooseCandidateKey: KeyCode = .kNone + private var cursorForwardKey: KeyCode = .kNone // 12 o'clock + private var cursorBackwardKey: KeyCode = .kNone // 6 o'clock + private var cursorKeyClockRight: KeyCode = .kNone // 3 o'clock + private var cursorKeyClockLeft: KeyCode = .kNone // 9 o'clock private(set) var emacsKey: EmacsKey public init( - inputText: String?, keyCode: UInt16, charCode: UInt16, flags: NSEvent.ModifierFlags, + inputText: String = "", keyCode: UInt16, charCode: UInt16, flags: NSEvent.ModifierFlags, isVerticalTyping: Bool = false, inputTextIgnoringModifiers: String? = nil ) { - self.inputText = AppleKeyboardConverter.cnvStringApple2ABC(inputText ?? "") + self.inputText = AppleKeyboardConverter.cnvStringApple2ABC(inputText) self.inputTextIgnoringModifiers = AppleKeyboardConverter.cnvStringApple2ABC( - inputTextIgnoringModifiers ?? inputText ?? "") + inputTextIgnoringModifiers ?? inputText) self.flags = flags isFlagChanged = false isTypingVertical = isVerticalTyping @@ -164,7 +165,7 @@ struct InputSignal: CustomStringConvertible { public init(event: NSEvent, isVerticalTyping: Bool = false) { inputText = AppleKeyboardConverter.cnvStringApple2ABC(event.characters ?? "") inputTextIgnoringModifiers = AppleKeyboardConverter.cnvStringApple2ABC( - event.charactersIgnoringModifiers ?? "") + event.charactersIgnoringModifiers ?? inputText) keyCode = event.keyCode flags = event.modifierFlags isFlagChanged = (event.type == .flagsChanged) @@ -188,173 +189,83 @@ struct InputSignal: CustomStringConvertible { mutating func defineArrowKeys() { cursorForwardKey = isTypingVertical ? .kDownArrow : .kRightArrow cursorBackwardKey = isTypingVertical ? .kUpArrow : .kLeftArrow - extraChooseCandidateKey = isTypingVertical ? .kLeftArrow : .kDownArrow - extraChooseCandidateKeyReverse = isTypingVertical ? .kRightArrow : .kUpArrow - absorbedArrowKey = isTypingVertical ? .kRightArrow : .kUpArrow - verticalTypingOnlyChooseCandidateKey = isTypingVertical ? absorbedArrowKey : .kNone + cursorKeyClockLeft = isTypingVertical ? .kRightArrow : .kUpArrow + cursorKeyClockRight = isTypingVertical ? .kLeftArrow : .kDownArrow } var description: String { - "" + var result = "<[InputSignal] " + result += "inputText:\(String(describing: inputText)), " + result += "inputTextIgnoringModifiers:\(String(describing: inputTextIgnoringModifiers)), " + result += "charCode:\(charCode), " + result += "keyCode:\(keyCode), " + result += "flags:\(flags), " + result += "cursorForwardKey:\(cursorForwardKey), " + result += "cursorBackwardKey:\(cursorBackwardKey), " + result += "cursorKeyClockRight:\(cursorKeyClockRight), " + result += "cursorKeyClockLeft:\(cursorKeyClockLeft), " + result += "emacsKey:\(emacsKey), " + result += "isTypingVertical:\(isTypingVertical)" + result += ">" + return result } // 除了 ANSI charCode 以外,其餘一律過濾掉,免得純 Swift 版 KeyHandler 被餵屎。 - var isInvalidInput: Bool { - switch charCode { - case 0x20...0xFF: // ANSI charCode 範圍 - return false - default: - if isReservedKey, !isKeyCodeBlacklisted { - return false - } - return true - } - } + var isInvalid: Bool { (0x20...0xFF).contains(charCode) ? false : !(isReservedKey && !isKeyCodeBlacklisted) } var isKeyCodeBlacklisted: Bool { - guard let code = KeyCodeBlackListed(rawValue: keyCode) else { - return false - } + guard let code = KeyCodeBlackListed(rawValue: keyCode) else { return false } return code.rawValue != KeyCode.kNone.rawValue } - var isShiftHold: Bool { - flags.contains([.shift]) - } - - var isCommandHold: Bool { - flags.contains([.command]) - } - - var isControlHold: Bool { - flags.contains([.control]) - } - - var isControlHotKey: Bool { - flags.contains([.control]) && inputText?.first?.isLetter ?? false - } - - var isOptionHold: Bool { - flags.contains([.option]) - } - - var isOptionHotKey: Bool { - flags.contains([.option]) && inputText?.first?.isLetter ?? false - } - - var isCapsLockOn: Bool { - flags.contains([.capsLock]) - } - - var isNumericPad: Bool { - flags.contains([.numericPad]) - } - - var isFunctionKeyHold: Bool { - flags.contains([.function]) - } - var isReservedKey: Bool { - guard let code = KeyCode(rawValue: keyCode) else { - return false - } + guard let code = KeyCode(rawValue: keyCode) else { return false } return code.rawValue != KeyCode.kNone.rawValue } + // 摁 Alt+Shift+主鍵盤區域數字鍵 的話,根據不同的 macOS 鍵盤佈局種類,會出現不同的符號結果。 + // 然而呢,KeyCode 卻是一致的。於是這裡直接準備一個換算表來用。 + let mapMainAreaNumKey: [UInt16: String] = [ + 18: "1", 19: "2", 20: "3", 21: "4", 23: "5", 22: "6", 26: "7", 28: "8", 25: "9", 29: "0", + ] + /// 單獨用 flags 來判定數字小鍵盤輸入的方法已經失效了,所以必須再增補用 KeyCode 判定的方法。 - var isNumericPadAreaKey: Bool { - arrNumpadKeyCodes.contains(keyCode) - } + var isNumericPadKey: Bool { arrNumpadKeyCodes.contains(keyCode) } + var isMainAreaNumKey: Bool { arrMainAreaNumKey.contains(keyCode) } + var isShiftHold: Bool { flags.contains([.shift]) } + var isCommandHold: Bool { flags.contains([.command]) } + var isControlHold: Bool { flags.contains([.control]) } + var isControlHotKey: Bool { flags.contains([.control]) && inputText.first?.isLetter ?? false } + var isOptionHold: Bool { flags.contains([.option]) } + var isOptionHotKey: Bool { flags.contains([.option]) && inputText.first?.isLetter ?? false } + var isCapsLockOn: Bool { flags.contains([.capsLock]) } + var isFunctionKeyHold: Bool { flags.contains([.function]) } + var isNonLaptopFunctionKey: Bool { flags.contains([.numericPad]) && !isNumericPadKey } + var isEnter: Bool { [KeyCode.kCarriageReturn, KeyCode.kLineFeed].contains(KeyCode(rawValue: keyCode)) } + var isTab: Bool { KeyCode(rawValue: keyCode) == KeyCode.kTab } + var isUp: Bool { KeyCode(rawValue: keyCode) == KeyCode.kUpArrow } + var isDown: Bool { KeyCode(rawValue: keyCode) == KeyCode.kDownArrow } + var isLeft: Bool { KeyCode(rawValue: keyCode) == KeyCode.kLeftArrow } + var isRight: Bool { KeyCode(rawValue: keyCode) == KeyCode.kRightArrow } + var isPageUp: Bool { KeyCode(rawValue: keyCode) == KeyCode.kPageUp } + var isPageDown: Bool { KeyCode(rawValue: keyCode) == KeyCode.kPageDown } + var isSpace: Bool { KeyCode(rawValue: keyCode) == KeyCode.kSpace } + var isBackSpace: Bool { KeyCode(rawValue: keyCode) == KeyCode.kBackSpace } + var isEsc: Bool { KeyCode(rawValue: keyCode) == KeyCode.kEscape } + var isHome: Bool { KeyCode(rawValue: keyCode) == KeyCode.kHome } + var isEnd: Bool { KeyCode(rawValue: keyCode) == KeyCode.kEnd } + var isDelete: Bool { KeyCode(rawValue: keyCode) == KeyCode.kWindowsDelete } + var isCursorBackward: Bool { KeyCode(rawValue: keyCode) == cursorBackwardKey } + var isCursorForward: Bool { KeyCode(rawValue: keyCode) == cursorForwardKey } + var isCursorClockRight: Bool { KeyCode(rawValue: keyCode) == cursorKeyClockRight } + var isCursorClockLeft: Bool { KeyCode(rawValue: keyCode) == cursorKeyClockLeft } - var isTab: Bool { - KeyCode(rawValue: keyCode) == KeyCode.kTab - } - - var isEnter: Bool { - (KeyCode(rawValue: keyCode) == KeyCode.kCarriageReturn) - || (KeyCode(rawValue: keyCode) == KeyCode.kLineFeed) - } - - var isUp: Bool { - KeyCode(rawValue: keyCode) == KeyCode.kUpArrow - } - - var isDown: Bool { - KeyCode(rawValue: keyCode) == KeyCode.kDownArrow - } - - var isLeft: Bool { - KeyCode(rawValue: keyCode) == KeyCode.kLeftArrow - } - - var isRight: Bool { - KeyCode(rawValue: keyCode) == KeyCode.kRightArrow - } - - var isPageUp: Bool { - KeyCode(rawValue: keyCode) == KeyCode.kPageUp - } - - var isPageDown: Bool { - KeyCode(rawValue: keyCode) == KeyCode.kPageDown - } - - var isSpace: Bool { - KeyCode(rawValue: keyCode) == KeyCode.kSpace - } - - var isBackSpace: Bool { - KeyCode(rawValue: keyCode) == KeyCode.kBackSpace - } - - var isEsc: Bool { - KeyCode(rawValue: keyCode) == KeyCode.kEscape - } - - var isHome: Bool { - KeyCode(rawValue: keyCode) == KeyCode.kHome - } - - var isEnd: Bool { - KeyCode(rawValue: keyCode) == KeyCode.kEnd - } - - var isDelete: Bool { - KeyCode(rawValue: keyCode) == KeyCode.kWindowsDelete - } - - var isCursorBackward: Bool { - KeyCode(rawValue: keyCode) == cursorBackwardKey - } - - var isCursorForward: Bool { - KeyCode(rawValue: keyCode) == cursorForwardKey - } - - var isAbsorbedArrowKey: Bool { - KeyCode(rawValue: keyCode) == absorbedArrowKey - } - - var isExtraChooseCandidateKey: Bool { - KeyCode(rawValue: keyCode) == extraChooseCandidateKey - } - - var isExtraChooseCandidateKeyReverse: Bool { - KeyCode(rawValue: keyCode) == extraChooseCandidateKeyReverse - } - - var isVerticalTypingOnlyChooseCandidateKey: Bool { - KeyCode(rawValue: keyCode) == verticalTypingOnlyChooseCandidateKey - } - - var isUpperCaseASCIILetterKey: Bool { - // 這裡必須加上「flags == .shift」,否則會出現某些情況下輸入法「誤判當前鍵入的非 Shift 字符為大寫」的問題。 - (65...90).contains(charCode) && flags == .shift - } + // 這裡必須加上「flags == .shift」,否則會出現某些情況下輸入法「誤判當前鍵入的非 Shift 字符為大寫」的問題。 + var isUpperCaseASCIILetterKey: Bool { (65...90).contains(charCode) && flags == .shift } + // 這裡必須用 KeyCode,這樣才不會受隨 macOS 版本更動的 Apple 動態注音鍵盤排列內容的影響。 + // 只是必須得與 ![input isShift] 搭配使用才可以(也就是僅判定 Shift 沒被摁下的情形)。 var isSymbolMenuPhysicalKey: Bool { - // 這裡必須用 KeyCode,這樣才不會受隨 macOS 版本更動的 Apple 動態注音鍵盤排列內容的影響。 - // 只是必須得與 ![input isShift] 搭配使用才可以(也就是僅判定 Shift 沒被摁下的情形)。 [KeyCode.kSymbolMenuPhysicalKeyIntl, KeyCode.kSymbolMenuPhysicalKeyJIS].contains(KeyCode(rawValue: keyCode)) } } diff --git a/Source/Modules/ControllerModules/InputState.swift b/Source/Modules/ControllerModules/InputState.swift index 07915b45..baf085b8 100644 --- a/Source/Modules/ControllerModules/InputState.swift +++ b/Source/Modules/ControllerModules/InputState.swift @@ -33,7 +33,7 @@ enum StateType { case ofDeactivated case ofAssociatedPhrases case ofEmpty - case ofEmptyIgnorePreviousState + case ofEmptyIgnoringPreviousState case ofCommitting case ofNotEmpty case ofInputting @@ -70,7 +70,7 @@ protocol InputStateProtocol { /// 組字區內存入任何東西,所以該狀態不受 .NotEmpty 的管轄。 /// - .Empty: 使用者剛剛切換至該輸入法、卻還沒有任何輸入行為。抑或是剛剛敲字遞交給 /// 客體應用、準備新的輸入行為。 -/// - .EmptyIgnorePreviousState: 與 Empty 類似,但會扔掉上一個狀態的內容、不將這些 +/// - .EmptyIgnoringPreviousState: 與 Empty 類似,但會扔掉上一個狀態的內容、不將這些 /// 內容遞交給客體應用。該狀態在處理完畢之後會被立刻切換至 .Empty()。 /// - .Committing: 該狀態會承載要遞交出去的內容,讓輸入法控制器處理時代為遞交。 /// - .NotEmpty: 非空狀態,是一種狀態大類、用以派生且代表下述諸狀態。 @@ -106,11 +106,11 @@ enum InputState { // MARK: - - /// .EmptyIgnorePreviousState: 與 Empty 類似, + /// .EmptyIgnoringPreviousState: 與 Empty 類似, /// 但會扔掉上一個狀態的內容、不將這些內容遞交給客體應用。 /// 該狀態在處理完畢之後會被立刻切換至 .Empty()。 class EmptyIgnoringPreviousState: Empty { - override public var type: StateType { .ofEmptyIgnorePreviousState } + override public var type: StateType { .ofEmptyIgnoringPreviousState } override var description: String { "" } @@ -126,6 +126,7 @@ enum InputState { convenience init(textToCommit: String) { self.init() self.textToCommit = textToCommit + ChineseConverter.ensureCurrencyNumerals(target: &self.textToCommit) } var description: String { @@ -146,6 +147,17 @@ enum InputState { self.isTypingVertical = isTypingVertical } + var attributedString: NSMutableAttributedString { + let attributedString = NSMutableAttributedString( + string: " ", + attributes: [ + .underlineStyle: NSUnderlineStyle.single.rawValue, + .markedClauseSegment: 0, + ] + ) + return attributedString + } + var description: String { "" } @@ -428,8 +440,11 @@ enum InputState { override public var type: StateType { .ofSymbolTable } var node: SymbolNode - init(node: SymbolNode, isTypingVertical: Bool) { + init(node: SymbolNode, previous: SymbolNode? = nil, isTypingVertical: Bool) { self.node = node + if let previous = previous { + self.node.previous = previous + } let candidates = node.children?.map(\.title) ?? [String]() super.init( composingBuffer: "", cursorIndex: 0, candidates: candidates.map { ("", $0) }, diff --git a/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift b/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift index f8e6a067..83501782 100644 --- a/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift +++ b/Source/Modules/ControllerModules/KeyHandler_HandleCandidate.swift @@ -59,7 +59,7 @@ extension KeyHandler { || ((input.isCursorBackward || input.isCursorForward) && input.isShiftHold) if cancelCandidateKey { - if (state is InputState.AssociatedPhrases) + if state is InputState.AssociatedPhrases || mgrPrefs.useSCPCTypingMode || compositor.isEmpty { @@ -72,6 +72,9 @@ extension KeyHandler { } else { stateCallback(buildInputtingState) } + if let state = state as? InputState.SymbolTable, let nodePrevious = state.node.previous { + stateCallback(InputState.SymbolTable(node: nodePrevious, isTypingVertical: state.isTypingVertical)) + } return true } @@ -288,7 +291,7 @@ extension KeyHandler { var index: Int = NSNotFound let match: String = - (state is InputState.AssociatedPhrases) ? input.inputTextIgnoringModifiers ?? "" : inputText ?? "" + (state is InputState.AssociatedPhrases) ? input.inputTextIgnoringModifiers ?? "" : inputText var j = 0 while j < ctlCandidateCurrent.keyLabels.count { diff --git a/Source/Modules/ControllerModules/KeyHandler_HandleComposition.swift b/Source/Modules/ControllerModules/KeyHandler_HandleComposition.swift new file mode 100644 index 00000000..9cecc0e8 --- /dev/null +++ b/Source/Modules/ControllerModules/KeyHandler_HandleComposition.swift @@ -0,0 +1,154 @@ +// Copyright (c) 2021 and onwards The vChewing Project (MIT-NTL License). +// Refactored from the ObjCpp-version of this class by: +// (c) 2011 and onwards The OpenVanilla Project (MIT 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. +*/ + +/// 該檔案用來處理 KeyHandler.HandleInput() 當中的與組字有關的行為。 + +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? { + + // MARK: 注音按鍵輸入處理 (Handle BPMF Keys) + + var keyConsumedByReading = false + let skipPhoneticHandling = + input.isReservedKey || input.isNumericPadKey || input.isNonLaptopFunctionKey + || input.isControlHold || input.isOptionHold || input.isShiftHold || input.isCommandHold + + // 這裡 inputValidityCheck() 是讓注拼槽檢查 charCode 這個 UniChar 是否是合法的注音輸入。 + // 如果是的話,就將這次傳入的這個按鍵訊號塞入注拼槽內且標記為「keyConsumedByReading」。 + // 函式 composer.receiveKey() 可以既接收 String 又接收 UniChar。 + if !skipPhoneticHandling && composer.inputValidityCheck(key: input.charCode) { + composer.receiveKey(fromCharCode: input.charCode) + keyConsumedByReading = true + + // 沒有調號的話,只需要 updateClientComposingBuffer() 且終止處理(return true)即可。 + // 有調號的話,則不需要這樣,而是轉而繼續在此之後的處理。 + if !composer.hasToneMarker() { + stateCallback(buildInputtingState) + return true + } + } + + var composeReading = composer.hasToneMarker() // 這裡不需要做排他性判斷。 + + // 如果當前的按鍵是 Enter 或 Space 的話,這時就可以取出 _composer 內的注音來做檢查了。 + // 來看看詞庫內到底有沒有對應的讀音索引。這裡用了類似「|=」的判斷處理方式。 + composeReading = composeReading || (!composer.isEmpty && (input.isSpace || input.isEnter)) + if composeReading { + if input.isSpace, !composer.hasToneMarker() { + // 補上空格,否則倚天忘形與許氏排列某些音無法響應不了陰平聲調。 + // 小麥注音因為使用 OVMandarin,所以不需要這樣補。但鐵恨引擎對所有聲調一視同仁。 + composer.receiveKey(fromString: " ") + } + let readingKey = composer.getComposition() // 拿取用來進行索引檢索用的注音。 + // 如果輸入法的辭典索引是漢語拼音的話,要注意上一行拿到的內容得是漢語拼音。 + + // 向語言模型詢問是否有對應的記錄。 + if !currentLM.hasUnigramsFor(key: readingKey) { + IME.prtDebugIntel("B49C0979:語彙庫內無「\(readingKey)」的匹配記錄。") + errorCallback() + composer.clear() + // 根據「組字器是否為空」來判定回呼哪一種狀態。 + stateCallback((compositor.isEmpty) ? InputState.EmptyIgnoringPreviousState() : buildInputtingState) + return true // 向 IMK 報告說這個按鍵訊號已經被輸入法攔截處理了。 + } + + // 將該讀音插入至組字器內的軌格當中。 + compositor.insertReading(readingKey) + + // 讓組字器反爬軌格。 + let textToCommit = commitOverflownCompositionAndWalk + + // 看看半衰記憶模組是否會對目前的狀態給出自動選字建議。 + fetchAndApplySuggestionsFromUserOverrideModel() + + // 將組字器內超出最大動態爬軌範圍的節錨都標記為「已經手動選字過」,減少之後的爬軌運算負擔。 + markNodesFixedIfNecessary() + + // 之後就是更新組字區了。先清空注拼槽的內容。 + composer.clear() + + // 再以回呼組字狀態的方式來執行 updateClientComposingBuffer()。 + let inputting = buildInputtingState + inputting.textToCommit = textToCommit + stateCallback(inputting) + + /// 逐字選字模式的處理。 + if mgrPrefs.useSCPCTypingMode { + let choosingCandidates: InputState.ChoosingCandidate = buildCandidate( + state: inputting, + isTypingVertical: input.isTypingVertical + ) + if choosingCandidates.candidates.count == 1 { + clear() + let reading: String = choosingCandidates.candidates.first?.0 ?? "" + let text: String = choosingCandidates.candidates.first?.1 ?? "" + stateCallback(InputState.Committing(textToCommit: text)) + + if !mgrPrefs.associatedPhrasesEnabled { + stateCallback(InputState.Empty()) + } else { + if let associatedPhrases = + buildAssociatePhraseState( + withPair: .init(key: reading, value: text), + isTypingVertical: input.isTypingVertical + ), !associatedPhrases.candidates.isEmpty + { + stateCallback(associatedPhrases) + } else { + stateCallback(InputState.Empty()) + } + } + } else { + stateCallback(choosingCandidates) + } + } + // 將「這個按鍵訊號已經被輸入法攔截處理了」的結果藉由 ctlInputMethod 回報給 IMK。 + return true + } + + /// 如果此時這個選項是 true 的話,可知當前注拼槽輸入了聲調、且上一次按鍵不是聲調按鍵。 + /// 比方說大千傳統佈局敲「6j」會出現「ˊㄨ」但並不會被認為是「ㄨˊ」,因為先輸入的調號 + /// 並非用來確認這個注音的調號。除非是:「ㄨˊ」「ˊㄨˊ」「ˊㄨˇ」「ˊㄨ 」等。 + if keyConsumedByReading { + // 以回呼組字狀態的方式來執行 updateClientComposingBuffer()。 + stateCallback(buildInputtingState) + return true + } + return nil + } +} diff --git a/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift b/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift index c801d32f..a0c5756d 100644 --- a/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift +++ b/Source/Modules/ControllerModules/KeyHandler_HandleInput.swift @@ -45,18 +45,17 @@ extension KeyHandler { stateCallback: @escaping (InputStateProtocol) -> Void, errorCallback: @escaping () -> Void ) -> Bool { + // 如果按鍵訊號內的 inputTest 是空的話,則忽略該按鍵輸入,因為很可能是功能修飾鍵。 + guard !input.inputText.isEmpty else { return false } + let charCode: UniChar = input.charCode + let inputText: String = input.inputText var state = state // 常數轉變數。 - // 如果按鍵訊號內的 inputTest 是空的話,則忽略該按鍵輸入,因為很可能是功能修飾鍵。 - guard let inputText: String = input.inputText, !inputText.isEmpty else { - return false - } - // 提前過濾掉一些不合規的按鍵訊號輸入,免得相關按鍵訊號被送給 Megrez 引發輸入法崩潰。 - if input.isInvalidInput { + if input.isInvalid { // 在「.Empty(IgnoringPreviousState) 與 .Deactivated」狀態下的首次不合規按鍵輸入可以直接放行。 - // 因為「.EmptyIgnorePreviousState」會在處理之後被自動轉為「.Empty」,所以不需要單獨判斷。 + // 因為「.EmptyIgnoringPreviousState」會在處理之後被自動轉為「.Empty」,所以不需要單獨判斷。 if state is InputState.Empty || state is InputState.Deactivated { return false } @@ -68,7 +67,7 @@ extension KeyHandler { // 如果當前組字器為空的話,就不再攔截某些修飾鍵,畢竟這些鍵可能會會用來觸發某些功能。 let isFunctionKey: Bool = - input.isControlHotKey || (input.isCommandHold || input.isOptionHotKey || input.isNumericPad) + input.isControlHotKey || (input.isCommandHold || input.isOptionHotKey || input.isNonLaptopFunctionKey) if !(state is InputState.NotEmpty) && !(state is InputState.AssociatedPhrases) && isFunctionKey { return false } @@ -79,8 +78,9 @@ extension KeyHandler { /// 這裡的處理原先是給威注音曾經有過的 Shift 切換英數模式來用的,但因為採 Chromium 核 /// 心的瀏覽器會讓 IMK 無法徹底攔截對 Shift 鍵的單擊行為、導致這個模式的使用體驗非常糟 /// 糕,故僅保留以 Caps Lock 驅動的英數模式。 - if input.isBackSpace || input.isEnter || input.isAbsorbedArrowKey || input.isExtraChooseCandidateKey - || input.isExtraChooseCandidateKeyReverse || input.isCursorForward || input.isCursorBackward + if input.isBackSpace || input.isEnter + || input.isCursorClockLeft || input.isCursorClockRight + || input.isCursorForward || input.isCursorBackward { // 略過對 BackSpace 的處理。 } else if input.isCapsLockOn { @@ -108,10 +108,10 @@ extension KeyHandler { // MARK: 處理數字小鍵盤 (Numeric Pad Processing) - // 這裡必須用「isNumericPadAreaKey」這個以 KeyCode 判定數字鍵區輸入的方法來鎖定按鍵範圍。 - // 不然的話,會誤傷到在主鍵盤區域的功能鍵。 + // 這裡的「isNumericPadKey」處理邏輯已經改成用 KeyCode 判定數字鍵區輸入、以鎖定按鍵範圍。 + // 不然、使用 Cocoa 內建的 flags 的話,會誤傷到在主鍵盤區域的功能鍵。 // 我們先規定允許小鍵盤區域操縱選字窗,其餘場合一律直接放行。 - if input.isNumericPadAreaKey { + if input.isNumericPadKey { if !(state is InputState.ChoosingCandidate || state is InputState.AssociatedPhrases || state is InputState.SymbolTable) { @@ -158,117 +158,17 @@ extension KeyHandler { // MARK: 注音按鍵輸入處理 (Handle BPMF Keys) - var keyConsumedByReading = false - let skipPhoneticHandling = input.isReservedKey || input.isControlHold || input.isOptionHold - - // 這裡 inputValidityCheck() 是讓注拼槽檢查 charCode 這個 UniChar 是否是合法的注音輸入。 - // 如果是的話,就將這次傳入的這個按鍵訊號塞入注拼槽內且標記為「keyConsumedByReading」。 - // 函式 composer.receiveKey() 可以既接收 String 又接收 UniChar。 - if !skipPhoneticHandling && composer.inputValidityCheck(key: charCode) { - composer.receiveKey(fromCharCode: charCode) - keyConsumedByReading = true - - // 沒有調號的話,只需要 updateClientComposingBuffer() 且終止處理(return true)即可。 - // 有調號的話,則不需要這樣,而是轉而繼續在此之後的處理。 - if !composer.hasToneMarker() { - stateCallback(buildInputtingState) - return true - } - } - - var composeReading = composer.hasToneMarker() // 這裡不需要做排他性判斷。 - - // 如果當前的按鍵是 Enter 或 Space 的話,這時就可以取出 _composer 內的注音來做檢查了。 - // 來看看詞庫內到底有沒有對應的讀音索引。這裡用了類似「|=」的判斷處理方式。 - composeReading = composeReading || (!composer.isEmpty && (input.isSpace || input.isEnter)) - if composeReading { - if input.isSpace, !composer.hasToneMarker() { - // 補上空格,否則倚天忘形與許氏排列某些音無法響應不了陰平聲調。 - // 小麥注音因為使用 OVMandarin,所以不需要這樣補。但鐵恨引擎對所有聲調一視同仁。 - composer.receiveKey(fromString: " ") - } - let readingKey = composer.getComposition() // 拿取用來進行索引檢索用的注音。 - // 如果輸入法的辭典索引是漢語拼音的話,要注意上一行拿到的內容得是漢語拼音。 - - // 向語言模型詢問是否有對應的記錄。 - if !currentLM.hasUnigramsFor(key: readingKey) { - IME.prtDebugIntel("B49C0979:語彙庫內無「\(readingKey)」的匹配記錄。") - errorCallback() - composer.clear() - // 根據「組字器是否為空」來判定回呼哪一種狀態。 - stateCallback((compositor.isEmpty) ? InputState.EmptyIgnoringPreviousState() : buildInputtingState) - return true // 向 IMK 報告說這個按鍵訊號已經被輸入法攔截處理了。 - } - - // 將該讀音插入至組字器內的軌格當中。 - compositor.insertReading(readingKey) - - // 讓組字器反爬軌格。 - let textToCommit = commitOverflownCompositionAndWalk - - // 看看半衰記憶模組是否會對目前的狀態給出自動選字建議。 - fetchAndApplySuggestionsFromUserOverrideModel() - - // 將組字器內超出最大動態爬軌範圍的節錨都標記為「已經手動選字過」,減少之後的爬軌運算負擔。 - markNodesFixedIfNecessary() - - // 之後就是更新組字區了。先清空注拼槽的內容。 - composer.clear() - - // 再以回呼組字狀態的方式來執行 updateClientComposingBuffer()。 - let inputting = buildInputtingState - inputting.textToCommit = textToCommit - stateCallback(inputting) - - /// 逐字選字模式的處理。 - if mgrPrefs.useSCPCTypingMode { - let choosingCandidates: InputState.ChoosingCandidate = buildCandidate( - state: inputting, - isTypingVertical: input.isTypingVertical - ) - if choosingCandidates.candidates.count == 1 { - clear() - let reading: String = choosingCandidates.candidates.first?.0 ?? "" - let text: String = choosingCandidates.candidates.first?.1 ?? "" - stateCallback(InputState.Committing(textToCommit: text)) - - if !mgrPrefs.associatedPhrasesEnabled { - stateCallback(InputState.Empty()) - } else { - if let associatedPhrases = - buildAssociatePhraseState( - withPair: .init(key: reading, value: text), - isTypingVertical: input.isTypingVertical - ), !associatedPhrases.candidates.isEmpty - { - stateCallback(associatedPhrases) - } else { - stateCallback(InputState.Empty()) - } - } - } else { - stateCallback(choosingCandidates) - } - } - // 將「這個按鍵訊號已經被輸入法攔截處理了」的結果藉由 ctlInputMethod 回報給 IMK。 - return true - } - - /// 如果此時這個選項是 true 的話,可知當前注拼槽輸入了聲調、且上一次按鍵不是聲調按鍵。 - /// 比方說大千傳統佈局敲「6j」會出現「ˊㄨ」但並不會被認為是「ㄨˊ」,因為先輸入的調號 - /// 並非用來確認這個注音的調號。除非是:「ㄨˊ」「ˊㄨˊ」「ˊㄨˇ」「ˊㄨ 」等。 - if keyConsumedByReading { - // 以回呼組字狀態的方式來執行 updateClientComposingBuffer()。 - stateCallback(buildInputtingState) - return true + if let compositionHandled = handleComposition( + input: input, state: state, stateCallback: stateCallback, errorCallback: errorCallback) + { + return compositionHandled } // MARK: 用上下左右鍵呼叫選字窗 (Calling candidate window using Up / Down or PageUp / PageDn.) if let currentState = state as? InputState.NotEmpty, composer.isEmpty, !input.isOptionHold, - input.isExtraChooseCandidateKey || input.isExtraChooseCandidateKeyReverse || input.isSpace + input.isCursorClockLeft || input.isCursorClockRight || input.isSpace || input.isPageDown || input.isPageUp || (input.isTab && mgrPrefs.specifyShiftTabKeyBehavior) - || (input.isTypingVertical && (input.isVerticalTypingOnlyChooseCandidateKey)) { if input.isSpace { /// 倘若沒有在偏好設定內將 Space 空格鍵設為選字窗呼叫用鍵的話……… @@ -354,22 +254,22 @@ extension KeyHandler { return handleEnd(state: state, stateCallback: stateCallback, errorCallback: errorCallback) } - // MARK: AbsorbedArrowKey + // MARK: Clock-Left & Clock-Right - if input.isAbsorbedArrowKey || input.isExtraChooseCandidateKey || input.isExtraChooseCandidateKeyReverse { + if input.isCursorClockLeft || input.isCursorClockRight { if input.isOptionHold, state is InputState.Inputting { - if input.isExtraChooseCandidateKey { + if input.isCursorClockRight { return handleInlineCandidateRotation( state: state, reverseModifier: false, stateCallback: stateCallback, errorCallback: errorCallback ) } - if input.isExtraChooseCandidateKeyReverse { + if input.isCursorClockLeft { return handleInlineCandidateRotation( state: state, reverseModifier: true, stateCallback: stateCallback, errorCallback: errorCallback ) } } - return handleAbsorbedArrowKey(state: state, stateCallback: stateCallback, errorCallback: errorCallback) + return handleClockKey(state: state, stateCallback: stateCallback, errorCallback: errorCallback) } // MARK: Backspace @@ -424,6 +324,23 @@ extension KeyHandler { } } + // MARK: 全形/半形阿拉伯數字輸入 (FW / HW Arabic Numbers Input) + + if state is InputState.Empty { + if input.isMainAreaNumKey, input.isShiftHold, input.isOptionHold, !input.isControlHold, !input.isCommandHold { + // NOTE: 將來棄用 macOS 10.11 El Capitan 支援的時候,把這裡由 CFStringTransform 改為 StringTransform: + // https://developer.apple.com/documentation/foundation/stringtransform + guard let stringRAW = input.mapMainAreaNumKey[input.keyCode] else { return false } + let string = NSMutableString(string: stringRAW) + CFStringTransform(string, nil, kCFStringTransformFullwidthHalfwidth, true) + stateCallback( + InputState.Committing(textToCommit: mgrPrefs.halfWidthPunctuationEnabled ? stringRAW : string as String) + ) + stateCallback(InputState.Empty()) + return true + } + } + // MARK: Punctuation /// 如果仍無匹配結果的話,先看一下: @@ -479,11 +396,11 @@ extension KeyHandler { /// 該功能僅可在當前組字區沒有任何內容的時候使用。 if state is InputState.Empty { - if input.isSpace, !input.isOptionHold, !input.isFunctionKeyHold, !input.isControlHold, !input.isCommandHold { + if input.isSpace, !input.isOptionHold, !input.isControlHold, !input.isCommandHold { stateCallback(InputState.Committing(textToCommit: input.isShiftHold ? " " : " ")) + stateCallback(InputState.Empty()) + return true } - stateCallback(InputState.Empty()) - return true } // MARK: - 終末處理 (Still Nothing) diff --git a/Source/Modules/ControllerModules/KeyHandler_States.swift b/Source/Modules/ControllerModules/KeyHandler_States.swift index 4bd97370..04bf65c0 100644 --- a/Source/Modules/ControllerModules/KeyHandler_States.swift +++ b/Source/Modules/ControllerModules/KeyHandler_States.swift @@ -509,7 +509,7 @@ extension KeyHandler { /// - stateCallback: 狀態回呼。 /// - errorCallback: 錯誤回呼。 /// - Returns: 將按鍵行為「是否有處理掉」藉由 ctlInputMethod 回報給 IMK。 - func handleAbsorbedArrowKey( + func handleClockKey( state: InputStateProtocol, stateCallback: @escaping (InputStateProtocol) -> Void, errorCallback: @escaping () -> Void diff --git a/Source/Modules/ControllerModules/ctlInputMethod_Delegates.swift b/Source/Modules/ControllerModules/ctlInputMethod_Delegates.swift index c930bffd..c6f19fb3 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_Delegates.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_Delegates.swift @@ -101,7 +101,7 @@ extension ctlInputMethod: ctlCandidateDelegate { if let children = node.children, !children.isEmpty { handle(state: InputState.Empty()) // 防止縱橫排選字窗同時出現 handle( - state: InputState.SymbolTable(node: node, isTypingVertical: state.isTypingVertical) + state: InputState.SymbolTable(node: node, previous: state.node, isTypingVertical: state.isTypingVertical) ) } else { handle(state: InputState.Committing(textToCommit: node.title)) @@ -120,6 +120,7 @@ extension ctlInputMethod: ctlCandidateDelegate { keyHandler.clear() let composingBuffer = inputting.composingBuffer handle(state: InputState.Committing(textToCommit: composingBuffer)) + // 此時是逐字選字模式,所以「selectedValue.1」是單個字、不用追加處理。 if mgrPrefs.associatedPhrasesEnabled, let associatePhrases = keyHandler.buildAssociatePhraseState( withPair: .init(key: selectedValue.0, value: selectedValue.1), @@ -139,16 +140,22 @@ extension ctlInputMethod: ctlCandidateDelegate { if let state = state as? InputState.AssociatedPhrases { let selectedValue = state.candidates[index] handle(state: InputState.Committing(textToCommit: selectedValue.1)) + // 此時是聯想詞選字模式,所以「selectedValue.1」必須只保留最後一個字。 + // 不然的話,一旦你選中了由多個字組成的聯想候選詞,則連續聯想會被打斷。 + guard let valueKept = selectedValue.1.last else { + handle(state: InputState.Empty()) + return + } if mgrPrefs.associatedPhrasesEnabled, let associatePhrases = keyHandler.buildAssociatePhraseState( - withPair: .init(key: selectedValue.0, value: selectedValue.1), + withPair: .init(key: selectedValue.0, value: String(valueKept)), isTypingVertical: state.isTypingVertical ), !associatePhrases.candidates.isEmpty { handle(state: associatePhrases) - } else { - handle(state: InputState.Empty()) + return } + handle(state: InputState.Empty()) } } } diff --git a/Source/Modules/ControllerModules/ctlInputMethod_HandleStates.swift b/Source/Modules/ControllerModules/ctlInputMethod_HandleStates.swift index 0c5f78a4..2e37e696 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_HandleStates.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_HandleStates.swift @@ -63,6 +63,14 @@ extension ctlInputMethod { /// 針對受 .NotEmpty() 管轄的非空狀態,在組字區內顯示游標。 private func setInlineDisplayWithCursor() { + if let state = state as? InputState.AssociatedPhrases { + client().setMarkedText( + state.attributedString, selectionRange: NSRange(location: 0, length: 0), + replacementRange: NSRange(location: NSNotFound, length: NSNotFound) + ) + return + } + guard let state = state as? InputState.NotEmpty else { clearInlineDisplay() return @@ -225,7 +233,7 @@ extension ctlInputMethod { private func handle(state: InputState.AssociatedPhrases, previous: InputStateProtocol) { _ = previous // 防止格式整理工具毀掉與此對應的參數。 ctlInputMethod.tooltipController.hide() - clearInlineDisplay() + setInlineDisplayWithCursor() show(candidateWindowWith: state) } } diff --git a/Source/Modules/ControllerModules/ctlInputMethod_Menu.swift b/Source/Modules/ControllerModules/ctlInputMethod_Menu.swift index ff852107..89bd4a92 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_Menu.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_Menu.swift @@ -79,6 +79,13 @@ extension ctlInputMethod { shiftJISConversionItem.state = mgrPrefs.shiftJISShinjitaiOutputEnabled.state } + let currencyNumeralsItem = menu.addItem( + withTitle: NSLocalizedString("Currency Numeral Output", comment: ""), + action: #selector(toggleCurrencyNumerals(_:)), keyEquivalent: mgrPrefs.usingHotKeyCurrencyNumerals ? "M" : "" + ) + currencyNumeralsItem.keyEquivalentModifierMask = [.command, .control] + currencyNumeralsItem.state = mgrPrefs.currencyNumeralsEnabled.state + let halfWidthPunctuationItem = menu.addItem( withTitle: NSLocalizedString("Half-Width Punctuation Mode", comment: ""), action: #selector(toggleHalfWidthPunctuation(_:)), keyEquivalent: mgrPrefs.usingHotKeyHalfWidthASCII ? "H" : "" @@ -245,6 +252,17 @@ extension ctlInputMethod { )) } + @objc func toggleCurrencyNumerals(_: Any?) { + resetKeyHandler() + NotifierController.notify( + message: String( + format: "%@%@%@", NSLocalizedString("Currency Numeral Output", comment: ""), "\n", + mgrPrefs.toggleCurrencyNumeralsEnabled() + ? NSLocalizedString("NotificationSwitchON", comment: "") + : NSLocalizedString("NotificationSwitchOFF", comment: "") + )) + } + @objc func toggleHalfWidthPunctuation(_: Any?) { resetKeyHandler() NotifierController.notify( diff --git a/Source/Modules/IMEModules/IME.swift b/Source/Modules/IMEModules/IME.swift index 45b5f1bb..516fdea7 100644 --- a/Source/Modules/IMEModules/IME.swift +++ b/Source/Modules/IMEModules/IME.swift @@ -450,6 +450,8 @@ extension Sequence { } } +// MARK: - Shell Extension + extension NSApplication { public static func shell(_ command: String) throws -> String { let task = Process() diff --git a/Source/Modules/IMEModules/mgrPrefs.swift b/Source/Modules/IMEModules/mgrPrefs.swift index e2e0ff2b..bbf0a40b 100644 --- a/Source/Modules/IMEModules/mgrPrefs.swift +++ b/Source/Modules/IMEModules/mgrPrefs.swift @@ -45,6 +45,7 @@ struct UserDef { 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" @@ -74,6 +75,7 @@ struct UserDef { static let kUsingHotKeyKangXi = "UsingHotKeyKangXi" static let kUsingHotKeyJIS = "UsingHotKeyJIS" static let kUsingHotKeyHalfWidthASCII = "UsingHotKeyHalfWidthASCII" + static let kUsingHotKeyCurrencyNumerals = "UsingHotKeyCurrencyNumerals" } private let kDefaultCandidateListTextSize: CGFloat = 18 @@ -286,6 +288,9 @@ public enum mgrPrefs { UserDefaults.standard.setDefault( mgrPrefs.alsoConfirmAssociatedCandidatesByEnter, forKey: UserDef.kAlsoConfirmAssociatedCandidatesByEnter ) + UserDefaults.standard.setDefault( + mgrPrefs.currencyNumeralsEnabled, forKey: UserDef.kCurrencyNumeralsEnabled + ) UserDefaults.standard.setDefault(mgrPrefs.usingHotKeySCPC, forKey: UserDef.kUsingHotKeySCPC) UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyAssociates, forKey: UserDef.kUsingHotKeyAssociates) @@ -293,6 +298,7 @@ public enum mgrPrefs { 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.synchronize() } @@ -456,6 +462,14 @@ public enum mgrPrefs { return shiftJISShinjitaiOutputEnabled } + @UserDefault(key: UserDef.kCurrencyNumeralsEnabled, defaultValue: false) + static var currencyNumeralsEnabled: Bool + + static func toggleCurrencyNumeralsEnabled() -> Bool { + currencyNumeralsEnabled = !currencyNumeralsEnabled + return currencyNumeralsEnabled + } + @UserDefault(key: UserDef.kHalfWidthPunctuationEnabled, defaultValue: false) static var halfWidthPunctuationEnabled: Bool @@ -584,6 +598,9 @@ public enum mgrPrefs { @UserDefault(key: UserDef.kUsingHotKeyHalfWidthASCII, defaultValue: true) static var usingHotKeyHalfWidthASCII: Bool + + @UserDefault(key: UserDef.kUsingHotKeyCurrencyNumerals, defaultValue: true) + static var usingHotKeyCurrencyNumerals: Bool } // MARK: Snapshot Extension @@ -609,6 +626,7 @@ extension mgrPrefs { UserDef.kUsingHotKeyAssociates, UserDef.kUsingHotKeyCNS, UserDef.kUsingHotKeyKangXi, UserDef.kUsingHotKeyJIS, UserDef.kUsingHotKeyHalfWidthASCII, UserDef.kUseFixecCandidateOrderOnSelection, UserDef.kAutoCorrectReadingCombination, UserDef.kAlsoConfirmAssociatedCandidatesByEnter, + UserDef.kCurrencyNumeralsEnabled, UserDef.kUsingHotKeyCurrencyNumerals, ] } diff --git a/Source/Modules/LangModelRelated/LMSymbolNode.swift b/Source/Modules/LangModelRelated/LMSymbolNode.swift index e7270e83..dc2ae11c 100644 --- a/Source/Modules/LangModelRelated/LMSymbolNode.swift +++ b/Source/Modules/LangModelRelated/LMSymbolNode.swift @@ -29,6 +29,7 @@ import Foundation class SymbolNode { var title: String var children: [SymbolNode]? + var previous: SymbolNode? init(_ title: String, _ children: [SymbolNode]? = nil) { self.title = title diff --git a/Source/Resources/Base.lproj/Localizable.strings b/Source/Resources/Base.lproj/Localizable.strings index bdb8adb1..5a350c35 100644 --- a/Source/Resources/Base.lproj/Localizable.strings +++ b/Source/Resources/Base.lproj/Localizable.strings @@ -64,6 +64,7 @@ "Core Dict loading complete." = "Core Dict loading complete."; "Optimize Memorized Phrases" = "Optimize Memorized Phrases"; "Clear Memorized Phrases" = "Clear Memorized Phrases"; +"Currency Numeral Output" = "Currency Numeral Output"; // The followings are the category names used in the Symbol menu. "catCommonSymbols" = "CommonSymbols"; diff --git a/Source/Resources/en.lproj/Localizable.strings b/Source/Resources/en.lproj/Localizable.strings index bdb8adb1..5a350c35 100644 --- a/Source/Resources/en.lproj/Localizable.strings +++ b/Source/Resources/en.lproj/Localizable.strings @@ -64,6 +64,7 @@ "Core Dict loading complete." = "Core Dict loading complete."; "Optimize Memorized Phrases" = "Optimize Memorized Phrases"; "Clear Memorized Phrases" = "Clear Memorized Phrases"; +"Currency Numeral Output" = "Currency Numeral Output"; // The followings are the category names used in the Symbol menu. "catCommonSymbols" = "CommonSymbols"; diff --git a/Source/Resources/ja.lproj/Localizable.strings b/Source/Resources/ja.lproj/Localizable.strings index b5dd73aa..cf68e40d 100644 --- a/Source/Resources/ja.lproj/Localizable.strings +++ b/Source/Resources/ja.lproj/Localizable.strings @@ -64,6 +64,7 @@ "Core Dict loading complete." = "核心辞書読込完了"; "Optimize Memorized Phrases" = "臨時記憶資料を整う"; "Clear Memorized Phrases" = "臨時記憶資料を削除"; +"Currency Numeral Output" = "数字大字変換"; // The followings are the category names used in the Symbol menu. "catCommonSymbols" = "常用"; diff --git a/Source/Resources/zh-Hans.lproj/Localizable.strings b/Source/Resources/zh-Hans.lproj/Localizable.strings index d3520bb3..05f23bf9 100644 --- a/Source/Resources/zh-Hans.lproj/Localizable.strings +++ b/Source/Resources/zh-Hans.lproj/Localizable.strings @@ -64,6 +64,7 @@ "Core Dict loading complete." = "核心辞典载入完毕"; "Optimize Memorized Phrases" = "精简临时记忆语汇资料"; "Clear Memorized Phrases" = "清除临时记忆语汇资料"; +"Currency Numeral Output" = "大写汉字数字输出"; // The followings are the category names used in the Symbol menu. "catCommonSymbols" = "常用"; diff --git a/Source/Resources/zh-Hant.lproj/Localizable.strings b/Source/Resources/zh-Hant.lproj/Localizable.strings index fb3b989e..64ec4aff 100644 --- a/Source/Resources/zh-Hant.lproj/Localizable.strings +++ b/Source/Resources/zh-Hant.lproj/Localizable.strings @@ -64,6 +64,7 @@ "Core Dict loading complete." = "核心辭典載入完畢"; "Optimize Memorized Phrases" = "精簡臨時記憶語彙資料"; "Clear Memorized Phrases" = "清除臨時記憶語彙資料"; +"Currency Numeral Output" = "大寫漢字數字輸出"; // The followings are the category names used in the Symbol menu. "catCommonSymbols" = "常用"; diff --git a/Source/UI/PrefUI/suiPrefPaneKeyboard.swift b/Source/UI/PrefUI/suiPrefPaneKeyboard.swift index 27fbae6e..734eb4db 100644 --- a/Source/UI/PrefUI/suiPrefPaneKeyboard.swift +++ b/Source/UI/PrefUI/suiPrefPaneKeyboard.swift @@ -37,6 +37,8 @@ struct suiPrefPaneKeyboard: View { @State private var selUsingHotKeyJIS = UserDefaults.standard.bool(forKey: UserDef.kUsingHotKeyJIS) @State private var selUsingHotKeyHalfWidthASCII = UserDefaults.standard.bool( forKey: UserDef.kUsingHotKeyHalfWidthASCII) + @State private var selUsingHotKeyCurrencyNumerals = UserDefaults.standard.bool( + forKey: UserDef.kUsingHotKeyCurrencyNumerals) private let contentWidth: Double = { switch mgrPrefs.appleLanguages[0] { @@ -174,6 +176,13 @@ struct suiPrefPaneKeyboard: View { mgrPrefs.usingHotKeyHalfWidthASCII = value selUsingHotKeyHalfWidthASCII = value } + Toggle( + LocalizedStringKey("Currency Numeral Output"), + isOn: $selUsingHotKeyCurrencyNumerals + ).onChange(of: selUsingHotKeyCurrencyNumerals) { value in + mgrPrefs.usingHotKeyCurrencyNumerals = value + selUsingHotKeyCurrencyNumerals = value + } } } Divider() diff --git a/Source/WindowNIBs/Base.lproj/frmPrefWindow.xib b/Source/WindowNIBs/Base.lproj/frmPrefWindow.xib index 758a4f72..51e6b55d 100644 --- a/Source/WindowNIBs/Base.lproj/frmPrefWindow.xib +++ b/Source/WindowNIBs/Base.lproj/frmPrefWindow.xib @@ -184,7 +184,7 @@ - - - - - + - + - + - - + + + + + - - + + + + + - - + - - + - - - - + + + + - + + - - - + - + diff --git a/Source/WindowNIBs/en.lproj/frmPrefWindow.strings b/Source/WindowNIBs/en.lproj/frmPrefWindow.strings index 20fce96f..faa4b4db 100644 --- a/Source/WindowNIBs/en.lproj/frmPrefWindow.strings +++ b/Source/WindowNIBs/en.lproj/frmPrefWindow.strings @@ -20,7 +20,6 @@ "29.title" = "Candidate UI font size:"; "2iG-Ic-gbl.label" = "Dictionary"; "2pS-nv-te4.title" = "Choose which keys you prefer for selecting candidates."; -"xibGeneralSettings.title" = "General Settings"; "5.title" = "OtherViews"; "6.title" = "Microsoft, Dachen, Wang, etc."; "62u-jY-BRh.title" = "Stop farting (when typed phonetic combination is invalid, etc.)"; @@ -74,15 +73,17 @@ "sZx-18-8dO.title" = "Debug Mode"; "TXr-FF-ehw.title" = "Traditional Chinese"; "ueU-Rz-a1C.title" = "Choose the behavior of (Shift+)Tab key in the candidate window."; -"xibOutputSettings.title" = "Output Settings"; "W24-T4-cg0.title" = "Enable CNS11643 Support (2022-07-20)"; "wFR-zX-M8H.title" = "Show Hanyu-Pinyin in the inline composition buffer & tooltip"; "wN3-k3-b2a.title" = "Choose your desired user data folder path. Will be omitted if invalid."; "wQ9-px-b07.title" = "Apple Dynamic Bopomofo Basic Keyboard Layouts (Dachen & Eten Traditional) must match the Dachen parser in order to be functional."; "Wvt-HE-LOv.title" = "Keyboard Layout"; "xC5-yV-1W1.title" = "Choose your preferred layout of the candidate window."; +"xibGeneralSettings.title" = "General Settings"; "xibKeyboardShortcuts.title" = "Keyboard Shortcuts"; "xibLabelBufferLimit.title" = "Buffer Limit:"; +"xibOutputSettings.title" = "Output Settings"; +"xibUsingCurrencyNumerals.title" = "Currency Numeral Output"; "xibUsingHotKeyAssociates.title" = "Per-Char Associated Phrases"; "xibUsingHotKeyCNS.title" = "CNS11643 Mode"; "xibUsingHotKeyHalfWidthASCII.title" = "Half-Width Punctuation Mode"; diff --git a/Source/WindowNIBs/ja.lproj/frmPrefWindow.strings b/Source/WindowNIBs/ja.lproj/frmPrefWindow.strings index 0cdb3dfc..21797c87 100644 --- a/Source/WindowNIBs/ja.lproj/frmPrefWindow.strings +++ b/Source/WindowNIBs/ja.lproj/frmPrefWindow.strings @@ -20,7 +20,6 @@ "29.title" = "候補文字の字号:"; "2iG-Ic-gbl.label" = "辞書"; "2pS-nv-te4.title" = "お好きなる言選り用キー陣列をお選びください。"; -"xibGeneralSettings.title" = "全般設定"; "5.title" = "OtherViews"; "6.title" = "大千配列(Microsoft 標準・王安など)"; "62u-jY-BRh.title" = "マナーモード // 外すと入力間違の時に変な音が出る"; @@ -74,15 +73,17 @@ "sZx-18-8dO.title" = "欠陥辿着モード"; "TXr-FF-ehw.title" = "繁体中国語"; "ueU-Rz-a1C.title" = "入力候補陳列での (Shift+)Tab キーの輪番切替対象をご指定ください。"; -"xibOutputSettings.title" = "出力設定"; "W24-T4-cg0.title" = "全字庫モード // 入力可能の漢字数倍増 (2022-07-20)"; "wFR-zX-M8H.title" = "弁音合併入力(入力緩衝列とヒントで音読みを漢語弁音に)"; "wN3-k3-b2a.title" = "欲しがるユーザー辞書保存先をご指定ください。無効の保存先設定は効かぬ。"; "wQ9-px-b07.title" = "Apple 動態注音キーボード (大千と倚天伝統) を使うには、共通語分析器の注音配列を大千と設定すべきである。"; "Wvt-HE-LOv.title" = "キーボード"; "xC5-yV-1W1.title" = "入力候補陳列の仕様をご指定ください。"; +"xibGeneralSettings.title" = "全般設定"; "xibKeyboardShortcuts.title" = "ショートカット"; "xibLabelBufferLimit.title" = "緩衝列の容量:"; +"xibOutputSettings.title" = "出力設定"; +"xibUsingCurrencyNumerals.title" = "数字大字変換"; "xibUsingHotKeyAssociates.title" = "全候補入力で連想語彙"; "xibUsingHotKeyCNS.title" = "全字庫モード"; "xibUsingHotKeyHalfWidthASCII.title" = "半角句読モード"; diff --git a/Source/WindowNIBs/zh-Hans.lproj/frmPrefWindow.strings b/Source/WindowNIBs/zh-Hans.lproj/frmPrefWindow.strings index 13328caa..0699b46e 100644 --- a/Source/WindowNIBs/zh-Hans.lproj/frmPrefWindow.strings +++ b/Source/WindowNIBs/zh-Hans.lproj/frmPrefWindow.strings @@ -20,7 +20,6 @@ "29.title" = "字型大小设定:"; "2iG-Ic-gbl.label" = "辞典"; "2pS-nv-te4.title" = "选择您所偏好的用来选字的按键组合。"; -"xibGeneralSettings.title" = "一般设定"; "5.title" = "OtherViews"; "6.title" = "微软/大千/王安/国乔/零壹/仲鼎"; "62u-jY-BRh.title" = "廉耻模式 // 取消勾选的话,敲错字时会有异音"; @@ -74,15 +73,17 @@ "sZx-18-8dO.title" = "侦错模式"; "TXr-FF-ehw.title" = "繁体中文"; "ueU-Rz-a1C.title" = "指定 (Shift+)Tab 热键在选字窗内的轮替操作对象。"; -"xibOutputSettings.title" = "输出设定"; "W24-T4-cg0.title" = "启用 CNS11643 全字库支援 (2022-07-20)"; "wFR-zX-M8H.title" = "拼音并击(组字区与工具提示内显示汉语拼音)"; "wN3-k3-b2a.title" = "请在此指定您想指定的使用者语汇档案目录。无效值会被忽略。"; "wQ9-px-b07.title" = "Apple 动态注音键盘布局(大千与倚天)要求普通话/国音分析器的注音排列得配置为大千排列。"; "Wvt-HE-LOv.title" = "键盘布局"; "xC5-yV-1W1.title" = "选择您所偏好的候选字窗布局。"; +"xibGeneralSettings.title" = "一般设定"; "xibKeyboardShortcuts.title" = "键盘快捷键"; "xibLabelBufferLimit.title" = "组字区容量:"; +"xibOutputSettings.title" = "输出设定"; +"xibUsingCurrencyNumerals.title" = "大写汉字数字输出"; "xibUsingHotKeyAssociates.title" = "逐字选字联想模式"; "xibUsingHotKeyCNS.title" = "全字库模式"; "xibUsingHotKeyHalfWidthASCII.title" = "半形标点模式"; diff --git a/Source/WindowNIBs/zh-Hant.lproj/frmPrefWindow.strings b/Source/WindowNIBs/zh-Hant.lproj/frmPrefWindow.strings index d3338f64..75944562 100644 --- a/Source/WindowNIBs/zh-Hant.lproj/frmPrefWindow.strings +++ b/Source/WindowNIBs/zh-Hant.lproj/frmPrefWindow.strings @@ -20,7 +20,6 @@ "29.title" = "字型大小設定:"; "2iG-Ic-gbl.label" = "辭典"; "2pS-nv-te4.title" = "選擇您所偏好的用來選字的按鍵組合。"; -"xibGeneralSettings.title" = "一般設定"; "5.title" = "OtherViews"; "6.title" = "微軟/大千/王安/國喬/零壹/仲鼎"; "62u-jY-BRh.title" = "廉恥模式 // 取消勾選的話,敲錯字時會有異音"; @@ -74,15 +73,17 @@ "sZx-18-8dO.title" = "偵錯模式"; "TXr-FF-ehw.title" = "繁體中文"; "ueU-Rz-a1C.title" = "指定 (Shift+)Tab 熱鍵在選字窗內的輪替操作對象。"; -"xibOutputSettings.title" = "輸出設定"; "W24-T4-cg0.title" = "啟用 CNS11643 全字庫支援 (2022-07-20)"; "wFR-zX-M8H.title" = "拼音並擊(組字區與工具提示內顯示漢語拼音)"; "wN3-k3-b2a.title" = "請在此指定您想指定的使用者語彙檔案目錄。無效值會被忽略。"; "wQ9-px-b07.title" = "Apple 動態注音鍵盤佈局(大千與倚天)要求普通話/國音分析器的注音排列得配置為大千排列。"; "Wvt-HE-LOv.title" = "鍵盤佈局"; "xC5-yV-1W1.title" = "選擇您所偏好的候選字窗佈局。"; +"xibGeneralSettings.title" = "一般設定"; "xibKeyboardShortcuts.title" = "鍵盤快速鍵"; "xibLabelBufferLimit.title" = "組字區容量:"; +"xibOutputSettings.title" = "輸出設定"; +"xibUsingCurrencyNumerals.title" = "大寫漢字數字輸出"; "xibUsingHotKeyAssociates.title" = "逐字選字聯想模式"; "xibUsingHotKeyCNS.title" = "全字庫模式"; "xibUsingHotKeyHalfWidthASCII.title" = "半形標點模式"; diff --git a/Update-Info.plist b/Update-Info.plist index 2c436890..4baff48b 100644 --- a/Update-Info.plist +++ b/Update-Info.plist @@ -3,9 +3,9 @@ CFBundleShortVersionString - 1.8.4 + 1.8.5 CFBundleVersion - 1984 + 1985 UpdateInfoEndpoint https://gitee.com/vchewing/vChewing-macOS/raw/main/Update-Info.plist UpdateInfoSite diff --git a/vChewing.pkgproj b/vChewing.pkgproj index 46430362..ff5330b3 100644 --- a/vChewing.pkgproj +++ b/vChewing.pkgproj @@ -726,7 +726,7 @@ USE_HFS+_COMPRESSION VERSION - 1.8.4 + 1.8.5 TYPE 0 diff --git a/vChewing.xcodeproj/project.pbxproj b/vChewing.xcodeproj/project.pbxproj index 12257b27..aea9c244 100644 --- a/vChewing.xcodeproj/project.pbxproj +++ b/vChewing.xcodeproj/project.pbxproj @@ -102,6 +102,7 @@ 5BD05C6A27B2BBEF004C4F1D /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD05C6527B2BBEF004C4F1D /* ViewController.swift */; }; 5BDC1CFA27FDF1310052C2B9 /* apiUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BDC1CF927FDF1310052C2B9 /* apiUpdate.swift */; }; 5BDCBB2E27B4E67A00D0CC59 /* vChewingPhraseEditor.app in Resources */ = {isa = PBXBuildFile; fileRef = 5BD05BB827B2A429004C4F1D /* vChewingPhraseEditor.app */; }; + 5BE377A0288FED8D0037365B /* KeyHandler_HandleComposition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BE3779F288FED8D0037365B /* KeyHandler_HandleComposition.swift */; }; 5BE78BD927B3775B005EA1BE /* ctlAboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BE78BD827B37750005EA1BE /* ctlAboutWindow.swift */; }; 5BE78BDD27B3776D005EA1BE /* frmAboutWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BE78BDA27B37764005EA1BE /* frmAboutWindow.xib */; }; 5BEDB721283B4C250078EB25 /* data-cns.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5BEDB71D283B4AEA0078EB25 /* data-cns.plist */; }; @@ -303,6 +304,7 @@ 5BDCBB4A27B4F6C700D0CC59 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = ""; }; 5BDCBB4B27B4F6C700D0CC59 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/frmAboutWindow.strings"; sourceTree = ""; }; 5BDCBB4D27B4F6C700D0CC59 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/InfoPlist.strings"; sourceTree = ""; }; + 5BE3779F288FED8D0037365B /* KeyHandler_HandleComposition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyHandler_HandleComposition.swift; sourceTree = ""; }; 5BE78BD827B37750005EA1BE /* ctlAboutWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlAboutWindow.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; 5BE78BDB27B37764005EA1BE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/frmAboutWindow.xib; sourceTree = ""; }; 5BE78BDF27B37968005EA1BE /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/frmAboutWindow.strings; sourceTree = ""; }; @@ -465,6 +467,7 @@ 5BD0113C2818543900609769 /* KeyHandler_Core.swift */, 5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */, 5B7F225C2808501000DDD3CB /* KeyHandler_HandleInput.swift */, + 5BE3779F288FED8D0037365B /* KeyHandler_HandleComposition.swift */, 5B3133BE280B229700A4A505 /* KeyHandler_States.swift */, 5B62A33727AE79CD00A19448 /* StringUtils.swift */, 5BAA8FBD282CAF380066C406 /* SyllableComposer.swift */, @@ -1204,6 +1207,7 @@ 5BA9FD3F27FEF3C8002DE248 /* Pane.swift in Sources */, 5BB802DA27FABA8300CF1C19 /* ctlInputMethod_Menu.swift in Sources */, 5B38F5A1281E2E49007D5F5D /* 1_Compositor.swift in Sources */, + 5BE377A0288FED8D0037365B /* KeyHandler_HandleComposition.swift in Sources */, 5BDC1CFA27FDF1310052C2B9 /* apiUpdate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1389,7 +1393,7 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1984; + CURRENT_PROJECT_VERSION = 1985; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -1399,7 +1403,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.8.4; + MARKETING_VERSION = 1.8.5; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewingTests; @@ -1428,13 +1432,13 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1984; + CURRENT_PROJECT_VERSION = 1985; 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.4; + MARKETING_VERSION = 1.8.5; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewingTests; @@ -1465,7 +1469,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1984; + CURRENT_PROJECT_VERSION = 1985; DEAD_CODE_STRIPPING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; @@ -1486,7 +1490,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.8.4; + MARKETING_VERSION = 1.8.5; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor; @@ -1515,7 +1519,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1984; + CURRENT_PROJECT_VERSION = 1985; DEAD_CODE_STRIPPING = YES; ENABLE_NS_ASSERTIONS = NO; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -1532,7 +1536,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.8.4; + MARKETING_VERSION = 1.8.5; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor; @@ -1646,7 +1650,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1984; + CURRENT_PROJECT_VERSION = 1985; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = ""; @@ -1674,7 +1678,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.8.4; + MARKETING_VERSION = 1.8.5; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1701,7 +1705,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1984; + CURRENT_PROJECT_VERSION = 1985; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = ""; @@ -1723,7 +1727,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.8.4; + MARKETING_VERSION = 1.8.5; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1745,7 +1749,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1984; + CURRENT_PROJECT_VERSION = 1985; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -1765,7 +1769,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.8.4; + MARKETING_VERSION = 1.8.5; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = "org.atelierInmu.vChewing.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1787,7 +1791,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1984; + CURRENT_PROJECT_VERSION = 1985; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -1801,7 +1805,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.8.4; + MARKETING_VERSION = 1.8.5; PRODUCT_BUNDLE_IDENTIFIER = "org.atelierInmu.vChewing.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/vChewingTests/KeyHandlerTestsNormalCHS.swift b/vChewingTests/KeyHandlerTestsNormalCHS.swift index c6e14668..8af4ccc5 100644 --- a/vChewingTests/KeyHandlerTestsNormalCHS.swift +++ b/vChewingTests/KeyHandlerTestsNormalCHS.swift @@ -296,7 +296,7 @@ class KeyHandlerTestsNormalCHS: XCTestCase { XCTAssertTrue(state is InputState.Empty, "\(state)") } - func testisNumericPad() { + func testisNumericPadKey() { var input = InputSignal(inputText: "b", keyCode: 0, charCode: charCode("b"), flags: [], isVerticalTyping: false) var state: InputStateProtocol = InputState.Empty() _ = handler.handle(input: input, state: state) { newState in @@ -304,7 +304,7 @@ class KeyHandlerTestsNormalCHS: XCTestCase { } errorCallback: { } input = InputSignal( - inputText: "1", keyCode: 0, charCode: charCode("1"), flags: .numericPad, isVerticalTyping: false + inputText: "1", keyCode: 83, charCode: charCode("1"), flags: [], isVerticalTyping: false ) var count = 0 var empty: InputStateProtocol = InputState.Empty()