diff --git a/Source/Modules/InputHandler_Core.swift b/Source/Modules/InputHandler_Core.swift index 38d97d40..75f7ceea 100644 --- a/Source/Modules/InputHandler_Core.swift +++ b/Source/Modules/InputHandler_Core.swift @@ -49,7 +49,7 @@ public protocol InputHandlerDelegate { var clientBundleIdentifier: String { get } func callError(_ logMessage: String) func switchState(_ newState: IMEStateProtocol) - func candidateController() -> CtlCandidateProtocol + func candidateController() -> CtlCandidateProtocol? func candidateSelectionCalledByInputHandler(at index: Int) func performUserPhraseOperation(addToFilter: Bool) -> Bool diff --git a/Source/Modules/InputHandler_HandleCandidate.swift b/Source/Modules/InputHandler_HandleCandidate.swift index 8442c343..baa8326d 100644 --- a/Source/Modules/InputHandler_HandleCandidate.swift +++ b/Source/Modules/InputHandler_HandleCandidate.swift @@ -21,7 +21,7 @@ extension InputHandler { /// - Returns: 告知 IMK「該按鍵是否已經被輸入法攔截處理」。 func handleCandidate(input: InputSignalProtocol) -> Bool { guard let delegate = delegate else { return false } - var ctlCandidate = delegate.candidateController() + guard var ctlCandidate = delegate.candidateController() else { return false } let state = delegate.state guard !state.candidates.isEmpty else { return false } diff --git a/Source/Modules/SessionCtl_Core.swift b/Source/Modules/SessionCtl_Core.swift index 2709963a..c820d7fd 100644 --- a/Source/Modules/SessionCtl_Core.swift +++ b/Source/Modules/SessionCtl_Core.swift @@ -29,16 +29,7 @@ public class SessionCtl: IMKInputController { public static var areWeNerfing = false /// 目前在用的的選字窗副本。 - public var candidateUI: CtlCandidateProtocol = { - let direction: NSUserInterfaceLayoutOrientation = - PrefMgr.shared.useHorizontalCandidateList ? .horizontal : .vertical - if #available(macOS 10.15, *) { - return PrefMgr.shared.useIMKCandidateWindow - ? CtlCandidateIMK(direction) : CtlCandidateTDK(direction) - } else { - return CtlCandidateIMK(direction) - } - }() + public var candidateUI: CtlCandidateProtocol? /// 工具提示視窗的副本。 public var tooltipInstance = TooltipUI() @@ -210,7 +201,6 @@ extension SessionCtl { // 因為偶爾會收到與 activateServer 有關的以「強制拆 nil」為理由的報錯, // 所以這裡添加這句、來試圖應對這種情況。 inputHandler.delegate = self - candidateUI.delegate = self // 這裡不需要 setValue(),因為 IMK 會在自動呼叫 activateServer() 之後自動執行 setValue()。 inputHandler.clear() // 這句不要砍,因為後面 handle State.Empty() 不一定執行。 inputHandler.ensureKeyboardParser() @@ -241,6 +231,10 @@ extension SessionCtl { isActivated = false resetInputHandler() // 這條會自動搞定 Empty 狀態。 switchState(IMEState.ofDeactivated()) + // IMK 選字窗可以不用 nil,不然反而會出問題。反正 IMK 選字窗記憶體開銷可以不計。 + if candidateUI is CtlCandidateTDK { + candidateUI = nil + } } } diff --git a/Source/Modules/SessionCtl_Delegates.swift b/Source/Modules/SessionCtl_Delegates.swift index 00ab6081..bc7a447b 100644 --- a/Source/Modules/SessionCtl_Delegates.swift +++ b/Source/Modules/SessionCtl_Delegates.swift @@ -16,7 +16,7 @@ extension SessionCtl: InputHandlerDelegate { return client.bundleIdentifier() ?? "" } - public func candidateController() -> CtlCandidateProtocol { candidateUI } + public func candidateController() -> CtlCandidateProtocol? { candidateUI } public func candidateSelectionCalledByInputHandler(at index: Int) { candidatePairSelected(at: index) diff --git a/Source/Modules/SessionCtl_HandleDisplay.swift b/Source/Modules/SessionCtl_HandleDisplay.swift index 1644e750..5e0badb0 100644 --- a/Source/Modules/SessionCtl_HandleDisplay.swift +++ b/Source/Modules/SessionCtl_HandleDisplay.swift @@ -82,7 +82,7 @@ extension SessionCtl { : .horizontal) /// 先取消既有的選字窗的內容顯示。否則可能會重複生成選字窗的 NSWindow()。 - candidateUI.visible = false + candidateUI?.visible = false /// 然後再重新初期化。 if #available(macOS 10.15, *) { candidateUI = @@ -95,22 +95,22 @@ extension SessionCtl { candidateUI = CtlCandidateIMK(candidateLayout) } - candidateUI.candidateFont = Self.candidateFont( + candidateUI?.candidateFont = Self.candidateFont( name: PrefMgr.shared.candidateTextFontName, size: PrefMgr.shared.candidateListTextSize ) if PrefMgr.shared.cassetteEnabled { - candidateUI.tooltip = + candidateUI?.tooltip = isVerticalTyping ? "📼" : "📼 " + NSLocalizedString("CIN Cassette Mode", comment: "") } if state.type == .ofAssociates { - candidateUI.tooltip = + candidateUI?.tooltip = isVerticalTyping ? "⇧" : NSLocalizedString("Hold ⇧ to choose associates.", comment: "") } - candidateUI.useLangIdentifier = PrefMgr.shared.handleDefaultCandidateFontsByLangIdentifier - candidateUI.locale = { + candidateUI?.useLangIdentifier = PrefMgr.shared.handleDefaultCandidateFontsByLangIdentifier + candidateUI?.locale = { switch inputMode { case .imeModeCHS: return "zh-Hans" case .imeModeCHT: @@ -131,11 +131,11 @@ extension SessionCtl { } } - candidateUI.delegate = self // 會自動觸發田所選字窗的資料重載。 - candidateUI.visible = true + candidateUI?.delegate = self // 會自動觸發田所選字窗的資料重載。 + candidateUI?.visible = true if isVerticalTyping { - candidateUI.set( + candidateUI?.set( windowTopLeftPoint: NSPoint( x: lineHeightRect().origin.x + lineHeightRect().size.width + 4.0, y: lineHeightRect().origin.y - 4.0 ), @@ -143,7 +143,7 @@ extension SessionCtl { useGCD: true ) } else { - candidateUI.set( + candidateUI?.set( windowTopLeftPoint: NSPoint(x: lineHeightRect().origin.x, y: lineHeightRect().origin.y - 4.0), bottomOutOfScreenAdjustmentHeight: lineHeightRect().size.height + 4.0, useGCD: true diff --git a/Source/Modules/SessionCtl_HandleStates.swift b/Source/Modules/SessionCtl_HandleStates.swift index 3cb2ce8e..30256655 100644 --- a/Source/Modules/SessionCtl_HandleStates.swift +++ b/Source/Modules/SessionCtl_HandleStates.swift @@ -45,7 +45,7 @@ extension SessionCtl { // 這裡移除一些處理,轉而交給 commitComposition() 代為執行。 // 這裡不需要 clearInlineDisplay() ,否則會觸發無限迴圈。 // 對於 IMK 選字窗的顯示狀態糾正的行為交給 inputMode.didSet() 來處理。 - candidateUI.visible = false + candidateUI?.visible = false popupCompositionBuffer.hide() tooltipInstance.hide() case .ofEmpty, .ofAbortion, .ofCommitting: @@ -58,7 +58,7 @@ extension SessionCtl { if replace { state = IMEState.ofEmpty() } default: break innerCircle } - candidateUI.visible = false + candidateUI?.visible = false // 全專案用以判斷「.Abortion」的地方僅此一處。 if previous.hasComposition, ![.ofAbortion, .ofCommitting].contains(newState.type) { commit(text: previous.displayedText) @@ -67,12 +67,12 @@ extension SessionCtl { clearInlineDisplay() inputHandler.clear() case .ofInputting: - candidateUI.visible = false + candidateUI?.visible = false commit(text: newState.textToCommit) setInlineDisplayWithCursor() showTooltip(newState.tooltip, duration: 1) // 會在工具提示為空的時候自動消除顯示。 case .ofMarking: - candidateUI.visible = false + candidateUI?.visible = false setInlineDisplayWithCursor() showTooltip(newState.tooltip) case .ofCandidates, .ofAssociates, .ofSymbolTable: