diff --git a/Source/Modules/ControllerModules/InputState.swift b/Source/Modules/ControllerModules/InputState.swift index 795e5e0f..50fc85b0 100644 --- a/Source/Modules/ControllerModules/InputState.swift +++ b/Source/Modules/ControllerModules/InputState.swift @@ -29,7 +29,7 @@ import Cocoa // 註:所有 InputState 型別均不適合使用 Struct,因為 Struct 無法相互繼承派生。 // 用以讓每個狀態自描述的 enum。 -enum StateType { +public enum StateType { case ofDeactivated case ofAssociatedPhrases case ofEmpty @@ -43,7 +43,7 @@ enum StateType { } // 所有 InputState 均遵守該協定: -protocol InputStateProtocol { +public protocol InputStateProtocol { var type: StateType { get } } @@ -79,7 +79,7 @@ protocol InputStateProtocol { /// 詞音組合放入語彙濾除清單。 /// - .ChoosingCandidate: 叫出選字窗、允許使用者選字。 /// - .SymbolTable: 波浪鍵符號選單專用的狀態,有自身的特殊處理。 -enum InputState { +public enum InputState { /// .Deactivated: 使用者沒在使用輸入法。 class Deactivated: InputStateProtocol { public var type: StateType { .ofDeactivated } diff --git a/Source/Modules/ControllerModules/KeyHandler_Core.swift b/Source/Modules/ControllerModules/KeyHandler_Core.swift index 7e9eedc3..dc54d872 100644 --- a/Source/Modules/ControllerModules/KeyHandler_Core.swift +++ b/Source/Modules/ControllerModules/KeyHandler_Core.swift @@ -46,7 +46,7 @@ protocol KeyHandlerDelegate { // MARK: - 核心 (Kernel). /// KeyHandler 按鍵調度模組。 -class KeyHandler { +public class KeyHandler { /// 半衰模組的衰減指數 let kEpsilon: Double = 0.000001 /// 檢測是否出現游標切斷組字圈內字符的情況 diff --git a/Source/Modules/ControllerModules/ctlInputMethod_Delegates.swift b/Source/Modules/ControllerModules/ctlInputMethod_Delegates.swift index c97458e4..925bb9c5 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_Delegates.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_Delegates.swift @@ -24,7 +24,7 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import Foundation +import Cocoa // MARK: - KeyHandler Delegate @@ -70,6 +70,53 @@ extension ctlInputMethod: KeyHandlerDelegate { // MARK: - Candidate Controller Delegate extension ctlInputMethod: ctlCandidateDelegate { + func handleDelegateEvent(_ event: NSEvent!) -> Bool { + /// 這裡仍舊需要判斷 flags。之前使輸入法狀態卡住無法敲漢字的問題已在 KeyHandler 內修復。 + /// 這裡不判斷 flags 的話,用方向鍵前後定位光標之後,再次試圖觸發組字區時、反而會在首次按鍵時失敗。 + /// 同時注意:必須在 event.type == .flagsChanged 結尾插入 return false, + /// 否則,每次處理這種判斷時都會觸發 NSInternalInconsistencyException。 + if event.type == .flagsChanged { + return false + } + + // 準備修飾鍵,用來判定是否需要利用就地新增語彙時的 Enter 鍵來砍詞。 + ctlInputMethod.areWeDeleting = event.modifierFlags.contains([.shift, .command]) + + var textFrame = NSRect.zero + + let attributes: [AnyHashable: Any]? = client().attributes( + forCharacterIndex: 0, lineHeightRectangle: &textFrame + ) + + let isTypingVertical = + (attributes?["IMKTextOrientation"] as? NSNumber)?.intValue == 0 || false + + if client().bundleIdentifier() + == "org.atelierInmu.vChewing.vChewingPhraseEditor" + { + IME.areWeUsingOurOwnPhraseEditor = true + } else { + IME.areWeUsingOurOwnPhraseEditor = false + } + + let input = InputSignal(event: event, isVerticalTyping: isTypingVertical) + + // 無法列印的訊號輸入,一概不作處理。 + // 這個過程不能放在 KeyHandler 內,否則不會起作用。 + if !input.charCode.isPrintable { + return false + } + + /// 將按鍵行為與當前輸入法狀態結合起來、交給按鍵調度模組來處理。 + /// 再根據返回的 result bool 數值來告知 IMK「這個按鍵事件是被處理了還是被放行了」。 + let result = keyHandler.handleCandidate(state: state, input: input) { newState in + self.handle(state: newState) + } errorCallback: { + clsSFX.beep() + } + return result + } + func candidateCountForController(_ controller: ctlCandidateProtocol) -> Int { _ = controller // 防止格式整理工具毀掉與此對應的參數。 if let state = state as? InputState.ChoosingCandidate { diff --git a/Source/UI/CandidateUI/ctlCandidate.swift b/Source/UI/CandidateUI/ctlCandidate.swift index 745f9104..017979b6 100644 --- a/Source/UI/CandidateUI/ctlCandidate.swift +++ b/Source/UI/CandidateUI/ctlCandidate.swift @@ -43,6 +43,7 @@ public class CandidateKeyLabel: NSObject { } public protocol ctlCandidateDelegate: AnyObject { + func handleDelegateEvent(_ event: NSEvent!) -> Bool func candidateCountForController(_ controller: ctlCandidateProtocol) -> Int func candidatesForController(_ controller: ctlCandidateProtocol) -> [(String, String)] func ctlCandidate(_ controller: ctlCandidateProtocol, candidateAtIndex index: Int)