InputHandler // Remove stateCallback().
This commit is contained in:
parent
4522921a0f
commit
36a83f25a2
|
@ -174,7 +174,6 @@ extension InputHandler {
|
|||
func handle(
|
||||
input: InputHandler,
|
||||
state: InputState,
|
||||
stateCallback: @escaping (InputState) -> Void,
|
||||
errorCallback: @escaping (String) -> Void
|
||||
) -> Bool {
|
||||
let charCode: UniChar = input.charCode
|
||||
|
@ -217,7 +216,7 @@ if !skipPhoneticHandling && _composer.inputValidityCheck(key: charCode) {
|
|||
// 有調號的話,則不需要這樣處理,轉而繼續在此之後的處理。
|
||||
let composeReading = _composer.hasToneMarker()
|
||||
if !composeReading {
|
||||
stateCallback(generateStateOfInputting())
|
||||
delegate.switchState(generateStateOfInputting())
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -246,7 +245,7 @@ if composeReading { // 符合按鍵組合條件
|
|||
errorCallback("114514") // 向狀態管理引擎回呼一個錯誤狀態
|
||||
_composer.clear() // 清空注拼槽的內容
|
||||
// 根據「天權星引擎 (威注音) 或 Gramambular (小麥) 的組字器是否為空」來判定回呼哪一種狀態
|
||||
stateCallback(
|
||||
delegate.switchState(
|
||||
(getCompositorLength() == 0) ? InputState.EmptyIgnoringPreviousState() : generateStateOfInputting())
|
||||
return true // 向 IMK 報告說這個按鍵訊號已經被輸入法攔截處理了
|
||||
}
|
||||
|
@ -269,7 +268,7 @@ if composeReading { // 符合按鍵組合條件
|
|||
// 再以回呼組字狀態的方式來執行updateClientComposingBuffer()
|
||||
let inputting = generateStateOfInputting()
|
||||
inputting.poppedText = poppedText
|
||||
stateCallback(inputting)
|
||||
delegate.switchState(inputting)
|
||||
|
||||
return true // 向 IMK 報告說這個按鍵訊號已經被輸入法攔截處理了
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ public protocol InputHandlerDelegate {
|
|||
var selectionKeys: String { get }
|
||||
var state: IMEStateProtocol { get set }
|
||||
var clientBundleIdentifier: String { get }
|
||||
func handle(state newState: IMEStateProtocol, replaceCurrent: Bool)
|
||||
func switchState(_ newState: IMEStateProtocol)
|
||||
func candidateController() -> CtlCandidateProtocol
|
||||
func candidateSelectionCalledByInputHandler(at index: Int)
|
||||
func performUserPhraseOperation(with state: IMEStateProtocol, addToFilter: Bool)
|
||||
|
|
|
@ -18,20 +18,20 @@ extension InputHandler {
|
|||
/// - Parameters:
|
||||
/// - input: 輸入訊號。
|
||||
/// - state: 給定狀態(通常為當前狀態)。
|
||||
/// - stateCallback: 狀態回呼,交給對應的型別內的專有函式來處理。
|
||||
/// - errorCallback: 錯誤回呼。
|
||||
/// - Returns: 告知 IMK「該按鍵是否已經被輸入法攔截處理」。
|
||||
func handleCandidate(
|
||||
state: IMEStateProtocol,
|
||||
input: InputSignalProtocol,
|
||||
stateCallback: @escaping (IMEStateProtocol) -> Void,
|
||||
errorCallback: @escaping (String) -> Void
|
||||
) -> Bool {
|
||||
guard var ctlCandidate = delegate?.candidateController() else {
|
||||
guard let delegate = delegate else {
|
||||
errorCallback("06661F6E")
|
||||
return true
|
||||
}
|
||||
|
||||
var ctlCandidate = delegate.candidateController()
|
||||
|
||||
// MARK: 取消選字 (Cancel Candidate)
|
||||
|
||||
let cancelCandidateKey =
|
||||
|
@ -47,12 +47,12 @@ extension InputHandler {
|
|||
// 就將當前的組字緩衝區析構處理、強制重設輸入狀態。
|
||||
// 否則,一個本不該出現的真空組字緩衝區會使前後方向鍵與 BackSpace 鍵失靈。
|
||||
// 所以這裡需要對 compositor.isEmpty 做判定。
|
||||
stateCallback(IMEState.ofAbortion())
|
||||
delegate.switchState(IMEState.ofAbortion())
|
||||
} else {
|
||||
stateCallback(generateStateOfInputting())
|
||||
delegate.switchState(generateStateOfInputting())
|
||||
}
|
||||
if state.type == .ofSymbolTable, let nodePrevious = state.node.previous, !nodePrevious.members.isEmpty {
|
||||
stateCallback(IMEState.ofSymbolTable(node: nodePrevious))
|
||||
delegate.switchState(IMEState.ofSymbolTable(node: nodePrevious))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -61,10 +61,10 @@ extension InputHandler {
|
|||
|
||||
if input.isEnter {
|
||||
if state.type == .ofAssociates, !prefs.alsoConfirmAssociatedCandidatesByEnter {
|
||||
stateCallback(IMEState.ofAbortion())
|
||||
delegate.switchState(IMEState.ofAbortion())
|
||||
return true
|
||||
}
|
||||
delegate?.candidateSelectionCalledByInputHandler(at: ctlCandidate.selectedCandidateIndex)
|
||||
delegate.candidateSelectionCalledByInputHandler(at: ctlCandidate.selectedCandidateIndex)
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -232,7 +232,7 @@ extension InputHandler {
|
|||
let match: String =
|
||||
(state.type == .ofAssociates) ? input.inputTextIgnoringModifiers ?? "" : input.text
|
||||
|
||||
let selectionKeys = delegate?.selectionKeys ?? PrefMgr.shared.candidateKeys
|
||||
let selectionKeys = delegate.selectionKeys
|
||||
|
||||
for j in 0..<selectionKeys.count {
|
||||
let label = selectionKeys.charComponents[j]
|
||||
|
@ -245,7 +245,7 @@ extension InputHandler {
|
|||
if index != NSNotFound {
|
||||
let candidateIndex = ctlCandidate.candidateIndexAtKeyLabelIndex(index)
|
||||
if candidateIndex != -114_514 {
|
||||
delegate?.candidateSelectionCalledByInputHandler(at: candidateIndex)
|
||||
delegate.candidateSelectionCalledByInputHandler(at: candidateIndex)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -285,10 +285,10 @@ extension InputHandler {
|
|||
if shouldAutoSelectCandidate {
|
||||
let candidateIndex = ctlCandidate.candidateIndexAtKeyLabelIndex(0)
|
||||
if candidateIndex != -114_514 {
|
||||
delegate?.candidateSelectionCalledByInputHandler(at: candidateIndex)
|
||||
stateCallback(IMEState.ofAbortion())
|
||||
delegate.candidateSelectionCalledByInputHandler(at: candidateIndex)
|
||||
delegate.switchState(IMEState.ofAbortion())
|
||||
return handleInput(
|
||||
event: input, state: IMEState.ofEmpty(), stateCallback: stateCallback, errorCallback: errorCallback
|
||||
event: input, state: IMEState.ofEmpty(), errorCallback: errorCallback
|
||||
)
|
||||
}
|
||||
return true
|
||||
|
|
|
@ -15,14 +15,14 @@ extension InputHandler {
|
|||
/// 用來處理 InputHandler.HandleInput() 當中的與組字有關的行為。
|
||||
/// - Parameters:
|
||||
/// - input: 輸入訊號。
|
||||
/// - stateCallback: 狀態回呼,交給對應的型別內的專有函式來處理。
|
||||
/// - errorCallback: 錯誤回呼。
|
||||
/// - Returns: 告知 IMK「該按鍵是否已經被輸入法攔截處理」。
|
||||
func handleComposition(
|
||||
input: InputSignalProtocol,
|
||||
stateCallback: @escaping (IMEStateProtocol) -> Void,
|
||||
errorCallback: @escaping (String) -> Void
|
||||
) -> Bool? {
|
||||
guard let delegate = delegate else { return nil }
|
||||
|
||||
// MARK: 注音按鍵輸入處理 (Handle BPMF Keys)
|
||||
|
||||
var keyConsumedByReading = false
|
||||
|
@ -65,7 +65,7 @@ extension InputHandler {
|
|||
// 沒有調號的話,只需要 setInlineDisplayWithCursor() 且終止處理(return true)即可。
|
||||
// 有調號的話,則不需要這樣,而是轉而繼續在此之後的處理。
|
||||
if !composer.hasToneMarker() {
|
||||
stateCallback(generateStateOfInputting())
|
||||
delegate.switchState(generateStateOfInputting())
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -90,16 +90,16 @@ extension InputHandler {
|
|||
|
||||
if prefs.keepReadingUponCompositionError {
|
||||
composer.intonation.clear() // 砍掉聲調。
|
||||
stateCallback(generateStateOfInputting())
|
||||
delegate.switchState(generateStateOfInputting())
|
||||
return true
|
||||
}
|
||||
|
||||
composer.clear()
|
||||
// 根據「組字器是否為空」來判定回呼哪一種狀態。
|
||||
switch compositor.isEmpty {
|
||||
case false: stateCallback(generateStateOfInputting())
|
||||
case false: delegate.switchState(generateStateOfInputting())
|
||||
case true:
|
||||
stateCallback(IMEState.ofAbortion())
|
||||
delegate.switchState(IMEState.ofAbortion())
|
||||
}
|
||||
return true // 向 IMK 報告說這個按鍵訊號已經被輸入法攔截處理了。
|
||||
}
|
||||
|
@ -122,27 +122,27 @@ extension InputHandler {
|
|||
// 再以回呼組字狀態的方式來執行 setInlineDisplayWithCursor()。
|
||||
var inputting = generateStateOfInputting()
|
||||
inputting.textToCommit = textToCommit
|
||||
stateCallback(inputting)
|
||||
delegate.switchState(inputting)
|
||||
|
||||
/// 逐字選字模式的處理。
|
||||
if prefs.useSCPCTypingMode {
|
||||
let candidateState: IMEStateProtocol = generateStateOfCandidates(state: inputting)
|
||||
switch candidateState.candidates.count {
|
||||
case 2...: stateCallback(candidateState)
|
||||
case 2...: delegate.switchState(candidateState)
|
||||
case 1:
|
||||
let firstCandidate = candidateState.candidates.first! // 一定會有,所以強制拆包也無妨。
|
||||
let reading: String = firstCandidate.0
|
||||
let text: String = firstCandidate.1
|
||||
stateCallback(IMEState.ofCommitting(textToCommit: text))
|
||||
delegate.switchState(IMEState.ofCommitting(textToCommit: text))
|
||||
|
||||
if !prefs.associatedPhrasesEnabled {
|
||||
stateCallback(IMEState.ofEmpty())
|
||||
delegate.switchState(IMEState.ofEmpty())
|
||||
} else {
|
||||
let associatedPhrases =
|
||||
generateStateOfAssociates(
|
||||
withPair: .init(key: reading, value: text)
|
||||
)
|
||||
stateCallback(associatedPhrases.candidates.isEmpty ? IMEState.ofEmpty() : associatedPhrases)
|
||||
delegate.switchState(associatedPhrases.candidates.isEmpty ? IMEState.ofEmpty() : associatedPhrases)
|
||||
}
|
||||
default: break
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ extension InputHandler {
|
|||
/// 是說此時注拼槽並非為空、卻還沒組音。這種情況下只可能是「注拼槽內只有聲調」。
|
||||
if keyConsumedByReading {
|
||||
// 以回呼組字狀態的方式來執行 setInlineDisplayWithCursor()。
|
||||
stateCallback(generateStateOfInputting())
|
||||
delegate.switchState(generateStateOfInputting())
|
||||
return true
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -18,22 +18,18 @@ extension InputHandler {
|
|||
/// - Parameter event: 由 IMK 選字窗接收的裝置操作輸入事件。
|
||||
/// - Returns: 回「`true`」以將該案件已攔截處理的訊息傳遞給 IMK;回「`false`」則放行、不作處理。
|
||||
public func handleEvent(_ event: NSEvent) -> Bool {
|
||||
imkCandidatesEventPreHandler(event: event) ?? commonEventHandler(event)
|
||||
imkCandidatesEventPreHandler(event: event) ?? doHandleInput(event)
|
||||
}
|
||||
|
||||
/// 將按鍵行為與當前輸入法狀態結合起來、交給按鍵調度模組來處理。
|
||||
/// 再根據返回的 result bool 數值來告知 IMK「這個按鍵事件是被處理了還是被放行了」。
|
||||
/// 這裡不用 handleCandidate() 是因為需要針對聯想詞輸入狀態做額外處理。
|
||||
private func commonEventHandler(_ event: NSEvent) -> Bool {
|
||||
private func doHandleInput(_ event: NSEvent) -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
|
||||
let result = handleInput(event: event, state: delegate.state) { newState in
|
||||
delegate.handle(state: newState, replaceCurrent: true)
|
||||
} errorCallback: { errorString in
|
||||
return handleInput(event: event, state: delegate.state) { errorString in
|
||||
vCLog(errorString)
|
||||
IMEApp.buzz()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/// 專門處理與 IMK 選字窗有關的判斷語句。
|
||||
|
@ -84,7 +80,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 commonEventHandler(event)
|
||||
return doHandleInput(event)
|
||||
} else if event.isSymbolMenuPhysicalKey {
|
||||
// 符號鍵的行為是固定的,不受偏好設定影響。
|
||||
switch imkC.currentLayout {
|
||||
|
@ -113,7 +109,7 @@ extension InputHandler {
|
|||
if let newEvent = newEvent {
|
||||
if prefs.useSCPCTypingMode, delegate.state.type == .ofAssociates {
|
||||
// 註:input.isShiftHold 已經在 Self.handle() 內處理,因為在那邊處理才有效。
|
||||
return event.isShiftHold ? true : commonEventHandler(event)
|
||||
return event.isShiftHold ? true : doHandleInput(event)
|
||||
} else {
|
||||
if #available(macOS 10.14, *) {
|
||||
imkC.handleKeyboardEvent(newEvent)
|
||||
|
@ -126,7 +122,7 @@ extension InputHandler {
|
|||
}
|
||||
|
||||
if prefs.useSCPCTypingMode, !event.isReservedKey {
|
||||
return commonEventHandler(event)
|
||||
return doHandleInput(event)
|
||||
}
|
||||
|
||||
if delegate.state.type == .ofAssociates,
|
||||
|
@ -134,7 +130,7 @@ extension InputHandler {
|
|||
!event.isCursorClockLeft, !event.isCursorClockRight, !event.isSpace,
|
||||
!event.isEnter || !prefs.alsoConfirmAssociatedCandidatesByEnter
|
||||
{
|
||||
return commonEventHandler(event)
|
||||
return doHandleInput(event)
|
||||
}
|
||||
imkC.interpretKeyEvents(eventArray)
|
||||
return true
|
||||
|
|
|
@ -21,17 +21,17 @@ extension InputHandler {
|
|||
/// - Parameters:
|
||||
/// - input: 輸入訊號。
|
||||
/// - state: 給定狀態(通常為當前狀態)。
|
||||
/// - stateCallback: 狀態回呼,交給對應的型別內的專有函式來處理。
|
||||
/// - errorCallback: 錯誤回呼。
|
||||
/// - Returns: 告知 IMK「該按鍵是否已經被輸入法攔截處理」。
|
||||
func handleInput(
|
||||
event input: InputSignalProtocol,
|
||||
state: IMEStateProtocol,
|
||||
stateCallback: @escaping (IMEStateProtocol) -> Void,
|
||||
errorCallback: @escaping (String) -> Void
|
||||
) -> Bool {
|
||||
// 如果按鍵訊號內的 inputTest 是空的話,則忽略該按鍵輸入,因為很可能是功能修飾鍵。
|
||||
guard !input.text.isEmpty, input.charCode.isPrintable else { return false }
|
||||
// 不處理任何包含不可列印字元的訊號。
|
||||
// delegate 必須存在,否則不處理。
|
||||
guard !input.text.isEmpty, input.charCode.isPrintable, let delegate = delegate else { return false }
|
||||
|
||||
let inputText: String = input.text
|
||||
var state = state // 常數轉變數。
|
||||
|
@ -44,7 +44,7 @@ extension InputHandler {
|
|||
return false
|
||||
}
|
||||
errorCallback("550BCF7B: InputHandler just refused an invalid input.")
|
||||
stateCallback(state)
|
||||
delegate.switchState(state)
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ extension InputHandler {
|
|||
// 略過對 BackSpace 的處理。
|
||||
} else if input.isCapsLockOn || state.isASCIIMode {
|
||||
// 但願能夠處理這種情況下所有可能的按鍵組合。
|
||||
stateCallback(IMEState.ofEmpty())
|
||||
delegate.switchState(IMEState.ofEmpty())
|
||||
|
||||
// 字母鍵摁 Shift 的話,無須額外處理,因為直接就會敲出大寫字母。
|
||||
if (input.isUpperCaseASCIILetterKey && state.isASCIIMode)
|
||||
|
@ -82,8 +82,8 @@ extension InputHandler {
|
|||
}
|
||||
|
||||
// 將整個組字區的內容遞交給客體應用。
|
||||
stateCallback(IMEState.ofCommitting(textToCommit: inputText.lowercased()))
|
||||
stateCallback(IMEState.ofEmpty())
|
||||
delegate.switchState(IMEState.ofCommitting(textToCommit: inputText.lowercased()))
|
||||
delegate.switchState(IMEState.ofEmpty())
|
||||
|
||||
return true
|
||||
}
|
||||
|
@ -97,9 +97,9 @@ extension InputHandler {
|
|||
if !(state.type == .ofCandidates || state.type == .ofAssociates
|
||||
|| state.type == .ofSymbolTable)
|
||||
{
|
||||
stateCallback(IMEState.ofEmpty())
|
||||
stateCallback(IMEState.ofCommitting(textToCommit: inputText.lowercased()))
|
||||
stateCallback(IMEState.ofEmpty())
|
||||
delegate.switchState(IMEState.ofEmpty())
|
||||
delegate.switchState(IMEState.ofCommitting(textToCommit: inputText.lowercased()))
|
||||
delegate.switchState(IMEState.ofEmpty())
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ extension InputHandler {
|
|||
|
||||
if [.ofCandidates, .ofSymbolTable].contains(state.type) {
|
||||
return handleCandidate(
|
||||
state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback
|
||||
state: state, input: input, errorCallback: errorCallback
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -116,11 +116,11 @@ extension InputHandler {
|
|||
|
||||
if state.type == .ofAssociates {
|
||||
if handleCandidate(
|
||||
state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback
|
||||
state: state, input: input, errorCallback: errorCallback
|
||||
) {
|
||||
return true
|
||||
} else {
|
||||
stateCallback(IMEState.ofEmpty())
|
||||
delegate.switchState(IMEState.ofEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,19 +128,19 @@ extension InputHandler {
|
|||
|
||||
if state.type == .ofMarking {
|
||||
if handleMarkingState(
|
||||
state, input: input, stateCallback: stateCallback,
|
||||
state, input: input,
|
||||
errorCallback: errorCallback
|
||||
) {
|
||||
return true
|
||||
}
|
||||
state = state.convertedToInputting
|
||||
stateCallback(state)
|
||||
delegate.switchState(state)
|
||||
}
|
||||
|
||||
// MARK: 注音按鍵輸入處理 (Handle BPMF Keys)
|
||||
|
||||
if let compositionHandled = handleComposition(
|
||||
input: input, stateCallback: stateCallback, errorCallback: errorCallback
|
||||
input: input, errorCallback: errorCallback
|
||||
) {
|
||||
return compositionHandled
|
||||
}
|
||||
|
@ -157,10 +157,10 @@ extension InputHandler {
|
|||
if compositor.cursor >= compositor.length {
|
||||
let displayedText = state.displayedText
|
||||
if !displayedText.isEmpty {
|
||||
stateCallback(IMEState.ofCommitting(textToCommit: displayedText))
|
||||
delegate.switchState(IMEState.ofCommitting(textToCommit: displayedText))
|
||||
}
|
||||
stateCallback(IMEState.ofCommitting(textToCommit: " "))
|
||||
stateCallback(IMEState.ofEmpty())
|
||||
delegate.switchState(IMEState.ofCommitting(textToCommit: " "))
|
||||
delegate.switchState(IMEState.ofEmpty())
|
||||
} else if currentLM.hasUnigramsFor(key: " ") {
|
||||
compositor.insertKey(" ")
|
||||
walk()
|
||||
|
@ -168,12 +168,12 @@ extension InputHandler {
|
|||
let textToCommit = commitOverflownComposition
|
||||
var inputting = generateStateOfInputting()
|
||||
inputting.textToCommit = textToCommit
|
||||
stateCallback(inputting)
|
||||
delegate.switchState(inputting)
|
||||
}
|
||||
return true
|
||||
} else if input.isShiftHold { // 臉書等網站會攔截 Tab 鍵,所以用 Shift+Command+Space 對候選字詞做正向/反向輪替。
|
||||
return handleInlineCandidateRotation(
|
||||
state: state, reverseModifier: input.isCommandHold, stateCallback: stateCallback,
|
||||
state: state, reverseModifier: input.isCommandHold,
|
||||
errorCallback: errorCallback
|
||||
)
|
||||
}
|
||||
|
@ -182,7 +182,7 @@ extension InputHandler {
|
|||
if candidateState.candidates.isEmpty {
|
||||
errorCallback("3572F238")
|
||||
} else {
|
||||
stateCallback(candidateState)
|
||||
delegate.switchState(candidateState)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -191,56 +191,56 @@ extension InputHandler {
|
|||
|
||||
if let keyCodeType = KeyCode(rawValue: input.keyCode) {
|
||||
switch keyCodeType {
|
||||
case .kEscape: return handleEsc(state: state, stateCallback: stateCallback)
|
||||
case .kEscape: return handleEsc(state: state)
|
||||
case .kTab:
|
||||
return handleInlineCandidateRotation(
|
||||
state: state, reverseModifier: input.isShiftHold, stateCallback: stateCallback, errorCallback: errorCallback
|
||||
state: state, reverseModifier: input.isShiftHold, errorCallback: errorCallback
|
||||
)
|
||||
case .kUpArrow, .kDownArrow, .kLeftArrow, .kRightArrow:
|
||||
if (input.isControlHold || input.isShiftHold) && (input.isOptionHold) {
|
||||
if input.isLeft { // Ctrl+PgLf / Shift+PgLf
|
||||
return handleHome(state: state, stateCallback: stateCallback, errorCallback: errorCallback)
|
||||
return handleHome(state: state, errorCallback: errorCallback)
|
||||
} else if input.isRight { // Ctrl+PgRt or Shift+PgRt
|
||||
return handleEnd(state: state, stateCallback: stateCallback, errorCallback: errorCallback)
|
||||
return handleEnd(state: state, errorCallback: errorCallback)
|
||||
}
|
||||
}
|
||||
if input.isCursorBackward { // Forward
|
||||
return handleBackward(
|
||||
state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback
|
||||
state: state, input: input, errorCallback: errorCallback
|
||||
)
|
||||
}
|
||||
if input.isCursorForward { // Backward
|
||||
return handleForward(
|
||||
state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback
|
||||
state: state, input: input, errorCallback: errorCallback
|
||||
)
|
||||
}
|
||||
if input.isCursorClockLeft || input.isCursorClockRight { // Clock keys
|
||||
if input.isOptionHold, state.type == .ofInputting {
|
||||
if input.isCursorClockRight {
|
||||
return handleInlineCandidateRotation(
|
||||
state: state, reverseModifier: false, stateCallback: stateCallback, errorCallback: errorCallback
|
||||
state: state, reverseModifier: false, errorCallback: errorCallback
|
||||
)
|
||||
}
|
||||
if input.isCursorClockLeft {
|
||||
return handleInlineCandidateRotation(
|
||||
state: state, reverseModifier: true, stateCallback: stateCallback, errorCallback: errorCallback
|
||||
state: state, reverseModifier: true, errorCallback: errorCallback
|
||||
)
|
||||
}
|
||||
}
|
||||
return handleClockKey(state: state, stateCallback: stateCallback, errorCallback: errorCallback)
|
||||
return handleClockKey(state: state, errorCallback: errorCallback)
|
||||
}
|
||||
case .kHome: return handleHome(state: state, stateCallback: stateCallback, errorCallback: errorCallback)
|
||||
case .kEnd: return handleEnd(state: state, stateCallback: stateCallback, errorCallback: errorCallback)
|
||||
case .kHome: return handleHome(state: state, errorCallback: errorCallback)
|
||||
case .kEnd: return handleEnd(state: state, errorCallback: errorCallback)
|
||||
case .kBackSpace:
|
||||
return handleBackSpace(state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback)
|
||||
return handleBackSpace(state: state, input: input, errorCallback: errorCallback)
|
||||
case .kWindowsDelete:
|
||||
return handleDelete(state: state, input: input, stateCallback: stateCallback, errorCallback: errorCallback)
|
||||
return handleDelete(state: state, input: input, errorCallback: errorCallback)
|
||||
case .kCarriageReturn, .kLineFeed:
|
||||
return (input.isCommandHold && input.isControlHold)
|
||||
? (input.isOptionHold
|
||||
? handleCtrlOptionCommandEnter(state: state, stateCallback: stateCallback)
|
||||
: handleCtrlCommandEnter(state: state, stateCallback: stateCallback))
|
||||
: handleEnter(state: state, stateCallback: stateCallback)
|
||||
? handleCtrlOptionCommandEnter(state: state)
|
||||
: handleCtrlCommandEnter(state: state))
|
||||
: handleEnter(state: state)
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
@ -257,12 +257,12 @@ extension InputHandler {
|
|||
let textToCommit = commitOverflownComposition
|
||||
var inputting = generateStateOfInputting()
|
||||
inputting.textToCommit = textToCommit
|
||||
stateCallback(inputting)
|
||||
delegate.switchState(inputting)
|
||||
let candidateState = generateStateOfCandidates(state: inputting)
|
||||
if candidateState.candidates.isEmpty {
|
||||
errorCallback("B5127D8A")
|
||||
} else {
|
||||
stateCallback(candidateState)
|
||||
delegate.switchState(candidateState)
|
||||
}
|
||||
} else { // 不要在注音沒敲完整的情況下叫出統合符號選單。
|
||||
errorCallback("17446655")
|
||||
|
@ -273,8 +273,8 @@ extension InputHandler {
|
|||
// 得在這裡先 commit buffer,不然會導致「在摁 ESC 離開符號選單時會重複輸入上一次的組字區的內容」的不當行為。
|
||||
// 於是這裡用「模擬一次 Enter 鍵的操作」使其代為執行這個 commit buffer 的動作。
|
||||
// 這裡不需要該函式所傳回的 bool 結果,所以用「_ =」解消掉。
|
||||
_ = handleEnter(state: state, stateCallback: stateCallback)
|
||||
stateCallback(IMEState.ofSymbolTable(node: CandidateNode.root))
|
||||
_ = handleEnter(state: state)
|
||||
delegate.switchState(IMEState.ofSymbolTable(node: CandidateNode.root))
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -286,10 +286,10 @@ extension InputHandler {
|
|||
guard let stringRAW = input.mainAreaNumKeyChar else { return false }
|
||||
let newStringFW = stringRAW.applyingTransform(.fullwidthToHalfwidth, reverse: true) ?? stringRAW
|
||||
let newStringHW = stringRAW.applyingTransform(.fullwidthToHalfwidth, reverse: false) ?? stringRAW
|
||||
stateCallback(
|
||||
delegate.switchState(
|
||||
IMEState.ofCommitting(textToCommit: prefs.halfWidthPunctuationEnabled ? newStringHW : newStringFW)
|
||||
)
|
||||
stateCallback(IMEState.ofEmpty())
|
||||
delegate.switchState(IMEState.ofEmpty())
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -307,7 +307,6 @@ extension InputHandler {
|
|||
if handlePunctuation(
|
||||
customPunctuation,
|
||||
state: state,
|
||||
stateCallback: stateCallback,
|
||||
errorCallback: errorCallback
|
||||
) {
|
||||
return true
|
||||
|
@ -321,7 +320,6 @@ extension InputHandler {
|
|||
if handlePunctuation(
|
||||
punctuation,
|
||||
state: state,
|
||||
stateCallback: stateCallback,
|
||||
errorCallback: errorCallback
|
||||
) {
|
||||
return true
|
||||
|
@ -332,8 +330,8 @@ extension InputHandler {
|
|||
/// 該功能僅可在當前組字區沒有任何內容的時候使用。
|
||||
if state.type == .ofEmpty {
|
||||
if input.isSpace, !input.isOptionHold, !input.isControlHold, !input.isCommandHold {
|
||||
stateCallback(IMEState.ofCommitting(textToCommit: input.isShiftHold ? " " : " "))
|
||||
stateCallback(IMEState.ofEmpty())
|
||||
delegate.switchState(IMEState.ofCommitting(textToCommit: input.isShiftHold ? " " : " "))
|
||||
delegate.switchState(IMEState.ofEmpty())
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -344,22 +342,21 @@ extension InputHandler {
|
|||
if input.isShiftHold { // 這裡先不要判斷 isOptionHold。
|
||||
switch prefs.upperCaseLetterKeyBehavior {
|
||||
case 1:
|
||||
stateCallback(IMEState.ofEmpty())
|
||||
stateCallback(IMEState.ofCommitting(textToCommit: inputText.lowercased()))
|
||||
stateCallback(IMEState.ofEmpty())
|
||||
delegate.switchState(IMEState.ofEmpty())
|
||||
delegate.switchState(IMEState.ofCommitting(textToCommit: inputText.lowercased()))
|
||||
delegate.switchState(IMEState.ofEmpty())
|
||||
return true
|
||||
|
||||
case 2:
|
||||
stateCallback(IMEState.ofEmpty())
|
||||
stateCallback(IMEState.ofCommitting(textToCommit: inputText.uppercased()))
|
||||
stateCallback(IMEState.ofEmpty())
|
||||
delegate.switchState(IMEState.ofEmpty())
|
||||
delegate.switchState(IMEState.ofCommitting(textToCommit: inputText.uppercased()))
|
||||
delegate.switchState(IMEState.ofEmpty())
|
||||
return true
|
||||
default: // 包括 case 0,直接塞給組字區。
|
||||
let letter = "_letter_\(inputText)"
|
||||
if handlePunctuation(
|
||||
letter,
|
||||
state: state,
|
||||
stateCallback: stateCallback,
|
||||
errorCallback: errorCallback
|
||||
) {
|
||||
return true
|
||||
|
@ -378,7 +375,7 @@ extension InputHandler {
|
|||
errorCallback(
|
||||
"Blocked data: charCode: \(input.charCode), keyCode: \(input.keyCode)")
|
||||
errorCallback("A9BFF20E")
|
||||
stateCallback(state)
|
||||
delegate.switchState(state)
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
@ -123,17 +123,17 @@ extension InputHandler {
|
|||
/// - Parameters:
|
||||
/// - state: 當前狀態。
|
||||
/// - input: 輸入按鍵訊號。
|
||||
/// - stateCallback: 狀態回呼。
|
||||
/// - errorCallback: 錯誤回呼。
|
||||
/// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。
|
||||
func handleMarkingState(
|
||||
_ state: IMEStateProtocol,
|
||||
input: InputSignalProtocol,
|
||||
stateCallback: @escaping (IMEStateProtocol) -> Void,
|
||||
errorCallback: @escaping (String) -> Void
|
||||
) -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
|
||||
if input.isEsc {
|
||||
stateCallback(generateStateOfInputting())
|
||||
delegate.switchState(generateStateOfInputting())
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -145,38 +145,34 @@ extension InputHandler {
|
|||
|
||||
// Enter
|
||||
if input.isEnter {
|
||||
if let sessionCtl = delegate {
|
||||
// 先判斷是否是在摁了降權組合鍵的時候目標不在庫。
|
||||
if input.isShiftHold, input.isCommandHold, !state.isFilterable {
|
||||
errorCallback("2EAC1F7A")
|
||||
return true
|
||||
}
|
||||
if !state.isMarkedLengthValid {
|
||||
errorCallback("9AAFAC00")
|
||||
return true
|
||||
}
|
||||
if !sessionCtl.performUserPhraseOperation(with: state, addToFilter: false) {
|
||||
errorCallback("5B69CC8D")
|
||||
return true
|
||||
}
|
||||
// 先判斷是否是在摁了降權組合鍵的時候目標不在庫。
|
||||
if input.isShiftHold, input.isCommandHold, !state.isFilterable {
|
||||
errorCallback("2EAC1F7A")
|
||||
return true
|
||||
}
|
||||
stateCallback(generateStateOfInputting())
|
||||
if !state.isMarkedLengthValid {
|
||||
errorCallback("9AAFAC00")
|
||||
return true
|
||||
}
|
||||
if !delegate.performUserPhraseOperation(with: state, addToFilter: false) {
|
||||
errorCallback("5B69CC8D")
|
||||
return true
|
||||
}
|
||||
delegate.switchState(generateStateOfInputting())
|
||||
return true
|
||||
}
|
||||
|
||||
// BackSpace & Delete
|
||||
if input.isBackSpace || input.isDelete {
|
||||
if let inputHandlerDelegate = delegate {
|
||||
if !state.isFilterable {
|
||||
errorCallback("1F88B191")
|
||||
return true
|
||||
}
|
||||
if !inputHandlerDelegate.performUserPhraseOperation(with: state, addToFilter: true) {
|
||||
errorCallback("68D3C6C8")
|
||||
return true
|
||||
}
|
||||
if !state.isFilterable {
|
||||
errorCallback("1F88B191")
|
||||
return true
|
||||
}
|
||||
stateCallback(generateStateOfInputting())
|
||||
if !delegate.performUserPhraseOperation(with: state, addToFilter: true) {
|
||||
errorCallback("68D3C6C8")
|
||||
return true
|
||||
}
|
||||
delegate.switchState(generateStateOfInputting())
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -194,10 +190,10 @@ extension InputHandler {
|
|||
marker: convertCursorForDisplay(compositor.marker)
|
||||
)
|
||||
marking.tooltipBackupForInputting = state.tooltipBackupForInputting
|
||||
stateCallback(marking.markedRange.isEmpty ? marking.convertedToInputting : marking)
|
||||
delegate.switchState(marking.markedRange.isEmpty ? marking.convertedToInputting : marking)
|
||||
} else {
|
||||
errorCallback("1149908D")
|
||||
stateCallback(state)
|
||||
delegate.switchState(state)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -216,10 +212,10 @@ extension InputHandler {
|
|||
marker: convertCursorForDisplay(compositor.marker)
|
||||
)
|
||||
marking.tooltipBackupForInputting = state.tooltipBackupForInputting
|
||||
stateCallback(marking.markedRange.isEmpty ? marking.convertedToInputting : marking)
|
||||
delegate.switchState(marking.markedRange.isEmpty ? marking.convertedToInputting : marking)
|
||||
} else {
|
||||
errorCallback("9B51408D")
|
||||
stateCallback(state)
|
||||
delegate.switchState(state)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -231,15 +227,15 @@ extension InputHandler {
|
|||
/// 標點輸入的處理。
|
||||
/// - Parameters:
|
||||
/// - customPunctuation: 自訂標點索引鍵頭。
|
||||
/// - stateCallback: 狀態回呼。
|
||||
/// - errorCallback: 錯誤回呼。
|
||||
/// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。
|
||||
func handlePunctuation(
|
||||
_ customPunctuation: String,
|
||||
state: IMEStateProtocol,
|
||||
stateCallback: @escaping (IMEStateProtocol) -> Void,
|
||||
errorCallback: @escaping (String) -> Void
|
||||
) -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
|
||||
if !currentLM.hasUnigramsFor(key: customPunctuation) {
|
||||
return false
|
||||
}
|
||||
|
@ -247,7 +243,7 @@ extension InputHandler {
|
|||
guard composer.isEmpty else {
|
||||
// 注音沒敲完的情況下,無視標點輸入。
|
||||
errorCallback("A9B69908D")
|
||||
stateCallback(state)
|
||||
delegate.switchState(state)
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -257,21 +253,21 @@ extension InputHandler {
|
|||
let textToCommit = commitOverflownComposition
|
||||
var inputting = generateStateOfInputting()
|
||||
inputting.textToCommit = textToCommit
|
||||
stateCallback(inputting)
|
||||
delegate.switchState(inputting)
|
||||
|
||||
// 從這一行之後開始,就是針對逐字選字模式的單獨處理。
|
||||
guard prefs.useSCPCTypingMode, composer.isEmpty else { return true }
|
||||
|
||||
let candidateState = generateStateOfCandidates(state: inputting)
|
||||
switch candidateState.candidates.count {
|
||||
case 2...: stateCallback(candidateState)
|
||||
case 2...: delegate.switchState(candidateState)
|
||||
case 1:
|
||||
clear() // 這句不要砍,因為下文可能會回呼 candidateState。
|
||||
if let candidateToCommit: (String, String) = candidateState.candidates.first, !candidateToCommit.1.isEmpty {
|
||||
stateCallback(IMEState.ofCommitting(textToCommit: candidateToCommit.1))
|
||||
stateCallback(IMEState.ofEmpty())
|
||||
delegate.switchState(IMEState.ofCommitting(textToCommit: candidateToCommit.1))
|
||||
delegate.switchState(IMEState.ofEmpty())
|
||||
} else {
|
||||
stateCallback(candidateState)
|
||||
delegate.switchState(candidateState)
|
||||
}
|
||||
default: errorCallback("8DA4096E")
|
||||
}
|
||||
|
@ -283,16 +279,15 @@ extension InputHandler {
|
|||
/// Enter 鍵的處理。
|
||||
/// - Parameters:
|
||||
/// - state: 當前狀態。
|
||||
/// - stateCallback: 狀態回呼。
|
||||
/// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。
|
||||
func handleEnter(
|
||||
state: IMEStateProtocol,
|
||||
stateCallback: @escaping (IMEStateProtocol) -> Void
|
||||
state: IMEStateProtocol
|
||||
) -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
guard state.type == .ofInputting else { return false }
|
||||
|
||||
stateCallback(IMEState.ofCommitting(textToCommit: state.displayedText))
|
||||
stateCallback(IMEState.ofEmpty())
|
||||
delegate.switchState(IMEState.ofCommitting(textToCommit: state.displayedText))
|
||||
delegate.switchState(IMEState.ofEmpty())
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -301,12 +296,11 @@ extension InputHandler {
|
|||
/// Command+Enter 鍵的處理(注音文)。
|
||||
/// - Parameters:
|
||||
/// - state: 當前狀態。
|
||||
/// - stateCallback: 狀態回呼。
|
||||
/// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。
|
||||
func handleCtrlCommandEnter(
|
||||
state: IMEStateProtocol,
|
||||
stateCallback: @escaping (IMEStateProtocol) -> Void
|
||||
state: IMEStateProtocol
|
||||
) -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
guard state.type == .ofInputting else { return false }
|
||||
|
||||
var displayedText = compositor.keys.joined(separator: "-")
|
||||
|
@ -315,12 +309,12 @@ extension InputHandler {
|
|||
displayedText = Tekkon.cnvPhonaToHanyuPinyin(target: displayedText) // 注音轉拼音
|
||||
}
|
||||
|
||||
if let delegate = delegate, !delegate.clientBundleIdentifier.contains("vChewingPhraseEditor") {
|
||||
if !delegate.clientBundleIdentifier.contains("vChewingPhraseEditor") {
|
||||
displayedText = displayedText.replacingOccurrences(of: "-", with: " ")
|
||||
}
|
||||
|
||||
stateCallback(IMEState.ofCommitting(textToCommit: displayedText))
|
||||
stateCallback(IMEState.ofEmpty())
|
||||
delegate.switchState(IMEState.ofCommitting(textToCommit: displayedText))
|
||||
delegate.switchState(IMEState.ofEmpty())
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -329,12 +323,11 @@ extension InputHandler {
|
|||
/// Command+Option+Enter 鍵的處理(網頁 Ruby 注音文標記)。
|
||||
/// - Parameters:
|
||||
/// - state: 當前狀態。
|
||||
/// - stateCallback: 狀態回呼。
|
||||
/// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。
|
||||
func handleCtrlOptionCommandEnter(
|
||||
state: IMEStateProtocol,
|
||||
stateCallback: @escaping (IMEStateProtocol) -> Void
|
||||
state: IMEStateProtocol
|
||||
) -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
guard state.type == .ofInputting else { return false }
|
||||
|
||||
var composed = ""
|
||||
|
@ -355,8 +348,8 @@ extension InputHandler {
|
|||
composed += key.contains("_") ? value : "<ruby>\(value)<rp>(</rp><rt>\(key)</rt><rp>)</rp></ruby>"
|
||||
}
|
||||
|
||||
stateCallback(IMEState.ofCommitting(textToCommit: composed))
|
||||
stateCallback(IMEState.ofEmpty())
|
||||
delegate.switchState(IMEState.ofCommitting(textToCommit: composed))
|
||||
delegate.switchState(IMEState.ofEmpty())
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -366,15 +359,14 @@ extension InputHandler {
|
|||
/// - Parameters:
|
||||
/// - state: 當前狀態。
|
||||
/// - input: 輸入按鍵訊號。
|
||||
/// - stateCallback: 狀態回呼。
|
||||
/// - errorCallback: 錯誤回呼。
|
||||
/// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。
|
||||
func handleBackSpace(
|
||||
state: IMEStateProtocol,
|
||||
input: InputSignalProtocol,
|
||||
stateCallback: @escaping (IMEStateProtocol) -> Void,
|
||||
errorCallback: @escaping (String) -> Void
|
||||
) -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
guard state.type == .ofInputting else { return false }
|
||||
|
||||
// 引入 macOS 內建注音輸入法的行為,允許用 Shift+BackSpace 解構前一個漢字的讀音。
|
||||
|
@ -386,16 +378,16 @@ extension InputHandler {
|
|||
compositor.dropKey(direction: .rear)
|
||||
walk() // 這裡必須 Walk 一次、來更新目前被 walk 的內容。
|
||||
prevReading.1.charComponents.forEach { composer.receiveKey(fromPhonabet: $0) }
|
||||
stateCallback(generateStateOfInputting())
|
||||
delegate.switchState(generateStateOfInputting())
|
||||
return true
|
||||
case 1:
|
||||
stateCallback(IMEState.ofAbortion())
|
||||
delegate.switchState(IMEState.ofAbortion())
|
||||
return true
|
||||
default: break
|
||||
}
|
||||
|
||||
if input.isShiftHold, input.isOptionHold {
|
||||
stateCallback(IMEState.ofAbortion())
|
||||
delegate.switchState(IMEState.ofAbortion())
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -407,7 +399,7 @@ extension InputHandler {
|
|||
walk()
|
||||
} else {
|
||||
errorCallback("9D69908D")
|
||||
stateCallback(state)
|
||||
delegate.switchState(state)
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
|
@ -415,9 +407,9 @@ extension InputHandler {
|
|||
}
|
||||
|
||||
switch composer.isEmpty && compositor.isEmpty {
|
||||
case false: stateCallback(generateStateOfInputting())
|
||||
case false: delegate.switchState(generateStateOfInputting())
|
||||
case true:
|
||||
stateCallback(IMEState.ofAbortion())
|
||||
delegate.switchState(IMEState.ofAbortion())
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -428,25 +420,24 @@ extension InputHandler {
|
|||
/// - Parameters:
|
||||
/// - state: 當前狀態。
|
||||
/// - input: 輸入按鍵訊號。
|
||||
/// - stateCallback: 狀態回呼。
|
||||
/// - errorCallback: 錯誤回呼。
|
||||
/// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。
|
||||
func handleDelete(
|
||||
state: IMEStateProtocol,
|
||||
input: InputSignalProtocol,
|
||||
stateCallback: @escaping (IMEStateProtocol) -> Void,
|
||||
errorCallback: @escaping (String) -> Void
|
||||
) -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
guard state.type == .ofInputting else { return false }
|
||||
|
||||
if input.isShiftHold {
|
||||
stateCallback(IMEState.ofAbortion())
|
||||
delegate.switchState(IMEState.ofAbortion())
|
||||
return true
|
||||
}
|
||||
|
||||
if compositor.cursor == compositor.length, composer.isEmpty {
|
||||
errorCallback("9B69938D")
|
||||
stateCallback(state)
|
||||
delegate.switchState(state)
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -460,9 +451,9 @@ extension InputHandler {
|
|||
let inputting = generateStateOfInputting()
|
||||
// 這裡不用「count > 0」,因為該整數變數只要「!isEmpty」那就必定滿足這個條件。
|
||||
switch inputting.displayedText.isEmpty {
|
||||
case false: stateCallback(inputting)
|
||||
case false: delegate.switchState(inputting)
|
||||
case true:
|
||||
stateCallback(IMEState.ofAbortion())
|
||||
delegate.switchState(IMEState.ofAbortion())
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -472,19 +463,18 @@ extension InputHandler {
|
|||
/// 處理與當前文字輸入排版前後方向呈 90 度的那兩個方向鍵的按鍵行為。
|
||||
/// - Parameters:
|
||||
/// - state: 當前狀態。
|
||||
/// - stateCallback: 狀態回呼。
|
||||
/// - errorCallback: 錯誤回呼。
|
||||
/// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。
|
||||
func handleClockKey(
|
||||
state: IMEStateProtocol,
|
||||
stateCallback: @escaping (IMEStateProtocol) -> Void,
|
||||
errorCallback: @escaping (String) -> Void
|
||||
) -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
guard state.type == .ofInputting else { return false }
|
||||
if !composer.isEmpty {
|
||||
errorCallback("9B6F908D")
|
||||
}
|
||||
stateCallback(state)
|
||||
delegate.switchState(state)
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -493,28 +483,27 @@ extension InputHandler {
|
|||
/// 處理 Home 鍵的行為。
|
||||
/// - Parameters:
|
||||
/// - state: 當前狀態。
|
||||
/// - stateCallback: 狀態回呼。
|
||||
/// - errorCallback: 錯誤回呼。
|
||||
/// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。
|
||||
func handleHome(
|
||||
state: IMEStateProtocol,
|
||||
stateCallback: @escaping (IMEStateProtocol) -> Void,
|
||||
errorCallback: @escaping (String) -> Void
|
||||
) -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
guard state.type == .ofInputting else { return false }
|
||||
|
||||
if !composer.isEmpty {
|
||||
errorCallback("ABC44080")
|
||||
stateCallback(state)
|
||||
delegate.switchState(state)
|
||||
return true
|
||||
}
|
||||
|
||||
if compositor.cursor != 0 {
|
||||
compositor.cursor = 0
|
||||
stateCallback(generateStateOfInputting())
|
||||
delegate.switchState(generateStateOfInputting())
|
||||
} else {
|
||||
errorCallback("66D97F90")
|
||||
stateCallback(state)
|
||||
delegate.switchState(state)
|
||||
}
|
||||
|
||||
return true
|
||||
|
@ -525,28 +514,27 @@ extension InputHandler {
|
|||
/// 處理 End 鍵的行為。
|
||||
/// - Parameters:
|
||||
/// - state: 當前狀態。
|
||||
/// - stateCallback: 狀態回呼。
|
||||
/// - errorCallback: 錯誤回呼。
|
||||
/// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。
|
||||
func handleEnd(
|
||||
state: IMEStateProtocol,
|
||||
stateCallback: @escaping (IMEStateProtocol) -> Void,
|
||||
errorCallback: @escaping (String) -> Void
|
||||
) -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
guard state.type == .ofInputting else { return false }
|
||||
|
||||
if !composer.isEmpty {
|
||||
errorCallback("9B69908D")
|
||||
stateCallback(state)
|
||||
delegate.switchState(state)
|
||||
return true
|
||||
}
|
||||
|
||||
if compositor.cursor != compositor.length {
|
||||
compositor.cursor = compositor.length
|
||||
stateCallback(generateStateOfInputting())
|
||||
delegate.switchState(generateStateOfInputting())
|
||||
} else {
|
||||
errorCallback("9B69908E")
|
||||
stateCallback(state)
|
||||
delegate.switchState(state)
|
||||
}
|
||||
|
||||
return true
|
||||
|
@ -557,26 +545,25 @@ extension InputHandler {
|
|||
/// 處理 Esc 鍵的行為。
|
||||
/// - Parameters:
|
||||
/// - state: 當前狀態。
|
||||
/// - stateCallback: 狀態回呼。
|
||||
/// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。
|
||||
func handleEsc(
|
||||
state: IMEStateProtocol,
|
||||
stateCallback: @escaping (IMEStateProtocol) -> Void
|
||||
state: IMEStateProtocol
|
||||
) -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
guard state.type == .ofInputting else { return false }
|
||||
|
||||
if prefs.escToCleanInputBuffer {
|
||||
/// 若啟用了該選項,則清空組字器的內容與注拼槽的內容。
|
||||
/// 此乃 macOS 內建注音輸入法預設之行為,但不太受 Windows 使用者群體之待見。
|
||||
stateCallback(IMEState.ofAbortion())
|
||||
delegate.switchState(IMEState.ofAbortion())
|
||||
} else {
|
||||
if composer.isEmpty { return true }
|
||||
/// 如果注拼槽不是空的話,則清空之。
|
||||
composer.clear()
|
||||
switch compositor.isEmpty {
|
||||
case false: stateCallback(generateStateOfInputting())
|
||||
case false: delegate.switchState(generateStateOfInputting())
|
||||
case true:
|
||||
stateCallback(IMEState.ofAbortion())
|
||||
delegate.switchState(IMEState.ofAbortion())
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
@ -588,20 +575,19 @@ extension InputHandler {
|
|||
/// - Parameters:
|
||||
/// - state: 當前狀態。
|
||||
/// - input: 輸入按鍵訊號。
|
||||
/// - stateCallback: 狀態回呼。
|
||||
/// - errorCallback: 錯誤回呼。
|
||||
/// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。
|
||||
func handleForward(
|
||||
state: IMEStateProtocol,
|
||||
input: InputSignalProtocol,
|
||||
stateCallback: @escaping (IMEStateProtocol) -> Void,
|
||||
errorCallback: @escaping (String) -> Void
|
||||
) -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
guard state.type == .ofInputting else { return false }
|
||||
|
||||
if !composer.isEmpty {
|
||||
errorCallback("B3BA5257")
|
||||
stateCallback(state)
|
||||
delegate.switchState(state)
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -619,32 +605,32 @@ extension InputHandler {
|
|||
marker: convertCursorForDisplay(compositor.marker)
|
||||
)
|
||||
marking.tooltipBackupForInputting = state.tooltip
|
||||
stateCallback(marking)
|
||||
delegate.switchState(marking)
|
||||
} else {
|
||||
errorCallback("BB7F6DB9")
|
||||
stateCallback(state)
|
||||
delegate.switchState(state)
|
||||
}
|
||||
} else if input.isOptionHold {
|
||||
if input.isControlHold {
|
||||
return handleEnd(state: state, stateCallback: stateCallback, errorCallback: errorCallback)
|
||||
return handleEnd(state: state, errorCallback: errorCallback)
|
||||
}
|
||||
// 游標跳轉動作無論怎樣都會執行,但如果出了執行失敗的結果的話則觸發報錯流程。
|
||||
if !compositor.jumpCursorBySpan(to: .front) {
|
||||
errorCallback("33C3B580")
|
||||
stateCallback(state)
|
||||
delegate.switchState(state)
|
||||
return true
|
||||
}
|
||||
stateCallback(generateStateOfInputting())
|
||||
delegate.switchState(generateStateOfInputting())
|
||||
} else {
|
||||
if compositor.cursor < compositor.length {
|
||||
compositor.cursor += 1
|
||||
if isCursorCuttingChar() {
|
||||
compositor.jumpCursorBySpan(to: .front)
|
||||
}
|
||||
stateCallback(generateStateOfInputting())
|
||||
delegate.switchState(generateStateOfInputting())
|
||||
} else {
|
||||
errorCallback("A96AAD58")
|
||||
stateCallback(state)
|
||||
delegate.switchState(state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -657,20 +643,19 @@ extension InputHandler {
|
|||
/// - Parameters:
|
||||
/// - state: 當前狀態。
|
||||
/// - input: 輸入按鍵訊號。
|
||||
/// - stateCallback: 狀態回呼。
|
||||
/// - errorCallback: 錯誤回呼。
|
||||
/// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。
|
||||
func handleBackward(
|
||||
state: IMEStateProtocol,
|
||||
input: InputSignalProtocol,
|
||||
stateCallback: @escaping (IMEStateProtocol) -> Void,
|
||||
errorCallback: @escaping (String) -> Void
|
||||
) -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
guard state.type == .ofInputting else { return false }
|
||||
|
||||
if !composer.isEmpty {
|
||||
errorCallback("6ED95318")
|
||||
stateCallback(state)
|
||||
delegate.switchState(state)
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -688,32 +673,32 @@ extension InputHandler {
|
|||
marker: convertCursorForDisplay(compositor.marker)
|
||||
)
|
||||
marking.tooltipBackupForInputting = state.tooltip
|
||||
stateCallback(marking)
|
||||
delegate.switchState(marking)
|
||||
} else {
|
||||
errorCallback("D326DEA3")
|
||||
stateCallback(state)
|
||||
delegate.switchState(state)
|
||||
}
|
||||
} else if input.isOptionHold {
|
||||
if input.isControlHold {
|
||||
return handleHome(state: state, stateCallback: stateCallback, errorCallback: errorCallback)
|
||||
return handleHome(state: state, errorCallback: errorCallback)
|
||||
}
|
||||
// 游標跳轉動作無論怎樣都會執行,但如果出了執行失敗的結果的話則觸發報錯流程。
|
||||
if !compositor.jumpCursorBySpan(to: .rear) {
|
||||
errorCallback("8D50DD9E")
|
||||
stateCallback(state)
|
||||
delegate.switchState(state)
|
||||
return true
|
||||
}
|
||||
stateCallback(generateStateOfInputting())
|
||||
delegate.switchState(generateStateOfInputting())
|
||||
} else {
|
||||
if compositor.cursor > 0 {
|
||||
compositor.cursor -= 1
|
||||
if isCursorCuttingChar() {
|
||||
compositor.jumpCursorBySpan(to: .rear)
|
||||
}
|
||||
stateCallback(generateStateOfInputting())
|
||||
delegate.switchState(generateStateOfInputting())
|
||||
} else {
|
||||
errorCallback("7045E6F3")
|
||||
stateCallback(state)
|
||||
delegate.switchState(state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -726,15 +711,14 @@ extension InputHandler {
|
|||
/// - Parameters:
|
||||
/// - state: 當前狀態。
|
||||
/// - reverseModifier: 是否有控制輪替方向的修飾鍵輸入。
|
||||
/// - stateCallback: 狀態回呼。
|
||||
/// - errorCallback: 錯誤回呼。
|
||||
/// - Returns: 將按鍵行為「是否有處理掉」藉由 SessionCtl 回報給 IMK。
|
||||
func handleInlineCandidateRotation(
|
||||
state: IMEStateProtocol,
|
||||
reverseModifier: Bool,
|
||||
stateCallback: @escaping (IMEStateProtocol) -> Void,
|
||||
errorCallback: @escaping (String) -> Void
|
||||
) -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
if composer.isEmpty, compositor.isEmpty || compositor.walkedNodes.isEmpty { return false }
|
||||
guard state.type == .ofInputting else {
|
||||
guard state.type == .ofEmpty else {
|
||||
|
@ -814,7 +798,7 @@ extension InputHandler {
|
|||
|
||||
consolidateNode(candidate: candidates[currentIndex], respectCursorPushing: false, preConsolidate: false)
|
||||
|
||||
stateCallback(generateStateOfInputting())
|
||||
delegate.switchState(generateStateOfInputting())
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ public class SessionCtl: IMKInputController {
|
|||
// ----------------------------
|
||||
Self.isVerticalTyping = isVerticalTyping
|
||||
// 強制重設當前鍵盤佈局、使其與偏好設定同步。這裡的這一步也不能省略。
|
||||
handle(state: IMEState.ofEmpty())
|
||||
switchState(IMEState.ofEmpty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,14 +170,14 @@ extension SessionCtl {
|
|||
// 過濾掉尚未完成拼寫的注音。
|
||||
if state.type == .ofInputting, PrefMgr.shared.trimUnfinishedReadingsOnCommit {
|
||||
inputHandler.composer.clear()
|
||||
handle(state: inputHandler.generateStateOfInputting())
|
||||
switchState(inputHandler.generateStateOfInputting())
|
||||
}
|
||||
let isSecureMode = PrefMgr.shared.clientsIMKTextInputIncapable.contains(clientBundleIdentifier)
|
||||
if state.hasComposition, !isSecureMode {
|
||||
/// 將傳回的新狀態交給調度函式。
|
||||
handle(state: IMEState.ofCommitting(textToCommit: state.displayedText))
|
||||
switchState(IMEState.ofCommitting(textToCommit: state.displayedText))
|
||||
}
|
||||
handle(state: isSecureMode ? IMEState.ofAbortion() : IMEState.ofEmpty())
|
||||
switchState(isSecureMode ? IMEState.ofAbortion() : IMEState.ofEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,7 +208,7 @@ extension SessionCtl {
|
|||
UpdateSputnik.shared.checkForUpdate(forced: false, url: kUpdateInfoSourceURL)
|
||||
}
|
||||
|
||||
handle(state: IMEState.ofEmpty())
|
||||
switchState(IMEState.ofEmpty())
|
||||
Self.allInstances.insert(self)
|
||||
}
|
||||
|
||||
|
@ -217,7 +217,7 @@ extension SessionCtl {
|
|||
public override func deactivateServer(_ sender: Any!) {
|
||||
_ = sender // 防止格式整理工具毀掉與此對應的參數。
|
||||
resetInputHandler() // 這條會自動搞定 Empty 狀態。
|
||||
handle(state: IMEState.ofDeactivated())
|
||||
switchState(IMEState.ofDeactivated())
|
||||
Self.allInstances.remove(self)
|
||||
}
|
||||
|
||||
|
|
|
@ -75,11 +75,11 @@ extension SessionCtl: CtlCandidateDelegate {
|
|||
if state.type == .ofSymbolTable, (0..<state.node.members.count).contains(index) {
|
||||
let node = state.node.members[index]
|
||||
if !node.members.isEmpty {
|
||||
handle(state: IMEState.ofEmpty()) // 防止縱橫排選字窗同時出現
|
||||
handle(state: IMEState.ofSymbolTable(node: node))
|
||||
switchState(IMEState.ofEmpty()) // 防止縱橫排選字窗同時出現
|
||||
switchState(IMEState.ofSymbolTable(node: node))
|
||||
} else {
|
||||
handle(state: IMEState.ofCommitting(textToCommit: node.name))
|
||||
handle(state: IMEState.ofEmpty())
|
||||
switchState(IMEState.ofCommitting(textToCommit: node.name))
|
||||
switchState(IMEState.ofEmpty())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -94,29 +94,29 @@ extension SessionCtl: CtlCandidateDelegate {
|
|||
let inputting = inputHandler.generateStateOfInputting()
|
||||
|
||||
if PrefMgr.shared.useSCPCTypingMode {
|
||||
handle(state: IMEState.ofCommitting(textToCommit: inputting.displayedText))
|
||||
switchState(IMEState.ofCommitting(textToCommit: inputting.displayedText))
|
||||
// 此時是逐字選字模式,所以「selectedValue.1」是單個字、不用追加處理。
|
||||
if PrefMgr.shared.associatedPhrasesEnabled {
|
||||
let associates = inputHandler.generateStateOfAssociates(
|
||||
withPair: .init(key: selectedValue.0, value: selectedValue.1)
|
||||
)
|
||||
handle(state: associates.candidates.isEmpty ? IMEState.ofEmpty() : associates)
|
||||
switchState(associates.candidates.isEmpty ? IMEState.ofEmpty() : associates)
|
||||
} else {
|
||||
handle(state: IMEState.ofEmpty())
|
||||
switchState(IMEState.ofEmpty())
|
||||
}
|
||||
} else {
|
||||
handle(state: inputting)
|
||||
switchState(inputting)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if state.type == .ofAssociates {
|
||||
let selectedValue = state.candidates[index]
|
||||
handle(state: IMEState.ofCommitting(textToCommit: selectedValue.1))
|
||||
switchState(IMEState.ofCommitting(textToCommit: selectedValue.1))
|
||||
// 此時是聯想詞選字模式,所以「selectedValue.1」必須只保留最後一個字。
|
||||
// 不然的話,一旦你選中了由多個字組成的聯想候選詞,則連續聯想會被打斷。
|
||||
guard let valueKept = selectedValue.1.last else {
|
||||
handle(state: IMEState.ofEmpty())
|
||||
switchState(IMEState.ofEmpty())
|
||||
return
|
||||
}
|
||||
if PrefMgr.shared.associatedPhrasesEnabled {
|
||||
|
@ -124,11 +124,11 @@ extension SessionCtl: CtlCandidateDelegate {
|
|||
withPair: .init(key: selectedValue.0, value: String(valueKept))
|
||||
)
|
||||
if !associates.candidates.isEmpty {
|
||||
handle(state: associates)
|
||||
switchState(associates)
|
||||
return
|
||||
}
|
||||
}
|
||||
handle(state: IMEState.ofEmpty())
|
||||
switchState(IMEState.ofEmpty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ extension SessionCtl {
|
|||
|
||||
// 如果是 deactivated 狀態的話,強制糾正其為 empty()。
|
||||
if let client = client(), state.type == .ofDeactivated {
|
||||
handle(state: IMEState.ofEmpty())
|
||||
switchState(IMEState.ofEmpty())
|
||||
return handle(event, client: client)
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,20 @@ import Shared
|
|||
// MARK: - 狀態調度 (State Handling)
|
||||
|
||||
extension SessionCtl {
|
||||
/// 針對傳入的新狀態進行調度、且將當前會話控制器的狀態切換至新狀態。
|
||||
///
|
||||
/// 先將舊狀態單獨記錄起來,再將新舊狀態作為參數,
|
||||
/// 根據新狀態本身的狀態種類來判斷交給哪一個專門的函式來處理。
|
||||
/// - Remark: ⚠️ 任何在這個函式當中被改變的變數均不得是靜態 (Static) 變數。
|
||||
/// 針對某一個客體的 deactivateServer() 可能會在使用者切換到另一個客體應用
|
||||
/// 且開始敲字之後才會執行。這個過程會使得不同的 SessionCtl 副本之間出現
|
||||
/// 不必要的互相干涉、打斷彼此的工作。
|
||||
/// - Note: 本來不用這麼複雜的,奈何 Swift Protocol 不允許給參數指定預設值。
|
||||
/// - Parameter newState: 新狀態。
|
||||
public func switchState(_ newState: IMEStateProtocol) {
|
||||
handle(state: newState, replace: true)
|
||||
}
|
||||
|
||||
/// 針對傳入的新狀態進行調度。
|
||||
///
|
||||
/// 先將舊狀態單獨記錄起來,再將新舊狀態作為參數,
|
||||
|
@ -21,9 +35,9 @@ extension SessionCtl {
|
|||
/// 且開始敲字之後才會執行。這個過程會使得不同的 SessionCtl 副本之間出現
|
||||
/// 不必要的互相干涉、打斷彼此的工作。
|
||||
/// - Parameter newState: 新狀態。
|
||||
public func handle(state newState: IMEStateProtocol, replaceCurrent: Bool = true) {
|
||||
public func handle(state newState: IMEStateProtocol, replace: Bool) {
|
||||
var previous = state
|
||||
if replaceCurrent { state = newState }
|
||||
if replace { state = newState }
|
||||
switch newState.type {
|
||||
case .ofDeactivated:
|
||||
ctlCandidateCurrent.visible = false
|
||||
|
@ -43,14 +57,14 @@ extension SessionCtl {
|
|||
for instance in Self.allInstances {
|
||||
guard let imkC = instance.ctlCandidateCurrent as? CtlCandidateIMK else { continue }
|
||||
if instance.state.isCandidateContainer, !imkC.visible {
|
||||
instance.handle(state: instance.state, replaceCurrent: false)
|
||||
instance.handle(state: instance.state, replace: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
case .ofEmpty, .ofAbortion:
|
||||
if newState.type == .ofAbortion {
|
||||
previous = IMEState.ofEmpty()
|
||||
if replaceCurrent { state = previous }
|
||||
if replace { state = previous }
|
||||
}
|
||||
ctlCandidateCurrent.visible = false
|
||||
tooltipInstance.hide()
|
||||
|
|
|
@ -75,7 +75,7 @@ extension SessionCtl {
|
|||
let candidateString: String = candidateString?.string ?? ""
|
||||
if state.type == .ofAssociates {
|
||||
if !PrefMgr.shared.alsoConfirmAssociatedCandidatesByEnter {
|
||||
handle(state: IMEState.ofAbortion())
|
||||
switchState(IMEState.ofAbortion())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue