Skip to content

Commit

Permalink
InputHandler // +Enum: TypingMethod.
Browse files Browse the repository at this point in the history
  • Loading branch information
ShikiSuen committed Feb 11, 2024
1 parent 96b51ac commit 12fac58
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 170 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -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 {
Expand All @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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
}
Expand All @@ -427,22 +438,54 @@ extension InputHandler {
updatedState.tooltipDuration = 0
updatedState.tooltip = "Invalid Code Point.".localized
delegate.switchState(updatedState)
isCodePointInputMode = true
currentTypingMethod = .codePoint
return true
}
// 某些舊版 macOS 會在這裡生成的字元後面插入垃圾字元。這裡只保留起始字元。
if char.count > 1 { char = char.map(\.description)[0] }
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
}
}
Loading

0 comments on commit 12fac58

Please sign in to comment.