From e1b7a4df9fb32246f644117bd9985633cd795b71 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 11 Feb 2024 20:29:38 +0800 Subject: [PATCH] InputHandler // +Enum: TypingMethod. --- .../InputHandler/InputHandler_Core.swift | 45 ++----- .../InputHandler_HandleCandidate.swift | 3 +- .../InputHandler_HandleComposition.swift | 111 ++++++++++++------ .../InputHandler_HandleStates.swift | 104 +++------------- .../InputHandler_TriageInput.swift | 20 +--- .../InputHandler_TypingMethod.swift | 80 +++++++++++++ 6 files changed, 193 insertions(+), 170 deletions(-) create mode 100644 Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_TypingMethod.swift diff --git a/Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_Core.swift b/Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_Core.swift index 6772897e..0852f1b2 100644 --- a/Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_Core.swift +++ b/Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_Core.swift @@ -22,7 +22,6 @@ public protocol InputHandlerProtocol { var currentLM: vChewingLM.LMInstantiator { get set } var currentUOM: vChewingLM.LMUserOverride { get set } var delegate: InputHandlerDelegate? { get set } - var composer: Tekkon.Composer { get set } var keySeparator: String { get } static var keySeparator: String { get } var isCompositorEmpty: Bool { get } @@ -89,13 +88,17 @@ public class InputHandler: InputHandlerProtocol { /// 用來記錄「叫出選字窗前」的游標位置的變數。 var backupCursor: Int? + /// 當前的打字模式。 + var currentTypingMethod: TypingMethod = .vChewingFactory /// 半衰模組的衰減指數 let kEpsilon: Double = 0.000_001 - public var calligrapher = "" // 磁帶專用組筆區 - public var composer: Tekkon.Composer = .init() // 注拼槽 - public var compositor: Megrez.Compositor // 組字器 + var strCodePointBuffer = "" // 內碼輸入專用組碼區 + var calligrapher = "" // 磁帶專用組筆區 + var composer: Tekkon.Composer = .init() // 注拼槽 + var compositor: Megrez.Compositor // 組字器 + public var currentUOM: vChewingLM.LMUserOverride public var currentLM: vChewingLM.LMInstantiator { didSet { @@ -120,43 +123,13 @@ public class InputHandler: InputHandlerProtocol { public func clear() { clearComposerAndCalligrapher() compositor.clear() - isCodePointInputMode = false - isHaninKeyboardSymbolMode = false + currentTypingMethod = .vChewingFactory backupCursor = nil } /// 警告:該參數僅代指組音區/組筆區域與組字區在目前狀態下被視為「空」。 var isConsideredEmptyForNow: Bool { - compositor.isEmpty && isComposerOrCalligrapherEmpty && !isCodePointInputMode && !isHaninKeyboardSymbolMode - } - - // MARK: - Hanin Keyboard Symbol Mode. - - var isHaninKeyboardSymbolMode = false - - static let tooltipHaninKeyboardSymbolMode: String = "\("Hanin Keyboard Symbol Input.".localized)" - - // MARK: - Codepoint Input Buffer. - - var isCodePointInputMode = false { - willSet { - strCodePointBuffer.removeAll() - } - } - - var strCodePointBuffer = "" - - var tooltipCodePointInputMode: String { - let commonTerm = NSMutableString() - commonTerm.insert("Code Point Input.".localized, at: 0) - if !(delegate?.isVerticalTyping ?? false) { - switch IMEApp.currentInputMode { - case .imeModeCHS: commonTerm.insert("[GB] ", at: 0) - case .imeModeCHT: commonTerm.insert("[Big5] ", at: 0) - default: break - } - } - return commonTerm.description + compositor.isEmpty && isComposerOrCalligrapherEmpty && currentTypingMethod == .vChewingFactory } // MARK: - Functions dealing with Megrez. diff --git a/Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_HandleCandidate.swift b/Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_HandleCandidate.swift index 44e118e9..fdaf93fd 100644 --- a/Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_HandleCandidate.swift +++ b/Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_HandleCandidate.swift @@ -311,7 +311,8 @@ extension InputHandler { if !updated { delegate.callError("66F3477B") } return true case .option where state.type == .ofSymbolTable: - return handleHaninKeyboardSymbolModeToggle() + // 繞過內碼輸入模式,直接進入漢音鍵盤符號模式。 + return revolveTypingMethod(to: .haninKeyboardSymbol) default: break } } diff --git a/Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_HandleComposition.swift b/Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_HandleComposition.swift index ea8169ca..c1a24f34 100644 --- a/Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_HandleComposition.swift +++ b/Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_HandleComposition.swift @@ -17,37 +17,29 @@ extension InputHandler { /// - Parameter input: 輸入訊號。 /// - Returns: 告知 IMK「該按鍵是否已經被輸入法攔截處理」。 func handleComposition(input: InputSignalProtocol) -> Bool? { - guard let delegate = delegate else { return nil } // 不處理任何包含不可列印字元的訊號。 - guard !input.text.isEmpty, input.charCode.isPrintable else { return nil } - if isCodePointInputMode { return handleCodePointComposition(input: input) } - if prefs.cassetteEnabled { - // 準備處理 `%quick` 選字行為。 - var handleQuickCandidate = true - if currentLM.areCassetteCandidateKeysShiftHeld { handleQuickCandidate = input.isShiftHold } - let hasQuickCandidates: Bool = delegate.state.type == .ofInputting && delegate.state.isCandidateContainer - - // 處理 `%symboldef` 選字行為。 - if handleCassetteSymbolTable(input: input) { - return true - } else if hasQuickCandidates, input.text != currentLM.cassetteWildcardKey { - // 處理 `%quick` 選字行為(當且僅當與 `%symboldef` 衝突的情況下)。 - guard !(handleQuickCandidate && handleCandidate(input: input, ignoringModifiers: true)) else { return true } - } else { - // 處理 `%quick` 選字行為。 - guard !(hasQuickCandidates && handleQuickCandidate && handleCandidate(input: input)) else { return true } - } + let hardRequirementMet = !input.text.isEmpty && input.charCode.isPrintable + switch currentTypingMethod { + case .codePoint where hardRequirementMet: + return handleCodePointComposition(input: input) + case .haninKeyboardSymbol where [[], .shift].contains(input.keyModifierFlags): + return handleHaninKeyboardSymbolModeInput(input: input) + case .vChewingFactory where hardRequirementMet && prefs.cassetteEnabled: return handleCassetteComposition(input: input) + case .vChewingFactory where hardRequirementMet && !prefs.cassetteEnabled: + return handlePhonabetComposition(input: input) + default: return nil } - return handlePhonabetComposition(input: input) } +} - // MARK: 注音按鍵輸入處理 (Handle BPMF Keys) +// MARK: - 注音按鍵輸入處理 (Handle BPMF Keys) +private extension InputHandler { /// 用來處理 InputHandler.HandleInput() 當中的與注音输入有關的組字行為。 /// - Parameter input: 輸入訊號。 /// - Returns: 告知 IMK「該按鍵是否已經被輸入法攔截處理」。 - private func handlePhonabetComposition(input: InputSignalProtocol) -> Bool? { + func handlePhonabetComposition(input: InputSignalProtocol) -> Bool? { guard let delegate = delegate else { return nil } var inputText = (input.inputTextIgnoringModifiers ?? input.text) inputText = inputText.lowercased().applyingTransformFW2HW(reverse: false) @@ -241,13 +233,31 @@ extension InputHandler { // MARK: - 磁帶模式的組字支援。 -extension InputHandler { - /// 用來處理 InputHandler.HandleInput() 當中的與磁帶模組有關的組字行為。 +private extension InputHandler { + /// 用來處理 InputHandler.HandleInput() 當中的與磁帶模組有關的組字行為。(前置處理) /// - Parameter input: 輸入訊號。 /// - Returns: 告知 IMK「該按鍵是否已經被輸入法攔截處理」。 - private func handleCassetteComposition(input: InputSignalProtocol) -> Bool? { + func handleCassetteComposition(input: InputSignalProtocol) -> Bool? { guard let delegate = delegate else { return nil } let state = delegate.state + + // 準備處理 `%quick` 選字行為。 + var handleQuickCandidate = true + if currentLM.areCassetteCandidateKeysShiftHeld { handleQuickCandidate = input.isShiftHold } + let hasQuickCandidates: Bool = state.type == .ofInputting && state.isCandidateContainer + + // 處理 `%symboldef` 選字行為。 + if handleCassetteSymbolTable(input: input) { + return true + } else if hasQuickCandidates, input.text != currentLM.cassetteWildcardKey { + // 處理 `%quick` 選字行為(當且僅當與 `%symboldef` 衝突的情況下)。 + guard !(handleQuickCandidate && handleCandidate(input: input, ignoringModifiers: true)) else { return true } + } else { + // 處理 `%quick` 選字行為。 + guard !(hasQuickCandidates && handleQuickCandidate && handleCandidate(input: input)) else { return true } + } + + // 正式處理。 var wildcardKey: String { currentLM.cassetteWildcardKey } // 花牌鍵。 let inputText = input.text let isWildcardKeyInput: Bool = (inputText == wildcardKey && !wildcardKey.isEmpty) @@ -266,7 +276,7 @@ extension InputHandler { calligrapher.count >= currentLM.maxCassetteKeyLength || isLongestPossibleKeyFormed } - prehandling: if !skipStrokeHandling && currentLM.isThisCassetteKeyAllowed(key: inputText) { + prehandling: if !skipStrokeHandling && currentLM.isThisCassetteKeyAllowed(key: inputText) { if calligrapher.isEmpty, isWildcardKeyInput { delegate.callError("3606B9C0") if input.beganWithLetter { @@ -385,16 +395,17 @@ extension InputHandler { // 將「這個按鍵訊號已經被輸入法攔截處理了」的結果藉由 SessionCtl 回報給 IMK。 return true } - return nil } +} - // MARK: 內碼區位輸入處理 (Handle Code Point Input) +// MARK: - 內碼區位輸入處理 (Handle Code Point Input) +private extension InputHandler { /// 用來處理 InputHandler.HandleInput() 當中的與內碼區位輸入有關的組字行為。 /// - Parameter input: 輸入訊號。 /// - Returns: 告知 IMK「該按鍵是否已經被輸入法攔截處理」。 - private func handleCodePointComposition(input: InputSignalProtocol) -> Bool? { + func handleCodePointComposition(input: InputSignalProtocol) -> Bool? { guard !input.isReservedKey else { return nil } guard let delegate = delegate, input.text.count == 1 else { return nil } guard !input.text.compactMap(\.hexDigitValue).isEmpty else { @@ -407,7 +418,7 @@ extension InputHandler { strCodePointBuffer.append(input.text) var updatedState = generateStateOfInputting(guarded: true) updatedState.tooltipDuration = 0 - updatedState.tooltip = tooltipCodePointInputMode + updatedState.tooltip = TypingMethod.codePoint.getTooltip(vertical: delegate.isVerticalTyping) delegate.switchState(updatedState) return true } @@ -427,7 +438,7 @@ extension InputHandler { updatedState.tooltipDuration = 0 updatedState.tooltip = "Invalid Code Point.".localized delegate.switchState(updatedState) - isCodePointInputMode = true + currentTypingMethod = .codePoint return true } // 某些舊版 macOS 會在這裡生成的字元後面插入垃圾字元。這裡只保留起始字元。 @@ -435,14 +446,46 @@ extension InputHandler { delegate.switchState(IMEState.ofCommitting(textToCommit: char)) var updatedState = generateStateOfInputting(guarded: true) updatedState.tooltipDuration = 0 - updatedState.tooltip = tooltipCodePointInputMode + updatedState.tooltip = TypingMethod.codePoint.getTooltip(vertical: delegate.isVerticalTyping) delegate.switchState(updatedState) - isCodePointInputMode = true + currentTypingMethod = .codePoint return true default: delegate.switchState(generateStateOfInputting()) - isCodePointInputMode = true + currentTypingMethod = .codePoint return true } } } + +// MARK: - 處理漢音鍵盤符號輸入狀態(Handle Hanin Keyboard Symbol Inputs) + +private extension InputHandler { + /// 處理漢音鍵盤符號輸入。 + /// - Parameters: + /// - input: 輸入按鍵訊號。 + /// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。 + func handleHaninKeyboardSymbolModeInput(input: InputSignalProtocol) -> Bool { + guard let delegate = delegate, delegate.state.type != .ofDeactivated else { return false } + let charText = input.text.lowercased().applyingTransformFW2HW(reverse: false) + guard CandidateNode.mapHaninKeyboardSymbols.keys.contains(charText) else { + return revolveTypingMethod(to: .vChewingFactory) + } + guard + charText.count == 1, let symbols = CandidateNode.queryHaninKeyboardSymbols(char: charText) + else { + delegate.callError("C1A760C7") + return true + } + // 得在這裡先 commit buffer,不然會導致「在摁 ESC 離開符號選單時會重複輸入上一次的組字區的內容」的不當行為。 + let textToCommit = generateStateOfInputting(sansReading: true).displayedText + delegate.switchState(IMEState.ofCommitting(textToCommit: textToCommit)) + if symbols.members.count == 1 { + delegate.switchState(IMEState.ofCommitting(textToCommit: symbols.members.map(\.name).joined())) + } else { + delegate.switchState(IMEState.ofSymbolTable(node: symbols)) + } + currentTypingMethod = .vChewingFactory // 用完就關掉,但保持選字窗開啟,所以這裡不用呼叫 toggle 函式。 + return true + } +} diff --git a/Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_HandleStates.swift b/Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_HandleStates.swift index f53c8613..818eb540 100644 --- a/Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_HandleStates.swift +++ b/Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_HandleStates.swift @@ -27,17 +27,18 @@ extension InputHandler { if isConsideredEmptyForNow, !guarded { return IMEState.ofAbortion() } restoreBackupCursor() // 只要叫了 Inputting 狀態,就盡可能還原游標備份。 var segHighlightedAt: Int? - let cpInput = isCodePointInputMode && !sansReading + let handleAsCodePointInput = currentTypingMethod == .codePoint && !sansReading /// 「更新內文組字區 (Update the composing buffer)」是指要求客體軟體將組字緩衝區的內容 /// 換成由此處重新生成的原始資料在 IMEStateData 當中生成的 NSAttributeString。 - var displayTextSegments: [String] = cpInput + var displayTextSegments: [String] = handleAsCodePointInput ? [strCodePointBuffer] : compositor.walkedNodes.values - var cursor = cpInput + var cursor = handleAsCodePointInput ? displayTextSegments.joined().count : convertCursorForDisplay(compositor.cursor) let cursorSansReading = cursor - let reading: String = (sansReading || isCodePointInputMode) ? "" : readingForDisplay // 先提出來,減輕運算負擔。 + // 先提出來讀音資料,減輕運算負擔。 + let reading: String = (sansReading || currentTypingMethod == .codePoint) ? "" : readingForDisplay if !reading.isEmpty { var newDisplayTextSegments = [String]() var temporaryNode = "" @@ -361,8 +362,9 @@ extension InputHandler { guard let delegate = delegate else { return false } let state = delegate.state - if isHaninKeyboardSymbolMode { return handleHaninKeyboardSymbolModeToggle() } - if isCodePointInputMode { return handleCodePointInputToggle() } + guard currentTypingMethod == .vChewingFactory else { + return revolveTypingMethod(to: .vChewingFactory) + } guard state.type == .ofInputting else { return false } @@ -470,31 +472,28 @@ extension InputHandler { guard let delegate = delegate else { return false } let state = delegate.state guard state.type == .ofInputting else { - isCodePointInputMode = false + currentTypingMethod = .vChewingFactory return false } - if isCodePointInputMode { + if currentTypingMethod == .codePoint { if !strCodePointBuffer.isEmpty { func refreshState() { var updatedState = generateStateOfInputting(guarded: true) updatedState.tooltipDuration = 0 - updatedState.tooltip = tooltipCodePointInputMode + updatedState.tooltip = delegate.state.tooltip delegate.switchState(updatedState) } strCodePointBuffer = strCodePointBuffer.dropLast(1).description if input.commonKeyModifierFlags == .option { - strCodePointBuffer.removeAll() - refreshState() - isCodePointInputMode = true - return true + return revolveTypingMethod(to: .codePoint) } if !strCodePointBuffer.isEmpty { refreshState() return true } } - return handleCodePointInputToggle() + return revolveTypingMethod(to: .vChewingFactory) } // 引入 macOS 內建注音輸入法的行為,允許用 Shift+BackSpace 解構前一個漢字的讀音。 @@ -575,8 +574,9 @@ extension InputHandler { guard let delegate = delegate else { return false } let state = delegate.state - if isHaninKeyboardSymbolMode { return handleHaninKeyboardSymbolModeToggle() } - if isCodePointInputMode { return handleCodePointInputToggle() } + guard currentTypingMethod == .vChewingFactory else { + return revolveTypingMethod(to: .vChewingFactory) + } guard state.type == .ofInputting else { return false } @@ -684,8 +684,9 @@ extension InputHandler { guard let delegate = delegate else { return false } let state = delegate.state - if isHaninKeyboardSymbolMode { return handleHaninKeyboardSymbolModeToggle() } - if isCodePointInputMode { return handleCodePointInputToggle() } + guard currentTypingMethod == .vChewingFactory else { + return revolveTypingMethod(to: .vChewingFactory) + } guard state.type == .ofInputting else { return false } @@ -920,73 +921,6 @@ extension InputHandler { return true } - // MARK: - 處理內碼區位輸入狀態的啟動過程(CodePoint Input Toggle) - - @discardableResult func handleCodePointInputToggle() -> Bool { - guard let delegate = delegate, delegate.state.type != .ofDeactivated else { return false } - if isCodePointInputMode { - isCodePointInputMode = false - delegate.switchState(IMEState.ofAbortion()) - return true - } - var updatedState = generateStateOfInputting(sansReading: true) - delegate.switchState(IMEState.ofCommitting(textToCommit: updatedState.displayedText)) - updatedState = generateStateOfInputting(guarded: true) - updatedState.tooltipDuration = 0 - updatedState.tooltip = tooltipCodePointInputMode - delegate.switchState(updatedState) - isCodePointInputMode = true - return true - } - - // MARK: - 處理漢音鍵盤符號輸入狀態的啟動過程(Hanin Pallete) - - @discardableResult func handleHaninKeyboardSymbolModeToggle() -> Bool { - guard let delegate = delegate, delegate.state.type != .ofDeactivated else { return false } - if isCodePointInputMode { isCodePointInputMode = false } - if isHaninKeyboardSymbolMode { - isHaninKeyboardSymbolMode = false - delegate.switchState(IMEState.ofAbortion()) - return true - } - var updatedState = generateStateOfInputting(sansReading: true) - delegate.switchState(IMEState.ofCommitting(textToCommit: updatedState.displayedText)) - updatedState = generateStateOfInputting(guarded: true) - updatedState.tooltipDuration = 0 - updatedState.tooltip = Self.tooltipHaninKeyboardSymbolMode - delegate.switchState(updatedState) - isHaninKeyboardSymbolMode = true - return true - } - - /// 處理漢音鍵盤符號輸入。 - /// - Parameters: - /// - input: 輸入按鍵訊號。 - /// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。 - func handleHaninKeyboardSymbolModeInput(input: InputSignalProtocol) -> Bool { - guard let delegate = delegate, delegate.state.type != .ofDeactivated else { return false } - let charText = input.text.lowercased().applyingTransformFW2HW(reverse: false) - guard CandidateNode.mapHaninKeyboardSymbols.keys.contains(charText) else { - return handleHaninKeyboardSymbolModeToggle() - } - guard - charText.count == 1, let symbols = CandidateNode.queryHaninKeyboardSymbols(char: charText) - else { - delegate.callError("C1A760C7") - return true - } - // 得在這裡先 commit buffer,不然會導致「在摁 ESC 離開符號選單時會重複輸入上一次的組字區的內容」的不當行為。 - let textToCommit = generateStateOfInputting(sansReading: true).displayedText - delegate.switchState(IMEState.ofCommitting(textToCommit: textToCommit)) - if symbols.members.count == 1 { - delegate.switchState(IMEState.ofCommitting(textToCommit: symbols.members.map(\.name).joined())) - } else { - delegate.switchState(IMEState.ofSymbolTable(node: symbols)) - } - isHaninKeyboardSymbolMode = false // 用完就關掉,但保持選字窗開啟,所以這裡不用呼叫 toggle 函式。 - return true - } - // MARK: - 處理符號選單(Symbol Menu Input) /// 處理符號選單。 diff --git a/Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_TriageInput.swift b/Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_TriageInput.swift index e6831b1a..26d8da14 100644 --- a/Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_TriageInput.swift +++ b/Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_TriageInput.swift @@ -48,7 +48,7 @@ public extension InputHandler { case .kCarriageReturn, .kLineFeed: let frontNode = compositor.walkedNodes.last return handleEnter(input: input) { - guard !self.isHaninKeyboardSymbolMode, !self.isCodePointInputMode else { return [] } + guard self.currentTypingMethod == .vChewingFactory else { return [] } guard let frontNode = frontNode else { return [] } let pair = Megrez.KeyValuePaired(keyArray: frontNode.keyArray, value: frontNode.value) let associates = self.generateArrayOfAssociates(withPair: pair) @@ -62,13 +62,7 @@ public extension InputHandler { case [.option, .shift]: return handlePunctuationList(alternative: true, isJIS: isJIS) case .option: - switch (isCodePointInputMode, isHaninKeyboardSymbolMode) { - case (false, false): return handleCodePointInputToggle() - case (true, false), (false, true): - return handleHaninKeyboardSymbolModeToggle() - default: break - } - return true + return revolveTypingMethod() default: break } case .kSpace: @@ -85,7 +79,7 @@ public extension InputHandler { if input.isShiftHold, !input.isControlHold, !input.isOptionHold { return revolveCandidate(reverseOrder: input.isCommandHold) } - if isCodePointInputMode { + if currentTypingMethod == .codePoint { delegate.callError("FDD88EDB") delegate.switchState(IMEState.ofAbortion()) return true @@ -148,13 +142,11 @@ public extension InputHandler { guard let x = input.inputTextIgnoringModifiers, "¥\\".contains(x), input.keyModifierFlags.isEmpty else { break haninSymbolInput } - return handleHaninKeyboardSymbolModeToggle() + return revolveTypingMethod(to: .haninKeyboardSymbol) } - // 注音按鍵輸入與漢音鍵盤符號輸入處理。 - if isHaninKeyboardSymbolMode, [[], .shift].contains(input.keyModifierFlags) { - return handleHaninKeyboardSymbolModeInput(input: input) - } else if let compositionHandled = handleComposition(input: input) { + // 注音/磁帶按鍵輸入與漢音鍵盤符號輸入處理。 + if let compositionHandled = handleComposition(input: input) { return compositionHandled } diff --git a/Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_TypingMethod.swift b/Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_TypingMethod.swift new file mode 100644 index 00000000..2b54b687 --- /dev/null +++ b/Packages/vChewing_MainAssembly/Sources/MainAssembly/InputHandler/InputHandler_TypingMethod.swift @@ -0,0 +1,80 @@ +// (c) 2021 and onwards The vChewing Project (MIT-NTL License). +// ==================== +// This code is released under the MIT license (SPDX-License-Identifier: MIT) +// ... with NTL restriction stating that: +// 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 defined in MIT License. + +import Foundation +import SwiftExtension + +// MARK: - Typing Method + +public extension InputHandler { + enum TypingMethod: Int, CaseIterable { + case vChewingFactory // 自動指派: 0 + case codePoint // 自動指派: 1 + case haninKeyboardSymbol // 自動指派: 2 + + mutating func revolveNext() { + var theInt = rawValue + theInt.revolveAsIndex(with: Self.allCases) + guard let nextMethod = TypingMethod(rawValue: theInt) else { return } + self = nextMethod + } + + func getTooltip(vertical: Bool = false) -> String { + switch self { + case .vChewingFactory: return "" + case .codePoint: + let commonTerm = NSMutableString() + commonTerm.insert("Code Point Input.".localized, at: 0) + if !vertical { + switch IMEApp.currentInputMode { + case .imeModeCHS: commonTerm.insert("[GB] ", at: 0) + case .imeModeCHT: commonTerm.insert("[Big5] ", at: 0) + default: break + } + } + return commonTerm.description + case .haninKeyboardSymbol: + return "\("Hanin Keyboard Symbol Input.".localized)" + } + } + } +} + +// MARK: - Handle Rotation Toggles + +public extension InputHandler { + @discardableResult func revolveTypingMethod(to specifiedMethod: TypingMethod? = nil) -> Bool { + guard let delegate = delegate else { return false } + var newMethod = currentTypingMethod + if let specified = specifiedMethod { + newMethod = specified + } else { + newMethod.revolveNext() + } + /// 接下來這行必須這樣 defer 處理, + /// 因為再接下來的 switch newMethod 的過程會影響到 currentTypingMethod 參數。 + defer { + currentTypingMethod = newMethod + } + switch newMethod { + case .vChewingFactory: + delegate.switchState(IMEState.ofAbortion()) + return true + case .codePoint: + strCodePointBuffer.removeAll() + case .haninKeyboardSymbol: break + } + var updatedState = generateStateOfInputting(sansReading: true) + delegate.switchState(IMEState.ofCommitting(textToCommit: updatedState.displayedText)) + updatedState = generateStateOfInputting(guarded: true) + updatedState.tooltipDuration = 0 + updatedState.tooltip = newMethod.getTooltip(vertical: delegate.isVerticalTyping) + delegate.switchState(updatedState) + return true + } +}