1.5.x Beta // Merge GitHub PR #63 from dev/1.5.x
KeyHandler Swiftification.
This commit is contained in:
commit
417c9ede7e
|
@ -210,7 +210,7 @@ typedef BopomofoSyllable BPMF;
|
|||
typedef std::map<char, std::vector<BPMF::Component>> BopomofoKeyToComponentMap;
|
||||
typedef std::map<BPMF::Component, char> BopomofoComponentToKeyMap;
|
||||
|
||||
class BopomofoKeyboardLayout
|
||||
extern "C" class BopomofoKeyboardLayout
|
||||
{
|
||||
public:
|
||||
static const BopomofoKeyboardLayout *StandardLayout();
|
||||
|
@ -438,7 +438,7 @@ class BopomofoKeyboardLayout
|
|||
BopomofoComponentToKeyMap m_componentToKey;
|
||||
};
|
||||
|
||||
class BopomofoReadingBuffer
|
||||
extern "C" class BopomofoReadingBuffer
|
||||
{
|
||||
public:
|
||||
explicit BopomofoReadingBuffer(const BopomofoKeyboardLayout *layout) : layout_(layout), pinyin_mode_(false)
|
||||
|
|
|
@ -36,6 +36,14 @@ extern InputMode imeModeCHT;
|
|||
extern InputMode imeModeCHS;
|
||||
extern InputMode imeModeNULL;
|
||||
|
||||
struct BufferStatePackage
|
||||
{
|
||||
NSString *composedText;
|
||||
NSInteger cursorIndex;
|
||||
NSString *resultOfBefore;
|
||||
NSString *resultOfAfter;
|
||||
};
|
||||
|
||||
@class KeyHandler;
|
||||
|
||||
@protocol KeyHandlerDelegate <NSObject>
|
||||
|
@ -47,21 +55,48 @@ 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)syncWithPreferences;
|
||||
- (void)fixNodeWithValue:(NSString *)value NS_SWIFT_NAME(fixNode(value:));
|
||||
- (void)clear;
|
||||
|
||||
- (InputState *)buildInputtingState;
|
||||
- (nullable InputState *)buildAssociatePhraseStateWithKey:(NSString *)key useVerticalMode:(BOOL)useVerticalMode;
|
||||
|
||||
@property(strong, nonatomic) InputMode inputMode;
|
||||
@property(weak, nonatomic) id<KeyHandlerDelegate> delegate;
|
||||
|
||||
// The following items need to be exposed to Swift:
|
||||
- (void)_walk;
|
||||
- (NSString *)_popOverflowComposingTextAndWalk;
|
||||
- (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;
|
||||
- (NSInteger)getPackagedCursorIndex;
|
||||
- (NSString *)getComposedText;
|
||||
- (NSString *)getCompositionFromPhoneticReadingBuffer;
|
||||
- (NSString *)getStrLocationResult:(BOOL)isAfter NS_SWIFT_NAME(getStrLocationResult(isAfter:));
|
||||
- (NSString *)getSyllableCompositionFromPhoneticReadingBuffer;
|
||||
- (void)clearPhoneticReadingBuffer;
|
||||
- (void)combinePhoneticReadingBufferKey:(UniChar)charCode;
|
||||
- (void)createNewBuilder;
|
||||
- (void)dealWithOverrideModelSuggestions;
|
||||
- (void)deleteBuilderReadingAfterCursor;
|
||||
- (void)deleteBuilderReadingInFrontOfCursor;
|
||||
- (void)doBackSpaceToPhoneticReadingBuffer;
|
||||
- (void)ensurePhoneticParser;
|
||||
- (void)insertReadingToBuilderAtCursor:(NSString *)reading;
|
||||
- (void)packageBufferStateMaterials;
|
||||
- (void)removeBuilderAndReset:(BOOL)shouldReset;
|
||||
- (void)setBuilderCursorIndex:(NSInteger)value;
|
||||
- (void)setInputModesToLM:(BOOL)isCHS;
|
||||
- (void)syncBaseLMPrefs;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,329 @@
|
|||
// 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()
|
||||
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 = getCurrentMandarinParser()
|
||||
|
||||
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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,426 @@
|
|||
// 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 Input with States.
|
||||
|
||||
@objc extension KeyHandler {
|
||||
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 into variable.
|
||||
let inputText: String = input.inputText ?? ""
|
||||
let emptyState = InputState.Empty()
|
||||
|
||||
// Ignore the input if its inputText is empty.
|
||||
// Reason: such inputs may be functional key combinations.
|
||||
|
||||
if (inputText).isEmpty {
|
||||
return false
|
||||
}
|
||||
|
||||
// Ignore the input if the composing buffer is empty with no reading
|
||||
// and there is some function key combination.
|
||||
let isFunctionKey: Bool =
|
||||
input.isControlHotKey || (input.isCommandHold || input.isOptionHotKey || input.isNumericPad)
|
||||
if !(state is InputState.NotEmpty) && !(state is InputState.AssociatedPhrases) && isFunctionKey {
|
||||
return false
|
||||
}
|
||||
|
||||
// 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
|
||||
|| input.isExtraChooseCandidateKeyReverse || input.isCursorForward || input.isCursorBackward
|
||||
{
|
||||
// Do nothing if backspace is pressed -- we ignore the key
|
||||
} else if input.isCapsLockOn {
|
||||
// Process all possible combination, we hope.
|
||||
clear()
|
||||
stateCallback(emptyState)
|
||||
|
||||
// When shift is pressed, don't do further processing...
|
||||
// ...since it outputs capital letter anyway.
|
||||
if input.isShiftHold {
|
||||
return false
|
||||
}
|
||||
|
||||
// If ASCII but not printable, don't use insertText:replacementRange:
|
||||
// Certain apps don't handle non-ASCII char insertions.
|
||||
if charCode < 0x80 && !isPrintable(charCode) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Commit the entire input buffer.
|
||||
let committingState = InputState.Committing(poppedText: inputText.lowercased())
|
||||
stateCallback(committingState)
|
||||
stateCallback(emptyState)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// MARK: Numeric Pad Processing.
|
||||
if input.isNumericPad {
|
||||
if !input.isLeft && !input.isRight && !input.isDown
|
||||
&& !input.isUp && !input.isSpace && isPrintable(charCode)
|
||||
{
|
||||
clear()
|
||||
stateCallback(emptyState)
|
||||
let committing = InputState.Committing(poppedText: inputText.lowercased())
|
||||
stateCallback(committing)
|
||||
stateCallback(emptyState)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Handle Candidates.
|
||||
if state is InputState.ChoosingCandidate {
|
||||
return _handleCandidateState(
|
||||
state, input: input, stateCallback: stateCallback, errorCallback: errorCallback)
|
||||
}
|
||||
|
||||
// MARK: Handle Associated Phrases.
|
||||
if state is InputState.AssociatedPhrases {
|
||||
let result = _handleCandidateState(
|
||||
state, input: input, stateCallback: stateCallback, errorCallback: errorCallback)
|
||||
if result {
|
||||
return true
|
||||
} else {
|
||||
stateCallback(emptyState)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Handle Marking.
|
||||
if state is InputState.Marking {
|
||||
let marking = state as! InputState.Marking
|
||||
|
||||
if _handleMarkingState(
|
||||
state as! InputState.Marking, input: input, stateCallback: stateCallback,
|
||||
errorCallback: errorCallback)
|
||||
{
|
||||
return true
|
||||
}
|
||||
|
||||
state = marking.convertToInputting()
|
||||
stateCallback(state)
|
||||
}
|
||||
|
||||
// MARK: Handle BPMF Keys.
|
||||
var composeReading: Bool = false
|
||||
let skipPhoneticHandling = input.isReservedKey || input.isControlHold || input.isOptionHold
|
||||
|
||||
// See if Phonetic reading is valid.
|
||||
if !skipPhoneticHandling && chkKeyValidity(charCode) {
|
||||
combinePhoneticReadingBufferKey(charCode)
|
||||
|
||||
// If we have a tone marker, we have to insert the reading to the
|
||||
// builder in other words, if we don't have a tone marker, we just
|
||||
// update the composing buffer.
|
||||
composeReading = checkWhetherToneMarkerConfirmsPhoneticReadingBuffer()
|
||||
if !composeReading {
|
||||
let inputting = buildInputtingState()
|
||||
stateCallback(inputting)
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 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 "|=".
|
||||
composeReading = composeReading || (!isPhoneticReadingBufferEmpty() && (input.isSpace || input.isEnter))
|
||||
if composeReading {
|
||||
let reading = getSyllableCompositionFromPhoneticReadingBuffer()
|
||||
|
||||
if !ifLangModelHasUnigrams(forKey: reading) {
|
||||
IME.prtDebugIntel("B49C0979")
|
||||
errorCallback()
|
||||
let inputting = buildInputtingState()
|
||||
stateCallback(inputting)
|
||||
return true
|
||||
}
|
||||
|
||||
// ... and insert it into the lattice grid...
|
||||
insertReadingToBuilder(atCursor: reading)
|
||||
|
||||
// ... then walk the lattice grid...
|
||||
let poppedText = _popOverflowComposingTextAndWalk()
|
||||
|
||||
// ... get and tweak override model suggestion if possible...
|
||||
dealWithOverrideModelSuggestions()
|
||||
|
||||
// ... then update the text.
|
||||
clearPhoneticReadingBuffer()
|
||||
|
||||
let inputting = buildInputtingState()
|
||||
inputting.poppedText = poppedText
|
||||
stateCallback(inputting)
|
||||
|
||||
if mgrPrefs.useSCPCTypingMode {
|
||||
let choosingCandidates: InputState.ChoosingCandidate = _buildCandidateState(
|
||||
inputting,
|
||||
useVerticalMode: input.useVerticalMode)
|
||||
if choosingCandidates.candidates.count == 1 {
|
||||
clear()
|
||||
let text: String = choosingCandidates.candidates.first ?? ""
|
||||
let committing = InputState.Committing(poppedText: text)
|
||||
stateCallback(committing)
|
||||
|
||||
if !mgrPrefs.associatedPhrasesEnabled {
|
||||
stateCallback(emptyState)
|
||||
} else {
|
||||
let associatedPhrases =
|
||||
buildAssociatePhraseState(
|
||||
withKey: text,
|
||||
useVerticalMode: input.useVerticalMode) as? InputState.AssociatedPhrases
|
||||
if let associatedPhrases = associatedPhrases {
|
||||
stateCallback(associatedPhrases)
|
||||
} else {
|
||||
stateCallback(emptyState)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
stateCallback(choosingCandidates)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// MARK: Calling candidate window using Space or Down or PageUp / PageDn.
|
||||
|
||||
if isPhoneticReadingBufferEmpty() && (state is InputState.NotEmpty)
|
||||
&& (input.isExtraChooseCandidateKey || input.isExtraChooseCandidateKeyReverse || input.isSpace
|
||||
|| input.isPageDown || input.isPageUp || input.isTab
|
||||
|| (input.useVerticalMode && (input.isVerticalModeOnlyChooseCandidateKey)))
|
||||
{
|
||||
if input.isSpace {
|
||||
// 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
|
||||
if (composingBuffer.count) != 0 {
|
||||
let committing = InputState.Committing(poppedText: composingBuffer)
|
||||
stateCallback(committing)
|
||||
}
|
||||
clear()
|
||||
let committing = InputState.Committing(poppedText: " ")
|
||||
stateCallback(committing)
|
||||
let empty = InputState.Empty()
|
||||
stateCallback(empty)
|
||||
} else if ifLangModelHasUnigrams(forKey: " ") {
|
||||
insertReadingToBuilder(atCursor: " ")
|
||||
let poppedText = _popOverflowComposingTextAndWalk()
|
||||
let inputting = buildInputtingState()
|
||||
inputting.poppedText = poppedText
|
||||
stateCallback(inputting)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
let choosingCandidates = _buildCandidateState(
|
||||
state as! InputState.NotEmpty,
|
||||
useVerticalMode: input.useVerticalMode)
|
||||
stateCallback(choosingCandidates)
|
||||
return true
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
// MARK: Esc
|
||||
if input.isESC { return _handleEscWithState(state, stateCallback: stateCallback, errorCallback: errorCallback) }
|
||||
|
||||
// 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 && input.isControlHold)
|
||||
? _handleCtrlCommandEnterWithState(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()
|
||||
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 = getCurrentMandarinParser()
|
||||
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.
|
||||
// 砍掉這一段會導致「F1-F12 按鍵干擾組字區」的問題。
|
||||
// 暫時只能先恢復這段,且補上偵錯彙報機制,方便今後排查故障。
|
||||
if (state is InputState.NotEmpty) || !isPhoneticReadingBufferEmpty() {
|
||||
IME.prtDebugIntel(
|
||||
"Blocked data: charCode: \(charCode), keyCode: \(input.keyCode)")
|
||||
IME.prtDebugIntel("A9BFF20E")
|
||||
errorCallback()
|
||||
stateCallback(state)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
// 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 getCurrentMandarinParser() -> String {
|
||||
return (mgrPrefs.mandarinParserName + "_")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,535 @@
|
|||
// 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: - 構築狀態(State Building)
|
||||
func buildInputtingState() -> InputState.Inputting {
|
||||
// 觸發資料封裝更新,否則下文拿到的數據會是錯的。
|
||||
packageBufferStateMaterials()
|
||||
// 獲取封裝好的資料
|
||||
let composedText = getComposedText()
|
||||
let packagedCursorIndex = UInt(getPackagedCursorIndex())
|
||||
let resultOfBefore = getStrLocationResult(isAfter: false)
|
||||
let resultOfAfter = getStrLocationResult(isAfter: true)
|
||||
|
||||
// 初期化狀態
|
||||
let newState = InputState.Inputting(composingBuffer: composedText, cursorIndex: packagedCursorIndex)
|
||||
|
||||
// 組建提示文本
|
||||
var tooltip = ""
|
||||
if (resultOfBefore == "") && (resultOfAfter != "") {
|
||||
tooltip = String(format: NSLocalizedString("Cursor is after \"%@\".", comment: ""), resultOfAfter)
|
||||
}
|
||||
if (resultOfBefore != "") && (resultOfAfter == "") {
|
||||
tooltip = String(format: NSLocalizedString("Cursor is before \"%@\".", comment: ""), resultOfBefore)
|
||||
}
|
||||
if (resultOfBefore != "") && (resultOfAfter != "") {
|
||||
tooltip = String(
|
||||
format: NSLocalizedString("Cursor is between \"%@\" and \"%@\".", comment: ""),
|
||||
resultOfAfter, resultOfBefore)
|
||||
}
|
||||
|
||||
// 給新狀態安插配置好的提示文本、且送出新狀態
|
||||
newState.tooltip = tooltip
|
||||
return newState
|
||||
}
|
||||
|
||||
// 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,
|
||||
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()
|
||||
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()
|
||||
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
|
||||
}
|
||||
|
||||
// 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()
|
||||
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 _handleCtrlCommandEnterWithState(
|
||||
_ 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()
|
||||
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()
|
||||
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()
|
||||
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()
|
||||
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()
|
||||
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()
|
||||
stateCallback(inputting)
|
||||
} else {
|
||||
IME.prtDebugIntel("7045E6F3")
|
||||
errorCallback()
|
||||
stateCallback(state)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
|
@ -60,7 +60,7 @@ class ctlInputMethod: IMKInputController {
|
|||
// 所以才需要「currentKeyHandler」這個假 keyHandler。
|
||||
// 這個「currentKeyHandler」僅用來讓其他模組知道當前的輸入模式是什麼模式,除此之外別無屌用。
|
||||
static var currentKeyHandler: KeyHandler = KeyHandler()
|
||||
@objc static var currentInputMode = ""
|
||||
@objc static var currentInputMode = mgrPrefs.mostRecentInputMode
|
||||
|
||||
// MARK: - Keyboard Layout Specifier
|
||||
|
||||
|
@ -75,6 +75,9 @@ class ctlInputMethod: IMKInputController {
|
|||
override init!(server: IMKServer!, delegate: Any!, client inputClient: Any!) {
|
||||
super.init(server: server, delegate: delegate, client: inputClient)
|
||||
keyHandler.delegate = self
|
||||
// 下述兩行很有必要,否則輸入法會在手動重啟之後無法立刻生效。
|
||||
activateServer(inputClient)
|
||||
resetKeyHandler()
|
||||
}
|
||||
|
||||
// MARK: - KeyHandler Reset Command
|
||||
|
@ -95,7 +98,7 @@ class ctlInputMethod: IMKInputController {
|
|||
currentClient = client
|
||||
|
||||
keyHandler.clear()
|
||||
keyHandler.syncWithPreferences()
|
||||
keyHandler.ensurePhoneticParser()
|
||||
if let bundleCheckID = (client as? IMKTextInput)?.bundleIdentifier() {
|
||||
if bundleCheckID != Bundle.main.bundleIdentifier {
|
||||
// Override the keyboard layout to the basic one.
|
||||
|
@ -598,9 +601,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
|
|||
let selectedValue = state.candidates[Int(index)]
|
||||
keyHandler.fixNode(value: selectedValue)
|
||||
|
||||
guard let inputting = keyHandler.buildInputtingState() as? InputState.Inputting else {
|
||||
return
|
||||
}
|
||||
let inputting = keyHandler.buildInputtingState()
|
||||
|
||||
if mgrPrefs.useSCPCTypingMode {
|
||||
keyHandler.clear()
|
||||
|
|
|
@ -261,6 +261,7 @@ extension ctlInputMethod {
|
|||
}
|
||||
|
||||
@objc func selfTerminate(_ sender: Any?) {
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
NSApp.terminate(nil)
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ import Cocoa
|
|||
|
||||
struct UserDef {
|
||||
static let kIsDebugModeEnabled = "_DebugMode"
|
||||
static let kMostRecentInputMode = "MostRecentInputMode"
|
||||
static let kUserDataFolderSpecified = "UserDataFolderSpecified"
|
||||
static let kCheckUpdateAutomatically = "CheckUpdateAutomatically"
|
||||
static let kMandarinParser = "MandarinParser"
|
||||
|
@ -204,6 +205,7 @@ public class mgrPrefs: NSObject {
|
|||
static var allKeys: [String] {
|
||||
[
|
||||
UserDef.kIsDebugModeEnabled,
|
||||
UserDef.kMostRecentInputMode,
|
||||
UserDef.kUserDataFolderSpecified,
|
||||
UserDef.kMandarinParser,
|
||||
UserDef.kBasicKeyboardLayout,
|
||||
|
@ -238,6 +240,7 @@ public class mgrPrefs: NSObject {
|
|||
// MARK: - 既然 Preferences Module 的預設屬性不自動寫入 plist,那這邊就先寫入了。
|
||||
@objc public static func setMissingDefaults() {
|
||||
UserDefaults.standard.setDefault(mgrPrefs.isDebugModeEnabled, forKey: UserDef.kIsDebugModeEnabled)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.mostRecentInputMode, forKey: UserDef.kMostRecentInputMode)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.checkUpdateAutomatically, forKey: UserDef.kCheckUpdateAutomatically)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.showPageButtonsInCandidateWindow, forKey: UserDef.kShowPageButtonsInCandidateWindow)
|
||||
|
@ -271,6 +274,9 @@ public class mgrPrefs: NSObject {
|
|||
@UserDefault(key: UserDef.kIsDebugModeEnabled, defaultValue: false)
|
||||
@objc static var isDebugModeEnabled: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kMostRecentInputMode, defaultValue: "")
|
||||
@objc static var mostRecentInputMode: String
|
||||
|
||||
@UserDefault(key: UserDef.kCheckUpdateAutomatically, defaultValue: false)
|
||||
@objc static var checkUpdateAutomatically: Bool
|
||||
|
||||
|
|
|
@ -13,7 +13,9 @@
|
|||
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 */; };
|
||||
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 */; };
|
||||
|
@ -29,7 +31,9 @@
|
|||
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 */; };
|
||||
5B7BC4B027AFFBE800F66C24 /* frmPrefWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5B7BC4AE27AFFBE800F66C24 /* frmPrefWindow.xib */; };
|
||||
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 */; };
|
||||
|
@ -185,7 +189,9 @@
|
|||
5B2DB16E27AF6891006D874E /* data-cht.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "data-cht.txt"; path = "Data/data-cht.txt"; sourceTree = "<group>"; };
|
||||
5B2DB17127AF8771006D874E /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; name = Makefile; path = Data/Makefile; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
|
@ -206,8 +212,10 @@
|
|||
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>"; };
|
||||
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_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>"; };
|
||||
|
@ -324,7 +332,7 @@
|
|||
D4E33D8927A838CF006DB1CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
D4E33D8E27A838F0006DB1CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
D4E569DA27A34CC100AC2CEF /* KeyHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyHandler.h; sourceTree = "<group>"; };
|
||||
D4E569DB27A34CC100AC2CEF /* KeyHandler.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyHandler.mm; sourceTree = "<group>"; };
|
||||
D4E569DB27A34CC100AC2CEF /* KeyHandler.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyHandler.mm; sourceTree = "<group>"; usesTabs = 0; };
|
||||
D4F0BBDE279AF1AF0071253C /* ArchiveUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArchiveUtil.swift; sourceTree = "<group>"; };
|
||||
D4F0BBE0279AF8B30071253C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
D4F0BBE2279B08900071253C /* Chronosphere.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Chronosphere.h; sourceTree = "<group>"; };
|
||||
|
@ -423,6 +431,10 @@
|
|||
D461B791279DAC010070E734 /* InputState.swift */,
|
||||
D4E569DA27A34CC100AC2CEF /* KeyHandler.h */,
|
||||
D4E569DB27A34CC100AC2CEF /* KeyHandler.mm */,
|
||||
5B7F225C2808501000DDD3CB /* KeyHandler_HandleInput.swift */,
|
||||
5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */,
|
||||
5B61B0C9280BEFD4002E3CFA /* KeyHandler_Misc.swift */,
|
||||
5B3133BE280B229700A4A505 /* KeyHandler_States.swift */,
|
||||
D456576D279E4F7B00DF6BC9 /* KeyParser.swift */,
|
||||
6ACC3D3E27914F2400F1B140 /* KeyValueBlobReader.cpp */,
|
||||
6ACC3D3C27914AAB00F1B140 /* KeyValueBlobReader.h */,
|
||||
|
@ -1061,6 +1073,7 @@
|
|||
D427F76C278CA2B0004A2160 /* AppDelegate.swift in Sources */,
|
||||
5BA9FD4527FEF3C9002DE248 /* ToolbarItemStyleViewController.swift in Sources */,
|
||||
5BA9FD4127FEF3C8002DE248 /* PreferencesStyle.swift in Sources */,
|
||||
5B7F225D2808501000DDD3CB /* KeyHandler_HandleInput.swift in Sources */,
|
||||
5BA9FD1227FEDB6B002DE248 /* suiPrefPaneExperience.swift in Sources */,
|
||||
6ACC3D442793701600F1B140 /* ParselessPhraseDB.cpp in Sources */,
|
||||
D461B792279DAC010070E734 /* InputState.swift in Sources */,
|
||||
|
@ -1076,6 +1089,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 */,
|
||||
|
@ -1090,6 +1104,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 */,
|
||||
|
@ -1099,6 +1114,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 */,
|
||||
|
@ -1434,7 +1450,6 @@
|
|||
"$(OTHER_CFLAGS)",
|
||||
"-fcxx-modules",
|
||||
);
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
|
@ -1475,8 +1490,6 @@
|
|||
"$(OTHER_CFLAGS)",
|
||||
"-fcxx-modules",
|
||||
);
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue