KeyHandler // Swiftify: day 4.

This commit is contained in:
ShikiSuen 2022-04-17 18:04:00 +08:00
parent b17e3f45a4
commit 6139879d68
8 changed files with 1009 additions and 1207 deletions

View File

@ -47,11 +47,6 @@ extern InputMode imeModeNULL;
@interface KeyHandler : NSObject
- (BOOL)isBuilderEmpty;
- (BOOL)handleInput:(keyParser *)input
state:(InputState *)state
stateCallback:(void (^)(InputState *))stateCallback
errorCallback:(void (^)(void))errorCallback
NS_SWIFT_NAME(handle(input:state:stateCallback:errorCallback:));
- (void)fixNodeWithValue:(NSString *)value NS_SWIFT_NAME(fixNode(value:));
- (void)clear;
@ -63,34 +58,34 @@ extern InputMode imeModeNULL;
@property(weak, nonatomic) id<KeyHandlerDelegate> delegate;
// The following items need to be exposed to Swift:
- (void)_walk;
- (NSString *)_popOverflowComposingTextAndWalk;
- (BOOL)_handleCandidateState:(InputState *)state
input:(keyParser *)input
stateCallback:(void (^)(InputState *))stateCallback
errorCallback:(void (^)(void))errorCallback
NS_SWIFT_NAME(handleCandidate(state:input:stateCallback:errorCallback:));
- (NSArray<NSString *> *)_currentReadings;
- (BOOL)checkWhetherToneMarkerConfirmsPhoneticReadingBuffer;
- (BOOL)chkKeyValidity:(UniChar)value;
- (BOOL)ifLangModelHasUnigramsForKey:(NSString *)reading;
- (BOOL)isPhoneticReadingBufferEmpty;
- (BOOL)isPrintable:(UniChar)charCode;
- (NSArray<NSString *> *)getCandidatesArray;
- (NSInteger)getBuilderCursorIndex;
- (NSInteger)getBuilderLength;
- (NSString *)_currentMandarinParser;
- (NSString *)getCompositionFromPhoneticReadingBuffer;
- (NSString *)getSyllableCompositionFromPhoneticReadingBuffer;
- (void)clearPhoneticReadingBuffer;
- (void)combinePhoneticReadingBufferKey:(UniChar)charCode;
- (void)doBackSpaceToPhoneticReadingBuffer;
- (void)removeBuilderAndReset:(BOOL)shouldReset;
- (void)createNewBuilder;
- (void)dealWithOverrideModelSuggestions;
- (void)deleteBuilderReadingAfterCursor;
- (void)deleteBuilderReadingInFrontOfCursor;
- (void)doBackSpaceToPhoneticReadingBuffer;
- (void)ensurePhoneticParser;
- (void)insertReadingToBuilderAtCursor:(NSString *)reading;
- (void)removeBuilderAndReset:(BOOL)shouldReset;
- (void)setBuilderCursorIndex:(NSInteger)value;
- (void)setInputModesToLM:(BOOL)isCHS;
- (void)syncBaseLMPrefs;
- (void)ensurePhoneticParser;
- (BOOL)ifLangModelHasUnigramsForKey:(NSString *)reading;
- (void)insertReadingToBuilderAtCursor:(NSString *)reading;
- (BOOL)isPrintable:(UniChar)charCode;
- (void)dealWithOverrideModelSuggestions;
- (NSMutableArray *)getCandidatesArray;
- (NSInteger)getBuilderCursorIndex;
- (NSInteger)getBuilderLength;
@end

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,33 @@
// 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: - § Build Input State.
@objc extension KeyHandler {
}

View File

@ -0,0 +1,328 @@
// 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: - § Handle Candidate State.
@objc extension KeyHandler {
func _handleCandidateState(
_ state: InputState,
input: keyParser,
stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
let inputText = input.inputText
let charCode: UniChar = input.charCode
let ctlCandidateCurrent = delegate!.ctlCandidate(for: self) as! ctlCandidate
let cancelCandidateKey =
input.isBackSpace || input.isESC || input.isDelete
|| ((input.isCursorBackward || input.isCursorForward) && input.isShiftHold)
if cancelCandidateKey {
if state is InputState.AssociatedPhrases {
clear()
let empty = InputState.EmptyIgnoringPreviousState()
stateCallback(empty)
} else if mgrPrefs.useSCPCTypingMode {
clear()
let empty = InputState.EmptyIgnoringPreviousState()
stateCallback(empty)
} else if isBuilderEmpty() {
//
//
// 使 BackSpace
clear()
let empty = InputState.EmptyIgnoringPreviousState()
stateCallback(empty)
} else {
let inputting = buildInputtingState() as! InputState.Inputting
stateCallback(inputting)
}
return true
}
if input.isEnter {
if state is InputState.AssociatedPhrases {
clear()
let empty = InputState.EmptyIgnoringPreviousState()
stateCallback(empty)
return true
}
delegate!.keyHandler(
self,
didSelectCandidateAt: Int(ctlCandidateCurrent.selectedCandidateIndex),
ctlCandidate: ctlCandidateCurrent)
return true
}
if input.isTab {
let updated: Bool =
mgrPrefs.specifyShiftTabKeyBehavior
? (input.isShiftHold ? ctlCandidateCurrent.showPreviousPage() : ctlCandidateCurrent.showNextPage())
: (input.isShiftHold
? ctlCandidateCurrent.highlightPreviousCandidate()
: ctlCandidateCurrent.highlightNextCandidate())
if !updated {
IME.prtDebugIntel("9B691919")
errorCallback()
}
return true
}
if input.isSpace {
let updated: Bool =
mgrPrefs.specifyShiftSpaceKeyBehavior
? (input.isShiftHold
? ctlCandidateCurrent.highlightNextCandidate()
: ctlCandidateCurrent.showNextPage())
: (input.isShiftHold
? ctlCandidateCurrent.showNextPage()
: ctlCandidateCurrent.highlightNextCandidate())
if !updated {
IME.prtDebugIntel("A11C781F")
errorCallback()
}
return true
}
if input.isPageDown || input.emacsKey == vChewingEmacsKey.nextPage {
let updated: Bool = ctlCandidateCurrent.showNextPage()
if !updated {
IME.prtDebugIntel("9B691919")
errorCallback()
}
return true
}
if input.isPageUp {
let updated: Bool = ctlCandidateCurrent.showPreviousPage()
if !updated {
IME.prtDebugIntel("9569955D")
errorCallback()
}
return true
}
if input.isLeft {
if ctlCandidateCurrent is ctlCandidateHorizontal {
let updated: Bool = ctlCandidateCurrent.highlightPreviousCandidate()
if !updated {
IME.prtDebugIntel("1145148D")
errorCallback()
}
} else {
let updated: Bool = ctlCandidateCurrent.showPreviousPage()
if !updated {
IME.prtDebugIntel("1919810D")
errorCallback()
}
}
return true
}
if input.emacsKey == vChewingEmacsKey.backward {
let updated: Bool = ctlCandidateCurrent.highlightPreviousCandidate()
if !updated {
IME.prtDebugIntel("9B89308D")
errorCallback()
}
return true
}
if input.isRight {
if ctlCandidateCurrent is ctlCandidateHorizontal {
let updated: Bool = ctlCandidateCurrent.highlightNextCandidate()
if !updated {
IME.prtDebugIntel("9B65138D")
errorCallback()
}
} else {
let updated: Bool = ctlCandidateCurrent.showNextPage()
if !updated {
IME.prtDebugIntel("9244908D")
errorCallback()
}
}
return true
}
if input.emacsKey == vChewingEmacsKey.forward {
let updated: Bool = ctlCandidateCurrent.highlightNextCandidate()
if !updated {
IME.prtDebugIntel("9B2428D")
errorCallback()
}
return true
}
if input.isUp {
if ctlCandidateCurrent is ctlCandidateHorizontal {
let updated: Bool = ctlCandidateCurrent.showPreviousPage()
if !updated {
IME.prtDebugIntel("9B614524")
errorCallback()
}
} else {
let updated: Bool = ctlCandidateCurrent.highlightPreviousCandidate()
if !updated {
IME.prtDebugIntel("ASD9908D")
errorCallback()
}
}
return true
}
if input.isDown {
if ctlCandidateCurrent is ctlCandidateHorizontal {
let updated: Bool = ctlCandidateCurrent.showNextPage()
if !updated {
IME.prtDebugIntel("92B990DD")
errorCallback()
}
} else {
let updated: Bool = ctlCandidateCurrent.highlightNextCandidate()
if !updated {
IME.prtDebugIntel("6B99908D")
errorCallback()
}
}
return true
}
if input.isHome || input.emacsKey == vChewingEmacsKey.home {
if ctlCandidateCurrent.selectedCandidateIndex == 0 {
IME.prtDebugIntel("9B6EDE8D")
errorCallback()
} else {
ctlCandidateCurrent.selectedCandidateIndex = 0
}
return true
}
var candidates: [String]!
if state is InputState.ChoosingCandidate {
candidates = (state as! InputState.ChoosingCandidate).candidates
} else if state is InputState.AssociatedPhrases {
candidates = (state as! InputState.AssociatedPhrases).candidates
}
if candidates.isEmpty { return false }
if (input.isEnd || input.emacsKey == vChewingEmacsKey.end) && candidates.count > 0 {
if ctlCandidateCurrent.selectedCandidateIndex == UInt(candidates.count - 1) {
IME.prtDebugIntel("9B69AAAD")
errorCallback()
} else {
ctlCandidateCurrent.selectedCandidateIndex = UInt(candidates.count - 1)
}
return true
}
if state is InputState.AssociatedPhrases {
if !input.isShiftHold { return false }
}
var index: Int = NSNotFound
var match: String!
if state is InputState.AssociatedPhrases { match = input.inputTextIgnoringModifiers } else { match = inputText }
var j = 0
while j < ctlCandidateCurrent.keyLabels.count {
let label: CandidateKeyLabel = ctlCandidateCurrent.keyLabels[j]
if match.compare(label.key, options: .caseInsensitive, range: nil, locale: .current) == .orderedSame {
index = j
break
}
j += 1
}
if index != NSNotFound {
let candidateIndex: UInt = ctlCandidateCurrent.candidateIndexAtKeyLabelIndex(UInt(index))
if candidateIndex != UInt.max {
delegate!.keyHandler(self, didSelectCandidateAt: Int(candidateIndex), ctlCandidate: ctlCandidateCurrent)
return true
}
}
if state is InputState.AssociatedPhrases { return false }
if mgrPrefs.useSCPCTypingMode {
var punctuationNamePrefix = ""
if input.isOptionHold {
punctuationNamePrefix = "_alt_punctuation_"
} else if input.isControlHold {
punctuationNamePrefix = "_ctrl_punctuation_"
} else if mgrPrefs.halfWidthPunctuationEnabled {
punctuationNamePrefix = "_half_punctuation_"
} else {
punctuationNamePrefix = "_punctuation_"
}
let parser: String! = _currentMandarinParser()
let arrCustomPunctuations: [String] = [
punctuationNamePrefix, parser, String(format: "%c", CChar(charCode)),
]
let customPunctuation: String = arrCustomPunctuations.joined(separator: "")
let arrPunctuations: [String] = [punctuationNamePrefix, String(format: "%c", CChar(charCode))]
let punctuation: String = arrPunctuations.joined(separator: "")
var shouldAutoSelectCandidate: Bool =
chkKeyValidity(charCode) || ifLangModelHasUnigrams(forKey: customPunctuation)
|| ifLangModelHasUnigrams(forKey: punctuation)
if !shouldAutoSelectCandidate && input.isUpperCaseASCIILetterKey {
let letter: String! = String(format: "%@%c", "_letter_", CChar(charCode))
if ifLangModelHasUnigrams(forKey: letter) { shouldAutoSelectCandidate = true }
}
if shouldAutoSelectCandidate {
let candidateIndex: UInt = ctlCandidateCurrent.candidateIndexAtKeyLabelIndex(0)
if candidateIndex != UInt.max {
delegate!.keyHandler(
self,
didSelectCandidateAt: Int(candidateIndex),
ctlCandidate: ctlCandidateCurrent)
clear()
let empty = InputState.EmptyIgnoringPreviousState()
stateCallback(empty)
return handle(input: input, state: empty, stateCallback: stateCallback, errorCallback: errorCallback)
}
return true
}
}
IME.prtDebugIntel("172A0F81")
errorCallback()
return true
}
}

View File

@ -26,16 +26,17 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import Cocoa
// MARK: - § Handle Inputs (WIP).
// MARK: - § Handle Input with States.
@objc extension KeyHandler {
func handleInputSwift(
func handle(
input: keyParser,
state inState: InputState,
stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
let charCode: UniChar = input.charCode
var state = inState // Turn this incoming constant to variable.
var state = inState // Turn this incoming constant into variable.
let inputText: String = input.inputText ?? ""
let emptyState = InputState.Empty()
@ -102,14 +103,14 @@ import Cocoa
// MARK: Handle Candidates.
if state is InputState.ChoosingCandidate {
return handleCandidate(
state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback)
return _handleCandidateState(
state, input: input, stateCallback: stateCallback, errorCallback: errorCallback)
}
// MARK: Handle Associated Phrases.
if state is InputState.AssociatedPhrases {
let result = handleCandidate(
state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback)
let result = _handleCandidateState(
state, input: input, stateCallback: stateCallback, errorCallback: errorCallback)
if result {
return true
} else {
@ -221,7 +222,7 @@ import Cocoa
|| (input.useVerticalMode && (input.isVerticalModeOnlyChooseCandidateKey)))
{
if input.isSpace {
// If the spacebar is NOT set to be a selection key
// If the Space key is NOT set to be a selection key
if input.isShiftHold || !mgrPrefs.chooseCandidateUsingSpace {
if getBuilderCursorIndex() >= getBuilderLength() {
let composingBuffer = (state as! InputState.NotEmpty).composingBuffer
@ -251,11 +252,161 @@ import Cocoa
return true
}
// MARK: Function Keys.
// MARK: -
// MARK: Esc
if input.isESC { return _handleEscWithState(state, stateCallback: stateCallback, errorCallback: errorCallback) }
// MARK: Still Nothing.
// MARK: Cursor backward
if input.isCursorBackward || input.emacsKey == vChewingEmacsKey.backward {
return _handleBackwardWithState(
state,
input: input,
stateCallback: stateCallback,
errorCallback: errorCallback)
}
// MARK: Cursor forward
if input.isCursorForward || input.emacsKey == vChewingEmacsKey.forward {
return _handleForwardWithState(
state, input: input, stateCallback: stateCallback, errorCallback: errorCallback)
}
// MARK: Home
if input.isHome || input.emacsKey == vChewingEmacsKey.home {
return _handleHomeWithState(state, stateCallback: stateCallback, errorCallback: errorCallback)
}
// MARK: End
if input.isEnd || input.emacsKey == vChewingEmacsKey.end {
return _handleEndWithState(state, stateCallback: stateCallback, errorCallback: errorCallback)
}
// MARK: Ctrl+PgLf or Shift+PgLf
if (input.isControlHold || input.isShiftHold) && (input.isOptionHold && input.isLeft) {
return _handleHomeWithState(state, stateCallback: stateCallback, errorCallback: errorCallback)
}
// MARK: Ctrl+PgRt or Shift+PgRt
if (input.isControlHold || input.isShiftHold) && (input.isOptionHold && input.isRight) {
return _handleEndWithState(state, stateCallback: stateCallback, errorCallback: errorCallback)
}
// MARK: AbsorbedArrowKey
if input.isAbsorbedArrowKey || input.isExtraChooseCandidateKey || input.isExtraChooseCandidateKeyReverse {
return _handleAbsorbedArrowKeyWithState(state, stateCallback: stateCallback, errorCallback: errorCallback)
}
// MARK: Backspace
if input.isBackSpace {
return _handleBackspaceWithState(state, stateCallback: stateCallback, errorCallback: errorCallback)
}
// MARK: Delete
if input.isDelete || input.emacsKey == vChewingEmacsKey.delete {
return _handleDeleteWithState(state, stateCallback: stateCallback, errorCallback: errorCallback)
}
// MARK: Enter
if input.isEnter {
return (input.isCommandHold)
? _handleCommandEnterWithState(state, stateCallback: stateCallback, errorCallback: errorCallback)
: _handleEnterWithState(state, stateCallback: stateCallback, errorCallback: errorCallback)
}
// MARK: -
// MARK: Punctuation list
if input.isSymbolMenuPhysicalKey && !input.isShiftHold {
if !input.isOptionHold {
if ifLangModelHasUnigrams(forKey: "_punctuation_list") {
if isPhoneticReadingBufferEmpty() {
insertReadingToBuilder(atCursor: "_punctuation_list")
let poppedText: String! = _popOverflowComposingTextAndWalk()
let inputting = buildInputtingState() as! InputState.Inputting
inputting.poppedText = poppedText
stateCallback(inputting)
let choosingCandidate =
_buildCandidateState(inputting, useVerticalMode: input.useVerticalMode)
stateCallback(choosingCandidate)
} else { // If there is still unfinished bpmf reading, ignore the punctuation
IME.prtDebugIntel("17446655")
errorCallback()
}
return true
}
} else {
// commit buffer ESC
// Enter 使 commit buffer
// bool _ =
_ = _handleEnterWithState(state, stateCallback: stateCallback, errorCallback: errorCallback)
let root: SymbolNode! = SymbolNode.root
let symbolState =
InputState.SymbolTable(node: root, useVerticalMode: input.useVerticalMode)
stateCallback(symbolState)
return true
}
}
// MARK: Punctuation
// if nothing is matched, see if it's a punctuation key for current layout.
var punctuationNamePrefix = ""
if input.isOptionHold {
punctuationNamePrefix = "_alt_punctuation_"
} else if input.isControlHold {
punctuationNamePrefix = "_ctrl_punctuation_"
} else if mgrPrefs.halfWidthPunctuationEnabled {
punctuationNamePrefix = "_half_punctuation_"
} else {
punctuationNamePrefix = "_punctuation_"
}
let parser: String! = _currentMandarinParser()
let arrCustomPunctuations: [String] = [
punctuationNamePrefix, parser, String(format: "%c", CChar(charCode)),
]
let customPunctuation: String = arrCustomPunctuations.joined(separator: "")
if _handlePunctuation(
customPunctuation,
state: state,
usingVerticalMode: input.useVerticalMode,
stateCallback: stateCallback,
errorCallback: errorCallback)
{
return true
}
// if nothing is matched, see if it's a punctuation key.
let arrPunctuations: [String] = [punctuationNamePrefix, String(format: "%c", CChar(charCode))]
let punctuation: String = arrPunctuations.joined(separator: "")
if _handlePunctuation(
punctuation,
state: state,
usingVerticalMode: input.useVerticalMode,
stateCallback: stateCallback,
errorCallback: errorCallback)
{
return true
}
// 使 2.2
if input.isUpperCaseASCIILetterKey {
let letter: String! = String(format: "%@%c", "_letter_", CChar(charCode))
if _handlePunctuation(
letter,
state: state,
usingVerticalMode: input.useVerticalMode,
stateCallback: stateCallback,
errorCallback: errorCallback)
{
return true
}
}
// 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.

View File

@ -0,0 +1,43 @@
// 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: - § Misc functions.
@objc extension KeyHandler {
func _actualCandidateCursorIndex() -> Int {
var cursorIndex = getBuilderCursorIndex()
// MS Phonetics IME style, phrase is *after* the cursor.
// (i.e. the cursor is always *before* the phrase.)
if (mgrPrefs.selectPhraseAfterCursorAsCandidate
&& (cursorIndex < getBuilderLength()))
|| cursorIndex == 0
{
cursorIndex += 1
}
return cursorIndex
}
}

View File

@ -29,7 +29,7 @@ import Cocoa
// MARK: - § State managements.
@objc extension KeyHandler {
// MARK:
// MARK: -
func _buildCandidateState(
_ currentState: InputState.NotEmpty,
useVerticalMode: Bool
@ -39,12 +39,12 @@ import Cocoa
let state = InputState.ChoosingCandidate(
composingBuffer: currentState.composingBuffer,
cursorIndex: currentState.cursorIndex,
candidates: candidatesArray as! [String],
candidates: candidatesArray,
useVerticalMode: useVerticalMode)
return state
}
// MARK:
// MARK: -
func _handleMarkingState(
_ state: InputState.Marking,
input: keyParser,
@ -127,4 +127,378 @@ import Cocoa
}
return false
}
// MARK: -
func _handlePunctuation(
_ customPunctuation: String,
state: InputState,
usingVerticalMode useVerticalMode: Bool,
stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
if !ifLangModelHasUnigrams(forKey: customPunctuation) {
return false
}
if isPhoneticReadingBufferEmpty() {
insertReadingToBuilder(atCursor: customPunctuation)
let poppedText = _popOverflowComposingTextAndWalk()
let inputting = buildInputtingState() as! InputState.Inputting
inputting.poppedText = poppedText
stateCallback(inputting)
if mgrPrefs.useSCPCTypingMode && isPhoneticReadingBufferEmpty() {
let candidateState = _buildCandidateState(
inputting, useVerticalMode: useVerticalMode)
if candidateState.candidates.count == 1 {
clear()
if let strPoppedText: String = candidateState.candidates.first {
let committing =
InputState.Committing(poppedText: strPoppedText) as InputState.Committing
stateCallback(committing)
let empty = InputState.Empty()
stateCallback(empty)
} else {
stateCallback(candidateState)
}
} else {
stateCallback(candidateState)
}
}
return true
} else {
// If there is still unfinished bpmf reading, ignore the punctuation
IME.prtDebugIntel("A9B69908D")
errorCallback()
stateCallback(state)
return true
}
}
// MARK: - Enter
@discardableResult func _handleEnterWithState(
_ state: InputState,
stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
if !(state is InputState.Inputting) {
return false
}
clear()
let current = state as! InputState.Inputting
let composingBuffer = current.composingBuffer
let committing = InputState.Committing(poppedText: composingBuffer)
stateCallback(committing)
let empty = InputState.Empty()
stateCallback(empty)
return true
}
// MARK: - CMD+Enter
func _handleCommandEnterWithState(
_ state: InputState,
stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
if !(state is InputState.Inputting) {
return false
}
let readings: [String] = _currentReadings()
let composingBuffer =
(IME.areWeUsingOurOwnPhraseEditor)
? readings.joined(separator: "-")
: readings.joined(separator: " ")
clear()
let committing = InputState.Committing(poppedText: composingBuffer)
stateCallback(committing)
let empty = InputState.Empty()
stateCallback(empty)
return true
}
// MARK: - Backspace (macOS Delete)
func _handleBackspaceWithState(
_ state: InputState,
stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
if !(state is InputState.Inputting) {
return false
}
if isPhoneticReadingBufferEmpty() {
if getBuilderCursorIndex() >= 0 {
deleteBuilderReadingInFrontOfCursor()
_walk()
} else {
IME.prtDebugIntel("9D69908D")
errorCallback()
stateCallback(state)
return true
}
} else {
doBackSpaceToPhoneticReadingBuffer()
}
if isPhoneticReadingBufferEmpty() && (getBuilderLength() == 0) {
let empty = InputState.EmptyIgnoringPreviousState()
stateCallback(empty)
} else {
let inputting = buildInputtingState() as! InputState.Inputting
stateCallback(inputting)
}
return true
}
// MARK: - PC Delete (macOS Fn+BackSpace)
func _handleDeleteWithState(
_ state: InputState,
stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
if !(state is InputState.Inputting) {
return false
}
if isPhoneticReadingBufferEmpty() {
if getBuilderCursorIndex() != getBuilderLength() {
deleteBuilderReadingAfterCursor()
_walk()
let inputting = buildInputtingState() as! InputState.Inputting
if inputting.composingBuffer.count == 0 {
let empty = InputState.EmptyIgnoringPreviousState()
stateCallback(empty)
} else {
stateCallback(inputting)
}
} else {
IME.prtDebugIntel("9B69938D")
errorCallback()
stateCallback(state)
}
} else {
IME.prtDebugIntel("9C69908D")
errorCallback()
stateCallback(state)
}
return true
}
// MARK: - 90
func _handleAbsorbedArrowKeyWithState(
_ state: InputState,
stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
if !(state is InputState.Inputting) {
return false
}
if !isPhoneticReadingBufferEmpty() {
IME.prtDebugIntel("9B6F908D")
errorCallback()
}
stateCallback(state)
return true
}
// MARK: - Home
func _handleHomeWithState(
_ state: InputState,
stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
if !(state is InputState.Inputting) {
return false
}
if !isPhoneticReadingBufferEmpty() {
IME.prtDebugIntel("ABC44080")
errorCallback()
stateCallback(state)
return true
}
if getBuilderCursorIndex() != 0 {
setBuilderCursorIndex(0)
let inputting = buildInputtingState() as! InputState.Inputting
stateCallback(inputting)
} else {
IME.prtDebugIntel("66D97F90")
errorCallback()
stateCallback(state)
}
return true
}
// MARK: - End
func _handleEndWithState(
_ state: InputState,
stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
if !(state is InputState.Inputting) {
return false
}
if !isPhoneticReadingBufferEmpty() {
IME.prtDebugIntel("9B69908D")
errorCallback()
stateCallback(state)
return true
}
if getBuilderCursorIndex() != getBuilderLength() {
setBuilderCursorIndex(getBuilderLength())
let inputting = buildInputtingState() as! InputState.Inputting
stateCallback(inputting)
} else {
IME.prtDebugIntel("9B69908E")
errorCallback()
stateCallback(state)
}
return true
}
// MARK: - Esc
func _handleEscWithState(
_ state: InputState,
stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
if !(state is InputState.Inputting) { return false }
let escToClearInputBufferEnabled: Bool = mgrPrefs.escToCleanInputBuffer
if escToClearInputBufferEnabled {
// If the option is enabled, we clear everything in the buffer.
// This includes walked nodes and the reading. Note that this convention
// is by default in macOS 10.0-10.5 built-in Panasonic Hanin and later macOS Zhuyin.
// Some Windows users hate this design, hence the option here to disable it.
clear()
let empty = InputState.EmptyIgnoringPreviousState()
stateCallback(empty)
} else {
// If reading is not empty, we cancel the reading.
if !isPhoneticReadingBufferEmpty() {
clearPhoneticReadingBuffer()
if getBuilderLength() == 0 {
let empty = InputState.Empty()
stateCallback(empty)
} else {
let inputting = buildInputtingState()
stateCallback(inputting)
}
}
}
return true
}
// MARK: -
func _handleForwardWithState(
_ state: InputState,
input: keyParser,
stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
if !(state is InputState.Inputting) { return false }
if !isPhoneticReadingBufferEmpty() {
IME.prtDebugIntel("B3BA5257")
errorCallback()
stateCallback(state)
return true
}
let currentState = state as! InputState.Inputting
if input.isShiftHold {
// Shift + Right
if currentState.cursorIndex < (currentState.composingBuffer as NSString).length {
let nextPosition = (currentState.composingBuffer as NSString).nextUtf16Position(
for: Int(currentState.cursorIndex))
let marking: InputState.Marking! = InputState.Marking(
composingBuffer: currentState.composingBuffer,
cursorIndex: currentState.cursorIndex,
markerIndex: UInt(nextPosition),
readings: _currentReadings())
marking.tooltipForInputting = currentState.tooltip
stateCallback(marking)
} else {
IME.prtDebugIntel("BB7F6DB9")
errorCallback()
stateCallback(state)
}
} else {
if getBuilderCursorIndex() < getBuilderLength() {
setBuilderCursorIndex(getBuilderCursorIndex() + 1)
let inputting = buildInputtingState() as! InputState.Inputting
stateCallback(inputting)
} else {
IME.prtDebugIntel("A96AAD58")
errorCallback()
stateCallback(state)
}
}
return true
}
// MARK: -
func _handleBackwardWithState(
_ state: InputState,
input: keyParser,
stateCallback: @escaping (InputState) -> Void,
errorCallback: @escaping () -> Void
) -> Bool {
if !(state is InputState.Inputting) { return false }
if !isPhoneticReadingBufferEmpty() {
IME.prtDebugIntel("6ED95318")
errorCallback()
stateCallback(state)
return true
}
let currentState = state as! InputState.Inputting
if input.isShiftHold {
// Shift + left
if currentState.cursorIndex > 0 {
let previousPosition = (currentState.composingBuffer as NSString).previousUtf16Position(
for: Int(currentState.cursorIndex))
let marking: InputState.Marking! = InputState.Marking(
composingBuffer: currentState.composingBuffer,
cursorIndex: currentState.cursorIndex,
markerIndex: UInt(previousPosition),
readings: _currentReadings())
marking.tooltipForInputting = currentState.tooltip
stateCallback(marking)
} else {
IME.prtDebugIntel("D326DEA3")
errorCallback()
stateCallback(state)
}
} else {
if getBuilderCursorIndex() > 0 {
setBuilderCursorIndex(getBuilderCursorIndex() - 1)
let inputting = buildInputtingState() as! InputState.Inputting
stateCallback(inputting)
} else {
IME.prtDebugIntel("7045E6F3")
errorCallback()
stateCallback(state)
}
}
return true
}
}

View File

@ -15,6 +15,7 @@
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 */; };
5B61B0CA280BEFD4002E3CFA /* KeyHandler_Misc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B61B0C9280BEFD4002E3CFA /* KeyHandler_Misc.swift */; };
5B62A32927AE77D100A19448 /* FSEventStreamHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A32827AE77D100A19448 /* FSEventStreamHelper.swift */; };
5B62A32F27AE78B000A19448 /* CoreLM.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A32D27AE78B000A19448 /* CoreLM.mm */; };
5B62A33227AE792F00A19448 /* InputSourceHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33127AE792F00A19448 /* InputSourceHelper.swift */; };
@ -30,8 +31,10 @@
5B707CE827D9F4590099EF99 /* OpenCCBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B707CE727D9F4590099EF99 /* OpenCCBridge.swift */; };
5B707CEC27D9F4870099EF99 /* OpenCC in Frameworks */ = {isa = PBXBuildFile; productRef = 5B707CEB27D9F4870099EF99 /* OpenCC */; };
5B73FB5E27B2BE1300E9BF49 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5B73FB6027B2BE1300E9BF49 /* InfoPlist.strings */; };
5B782EC4280C243C007276DE /* KeyHandler_HandleCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */; };
5B782EC6280C2F4B007276DE /* KeyHandler_BuildInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B782EC5280C2F4B007276DE /* KeyHandler_BuildInput.swift */; };
5B7BC4B027AFFBE800F66C24 /* frmPrefWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5B7BC4AE27AFFBE800F66C24 /* frmPrefWindow.xib */; };
5B7F225D2808501000DDD3CB /* KeyHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B7F225C2808501000DDD3CB /* KeyHandler.swift */; };
5B7F225D2808501000DDD3CB /* KeyHandler_HandleInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B7F225C2808501000DDD3CB /* KeyHandler_HandleInput.swift */; };
5BA9FD0F27FEDB6B002DE248 /* suiPrefPaneGeneral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA9FD0A27FEDB6B002DE248 /* suiPrefPaneGeneral.swift */; };
5BA9FD1027FEDB6B002DE248 /* suiPrefPaneKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA9FD0B27FEDB6B002DE248 /* suiPrefPaneKeyboard.swift */; };
5BA9FD1127FEDB6B002DE248 /* ctlPrefUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA9FD0C27FEDB6B002DE248 /* ctlPrefUI.swift */; };
@ -189,6 +192,7 @@
5B30F11227BA568800484E24 /* vChewingKeyLayout.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = vChewingKeyLayout.bundle; sourceTree = "<group>"; };
5B3133BE280B229700A4A505 /* KeyHandler_States.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyHandler_States.swift; sourceTree = "<group>"; };
5B5E535127EF261400C6AA1E /* IME.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IME.swift; sourceTree = "<group>"; };
5B61B0C9280BEFD4002E3CFA /* KeyHandler_Misc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyHandler_Misc.swift; sourceTree = "<group>"; };
5B62A32627AE77BB00A19448 /* LMConsolidator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LMConsolidator.h; sourceTree = "<group>"; };
5B62A32727AE77BB00A19448 /* LMConsolidator.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = LMConsolidator.mm; sourceTree = "<group>"; };
5B62A32827AE77D100A19448 /* FSEventStreamHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FSEventStreamHelper.swift; sourceTree = "<group>"; };
@ -209,9 +213,11 @@
5B7111C727DEF9FF00444310 /* UserSymbolLM.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UserSymbolLM.h; sourceTree = "<group>"; };
5B73FB5427B2BD6900E9BF49 /* PhraseEditor-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "PhraseEditor-Info.plist"; path = "UserPhraseEditor/PhraseEditor-Info.plist"; sourceTree = SOURCE_ROOT; };
5B73FB5F27B2BE1300E9BF49 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyHandler_HandleCandidate.swift; sourceTree = "<group>"; };
5B782EC5280C2F4B007276DE /* KeyHandler_BuildInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyHandler_BuildInput.swift; sourceTree = "<group>"; };
5B7BC4AF27AFFBE800F66C24 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/frmPrefWindow.xib; sourceTree = "<group>"; };
5B7BC4B227AFFC0B00F66C24 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/frmPrefWindow.strings; sourceTree = "<group>"; };
5B7F225C2808501000DDD3CB /* KeyHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyHandler.swift; sourceTree = "<group>"; };
5B7F225C2808501000DDD3CB /* KeyHandler_HandleInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyHandler_HandleInput.swift; sourceTree = "<group>"; };
5B8F43ED27C9BC220069AC27 /* SymbolLM.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SymbolLM.h; sourceTree = "<group>"; };
5BA9FD0A27FEDB6B002DE248 /* suiPrefPaneGeneral.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = suiPrefPaneGeneral.swift; sourceTree = "<group>"; };
5BA9FD0B27FEDB6B002DE248 /* suiPrefPaneKeyboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = suiPrefPaneKeyboard.swift; sourceTree = "<group>"; };
@ -427,7 +433,10 @@
D461B791279DAC010070E734 /* InputState.swift */,
D4E569DA27A34CC100AC2CEF /* KeyHandler.h */,
D4E569DB27A34CC100AC2CEF /* KeyHandler.mm */,
5B7F225C2808501000DDD3CB /* KeyHandler.swift */,
5B782EC5280C2F4B007276DE /* KeyHandler_BuildInput.swift */,
5B7F225C2808501000DDD3CB /* KeyHandler_HandleInput.swift */,
5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */,
5B61B0C9280BEFD4002E3CFA /* KeyHandler_Misc.swift */,
5B3133BE280B229700A4A505 /* KeyHandler_States.swift */,
D456576D279E4F7B00DF6BC9 /* KeyParser.swift */,
6ACC3D3E27914F2400F1B140 /* KeyValueBlobReader.cpp */,
@ -1067,7 +1076,7 @@
D427F76C278CA2B0004A2160 /* AppDelegate.swift in Sources */,
5BA9FD4527FEF3C9002DE248 /* ToolbarItemStyleViewController.swift in Sources */,
5BA9FD4127FEF3C8002DE248 /* PreferencesStyle.swift in Sources */,
5B7F225D2808501000DDD3CB /* KeyHandler.swift in Sources */,
5B7F225D2808501000DDD3CB /* KeyHandler_HandleInput.swift in Sources */,
5BA9FD1227FEDB6B002DE248 /* suiPrefPaneExperience.swift in Sources */,
6ACC3D442793701600F1B140 /* ParselessPhraseDB.cpp in Sources */,
D461B792279DAC010070E734 /* InputState.swift in Sources */,
@ -1082,6 +1091,7 @@
5B62A32F27AE78B000A19448 /* CoreLM.mm in Sources */,
5BE78BE027B38804005EA1BE /* LMConsolidator.mm in Sources */,
D456576E279E4F7B00DF6BC9 /* KeyParser.swift in Sources */,
5B782EC6280C2F4B007276DE /* KeyHandler_BuildInput.swift in Sources */,
5BA9FD1027FEDB6B002DE248 /* suiPrefPaneKeyboard.swift in Sources */,
5B3133BF280B229700A4A505 /* KeyHandler_States.swift in Sources */,
5BA9FD4327FEF3C8002DE248 /* Preferences.swift in Sources */,
@ -1098,6 +1108,7 @@
D47F7DD3278C1263002F9DD7 /* UserOverrideModel.cpp in Sources */,
5B62A33627AE795800A19448 /* mgrPrefs.swift in Sources */,
5BAEFAD028012565001F42C9 /* mgrLangModel.swift in Sources */,
5B782EC4280C243C007276DE /* KeyHandler_HandleCandidate.swift in Sources */,
5B62A33827AE79CD00A19448 /* NSStringUtils.swift in Sources */,
5BA9FD0F27FEDB6B002DE248 /* suiPrefPaneGeneral.swift in Sources */,
5BA9FD4927FEF3C9002DE248 /* Section.swift in Sources */,
@ -1107,6 +1118,7 @@
5B5E535227EF261400C6AA1E /* IME.swift in Sources */,
5B62A34927AE7CD900A19448 /* TooltipController.swift in Sources */,
6A0D4F4515FC0EB100ABF4B3 /* Mandarin.cpp in Sources */,
5B61B0CA280BEFD4002E3CFA /* KeyHandler_Misc.swift in Sources */,
5B62A34827AE7CD900A19448 /* ctlCandidateVertical.swift in Sources */,
5BA9FD4027FEF3C8002DE248 /* Localization.swift in Sources */,
5BA9FD1327FEDB6B002DE248 /* suiPrefPaneDictionary.swift in Sources */,