KeyHandler // Swiftify: day 2.
This commit is contained in:
parent
5475a05241
commit
ab8ed8f73c
|
@ -50,10 +50,8 @@ extern InputMode imeModeNULL;
|
||||||
- (BOOL)handleInput:(keyParser *)input
|
- (BOOL)handleInput:(keyParser *)input
|
||||||
state:(InputState *)state
|
state:(InputState *)state
|
||||||
stateCallback:(void (^)(InputState *))stateCallback
|
stateCallback:(void (^)(InputState *))stateCallback
|
||||||
errorCallback:(void (^)(void))errorCallback
|
errorCallback:(void (^)(void))errorCallback NS_SWIFT_NAME(handle(input:state:stateCallback:errorCallback:));
|
||||||
NS_SWIFT_NAME(handle(input:state:stateCallback:errorCallback:));
|
|
||||||
|
|
||||||
- (void)ensurePhoneticParser;
|
|
||||||
- (void)fixNodeWithValue:(NSString *)value NS_SWIFT_NAME(fixNode(value:));
|
- (void)fixNodeWithValue:(NSString *)value NS_SWIFT_NAME(fixNode(value:));
|
||||||
- (void)clear;
|
- (void)clear;
|
||||||
|
|
||||||
|
@ -63,8 +61,40 @@ extern InputMode imeModeNULL;
|
||||||
@property(strong, nonatomic) InputMode inputMode;
|
@property(strong, nonatomic) InputMode inputMode;
|
||||||
@property(weak, nonatomic) id<KeyHandlerDelegate> delegate;
|
@property(weak, nonatomic) id<KeyHandlerDelegate> delegate;
|
||||||
|
|
||||||
|
// The following items need to be exposed to Swift:
|
||||||
|
- (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:));
|
||||||
|
- (BOOL)_handleMarkingState:(InputState *)state
|
||||||
|
input:(keyParser *)input
|
||||||
|
stateCallback:(void (^)(InputState *))stateCallback
|
||||||
|
errorCallback:(void (^)(void))errorCallback
|
||||||
|
NS_SWIFT_NAME(handleMarking(state:input:stateCallback:errorCallback:));
|
||||||
|
|
||||||
|
- (BOOL)checkWhetherToneMarkerConfirmsPhoneticReadingBuffer;
|
||||||
|
- (BOOL)chkKeyValidity:(UniChar)value;
|
||||||
- (BOOL)isPhoneticReadingBufferEmpty;
|
- (BOOL)isPhoneticReadingBufferEmpty;
|
||||||
|
- (NSString *)getCompositionFromPhoneticReadingBuffer;
|
||||||
|
- (NSString *)getSyllableCompositionFromPhoneticReadingBuffer;
|
||||||
|
- (void)clearPhoneticReadingBuffer;
|
||||||
|
- (void)combinePhoneticReadingBufferKey:(UniChar)charCode;
|
||||||
|
- (void)doBackSpaceToPhoneticReadingBuffer;
|
||||||
|
- (void)removeBuilderAndReset:(BOOL)shouldReset;
|
||||||
|
- (void)createNewBuilder;
|
||||||
|
- (void)setInputModesToLM:(BOOL)isCHS;
|
||||||
|
- (void)syncBaseLMPrefs;
|
||||||
|
- (void)ensurePhoneticParser;
|
||||||
|
- (BOOL)ifLangModelHasUnigramsForKey:(NSString *)reading;
|
||||||
|
- (void)insertReadingToBuilderAtCursor:(NSString *)reading;
|
||||||
- (BOOL)isPrintable:(UniChar)charCode;
|
- (BOOL)isPrintable:(UniChar)charCode;
|
||||||
|
- (void)dealWithOverrideModelSuggestions;
|
||||||
|
- (NSMutableArray *)getCandidatesArray;
|
||||||
|
- (NSInteger)getBuilderCursorIndex;
|
||||||
|
- (NSInteger)getBuilderLength;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
@ -198,9 +198,9 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot";
|
||||||
_walkedNodes.clear();
|
_walkedNodes.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (std::string)_currentMandarinParser
|
- (NSString *)_currentMandarinParser
|
||||||
{
|
{
|
||||||
return std::string(mgrPrefs.mandarinParserName.UTF8String) + std::string("_");
|
return [mgrPrefs.mandarinParserName stringByAppendingString:@"_"];
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Handling Input
|
// MARK: - Handling Input
|
||||||
|
@ -331,10 +331,10 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot";
|
||||||
if (composeReading)
|
if (composeReading)
|
||||||
{
|
{
|
||||||
// combine the reading
|
// combine the reading
|
||||||
std::string reading = [[self getSyllableCompositionFromPhoneticReadingBuffer] UTF8String];
|
NSString *reading = [self getSyllableCompositionFromPhoneticReadingBuffer];
|
||||||
|
|
||||||
// see if we have an unigram for this
|
// see if we have an unigram for this
|
||||||
if (!_languageModel->hasUnigramsForKey(reading))
|
if (![self ifLangModelHasUnigramsForKey:reading])
|
||||||
{
|
{
|
||||||
[IME prtDebugIntel:@"B49C0979"];
|
[IME prtDebugIntel:@"B49C0979"];
|
||||||
errorCallback();
|
errorCallback();
|
||||||
|
@ -344,25 +344,13 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot";
|
||||||
}
|
}
|
||||||
|
|
||||||
// and insert it into the lattice
|
// and insert it into the lattice
|
||||||
_builder->insertReadingAtCursor(reading);
|
[self insertReadingToBuilderAtCursor:reading];
|
||||||
|
|
||||||
// then walk the lattice
|
// then walk the lattice
|
||||||
NSString *poppedText = [self _popOverflowComposingTextAndWalk];
|
NSString *poppedText = [self _popOverflowComposingTextAndWalk];
|
||||||
|
|
||||||
// get user override model suggestion
|
// get user override model suggestion
|
||||||
std::string overrideValue = (mgrPrefs.useSCPCTypingMode)
|
[self dealWithOverrideModelSuggestions];
|
||||||
? ""
|
|
||||||
: _userOverrideModel->suggest(_walkedNodes, _builder->cursorIndex(),
|
|
||||||
[[NSDate date] timeIntervalSince1970]);
|
|
||||||
|
|
||||||
if (!overrideValue.empty())
|
|
||||||
{
|
|
||||||
size_t cursorIndex = [self _actualCandidateCursorIndex];
|
|
||||||
std::vector<Gramambular::NodeAnchor> nodes = _builder->grid().nodesCrossingOrEndingAt(cursorIndex);
|
|
||||||
double highestScore = FindHighestScore(nodes, kEpsilon);
|
|
||||||
_builder->grid().overrideNodeScoreForSelectedCandidate(cursorIndex, overrideValue,
|
|
||||||
static_cast<float>(highestScore));
|
|
||||||
}
|
|
||||||
|
|
||||||
// then update the text
|
// then update the text
|
||||||
[self clearPhoneticReadingBuffer];
|
[self clearPhoneticReadingBuffer];
|
||||||
|
@ -420,7 +408,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot";
|
||||||
// if the spacebar is NOT set to be a selection key
|
// if the spacebar is NOT set to be a selection key
|
||||||
if ([input isShiftHold] || !mgrPrefs.chooseCandidateUsingSpace)
|
if ([input isShiftHold] || !mgrPrefs.chooseCandidateUsingSpace)
|
||||||
{
|
{
|
||||||
if (_builder->cursorIndex() >= _builder->length())
|
if ([self getBuilderCursorIndex] >= [self getBuilderLength])
|
||||||
{
|
{
|
||||||
NSString *composingBuffer = [(InputStateNotEmpty *)state composingBuffer];
|
NSString *composingBuffer = [(InputStateNotEmpty *)state composingBuffer];
|
||||||
if (composingBuffer.length)
|
if (composingBuffer.length)
|
||||||
|
@ -435,9 +423,9 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot";
|
||||||
InputStateEmpty *empty = [[InputStateEmpty alloc] init];
|
InputStateEmpty *empty = [[InputStateEmpty alloc] init];
|
||||||
stateCallback(empty);
|
stateCallback(empty);
|
||||||
}
|
}
|
||||||
else if (_languageModel->hasUnigramsForKey(" "))
|
else if ([self ifLangModelHasUnigramsForKey:@" "])
|
||||||
{
|
{
|
||||||
_builder->insertReadingAtCursor(" ");
|
[self insertReadingToBuilderAtCursor:@" "];
|
||||||
NSString *poppedText = [self _popOverflowComposingTextAndWalk];
|
NSString *poppedText = [self _popOverflowComposingTextAndWalk];
|
||||||
InputStateInputting *inputting = (InputStateInputting *)[self buildInputtingState];
|
InputStateInputting *inputting = (InputStateInputting *)[self buildInputtingState];
|
||||||
inputting.poppedText = poppedText;
|
inputting.poppedText = poppedText;
|
||||||
|
@ -508,11 +496,11 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot";
|
||||||
{
|
{
|
||||||
if (![input isOptionHold])
|
if (![input isOptionHold])
|
||||||
{
|
{
|
||||||
if (_languageModel->hasUnigramsForKey("_punctuation_list"))
|
if ([self ifLangModelHasUnigramsForKey:@"_punctuation_list "])
|
||||||
{
|
{
|
||||||
if ([self isPhoneticReadingBufferEmpty])
|
if ([self isPhoneticReadingBufferEmpty])
|
||||||
{
|
{
|
||||||
_builder->insertReadingAtCursor(string("_punctuation_list"));
|
[self insertReadingToBuilderAtCursor:@"_punctuation_list"];
|
||||||
NSString *poppedText = [self _popOverflowComposingTextAndWalk];
|
NSString *poppedText = [self _popOverflowComposingTextAndWalk];
|
||||||
InputStateInputting *inputting = (InputStateInputting *)[self buildInputtingState];
|
InputStateInputting *inputting = (InputStateInputting *)[self buildInputtingState];
|
||||||
inputting.poppedText = poppedText;
|
inputting.poppedText = poppedText;
|
||||||
|
@ -546,19 +534,21 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot";
|
||||||
// MARK: Punctuation
|
// MARK: Punctuation
|
||||||
// if nothing is matched, see if it's a punctuation key for current layout.
|
// if nothing is matched, see if it's a punctuation key for current layout.
|
||||||
|
|
||||||
std::string punctuationNamePrefix;
|
NSString *punctuationNamePrefix;
|
||||||
|
|
||||||
if ([input isOptionHold])
|
if ([input isOptionHold])
|
||||||
punctuationNamePrefix = std::string("_alt_punctuation_");
|
punctuationNamePrefix = @"_alt_punctuation_";
|
||||||
else if ([input isControlHold])
|
else if ([input isControlHold])
|
||||||
punctuationNamePrefix = std::string("_ctrl_punctuation_");
|
punctuationNamePrefix = @"_ctrl_punctuation_";
|
||||||
else if (mgrPrefs.halfWidthPunctuationEnabled)
|
else if (mgrPrefs.halfWidthPunctuationEnabled)
|
||||||
punctuationNamePrefix = std::string("_half_punctuation_");
|
punctuationNamePrefix = @"_half_punctuation_";
|
||||||
else
|
else
|
||||||
punctuationNamePrefix = std::string("_punctuation_");
|
punctuationNamePrefix = @"_punctuation_";
|
||||||
|
|
||||||
std::string parser = [self _currentMandarinParser];
|
NSString *parser = [self _currentMandarinParser];
|
||||||
std::string customPunctuation = punctuationNamePrefix + parser + std::string(1, (char)charCode);
|
NSArray *arrCustomPunctuations =
|
||||||
|
@[ punctuationNamePrefix, parser, [NSString stringWithFormat:@"%c", (char)charCode] ];
|
||||||
|
NSString *customPunctuation = [arrCustomPunctuations componentsJoinedByString:@""];
|
||||||
if ([self _handlePunctuation:customPunctuation
|
if ([self _handlePunctuation:customPunctuation
|
||||||
state:state
|
state:state
|
||||||
usingVerticalMode:input.useVerticalMode
|
usingVerticalMode:input.useVerticalMode
|
||||||
|
@ -567,7 +557,9 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot";
|
||||||
return YES;
|
return YES;
|
||||||
|
|
||||||
// if nothing is matched, see if it's a punctuation key.
|
// if nothing is matched, see if it's a punctuation key.
|
||||||
std::string punctuation = punctuationNamePrefix + std::string(1, (char)charCode);
|
NSArray *arrPunctuations = @[ punctuationNamePrefix, [NSString stringWithFormat:@"%c", (char)charCode] ];
|
||||||
|
NSString *punctuation = [arrPunctuations componentsJoinedByString:@""];
|
||||||
|
|
||||||
if ([self _handlePunctuation:punctuation
|
if ([self _handlePunctuation:punctuation
|
||||||
state:state
|
state:state
|
||||||
usingVerticalMode:input.useVerticalMode
|
usingVerticalMode:input.useVerticalMode
|
||||||
|
@ -575,12 +567,10 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot";
|
||||||
errorCallback:errorCallback])
|
errorCallback:errorCallback])
|
||||||
return YES;
|
return YES;
|
||||||
|
|
||||||
// Lukhnos 這裡的處理反而會使得 Apple 倚天注音動態鍵盤佈局「敲不了半形大寫英文」的缺點曝露無疑,所以注釋掉。
|
// 這裡不使用小麥注音 2.2. 的組字區處理方式,而是直接由詞庫負責。
|
||||||
// 至於他試圖用這種處理來解決的上游 UPR293
|
if ([input isUpperCaseASCIILetterKey])
|
||||||
// 的問題,其實針對詞庫檔案的排序做點手腳就可以解決。威注音本來也就是這麼做的。
|
|
||||||
if (/*[state isKindOfClass:[InputStateNotEmpty class]] && */ [input isUpperCaseASCIILetterKey])
|
|
||||||
{
|
{
|
||||||
std::string letter = std::string("_letter_") + std::string(1, (char)charCode);
|
NSString *letter = [NSString stringWithFormat:@"%@%c", @"_letter_", (char)charCode];
|
||||||
if ([self _handlePunctuation:letter
|
if ([self _handlePunctuation:letter
|
||||||
state:state
|
state:state
|
||||||
usingVerticalMode:input.useVerticalMode
|
usingVerticalMode:input.useVerticalMode
|
||||||
|
@ -957,19 +947,19 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot";
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)_handlePunctuation:(std::string)customPunctuation
|
- (BOOL)_handlePunctuation:(NSString *)customPunctuation
|
||||||
state:(InputState *)state
|
state:(InputState *)state
|
||||||
usingVerticalMode:(BOOL)useVerticalMode
|
usingVerticalMode:(BOOL)useVerticalMode
|
||||||
stateCallback:(void (^)(InputState *))stateCallback
|
stateCallback:(void (^)(InputState *))stateCallback
|
||||||
errorCallback:(void (^)(void))errorCallback
|
errorCallback:(void (^)(void))errorCallback
|
||||||
{
|
{
|
||||||
if (!_languageModel->hasUnigramsForKey(customPunctuation))
|
if (![self ifLangModelHasUnigramsForKey:customPunctuation])
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
NSString *poppedText;
|
NSString *poppedText;
|
||||||
if ([self isPhoneticReadingBufferEmpty])
|
if ([self isPhoneticReadingBufferEmpty])
|
||||||
{
|
{
|
||||||
_builder->insertReadingAtCursor(customPunctuation);
|
[self insertReadingToBuilderAtCursor:customPunctuation];
|
||||||
poppedText = [self _popOverflowComposingTextAndWalk];
|
poppedText = [self _popOverflowComposingTextAndWalk];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1389,28 +1379,34 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot";
|
||||||
|
|
||||||
if (mgrPrefs.useSCPCTypingMode)
|
if (mgrPrefs.useSCPCTypingMode)
|
||||||
{
|
{
|
||||||
std::string punctuationNamePrefix;
|
NSString *punctuationNamePrefix;
|
||||||
if ([input isOptionHold])
|
|
||||||
punctuationNamePrefix = std::string("_alt_punctuation_");
|
|
||||||
else if ([input isControlHold])
|
|
||||||
punctuationNamePrefix = std::string("_ctrl_punctuation_");
|
|
||||||
else if (mgrPrefs.halfWidthPunctuationEnabled)
|
|
||||||
punctuationNamePrefix = std::string("_half_punctuation_");
|
|
||||||
else
|
|
||||||
punctuationNamePrefix = std::string("_punctuation_");
|
|
||||||
|
|
||||||
std::string parser = [self _currentMandarinParser];
|
if ([input isOptionHold])
|
||||||
std::string customPunctuation = punctuationNamePrefix + parser + std::string(1, (char)charCode);
|
punctuationNamePrefix = @"_alt_punctuation_";
|
||||||
std::string punctuation = punctuationNamePrefix + std::string(1, (char)charCode);
|
else if ([input isControlHold])
|
||||||
|
punctuationNamePrefix = @"_ctrl_punctuation_";
|
||||||
|
else if (mgrPrefs.halfWidthPunctuationEnabled)
|
||||||
|
punctuationNamePrefix = @"_half_punctuation_";
|
||||||
|
else
|
||||||
|
punctuationNamePrefix = @"_punctuation_";
|
||||||
|
|
||||||
|
NSString *parser = [self _currentMandarinParser];
|
||||||
|
|
||||||
|
NSArray *arrCustomPunctuations =
|
||||||
|
@[ punctuationNamePrefix, parser, [NSString stringWithFormat:@"%c", (char)charCode] ];
|
||||||
|
NSString *customPunctuation = [arrCustomPunctuations componentsJoinedByString:@""];
|
||||||
|
|
||||||
|
NSArray *arrPunctuations = @[ punctuationNamePrefix, [NSString stringWithFormat:@"%c", (char)charCode] ];
|
||||||
|
NSString *punctuation = [arrPunctuations componentsJoinedByString:@""];
|
||||||
|
|
||||||
BOOL shouldAutoSelectCandidate = [self chkKeyValidity:charCode] ||
|
BOOL shouldAutoSelectCandidate = [self chkKeyValidity:charCode] ||
|
||||||
_languageModel->hasUnigramsForKey(customPunctuation) ||
|
[self ifLangModelHasUnigramsForKey:customPunctuation] ||
|
||||||
_languageModel->hasUnigramsForKey(punctuation);
|
[self ifLangModelHasUnigramsForKey:punctuation];
|
||||||
|
|
||||||
if (!shouldAutoSelectCandidate && [input isUpperCaseASCIILetterKey])
|
if (!shouldAutoSelectCandidate && [input isUpperCaseASCIILetterKey])
|
||||||
{
|
{
|
||||||
std::string letter = std::string("_letter_") + std::string(1, (char)charCode);
|
NSString *letter = [NSString stringWithFormat:@"%@%c", @"_letter_", (char)charCode];
|
||||||
if (_languageModel->hasUnigramsForKey(letter))
|
if ([self ifLangModelHasUnigramsForKey:letter])
|
||||||
shouldAutoSelectCandidate = YES;
|
shouldAutoSelectCandidate = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1598,34 +1594,6 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot";
|
||||||
return poppedText;
|
return poppedText;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (InputStateChoosingCandidate *)_buildCandidateState:(InputStateNotEmpty *)currentState
|
|
||||||
useVerticalMode:(BOOL)useVerticalMode
|
|
||||||
{
|
|
||||||
NSMutableArray *candidatesArray = [[NSMutableArray alloc] init];
|
|
||||||
|
|
||||||
size_t cursorIndex = [self _actualCandidateCursorIndex];
|
|
||||||
std::vector<Gramambular::NodeAnchor> nodes = _builder->grid().nodesCrossingOrEndingAt(cursorIndex);
|
|
||||||
|
|
||||||
// sort the nodes, so that longer nodes (representing longer phrases) are placed at the top of the candidate list
|
|
||||||
stable_sort(nodes.begin(), nodes.end(), NodeAnchorDescendingSorter());
|
|
||||||
|
|
||||||
// then use the C++ trick to retrieve the candidates for each node at/crossing the cursor
|
|
||||||
for (std::vector<Gramambular::NodeAnchor>::iterator ni = nodes.begin(), ne = nodes.end(); ni != ne; ++ni)
|
|
||||||
{
|
|
||||||
const std::vector<Gramambular::KeyValuePair> &candidates = (*ni).node->candidates();
|
|
||||||
for (std::vector<Gramambular::KeyValuePair>::const_iterator ci = candidates.begin(), ce = candidates.end();
|
|
||||||
ci != ce; ++ci)
|
|
||||||
[candidatesArray addObject:[NSString stringWithUTF8String:(*ci).value.c_str()]];
|
|
||||||
}
|
|
||||||
|
|
||||||
InputStateChoosingCandidate *state =
|
|
||||||
[[InputStateChoosingCandidate alloc] initWithComposingBuffer:currentState.composingBuffer
|
|
||||||
cursorIndex:currentState.cursorIndex
|
|
||||||
candidates:candidatesArray
|
|
||||||
useVerticalMode:useVerticalMode];
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NON-SWIFTIFIABLE
|
// NON-SWIFTIFIABLE
|
||||||
- (size_t)_actualCandidateCursorIndex
|
- (size_t)_actualCandidateCursorIndex
|
||||||
{
|
{
|
||||||
|
@ -1787,7 +1755,69 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - 威注音認為有必要單獨拿出來處理的部分。
|
// ----
|
||||||
|
|
||||||
|
- (BOOL)ifLangModelHasUnigramsForKey:(NSString *)reading
|
||||||
|
{
|
||||||
|
return _languageModel->hasUnigramsForKey((std::string)[reading UTF8String]);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)insertReadingToBuilderAtCursor:(NSString *)reading
|
||||||
|
{
|
||||||
|
_builder->insertReadingAtCursor((std::string)[reading UTF8String]);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealWithOverrideModelSuggestions
|
||||||
|
{
|
||||||
|
// 這一整段都太 C++ 且只出現一次,就整個端過來了。
|
||||||
|
// 拆開封裝的話,只會把問題搞得更麻煩而已。
|
||||||
|
std::string overrideValue =
|
||||||
|
(mgrPrefs.useSCPCTypingMode)
|
||||||
|
? ""
|
||||||
|
: _userOverrideModel->suggest(_walkedNodes, _builder->cursorIndex(), [[NSDate date] timeIntervalSince1970]);
|
||||||
|
|
||||||
|
if (!overrideValue.empty())
|
||||||
|
{
|
||||||
|
size_t cursorIndex = [self _actualCandidateCursorIndex];
|
||||||
|
std::vector<Gramambular::NodeAnchor> nodes = _builder->grid().nodesCrossingOrEndingAt(cursorIndex);
|
||||||
|
double highestScore = FindHighestScore(nodes, kEpsilon);
|
||||||
|
_builder->grid().overrideNodeScoreForSelectedCandidate(cursorIndex, overrideValue,
|
||||||
|
static_cast<float>(highestScore));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)getBuilderCursorIndex
|
||||||
|
{
|
||||||
|
return _builder->cursorIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)getBuilderLength
|
||||||
|
{
|
||||||
|
return _builder->length();
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSMutableArray *)getCandidatesArray
|
||||||
|
{
|
||||||
|
NSMutableArray<NSString *> *candidatesArray = [[NSMutableArray alloc] init];
|
||||||
|
|
||||||
|
size_t cursorIndex = [self _actualCandidateCursorIndex];
|
||||||
|
std::vector<Gramambular::NodeAnchor> nodes = _builder->grid().nodesCrossingOrEndingAt(cursorIndex);
|
||||||
|
|
||||||
|
// sort the nodes, so that longer nodes (representing longer phrases) are placed at the top of the candidate list
|
||||||
|
stable_sort(nodes.begin(), nodes.end(), NodeAnchorDescendingSorter());
|
||||||
|
|
||||||
|
// then use the C++ trick to retrieve the candidates for each node at/crossing the cursor
|
||||||
|
for (std::vector<Gramambular::NodeAnchor>::iterator ni = nodes.begin(), ne = nodes.end(); ni != ne; ++ni)
|
||||||
|
{
|
||||||
|
const std::vector<Gramambular::KeyValuePair> &candidates = (*ni).node->candidates();
|
||||||
|
for (std::vector<Gramambular::KeyValuePair>::const_iterator ci = candidates.begin(), ce = candidates.end();
|
||||||
|
ci != ce; ++ci)
|
||||||
|
[candidatesArray addObject:[NSString stringWithUTF8String:(*ci).value.c_str()]];
|
||||||
|
}
|
||||||
|
return candidatesArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - 威注音認為有必要單獨拿出來處理的部分,交給 Swift 則有些困難。
|
||||||
|
|
||||||
- (BOOL)isPrintable:(UniChar)charCode
|
- (BOOL)isPrintable:(UniChar)charCode
|
||||||
{
|
{
|
||||||
|
|
|
@ -29,13 +29,15 @@ import Cocoa
|
||||||
@objc extension KeyHandler {
|
@objc extension KeyHandler {
|
||||||
func handleInputSwift(
|
func handleInputSwift(
|
||||||
input: keyParser,
|
input: keyParser,
|
||||||
state: InputState,
|
state inState: InputState,
|
||||||
stateCallback: @escaping (InputState) -> Void,
|
stateCallback: @escaping (InputState) -> Void,
|
||||||
errorCallback: @escaping () -> Void
|
errorCallback: @escaping () -> Void
|
||||||
) -> Bool {
|
) -> Bool {
|
||||||
let charCode: UniChar = input.charCode
|
let charCode: UniChar = input.charCode
|
||||||
// let emacsKey: vChewingEmacsKey = input.emacsKey
|
var state = inState // Turn this incoming constant to variable.
|
||||||
|
let emacsKey: vChewingEmacsKey = input.emacsKey
|
||||||
let inputText: String = input.inputText ?? ""
|
let inputText: String = input.inputText ?? ""
|
||||||
|
let emptyState = InputState.Empty()
|
||||||
|
|
||||||
// Ignore the input if its inputText is empty.
|
// Ignore the input if its inputText is empty.
|
||||||
// Reason: such inputs may be functional key combinations.
|
// Reason: such inputs may be functional key combinations.
|
||||||
|
@ -62,7 +64,6 @@ import Cocoa
|
||||||
} else if input.isCapsLockOn {
|
} else if input.isCapsLockOn {
|
||||||
// Process all possible combination, we hope.
|
// Process all possible combination, we hope.
|
||||||
clear()
|
clear()
|
||||||
let emptyState = InputState.Empty()
|
|
||||||
stateCallback(emptyState)
|
stateCallback(emptyState)
|
||||||
|
|
||||||
// When shift is pressed, don't do further processing...
|
// When shift is pressed, don't do further processing...
|
||||||
|
@ -91,7 +92,6 @@ import Cocoa
|
||||||
&& !input.isUp && !input.isSpace && isPrintable(charCode)
|
&& !input.isUp && !input.isSpace && isPrintable(charCode)
|
||||||
{
|
{
|
||||||
clear()
|
clear()
|
||||||
let emptyState = InputState.Empty()
|
|
||||||
stateCallback(emptyState)
|
stateCallback(emptyState)
|
||||||
let committing = InputState.Committing(poppedText: inputText.lowercased())
|
let committing = InputState.Committing(poppedText: inputText.lowercased())
|
||||||
stateCallback(committing)
|
stateCallback(committing)
|
||||||
|
@ -100,6 +100,161 @@ import Cocoa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Handle Candidates.
|
||||||
|
if state is InputState.ChoosingCandidate {
|
||||||
|
return handleCandidate(
|
||||||
|
state: 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)
|
||||||
|
if result {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
stateCallback(emptyState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Handle Marking.
|
||||||
|
if state is InputState.Marking {
|
||||||
|
let marking = state as! InputState.Marking
|
||||||
|
|
||||||
|
if handleMarking(
|
||||||
|
state: state as! InputState.Marking, input: input, stateCallback: stateCallback,
|
||||||
|
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() as! InputState.Inputting
|
||||||
|
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() as! InputState.Inputting
|
||||||
|
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() as! InputState.Inputting
|
||||||
|
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 spacebar is NOT set to be a selection key
|
||||||
|
if input.isShiftHold || !mgrPrefs.chooseCandidateUsingSpace {
|
||||||
|
if getBuilderCursorIndex() >= getBuilderLength() {
|
||||||
|
let composingBuffer = (state as! InputState.NotEmpty).composingBuffer
|
||||||
|
if (composingBuffer.count) != 0 {
|
||||||
|
let 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() as! InputState.Inputting
|
||||||
|
inputting.poppedText = poppedText
|
||||||
|
stateCallback(inputting)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let choosingCandidates = _buildCandidateState(
|
||||||
|
state as! InputState.NotEmpty,
|
||||||
|
useVerticalMode: input.useVerticalMode)
|
||||||
|
stateCallback(choosingCandidates)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Function Keys.
|
||||||
|
|
||||||
|
// MARK: Esc
|
||||||
|
|
||||||
// MARK: - Still Nothing.
|
// MARK: - Still Nothing.
|
||||||
// Still nothing? Then we update the composing buffer.
|
// Still nothing? Then we update the composing buffer.
|
||||||
// Note that some app has strange behavior if we don't do this,
|
// Note that some app has strange behavior if we don't do this,
|
||||||
|
@ -118,3 +273,21 @@ import Cocoa
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - State managements.
|
||||||
|
@objc extension KeyHandler {
|
||||||
|
func _buildCandidateState(
|
||||||
|
_ currentState: InputState.NotEmpty,
|
||||||
|
useVerticalMode: Bool
|
||||||
|
) -> InputState.ChoosingCandidate {
|
||||||
|
let candidatesArray = getCandidatesArray()
|
||||||
|
|
||||||
|
let state = InputState.ChoosingCandidate(
|
||||||
|
composingBuffer: currentState.composingBuffer,
|
||||||
|
cursorIndex: currentState.cursorIndex,
|
||||||
|
candidates: candidatesArray as! [String],
|
||||||
|
useVerticalMode: useVerticalMode)
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue