InputHandler // +Enum: TypingMethod.

This commit is contained in:
ShikiSuen 2024-02-11 20:29:38 +08:00
parent 28e53c27ad
commit e1b7a4df9f
6 changed files with 193 additions and 170 deletions

View File

@ -22,7 +22,6 @@ public protocol InputHandlerProtocol {
var currentLM: vChewingLM.LMInstantiator { get set }
var currentUOM: vChewingLM.LMUserOverride { get set }
var delegate: InputHandlerDelegate? { get set }
var composer: Tekkon.Composer { get set }
var keySeparator: String { get }
static var keySeparator: String { get }
var isCompositorEmpty: Bool { get }
@ -89,13 +88,17 @@ public class InputHandler: InputHandlerProtocol {
///
var backupCursor: Int?
///
var currentTypingMethod: TypingMethod = .vChewingFactory
///
let kEpsilon: Double = 0.000_001
public var calligrapher = "" //
public var composer: Tekkon.Composer = .init() //
public var compositor: Megrez.Compositor //
var strCodePointBuffer = "" //
var calligrapher = "" //
var composer: Tekkon.Composer = .init() //
var compositor: Megrez.Compositor //
public var currentUOM: vChewingLM.LMUserOverride
public var currentLM: vChewingLM.LMInstantiator {
didSet {
@ -120,43 +123,13 @@ public class InputHandler: InputHandlerProtocol {
public func clear() {
clearComposerAndCalligrapher()
compositor.clear()
isCodePointInputMode = false
isHaninKeyboardSymbolMode = false
currentTypingMethod = .vChewingFactory
backupCursor = nil
}
/// /
var isConsideredEmptyForNow: Bool {
compositor.isEmpty && isComposerOrCalligrapherEmpty && !isCodePointInputMode && !isHaninKeyboardSymbolMode
}
// MARK: - Hanin Keyboard Symbol Mode.
var isHaninKeyboardSymbolMode = false
static let tooltipHaninKeyboardSymbolMode: String = "\("Hanin Keyboard Symbol Input.".localized)"
// MARK: - Codepoint Input Buffer.
var isCodePointInputMode = false {
willSet {
strCodePointBuffer.removeAll()
}
}
var strCodePointBuffer = ""
var tooltipCodePointInputMode: String {
let commonTerm = NSMutableString()
commonTerm.insert("Code Point Input.".localized, at: 0)
if !(delegate?.isVerticalTyping ?? false) {
switch IMEApp.currentInputMode {
case .imeModeCHS: commonTerm.insert("[GB] ", at: 0)
case .imeModeCHT: commonTerm.insert("[Big5] ", at: 0)
default: break
}
}
return commonTerm.description
compositor.isEmpty && isComposerOrCalligrapherEmpty && currentTypingMethod == .vChewingFactory
}
// MARK: - Functions dealing with Megrez.

View File

@ -311,7 +311,8 @@ extension InputHandler {
if !updated { delegate.callError("66F3477B") }
return true
case .option where state.type == .ofSymbolTable:
return handleHaninKeyboardSymbolModeToggle()
//
return revolveTypingMethod(to: .haninKeyboardSymbol)
default: break
}
}

View File

@ -17,37 +17,29 @@ extension InputHandler {
/// - Parameter input:
/// - Returns: IMK
func handleComposition(input: InputSignalProtocol) -> Bool? {
guard let delegate = delegate else { return nil }
//
guard !input.text.isEmpty, input.charCode.isPrintable else { return nil }
if isCodePointInputMode { return handleCodePointComposition(input: input) }
if prefs.cassetteEnabled {
// `%quick`
var handleQuickCandidate = true
if currentLM.areCassetteCandidateKeysShiftHeld { handleQuickCandidate = input.isShiftHold }
let hasQuickCandidates: Bool = delegate.state.type == .ofInputting && delegate.state.isCandidateContainer
// `%symboldef`
if handleCassetteSymbolTable(input: input) {
return true
} else if hasQuickCandidates, input.text != currentLM.cassetteWildcardKey {
// `%quick` `%symboldef`
guard !(handleQuickCandidate && handleCandidate(input: input, ignoringModifiers: true)) else { return true }
} else {
// `%quick`
guard !(hasQuickCandidates && handleQuickCandidate && handleCandidate(input: input)) else { return true }
}
let hardRequirementMet = !input.text.isEmpty && input.charCode.isPrintable
switch currentTypingMethod {
case .codePoint where hardRequirementMet:
return handleCodePointComposition(input: input)
case .haninKeyboardSymbol where [[], .shift].contains(input.keyModifierFlags):
return handleHaninKeyboardSymbolModeInput(input: input)
case .vChewingFactory where hardRequirementMet && prefs.cassetteEnabled:
return handleCassetteComposition(input: input)
case .vChewingFactory where hardRequirementMet && !prefs.cassetteEnabled:
return handlePhonabetComposition(input: input)
default: return nil
}
return handlePhonabetComposition(input: input)
}
}
// MARK: (Handle BPMF Keys)
// MARK: - (Handle BPMF Keys)
private extension InputHandler {
/// InputHandler.HandleInput()
/// - Parameter input:
/// - Returns: IMK
private func handlePhonabetComposition(input: InputSignalProtocol) -> Bool? {
func handlePhonabetComposition(input: InputSignalProtocol) -> Bool? {
guard let delegate = delegate else { return nil }
var inputText = (input.inputTextIgnoringModifiers ?? input.text)
inputText = inputText.lowercased().applyingTransformFW2HW(reverse: false)
@ -241,13 +233,31 @@ extension InputHandler {
// MARK: -
extension InputHandler {
/// InputHandler.HandleInput()
private extension InputHandler {
/// InputHandler.HandleInput()
/// - Parameter input:
/// - Returns: IMK
private func handleCassetteComposition(input: InputSignalProtocol) -> Bool? {
func handleCassetteComposition(input: InputSignalProtocol) -> Bool? {
guard let delegate = delegate else { return nil }
let state = delegate.state
// `%quick`
var handleQuickCandidate = true
if currentLM.areCassetteCandidateKeysShiftHeld { handleQuickCandidate = input.isShiftHold }
let hasQuickCandidates: Bool = state.type == .ofInputting && state.isCandidateContainer
// `%symboldef`
if handleCassetteSymbolTable(input: input) {
return true
} else if hasQuickCandidates, input.text != currentLM.cassetteWildcardKey {
// `%quick` `%symboldef`
guard !(handleQuickCandidate && handleCandidate(input: input, ignoringModifiers: true)) else { return true }
} else {
// `%quick`
guard !(hasQuickCandidates && handleQuickCandidate && handleCandidate(input: input)) else { return true }
}
//
var wildcardKey: String { currentLM.cassetteWildcardKey } //
let inputText = input.text
let isWildcardKeyInput: Bool = (inputText == wildcardKey && !wildcardKey.isEmpty)
@ -266,7 +276,7 @@ extension InputHandler {
calligrapher.count >= currentLM.maxCassetteKeyLength || isLongestPossibleKeyFormed
}
prehandling: if !skipStrokeHandling && currentLM.isThisCassetteKeyAllowed(key: inputText) {
prehandling: if !skipStrokeHandling && currentLM.isThisCassetteKeyAllowed(key: inputText) {
if calligrapher.isEmpty, isWildcardKeyInput {
delegate.callError("3606B9C0")
if input.beganWithLetter {
@ -385,16 +395,17 @@ extension InputHandler {
// SessionCtl IMK
return true
}
return nil
}
}
// MARK: (Handle Code Point Input)
// MARK: - (Handle Code Point Input)
private extension InputHandler {
/// InputHandler.HandleInput()
/// - Parameter input:
/// - Returns: IMK
private func handleCodePointComposition(input: InputSignalProtocol) -> Bool? {
func handleCodePointComposition(input: InputSignalProtocol) -> Bool? {
guard !input.isReservedKey else { return nil }
guard let delegate = delegate, input.text.count == 1 else { return nil }
guard !input.text.compactMap(\.hexDigitValue).isEmpty else {
@ -407,7 +418,7 @@ extension InputHandler {
strCodePointBuffer.append(input.text)
var updatedState = generateStateOfInputting(guarded: true)
updatedState.tooltipDuration = 0
updatedState.tooltip = tooltipCodePointInputMode
updatedState.tooltip = TypingMethod.codePoint.getTooltip(vertical: delegate.isVerticalTyping)
delegate.switchState(updatedState)
return true
}
@ -427,7 +438,7 @@ extension InputHandler {
updatedState.tooltipDuration = 0
updatedState.tooltip = "Invalid Code Point.".localized
delegate.switchState(updatedState)
isCodePointInputMode = true
currentTypingMethod = .codePoint
return true
}
// macOS
@ -435,14 +446,46 @@ extension InputHandler {
delegate.switchState(IMEState.ofCommitting(textToCommit: char))
var updatedState = generateStateOfInputting(guarded: true)
updatedState.tooltipDuration = 0
updatedState.tooltip = tooltipCodePointInputMode
updatedState.tooltip = TypingMethod.codePoint.getTooltip(vertical: delegate.isVerticalTyping)
delegate.switchState(updatedState)
isCodePointInputMode = true
currentTypingMethod = .codePoint
return true
default:
delegate.switchState(generateStateOfInputting())
isCodePointInputMode = true
currentTypingMethod = .codePoint
return true
}
}
}
// MARK: - Handle Hanin Keyboard Symbol Inputs
private extension InputHandler {
///
/// - Parameters:
/// - input:
/// - Returns: SessionCtl IMK
func handleHaninKeyboardSymbolModeInput(input: InputSignalProtocol) -> Bool {
guard let delegate = delegate, delegate.state.type != .ofDeactivated else { return false }
let charText = input.text.lowercased().applyingTransformFW2HW(reverse: false)
guard CandidateNode.mapHaninKeyboardSymbols.keys.contains(charText) else {
return revolveTypingMethod(to: .vChewingFactory)
}
guard
charText.count == 1, let symbols = CandidateNode.queryHaninKeyboardSymbols(char: charText)
else {
delegate.callError("C1A760C7")
return true
}
// commit buffer ESC
let textToCommit = generateStateOfInputting(sansReading: true).displayedText
delegate.switchState(IMEState.ofCommitting(textToCommit: textToCommit))
if symbols.members.count == 1 {
delegate.switchState(IMEState.ofCommitting(textToCommit: symbols.members.map(\.name).joined()))
} else {
delegate.switchState(IMEState.ofSymbolTable(node: symbols))
}
currentTypingMethod = .vChewingFactory // toggle
return true
}
}

View File

@ -27,17 +27,18 @@ extension InputHandler {
if isConsideredEmptyForNow, !guarded { return IMEState.ofAbortion() }
restoreBackupCursor() // Inputting
var segHighlightedAt: Int?
let cpInput = isCodePointInputMode && !sansReading
let handleAsCodePointInput = currentTypingMethod == .codePoint && !sansReading
/// (Update the composing buffer)
/// IMEStateData NSAttributeString
var displayTextSegments: [String] = cpInput
var displayTextSegments: [String] = handleAsCodePointInput
? [strCodePointBuffer]
: compositor.walkedNodes.values
var cursor = cpInput
var cursor = handleAsCodePointInput
? displayTextSegments.joined().count
: convertCursorForDisplay(compositor.cursor)
let cursorSansReading = cursor
let reading: String = (sansReading || isCodePointInputMode) ? "" : readingForDisplay //
//
let reading: String = (sansReading || currentTypingMethod == .codePoint) ? "" : readingForDisplay
if !reading.isEmpty {
var newDisplayTextSegments = [String]()
var temporaryNode = ""
@ -361,8 +362,9 @@ extension InputHandler {
guard let delegate = delegate else { return false }
let state = delegate.state
if isHaninKeyboardSymbolMode { return handleHaninKeyboardSymbolModeToggle() }
if isCodePointInputMode { return handleCodePointInputToggle() }
guard currentTypingMethod == .vChewingFactory else {
return revolveTypingMethod(to: .vChewingFactory)
}
guard state.type == .ofInputting else { return false }
@ -470,31 +472,28 @@ extension InputHandler {
guard let delegate = delegate else { return false }
let state = delegate.state
guard state.type == .ofInputting else {
isCodePointInputMode = false
currentTypingMethod = .vChewingFactory
return false
}
if isCodePointInputMode {
if currentTypingMethod == .codePoint {
if !strCodePointBuffer.isEmpty {
func refreshState() {
var updatedState = generateStateOfInputting(guarded: true)
updatedState.tooltipDuration = 0
updatedState.tooltip = tooltipCodePointInputMode
updatedState.tooltip = delegate.state.tooltip
delegate.switchState(updatedState)
}
strCodePointBuffer = strCodePointBuffer.dropLast(1).description
if input.commonKeyModifierFlags == .option {
strCodePointBuffer.removeAll()
refreshState()
isCodePointInputMode = true
return true
return revolveTypingMethod(to: .codePoint)
}
if !strCodePointBuffer.isEmpty {
refreshState()
return true
}
}
return handleCodePointInputToggle()
return revolveTypingMethod(to: .vChewingFactory)
}
// macOS Shift+BackSpace
@ -575,8 +574,9 @@ extension InputHandler {
guard let delegate = delegate else { return false }
let state = delegate.state
if isHaninKeyboardSymbolMode { return handleHaninKeyboardSymbolModeToggle() }
if isCodePointInputMode { return handleCodePointInputToggle() }
guard currentTypingMethod == .vChewingFactory else {
return revolveTypingMethod(to: .vChewingFactory)
}
guard state.type == .ofInputting else { return false }
@ -684,8 +684,9 @@ extension InputHandler {
guard let delegate = delegate else { return false }
let state = delegate.state
if isHaninKeyboardSymbolMode { return handleHaninKeyboardSymbolModeToggle() }
if isCodePointInputMode { return handleCodePointInputToggle() }
guard currentTypingMethod == .vChewingFactory else {
return revolveTypingMethod(to: .vChewingFactory)
}
guard state.type == .ofInputting else { return false }
@ -920,73 +921,6 @@ extension InputHandler {
return true
}
// MARK: - CodePoint Input Toggle
@discardableResult func handleCodePointInputToggle() -> Bool {
guard let delegate = delegate, delegate.state.type != .ofDeactivated else { return false }
if isCodePointInputMode {
isCodePointInputMode = false
delegate.switchState(IMEState.ofAbortion())
return true
}
var updatedState = generateStateOfInputting(sansReading: true)
delegate.switchState(IMEState.ofCommitting(textToCommit: updatedState.displayedText))
updatedState = generateStateOfInputting(guarded: true)
updatedState.tooltipDuration = 0
updatedState.tooltip = tooltipCodePointInputMode
delegate.switchState(updatedState)
isCodePointInputMode = true
return true
}
// MARK: - Hanin Pallete
@discardableResult func handleHaninKeyboardSymbolModeToggle() -> Bool {
guard let delegate = delegate, delegate.state.type != .ofDeactivated else { return false }
if isCodePointInputMode { isCodePointInputMode = false }
if isHaninKeyboardSymbolMode {
isHaninKeyboardSymbolMode = false
delegate.switchState(IMEState.ofAbortion())
return true
}
var updatedState = generateStateOfInputting(sansReading: true)
delegate.switchState(IMEState.ofCommitting(textToCommit: updatedState.displayedText))
updatedState = generateStateOfInputting(guarded: true)
updatedState.tooltipDuration = 0
updatedState.tooltip = Self.tooltipHaninKeyboardSymbolMode
delegate.switchState(updatedState)
isHaninKeyboardSymbolMode = true
return true
}
///
/// - Parameters:
/// - input:
/// - Returns: SessionCtl IMK
func handleHaninKeyboardSymbolModeInput(input: InputSignalProtocol) -> Bool {
guard let delegate = delegate, delegate.state.type != .ofDeactivated else { return false }
let charText = input.text.lowercased().applyingTransformFW2HW(reverse: false)
guard CandidateNode.mapHaninKeyboardSymbols.keys.contains(charText) else {
return handleHaninKeyboardSymbolModeToggle()
}
guard
charText.count == 1, let symbols = CandidateNode.queryHaninKeyboardSymbols(char: charText)
else {
delegate.callError("C1A760C7")
return true
}
// commit buffer ESC
let textToCommit = generateStateOfInputting(sansReading: true).displayedText
delegate.switchState(IMEState.ofCommitting(textToCommit: textToCommit))
if symbols.members.count == 1 {
delegate.switchState(IMEState.ofCommitting(textToCommit: symbols.members.map(\.name).joined()))
} else {
delegate.switchState(IMEState.ofSymbolTable(node: symbols))
}
isHaninKeyboardSymbolMode = false // toggle
return true
}
// MARK: - Symbol Menu Input
///

View File

@ -48,7 +48,7 @@ public extension InputHandler {
case .kCarriageReturn, .kLineFeed:
let frontNode = compositor.walkedNodes.last
return handleEnter(input: input) {
guard !self.isHaninKeyboardSymbolMode, !self.isCodePointInputMode else { return [] }
guard self.currentTypingMethod == .vChewingFactory else { return [] }
guard let frontNode = frontNode else { return [] }
let pair = Megrez.KeyValuePaired(keyArray: frontNode.keyArray, value: frontNode.value)
let associates = self.generateArrayOfAssociates(withPair: pair)
@ -62,13 +62,7 @@ public extension InputHandler {
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
return revolveTypingMethod()
default: break
}
case .kSpace:
@ -85,7 +79,7 @@ public extension InputHandler {
if input.isShiftHold, !input.isControlHold, !input.isOptionHold {
return revolveCandidate(reverseOrder: input.isCommandHold)
}
if isCodePointInputMode {
if currentTypingMethod == .codePoint {
delegate.callError("FDD88EDB")
delegate.switchState(IMEState.ofAbortion())
return true
@ -148,13 +142,11 @@ public extension InputHandler {
guard let x = input.inputTextIgnoringModifiers,
"¥\\".contains(x), input.keyModifierFlags.isEmpty
else { break haninSymbolInput }
return handleHaninKeyboardSymbolModeToggle()
return revolveTypingMethod(to: .haninKeyboardSymbol)
}
//
if isHaninKeyboardSymbolMode, [[], .shift].contains(input.keyModifierFlags) {
return handleHaninKeyboardSymbolModeInput(input: input)
} else if let compositionHandled = handleComposition(input: input) {
// /
if let compositionHandled = handleComposition(input: input) {
return compositionHandled
}

View File

@ -0,0 +1,80 @@
// (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.
import Foundation
import SwiftExtension
// MARK: - Typing Method
public extension InputHandler {
enum TypingMethod: Int, CaseIterable {
case vChewingFactory // : 0
case codePoint // : 1
case haninKeyboardSymbol // : 2
mutating func revolveNext() {
var theInt = rawValue
theInt.revolveAsIndex(with: Self.allCases)
guard let nextMethod = TypingMethod(rawValue: theInt) else { return }
self = nextMethod
}
func getTooltip(vertical: Bool = false) -> String {
switch self {
case .vChewingFactory: return ""
case .codePoint:
let commonTerm = NSMutableString()
commonTerm.insert("Code Point Input.".localized, at: 0)
if !vertical {
switch IMEApp.currentInputMode {
case .imeModeCHS: commonTerm.insert("[GB] ", at: 0)
case .imeModeCHT: commonTerm.insert("[Big5] ", at: 0)
default: break
}
}
return commonTerm.description
case .haninKeyboardSymbol:
return "\("Hanin Keyboard Symbol Input.".localized)"
}
}
}
}
// MARK: - Handle Rotation Toggles
public extension InputHandler {
@discardableResult func revolveTypingMethod(to specifiedMethod: TypingMethod? = nil) -> Bool {
guard let delegate = delegate else { return false }
var newMethod = currentTypingMethod
if let specified = specifiedMethod {
newMethod = specified
} else {
newMethod.revolveNext()
}
/// defer
/// switch newMethod currentTypingMethod
defer {
currentTypingMethod = newMethod
}
switch newMethod {
case .vChewingFactory:
delegate.switchState(IMEState.ofAbortion())
return true
case .codePoint:
strCodePointBuffer.removeAll()
case .haninKeyboardSymbol: break
}
var updatedState = generateStateOfInputting(sansReading: true)
delegate.switchState(IMEState.ofCommitting(textToCommit: updatedState.displayedText))
updatedState = generateStateOfInputting(guarded: true)
updatedState.tooltipDuration = 0
updatedState.tooltip = newMethod.getTooltip(vertical: delegate.isVerticalTyping)
delegate.switchState(updatedState)
return true
}
}