InputHandler // Refactor handleInput() to triageInput().

This commit is contained in:
ShikiSuen 2023-06-09 14:34:35 +08:00
parent 05213c04b1
commit 8562c35535
10 changed files with 353 additions and 325 deletions

View File

@ -129,7 +129,7 @@ public extension NSEvent {
var isShiftHold: Bool { modifierFlags.contains([.shift]) } var isShiftHold: Bool { modifierFlags.contains([.shift]) }
var isCommandHold: Bool { modifierFlags.contains([.command]) } var isCommandHold: Bool { modifierFlags.contains([.command]) }
var isControlHold: Bool { modifierFlags.contains([.control]) } var isControlHold: Bool { modifierFlags.contains([.control]) }
var isControlHotKey: Bool { modifierFlags.contains([.control]) && text.first?.isLetter ?? false } var beganWithLetter: Bool { text.first?.isLetter ?? false }
var isOptionHold: Bool { modifierFlags.contains([.option]) } var isOptionHold: Bool { modifierFlags.contains([.option]) }
var isOptionHotKey: Bool { modifierFlags.contains([.option]) && text.first?.isLetter ?? false } var isOptionHotKey: Bool { modifierFlags.contains([.option]) && text.first?.isLetter ?? false }
var isCapsLockOn: Bool { modifierFlags.contains([.capsLock]) } var isCapsLockOn: Bool { modifierFlags.contains([.capsLock]) }

View File

@ -33,7 +33,7 @@ public protocol InputSignalProtocol {
var isShiftHold: Bool { get } var isShiftHold: Bool { get }
var isCommandHold: Bool { get } var isCommandHold: Bool { get }
var isControlHold: Bool { get } var isControlHold: Bool { get }
var isControlHotKey: Bool { get } var beganWithLetter: Bool { get }
var isOptionHold: Bool { get } var isOptionHold: Bool { get }
var isOptionHotKey: Bool { get } var isOptionHotKey: Bool { get }
var isCapsLockOn: Bool { get } var isCapsLockOn: Bool { get }

View File

@ -542,9 +542,9 @@ public class InputHandler: InputHandlerProtocol {
return result > 0 return result > 0
} }
/// ///
/// - Parameter input: /// - Parameter input:
/// - Returns: /// - Returns:
func generatePunctuationNamePrefix(withKeyCondition input: InputSignalProtocol) -> String { func generatePunctuationNamePrefix(withKeyCondition input: InputSignalProtocol) -> String {
if prefs.halfWidthPunctuationEnabled { return "_half_punctuation_" } if prefs.halfWidthPunctuationEnabled { return "_half_punctuation_" }
// SHIFT+ALT+ // SHIFT+ALT+
@ -559,6 +559,27 @@ public class InputHandler: InputHandlerProtocol {
} }
return result return result
} }
///
/// - Parameter input:
/// - Returns:
func punctuationQueryStrings(input: InputSignalProtocol) -> [String] {
///
/// - /
/// -
var result: [String] = []
let inputText = input.text
let punctuationNamePrefix: String = generatePunctuationNamePrefix(withKeyCondition: input)
let parser = currentKeyboardParser
let arrCustomPunctuations: [String] = [punctuationNamePrefix, parser, inputText]
let customPunctuation: String = arrCustomPunctuations.joined()
result.append(customPunctuation)
///
let arrPunctuations: [String] = [punctuationNamePrefix, inputText]
let punctuation: String = arrPunctuations.joined()
result.append(punctuation)
return result
}
} }
// MARK: - Components for Popup Composition Buffer (PCB) Window. // MARK: - Components for Popup Composition Buffer (PCB) Window.

View File

@ -16,8 +16,7 @@ import Shared
extension InputHandler { extension InputHandler {
/// ///
/// - Parameters: /// - Parameter input:
/// - input:
/// - Returns: IMK /// - Returns: IMK
func handleCandidate(input: InputSignalProtocol) -> Bool { func handleCandidate(input: InputSignalProtocol) -> Bool {
guard let delegate = delegate else { return false } guard let delegate = delegate else { return false }
@ -69,7 +68,7 @@ extension InputHandler {
} else { } else {
delegate.switchState(generateStateOfInputting()) delegate.switchState(generateStateOfInputting())
if input.isCursorBackward || input.isCursorForward, input.isShiftHold { if input.isCursorBackward || input.isCursorForward, input.isShiftHold {
return handleInput(event: input) return triageInput(event: input)
} }
} }
if state.type == .ofSymbolTable, let nodePrevious = state.node.previous, !nodePrevious.members.isEmpty { if state.type == .ofSymbolTable, let nodePrevious = state.node.previous, !nodePrevious.members.isEmpty {
@ -212,7 +211,7 @@ extension InputHandler {
guard let candidateIndex = ctlCandidate.candidateIndexAtKeyLabelIndex(0) else { return true } guard let candidateIndex = ctlCandidate.candidateIndexAtKeyLabelIndex(0) else { return true }
delegate.candidateSelectionConfirmedByInputHandler(at: candidateIndex) delegate.candidateSelectionConfirmedByInputHandler(at: candidateIndex)
delegate.switchState(IMEState.ofAbortion()) delegate.switchState(IMEState.ofAbortion())
return handleInput(event: input) return triageInput(event: input)
} }
} }

View File

@ -13,8 +13,7 @@ import Tekkon
extension InputHandler { extension InputHandler {
/// InputHandler.HandleInput() /// InputHandler.HandleInput()
/// - Parameters: /// - Parameter input:
/// - input:
/// - Returns: IMK /// - Returns: IMK
func handleComposition(input: InputSignalProtocol) -> Bool? { func handleComposition(input: InputSignalProtocol) -> Bool? {
// //
@ -27,8 +26,7 @@ extension InputHandler {
// MARK: (Handle BPMF Keys) // MARK: (Handle BPMF Keys)
/// InputHandler.HandleInput() /// InputHandler.HandleInput()
/// - Parameters: /// - Parameter input:
/// - input:
/// - Returns: IMK /// - Returns: IMK
private func handlePhonabetComposition(input: InputSignalProtocol) -> Bool? { private func handlePhonabetComposition(input: InputSignalProtocol) -> Bool? {
guard let delegate = delegate else { return nil } guard let delegate = delegate else { return nil }
@ -208,8 +206,7 @@ extension InputHandler {
extension InputHandler { extension InputHandler {
/// InputHandler.HandleInput() /// InputHandler.HandleInput()
/// - Parameters: /// - Parameter input:
/// - input:
/// - Returns: IMK /// - Returns: IMK
private func handleCassetteComposition(input: InputSignalProtocol) -> Bool? { private func handleCassetteComposition(input: InputSignalProtocol) -> Bool? {
guard let delegate = delegate else { return nil } guard let delegate = delegate else { return nil }
@ -339,8 +336,7 @@ extension InputHandler {
// MARK: (Handle Code Point Input) // MARK: (Handle Code Point Input)
/// InputHandler.HandleInput() /// InputHandler.HandleInput()
/// - Parameters: /// - Parameter input:
/// - input:
/// - Returns: IMK /// - Returns: IMK
private func handleCodePointComposition(input: InputSignalProtocol) -> Bool? { private func handleCodePointComposition(input: InputSignalProtocol) -> Bool? {
guard !input.isReservedKey else { return nil } guard !input.isReservedKey else { return nil }

View File

@ -18,7 +18,7 @@ extension InputHandler {
/// - Parameter event: IMK /// - Parameter event: IMK
/// - Returns: `true` IMK`false` /// - Returns: `true` IMK`false`
public func handleEvent(_ event: NSEvent) -> Bool { public func handleEvent(_ event: NSEvent) -> Bool {
imkCandidatesEventPreHandler(event: event) ?? handleInput(event: event) imkCandidatesEventPreHandler(event: event) ?? triageInput(event: event)
} }
/// IMK /// IMK
@ -71,7 +71,7 @@ extension InputHandler {
let eventArray = [event] let eventArray = [event]
guard let imkC = delegate.candidateController() as? CtlCandidateIMK else { return false } guard let imkC = delegate.candidateController() as? CtlCandidateIMK else { return false }
if event.isEsc || event.isBackSpace || event.isDelete || (event.isShiftHold && !event.isSpace) { if event.isEsc || event.isBackSpace || event.isDelete || (event.isShiftHold && !event.isSpace) {
return handleInput(event: event) return triageInput(event: event)
} else if event.isSymbolMenuPhysicalKey { } else if event.isSymbolMenuPhysicalKey {
// //
switch imkC.currentLayout { switch imkC.currentLayout {
@ -118,7 +118,7 @@ extension InputHandler {
if let newEvent = newEvent { if let newEvent = newEvent {
if prefs.useSCPCTypingMode, delegate.state.type == .ofAssociates { if prefs.useSCPCTypingMode, delegate.state.type == .ofAssociates {
// input.isShiftHold delegate.handle() // input.isShiftHold delegate.handle()
return event.isShiftHold ? true : handleInput(event: event) return event.isShiftHold ? true : triageInput(event: event)
} else { } else {
if #available(macOS 10.14, *) { if #available(macOS 10.14, *) {
PrefMgr.shared.failureFlagForIMKCandidates = true PrefMgr.shared.failureFlagForIMKCandidates = true
@ -149,7 +149,7 @@ extension InputHandler {
} }
if prefs.useSCPCTypingMode, !event.isReservedKey { if prefs.useSCPCTypingMode, !event.isReservedKey {
return handleInput(event: event) return triageInput(event: event)
} }
if delegate.state.type == .ofAssociates, if delegate.state.type == .ofAssociates,
@ -157,7 +157,7 @@ extension InputHandler {
!event.isCursorClockLeft, !event.isCursorClockRight, !event.isSpace, !event.isCursorClockLeft, !event.isCursorClockRight, !event.isSpace,
!event.isEnter || !prefs.alsoConfirmAssociatedCandidatesByEnter !event.isEnter || !prefs.alsoConfirmAssociatedCandidatesByEnter
{ {
return handleInput(event: event) return triageInput(event: event)
} }
imkC.interpretKeyEvents(eventArray) imkC.interpretKeyEvents(eventArray)
return true return true

View File

@ -1,297 +0,0 @@
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
// ... with NTL restriction stating that:
// 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 defined in MIT License.
/// 調 IMK 調
/// 調
import CocoaExtension
import IMKUtils
import LangModelAssembly
import Shared
// MARK: - § 調 (Handle Input with States)
extension InputHandler {
///
/// - Remark: inputHandler?.handleEvent() IMKCandidates
/// - Parameters:
/// - input:
/// - Returns: IMK
func handleInput(event input: InputSignalProtocol) -> Bool {
// delegate
guard let delegate = delegate else { return false }
let inputText: String = input.text
var state: IMEStateProtocol { delegate.state } //
//
if input.isInvalid, state.type == .ofEmpty || state.type == .ofDeactivated { return false }
//
let isFunctionKey: Bool =
input.isControlHotKey || (input.isCommandHold || input.isOptionHotKey || input.isNonLaptopFunctionKey)
if state.type != .ofAssociates, !state.hasComposition, !state.isCandidateContainer, isFunctionKey {
return false
}
// MARK: Caps Lock
/// Caps Lock
/// Caps Lock
if input.isCapsLockOn || delegate.isASCIIMode {
// macOS 12 CapsLock .ofEmpty()
delegate.switchState(IMEState.ofEmpty())
// Shift
if (input.isUpperCaseASCIILetterKey && delegate.isASCIIMode)
|| (input.isCapsLockOn && input.isShiftHold)
{
return false
}
/// ASCII 使insertText:replacementRange:
/// ASCII
if input.isASCII, !input.charCode.isPrintableASCII { return false }
//
delegate.switchState(IMEState.ofCommitting(textToCommit: inputText.lowercased()))
return true
}
// MARK: (Numeric Pad Processing)
// isNumericPadKey KeyCode
// 使 Cocoa flags
//
if input.isNumericPadKey {
if ![.ofCandidates, .ofAssociates, .ofSymbolTable].contains(state.type) {
delegate.switchState(IMEState.ofEmpty())
delegate.switchState(IMEState.ofCommitting(textToCommit: inputText.lowercased()))
return true
}
}
// MARK: (Handle Candidates)
if [.ofCandidates, .ofSymbolTable].contains(state.type) { return handleCandidate(input: input) }
// MARK:
// inputTest
//
guard !input.text.isEmpty, input.charCode.isPrintable else { return false }
// MARK: (Handle Associated Phrases)
if state.type == .ofAssociates {
if handleCandidate(input: input) {
return true
} else {
delegate.switchState(IMEState.ofEmpty())
}
}
// MARK: 便使() (Handle Marking)
if state.type == .ofMarking {
if handleMarkingState(input: input) { return true }
delegate.switchState(state.convertedToInputting)
}
// MARK:
if let x = input.inputTextIgnoringModifiers,
"¥\\".contains(x), input.modifierFlags.isEmpty,
prefs.classicHaninKeyboardSymbolModeShortcutEnabled
{
return handleHaninKeyboardSymbolModeToggle()
}
// MARK: (Handle BPMF Keys & Hanin Keyboard Symbol Input)
if isHaninKeyboardSymbolMode, [[], .shift].contains(input.modifierFlags) {
return handleHaninKeyboardSymbolModeInput(input: input)
} else if let compositionHandled = handleComposition(input: input) {
return compositionHandled
}
// MARK: (Calling candidate window using Up / Down or PageUp / PageDn.)
// state.hasComposition
// !input.isFunctionKeyHold
if state.hasComposition, !compositor.isEmpty, isComposerOrCalligrapherEmpty,
!input.isOptionHold, !input.isShiftHold, !input.isCommandHold, !input.isControlHold,
input.isCursorClockLeft || input.isCursorClockRight || (input.isSpace && prefs.chooseCandidateUsingSpace)
|| input.isPageDown || input.isPageUp || (input.isTab && prefs.specifyShiftTabKeyBehavior)
{
//
let candidateState: IMEStateProtocol = generateStateOfCandidates()
_ = candidateState.candidates.isEmpty ? delegate.callError("3572F238") : delegate.switchState(candidateState)
return true
}
// MARK: Ctrl+Command+[]
// Shift+Command+[] Chrome Ctrl
revolveCandidateWithBrackets: if input.modifierFlags == [.control, .command] {
if state.type != .ofInputting { break revolveCandidateWithBrackets }
// JIS US
let isJIS: Bool = KBGetLayoutType(Int16(LMGetKbdType())) == kKeyboardJIS
switch (input.keyCode, isJIS) {
case (30, true), (33, false): return revolveCandidate(reverseOrder: true)
case (42, true), (30, false): return revolveCandidate(reverseOrder: false)
default: break
}
}
// MARK:
if let keyCodeType = KeyCode(rawValue: input.keyCode) {
switch keyCodeType {
case .kEscape: return handleEsc()
case .kTab, .kContextMenu: return revolveCandidate(reverseOrder: input.isShiftHold)
case .kUpArrow, .kDownArrow, .kLeftArrow, .kRightArrow:
let rotation: Bool = (input.isOptionHold || input.isShiftHold) && state.type == .ofInputting
handleArrowKey: switch (keyCodeType, delegate.isVerticalTyping) {
case (.kLeftArrow, false), (.kUpArrow, true): return handleBackward(input: input)
case (.kRightArrow, false), (.kDownArrow, true): return handleForward(input: input)
case (.kUpArrow, false), (.kLeftArrow, true):
return rotation ? revolveCandidate(reverseOrder: true) : handleClockKey()
case (.kDownArrow, false), (.kRightArrow, true):
return rotation ? revolveCandidate(reverseOrder: false) : handleClockKey()
default: break handleArrowKey //
}
case .kHome: return handleHome()
case .kEnd: return handleEnd()
case .kBackSpace: return handleBackSpace(input: input)
case .kWindowsDelete: return handleDelete(input: input)
case .kCarriageReturn, .kLineFeed: return handleEnter(input: input)
case .kSymbolMenuPhysicalKeyJIS, .kSymbolMenuPhysicalKeyIntl:
let isJIS = keyCodeType == .kSymbolMenuPhysicalKeyJIS
switch input.modifierFlags {
case []:
return handlePunctuationList(alternative: false, isJIS: isJIS)
case [.option, .shift]:
return handlePunctuationList(alternative: true, isJIS: isJIS)
case .option:
switch (isCodePointInputMode, isHaninKeyboardSymbolMode) {
case (false, false): return handleCodePointInputToggle()
case (true, false), (false, true):
return handleHaninKeyboardSymbolModeToggle()
default: break
}
return true
default: break
}
case .kSpace: // Space
//
switch state.type {
case .ofEmpty:
if !input.isOptionHold, !input.isControlHold, !input.isCommandHold {
delegate.switchState(IMEState.ofCommitting(textToCommit: input.isShiftHold ? " " : " "))
return true
}
case .ofInputting:
// Tab Shift+Command+Space /
if input.isShiftHold, !input.isControlHold, !input.isOptionHold {
return revolveCandidate(reverseOrder: input.isCommandHold)
}
if compositor.cursor < compositor.length, compositor.insertKey(" ") {
walk()
// App
let textToCommit = commitOverflownComposition
var inputting = generateStateOfInputting()
inputting.textToCommit = textToCommit
delegate.switchState(inputting)
} else {
let displayedText = state.displayedText
if !displayedText.isEmpty, !isConsideredEmptyForNow {
delegate.switchState(IMEState.ofCommitting(textToCommit: displayedText))
}
delegate.switchState(IMEState.ofCommitting(textToCommit: " "))
}
return true
default: break
}
default: break
}
}
// MARK: / (FW / HW Arabic Numbers Input)
if state.type == .ofEmpty {
if input.isMainAreaNumKey, input.isOptionHold, !input.isCommandHold, !input.isControlHold {
guard let strRAW = input.mainAreaNumKeyChar else { return false }
let newString: String = {
if input.isShiftHold {
return strRAW.applyingTransformFW2HW(reverse: !prefs.halfWidthPunctuationEnabled)
}
return strRAW.applyingTransformFW2HW(reverse: false)
}()
delegate.switchState(IMEState.ofCommitting(textToCommit: newString))
return true
}
}
// MARK: Punctuation
///
/// - /
/// -
let punctuationNamePrefix: String = generatePunctuationNamePrefix(withKeyCondition: input)
let parser = currentKeyboardParser
let arrCustomPunctuations: [String] = [punctuationNamePrefix, parser, input.text]
let customPunctuation: String = arrCustomPunctuations.joined()
if handlePunctuation(customPunctuation) { return true }
///
let arrPunctuations: [String] = [punctuationNamePrefix, input.text]
let punctuation: String = arrPunctuations.joined()
if handlePunctuation(punctuation) { return true }
// MARK: Shift+ (Shift+Letter Processing)
if input.isUpperCaseASCIILetterKey, !input.isCommandHold, !input.isControlHold {
if input.isShiftHold { // isOptionHold
switch prefs.upperCaseLetterKeyBehavior {
case 1, 3:
if prefs.upperCaseLetterKeyBehavior == 3, !isConsideredEmptyForNow { break }
let commitText = generateStateOfInputting(sansReading: true).displayedText
delegate.switchState(IMEState.ofCommitting(textToCommit: commitText + inputText.lowercased()))
return true
case 2, 4:
if prefs.upperCaseLetterKeyBehavior == 4, !isConsideredEmptyForNow { break }
let commitText = generateStateOfInputting(sansReading: true).displayedText
delegate.switchState(IMEState.ofCommitting(textToCommit: commitText + inputText.uppercased()))
return true
default: // case 0
break
}
//
let letter = "_letter_\(inputText)"
if handlePunctuation(letter) {
return true
}
}
}
// MARK: - (Still Nothing)
/// SessionCtl
///
/// F1-F12
/// 便
if state.hasComposition || !isComposerOrCalligrapherEmpty {
delegate.callError("Blocked data: charCode: \(input.charCode), keyCode: \(input.keyCode), text: \(input.text)")
delegate.callError("A9BFF20E")
return true
}
return false
}
}

View File

@ -864,7 +864,7 @@ extension InputHandler {
return true return true
} }
// MARK: - // MARK: - CodePoint Input Toggle
@discardableResult func handleCodePointInputToggle() -> Bool { @discardableResult func handleCodePointInputToggle() -> Bool {
guard let delegate = delegate, delegate.state.type != .ofDeactivated else { return false } guard let delegate = delegate, delegate.state.type != .ofDeactivated else { return false }
@ -883,7 +883,7 @@ extension InputHandler {
return true return true
} }
// MARK: - // MARK: - Hanin Pallete
@discardableResult func handleHaninKeyboardSymbolModeToggle() -> Bool { @discardableResult func handleHaninKeyboardSymbolModeToggle() -> Bool {
guard let delegate = delegate, delegate.state.type != .ofDeactivated else { return false } guard let delegate = delegate, delegate.state.type != .ofDeactivated else { return false }
@ -931,7 +931,7 @@ extension InputHandler {
return true return true
} }
// MARK: - // MARK: - Symbol Menu Input
/// ///
/// - Parameters: /// - Parameters:
@ -976,4 +976,117 @@ extension InputHandler {
return true return true
} }
} }
// MARK: - Caps Lock Caps Lock and Alphanumerical mode
/// CapsLock
/// - Remark: Caps Lock
/// Caps Lock
/// - Parameter input:
/// - Returns: IMK
func handleCapsLockAndAlphanumericalMode(input: InputSignalProtocol) -> Bool? {
guard let delegate = delegate else { return nil }
guard input.isCapsLockOn || delegate.isASCIIMode else { return nil }
// macOS 12 CapsLock
// .ofEmpty()
delegate.switchState(IMEState.ofEmpty())
// Shift
if (input.isUpperCaseASCIILetterKey && delegate.isASCIIMode)
|| (input.isCapsLockOn && input.isShiftHold)
{
return false
}
/// ASCII
/// 使insertText:replacementRange:
/// ASCII
if input.isASCII, !input.charCode.isPrintableASCII { return false }
//
delegate.switchState(IMEState.ofCommitting(textToCommit: input.text.lowercased()))
return true
}
// MARK: - Intentionally Call Candidate Window
///
/// - Parameter input:
/// - Returns: IMK
func callCandidateState(input: InputSignalProtocol) -> Bool {
guard let delegate = delegate else { return false }
var state: IMEStateProtocol { delegate.state }
//
// state.hasComposition
// !input.isFunctionKeyHold
//
let notEmpty = state.hasComposition && !compositor.isEmpty && isComposerOrCalligrapherEmpty
let bannedModifiers: NSEvent.ModifierFlags = [.option, .shift, .command, .control]
let noBannedModifiers = bannedModifiers.intersection(input.modifierFlags).isEmpty
var triggered = input.isCursorClockLeft || input.isCursorClockRight
triggered = triggered || (input.isSpace && prefs.chooseCandidateUsingSpace)
triggered = triggered || input.isPageDown || input.isPageUp
triggered = triggered || (input.isTab && prefs.specifyShiftTabKeyBehavior)
guard notEmpty, noBannedModifiers, triggered else { return false }
//
let candidateState: IMEStateProtocol = generateStateOfCandidates()
_ = candidateState.candidates.isEmpty ? delegate.callError("3572F238") : delegate.switchState(candidateState)
return true
}
// MARK: - /FW/HW Arabic Numerals
/// /
/// - Parameter input:
/// - Returns: IMK
func handleArabicNumeralInputs(input: InputSignalProtocol) -> Bool {
guard let delegate = delegate else { return false }
guard delegate.state.type == .ofEmpty, input.isMainAreaNumKey else { return false }
guard input.isOptionHold, !input.isCommandHold, !input.isControlHold else { return false }
guard let strRAW = input.mainAreaNumKeyChar else { return false }
let newString: String = {
if input.isShiftHold {
return strRAW.applyingTransformFW2HW(reverse: !prefs.halfWidthPunctuationEnabled)
}
return strRAW.applyingTransformFW2HW(reverse: false)
}()
delegate.switchState(IMEState.ofCommitting(textToCommit: newString))
return true
}
// MARK: - SHIFT Shift + Letter keys
/// SHIFT
/// - Parameter input:
/// - Returns: IMK
func handleLettersWithShiftHold(input: InputSignalProtocol) -> Bool {
guard let delegate = delegate else { return false }
let inputText = input.text
if input.isUpperCaseASCIILetterKey, !input.isCommandHold, !input.isControlHold {
if input.isShiftHold { // isOptionHold
switch prefs.upperCaseLetterKeyBehavior {
case 1, 3:
if prefs.upperCaseLetterKeyBehavior == 3, !isConsideredEmptyForNow { break }
let commitText = generateStateOfInputting(sansReading: true).displayedText
delegate.switchState(IMEState.ofCommitting(textToCommit: commitText + inputText.lowercased()))
return true
case 2, 4:
if prefs.upperCaseLetterKeyBehavior == 4, !isConsideredEmptyForNow { break }
let commitText = generateStateOfInputting(sansReading: true).displayedText
delegate.switchState(IMEState.ofCommitting(textToCommit: commitText + inputText.uppercased()))
return true
default: // case 0
break
}
//
let letter = "_letter_\(inputText)"
if handlePunctuation(letter) {
return true
}
}
}
return false
}
} }

View File

@ -0,0 +1,196 @@
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
// ... with NTL restriction stating that:
// 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 defined in MIT License.
/// 調 IMK 調
/// 調
import CocoaExtension
import IMKUtils
import LangModelAssembly
import Shared
// MARK: - § 調 (Handle Input with States) * Triage
extension InputHandler {
func triageInput(event input: InputSignalProtocol) -> Bool {
guard let delegate = delegate else { return false }
var state: IMEStateProtocol { delegate.state }
let inputText = input.text
// MARK: - Triage by KeyCode
func triageByKeyCode() -> Bool? {
guard let keyCodeType = KeyCode(rawValue: input.keyCode) else { return nil }
switch keyCodeType {
case .kEscape: return handleEsc()
case .kTab, .kContextMenu: return revolveCandidate(reverseOrder: input.isShiftHold)
case .kUpArrow, .kDownArrow, .kLeftArrow, .kRightArrow:
let rotation: Bool = (input.isOptionHold || input.isShiftHold) && state.type == .ofInputting
handleArrowKey: switch (keyCodeType, delegate.isVerticalTyping) {
case (.kLeftArrow, false), (.kUpArrow, true): return handleBackward(input: input)
case (.kRightArrow, false), (.kDownArrow, true): return handleForward(input: input)
case (.kUpArrow, false), (.kLeftArrow, true):
return rotation ? revolveCandidate(reverseOrder: true) : handleClockKey()
case (.kDownArrow, false), (.kRightArrow, true):
return rotation ? revolveCandidate(reverseOrder: false) : handleClockKey()
default: break handleArrowKey //
}
case .kHome: return handleHome()
case .kEnd: return handleEnd()
case .kBackSpace: return handleBackSpace(input: input)
case .kWindowsDelete: return handleDelete(input: input)
case .kCarriageReturn, .kLineFeed: return handleEnter(input: input)
case .kSymbolMenuPhysicalKeyJIS, .kSymbolMenuPhysicalKeyIntl:
let isJIS = keyCodeType == .kSymbolMenuPhysicalKeyJIS
switch input.modifierFlags {
case []:
return handlePunctuationList(alternative: false, isJIS: isJIS)
case [.option, .shift]:
return handlePunctuationList(alternative: true, isJIS: isJIS)
case .option:
switch (isCodePointInputMode, isHaninKeyboardSymbolMode) {
case (false, false): return handleCodePointInputToggle()
case (true, false), (false, true):
return handleHaninKeyboardSymbolModeToggle()
default: break
}
return true
default: break
}
case .kSpace:
// Space
//
switch state.type {
case .ofEmpty:
if !input.isOptionHold, !input.isControlHold, !input.isCommandHold {
delegate.switchState(IMEState.ofCommitting(textToCommit: input.isShiftHold ? " " : " "))
return true
}
case .ofInputting:
// Tab Shift+Command+Space /
if input.isShiftHold, !input.isControlHold, !input.isOptionHold {
return revolveCandidate(reverseOrder: input.isCommandHold)
}
if compositor.cursor < compositor.length, compositor.insertKey(" ") {
walk()
// App
let textToCommit = commitOverflownComposition
var inputting = generateStateOfInputting()
inputting.textToCommit = textToCommit
delegate.switchState(inputting)
} else {
let displayedText = state.displayedText
if !displayedText.isEmpty, !isConsideredEmptyForNow {
delegate.switchState(IMEState.ofCommitting(textToCommit: displayedText))
}
delegate.switchState(IMEState.ofCommitting(textToCommit: " "))
}
return true
default: break
}
default: break
}
return nil
}
// MARK: - Triage by States
switch state.type {
case .ofDeactivated, .ofAbortion, .ofCommitting: return false
case .ofAssociates, .ofCandidates, .ofSymbolTable:
let result = handleCandidate(input: input)
guard !result, state.type == .ofAssociates else { return true }
delegate.switchState(IMEState.ofEmpty())
return triageInput(event: input)
case .ofMarking:
if handleMarkingState(input: input) { return true }
delegate.switchState(state.convertedToInputting)
return triageInput(event: input)
case .ofEmpty, .ofInputting:
//
guard !(input.isInvalid && state.type == .ofEmpty) else { return false }
//
let isFunctional: Bool = (input.isControlHold && input.beganWithLetter)
|| (input.isCommandHold || input.isOptionHotKey || input.isNonLaptopFunctionKey)
if !state.hasComposition, isFunctional { return false }
// Caps Lock
// Caps Lock
if let capsHandleResult = handleCapsLockAndAlphanumericalMode(input: input) {
return capsHandleResult
}
//
if input.isNumericPadKey {
delegate.switchState(IMEState.ofEmpty())
delegate.switchState(IMEState.ofCommitting(textToCommit: inputText.lowercased()))
return true
}
//
haninSymbolInput: if prefs.classicHaninKeyboardSymbolModeShortcutEnabled {
guard let x = input.inputTextIgnoringModifiers,
"¥\\".contains(x), input.modifierFlags.isEmpty
else { break haninSymbolInput }
return handleHaninKeyboardSymbolModeToggle()
}
//
if isHaninKeyboardSymbolMode, [[], .shift].contains(input.modifierFlags) {
return handleHaninKeyboardSymbolModeInput(input: input)
} else if let compositionHandled = handleComposition(input: input) {
return compositionHandled
}
//
if callCandidateState(input: input) { return true }
// Ctrl+Command+[]
// Shift+Command+[] Chrome Ctrl
revolveCandidateWithBrackets: if input.modifierFlags == [.control, .command] {
if state.type != .ofInputting { break revolveCandidateWithBrackets }
// JIS US
let isJIS: Bool = KBGetLayoutType(Int16(LMGetKbdType())) == kKeyboardJIS
switch (input.keyCode, isJIS) {
case (30, true), (33, false): return revolveCandidate(reverseOrder: true)
case (42, true), (30, false): return revolveCandidate(reverseOrder: false)
default: break
}
}
// keyCode
if let keyCodeTriaged = triageByKeyCode() { return keyCodeTriaged }
// /
if handleArabicNumeralInputs(input: input) { return true }
//
let queryStrings: [String] = punctuationQueryStrings(input: input)
for queryString in queryStrings {
guard !handlePunctuation(queryString) else { return true }
}
// Shift+
if handleLettersWithShiftHold(input: input) { return true }
}
// Still Nothing
// SessionCtl
//
// F1-F12
// 便
if state.hasComposition || !isComposerOrCalligrapherEmpty {
delegate.callError("Blocked data: charCode: \(input.charCode), keyCode: \(input.keyCode), text: \(input.text)")
delegate.callError("A9BFF20E")
return true
}
return false
}
}

View File

@ -49,7 +49,7 @@
5B78EE0D28A562B4009456C1 /* VwrPrefPaneDevZone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B78EE0C28A562B4009456C1 /* VwrPrefPaneDevZone.swift */; }; 5B78EE0D28A562B4009456C1 /* VwrPrefPaneDevZone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B78EE0C28A562B4009456C1 /* VwrPrefPaneDevZone.swift */; };
5B7BC4B027AFFBE800F66C24 /* frmPrefWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5B7BC4AE27AFFBE800F66C24 /* frmPrefWindow.xib */; }; 5B7BC4B027AFFBE800F66C24 /* frmPrefWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5B7BC4AE27AFFBE800F66C24 /* frmPrefWindow.xib */; };
5B7DA80328BF6BC600D7B2AD /* fixinstall.sh in Resources */ = {isa = PBXBuildFile; fileRef = 5B7DA80228BF6BBA00D7B2AD /* fixinstall.sh */; }; 5B7DA80328BF6BC600D7B2AD /* fixinstall.sh in Resources */ = {isa = PBXBuildFile; fileRef = 5B7DA80228BF6BBA00D7B2AD /* fixinstall.sh */; };
5B7F225D2808501000DDD3CB /* InputHandler_HandleInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B7F225C2808501000DDD3CB /* InputHandler_HandleInput.swift */; }; 5B7F225D2808501000DDD3CB /* InputHandler_TriageInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B7F225C2808501000DDD3CB /* InputHandler_TriageInput.swift */; };
5B84579E2871AD2200C93B01 /* convdict.json in Resources */ = {isa = PBXBuildFile; fileRef = 5B84579C2871AD2200C93B01 /* convdict.json */; }; 5B84579E2871AD2200C93B01 /* convdict.json in Resources */ = {isa = PBXBuildFile; fileRef = 5B84579C2871AD2200C93B01 /* convdict.json */; };
5B8457A12871ADBE00C93B01 /* ChineseConverterBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B8457A02871ADBE00C93B01 /* ChineseConverterBridge.swift */; }; 5B8457A12871ADBE00C93B01 /* ChineseConverterBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B8457A02871ADBE00C93B01 /* ChineseConverterBridge.swift */; };
5B963C9D28D5BFB800DCEE88 /* CocoaExtension in Frameworks */ = {isa = PBXBuildFile; productRef = 5B963C9C28D5BFB800DCEE88 /* CocoaExtension */; }; 5B963C9D28D5BFB800DCEE88 /* CocoaExtension in Frameworks */ = {isa = PBXBuildFile; productRef = 5B963C9C28D5BFB800DCEE88 /* CocoaExtension */; };
@ -250,7 +250,7 @@
5B7BC4AF27AFFBE800F66C24 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/frmPrefWindow.xib; 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>"; }; 5B7BC4B227AFFC0B00F66C24 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/frmPrefWindow.strings; sourceTree = "<group>"; };
5B7DA80228BF6BBA00D7B2AD /* fixinstall.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; lineEnding = 0; path = fixinstall.sh; sourceTree = "<group>"; }; 5B7DA80228BF6BBA00D7B2AD /* fixinstall.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; lineEnding = 0; path = fixinstall.sh; sourceTree = "<group>"; };
5B7F225C2808501000DDD3CB /* InputHandler_HandleInput.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = InputHandler_HandleInput.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 5B7F225C2808501000DDD3CB /* InputHandler_TriageInput.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = InputHandler_TriageInput.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
5B84579C2871AD2200C93B01 /* convdict.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = convdict.json; sourceTree = "<group>"; }; 5B84579C2871AD2200C93B01 /* convdict.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = convdict.json; sourceTree = "<group>"; };
5B8457A02871ADBE00C93B01 /* ChineseConverterBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChineseConverterBridge.swift; sourceTree = "<group>"; }; 5B8457A02871ADBE00C93B01 /* ChineseConverterBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChineseConverterBridge.swift; sourceTree = "<group>"; };
5B963C9B28D5BE4100DCEE88 /* vChewing_CocoaExtension */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_CocoaExtension; path = Packages/vChewing_CocoaExtension; sourceTree = "<group>"; }; 5B963C9B28D5BE4100DCEE88 /* vChewing_CocoaExtension */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_CocoaExtension; path = Packages/vChewing_CocoaExtension; sourceTree = "<group>"; };
@ -694,8 +694,8 @@
5B782EC3280C243C007276DE /* InputHandler_HandleCandidate.swift */, 5B782EC3280C243C007276DE /* InputHandler_HandleCandidate.swift */,
5BE3779F288FED8D0037365B /* InputHandler_HandleComposition.swift */, 5BE3779F288FED8D0037365B /* InputHandler_HandleComposition.swift */,
5BE1F8A828F86AB5006C7FF5 /* InputHandler_HandleEvent.swift */, 5BE1F8A828F86AB5006C7FF5 /* InputHandler_HandleEvent.swift */,
5B7F225C2808501000DDD3CB /* InputHandler_HandleInput.swift */,
5B3133BE280B229700A4A505 /* InputHandler_HandleStates.swift */, 5B3133BE280B229700A4A505 /* InputHandler_HandleStates.swift */,
5B7F225C2808501000DDD3CB /* InputHandler_TriageInput.swift */,
5BAEFACF28012565001F42C9 /* LMMgr_Core.swift */, 5BAEFACF28012565001F42C9 /* LMMgr_Core.swift */,
5B33844E29B8B4C200FCB497 /* LMMgr_PhraseEditorDelegate.swift */, 5B33844E29B8B4C200FCB497 /* LMMgr_PhraseEditorDelegate.swift */,
5B33844C29B8980100FCB497 /* LMMgr_UserPhraseStructure.swift */, 5B33844C29B8980100FCB497 /* LMMgr_UserPhraseStructure.swift */,
@ -1106,7 +1106,7 @@
5BF018FD299923C200248CDD /* VwrPrefPaneCandidates.swift in Sources */, 5BF018FD299923C200248CDD /* VwrPrefPaneCandidates.swift in Sources */,
5B963CA828D5DB1400DCEE88 /* PrefMgr_Core.swift in Sources */, 5B963CA828D5DB1400DCEE88 /* PrefMgr_Core.swift in Sources */,
D427F76C278CA2B0004A2160 /* AppDelegate.swift in Sources */, D427F76C278CA2B0004A2160 /* AppDelegate.swift in Sources */,
5B7F225D2808501000DDD3CB /* InputHandler_HandleInput.swift in Sources */, 5B7F225D2808501000DDD3CB /* InputHandler_TriageInput.swift in Sources */,
5B660A8628F64A8800E5E4F6 /* SymbolMenuDefaultData.swift in Sources */, 5B660A8628F64A8800E5E4F6 /* SymbolMenuDefaultData.swift in Sources */,
5BF56F9828C39A2700DD6839 /* IMEState.swift in Sources */, 5BF56F9828C39A2700DD6839 /* IMEState.swift in Sources */,
5B62A33D27AE7CC100A19448 /* CtlAboutWindow.swift in Sources */, 5B62A33D27AE7CC100A19448 /* CtlAboutWindow.swift in Sources */,