Repo // Handle NSInternalInconsistencyException.
This commit is contained in:
parent
738aec4260
commit
3987cad35e
|
@ -22,15 +22,19 @@ extension NSEvent {
|
||||||
isARepeat: Bool? = nil,
|
isARepeat: Bool? = nil,
|
||||||
keyCode: UInt16? = nil
|
keyCode: UInt16? = nil
|
||||||
) -> NSEvent? {
|
) -> NSEvent? {
|
||||||
NSEvent.keyEvent(
|
let oldChars: String = {
|
||||||
|
if self.type == .flagsChanged { return "" }
|
||||||
|
return self.characters ?? ""
|
||||||
|
}()
|
||||||
|
return NSEvent.keyEvent(
|
||||||
with: type ?? self.type,
|
with: type ?? self.type,
|
||||||
location: location ?? locationInWindow,
|
location: location ?? locationInWindow,
|
||||||
modifierFlags: modifierFlags ?? self.modifierFlags,
|
modifierFlags: modifierFlags ?? self.modifierFlags,
|
||||||
timestamp: timestamp ?? self.timestamp,
|
timestamp: timestamp ?? self.timestamp,
|
||||||
windowNumber: windowNumber ?? self.windowNumber,
|
windowNumber: windowNumber ?? self.windowNumber,
|
||||||
context: nil,
|
context: nil,
|
||||||
characters: characters ?? self.characters ?? "",
|
characters: characters ?? oldChars,
|
||||||
charactersIgnoringModifiers: charactersIgnoringModifiers ?? self.characters ?? "",
|
charactersIgnoringModifiers: charactersIgnoringModifiers ?? characters ?? oldChars,
|
||||||
isARepeat: isARepeat ?? self.isARepeat,
|
isARepeat: isARepeat ?? self.isARepeat,
|
||||||
keyCode: keyCode ?? self.keyCode
|
keyCode: keyCode ?? self.keyCode
|
||||||
)
|
)
|
||||||
|
@ -81,6 +85,8 @@ extension NSEvent: InputSignalProtocol {
|
||||||
}
|
}
|
||||||
|
|
||||||
public var charCode: UInt16 {
|
public var charCode: UInt16 {
|
||||||
|
guard type != .flagsChanged else { return 0 }
|
||||||
|
guard characters != nil else { return 0 }
|
||||||
// 這裡不用「count > 0」,因為該整數變數只要「!isEmpty」那就必定滿足這個條件。
|
// 這裡不用「count > 0」,因為該整數變數只要「!isEmpty」那就必定滿足這個條件。
|
||||||
guard !text.isEmpty else { return 0 }
|
guard !text.isEmpty else { return 0 }
|
||||||
let scalars = text.unicodeScalars
|
let scalars = text.unicodeScalars
|
||||||
|
|
|
@ -16,59 +16,9 @@ extension ctlInputMethod {
|
||||||
/// - Parameter event: 由 IMK 選字窗接收的裝置操作輸入事件。
|
/// - Parameter event: 由 IMK 選字窗接收的裝置操作輸入事件。
|
||||||
/// - Returns: 回「`true`」以將該案件已攔截處理的訊息傳遞給 IMK;回「`false`」則放行、不作處理。
|
/// - Returns: 回「`true`」以將該案件已攔截處理的訊息傳遞給 IMK;回「`false`」則放行、不作處理。
|
||||||
func commonEventHandler(_ event: NSEvent) -> Bool {
|
func commonEventHandler(_ event: NSEvent) -> Bool {
|
||||||
// 用 Shift 開關半形英數模式,僅對 macOS 10.15 及之後的 macOS 有效。
|
|
||||||
let shouldUseHandle: Bool = {
|
|
||||||
switch mgrPrefs.shiftKeyAccommodationBehavior {
|
|
||||||
case 0: return false
|
|
||||||
case 1: return IME.arrClientShiftHandlingExceptionList.contains(clientBundleIdentifier)
|
|
||||||
case 2: return true
|
|
||||||
default: return false
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if #available(macOS 10.15, *) {
|
|
||||||
if ShiftKeyUpChecker.check(event), !mgrPrefs.disableShiftTogglingAlphanumericalMode {
|
|
||||||
if !shouldUseHandle || (!rencentKeyHandledByKeyHandler && shouldUseHandle) {
|
|
||||||
NotifierController.notify(
|
|
||||||
message: NSLocalizedString("Alphanumerical Mode", comment: "") + "\n"
|
|
||||||
+ (toggleASCIIMode()
|
|
||||||
? NSLocalizedString("NotificationSwitchON", comment: "")
|
|
||||||
: NSLocalizedString("NotificationSwitchOFF", comment: ""))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if shouldUseHandle {
|
|
||||||
rencentKeyHandledByKeyHandler = false
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 沒有文字輸入客體的話,就不要再往下處理了。
|
|
||||||
guard client() != nil else { return false }
|
|
||||||
|
|
||||||
var event = event
|
|
||||||
// 使 NSEvent 自翻譯,這樣可以讓 Emacs NSEvent 變成標準 NSEvent。
|
|
||||||
if event.isEmacsKey {
|
|
||||||
let verticalProcessing =
|
|
||||||
(state.isCandidateContainer)
|
|
||||||
? ctlInputMethod.isVerticalCandidateSituation : ctlInputMethod.isVerticalTypingSituation
|
|
||||||
event = event.convertFromEmacKeyEvent(isVerticalContext: verticalProcessing)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 這裡仍舊需要判斷 flags。之前使輸入法狀態卡住無法敲漢字的問題已在 KeyHandler 內修復。
|
|
||||||
/// 這裡不判斷 flags 的話,用方向鍵前後定位光標之後,再次試圖觸發組字區時、反而會在首次按鍵時失敗。
|
|
||||||
/// 同時注意:必須在 event.type == .flagsChanged 結尾插入 return false,
|
|
||||||
/// 否則,每次處理這種判斷時都會觸發 NSInternalInconsistencyException。
|
|
||||||
if event.type == .flagsChanged { return false }
|
|
||||||
|
|
||||||
// 準備修飾鍵,用來判定要新增的詞彙是否需要賦以非常低的權重。
|
|
||||||
ctlInputMethod.areWeNerfing = event.modifierFlags.contains([.shift, .command])
|
|
||||||
|
|
||||||
// 無法列印的訊號輸入,一概不作處理。
|
// 無法列印的訊號輸入,一概不作處理。
|
||||||
// 這個過程不能放在 KeyHandler 內,否則不會起作用。
|
// 這個過程不能放在 KeyHandler 內,否則不會起作用。
|
||||||
if !event.charCode.isPrintable {
|
if !event.charCode.isPrintable { return false }
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 將按鍵行為與當前輸入法狀態結合起來、交給按鍵調度模組來處理。
|
/// 將按鍵行為與當前輸入法狀態結合起來、交給按鍵調度模組來處理。
|
||||||
/// 再根據返回的 result bool 數值來告知 IMK「這個按鍵事件是被處理了還是被放行了」。
|
/// 再根據返回的 result bool 數值來告知 IMK「這個按鍵事件是被處理了還是被放行了」。
|
||||||
|
@ -78,9 +28,6 @@ extension ctlInputMethod {
|
||||||
} errorCallback: {
|
} errorCallback: {
|
||||||
clsSFX.beep()
|
clsSFX.beep()
|
||||||
}
|
}
|
||||||
if shouldUseHandle {
|
|
||||||
rencentKeyHandledByKeyHandler = result
|
|
||||||
}
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ class ctlInputMethod: IMKInputController {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `handle(event:)` 會利用這個參數判定某次 Shift 按鍵是否用來切換中英文輸入。
|
/// `handle(event:)` 會利用這個參數判定某次 Shift 按鍵是否用來切換中英文輸入。
|
||||||
var rencentKeyHandledByKeyHandler = false
|
var rencentKeyHandledByKeyHandlerEtc = false
|
||||||
|
|
||||||
// MARK: - 工具函式
|
// MARK: - 工具函式
|
||||||
|
|
||||||
|
@ -207,6 +207,8 @@ class ctlInputMethod: IMKInputController {
|
||||||
@objc(handleEvent:client:) override func handle(_ event: NSEvent!, client sender: Any!) -> Bool {
|
@objc(handleEvent:client:) override func handle(_ event: NSEvent!, client sender: Any!) -> Bool {
|
||||||
_ = sender // 防止格式整理工具毀掉與此對應的參數。
|
_ = sender // 防止格式整理工具毀掉與此對應的參數。
|
||||||
|
|
||||||
|
// MARK: 前置處理
|
||||||
|
|
||||||
// 更新此時的靜態狀態標記。
|
// 更新此時的靜態狀態標記。
|
||||||
ctlInputMethod.isASCIIModeSituation = isASCIIMode
|
ctlInputMethod.isASCIIModeSituation = isASCIIMode
|
||||||
ctlInputMethod.isVerticalTypingSituation = isVerticalTyping
|
ctlInputMethod.isVerticalTypingSituation = isVerticalTyping
|
||||||
|
@ -218,17 +220,63 @@ class ctlInputMethod: IMKInputController {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 用 Shift 開關半形英數模式,僅對 macOS 10.15 及之後的 macOS 有效。
|
||||||
|
let shouldUseShiftToggleHandle: Bool = {
|
||||||
|
switch mgrPrefs.shiftKeyAccommodationBehavior {
|
||||||
|
case 0: return false
|
||||||
|
case 1: return IME.arrClientShiftHandlingExceptionList.contains(clientBundleIdentifier)
|
||||||
|
case 2: return true
|
||||||
|
default: return false
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if #available(macOS 10.15, *) {
|
||||||
|
if ShiftKeyUpChecker.check(event), !mgrPrefs.disableShiftTogglingAlphanumericalMode {
|
||||||
|
if !shouldUseShiftToggleHandle || (!rencentKeyHandledByKeyHandlerEtc && shouldUseShiftToggleHandle) {
|
||||||
|
NotifierController.notify(
|
||||||
|
message: NSLocalizedString("Alphanumerical Mode", comment: "") + "\n"
|
||||||
|
+ (toggleASCIIMode()
|
||||||
|
? NSLocalizedString("NotificationSwitchON", comment: "")
|
||||||
|
: NSLocalizedString("NotificationSwitchOFF", comment: ""))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if shouldUseShiftToggleHandle {
|
||||||
|
rencentKeyHandledByKeyHandlerEtc = false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: 針對客體的具體處理
|
||||||
|
|
||||||
|
/// 這裡仍舊需要判斷 flags。之前使輸入法狀態卡住無法敲漢字的問題已在 KeyHandler 內修復。
|
||||||
|
/// 這裡不判斷 flags 的話,用方向鍵前後定位光標之後,再次試圖觸發組字區時、反而會在首次按鍵時失敗。
|
||||||
|
/// 同時注意:必須在 event.type == .flagsChanged 結尾插入 return false,
|
||||||
|
/// 否則,每次處理這種判斷時都會觸發 NSInternalInconsistencyException。
|
||||||
|
if event.type == .flagsChanged { return false }
|
||||||
|
|
||||||
|
/// 沒有文字輸入客體的話,就不要再往下處理了。
|
||||||
|
guard client() != nil else { return false }
|
||||||
|
|
||||||
|
var eventToDeal = event
|
||||||
|
// 使 NSEvent 自翻譯,這樣可以讓 Emacs NSEvent 變成標準 NSEvent。
|
||||||
|
if eventToDeal.isEmacsKey {
|
||||||
|
let verticalProcessing =
|
||||||
|
(state.isCandidateContainer)
|
||||||
|
? ctlInputMethod.isVerticalCandidateSituation : ctlInputMethod.isVerticalTypingSituation
|
||||||
|
eventToDeal = eventToDeal.convertFromEmacKeyEvent(isVerticalContext: verticalProcessing)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 準備修飾鍵,用來判定要新增的詞彙是否需要賦以非常低的權重。
|
||||||
|
ctlInputMethod.areWeNerfing = eventToDeal.modifierFlags.contains([.shift, .command])
|
||||||
|
|
||||||
// IMK 選字窗處理,當且僅當啟用了 IMK 選字窗的時候才會生效。
|
// IMK 選字窗處理,當且僅當啟用了 IMK 選字窗的時候才會生效。
|
||||||
// 這樣可以讓 interpretKeyEvents() 函式自行判斷:
|
// 這樣可以讓 interpretKeyEvents() 函式自行判斷:
|
||||||
// - 是就地交給 super.interpretKeyEvents() 處理?
|
// - 是就地交給 super.interpretKeyEvents() 處理?
|
||||||
// - 還是藉由 delegate 扔回 ctlInputMethod 給 KeyHandler 處理?
|
// - 還是藉由 delegate 扔回 ctlInputMethod 給 KeyHandler 處理?
|
||||||
proc: if let ctlCandidateCurrent = ctlInputMethod.ctlCandidateCurrent as? ctlCandidateIMK {
|
proc: if let ctlCandidateCurrent = ctlInputMethod.ctlCandidateCurrent as? ctlCandidateIMK {
|
||||||
guard ctlCandidateCurrent.visible else { break proc }
|
guard ctlCandidateCurrent.visible else { break proc }
|
||||||
var event: NSEvent = ctlCandidateIMK.replaceNumPadKeyCodes(target: event) ?? event
|
var event: NSEvent = ctlCandidateIMK.replaceNumPadKeyCodes(target: eventToDeal) ?? eventToDeal
|
||||||
// 使 NSEvent 自翻譯,這樣可以讓 Emacs NSEvent 變成標準 NSEvent。
|
|
||||||
if event.isEmacsKey {
|
|
||||||
event = event.convertFromEmacKeyEvent(isVerticalContext: ctlInputMethod.isVerticalCandidateSituation)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shift+Enter 是個特殊情形,不提前攔截處理的話、會有垃圾參數傳給 delegate 的 keyHandler 從而崩潰。
|
// Shift+Enter 是個特殊情形,不提前攔截處理的話、會有垃圾參數傳給 delegate 的 keyHandler 從而崩潰。
|
||||||
// 所以這裡直接將 Shift Flags 清空。
|
// 所以這裡直接將 Shift Flags 清空。
|
||||||
|
@ -255,7 +303,11 @@ class ctlInputMethod: IMKInputController {
|
||||||
/// 我們不在這裡處理了,直接交給 commonEventHandler 來處理。
|
/// 我們不在這裡處理了,直接交給 commonEventHandler 來處理。
|
||||||
/// 這樣可以與 IMK 選字窗共用按鍵處理資源,維護起來也比較方便。
|
/// 這樣可以與 IMK 選字窗共用按鍵處理資源,維護起來也比較方便。
|
||||||
/// 警告:這裡的 event 必須是原始 event 且不能被 var,否則會影響 Shift 中英模式判定。
|
/// 警告:這裡的 event 必須是原始 event 且不能被 var,否則會影響 Shift 中英模式判定。
|
||||||
return commonEventHandler(event)
|
let result = commonEventHandler(eventToDeal)
|
||||||
|
if shouldUseShiftToggleHandle {
|
||||||
|
rencentKeyHandledByKeyHandlerEtc = result
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 有時會出現某些 App 攔截輸入法的 Ctrl+Enter / Shift+Enter 熱鍵的情況。
|
/// 有時會出現某些 App 攔截輸入法的 Ctrl+Enter / Shift+Enter 熱鍵的情況。
|
||||||
|
|
Loading…
Reference in New Issue