From b0c2bfc54e11004dd256d6057552a4c9afbff348 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Wed, 14 Jun 2023 11:11:24 +0800 Subject: [PATCH] SessionCtl // Cope with `%quick` candidates. --- Source/Modules/SessionCtl_Delegates.swift | 37 +++++++++++++++---- Source/Modules/SessionCtl_HandleDisplay.swift | 30 +++++++-------- Source/Modules/SessionCtl_HandleStates.swift | 1 + .../SessionCtl_IMKCandidatesData.swift | 25 +++++++++---- .../Resources/Base.lproj/Localizable.strings | 1 + Source/Resources/en.lproj/Localizable.strings | 1 + Source/Resources/ja.lproj/Localizable.strings | 1 + .../zh-Hans.lproj/Localizable.strings | 1 + .../zh-Hant.lproj/Localizable.strings | 1 + 9 files changed, 67 insertions(+), 31 deletions(-) diff --git a/Source/Modules/SessionCtl_Delegates.swift b/Source/Modules/SessionCtl_Delegates.swift index c73c10cd..c14e3b58 100644 --- a/Source/Modules/SessionCtl_Delegates.swift +++ b/Source/Modules/SessionCtl_Delegates.swift @@ -84,6 +84,12 @@ extension SessionCtl: CtlCandidateDelegate { let blankResult: [String] = [] // 這一段專門處理「反查」。 if !PrefMgr.shared.showReverseLookupInCandidateUI { return blankResult } + if state.type == .ofInputting, state.isCandidateContainer, + inputHandler?.currentLM.nullCandidateInCassette == value + { + return blankResult + } + if !PrefMgr.shared.showReverseLookupInCandidateUI { return blankResult } if isVerticalTyping { return blankResult } // 縱排輸入的場合,選字窗沒有足夠的空間顯示反查結果。 if value.isEmpty { return blankResult } // 空字串沒有需要反查的東西。 if value.contains("_") { return blankResult } @@ -92,7 +98,17 @@ extension SessionCtl: CtlCandidateDelegate { } public var selectionKeys: String { - PrefMgr.shared.useIMKCandidateWindow ? "123456789" : PrefMgr.shared.candidateKeys + // `%quick` 模式僅支援 1234567890 選字鍵。 + if state.type == .ofInputting, state.isCandidateContainer { + guard let cinCandidateKey = LMMgr.currentLM.cassetteSelectionKey, + CandidateKey.validate(keys: cinCandidateKey) == nil + else { + return "1234567890" + } + return cinCandidateKey + } + if PrefMgr.shared.useIMKCandidateWindow { return "123456789" } + return PrefMgr.shared.candidateKeys } public func candidatePairs(conv: Bool = false) -> [(keyArray: [String], value: String)] { @@ -114,6 +130,7 @@ extension SessionCtl: CtlCandidateDelegate { public func candidatePairSelectionConfirmed(at index: Int) { guard let inputHandler = inputHandler else { return } + guard state.isCandidateContainer else { return } switch state.type { case .ofSymbolTable where (0 ..< state.node.members.count).contains(index): let node = state.node.members[index] @@ -124,12 +141,10 @@ extension SessionCtl: CtlCandidateDelegate { } case .ofCandidates where (0 ..< state.candidates.count).contains(index): let selectedValue = state.candidates[index] - if state.type == .ofCandidates { - inputHandler.consolidateNode( - candidate: selectedValue, respectCursorPushing: true, - preConsolidate: PrefMgr.shared.consolidateContextOnCandidateSelection - ) - } + inputHandler.consolidateNode( + candidate: selectedValue, respectCursorPushing: true, + preConsolidate: PrefMgr.shared.consolidateContextOnCandidateSelection + ) var result: IMEStateProtocol = inputHandler.generateStateOfInputting() defer { switchState(result) } // 這是最終輸出結果。 if PrefMgr.shared.useSCPCTypingMode { @@ -157,6 +172,14 @@ extension SessionCtl: CtlCandidateDelegate { withPair: .init(keyArray: selectedValue.keyArray, value: valueKept) ) if !associates.candidates.isEmpty { result = associates } + case .ofInputting where (0 ..< state.candidates.count).contains(index): + let chosenStr = state.candidates[index].value + guard !chosenStr.isEmpty, chosenStr != inputHandler.currentLM.nullCandidateInCassette else { + callError("907F9F64") + return + } + let strToCommitFirst = inputHandler.generateStateOfInputting(sansReading: true).displayedText + switchState(IMEState.ofCommitting(textToCommit: strToCommitFirst + chosenStr)) default: return } } diff --git a/Source/Modules/SessionCtl_HandleDisplay.swift b/Source/Modules/SessionCtl_HandleDisplay.swift index 88b0b3e0..cf12c3a5 100644 --- a/Source/Modules/SessionCtl_HandleDisplay.swift +++ b/Source/Modules/SessionCtl_HandleDisplay.swift @@ -84,35 +84,33 @@ public extension SessionCtl { ? .vertical : .horizontal) + let isInputtingWithCandidates = state.type == .ofInputting && state.isCandidateContainer /// 先取消既有的選字窗的內容顯示。否則可能會重複生成選字窗的 NSWindow()。 candidateUI?.visible = false - /// 然後再重新初期化。 if #available(macOS 10.13, *) { - candidateUI = - PrefMgr.shared.useIMKCandidateWindow - ? CtlCandidateIMK(candidateLayout) : CtlCandidateTDK(candidateLayout) - if let candidateTDK = candidateUI as? CtlCandidateTDK { - let singleLine = isVerticalTyping || PrefMgr.shared.candidateWindowShowOnlyOneLine - candidateTDK.maxLinesPerPage = singleLine ? 1 : 4 - } + /// 然後再重新初期化。 + let useIMK = PrefMgr.shared.useIMKCandidateWindow + candidateUI = useIMK ? CtlCandidateIMK(candidateLayout) : CtlCandidateTDK(candidateLayout) } else { candidateUI = CtlCandidateTDK(candidateLayout) } + var singleLine = isVerticalTyping || PrefMgr.shared.candidateWindowShowOnlyOneLine + singleLine = singleLine || isInputtingWithCandidates + (candidateUI as? CtlCandidateTDK)?.maxLinesPerPage = singleLine ? 1 : 4 candidateUI?.candidateFont = Self.candidateFont( name: PrefMgr.shared.candidateTextFontName, size: PrefMgr.shared.candidateListTextSize ) - let singleColumn = isVerticalTyping || PrefMgr.shared.candidateWindowShowOnlyOneLine - - if PrefMgr.shared.cassetteEnabled { - candidateUI?.tooltip = - singleColumn ? "📼" : "📼 " + NSLocalizedString("CIN Cassette Mode", comment: "") - } - if state.type == .ofAssociates { candidateUI?.tooltip = - singleColumn ? "⇧" : NSLocalizedString("Hold ⇧ to choose associates.", comment: "") + singleLine ? "⇧" : NSLocalizedString("Hold ⇧ to choose associates.", comment: "") + } else if state.type == .ofInputting, state.isCandidateContainer { + candidateUI?.tooltip = + singleLine ? "⚡️" : "⚡️ " + NSLocalizedString("Quick Candidates", comment: "") + } else if PrefMgr.shared.cassetteEnabled { + candidateUI?.tooltip = + singleLine ? "📼" : "📼 " + NSLocalizedString("CIN Cassette Mode", comment: "") } candidateUI?.locale = { diff --git a/Source/Modules/SessionCtl_HandleStates.swift b/Source/Modules/SessionCtl_HandleStates.swift index 4e0f397f..0cce28d8 100644 --- a/Source/Modules/SessionCtl_HandleStates.swift +++ b/Source/Modules/SessionCtl_HandleStates.swift @@ -75,6 +75,7 @@ public extension SessionCtl { setInlineDisplayWithCursor() // 會在工具提示為空的時候自動消除顯示。 showTooltip(newState.tooltip, duration: newState.tooltipDuration) + if newState.isCandidateContainer { showCandidates() } case .ofMarking: candidateUI?.visible = false setInlineDisplayWithCursor() diff --git a/Source/Modules/SessionCtl_IMKCandidatesData.swift b/Source/Modules/SessionCtl_IMKCandidatesData.swift index 3799f0f3..77def6a3 100644 --- a/Source/Modules/SessionCtl_IMKCandidatesData.swift +++ b/Source/Modules/SessionCtl_IMKCandidatesData.swift @@ -49,18 +49,23 @@ public extension SessionCtl { } } - if state.type == .ofAssociates { + switch state.type { + case .ofDeactivated, .ofEmpty, .ofAbortion, .ofCommitting, .ofMarking: break + case .ofAssociates: handleIMKCandidatesPrepared(state.candidates, prefix: "⇧") - } else if state.type == .ofSymbolTable { - // 分類符號選單不會出現同符異音項、不需要康熙 / JIS 轉換,所以使用簡化過的處理方式。 - arrResult = state.candidates.map(\.value) - } else if state.type == .ofCandidates { + case .ofInputting where state.isCandidateContainer: + handleIMKCandidatesPrepared(state.candidates, prefix: "🗲") + case .ofCandidates: guard !state.candidates.isEmpty else { return .init() } if state.candidates[0].keyArray.joined(separator: "-").contains("_punctuation") { arrResult = state.candidates.map(\.value) // 標點符號選單處理。 } else { handleIMKCandidatesPrepared(state.candidates) } + case .ofSymbolTable: + // 分類符號選單不會出現同符異音項、不需要康熙 / JIS 轉換,所以使用簡化過的處理方式。 + arrResult = state.candidates.map(\.value) + default: break } return arrResult @@ -113,17 +118,21 @@ public extension SessionCtl { } } - if state.type == .ofAssociates { + switch state.type { + case .ofAssociates: fixIndexForIMKCandidates(&indexDeducted, prefix: "⇧", source: candidateString) - } else if state.type == .ofSymbolTable { + case .ofInputting where state.isCandidateContainer: + fixIndexForIMKCandidates(&indexDeducted, prefix: "🗲", source: candidateString) + case .ofSymbolTable: fixSymbolIndexForIMKCandidates() - } else if state.type == .ofCandidates { + case .ofCandidates: guard !state.candidates.isEmpty else { return } if state.candidates[0].keyArray.description.contains("_punctuation") { fixSymbolIndexForIMKCandidates() // 標點符號選單處理。 } else { fixIndexForIMKCandidates(&indexDeducted, source: candidateString) } + default: break } candidatePairSelectionConfirmed(at: indexDeducted) } diff --git a/Source/Resources/Base.lproj/Localizable.strings b/Source/Resources/Base.lproj/Localizable.strings index 3a95bd94..9afec45a 100644 --- a/Source/Resources/Base.lproj/Localizable.strings +++ b/Source/Resources/Base.lproj/Localizable.strings @@ -1,4 +1,5 @@ "vChewing" = "vChewing"; +"Quick Candidates" = "Quick Candidates"; "Alvin Liu (Imitative)" = "Alvin Liu (Imitative)"; "Previous intonation has been overridden." = "Previous intonation has been overridden."; "It will attempt to combine with the incoming phonabet input." = "It will attempt to combine with the incoming phonabet input."; diff --git a/Source/Resources/en.lproj/Localizable.strings b/Source/Resources/en.lproj/Localizable.strings index 3a95bd94..9afec45a 100644 --- a/Source/Resources/en.lproj/Localizable.strings +++ b/Source/Resources/en.lproj/Localizable.strings @@ -1,4 +1,5 @@ "vChewing" = "vChewing"; +"Quick Candidates" = "Quick Candidates"; "Alvin Liu (Imitative)" = "Alvin Liu (Imitative)"; "Previous intonation has been overridden." = "Previous intonation has been overridden."; "It will attempt to combine with the incoming phonabet input." = "It will attempt to combine with the incoming phonabet input."; diff --git a/Source/Resources/ja.lproj/Localizable.strings b/Source/Resources/ja.lproj/Localizable.strings index af7b695c..e7fecb18 100644 --- a/Source/Resources/ja.lproj/Localizable.strings +++ b/Source/Resources/ja.lproj/Localizable.strings @@ -1,4 +1,5 @@ "vChewing" = "威注音入力アプリ"; +"Quick Candidates" = "早速候補"; "Alvin Liu (Imitative)" = "劉又銘擬音注音配列"; "Previous intonation has been overridden." = "後ろ側の漢字の音調を書き直しました。"; "It will attempt to combine with the incoming phonabet input." = "この音調は次の注音入力と組み合わす。"; diff --git a/Source/Resources/zh-Hans.lproj/Localizable.strings b/Source/Resources/zh-Hans.lproj/Localizable.strings index ee7a1234..a262e52a 100644 --- a/Source/Resources/zh-Hans.lproj/Localizable.strings +++ b/Source/Resources/zh-Hans.lproj/Localizable.strings @@ -1,4 +1,5 @@ "vChewing" = "威注音输入法"; +"Quick Candidates" = "快速候选"; "Alvin Liu (Imitative)" = "刘又铭拟音注音排列"; "Previous intonation has been overridden." = "已覆写游标身后的汉字的音调。"; "It will attempt to combine with the incoming phonabet input." = "该声调亦会尝试与接下来输入的注音相组合。"; diff --git a/Source/Resources/zh-Hant.lproj/Localizable.strings b/Source/Resources/zh-Hant.lproj/Localizable.strings index 5f683caa..ee752311 100644 --- a/Source/Resources/zh-Hant.lproj/Localizable.strings +++ b/Source/Resources/zh-Hant.lproj/Localizable.strings @@ -1,4 +1,5 @@ "vChewing" = "威注音輸入法"; +"Quick Candidates" = "快速候選"; "Alvin Liu (Imitative)" = "劉又銘擬音注音排列"; "Previous intonation has been overridden." = "已覆寫游標身後的漢字的音調。"; "It will attempt to combine with the incoming phonabet input." = "該聲調亦會嘗試與接下來輸入的注音相組合。";