InputSignal // Reformat & refactor.
This commit is contained in:
parent
29a96ae747
commit
5ccc57f2b4
|
@ -128,7 +128,7 @@ enum CharCode: UInt16 {
|
|||
|
||||
struct InputSignal: CustomStringConvertible {
|
||||
private(set) var isTypingVertical: Bool
|
||||
private(set) var inputText: String?
|
||||
private(set) var inputText: String
|
||||
private(set) var inputTextIgnoringModifiers: String?
|
||||
private(set) var charCode: UInt16
|
||||
private(set) var keyCode: UInt16
|
||||
|
@ -139,16 +139,16 @@ struct InputSignal: CustomStringConvertible {
|
|||
private var extraChooseCandidateKey: KeyCode = .kNone
|
||||
private var extraChooseCandidateKeyReverse: KeyCode = .kNone
|
||||
private var absorbedArrowKey: KeyCode = .kNone
|
||||
private var verticalTypingOnlyChooseCandidateKey: KeyCode = .kNone
|
||||
private var verticalTypingCandidateKey: KeyCode = .kNone
|
||||
private(set) var emacsKey: EmacsKey
|
||||
|
||||
public init(
|
||||
inputText: String?, keyCode: UInt16, charCode: UInt16, flags: NSEvent.ModifierFlags,
|
||||
inputText: String = "", keyCode: UInt16, charCode: UInt16, flags: NSEvent.ModifierFlags,
|
||||
isVerticalTyping: Bool = false, inputTextIgnoringModifiers: String? = nil
|
||||
) {
|
||||
self.inputText = AppleKeyboardConverter.cnvStringApple2ABC(inputText ?? "")
|
||||
self.inputText = AppleKeyboardConverter.cnvStringApple2ABC(inputText)
|
||||
self.inputTextIgnoringModifiers = AppleKeyboardConverter.cnvStringApple2ABC(
|
||||
inputTextIgnoringModifiers ?? inputText ?? "")
|
||||
inputTextIgnoringModifiers ?? inputText)
|
||||
self.flags = flags
|
||||
isFlagChanged = false
|
||||
isTypingVertical = isVerticalTyping
|
||||
|
@ -164,7 +164,7 @@ struct InputSignal: CustomStringConvertible {
|
|||
public init(event: NSEvent, isVerticalTyping: Bool = false) {
|
||||
inputText = AppleKeyboardConverter.cnvStringApple2ABC(event.characters ?? "")
|
||||
inputTextIgnoringModifiers = AppleKeyboardConverter.cnvStringApple2ABC(
|
||||
event.charactersIgnoringModifiers ?? "")
|
||||
event.charactersIgnoringModifiers ?? inputText)
|
||||
keyCode = event.keyCode
|
||||
flags = event.modifierFlags
|
||||
isFlagChanged = (event.type == .flagsChanged)
|
||||
|
@ -191,170 +191,64 @@ struct InputSignal: CustomStringConvertible {
|
|||
extraChooseCandidateKey = isTypingVertical ? .kLeftArrow : .kDownArrow
|
||||
extraChooseCandidateKeyReverse = isTypingVertical ? .kRightArrow : .kUpArrow
|
||||
absorbedArrowKey = isTypingVertical ? .kRightArrow : .kUpArrow
|
||||
verticalTypingOnlyChooseCandidateKey = isTypingVertical ? absorbedArrowKey : .kNone
|
||||
verticalTypingCandidateKey = isTypingVertical ? absorbedArrowKey : .kNone
|
||||
}
|
||||
|
||||
var description: String {
|
||||
"<inputText:\(String(describing: inputText)), inputTextIgnoringModifiers:\(String(describing: inputTextIgnoringModifiers)) charCode:\(charCode), keyCode:\(keyCode), flags:\(flags), cursorForwardKey:\(cursorForwardKey), cursorBackwardKey:\(cursorBackwardKey), extraChooseCandidateKey:\(extraChooseCandidateKey), extraChooseCandidateKeyReverse:\(extraChooseCandidateKeyReverse), absorbedArrowKey:\(absorbedArrowKey), verticalTypingOnlyChooseCandidateKey:\(verticalTypingOnlyChooseCandidateKey), emacsKey:\(emacsKey), isTypingVertical:\(isTypingVertical)>"
|
||||
"<inputText:\(String(describing: inputText)), inputTextIgnoringModifiers:\(String(describing: inputTextIgnoringModifiers)) charCode:\(charCode), keyCode:\(keyCode), flags:\(flags), cursorForwardKey:\(cursorForwardKey), cursorBackwardKey:\(cursorBackwardKey), extraChooseCandidateKey:\(extraChooseCandidateKey), extraChooseCandidateKeyReverse:\(extraChooseCandidateKeyReverse), absorbedArrowKey:\(absorbedArrowKey), verticalTypingCandidateKey:\(verticalTypingCandidateKey), emacsKey:\(emacsKey), isTypingVertical:\(isTypingVertical)>"
|
||||
}
|
||||
|
||||
// 除了 ANSI charCode 以外,其餘一律過濾掉,免得純 Swift 版 KeyHandler 被餵屎。
|
||||
var isInvalidInput: Bool {
|
||||
switch charCode {
|
||||
case 0x20...0xFF: // ANSI charCode 範圍
|
||||
return false
|
||||
default:
|
||||
if isReservedKey, !isKeyCodeBlacklisted {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
var isInvalid: Bool { (0x20...0xFF).contains(charCode) ? false : !(isReservedKey && !isKeyCodeBlacklisted) }
|
||||
|
||||
var isKeyCodeBlacklisted: Bool {
|
||||
guard let code = KeyCodeBlackListed(rawValue: keyCode) else {
|
||||
return false
|
||||
}
|
||||
guard let code = KeyCodeBlackListed(rawValue: keyCode) else { return false }
|
||||
return code.rawValue != KeyCode.kNone.rawValue
|
||||
}
|
||||
|
||||
var isShiftHold: Bool {
|
||||
flags.contains([.shift])
|
||||
}
|
||||
|
||||
var isCommandHold: Bool {
|
||||
flags.contains([.command])
|
||||
}
|
||||
|
||||
var isControlHold: Bool {
|
||||
flags.contains([.control])
|
||||
}
|
||||
|
||||
var isControlHotKey: Bool {
|
||||
flags.contains([.control]) && inputText?.first?.isLetter ?? false
|
||||
}
|
||||
|
||||
var isOptionHold: Bool {
|
||||
flags.contains([.option])
|
||||
}
|
||||
|
||||
var isOptionHotKey: Bool {
|
||||
flags.contains([.option]) && inputText?.first?.isLetter ?? false
|
||||
}
|
||||
|
||||
var isCapsLockOn: Bool {
|
||||
flags.contains([.capsLock])
|
||||
}
|
||||
|
||||
var isNumericPad: Bool {
|
||||
flags.contains([.numericPad])
|
||||
}
|
||||
|
||||
var isFunctionKeyHold: Bool {
|
||||
flags.contains([.function])
|
||||
}
|
||||
|
||||
var isReservedKey: Bool {
|
||||
guard let code = KeyCode(rawValue: keyCode) else {
|
||||
return false
|
||||
}
|
||||
guard let code = KeyCode(rawValue: keyCode) else { return false }
|
||||
return code.rawValue != KeyCode.kNone.rawValue
|
||||
}
|
||||
|
||||
/// 單獨用 flags 來判定數字小鍵盤輸入的方法已經失效了,所以必須再增補用 KeyCode 判定的方法。
|
||||
var isNumericPadAreaKey: Bool {
|
||||
arrNumpadKeyCodes.contains(keyCode)
|
||||
}
|
||||
var isNumericPadKey: Bool { arrNumpadKeyCodes.contains(keyCode) }
|
||||
var isShiftHold: Bool { flags.contains([.shift]) }
|
||||
var isCommandHold: Bool { flags.contains([.command]) }
|
||||
var isControlHold: Bool { flags.contains([.control]) }
|
||||
var isControlHotKey: Bool { flags.contains([.control]) && inputText.first?.isLetter ?? false }
|
||||
var isOptionHold: Bool { flags.contains([.option]) }
|
||||
var isOptionHotKey: Bool { flags.contains([.option]) && inputText.first?.isLetter ?? false }
|
||||
var isCapsLockOn: Bool { flags.contains([.capsLock]) }
|
||||
var isFunctionKeyHold: Bool { flags.contains([.function]) }
|
||||
var isNonLaptopFunctionKey: Bool { flags.contains([.numericPad]) && !isNumericPadKey }
|
||||
var isEnter: Bool { [KeyCode.kCarriageReturn, KeyCode.kLineFeed].contains(KeyCode(rawValue: keyCode)) }
|
||||
var isTab: Bool { KeyCode(rawValue: keyCode) == KeyCode.kTab }
|
||||
var isUp: Bool { KeyCode(rawValue: keyCode) == KeyCode.kUpArrow }
|
||||
var isDown: Bool { KeyCode(rawValue: keyCode) == KeyCode.kDownArrow }
|
||||
var isLeft: Bool { KeyCode(rawValue: keyCode) == KeyCode.kLeftArrow }
|
||||
var isRight: Bool { KeyCode(rawValue: keyCode) == KeyCode.kRightArrow }
|
||||
var isPageUp: Bool { KeyCode(rawValue: keyCode) == KeyCode.kPageUp }
|
||||
var isPageDown: Bool { KeyCode(rawValue: keyCode) == KeyCode.kPageDown }
|
||||
var isSpace: Bool { KeyCode(rawValue: keyCode) == KeyCode.kSpace }
|
||||
var isBackSpace: Bool { KeyCode(rawValue: keyCode) == KeyCode.kBackSpace }
|
||||
var isEsc: Bool { KeyCode(rawValue: keyCode) == KeyCode.kEscape }
|
||||
var isHome: Bool { KeyCode(rawValue: keyCode) == KeyCode.kHome }
|
||||
var isEnd: Bool { KeyCode(rawValue: keyCode) == KeyCode.kEnd }
|
||||
var isDelete: Bool { KeyCode(rawValue: keyCode) == KeyCode.kWindowsDelete }
|
||||
var isCursorBackward: Bool { KeyCode(rawValue: keyCode) == cursorBackwardKey }
|
||||
var isCursorForward: Bool { KeyCode(rawValue: keyCode) == cursorForwardKey }
|
||||
var isAbsorbedArrowKey: Bool { KeyCode(rawValue: keyCode) == absorbedArrowKey }
|
||||
var isExtraChooseCandidateKey: Bool { KeyCode(rawValue: keyCode) == extraChooseCandidateKey }
|
||||
var isExtraChooseCandidateKeyReverse: Bool { KeyCode(rawValue: keyCode) == extraChooseCandidateKeyReverse }
|
||||
var isVerticalTypingCandidateKey: Bool { KeyCode(rawValue: keyCode) == verticalTypingCandidateKey }
|
||||
|
||||
var isTab: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kTab
|
||||
}
|
||||
|
||||
var isEnter: Bool {
|
||||
(KeyCode(rawValue: keyCode) == KeyCode.kCarriageReturn)
|
||||
|| (KeyCode(rawValue: keyCode) == KeyCode.kLineFeed)
|
||||
}
|
||||
|
||||
var isUp: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kUpArrow
|
||||
}
|
||||
|
||||
var isDown: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kDownArrow
|
||||
}
|
||||
|
||||
var isLeft: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kLeftArrow
|
||||
}
|
||||
|
||||
var isRight: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kRightArrow
|
||||
}
|
||||
|
||||
var isPageUp: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kPageUp
|
||||
}
|
||||
|
||||
var isPageDown: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kPageDown
|
||||
}
|
||||
|
||||
var isSpace: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kSpace
|
||||
}
|
||||
|
||||
var isBackSpace: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kBackSpace
|
||||
}
|
||||
|
||||
var isEsc: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kEscape
|
||||
}
|
||||
|
||||
var isHome: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kHome
|
||||
}
|
||||
|
||||
var isEnd: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kEnd
|
||||
}
|
||||
|
||||
var isDelete: Bool {
|
||||
KeyCode(rawValue: keyCode) == KeyCode.kWindowsDelete
|
||||
}
|
||||
|
||||
var isCursorBackward: Bool {
|
||||
KeyCode(rawValue: keyCode) == cursorBackwardKey
|
||||
}
|
||||
|
||||
var isCursorForward: Bool {
|
||||
KeyCode(rawValue: keyCode) == cursorForwardKey
|
||||
}
|
||||
|
||||
var isAbsorbedArrowKey: Bool {
|
||||
KeyCode(rawValue: keyCode) == absorbedArrowKey
|
||||
}
|
||||
|
||||
var isExtraChooseCandidateKey: Bool {
|
||||
KeyCode(rawValue: keyCode) == extraChooseCandidateKey
|
||||
}
|
||||
|
||||
var isExtraChooseCandidateKeyReverse: Bool {
|
||||
KeyCode(rawValue: keyCode) == extraChooseCandidateKeyReverse
|
||||
}
|
||||
|
||||
var isVerticalTypingOnlyChooseCandidateKey: Bool {
|
||||
KeyCode(rawValue: keyCode) == verticalTypingOnlyChooseCandidateKey
|
||||
}
|
||||
|
||||
var isUpperCaseASCIILetterKey: Bool {
|
||||
// 這裡必須加上「flags == .shift」,否則會出現某些情況下輸入法「誤判當前鍵入的非 Shift 字符為大寫」的問題。
|
||||
(65...90).contains(charCode) && flags == .shift
|
||||
}
|
||||
// 這裡必須加上「flags == .shift」,否則會出現某些情況下輸入法「誤判當前鍵入的非 Shift 字符為大寫」的問題。
|
||||
var isUpperCaseASCIILetterKey: Bool { (65...90).contains(charCode) && flags == .shift }
|
||||
|
||||
// 這裡必須用 KeyCode,這樣才不會受隨 macOS 版本更動的 Apple 動態注音鍵盤排列內容的影響。
|
||||
// 只是必須得與 ![input isShift] 搭配使用才可以(也就是僅判定 Shift 沒被摁下的情形)。
|
||||
var isSymbolMenuPhysicalKey: Bool {
|
||||
// 這裡必須用 KeyCode,這樣才不會受隨 macOS 版本更動的 Apple 動態注音鍵盤排列內容的影響。
|
||||
// 只是必須得與 ![input isShift] 搭配使用才可以(也就是僅判定 Shift 沒被摁下的情形)。
|
||||
[KeyCode.kSymbolMenuPhysicalKeyIntl, KeyCode.kSymbolMenuPhysicalKeyJIS].contains(KeyCode(rawValue: keyCode))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -288,7 +288,7 @@ extension KeyHandler {
|
|||
|
||||
var index: Int = NSNotFound
|
||||
let match: String =
|
||||
(state is InputState.AssociatedPhrases) ? input.inputTextIgnoringModifiers ?? "" : inputText ?? ""
|
||||
(state is InputState.AssociatedPhrases) ? input.inputTextIgnoringModifiers ?? "" : inputText
|
||||
|
||||
var j = 0
|
||||
while j < ctlCandidateCurrent.keyLabels.count {
|
||||
|
|
|
@ -45,16 +45,15 @@ extension KeyHandler {
|
|||
stateCallback: @escaping (InputStateProtocol) -> Void,
|
||||
errorCallback: @escaping () -> Void
|
||||
) -> Bool {
|
||||
// 如果按鍵訊號內的 inputTest 是空的話,則忽略該按鍵輸入,因為很可能是功能修飾鍵。
|
||||
guard !input.inputText.isEmpty else { return false }
|
||||
|
||||
let charCode: UniChar = input.charCode
|
||||
let inputText: String = input.inputText
|
||||
var state = state // 常數轉變數。
|
||||
|
||||
// 如果按鍵訊號內的 inputTest 是空的話,則忽略該按鍵輸入,因為很可能是功能修飾鍵。
|
||||
guard let inputText: String = input.inputText, !inputText.isEmpty else {
|
||||
return false
|
||||
}
|
||||
|
||||
// 提前過濾掉一些不合規的按鍵訊號輸入,免得相關按鍵訊號被送給 Megrez 引發輸入法崩潰。
|
||||
if input.isInvalidInput {
|
||||
if input.isInvalid {
|
||||
// 在「.Empty(IgnoringPreviousState) 與 .Deactivated」狀態下的首次不合規按鍵輸入可以直接放行。
|
||||
// 因為「.EmptyIgnorePreviousState」會在處理之後被自動轉為「.Empty」,所以不需要單獨判斷。
|
||||
if state is InputState.Empty || state is InputState.Deactivated {
|
||||
|
@ -68,7 +67,7 @@ extension KeyHandler {
|
|||
|
||||
// 如果當前組字器為空的話,就不再攔截某些修飾鍵,畢竟這些鍵可能會會用來觸發某些功能。
|
||||
let isFunctionKey: Bool =
|
||||
input.isControlHotKey || (input.isCommandHold || input.isOptionHotKey || input.isNumericPad)
|
||||
input.isControlHotKey || (input.isCommandHold || input.isOptionHotKey || input.isNonLaptopFunctionKey)
|
||||
if !(state is InputState.NotEmpty) && !(state is InputState.AssociatedPhrases) && isFunctionKey {
|
||||
return false
|
||||
}
|
||||
|
@ -108,10 +107,10 @@ extension KeyHandler {
|
|||
|
||||
// MARK: 處理數字小鍵盤 (Numeric Pad Processing)
|
||||
|
||||
// 這裡必須用「isNumericPadAreaKey」這個以 KeyCode 判定數字鍵區輸入的方法來鎖定按鍵範圍。
|
||||
// 不然的話,會誤傷到在主鍵盤區域的功能鍵。
|
||||
// 這裡的「isNumericPadKey」處理邏輯已經改成用 KeyCode 判定數字鍵區輸入、以鎖定按鍵範圍。
|
||||
// 不然、使用 Cocoa 內建的 flags 的話,會誤傷到在主鍵盤區域的功能鍵。
|
||||
// 我們先規定允許小鍵盤區域操縱選字窗,其餘場合一律直接放行。
|
||||
if input.isNumericPadAreaKey {
|
||||
if input.isNumericPadKey {
|
||||
if !(state is InputState.ChoosingCandidate || state is InputState.AssociatedPhrases
|
||||
|| state is InputState.SymbolTable)
|
||||
{
|
||||
|
@ -268,7 +267,7 @@ extension KeyHandler {
|
|||
if let currentState = state as? InputState.NotEmpty, composer.isEmpty, !input.isOptionHold,
|
||||
input.isExtraChooseCandidateKey || input.isExtraChooseCandidateKeyReverse || input.isSpace
|
||||
|| input.isPageDown || input.isPageUp || (input.isTab && mgrPrefs.specifyShiftTabKeyBehavior)
|
||||
|| (input.isTypingVertical && (input.isVerticalTypingOnlyChooseCandidateKey))
|
||||
|| (input.isTypingVertical && (input.isVerticalTypingCandidateKey))
|
||||
{
|
||||
if input.isSpace {
|
||||
/// 倘若沒有在偏好設定內將 Space 空格鍵設為選字窗呼叫用鍵的話………
|
||||
|
@ -479,11 +478,11 @@ extension KeyHandler {
|
|||
|
||||
/// 該功能僅可在當前組字區沒有任何內容的時候使用。
|
||||
if state is InputState.Empty {
|
||||
if input.isSpace, !input.isOptionHold, !input.isFunctionKeyHold, !input.isControlHold, !input.isCommandHold {
|
||||
if input.isSpace, !input.isOptionHold, !input.isControlHold, !input.isCommandHold {
|
||||
stateCallback(InputState.Committing(textToCommit: input.isShiftHold ? " " : " "))
|
||||
stateCallback(InputState.Empty())
|
||||
return true
|
||||
}
|
||||
stateCallback(InputState.Empty())
|
||||
return true
|
||||
}
|
||||
|
||||
// MARK: - 終末處理 (Still Nothing)
|
||||
|
|
|
@ -296,7 +296,7 @@ class KeyHandlerTestsNormalCHS: XCTestCase {
|
|||
XCTAssertTrue(state is InputState.Empty, "\(state)")
|
||||
}
|
||||
|
||||
func testisNumericPad() {
|
||||
func testisNumericPadKey() {
|
||||
var input = InputSignal(inputText: "b", keyCode: 0, charCode: charCode("b"), flags: [], isVerticalTyping: false)
|
||||
var state: InputStateProtocol = InputState.Empty()
|
||||
_ = handler.handle(input: input, state: state) { newState in
|
||||
|
@ -304,7 +304,7 @@ class KeyHandlerTestsNormalCHS: XCTestCase {
|
|||
} errorCallback: {
|
||||
}
|
||||
input = InputSignal(
|
||||
inputText: "1", keyCode: 0, charCode: charCode("1"), flags: .numericPad, isVerticalTyping: false
|
||||
inputText: "1", keyCode: 83, charCode: charCode("1"), flags: [], isVerticalTyping: false
|
||||
)
|
||||
var count = 0
|
||||
var empty: InputStateProtocol = InputState.Empty()
|
||||
|
|
Loading…
Reference in New Issue