diff --git a/Source/Modules/ControllerModules/KeyHandler.h b/Source/Modules/ControllerModules/KeyHandler.h index 69ff592a..28214727 100644 --- a/Source/Modules/ControllerModules/KeyHandler.h +++ b/Source/Modules/ControllerModules/KeyHandler.h @@ -50,7 +50,8 @@ extern InputMode imeModeNULL; - (BOOL)handleInput:(keyParser *)input state:(InputState *)state stateCallback:(void (^)(InputState *))stateCallback - errorCallback:(void (^)(void))errorCallback NS_SWIFT_NAME(handle(input:state:stateCallback:errorCallback:)); + errorCallback:(void (^)(void))errorCallback + NS_SWIFT_NAME(handle(input:state:stateCallback:errorCallback:)); - (void)fixNodeWithValue:(NSString *)value NS_SWIFT_NAME(fixNode(value:)); - (void)clear; @@ -69,11 +70,6 @@ extern InputMode imeModeNULL; stateCallback:(void (^)(InputState *))stateCallback errorCallback:(void (^)(void))errorCallback NS_SWIFT_NAME(handleCandidate(state:input:stateCallback:errorCallback:)); -- (BOOL)_handleMarkingState:(InputState *)state - input:(keyParser *)input - stateCallback:(void (^)(InputState *))stateCallback - errorCallback:(void (^)(void))errorCallback - NS_SWIFT_NAME(handleMarking(state:input:stateCallback:errorCallback:)); - (BOOL)checkWhetherToneMarkerConfirmsPhoneticReadingBuffer; - (BOOL)chkKeyValidity:(UniChar)value; diff --git a/Source/Modules/ControllerModules/KeyHandler.mm b/Source/Modules/ControllerModules/KeyHandler.mm index 8c73f829..02ad9d5f 100644 --- a/Source/Modules/ControllerModules/KeyHandler.mm +++ b/Source/Modules/ControllerModules/KeyHandler.mm @@ -994,94 +994,6 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot"; return YES; } -- (BOOL)_handleMarkingState:(InputStateMarking *)state - input:(keyParser *)input - stateCallback:(void (^)(InputState *))stateCallback - errorCallback:(void (^)(void))errorCallback -{ - - if ([input isESC]) - { - InputStateInputting *inputting = (InputStateInputting *)[self buildInputtingState]; - stateCallback(inputting); - return YES; - } - - // Enter - if ([input isEnter]) - { - if (![self.delegate keyHandler:self didRequestWriteUserPhraseWithState:state]) - { - [IME prtDebugIntel:@"5B69CC8D"]; - errorCallback(); - return YES; - } - InputStateInputting *inputting = (InputStateInputting *)[self buildInputtingState]; - stateCallback(inputting); - return YES; - } - - // Shift + left - if (([input isCursorBackward] || input.emacsKey == vChewingEmacsKeyBackward) && ([input isShiftHold])) - { - NSUInteger index = state.markerIndex; - if (index > 0) - { - index = [state.composingBuffer previousUtf16PositionFor:index]; - InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:state.composingBuffer - cursorIndex:state.cursorIndex - markerIndex:index - readings:state.readings]; - marking.tooltipForInputting = state.tooltipForInputting; - - if (marking.markedRange.length == 0) - { - InputState *inputting = [marking convertToInputting]; - stateCallback(inputting); - } - else - stateCallback(marking); - } - else - { - [IME prtDebugIntel:@"1149908D"]; - errorCallback(); - stateCallback(state); - } - return YES; - } - - // Shift + Right - if (([input isCursorForward] || input.emacsKey == vChewingEmacsKeyForward) && ([input isShiftHold])) - { - NSUInteger index = state.markerIndex; - if (index < state.composingBuffer.length) - { - index = [state.composingBuffer nextUtf16PositionFor:index]; - InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:state.composingBuffer - cursorIndex:state.cursorIndex - markerIndex:index - readings:state.readings]; - marking.tooltipForInputting = state.tooltipForInputting; - if (marking.markedRange.length == 0) - { - InputState *inputting = [marking convertToInputting]; - stateCallback(inputting); - } - else - stateCallback(marking); - } - else - { - [IME prtDebugIntel:@"9B51408D"]; - errorCallback(); - stateCallback(state); - } - return YES; - } - return NO; -} - - (BOOL)_handleCandidateState:(InputState *)state input:(keyParser *)input stateCallback:(void (^)(InputState *))stateCallback diff --git a/Source/Modules/ControllerModules/KeyHandler.swift b/Source/Modules/ControllerModules/KeyHandler.swift index 8f2004c5..40236760 100644 --- a/Source/Modules/ControllerModules/KeyHandler.swift +++ b/Source/Modules/ControllerModules/KeyHandler.swift @@ -1,6 +1,6 @@ -// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License). -// All possible vChewing-specific modifications are of: -// (c) 2021 and onwards The vChewing Project (MIT-NTL License). +// 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 @@ -26,6 +26,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import Cocoa +// MARK: - § Handle Inputs (WIP). @objc extension KeyHandler { func handleInputSwift( input: keyParser, @@ -35,7 +36,6 @@ import Cocoa ) -> Bool { let charCode: UniChar = input.charCode var state = inState // Turn this incoming constant to variable. - let emacsKey: vChewingEmacsKey = input.emacsKey let inputText: String = input.inputText ?? "" let emptyState = InputState.Empty() @@ -54,7 +54,7 @@ import Cocoa return false } - // MARK: - Caps Lock processing. + // MARK: Caps Lock processing. // If Caps Lock is ON, temporarily disable bopomofo. // Note: Alphanumerical mode processing. if input.isBackSpace || input.isEnter || input.isAbsorbedArrowKey || input.isExtraChooseCandidateKey @@ -86,7 +86,7 @@ import Cocoa return true } - // MARK: - Numeric Pad Processing. + // MARK: Numeric Pad Processing. if input.isNumericPad { if !input.isLeft && !input.isRight && !input.isDown && !input.isUp && !input.isSpace && isPrintable(charCode) @@ -100,13 +100,13 @@ import Cocoa } } - // MARK: - Handle Candidates. + // MARK: Handle Candidates. if state is InputState.ChoosingCandidate { return handleCandidate( state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback) } - // MARK: - Handle Associated Phrases. + // MARK: Handle Associated Phrases. if state is InputState.AssociatedPhrases { let result = handleCandidate( state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback) @@ -117,12 +117,12 @@ import Cocoa } } - // MARK: - Handle Marking. + // MARK: Handle Marking. if state is InputState.Marking { let marking = state as! InputState.Marking - if handleMarking( - state: state as! InputState.Marking, input: input, stateCallback: stateCallback, + if _handleMarkingState( + state as! InputState.Marking, input: input, stateCallback: stateCallback, errorCallback: errorCallback) { return true @@ -132,7 +132,7 @@ import Cocoa stateCallback(state) } - // MARK: - Handle BPMF Keys. + // MARK: Handle BPMF Keys. var composeReading: Bool = false let skipPhoneticHandling = input.isReservedKey || input.isControlHold || input.isOptionHold @@ -154,7 +154,7 @@ import Cocoa // See if we have composition if Enter/Space is hit and buffer is not empty. // We use "|=" conditioning so that the tone marker key is also taken into account. - // However, Swift does not support "|=". + // However, Swift does not support "|=". composeReading = composeReading || (!isPhoneticReadingBufferEmpty() && (input.isSpace || input.isEnter)) if composeReading { let reading = getSyllableCompositionFromPhoneticReadingBuffer() @@ -189,7 +189,7 @@ import Cocoa useVerticalMode: input.useVerticalMode) if choosingCandidates.candidates.count == 1 { clear() - let text: String = choosingCandidates.candidates.first ?? "" + let text: String = choosingCandidates.candidates.first ?? "" let committing = InputState.Committing(poppedText: text) stateCallback(committing) @@ -213,7 +213,7 @@ import Cocoa return true } - // MARK: - Calling candidate window using Space or Down or PageUp / PageDn. + // MARK: Calling candidate window using Space or Down or PageUp / PageDn. if isPhoneticReadingBufferEmpty() && (state is InputState.NotEmpty) && (input.isExtraChooseCandidateKey || input.isExtraChooseCandidateKeyReverse || input.isSpace @@ -224,8 +224,8 @@ import Cocoa // If the spacebar is NOT set to be a selection key if input.isShiftHold || !mgrPrefs.chooseCandidateUsingSpace { if getBuilderCursorIndex() >= getBuilderLength() { - let composingBuffer = (state as! InputState.NotEmpty).composingBuffer - if (composingBuffer.count) != 0 { + let composingBuffer = (state as! InputState.NotEmpty).composingBuffer + if (composingBuffer.count) != 0 { let committing = InputState.Committing(poppedText: composingBuffer) stateCallback(committing) } @@ -251,14 +251,14 @@ import Cocoa return true } - // MARK: - Function Keys. + // MARK: Function Keys. // MARK: Esc - // MARK: - Still Nothing. + // MARK: Still Nothing. // Still nothing? Then we update the composing buffer. // Note that some app has strange behavior if we don't do this, - // "thinking" that the key is not actually consumed). + // "thinking" that the key is not actually consumed. // 砍掉這一段會導致「F1-F12 按鍵干擾組字區」的問題。 // 暫時只能先恢復這段,且補上偵錯彙報機制,方便今後排查故障。 if (state is InputState.NotEmpty) || !isPhoneticReadingBufferEmpty() { @@ -273,21 +273,3 @@ import Cocoa return false } } - -// MARK: - State managements. -@objc extension KeyHandler { - func _buildCandidateState( - _ currentState: InputState.NotEmpty, - useVerticalMode: Bool - ) -> InputState.ChoosingCandidate { - let candidatesArray = getCandidatesArray() - - let state = InputState.ChoosingCandidate( - composingBuffer: currentState.composingBuffer, - cursorIndex: currentState.cursorIndex, - candidates: candidatesArray as! [String], - useVerticalMode: useVerticalMode) - return state - } - -} diff --git a/Source/Modules/ControllerModules/KeyHandler_States.swift b/Source/Modules/ControllerModules/KeyHandler_States.swift new file mode 100644 index 00000000..ede71207 --- /dev/null +++ b/Source/Modules/ControllerModules/KeyHandler_States.swift @@ -0,0 +1,130 @@ +// 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. +*/ + +import Cocoa + +// MARK: - § State managements. +@objc extension KeyHandler { + + // MARK: 用以生成候選詞數組 + func _buildCandidateState( + _ currentState: InputState.NotEmpty, + useVerticalMode: Bool + ) -> InputState.ChoosingCandidate { + let candidatesArray = getCandidatesArray() + + let state = InputState.ChoosingCandidate( + composingBuffer: currentState.composingBuffer, + cursorIndex: currentState.cursorIndex, + candidates: candidatesArray as! [String], + useVerticalMode: useVerticalMode) + return state + } + + // MARK: 用以處理就地新增自訂語彙時的行為 + func _handleMarkingState( + _ state: InputState.Marking, + input: keyParser, + stateCallback: @escaping (InputState) -> Void, + errorCallback: @escaping () -> Void + ) -> Bool { + + if input.isESC { + let inputting = buildInputtingState() as! InputState.Inputting + stateCallback(inputting) + return true + } + + // Enter + if input.isEnter { + if let keyHandlerDelegate = delegate { + if !keyHandlerDelegate.keyHandler(self, didRequestWriteUserPhraseWith: state) { + IME.prtDebugIntel("5B69CC8D") + errorCallback() + return true + } + } + + let inputting = buildInputtingState() as! InputState.Inputting + stateCallback(inputting) + return true + } + + // Shift + Left + if (input.isCursorBackward || input.emacsKey == vChewingEmacsKey.backward) && (input.isShiftHold) { + var index = state.markerIndex + if index > 0 { + index = UInt((state.composingBuffer as NSString).previousUtf16Position(for: Int(index))) + let marking = InputState.Marking( + composingBuffer: state.composingBuffer, + cursorIndex: state.cursorIndex, + markerIndex: index, + readings: state.readings) + marking.tooltipForInputting = state.tooltipForInputting + + if marking.markedRange.length == 0 { + let inputting = marking.convertToInputting() + stateCallback(inputting) + } else { + stateCallback(marking) + } + } else { + IME.prtDebugIntel("1149908D") + errorCallback() + stateCallback(state) + } + return true + } + + // Shift + Right + if (input.isCursorForward || input.emacsKey == vChewingEmacsKey.forward) && (input.isShiftHold) { + var index = state.markerIndex + // 這裡繼續用 NSString 是為了與 Zonble 之前引入的 NSStringUtils 相容。 + // 不然的話,這行判斷會失敗、引發「9B51408D」錯誤。 + if index < ((state.composingBuffer as NSString).length) { + index = UInt((state.composingBuffer as NSString).nextUtf16Position(for: Int(index))) + let marking = InputState.Marking( + composingBuffer: state.composingBuffer, + cursorIndex: state.cursorIndex, + markerIndex: index, + readings: state.readings) + marking.tooltipForInputting = state.tooltipForInputting + if marking.markedRange.length == 0 { + let inputting = marking.convertToInputting() + stateCallback(inputting) + } else { + stateCallback(marking) + } + } else { + IME.prtDebugIntel("9B51408D") + errorCallback() + stateCallback(state) + } + return true + } + return false + } +} diff --git a/vChewing.xcodeproj/project.pbxproj b/vChewing.xcodeproj/project.pbxproj index c6b2e3f9..fad18f44 100644 --- a/vChewing.xcodeproj/project.pbxproj +++ b/vChewing.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 5B27AD6B27CB1F9B000ED75B /* data-zhuyinwen.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5B27AD6927CB1F9B000ED75B /* data-zhuyinwen.txt */; }; 5B2DB16F27AF6891006D874E /* data-chs.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5B2DB16D27AF6891006D874E /* data-chs.txt */; }; 5B2DB17027AF6891006D874E /* data-cht.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5B2DB16E27AF6891006D874E /* data-cht.txt */; }; + 5B3133BF280B229700A4A505 /* KeyHandler_States.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B3133BE280B229700A4A505 /* KeyHandler_States.swift */; }; 5B5E535227EF261400C6AA1E /* IME.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B5E535127EF261400C6AA1E /* IME.swift */; }; 5B62A32927AE77D100A19448 /* FSEventStreamHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A32827AE77D100A19448 /* FSEventStreamHelper.swift */; }; 5B62A32F27AE78B000A19448 /* CoreLM.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A32D27AE78B000A19448 /* CoreLM.mm */; }; @@ -186,6 +187,7 @@ 5B2DB16E27AF6891006D874E /* data-cht.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "data-cht.txt"; path = "Data/data-cht.txt"; sourceTree = ""; }; 5B2DB17127AF8771006D874E /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; name = Makefile; path = Data/Makefile; sourceTree = ""; }; 5B30F11227BA568800484E24 /* vChewingKeyLayout.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = vChewingKeyLayout.bundle; sourceTree = ""; }; + 5B3133BE280B229700A4A505 /* KeyHandler_States.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyHandler_States.swift; sourceTree = ""; }; 5B5E535127EF261400C6AA1E /* IME.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IME.swift; sourceTree = ""; }; 5B62A32627AE77BB00A19448 /* LMConsolidator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LMConsolidator.h; sourceTree = ""; }; 5B62A32727AE77BB00A19448 /* LMConsolidator.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = LMConsolidator.mm; sourceTree = ""; }; @@ -426,6 +428,7 @@ D4E569DA27A34CC100AC2CEF /* KeyHandler.h */, D4E569DB27A34CC100AC2CEF /* KeyHandler.mm */, 5B7F225C2808501000DDD3CB /* KeyHandler.swift */, + 5B3133BE280B229700A4A505 /* KeyHandler_States.swift */, D456576D279E4F7B00DF6BC9 /* KeyParser.swift */, 6ACC3D3E27914F2400F1B140 /* KeyValueBlobReader.cpp */, 6ACC3D3C27914AAB00F1B140 /* KeyValueBlobReader.h */, @@ -1080,6 +1083,7 @@ 5BE78BE027B38804005EA1BE /* LMConsolidator.mm in Sources */, D456576E279E4F7B00DF6BC9 /* KeyParser.swift in Sources */, 5BA9FD1027FEDB6B002DE248 /* suiPrefPaneKeyboard.swift in Sources */, + 5B3133BF280B229700A4A505 /* KeyHandler_States.swift in Sources */, 5BA9FD4327FEF3C8002DE248 /* Preferences.swift in Sources */, 5BA9FD4427FEF3C8002DE248 /* SegmentedControlStyleViewController.swift in Sources */, D47F7DCE278BFB57002F9DD7 /* ctlPrefWindow.swift in Sources */,