From 36a83f25a228bff466e425487b5e361d84ed17bc Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Fri, 14 Oct 2022 12:03:14 +0800 Subject: [PATCH] InputHandler // Remove stateCallback(). --- Packages/vChewing_Tekkon/README.md | 7 +- Source/Modules/InputHandler_Core.swift | 2 +- .../InputHandler_HandleCandidate.swift | 26 +-- .../InputHandler_HandleComposition.swift | 24 +- Source/Modules/InputHandler_HandleEvent.swift | 18 +- Source/Modules/InputHandler_HandleInput.swift | 109 +++++---- Source/Modules/InputHandler_States.swift | 208 ++++++++---------- Source/Modules/SessionCtl_Core.swift | 12 +- Source/Modules/SessionCtl_Delegates.swift | 24 +- Source/Modules/SessionCtl_HandleEvent.swift | 2 +- Source/Modules/SessionCtl_HandleStates.swift | 22 +- .../SessionCtl_IMKCandidatesData.swift | 2 +- 12 files changed, 223 insertions(+), 233 deletions(-) diff --git a/Packages/vChewing_Tekkon/README.md b/Packages/vChewing_Tekkon/README.md index e7a073e0..248a8a79 100644 --- a/Packages/vChewing_Tekkon/README.md +++ b/Packages/vChewing_Tekkon/README.md @@ -174,7 +174,6 @@ extension InputHandler { func handle( input: InputHandler, state: InputState, - stateCallback: @escaping (InputState) -> Void, errorCallback: @escaping (String) -> Void ) -> Bool { let charCode: UniChar = input.charCode @@ -217,7 +216,7 @@ if !skipPhoneticHandling && _composer.inputValidityCheck(key: charCode) { // 有調號的話,則不需要這樣處理,轉而繼續在此之後的處理。 let composeReading = _composer.hasToneMarker() if !composeReading { - stateCallback(generateStateOfInputting()) + delegate.switchState(generateStateOfInputting()) return true } } @@ -246,7 +245,7 @@ if composeReading { // 符合按鍵組合條件 errorCallback("114514") // 向狀態管理引擎回呼一個錯誤狀態 _composer.clear() // 清空注拼槽的內容 // 根據「天權星引擎 (威注音) 或 Gramambular (小麥) 的組字器是否為空」來判定回呼哪一種狀態 - stateCallback( + delegate.switchState( (getCompositorLength() == 0) ? InputState.EmptyIgnoringPreviousState() : generateStateOfInputting()) return true // 向 IMK 報告說這個按鍵訊號已經被輸入法攔截處理了 } @@ -269,7 +268,7 @@ if composeReading { // 符合按鍵組合條件 // 再以回呼組字狀態的方式來執行updateClientComposingBuffer() let inputting = generateStateOfInputting() inputting.poppedText = poppedText - stateCallback(inputting) + delegate.switchState(inputting) return true // 向 IMK 報告說這個按鍵訊號已經被輸入法攔截處理了 } diff --git a/Source/Modules/InputHandler_Core.swift b/Source/Modules/InputHandler_Core.swift index 2176af29..a3532e41 100644 --- a/Source/Modules/InputHandler_Core.swift +++ b/Source/Modules/InputHandler_Core.swift @@ -37,7 +37,7 @@ public protocol InputHandlerDelegate { var selectionKeys: String { get } var state: IMEStateProtocol { get set } var clientBundleIdentifier: String { get } - func handle(state newState: IMEStateProtocol, replaceCurrent: Bool) + func switchState(_ newState: IMEStateProtocol) func candidateController() -> CtlCandidateProtocol func candidateSelectionCalledByInputHandler(at index: Int) func performUserPhraseOperation(with state: IMEStateProtocol, addToFilter: Bool) diff --git a/Source/Modules/InputHandler_HandleCandidate.swift b/Source/Modules/InputHandler_HandleCandidate.swift index 6ca272a0..d23183c5 100644 --- a/Source/Modules/InputHandler_HandleCandidate.swift +++ b/Source/Modules/InputHandler_HandleCandidate.swift @@ -18,20 +18,20 @@ extension InputHandler { /// - Parameters: /// - input: 輸入訊號。 /// - state: 給定狀態(通常為當前狀態)。 - /// - stateCallback: 狀態回呼,交給對應的型別內的專有函式來處理。 /// - errorCallback: 錯誤回呼。 /// - Returns: 告知 IMK「該按鍵是否已經被輸入法攔截處理」。 func handleCandidate( state: IMEStateProtocol, input: InputSignalProtocol, - stateCallback: @escaping (IMEStateProtocol) -> Void, errorCallback: @escaping (String) -> Void ) -> Bool { - guard var ctlCandidate = delegate?.candidateController() else { + guard let delegate = delegate else { errorCallback("06661F6E") return true } + var ctlCandidate = delegate.candidateController() + // MARK: 取消選字 (Cancel Candidate) let cancelCandidateKey = @@ -47,12 +47,12 @@ extension InputHandler { // 就將當前的組字緩衝區析構處理、強制重設輸入狀態。 // 否則,一個本不該出現的真空組字緩衝區會使前後方向鍵與 BackSpace 鍵失靈。 // 所以這裡需要對 compositor.isEmpty 做判定。 - stateCallback(IMEState.ofAbortion()) + delegate.switchState(IMEState.ofAbortion()) } else { - stateCallback(generateStateOfInputting()) + delegate.switchState(generateStateOfInputting()) } if state.type == .ofSymbolTable, let nodePrevious = state.node.previous, !nodePrevious.members.isEmpty { - stateCallback(IMEState.ofSymbolTable(node: nodePrevious)) + delegate.switchState(IMEState.ofSymbolTable(node: nodePrevious)) } return true } @@ -61,10 +61,10 @@ extension InputHandler { if input.isEnter { if state.type == .ofAssociates, !prefs.alsoConfirmAssociatedCandidatesByEnter { - stateCallback(IMEState.ofAbortion()) + delegate.switchState(IMEState.ofAbortion()) return true } - delegate?.candidateSelectionCalledByInputHandler(at: ctlCandidate.selectedCandidateIndex) + delegate.candidateSelectionCalledByInputHandler(at: ctlCandidate.selectedCandidateIndex) return true } @@ -232,7 +232,7 @@ extension InputHandler { let match: String = (state.type == .ofAssociates) ? input.inputTextIgnoringModifiers ?? "" : input.text - let selectionKeys = delegate?.selectionKeys ?? PrefMgr.shared.candidateKeys + let selectionKeys = delegate.selectionKeys for j in 0.. Void, errorCallback: @escaping (String) -> Void ) -> Bool? { + guard let delegate = delegate else { return nil } + // MARK: 注音按鍵輸入處理 (Handle BPMF Keys) var keyConsumedByReading = false @@ -65,7 +65,7 @@ extension InputHandler { // 沒有調號的話,只需要 setInlineDisplayWithCursor() 且終止處理(return true)即可。 // 有調號的話,則不需要這樣,而是轉而繼續在此之後的處理。 if !composer.hasToneMarker() { - stateCallback(generateStateOfInputting()) + delegate.switchState(generateStateOfInputting()) return true } } @@ -90,16 +90,16 @@ extension InputHandler { if prefs.keepReadingUponCompositionError { composer.intonation.clear() // 砍掉聲調。 - stateCallback(generateStateOfInputting()) + delegate.switchState(generateStateOfInputting()) return true } composer.clear() // 根據「組字器是否為空」來判定回呼哪一種狀態。 switch compositor.isEmpty { - case false: stateCallback(generateStateOfInputting()) + case false: delegate.switchState(generateStateOfInputting()) case true: - stateCallback(IMEState.ofAbortion()) + delegate.switchState(IMEState.ofAbortion()) } return true // 向 IMK 報告說這個按鍵訊號已經被輸入法攔截處理了。 } @@ -122,27 +122,27 @@ extension InputHandler { // 再以回呼組字狀態的方式來執行 setInlineDisplayWithCursor()。 var inputting = generateStateOfInputting() inputting.textToCommit = textToCommit - stateCallback(inputting) + delegate.switchState(inputting) /// 逐字選字模式的處理。 if prefs.useSCPCTypingMode { let candidateState: IMEStateProtocol = generateStateOfCandidates(state: inputting) switch candidateState.candidates.count { - case 2...: stateCallback(candidateState) + case 2...: delegate.switchState(candidateState) case 1: let firstCandidate = candidateState.candidates.first! // 一定會有,所以強制拆包也無妨。 let reading: String = firstCandidate.0 let text: String = firstCandidate.1 - stateCallback(IMEState.ofCommitting(textToCommit: text)) + delegate.switchState(IMEState.ofCommitting(textToCommit: text)) if !prefs.associatedPhrasesEnabled { - stateCallback(IMEState.ofEmpty()) + delegate.switchState(IMEState.ofEmpty()) } else { let associatedPhrases = generateStateOfAssociates( withPair: .init(key: reading, value: text) ) - stateCallback(associatedPhrases.candidates.isEmpty ? IMEState.ofEmpty() : associatedPhrases) + delegate.switchState(associatedPhrases.candidates.isEmpty ? IMEState.ofEmpty() : associatedPhrases) } default: break } @@ -154,7 +154,7 @@ extension InputHandler { /// 是說此時注拼槽並非為空、卻還沒組音。這種情況下只可能是「注拼槽內只有聲調」。 if keyConsumedByReading { // 以回呼組字狀態的方式來執行 setInlineDisplayWithCursor()。 - stateCallback(generateStateOfInputting()) + delegate.switchState(generateStateOfInputting()) return true } return nil diff --git a/Source/Modules/InputHandler_HandleEvent.swift b/Source/Modules/InputHandler_HandleEvent.swift index 96aabbd6..f06df3c0 100644 --- a/Source/Modules/InputHandler_HandleEvent.swift +++ b/Source/Modules/InputHandler_HandleEvent.swift @@ -18,22 +18,18 @@ extension InputHandler { /// - Parameter event: 由 IMK 選字窗接收的裝置操作輸入事件。 /// - Returns: 回「`true`」以將該案件已攔截處理的訊息傳遞給 IMK;回「`false`」則放行、不作處理。 public func handleEvent(_ event: NSEvent) -> Bool { - imkCandidatesEventPreHandler(event: event) ?? commonEventHandler(event) + imkCandidatesEventPreHandler(event: event) ?? doHandleInput(event) } /// 將按鍵行為與當前輸入法狀態結合起來、交給按鍵調度模組來處理。 /// 再根據返回的 result bool 數值來告知 IMK「這個按鍵事件是被處理了還是被放行了」。 /// 這裡不用 handleCandidate() 是因為需要針對聯想詞輸入狀態做額外處理。 - private func commonEventHandler(_ event: NSEvent) -> Bool { + private func doHandleInput(_ event: NSEvent) -> Bool { guard let delegate = delegate else { return false } - - let result = handleInput(event: event, state: delegate.state) { newState in - delegate.handle(state: newState, replaceCurrent: true) - } errorCallback: { errorString in + return handleInput(event: event, state: delegate.state) { errorString in vCLog(errorString) IMEApp.buzz() } - return result } /// 專門處理與 IMK 選字窗有關的判斷語句。 @@ -84,7 +80,7 @@ extension InputHandler { let eventArray = [event] guard let imkC = delegate.candidateController() as? CtlCandidateIMK else { return false } if event.isEsc || event.isBackSpace || event.isDelete || (event.isShiftHold && !event.isSpace) { - return commonEventHandler(event) + return doHandleInput(event) } else if event.isSymbolMenuPhysicalKey { // 符號鍵的行為是固定的,不受偏好設定影響。 switch imkC.currentLayout { @@ -113,7 +109,7 @@ extension InputHandler { if let newEvent = newEvent { if prefs.useSCPCTypingMode, delegate.state.type == .ofAssociates { // 註:input.isShiftHold 已經在 Self.handle() 內處理,因為在那邊處理才有效。 - return event.isShiftHold ? true : commonEventHandler(event) + return event.isShiftHold ? true : doHandleInput(event) } else { if #available(macOS 10.14, *) { imkC.handleKeyboardEvent(newEvent) @@ -126,7 +122,7 @@ extension InputHandler { } if prefs.useSCPCTypingMode, !event.isReservedKey { - return commonEventHandler(event) + return doHandleInput(event) } if delegate.state.type == .ofAssociates, @@ -134,7 +130,7 @@ extension InputHandler { !event.isCursorClockLeft, !event.isCursorClockRight, !event.isSpace, !event.isEnter || !prefs.alsoConfirmAssociatedCandidatesByEnter { - return commonEventHandler(event) + return doHandleInput(event) } imkC.interpretKeyEvents(eventArray) return true diff --git a/Source/Modules/InputHandler_HandleInput.swift b/Source/Modules/InputHandler_HandleInput.swift index 620ebe95..48c78f5e 100644 --- a/Source/Modules/InputHandler_HandleInput.swift +++ b/Source/Modules/InputHandler_HandleInput.swift @@ -21,17 +21,17 @@ extension InputHandler { /// - Parameters: /// - input: 輸入訊號。 /// - state: 給定狀態(通常為當前狀態)。 - /// - stateCallback: 狀態回呼,交給對應的型別內的專有函式來處理。 /// - errorCallback: 錯誤回呼。 /// - Returns: 告知 IMK「該按鍵是否已經被輸入法攔截處理」。 func handleInput( event input: InputSignalProtocol, state: IMEStateProtocol, - stateCallback: @escaping (IMEStateProtocol) -> Void, errorCallback: @escaping (String) -> Void ) -> Bool { // 如果按鍵訊號內的 inputTest 是空的話,則忽略該按鍵輸入,因為很可能是功能修飾鍵。 - guard !input.text.isEmpty, input.charCode.isPrintable else { return false } + // 不處理任何包含不可列印字元的訊號。 + // delegate 必須存在,否則不處理。 + guard !input.text.isEmpty, input.charCode.isPrintable, let delegate = delegate else { return false } let inputText: String = input.text var state = state // 常數轉變數。 @@ -44,7 +44,7 @@ extension InputHandler { return false } errorCallback("550BCF7B: InputHandler just refused an invalid input.") - stateCallback(state) + delegate.switchState(state) return true } @@ -66,7 +66,7 @@ extension InputHandler { // 略過對 BackSpace 的處理。 } else if input.isCapsLockOn || state.isASCIIMode { // 但願能夠處理這種情況下所有可能的按鍵組合。 - stateCallback(IMEState.ofEmpty()) + delegate.switchState(IMEState.ofEmpty()) // 字母鍵摁 Shift 的話,無須額外處理,因為直接就會敲出大寫字母。 if (input.isUpperCaseASCIILetterKey && state.isASCIIMode) @@ -82,8 +82,8 @@ extension InputHandler { } // 將整個組字區的內容遞交給客體應用。 - stateCallback(IMEState.ofCommitting(textToCommit: inputText.lowercased())) - stateCallback(IMEState.ofEmpty()) + delegate.switchState(IMEState.ofCommitting(textToCommit: inputText.lowercased())) + delegate.switchState(IMEState.ofEmpty()) return true } @@ -97,9 +97,9 @@ extension InputHandler { if !(state.type == .ofCandidates || state.type == .ofAssociates || state.type == .ofSymbolTable) { - stateCallback(IMEState.ofEmpty()) - stateCallback(IMEState.ofCommitting(textToCommit: inputText.lowercased())) - stateCallback(IMEState.ofEmpty()) + delegate.switchState(IMEState.ofEmpty()) + delegate.switchState(IMEState.ofCommitting(textToCommit: inputText.lowercased())) + delegate.switchState(IMEState.ofEmpty()) return true } } @@ -108,7 +108,7 @@ extension InputHandler { if [.ofCandidates, .ofSymbolTable].contains(state.type) { return handleCandidate( - state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback + state: state, input: input, errorCallback: errorCallback ) } @@ -116,11 +116,11 @@ extension InputHandler { if state.type == .ofAssociates { if handleCandidate( - state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback + state: state, input: input, errorCallback: errorCallback ) { return true } else { - stateCallback(IMEState.ofEmpty()) + delegate.switchState(IMEState.ofEmpty()) } } @@ -128,19 +128,19 @@ extension InputHandler { if state.type == .ofMarking { if handleMarkingState( - state, input: input, stateCallback: stateCallback, + state, input: input, errorCallback: errorCallback ) { return true } state = state.convertedToInputting - stateCallback(state) + delegate.switchState(state) } // MARK: 注音按鍵輸入處理 (Handle BPMF Keys) if let compositionHandled = handleComposition( - input: input, stateCallback: stateCallback, errorCallback: errorCallback + input: input, errorCallback: errorCallback ) { return compositionHandled } @@ -157,10 +157,10 @@ extension InputHandler { if compositor.cursor >= compositor.length { let displayedText = state.displayedText if !displayedText.isEmpty { - stateCallback(IMEState.ofCommitting(textToCommit: displayedText)) + delegate.switchState(IMEState.ofCommitting(textToCommit: displayedText)) } - stateCallback(IMEState.ofCommitting(textToCommit: " ")) - stateCallback(IMEState.ofEmpty()) + delegate.switchState(IMEState.ofCommitting(textToCommit: " ")) + delegate.switchState(IMEState.ofEmpty()) } else if currentLM.hasUnigramsFor(key: " ") { compositor.insertKey(" ") walk() @@ -168,12 +168,12 @@ extension InputHandler { let textToCommit = commitOverflownComposition var inputting = generateStateOfInputting() inputting.textToCommit = textToCommit - stateCallback(inputting) + delegate.switchState(inputting) } return true } else if input.isShiftHold { // 臉書等網站會攔截 Tab 鍵,所以用 Shift+Command+Space 對候選字詞做正向/反向輪替。 return handleInlineCandidateRotation( - state: state, reverseModifier: input.isCommandHold, stateCallback: stateCallback, + state: state, reverseModifier: input.isCommandHold, errorCallback: errorCallback ) } @@ -182,7 +182,7 @@ extension InputHandler { if candidateState.candidates.isEmpty { errorCallback("3572F238") } else { - stateCallback(candidateState) + delegate.switchState(candidateState) } return true } @@ -191,56 +191,56 @@ extension InputHandler { if let keyCodeType = KeyCode(rawValue: input.keyCode) { switch keyCodeType { - case .kEscape: return handleEsc(state: state, stateCallback: stateCallback) + case .kEscape: return handleEsc(state: state) case .kTab: return handleInlineCandidateRotation( - state: state, reverseModifier: input.isShiftHold, stateCallback: stateCallback, errorCallback: errorCallback + state: state, reverseModifier: input.isShiftHold, errorCallback: errorCallback ) case .kUpArrow, .kDownArrow, .kLeftArrow, .kRightArrow: if (input.isControlHold || input.isShiftHold) && (input.isOptionHold) { if input.isLeft { // Ctrl+PgLf / Shift+PgLf - return handleHome(state: state, stateCallback: stateCallback, errorCallback: errorCallback) + return handleHome(state: state, errorCallback: errorCallback) } else if input.isRight { // Ctrl+PgRt or Shift+PgRt - return handleEnd(state: state, stateCallback: stateCallback, errorCallback: errorCallback) + return handleEnd(state: state, errorCallback: errorCallback) } } if input.isCursorBackward { // Forward return handleBackward( - state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback + state: state, input: input, errorCallback: errorCallback ) } if input.isCursorForward { // Backward return handleForward( - state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback + state: state, input: input, errorCallback: errorCallback ) } if input.isCursorClockLeft || input.isCursorClockRight { // Clock keys if input.isOptionHold, state.type == .ofInputting { if input.isCursorClockRight { return handleInlineCandidateRotation( - state: state, reverseModifier: false, stateCallback: stateCallback, errorCallback: errorCallback + state: state, reverseModifier: false, errorCallback: errorCallback ) } if input.isCursorClockLeft { return handleInlineCandidateRotation( - state: state, reverseModifier: true, stateCallback: stateCallback, errorCallback: errorCallback + state: state, reverseModifier: true, errorCallback: errorCallback ) } } - return handleClockKey(state: state, stateCallback: stateCallback, errorCallback: errorCallback) + return handleClockKey(state: state, errorCallback: errorCallback) } - case .kHome: return handleHome(state: state, stateCallback: stateCallback, errorCallback: errorCallback) - case .kEnd: return handleEnd(state: state, stateCallback: stateCallback, errorCallback: errorCallback) + case .kHome: return handleHome(state: state, errorCallback: errorCallback) + case .kEnd: return handleEnd(state: state, errorCallback: errorCallback) case .kBackSpace: - return handleBackSpace(state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback) + return handleBackSpace(state: state, input: input, errorCallback: errorCallback) case .kWindowsDelete: - return handleDelete(state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback) + return handleDelete(state: state, input: input, errorCallback: errorCallback) case .kCarriageReturn, .kLineFeed: return (input.isCommandHold && input.isControlHold) ? (input.isOptionHold - ? handleCtrlOptionCommandEnter(state: state, stateCallback: stateCallback) - : handleCtrlCommandEnter(state: state, stateCallback: stateCallback)) - : handleEnter(state: state, stateCallback: stateCallback) + ? handleCtrlOptionCommandEnter(state: state) + : handleCtrlCommandEnter(state: state)) + : handleEnter(state: state) default: break } } @@ -257,12 +257,12 @@ extension InputHandler { let textToCommit = commitOverflownComposition var inputting = generateStateOfInputting() inputting.textToCommit = textToCommit - stateCallback(inputting) + delegate.switchState(inputting) let candidateState = generateStateOfCandidates(state: inputting) if candidateState.candidates.isEmpty { errorCallback("B5127D8A") } else { - stateCallback(candidateState) + delegate.switchState(candidateState) } } else { // 不要在注音沒敲完整的情況下叫出統合符號選單。 errorCallback("17446655") @@ -273,8 +273,8 @@ extension InputHandler { // 得在這裡先 commit buffer,不然會導致「在摁 ESC 離開符號選單時會重複輸入上一次的組字區的內容」的不當行為。 // 於是這裡用「模擬一次 Enter 鍵的操作」使其代為執行這個 commit buffer 的動作。 // 這裡不需要該函式所傳回的 bool 結果,所以用「_ =」解消掉。 - _ = handleEnter(state: state, stateCallback: stateCallback) - stateCallback(IMEState.ofSymbolTable(node: CandidateNode.root)) + _ = handleEnter(state: state) + delegate.switchState(IMEState.ofSymbolTable(node: CandidateNode.root)) return true } } @@ -286,10 +286,10 @@ extension InputHandler { guard let stringRAW = input.mainAreaNumKeyChar else { return false } let newStringFW = stringRAW.applyingTransform(.fullwidthToHalfwidth, reverse: true) ?? stringRAW let newStringHW = stringRAW.applyingTransform(.fullwidthToHalfwidth, reverse: false) ?? stringRAW - stateCallback( + delegate.switchState( IMEState.ofCommitting(textToCommit: prefs.halfWidthPunctuationEnabled ? newStringHW : newStringFW) ) - stateCallback(IMEState.ofEmpty()) + delegate.switchState(IMEState.ofEmpty()) return true } } @@ -307,7 +307,6 @@ extension InputHandler { if handlePunctuation( customPunctuation, state: state, - stateCallback: stateCallback, errorCallback: errorCallback ) { return true @@ -321,7 +320,6 @@ extension InputHandler { if handlePunctuation( punctuation, state: state, - stateCallback: stateCallback, errorCallback: errorCallback ) { return true @@ -332,8 +330,8 @@ extension InputHandler { /// 該功能僅可在當前組字區沒有任何內容的時候使用。 if state.type == .ofEmpty { if input.isSpace, !input.isOptionHold, !input.isControlHold, !input.isCommandHold { - stateCallback(IMEState.ofCommitting(textToCommit: input.isShiftHold ? " " : " ")) - stateCallback(IMEState.ofEmpty()) + delegate.switchState(IMEState.ofCommitting(textToCommit: input.isShiftHold ? " " : " ")) + delegate.switchState(IMEState.ofEmpty()) return true } } @@ -344,22 +342,21 @@ extension InputHandler { if input.isShiftHold { // 這裡先不要判斷 isOptionHold。 switch prefs.upperCaseLetterKeyBehavior { case 1: - stateCallback(IMEState.ofEmpty()) - stateCallback(IMEState.ofCommitting(textToCommit: inputText.lowercased())) - stateCallback(IMEState.ofEmpty()) + delegate.switchState(IMEState.ofEmpty()) + delegate.switchState(IMEState.ofCommitting(textToCommit: inputText.lowercased())) + delegate.switchState(IMEState.ofEmpty()) return true case 2: - stateCallback(IMEState.ofEmpty()) - stateCallback(IMEState.ofCommitting(textToCommit: inputText.uppercased())) - stateCallback(IMEState.ofEmpty()) + delegate.switchState(IMEState.ofEmpty()) + delegate.switchState(IMEState.ofCommitting(textToCommit: inputText.uppercased())) + delegate.switchState(IMEState.ofEmpty()) return true default: // 包括 case 0,直接塞給組字區。 let letter = "_letter_\(inputText)" if handlePunctuation( letter, state: state, - stateCallback: stateCallback, errorCallback: errorCallback ) { return true @@ -378,7 +375,7 @@ extension InputHandler { errorCallback( "Blocked data: charCode: \(input.charCode), keyCode: \(input.keyCode)") errorCallback("A9BFF20E") - stateCallback(state) + delegate.switchState(state) return true } diff --git a/Source/Modules/InputHandler_States.swift b/Source/Modules/InputHandler_States.swift index d1030132..c86ea1d6 100644 --- a/Source/Modules/InputHandler_States.swift +++ b/Source/Modules/InputHandler_States.swift @@ -123,17 +123,17 @@ extension InputHandler { /// - Parameters: /// - state: 當前狀態。 /// - input: 輸入按鍵訊號。 - /// - stateCallback: 狀態回呼。 /// - errorCallback: 錯誤回呼。 /// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。 func handleMarkingState( _ state: IMEStateProtocol, input: InputSignalProtocol, - stateCallback: @escaping (IMEStateProtocol) -> Void, errorCallback: @escaping (String) -> Void ) -> Bool { + guard let delegate = delegate else { return false } + if input.isEsc { - stateCallback(generateStateOfInputting()) + delegate.switchState(generateStateOfInputting()) return true } @@ -145,38 +145,34 @@ extension InputHandler { // Enter if input.isEnter { - if let sessionCtl = delegate { - // 先判斷是否是在摁了降權組合鍵的時候目標不在庫。 - if input.isShiftHold, input.isCommandHold, !state.isFilterable { - errorCallback("2EAC1F7A") - return true - } - if !state.isMarkedLengthValid { - errorCallback("9AAFAC00") - return true - } - if !sessionCtl.performUserPhraseOperation(with: state, addToFilter: false) { - errorCallback("5B69CC8D") - return true - } + // 先判斷是否是在摁了降權組合鍵的時候目標不在庫。 + if input.isShiftHold, input.isCommandHold, !state.isFilterable { + errorCallback("2EAC1F7A") + return true } - stateCallback(generateStateOfInputting()) + if !state.isMarkedLengthValid { + errorCallback("9AAFAC00") + return true + } + if !delegate.performUserPhraseOperation(with: state, addToFilter: false) { + errorCallback("5B69CC8D") + return true + } + delegate.switchState(generateStateOfInputting()) return true } // BackSpace & Delete if input.isBackSpace || input.isDelete { - if let inputHandlerDelegate = delegate { - if !state.isFilterable { - errorCallback("1F88B191") - return true - } - if !inputHandlerDelegate.performUserPhraseOperation(with: state, addToFilter: true) { - errorCallback("68D3C6C8") - return true - } + if !state.isFilterable { + errorCallback("1F88B191") + return true } - stateCallback(generateStateOfInputting()) + if !delegate.performUserPhraseOperation(with: state, addToFilter: true) { + errorCallback("68D3C6C8") + return true + } + delegate.switchState(generateStateOfInputting()) return true } @@ -194,10 +190,10 @@ extension InputHandler { marker: convertCursorForDisplay(compositor.marker) ) marking.tooltipBackupForInputting = state.tooltipBackupForInputting - stateCallback(marking.markedRange.isEmpty ? marking.convertedToInputting : marking) + delegate.switchState(marking.markedRange.isEmpty ? marking.convertedToInputting : marking) } else { errorCallback("1149908D") - stateCallback(state) + delegate.switchState(state) } return true } @@ -216,10 +212,10 @@ extension InputHandler { marker: convertCursorForDisplay(compositor.marker) ) marking.tooltipBackupForInputting = state.tooltipBackupForInputting - stateCallback(marking.markedRange.isEmpty ? marking.convertedToInputting : marking) + delegate.switchState(marking.markedRange.isEmpty ? marking.convertedToInputting : marking) } else { errorCallback("9B51408D") - stateCallback(state) + delegate.switchState(state) } return true } @@ -231,15 +227,15 @@ extension InputHandler { /// 標點輸入的處理。 /// - Parameters: /// - customPunctuation: 自訂標點索引鍵頭。 - /// - stateCallback: 狀態回呼。 /// - errorCallback: 錯誤回呼。 /// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。 func handlePunctuation( _ customPunctuation: String, state: IMEStateProtocol, - stateCallback: @escaping (IMEStateProtocol) -> Void, errorCallback: @escaping (String) -> Void ) -> Bool { + guard let delegate = delegate else { return false } + if !currentLM.hasUnigramsFor(key: customPunctuation) { return false } @@ -247,7 +243,7 @@ extension InputHandler { guard composer.isEmpty else { // 注音沒敲完的情況下,無視標點輸入。 errorCallback("A9B69908D") - stateCallback(state) + delegate.switchState(state) return true } @@ -257,21 +253,21 @@ extension InputHandler { let textToCommit = commitOverflownComposition var inputting = generateStateOfInputting() inputting.textToCommit = textToCommit - stateCallback(inputting) + delegate.switchState(inputting) // 從這一行之後開始,就是針對逐字選字模式的單獨處理。 guard prefs.useSCPCTypingMode, composer.isEmpty else { return true } let candidateState = generateStateOfCandidates(state: inputting) switch candidateState.candidates.count { - case 2...: stateCallback(candidateState) + case 2...: delegate.switchState(candidateState) case 1: clear() // 這句不要砍,因為下文可能會回呼 candidateState。 if let candidateToCommit: (String, String) = candidateState.candidates.first, !candidateToCommit.1.isEmpty { - stateCallback(IMEState.ofCommitting(textToCommit: candidateToCommit.1)) - stateCallback(IMEState.ofEmpty()) + delegate.switchState(IMEState.ofCommitting(textToCommit: candidateToCommit.1)) + delegate.switchState(IMEState.ofEmpty()) } else { - stateCallback(candidateState) + delegate.switchState(candidateState) } default: errorCallback("8DA4096E") } @@ -283,16 +279,15 @@ extension InputHandler { /// Enter 鍵的處理。 /// - Parameters: /// - state: 當前狀態。 - /// - stateCallback: 狀態回呼。 /// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。 func handleEnter( - state: IMEStateProtocol, - stateCallback: @escaping (IMEStateProtocol) -> Void + state: IMEStateProtocol ) -> Bool { + guard let delegate = delegate else { return false } guard state.type == .ofInputting else { return false } - stateCallback(IMEState.ofCommitting(textToCommit: state.displayedText)) - stateCallback(IMEState.ofEmpty()) + delegate.switchState(IMEState.ofCommitting(textToCommit: state.displayedText)) + delegate.switchState(IMEState.ofEmpty()) return true } @@ -301,12 +296,11 @@ extension InputHandler { /// Command+Enter 鍵的處理(注音文)。 /// - Parameters: /// - state: 當前狀態。 - /// - stateCallback: 狀態回呼。 /// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。 func handleCtrlCommandEnter( - state: IMEStateProtocol, - stateCallback: @escaping (IMEStateProtocol) -> Void + state: IMEStateProtocol ) -> Bool { + guard let delegate = delegate else { return false } guard state.type == .ofInputting else { return false } var displayedText = compositor.keys.joined(separator: "-") @@ -315,12 +309,12 @@ extension InputHandler { displayedText = Tekkon.cnvPhonaToHanyuPinyin(target: displayedText) // 注音轉拼音 } - if let delegate = delegate, !delegate.clientBundleIdentifier.contains("vChewingPhraseEditor") { + if !delegate.clientBundleIdentifier.contains("vChewingPhraseEditor") { displayedText = displayedText.replacingOccurrences(of: "-", with: " ") } - stateCallback(IMEState.ofCommitting(textToCommit: displayedText)) - stateCallback(IMEState.ofEmpty()) + delegate.switchState(IMEState.ofCommitting(textToCommit: displayedText)) + delegate.switchState(IMEState.ofEmpty()) return true } @@ -329,12 +323,11 @@ extension InputHandler { /// Command+Option+Enter 鍵的處理(網頁 Ruby 注音文標記)。 /// - Parameters: /// - state: 當前狀態。 - /// - stateCallback: 狀態回呼。 /// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。 func handleCtrlOptionCommandEnter( - state: IMEStateProtocol, - stateCallback: @escaping (IMEStateProtocol) -> Void + state: IMEStateProtocol ) -> Bool { + guard let delegate = delegate else { return false } guard state.type == .ofInputting else { return false } var composed = "" @@ -355,8 +348,8 @@ extension InputHandler { composed += key.contains("_") ? value : "\(value)(\(key))" } - stateCallback(IMEState.ofCommitting(textToCommit: composed)) - stateCallback(IMEState.ofEmpty()) + delegate.switchState(IMEState.ofCommitting(textToCommit: composed)) + delegate.switchState(IMEState.ofEmpty()) return true } @@ -366,15 +359,14 @@ extension InputHandler { /// - Parameters: /// - state: 當前狀態。 /// - input: 輸入按鍵訊號。 - /// - stateCallback: 狀態回呼。 /// - errorCallback: 錯誤回呼。 /// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。 func handleBackSpace( state: IMEStateProtocol, input: InputSignalProtocol, - stateCallback: @escaping (IMEStateProtocol) -> Void, errorCallback: @escaping (String) -> Void ) -> Bool { + guard let delegate = delegate else { return false } guard state.type == .ofInputting else { return false } // 引入 macOS 內建注音輸入法的行為,允許用 Shift+BackSpace 解構前一個漢字的讀音。 @@ -386,16 +378,16 @@ extension InputHandler { compositor.dropKey(direction: .rear) walk() // 這裡必須 Walk 一次、來更新目前被 walk 的內容。 prevReading.1.charComponents.forEach { composer.receiveKey(fromPhonabet: $0) } - stateCallback(generateStateOfInputting()) + delegate.switchState(generateStateOfInputting()) return true case 1: - stateCallback(IMEState.ofAbortion()) + delegate.switchState(IMEState.ofAbortion()) return true default: break } if input.isShiftHold, input.isOptionHold { - stateCallback(IMEState.ofAbortion()) + delegate.switchState(IMEState.ofAbortion()) return true } @@ -407,7 +399,7 @@ extension InputHandler { walk() } else { errorCallback("9D69908D") - stateCallback(state) + delegate.switchState(state) return true } } else { @@ -415,9 +407,9 @@ extension InputHandler { } switch composer.isEmpty && compositor.isEmpty { - case false: stateCallback(generateStateOfInputting()) + case false: delegate.switchState(generateStateOfInputting()) case true: - stateCallback(IMEState.ofAbortion()) + delegate.switchState(IMEState.ofAbortion()) } return true } @@ -428,25 +420,24 @@ extension InputHandler { /// - Parameters: /// - state: 當前狀態。 /// - input: 輸入按鍵訊號。 - /// - stateCallback: 狀態回呼。 /// - errorCallback: 錯誤回呼。 /// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。 func handleDelete( state: IMEStateProtocol, input: InputSignalProtocol, - stateCallback: @escaping (IMEStateProtocol) -> Void, errorCallback: @escaping (String) -> Void ) -> Bool { + guard let delegate = delegate else { return false } guard state.type == .ofInputting else { return false } if input.isShiftHold { - stateCallback(IMEState.ofAbortion()) + delegate.switchState(IMEState.ofAbortion()) return true } if compositor.cursor == compositor.length, composer.isEmpty { errorCallback("9B69938D") - stateCallback(state) + delegate.switchState(state) return true } @@ -460,9 +451,9 @@ extension InputHandler { let inputting = generateStateOfInputting() // 這裡不用「count > 0」,因為該整數變數只要「!isEmpty」那就必定滿足這個條件。 switch inputting.displayedText.isEmpty { - case false: stateCallback(inputting) + case false: delegate.switchState(inputting) case true: - stateCallback(IMEState.ofAbortion()) + delegate.switchState(IMEState.ofAbortion()) } return true } @@ -472,19 +463,18 @@ extension InputHandler { /// 處理與當前文字輸入排版前後方向呈 90 度的那兩個方向鍵的按鍵行為。 /// - Parameters: /// - state: 當前狀態。 - /// - stateCallback: 狀態回呼。 /// - errorCallback: 錯誤回呼。 /// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。 func handleClockKey( state: IMEStateProtocol, - stateCallback: @escaping (IMEStateProtocol) -> Void, errorCallback: @escaping (String) -> Void ) -> Bool { + guard let delegate = delegate else { return false } guard state.type == .ofInputting else { return false } if !composer.isEmpty { errorCallback("9B6F908D") } - stateCallback(state) + delegate.switchState(state) return true } @@ -493,28 +483,27 @@ extension InputHandler { /// 處理 Home 鍵的行為。 /// - Parameters: /// - state: 當前狀態。 - /// - stateCallback: 狀態回呼。 /// - errorCallback: 錯誤回呼。 /// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。 func handleHome( state: IMEStateProtocol, - stateCallback: @escaping (IMEStateProtocol) -> Void, errorCallback: @escaping (String) -> Void ) -> Bool { + guard let delegate = delegate else { return false } guard state.type == .ofInputting else { return false } if !composer.isEmpty { errorCallback("ABC44080") - stateCallback(state) + delegate.switchState(state) return true } if compositor.cursor != 0 { compositor.cursor = 0 - stateCallback(generateStateOfInputting()) + delegate.switchState(generateStateOfInputting()) } else { errorCallback("66D97F90") - stateCallback(state) + delegate.switchState(state) } return true @@ -525,28 +514,27 @@ extension InputHandler { /// 處理 End 鍵的行為。 /// - Parameters: /// - state: 當前狀態。 - /// - stateCallback: 狀態回呼。 /// - errorCallback: 錯誤回呼。 /// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。 func handleEnd( state: IMEStateProtocol, - stateCallback: @escaping (IMEStateProtocol) -> Void, errorCallback: @escaping (String) -> Void ) -> Bool { + guard let delegate = delegate else { return false } guard state.type == .ofInputting else { return false } if !composer.isEmpty { errorCallback("9B69908D") - stateCallback(state) + delegate.switchState(state) return true } if compositor.cursor != compositor.length { compositor.cursor = compositor.length - stateCallback(generateStateOfInputting()) + delegate.switchState(generateStateOfInputting()) } else { errorCallback("9B69908E") - stateCallback(state) + delegate.switchState(state) } return true @@ -557,26 +545,25 @@ extension InputHandler { /// 處理 Esc 鍵的行為。 /// - Parameters: /// - state: 當前狀態。 - /// - stateCallback: 狀態回呼。 /// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。 func handleEsc( - state: IMEStateProtocol, - stateCallback: @escaping (IMEStateProtocol) -> Void + state: IMEStateProtocol ) -> Bool { + guard let delegate = delegate else { return false } guard state.type == .ofInputting else { return false } if prefs.escToCleanInputBuffer { /// 若啟用了該選項,則清空組字器的內容與注拼槽的內容。 /// 此乃 macOS 內建注音輸入法預設之行為,但不太受 Windows 使用者群體之待見。 - stateCallback(IMEState.ofAbortion()) + delegate.switchState(IMEState.ofAbortion()) } else { if composer.isEmpty { return true } /// 如果注拼槽不是空的話,則清空之。 composer.clear() switch compositor.isEmpty { - case false: stateCallback(generateStateOfInputting()) + case false: delegate.switchState(generateStateOfInputting()) case true: - stateCallback(IMEState.ofAbortion()) + delegate.switchState(IMEState.ofAbortion()) } } return true @@ -588,20 +575,19 @@ extension InputHandler { /// - Parameters: /// - state: 當前狀態。 /// - input: 輸入按鍵訊號。 - /// - stateCallback: 狀態回呼。 /// - errorCallback: 錯誤回呼。 /// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。 func handleForward( state: IMEStateProtocol, input: InputSignalProtocol, - stateCallback: @escaping (IMEStateProtocol) -> Void, errorCallback: @escaping (String) -> Void ) -> Bool { + guard let delegate = delegate else { return false } guard state.type == .ofInputting else { return false } if !composer.isEmpty { errorCallback("B3BA5257") - stateCallback(state) + delegate.switchState(state) return true } @@ -619,32 +605,32 @@ extension InputHandler { marker: convertCursorForDisplay(compositor.marker) ) marking.tooltipBackupForInputting = state.tooltip - stateCallback(marking) + delegate.switchState(marking) } else { errorCallback("BB7F6DB9") - stateCallback(state) + delegate.switchState(state) } } else if input.isOptionHold { if input.isControlHold { - return handleEnd(state: state, stateCallback: stateCallback, errorCallback: errorCallback) + return handleEnd(state: state, errorCallback: errorCallback) } // 游標跳轉動作無論怎樣都會執行,但如果出了執行失敗的結果的話則觸發報錯流程。 if !compositor.jumpCursorBySpan(to: .front) { errorCallback("33C3B580") - stateCallback(state) + delegate.switchState(state) return true } - stateCallback(generateStateOfInputting()) + delegate.switchState(generateStateOfInputting()) } else { if compositor.cursor < compositor.length { compositor.cursor += 1 if isCursorCuttingChar() { compositor.jumpCursorBySpan(to: .front) } - stateCallback(generateStateOfInputting()) + delegate.switchState(generateStateOfInputting()) } else { errorCallback("A96AAD58") - stateCallback(state) + delegate.switchState(state) } } @@ -657,20 +643,19 @@ extension InputHandler { /// - Parameters: /// - state: 當前狀態。 /// - input: 輸入按鍵訊號。 - /// - stateCallback: 狀態回呼。 /// - errorCallback: 錯誤回呼。 /// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。 func handleBackward( state: IMEStateProtocol, input: InputSignalProtocol, - stateCallback: @escaping (IMEStateProtocol) -> Void, errorCallback: @escaping (String) -> Void ) -> Bool { + guard let delegate = delegate else { return false } guard state.type == .ofInputting else { return false } if !composer.isEmpty { errorCallback("6ED95318") - stateCallback(state) + delegate.switchState(state) return true } @@ -688,32 +673,32 @@ extension InputHandler { marker: convertCursorForDisplay(compositor.marker) ) marking.tooltipBackupForInputting = state.tooltip - stateCallback(marking) + delegate.switchState(marking) } else { errorCallback("D326DEA3") - stateCallback(state) + delegate.switchState(state) } } else if input.isOptionHold { if input.isControlHold { - return handleHome(state: state, stateCallback: stateCallback, errorCallback: errorCallback) + return handleHome(state: state, errorCallback: errorCallback) } // 游標跳轉動作無論怎樣都會執行,但如果出了執行失敗的結果的話則觸發報錯流程。 if !compositor.jumpCursorBySpan(to: .rear) { errorCallback("8D50DD9E") - stateCallback(state) + delegate.switchState(state) return true } - stateCallback(generateStateOfInputting()) + delegate.switchState(generateStateOfInputting()) } else { if compositor.cursor > 0 { compositor.cursor -= 1 if isCursorCuttingChar() { compositor.jumpCursorBySpan(to: .rear) } - stateCallback(generateStateOfInputting()) + delegate.switchState(generateStateOfInputting()) } else { errorCallback("7045E6F3") - stateCallback(state) + delegate.switchState(state) } } @@ -726,15 +711,14 @@ extension InputHandler { /// - Parameters: /// - state: 當前狀態。 /// - reverseModifier: 是否有控制輪替方向的修飾鍵輸入。 - /// - stateCallback: 狀態回呼。 /// - errorCallback: 錯誤回呼。 /// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。 func handleInlineCandidateRotation( state: IMEStateProtocol, reverseModifier: Bool, - stateCallback: @escaping (IMEStateProtocol) -> Void, errorCallback: @escaping (String) -> Void ) -> Bool { + guard let delegate = delegate else { return false } if composer.isEmpty, compositor.isEmpty || compositor.walkedNodes.isEmpty { return false } guard state.type == .ofInputting else { guard state.type == .ofEmpty else { @@ -814,7 +798,7 @@ extension InputHandler { consolidateNode(candidate: candidates[currentIndex], respectCursorPushing: false, preConsolidate: false) - stateCallback(generateStateOfInputting()) + delegate.switchState(generateStateOfInputting()) return true } } diff --git a/Source/Modules/SessionCtl_Core.swift b/Source/Modules/SessionCtl_Core.swift index d7214f0a..e58a1688 100644 --- a/Source/Modules/SessionCtl_Core.swift +++ b/Source/Modules/SessionCtl_Core.swift @@ -119,7 +119,7 @@ public class SessionCtl: IMKInputController { // ---------------------------- Self.isVerticalTyping = isVerticalTyping // 強制重設當前鍵盤佈局、使其與偏好設定同步。這裡的這一步也不能省略。 - handle(state: IMEState.ofEmpty()) + switchState(IMEState.ofEmpty()) } } } @@ -170,14 +170,14 @@ extension SessionCtl { // 過濾掉尚未完成拼寫的注音。 if state.type == .ofInputting, PrefMgr.shared.trimUnfinishedReadingsOnCommit { inputHandler.composer.clear() - handle(state: inputHandler.generateStateOfInputting()) + switchState(inputHandler.generateStateOfInputting()) } let isSecureMode = PrefMgr.shared.clientsIMKTextInputIncapable.contains(clientBundleIdentifier) if state.hasComposition, !isSecureMode { /// 將傳回的新狀態交給調度函式。 - handle(state: IMEState.ofCommitting(textToCommit: state.displayedText)) + switchState(IMEState.ofCommitting(textToCommit: state.displayedText)) } - handle(state: isSecureMode ? IMEState.ofAbortion() : IMEState.ofEmpty()) + switchState(isSecureMode ? IMEState.ofAbortion() : IMEState.ofEmpty()) } } @@ -208,7 +208,7 @@ extension SessionCtl { UpdateSputnik.shared.checkForUpdate(forced: false, url: kUpdateInfoSourceURL) } - handle(state: IMEState.ofEmpty()) + switchState(IMEState.ofEmpty()) Self.allInstances.insert(self) } @@ -217,7 +217,7 @@ extension SessionCtl { public override func deactivateServer(_ sender: Any!) { _ = sender // 防止格式整理工具毀掉與此對應的參數。 resetInputHandler() // 這條會自動搞定 Empty 狀態。 - handle(state: IMEState.ofDeactivated()) + switchState(IMEState.ofDeactivated()) Self.allInstances.remove(self) } diff --git a/Source/Modules/SessionCtl_Delegates.swift b/Source/Modules/SessionCtl_Delegates.swift index f912950b..dbd06c11 100644 --- a/Source/Modules/SessionCtl_Delegates.swift +++ b/Source/Modules/SessionCtl_Delegates.swift @@ -75,11 +75,11 @@ extension SessionCtl: CtlCandidateDelegate { if state.type == .ofSymbolTable, (0..