InputHandler // Refactor handleInput() to triageInput().
This commit is contained in:
parent
05213c04b1
commit
8562c35535
|
@ -129,7 +129,7 @@ public extension NSEvent {
|
|||
var isShiftHold: Bool { modifierFlags.contains([.shift]) }
|
||||
var isCommandHold: Bool { modifierFlags.contains([.command]) }
|
||||
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 isOptionHotKey: Bool { modifierFlags.contains([.option]) && text.first?.isLetter ?? false }
|
||||
var isCapsLockOn: Bool { modifierFlags.contains([.capsLock]) }
|
||||
|
|
|
@ -33,7 +33,7 @@ public protocol InputSignalProtocol {
|
|||
var isShiftHold: Bool { get }
|
||||
var isCommandHold: Bool { get }
|
||||
var isControlHold: Bool { get }
|
||||
var isControlHotKey: Bool { get }
|
||||
var beganWithLetter: Bool { get }
|
||||
var isOptionHold: Bool { get }
|
||||
var isOptionHotKey: Bool { get }
|
||||
var isCapsLockOn: Bool { get }
|
||||
|
|
|
@ -542,9 +542,9 @@ public class InputHandler: InputHandlerProtocol {
|
|||
return result > 0
|
||||
}
|
||||
|
||||
/// 生成標點符號索引鍵。
|
||||
/// 生成標點符號索引鍵頭。
|
||||
/// - Parameter input: 輸入的按鍵訊號。
|
||||
/// - Returns: 生成的標點符號索引鍵。
|
||||
/// - Returns: 生成的標點符號索引鍵頭。
|
||||
func generatePunctuationNamePrefix(withKeyCondition input: InputSignalProtocol) -> String {
|
||||
if prefs.halfWidthPunctuationEnabled { return "_half_punctuation_" }
|
||||
// 注意:這一行為 SHIFT+ALT+主鍵盤數字鍵專用,強制無視不同地區的鍵盤在這個按鍵組合下的符號輸入差異。
|
||||
|
@ -559,6 +559,27 @@ public class InputHandler: InputHandlerProtocol {
|
|||
}
|
||||
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.
|
||||
|
|
|
@ -16,8 +16,7 @@ import Shared
|
|||
|
||||
extension InputHandler {
|
||||
/// 當且僅當選字窗出現時,對於經過初次篩選處理的輸入訊號的處理均藉由此函式來進行。
|
||||
/// - Parameters:
|
||||
/// - input: 輸入訊號。
|
||||
/// - Parameter input: 輸入訊號。
|
||||
/// - Returns: 告知 IMK「該按鍵是否已經被輸入法攔截處理」。
|
||||
func handleCandidate(input: InputSignalProtocol) -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
|
@ -69,7 +68,7 @@ extension InputHandler {
|
|||
} else {
|
||||
delegate.switchState(generateStateOfInputting())
|
||||
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 {
|
||||
|
@ -212,7 +211,7 @@ extension InputHandler {
|
|||
guard let candidateIndex = ctlCandidate.candidateIndexAtKeyLabelIndex(0) else { return true }
|
||||
delegate.candidateSelectionConfirmedByInputHandler(at: candidateIndex)
|
||||
delegate.switchState(IMEState.ofAbortion())
|
||||
return handleInput(event: input)
|
||||
return triageInput(event: input)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,7 @@ import Tekkon
|
|||
|
||||
extension InputHandler {
|
||||
/// 用來處理 InputHandler.HandleInput() 當中的與組字有關的行為。
|
||||
/// - Parameters:
|
||||
/// - input: 輸入訊號。
|
||||
/// - Parameter input: 輸入訊號。
|
||||
/// - Returns: 告知 IMK「該按鍵是否已經被輸入法攔截處理」。
|
||||
func handleComposition(input: InputSignalProtocol) -> Bool? {
|
||||
// 不處理任何包含不可列印字元的訊號。
|
||||
|
@ -27,8 +26,7 @@ extension InputHandler {
|
|||
// MARK: 注音按鍵輸入處理 (Handle BPMF Keys)
|
||||
|
||||
/// 用來處理 InputHandler.HandleInput() 當中的與注音输入有關的組字行為。
|
||||
/// - Parameters:
|
||||
/// - input: 輸入訊號。
|
||||
/// - Parameter input: 輸入訊號。
|
||||
/// - Returns: 告知 IMK「該按鍵是否已經被輸入法攔截處理」。
|
||||
private func handlePhonabetComposition(input: InputSignalProtocol) -> Bool? {
|
||||
guard let delegate = delegate else { return nil }
|
||||
|
@ -208,8 +206,7 @@ extension InputHandler {
|
|||
|
||||
extension InputHandler {
|
||||
/// 用來處理 InputHandler.HandleInput() 當中的與磁帶模組有關的組字行為。
|
||||
/// - Parameters:
|
||||
/// - input: 輸入訊號。
|
||||
/// - Parameter input: 輸入訊號。
|
||||
/// - Returns: 告知 IMK「該按鍵是否已經被輸入法攔截處理」。
|
||||
private func handleCassetteComposition(input: InputSignalProtocol) -> Bool? {
|
||||
guard let delegate = delegate else { return nil }
|
||||
|
@ -339,8 +336,7 @@ extension InputHandler {
|
|||
// MARK: 區位輸入處理 (Handle Code Point Input)
|
||||
|
||||
/// 用來處理 InputHandler.HandleInput() 當中的與區位輸入有關的組字行為。
|
||||
/// - Parameters:
|
||||
/// - input: 輸入訊號。
|
||||
/// - Parameter input: 輸入訊號。
|
||||
/// - Returns: 告知 IMK「該按鍵是否已經被輸入法攔截處理」。
|
||||
private func handleCodePointComposition(input: InputSignalProtocol) -> Bool? {
|
||||
guard !input.isReservedKey else { return nil }
|
||||
|
|
|
@ -18,7 +18,7 @@ extension InputHandler {
|
|||
/// - Parameter event: 由 IMK 選字窗接收的裝置操作輸入事件。
|
||||
/// - Returns: 回「`true`」以將該按鍵已攔截處理的訊息傳遞給 IMK;回「`false`」則放行、不作處理。
|
||||
public func handleEvent(_ event: NSEvent) -> Bool {
|
||||
imkCandidatesEventPreHandler(event: event) ?? handleInput(event: event)
|
||||
imkCandidatesEventPreHandler(event: event) ?? triageInput(event: event)
|
||||
}
|
||||
|
||||
/// 專門處理與 IMK 選字窗有關的判斷語句。
|
||||
|
@ -71,7 +71,7 @@ extension InputHandler {
|
|||
let eventArray = [event]
|
||||
guard let imkC = delegate.candidateController() as? CtlCandidateIMK else { return false }
|
||||
if event.isEsc || event.isBackSpace || event.isDelete || (event.isShiftHold && !event.isSpace) {
|
||||
return handleInput(event: event)
|
||||
return triageInput(event: event)
|
||||
} else if event.isSymbolMenuPhysicalKey {
|
||||
// 符號鍵的行為是固定的,不受偏好設定影響。
|
||||
switch imkC.currentLayout {
|
||||
|
@ -118,7 +118,7 @@ extension InputHandler {
|
|||
if let newEvent = newEvent {
|
||||
if prefs.useSCPCTypingMode, delegate.state.type == .ofAssociates {
|
||||
// 註:input.isShiftHold 已經在 delegate.handle() 內處理,因為在那邊處理才有效。
|
||||
return event.isShiftHold ? true : handleInput(event: event)
|
||||
return event.isShiftHold ? true : triageInput(event: event)
|
||||
} else {
|
||||
if #available(macOS 10.14, *) {
|
||||
PrefMgr.shared.failureFlagForIMKCandidates = true
|
||||
|
@ -149,7 +149,7 @@ extension InputHandler {
|
|||
}
|
||||
|
||||
if prefs.useSCPCTypingMode, !event.isReservedKey {
|
||||
return handleInput(event: event)
|
||||
return triageInput(event: event)
|
||||
}
|
||||
|
||||
if delegate.state.type == .ofAssociates,
|
||||
|
@ -157,7 +157,7 @@ extension InputHandler {
|
|||
!event.isCursorClockLeft, !event.isCursorClockRight, !event.isSpace,
|
||||
!event.isEnter || !prefs.alsoConfirmAssociatedCandidatesByEnter
|
||||
{
|
||||
return handleInput(event: event)
|
||||
return triageInput(event: event)
|
||||
}
|
||||
imkC.interpretKeyEvents(eventArray)
|
||||
return true
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -864,7 +864,7 @@ extension InputHandler {
|
|||
return true
|
||||
}
|
||||
|
||||
// MARK: - 處理區位輸入狀態的啟動過程
|
||||
// MARK: - 處理區位輸入狀態的啟動過程(CodePoint Input Toggle)
|
||||
|
||||
@discardableResult func handleCodePointInputToggle() -> Bool {
|
||||
guard let delegate = delegate, delegate.state.type != .ofDeactivated else { return false }
|
||||
|
@ -883,7 +883,7 @@ extension InputHandler {
|
|||
return true
|
||||
}
|
||||
|
||||
// MARK: - 處理漢音鍵盤符號輸入狀態的啟動過程
|
||||
// MARK: - 處理漢音鍵盤符號輸入狀態的啟動過程(Hanin Pallete)
|
||||
|
||||
@discardableResult func handleHaninKeyboardSymbolModeToggle() -> Bool {
|
||||
guard let delegate = delegate, delegate.state.type != .ofDeactivated else { return false }
|
||||
|
@ -931,7 +931,7 @@ extension InputHandler {
|
|||
return true
|
||||
}
|
||||
|
||||
// MARK: - 處理符號選單
|
||||
// MARK: - 處理符號選單(Symbol Menu Input)
|
||||
|
||||
/// 處理符號選單。
|
||||
/// - Parameters:
|
||||
|
@ -976,4 +976,117 @@ extension InputHandler {
|
|||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -49,7 +49,7 @@
|
|||
5B78EE0D28A562B4009456C1 /* VwrPrefPaneDevZone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B78EE0C28A562B4009456C1 /* VwrPrefPaneDevZone.swift */; };
|
||||
5B7BC4B027AFFBE800F66C24 /* frmPrefWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5B7BC4AE27AFFBE800F66C24 /* frmPrefWindow.xib */; };
|
||||
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 */; };
|
||||
5B8457A12871ADBE00C93B01 /* ChineseConverterBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B8457A02871ADBE00C93B01 /* ChineseConverterBridge.swift */; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
|
@ -694,8 +694,8 @@
|
|||
5B782EC3280C243C007276DE /* InputHandler_HandleCandidate.swift */,
|
||||
5BE3779F288FED8D0037365B /* InputHandler_HandleComposition.swift */,
|
||||
5BE1F8A828F86AB5006C7FF5 /* InputHandler_HandleEvent.swift */,
|
||||
5B7F225C2808501000DDD3CB /* InputHandler_HandleInput.swift */,
|
||||
5B3133BE280B229700A4A505 /* InputHandler_HandleStates.swift */,
|
||||
5B7F225C2808501000DDD3CB /* InputHandler_TriageInput.swift */,
|
||||
5BAEFACF28012565001F42C9 /* LMMgr_Core.swift */,
|
||||
5B33844E29B8B4C200FCB497 /* LMMgr_PhraseEditorDelegate.swift */,
|
||||
5B33844C29B8980100FCB497 /* LMMgr_UserPhraseStructure.swift */,
|
||||
|
@ -1106,7 +1106,7 @@
|
|||
5BF018FD299923C200248CDD /* VwrPrefPaneCandidates.swift in Sources */,
|
||||
5B963CA828D5DB1400DCEE88 /* PrefMgr_Core.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 */,
|
||||
5BF56F9828C39A2700DD6839 /* IMEState.swift in Sources */,
|
||||
5B62A33D27AE7CC100A19448 /* CtlAboutWindow.swift in Sources */,
|
||||
|
|
Loading…
Reference in New Issue