From b38938de54081d98dff73712ea4a43819cd98de6 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Wed, 28 Sep 2022 23:26:57 +0800 Subject: [PATCH] CtlCandidateTDK // Performance boost. --- Packages/vChewing_CandidateWindow/README.md | 1 + .../CandidateWindow/CandidatePool.swift | 10 +++-- .../CandidateWindow/CtlCandidateTDK.swift | 22 +++++----- .../CandidateWindow/VwrCandidateTDK.swift | 40 +++++++++---------- .../Sources/Shared/CandidateBasicUnits.swift | 14 +++++-- .../Protocols/CtlCandidateProtocol.swift | 3 +- Source/Modules/SessionCtl_Delegates.swift | 12 +++--- Source/Modules/SessionCtl_HandleDisplay.swift | 3 +- 8 files changed, 56 insertions(+), 49 deletions(-) diff --git a/Packages/vChewing_CandidateWindow/README.md b/Packages/vChewing_CandidateWindow/README.md index e929a70b..31da3503 100644 --- a/Packages/vChewing_CandidateWindow/README.md +++ b/Packages/vChewing_CandidateWindow/README.md @@ -9,6 +9,7 @@ TDK 選字窗以純 SwiftUI 構築,用以取代此前自上游繼承來的 Vol 然而,TDK 選字窗目前有下述侷限: - 因 SwiftUI 自身特性所導致的嚴重的效能問題。基本上來講,如果您經常使用全字庫模式的話,請在偏好設定內啟用效能更高的 IMK 選字窗。 +- 同樣出於上述原因,為了讓田所選字窗至少處於可在生產力環境下正常使用的狀態,就犧牲了捲動檢視的功能。也就是說,每次只顯示六行,但顯示內容則隨著使用者的游標操作而更新。 - TDK 選字窗目前僅完成了橫版矩陣陳列模式的實作,且尚未引入對縱排選字窗陳列佈局的支援。 因為這些問題恐怕需要很久才能全部解決,所以威注音會在這段時間內推薦使用者們優先使用 IMK 選字窗。 diff --git a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift index dcdae5b8..2c090641 100644 --- a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift +++ b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift @@ -9,8 +9,8 @@ import Cocoa import Shared -/// 候選字窗會用到的資料池單位。用 class 型別會更方便一些。 -public class CandidatePool { +/// 候選字窗會用到的資料池單位。 +public struct CandidatePool { public var currentRowNumber = 0 public private(set) var selectionKeys: String public private(set) var highlightedIndex: Int = 0 @@ -24,6 +24,8 @@ public class CandidatePool { ceil(Double(maxColumnCapacity + 3) * 2.7 * ceil(CandidateCellData.unifiedSize) * 1.2) } + public var rangeForCurrentPage: Range { currentRowNumber.. Bool { diff --git a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/VwrCandidateTDK.swift b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/VwrCandidateTDK.swift index adbbda0e..45a32632 100644 --- a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/VwrCandidateTDK.swift +++ b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/VwrCandidateTDK.swift @@ -21,7 +21,7 @@ struct CandidatePoolViewUI_Previews: PreviewProvider { "吹", "大", "地", "草", "枝", "擺", ] static var thePool: CandidatePool { - let result = CandidatePool(candidates: testCandidates, columnCapacity: 6) + var result = CandidatePool(candidates: testCandidates, columnCapacity: 6) // 下一行待解決:無論這裡怎麼指定高亮選中項是哪一筆,其所在行都得被卷動到使用者眼前。 result.highlight(at: 14) return result @@ -50,28 +50,24 @@ public struct VwrCandidateTDK: View { public var body: some View { VStack(alignment: .leading, spacing: 0) { - ScrollViewReader { proxy in - ScrollView(.vertical, showsIndicators: true) { - VStack(alignment: .leading, spacing: 1.6) { - ForEach(thePool.candidateRows.indices, id: \.self) { columnIndex in - HStack(spacing: 10) { - ForEach(Array(thePool.candidateRows[columnIndex]), id: \.self) { currentCandidate in - currentCandidate.attributedStringForSwiftUI.fixedSize() - .frame(maxWidth: .infinity, alignment: .topLeading) - .contentShape(Rectangle()) - .onTapGesture { didSelectCandidateAt(currentCandidate.index) } - } - Spacer() - }.frame( - minWidth: 0, - maxWidth: .infinity, - alignment: .topLeading - ).id(columnIndex) - Divider() - } + ScrollView(.vertical, showsIndicators: true) { + VStack(alignment: .leading, spacing: 1.6) { + ForEach(thePool.rangeForCurrentPage, id: \.self) { columnIndex in + HStack(spacing: 10) { + ForEach(Array(thePool.candidateRows[columnIndex]), id: \.self) { currentCandidate in + currentCandidate.attributedStringForSwiftUI.fixedSize() + .frame(maxWidth: .infinity, alignment: .topLeading) + .contentShape(Rectangle()) + .onTapGesture { didSelectCandidateAt(currentCandidate.index) } + } + Spacer() + }.frame( + minWidth: 0, + maxWidth: .infinity, + alignment: .topLeading + ).id(columnIndex) + Divider() } - }.onAppear { - proxy.scrollTo(thePool.currentRowNumber) } } .frame(minHeight: thePool.maxWindowHeight, maxHeight: thePool.maxWindowHeight).padding(5) diff --git a/Packages/vChewing_Shared/Sources/Shared/CandidateBasicUnits.swift b/Packages/vChewing_Shared/Sources/Shared/CandidateBasicUnits.swift index 74f57af4..deacf56c 100644 --- a/Packages/vChewing_Shared/Sources/Shared/CandidateBasicUnits.swift +++ b/Packages/vChewing_Shared/Sources/Shared/CandidateBasicUnits.swift @@ -44,9 +44,9 @@ public class CandidateCellData: Hashable { } public var cellLength: Int { - let rect = attributedString.boundingRect( - with: NSSize(width: 1600.0, height: 1600.0), - options: [.usesLineFragmentOrigin] + if displayedText.count <= 2 { return Int(ceil(size * 3)) } + let rect = attributedStringForLengthCalculation.boundingRect( + with: NSSize(width: 1600.0, height: 1600.0), options: [.usesLineFragmentOrigin] ) let rawResult = ceil(rect.width + size / size) return Int(rawResult) @@ -72,6 +72,14 @@ public class CandidateCellData: Hashable { return attrStrKey } + public var attributedStringForLengthCalculation: NSAttributedString { + let attrCandidate: [NSAttributedString.Key: AnyObject] = [ + .font: NSFont.monospacedDigitSystemFont(ofSize: size, weight: .regular) + ] + let attrStrCandidate = NSMutableAttributedString(string: displayedText + " ", attributes: attrCandidate) + return attrStrCandidate + } + public var attributedString: NSAttributedString { let paraStyleKey = NSMutableParagraphStyle() paraStyleKey.setParagraphStyle(NSParagraphStyle.default) diff --git a/Packages/vChewing_Shared/Sources/Shared/Protocols/CtlCandidateProtocol.swift b/Packages/vChewing_Shared/Sources/Shared/Protocols/CtlCandidateProtocol.swift index b066f4c2..358e986a 100644 --- a/Packages/vChewing_Shared/Sources/Shared/Protocols/CtlCandidateProtocol.swift +++ b/Packages/vChewing_Shared/Sources/Shared/Protocols/CtlCandidateProtocol.swift @@ -12,8 +12,9 @@ public protocol CtlCandidateDelegate: AnyObject { func candidatePairs(conv: Bool) -> [(String, String)] func candidatePairAt(_ index: Int) -> (String, String) func candidatePairSelected(at index: Int) + func candidates(_ sender: Any!) -> [Any]! func buzz() - func kanjiConversionIfRequired(_ target: String) -> String + var selectionKeys: String { get } } public protocol CtlCandidateProtocol { diff --git a/Source/Modules/SessionCtl_Delegates.swift b/Source/Modules/SessionCtl_Delegates.swift index 1255bb1d..05df8620 100644 --- a/Source/Modules/SessionCtl_Delegates.swift +++ b/Source/Modules/SessionCtl_Delegates.swift @@ -46,17 +46,17 @@ extension SessionCtl: KeyHandlerDelegate { // MARK: - Candidate Controller Delegate extension SessionCtl: CtlCandidateDelegate { + var selectionKeys: String { PrefMgr.shared.candidateKeys } + func buzz() { IMEApp.buzz() } - func kanjiConversionIfRequired(_ target: String) -> String { - ChineseConverter.kanjiConversionIfRequired(target) - } - func candidatePairs(conv: Bool = false) -> [(String, String)] { - if !state.isCandidateContainer { return [] } - if !conv { return state.candidates } + if !state.isCandidateContainer || state.candidates.isEmpty { return [] } + if !conv || PrefMgr.shared.cns11643Enabled || state.candidates[0].0.contains("_punctuation") { + return state.candidates + } let convertedCandidates: [(String, String)] = state.candidates.map { theCandidatePair -> (String, String) in let theCandidate = theCandidatePair.1 let theConverted = ChineseConverter.kanjiConversionIfRequired(theCandidate) diff --git a/Source/Modules/SessionCtl_HandleDisplay.swift b/Source/Modules/SessionCtl_HandleDisplay.swift index 21b412c8..a0ff1734 100644 --- a/Source/Modules/SessionCtl_HandleDisplay.swift +++ b/Source/Modules/SessionCtl_HandleDisplay.swift @@ -142,7 +142,6 @@ extension SessionCtl { Self.ctlCandidateCurrent.hint = NSLocalizedString("Hold ⇧ to choose associates.", comment: "") } - Self.ctlCandidateCurrent.delegate = self Self.ctlCandidateCurrent.showPageButtons = PrefMgr.shared.showPageButtonsInCandidateWindow Self.ctlCandidateCurrent.useLangIdentifier = PrefMgr.shared.handleDefaultCandidateFontsByLangIdentifier Self.ctlCandidateCurrent.locale = { @@ -156,7 +155,6 @@ extension SessionCtl { default: return "" } }() - Self.ctlCandidateCurrent.reloadData() if #available(macOS 10.14, *) { // Spotlight 視窗會擋住 IMK 選字窗,所以需要特殊處理。 @@ -167,6 +165,7 @@ extension SessionCtl { } } + Self.ctlCandidateCurrent.delegate = self Self.ctlCandidateCurrent.visible = true if isVerticalTyping {