diff --git a/Source/Modules/ControllerModules/KeyHandler.h b/Source/Modules/ControllerModules/KeyHandler.h index 48edd962..69ff592a 100644 --- a/Source/Modules/ControllerModules/KeyHandler.h +++ b/Source/Modules/ControllerModules/KeyHandler.h @@ -50,10 +50,8 @@ extern InputMode imeModeNULL; - (BOOL)handleInput:(keyParser *)input state:(InputState *)state stateCallback:(void (^)(InputState *))stateCallback - errorCallback:(void (^)(void))errorCallback - NS_SWIFT_NAME(handle(input:state:stateCallback:errorCallback:)); + errorCallback:(void (^)(void))errorCallback NS_SWIFT_NAME(handle(input:state:stateCallback:errorCallback:)); -- (void)ensurePhoneticParser; - (void)fixNodeWithValue:(NSString *)value NS_SWIFT_NAME(fixNode(value:)); - (void)clear; @@ -63,8 +61,40 @@ extern InputMode imeModeNULL; @property(strong, nonatomic) InputMode inputMode; @property(weak, nonatomic) id 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; +- (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; +- (void)dealWithOverrideModelSuggestions; +- (NSMutableArray *)getCandidatesArray; +- (NSInteger)getBuilderCursorIndex; +- (NSInteger)getBuilderLength; @end diff --git a/Source/Modules/ControllerModules/KeyHandler.mm b/Source/Modules/ControllerModules/KeyHandler.mm index 56ad7b67..8c73f829 100644 --- a/Source/Modules/ControllerModules/KeyHandler.mm +++ b/Source/Modules/ControllerModules/KeyHandler.mm @@ -198,9 +198,9 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot"; _walkedNodes.clear(); } -- (std::string)_currentMandarinParser +- (NSString *)_currentMandarinParser { - return std::string(mgrPrefs.mandarinParserName.UTF8String) + std::string("_"); + return [mgrPrefs.mandarinParserName stringByAppendingString:@"_"]; } // MARK: - Handling Input @@ -331,10 +331,10 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot"; if (composeReading) { // combine the reading - std::string reading = [[self getSyllableCompositionFromPhoneticReadingBuffer] UTF8String]; + NSString *reading = [self getSyllableCompositionFromPhoneticReadingBuffer]; // see if we have an unigram for this - if (!_languageModel->hasUnigramsForKey(reading)) + if (![self ifLangModelHasUnigramsForKey:reading]) { [IME prtDebugIntel:@"B49C0979"]; errorCallback(); @@ -344,25 +344,13 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot"; } // and insert it into the lattice - _builder->insertReadingAtCursor(reading); + [self insertReadingToBuilderAtCursor:reading]; // then walk the lattice NSString *poppedText = [self _popOverflowComposingTextAndWalk]; // get user override model suggestion - std::string overrideValue = (mgrPrefs.useSCPCTypingMode) - ? "" - : _userOverrideModel->suggest(_walkedNodes, _builder->cursorIndex(), - [[NSDate date] timeIntervalSince1970]); - - if (!overrideValue.empty()) - { - size_t cursorIndex = [self _actualCandidateCursorIndex]; - std::vector nodes = _builder->grid().nodesCrossingOrEndingAt(cursorIndex); - double highestScore = FindHighestScore(nodes, kEpsilon); - _builder->grid().overrideNodeScoreForSelectedCandidate(cursorIndex, overrideValue, - static_cast(highestScore)); - } + [self dealWithOverrideModelSuggestions]; // then update the text [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 ([input isShiftHold] || !mgrPrefs.chooseCandidateUsingSpace) { - if (_builder->cursorIndex() >= _builder->length()) + if ([self getBuilderCursorIndex] >= [self getBuilderLength]) { NSString *composingBuffer = [(InputStateNotEmpty *)state composingBuffer]; if (composingBuffer.length) @@ -435,9 +423,9 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot"; InputStateEmpty *empty = [[InputStateEmpty alloc] init]; stateCallback(empty); } - else if (_languageModel->hasUnigramsForKey(" ")) + else if ([self ifLangModelHasUnigramsForKey:@" "]) { - _builder->insertReadingAtCursor(" "); + [self insertReadingToBuilderAtCursor:@" "]; NSString *poppedText = [self _popOverflowComposingTextAndWalk]; InputStateInputting *inputting = (InputStateInputting *)[self buildInputtingState]; inputting.poppedText = poppedText; @@ -508,11 +496,11 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot"; { if (![input isOptionHold]) { - if (_languageModel->hasUnigramsForKey("_punctuation_list")) + if ([self ifLangModelHasUnigramsForKey:@"_punctuation_list "]) { if ([self isPhoneticReadingBufferEmpty]) { - _builder->insertReadingAtCursor(string("_punctuation_list")); + [self insertReadingToBuilderAtCursor:@"_punctuation_list"]; NSString *poppedText = [self _popOverflowComposingTextAndWalk]; InputStateInputting *inputting = (InputStateInputting *)[self buildInputtingState]; inputting.poppedText = poppedText; @@ -546,19 +534,21 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot"; // MARK: Punctuation // if nothing is matched, see if it's a punctuation key for current layout. - std::string punctuationNamePrefix; + NSString *punctuationNamePrefix; if ([input isOptionHold]) - punctuationNamePrefix = std::string("_alt_punctuation_"); + punctuationNamePrefix = @"_alt_punctuation_"; else if ([input isControlHold]) - punctuationNamePrefix = std::string("_ctrl_punctuation_"); + punctuationNamePrefix = @"_ctrl_punctuation_"; else if (mgrPrefs.halfWidthPunctuationEnabled) - punctuationNamePrefix = std::string("_half_punctuation_"); + punctuationNamePrefix = @"_half_punctuation_"; else - punctuationNamePrefix = std::string("_punctuation_"); + punctuationNamePrefix = @"_punctuation_"; - std::string parser = [self _currentMandarinParser]; - std::string customPunctuation = punctuationNamePrefix + parser + std::string(1, (char)charCode); + NSString *parser = [self _currentMandarinParser]; + NSArray *arrCustomPunctuations = + @[ punctuationNamePrefix, parser, [NSString stringWithFormat:@"%c", (char)charCode] ]; + NSString *customPunctuation = [arrCustomPunctuations componentsJoinedByString:@""]; if ([self _handlePunctuation:customPunctuation state:state usingVerticalMode:input.useVerticalMode @@ -567,7 +557,9 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot"; return YES; // 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 state:state usingVerticalMode:input.useVerticalMode @@ -575,12 +567,10 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot"; errorCallback:errorCallback]) return YES; - // Lukhnos 這裡的處理反而會使得 Apple 倚天注音動態鍵盤佈局「敲不了半形大寫英文」的缺點曝露無疑,所以注釋掉。 - // 至於他試圖用這種處理來解決的上游 UPR293 - // 的問題,其實針對詞庫檔案的排序做點手腳就可以解決。威注音本來也就是這麼做的。 - if (/*[state isKindOfClass:[InputStateNotEmpty class]] && */ [input isUpperCaseASCIILetterKey]) + // 這裡不使用小麥注音 2.2. 的組字區處理方式,而是直接由詞庫負責。 + if ([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 state:state usingVerticalMode:input.useVerticalMode @@ -957,19 +947,19 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot"; return YES; } -- (BOOL)_handlePunctuation:(std::string)customPunctuation +- (BOOL)_handlePunctuation:(NSString *)customPunctuation state:(InputState *)state usingVerticalMode:(BOOL)useVerticalMode stateCallback:(void (^)(InputState *))stateCallback errorCallback:(void (^)(void))errorCallback { - if (!_languageModel->hasUnigramsForKey(customPunctuation)) + if (![self ifLangModelHasUnigramsForKey:customPunctuation]) return NO; NSString *poppedText; if ([self isPhoneticReadingBufferEmpty]) { - _builder->insertReadingAtCursor(customPunctuation); + [self insertReadingToBuilderAtCursor:customPunctuation]; poppedText = [self _popOverflowComposingTextAndWalk]; } else @@ -1389,28 +1379,34 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot"; if (mgrPrefs.useSCPCTypingMode) { - std::string 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_"); + NSString *punctuationNamePrefix; - std::string parser = [self _currentMandarinParser]; - std::string customPunctuation = punctuationNamePrefix + parser + std::string(1, (char)charCode); - std::string punctuation = punctuationNamePrefix + std::string(1, (char)charCode); + if ([input isOptionHold]) + punctuationNamePrefix = @"_alt_punctuation_"; + 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] || - _languageModel->hasUnigramsForKey(customPunctuation) || - _languageModel->hasUnigramsForKey(punctuation); + [self ifLangModelHasUnigramsForKey:customPunctuation] || + [self ifLangModelHasUnigramsForKey:punctuation]; if (!shouldAutoSelectCandidate && [input isUpperCaseASCIILetterKey]) { - std::string letter = std::string("_letter_") + std::string(1, (char)charCode); - if (_languageModel->hasUnigramsForKey(letter)) + NSString *letter = [NSString stringWithFormat:@"%@%c", @"_letter_", (char)charCode]; + if ([self ifLangModelHasUnigramsForKey:letter]) shouldAutoSelectCandidate = YES; } @@ -1598,34 +1594,6 @@ static NSString *const kGraphVizOutputfile = @"/tmp/vChewing-visualization.dot"; return poppedText; } -- (InputStateChoosingCandidate *)_buildCandidateState:(InputStateNotEmpty *)currentState - useVerticalMode:(BOOL)useVerticalMode -{ - NSMutableArray *candidatesArray = [[NSMutableArray alloc] init]; - - size_t cursorIndex = [self _actualCandidateCursorIndex]; - std::vector 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::iterator ni = nodes.begin(), ne = nodes.end(); ni != ne; ++ni) - { - const std::vector &candidates = (*ni).node->candidates(); - for (std::vector::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 - (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 nodes = _builder->grid().nodesCrossingOrEndingAt(cursorIndex); + double highestScore = FindHighestScore(nodes, kEpsilon); + _builder->grid().overrideNodeScoreForSelectedCandidate(cursorIndex, overrideValue, + static_cast(highestScore)); + } +} + +- (NSInteger)getBuilderCursorIndex +{ + return _builder->cursorIndex(); +} + +- (NSInteger)getBuilderLength +{ + return _builder->length(); +} + +- (NSMutableArray *)getCandidatesArray +{ + NSMutableArray *candidatesArray = [[NSMutableArray alloc] init]; + + size_t cursorIndex = [self _actualCandidateCursorIndex]; + std::vector 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::iterator ni = nodes.begin(), ne = nodes.end(); ni != ne; ++ni) + { + const std::vector &candidates = (*ni).node->candidates(); + for (std::vector::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 { diff --git a/Source/Modules/ControllerModules/KeyHandler.swift b/Source/Modules/ControllerModules/KeyHandler.swift index 5d80b77d..8f2004c5 100644 --- a/Source/Modules/ControllerModules/KeyHandler.swift +++ b/Source/Modules/ControllerModules/KeyHandler.swift @@ -29,13 +29,15 @@ import Cocoa @objc extension KeyHandler { func handleInputSwift( input: keyParser, - state: InputState, + state inState: InputState, stateCallback: @escaping (InputState) -> Void, errorCallback: @escaping () -> Void ) -> Bool { 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 emptyState = InputState.Empty() // Ignore the input if its inputText is empty. // Reason: such inputs may be functional key combinations. @@ -62,7 +64,6 @@ import Cocoa } else if input.isCapsLockOn { // Process all possible combination, we hope. clear() - let emptyState = InputState.Empty() stateCallback(emptyState) // When shift is pressed, don't do further processing... @@ -91,7 +92,6 @@ import Cocoa && !input.isUp && !input.isSpace && isPrintable(charCode) { clear() - let emptyState = InputState.Empty() stateCallback(emptyState) let committing = InputState.Committing(poppedText: inputText.lowercased()) 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. // Still nothing? Then we update the composing buffer. // Note that some app has strange behavior if we don't do this, @@ -118,3 +273,21 @@ import Cocoa return false } } + +// MARK: - State managements. +@objc extension KeyHandler { + func _buildCandidateState( + _ currentState: InputState.NotEmpty, + useVerticalMode: Bool + ) -> InputState.ChoosingCandidate { + let candidatesArray = getCandidatesArray() + + let state = InputState.ChoosingCandidate( + composingBuffer: currentState.composingBuffer, + cursorIndex: currentState.cursorIndex, + candidates: candidatesArray as! [String], + useVerticalMode: useVerticalMode) + return state + } + +}