From 682aaf5053ee399d85e8d981eca8c93a517630c5 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Tue, 30 May 2023 15:29:43 +0800 Subject: [PATCH] TDKCandidates // Make CandidatePool into a class, etc. --- .../CandidateCellData_Core.swift | 17 +++++ .../CandidateCellData_SwiftUIImpl.swift | 6 +- .../CandidateWindow/CandidatePool.swift | 64 ++++++++++++------- .../CandidatePool_CocoaImpl.swift | 12 ++-- .../TDKCandidates/CtlCandidateTDK.swift | 2 + .../TDKCandidates/VwrCandidateTDK_Cocoa.swift | 12 ++-- .../VwrCandidateTDK_SwiftUI.swift | 34 +++++----- Source/Modules/SessionCtl_Core.swift | 8 +++ 8 files changed, 99 insertions(+), 56 deletions(-) diff --git a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidateCellData_Core.swift b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidateCellData_Core.swift index 2a1675ef..67799c74 100644 --- a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidateCellData_Core.swift +++ b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidateCellData_Core.swift @@ -39,6 +39,23 @@ public class CandidateCellData: Hashable { : .init(red: 142 / 255, green: 142 / 255, blue: 147 / 255, alpha: 1) } + public var hardCopy: CandidateCellData { + let result = CandidateCellData(key: selectionKey, displayedText: displayedText, spanLength: spanLength, isSelected: isHighlighted) + result.visualDimension = visualDimension + result.locale = locale + result.whichLine = whichLine + result.index = index + result.subIndex = subIndex + return result + } + + public var cleanCopy: CandidateCellData { + let result = hardCopy + result.isHighlighted = false + result.selectionKey = " " + return result + } + public init( key: String, displayedText: String, spanLength spanningLength: Int? = nil, isSelected: Bool = false diff --git a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidateCellData_SwiftUIImpl.swift b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidateCellData_SwiftUIImpl.swift index b3fb4377..c2e23fe0 100644 --- a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidateCellData_SwiftUIImpl.swift +++ b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidateCellData_SwiftUIImpl.swift @@ -39,7 +39,7 @@ public extension CandidateCellData { Text(verbatim: displayedText) .font(.init(CTFontCreateUIFontForLanguage(.system, fontSizeCandidate, locale as CFString)!)) .foregroundColor(Color(white: 1)).lineLimit(1) - }.padding(3).foregroundColor(Color(white: 0.9)) + }.padding(.vertical, 3).padding(.horizontal, 5) }.frame(alignment: .leading) } else { VStack(spacing: 0) { @@ -49,7 +49,7 @@ public extension CandidateCellData { Text(verbatim: displayedText) .font(.init(CTFontCreateUIFontForLanguage(.system, fontSizeCandidate, locale as CFString)!)) .foregroundColor(Color.primary).lineLimit(1) - }.padding(3).foregroundColor(Color(white: 0.9)) + }.padding(.vertical, 3).padding(.horizontal, 5) }.frame(alignment: .leading) } }.fixedSize(horizontal: false, vertical: true) @@ -71,7 +71,7 @@ public extension CandidateCellData { Text(verbatim: displayedText) .font(.init(CTFontCreateUIFontForLanguage(.system, fontSizeCandidate, locale as CFString)!)) .foregroundColor(.init(nsColor: fontColorCandidate)).lineLimit(1) - }.padding(3) + }.padding(.vertical, 3).padding(.horizontal, 5) }.frame(alignment: .leading) }.fixedSize(horizontal: false, vertical: true) } diff --git a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift index aec467c9..0c37d779 100644 --- a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift +++ b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift @@ -10,13 +10,14 @@ import Foundation import Shared /// 候選字窗會用到的資料池單位,即用即拋。 -public struct CandidatePool { - public let blankCell: CandidateCellData - public let shitCell: CandidateCellData // 只用來測量單漢字候選字 cell 的最大可能寬度。 - public let maxLinesPerPage: Int - public let layout: LayoutOrientation - public let selectionKeys: String - public let candidateDataAll: [CandidateCellData] +public class CandidatePool { + // 只用來測量單漢字候選字 cell 的最大可能寬度。 + public static let shitCell = CandidateCellData(key: " ", displayedText: "💩", isSelected: false) + public static let blankCell = CandidateCellData(key: " ", displayedText: " ", isSelected: false) + public private(set) var maxLinesPerPage: Int + public private(set) var layout: LayoutOrientation + public private(set) var selectionKeys: String + public private(set) var candidateDataAll: [CandidateCellData] public var candidateLines: [[CandidateCellData]] = [] public var tooltip: String = "" public var reverseLookupResult: [String] = [] @@ -30,7 +31,7 @@ public struct CandidatePool { /// 用來在初期化一個候選字詞資料池的時候研判「橫版多行選字窗每行最大應該塞多少個候選字詞」。 /// 注意:該參數不用來計算視窗寬度,所以無須算上候選字詞間距。 - public var maxRowWidth: Double { ceil(Double(maxLineCapacity) * blankCell.cellLength()) } + public var maxRowWidth: Double { ceil(Double(maxLineCapacity) * Self.blankCell.cellLength()) } /// 當前高亮的候選字詞的順序標籤(同時顯示資料池內已有的全部的候選字詞的數量) public var currentPositionLabelText: String { @@ -79,18 +80,35 @@ public struct CandidatePool { public init( candidates: [(keyArray: [String], value: String)], lines: Int = 3, selectionKeys: String = "123456789", layout: LayoutOrientation = .vertical, locale: String = "" + ) { + maxLinesPerPage = 1 + self.layout = .horizontal + self.selectionKeys = "123456789" + candidateDataAll = [] + // 以上只是為了糊弄 compiler。接下來才是正式的初期化。 + construct(candidates: candidates, lines: lines, selectionKeys: selectionKeys, layout: layout, locale: locale) + } + + /// 初期化(或者自我重新初期化)一個候選字窗專用資料池。 + /// - Parameters: + /// - candidates: 要塞入的候選字詞陣列。 + /// - selectionKeys: 選字鍵。 + /// - direction: 橫向排列還是縱向排列(預設情況下是縱向)。 + /// - locale: 區域編碼。例:「zh-Hans」或「zh-Hant」。 + private func construct( + candidates: [(keyArray: [String], value: String)], lines: Int = 3, selectionKeys: String = "123456789", + layout: LayoutOrientation = .vertical, locale: String = "" ) { self.layout = layout maxLinesPerPage = max(1, lines) - blankCell = CandidateCellData(key: " ", displayedText: " ", isSelected: false) - shitCell = CandidateCellData(key: " ", displayedText: "💩", isSelected: false) - blankCell.locale = locale + Self.blankCell.locale = locale self.selectionKeys = selectionKeys.isEmpty ? "123456789" : selectionKeys var allCandidates = candidates.map { CandidateCellData(key: " ", displayedText: $0.value, spanLength: $0.keyArray.count) } - if allCandidates.isEmpty { allCandidates.append(blankCell) } + if allCandidates.isEmpty { allCandidates.append(Self.blankCell) } candidateDataAll = allCandidates + candidateLines.removeAll() var currentColumn: [CandidateCellData] = [] for (i, candidate) in candidateDataAll.enumerated() { candidate.index = i @@ -127,7 +145,7 @@ public extension CandidatePool { /// 往指定的方向翻頁。 /// - Parameter isBackward: 是否逆向翻頁。 /// - Returns: 操作是否順利。 - @discardableResult mutating func flipPage(isBackward: Bool) -> Bool { + @discardableResult func flipPage(isBackward: Bool) -> Bool { backupLineRangeForCurrentPage() defer { flipLineRangeToNeighborPage(isBackward: isBackward) } return consecutivelyFlipLines(isBackward: isBackward, count: maxLinesPerPage) @@ -147,7 +165,7 @@ public extension CandidatePool { /// - isBackward: 是否逆向翻行。 /// - count: 翻幾行。 /// - Returns: 操作是否順利。 - @discardableResult mutating func consecutivelyFlipLines(isBackward: Bool, count: Int) -> Bool { + @discardableResult func consecutivelyFlipLines(isBackward: Bool, count: Int) -> Bool { switch isBackward { case false where currentLineNumber == candidateLines.count - 1: return highlightNeighborCandidate(isBackward: false) @@ -165,7 +183,7 @@ public extension CandidatePool { /// 嘗試高亮前方或者後方的鄰近候選字詞。 /// - Parameter isBackward: 是否是後方的鄰近候選字詞。 /// - Returns: 是否成功。 - @discardableResult mutating func highlightNeighborCandidate(isBackward: Bool) -> Bool { + @discardableResult func highlightNeighborCandidate(isBackward: Bool) -> Bool { switch isBackward { case false where highlightedIndex >= candidateDataAll.count - 1: highlight(at: 0) @@ -181,7 +199,7 @@ public extension CandidatePool { /// 高亮指定的候選字。 /// - Parameter indexSpecified: 給定的候選字詞索引編號,得是資料池內的總索引編號。 - mutating func highlight(at indexSpecified: Int) { + func highlight(at indexSpecified: Int) { var indexSpecified = indexSpecified let isBackward: Bool = indexSpecified > highlightedIndex highlightedIndex = indexSpecified @@ -222,7 +240,7 @@ public extension CandidatePool { } func cellWidth(_ cell: CandidateCellData) -> (min: CGFloat?, max: CGFloat?) { - let minAccepted = ceil(shitCell.cellLength(isMatrix: false)) + let minAccepted = ceil(Self.shitCell.cellLength(isMatrix: false)) let defaultMin: CGFloat = cell.cellLength(isMatrix: maxLinesPerPage != 1) var min: CGFloat = defaultMin if layout != .vertical, maxLinesPerPage == 1 { @@ -267,14 +285,14 @@ private extension CandidatePool { max(0, candidateLines.count - maxLinesPerPage) ..< candidateLines.count } - mutating func selectNewNeighborLine(isBackward: Bool) { + func selectNewNeighborLine(isBackward: Bool) { switch layout { case .horizontal: selectNewNeighborRow(direction: isBackward ? .up : .down) case .vertical: selectNewNeighborColumn(direction: isBackward ? .left : .right) } } - mutating func fixLineRange(isBackward: Bool = false) { + func fixLineRange(isBackward: Bool = false) { if !lineRangeForCurrentPage.contains(currentLineNumber) { switch isBackward { case false: @@ -289,11 +307,11 @@ private extension CandidatePool { } } - mutating func backupLineRangeForCurrentPage() { + func backupLineRangeForCurrentPage() { previouslyRecordedLineRangeForPreviousPage = lineRangeForCurrentPage } - mutating func flipLineRangeToNeighborPage(isBackward: Bool = false) { + func flipLineRangeToNeighborPage(isBackward: Bool = false) { guard let prevRange = previouslyRecordedLineRangeForPreviousPage else { return } var lowerBound = prevRange.lowerBound var upperBound = prevRange.upperBound @@ -323,7 +341,7 @@ private extension CandidatePool { // 應該不會有漏檢的情形了。 } - mutating func selectNewNeighborRow(direction: VerticalDirection) { + func selectNewNeighborRow(direction: VerticalDirection) { let currentSubIndex = candidateDataAll[highlightedIndex].subIndex var result = currentSubIndex branch: switch direction { @@ -369,7 +387,7 @@ private extension CandidatePool { } } - mutating func selectNewNeighborColumn(direction: HorizontalDirection) { + func selectNewNeighborColumn(direction: HorizontalDirection) { let currentSubIndex = candidateDataAll[highlightedIndex].subIndex switch direction { case .left: diff --git a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool_CocoaImpl.swift b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool_CocoaImpl.swift index e767c42b..7a8c07dd 100644 --- a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool_CocoaImpl.swift +++ b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool_CocoaImpl.swift @@ -25,7 +25,7 @@ extension CandidatePool { private var attributedDescriptionHorizontal: NSAttributedString { let paragraphStyle = sharedParagraphStyle let attrCandidate: [NSAttributedString.Key: AnyObject] = [ - .font: blankCell.phraseFont(size: blankCell.size), + .font: Self.blankCell.phraseFont(size: Self.blankCell.size), .paragraphStyle: paragraphStyle, ] let result = NSMutableAttributedString(string: "", attributes: attrCandidate) @@ -64,7 +64,7 @@ extension CandidatePool { private var attributedDescriptionVertical: NSAttributedString { let paragraphStyle = sharedParagraphStyle let attrCandidate: [NSAttributedString.Key: AnyObject] = [ - .font: blankCell.phraseFont(size: blankCell.size), + .font: Self.blankCell.phraseFont(size: Self.blankCell.size), .paragraphStyle: paragraphStyle, ] let result = NSMutableAttributedString(string: "", attributes: attrCandidate) @@ -129,7 +129,7 @@ extension CandidatePool { let positionCounterColorText = NSColor.controlTextColor let positionCounterTextSize = max(ceil(CandidateCellData.unifiedSize * 0.7), 11) let attrPositionCounter: [NSAttributedString.Key: AnyObject] = [ - .font: blankCell.phraseFontEmphasized(size: positionCounterTextSize), + .font: Self.blankCell.phraseFontEmphasized(size: positionCounterTextSize), .backgroundColor: positionCounterColorBG, .foregroundColor: positionCounterColorText, ] @@ -142,7 +142,7 @@ extension CandidatePool { private var attributedDescriptionTooltip: NSAttributedString { let positionCounterTextSize = max(ceil(CandidateCellData.unifiedSize * 0.7), 11) let attrTooltip: [NSAttributedString.Key: AnyObject] = [ - .font: blankCell.phraseFontEmphasized(size: positionCounterTextSize), + .font: Self.blankCell.phraseFontEmphasized(size: positionCounterTextSize), ] let tooltipText = NSAttributedString( string: " \(tooltip) ", attributes: attrTooltip @@ -153,10 +153,10 @@ extension CandidatePool { private var attributedDescriptionReverseLookp: NSAttributedString { let reverseLookupTextSize = max(ceil(CandidateCellData.unifiedSize * 0.6), 9) let attrReverseLookup: [NSAttributedString.Key: AnyObject] = [ - .font: blankCell.phraseFont(size: reverseLookupTextSize), + .font: Self.blankCell.phraseFont(size: reverseLookupTextSize), ] let attrReverseLookupSpacer: [NSAttributedString.Key: AnyObject] = [ - .font: blankCell.phraseFont(size: reverseLookupTextSize), + .font: Self.blankCell.phraseFont(size: reverseLookupTextSize), ] let result = NSMutableAttributedString(string: "", attributes: attrReverseLookupSpacer) for neta in reverseLookupResult { diff --git a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/TDKCandidates/CtlCandidateTDK.swift b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/TDKCandidates/CtlCandidateTDK.swift index 77d6b686..79c1f38b 100644 --- a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/TDKCandidates/CtlCandidateTDK.swift +++ b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/TDKCandidates/CtlCandidateTDK.swift @@ -31,6 +31,8 @@ public class CtlCandidateTDK: CtlCandidate, NSWindowDelegate { public var useMouseScrolling: Bool = true private static var thePool: CandidatePool = .init(candidates: []) private static var currentView: NSView = .init() + public static var currentWindow: NSWindow? + public static var currentMenu: NSMenu? @available(macOS 10.15, *) private var theView: some View { diff --git a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/TDKCandidates/VwrCandidateTDK_Cocoa.swift b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/TDKCandidates/VwrCandidateTDK_Cocoa.swift index 940022ef..4afbeb67 100644 --- a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/TDKCandidates/VwrCandidateTDK_Cocoa.swift +++ b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/TDKCandidates/VwrCandidateTDK_Cocoa.swift @@ -66,9 +66,10 @@ public extension VwrCandidateTDKCocoa { } if thePool.maxLinesPerPage - thePool.lineRangeForCurrentPage.count > 0 { thePool.lineRangeForFinalPageBlanked.enumerated().forEach { _ in - var theLine = [thePool.blankCell] - for _ in 1 ..< thePool.maxLineCapacity { - theLine.append(thePool.blankCell) + var theLine = [CandidateCellData]() + let copied = CandidatePool.blankCell.cleanCopy + for _ in 0 ..< thePool.maxLineCapacity { + theLine.append(copied) } let vwrCurrentLine = generateLineContainer(&theLine) candidateContainer.addView(vwrCurrentLine, in: isVerticalListing ? .top : .leading) @@ -88,7 +89,7 @@ public extension VwrCandidateTDKCocoa { let line = Array(lines[viewLineID]) columnWidth = line.map(\.visualDimension.width).max() ?? lineDimension.width } else { - columnWidth = thePool.blankCell.visualDimension.width + columnWidth = CandidatePool.blankCell.visualDimension.width } accumulatedWidth += columnWidth Self.makeSimpleConstraint(item: vwrCurrentLine, attribute: .width, relation: .equal, value: columnWidth) @@ -167,7 +168,7 @@ private extension VwrCandidateTDKCocoa { } private func drawCellCocoa(_ theCell: CandidateCellData? = nil) -> NSView { - let theCell = theCell ?? thePool.blankCell + let theCell = theCell ?? CandidatePool.blankCell.cleanCopy let cellLabel = VwrCandidateCell(cell: theCell) cellLabel.target = self Self.makeSimpleConstraint(item: cellLabel, attribute: .width, relation: .equal, value: cellLabel.fittingSize.width) @@ -362,6 +363,7 @@ private extension VwrCandidateTDKCocoa { } theMenu = newMenu + CtlCandidateTDK.currentMenu = newMenu } @objc func menuActionOfBoosting(_: Any? = nil) { diff --git a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/TDKCandidates/VwrCandidateTDK_SwiftUI.swift b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/TDKCandidates/VwrCandidateTDK_SwiftUI.swift index f1bbe16f..5f861fa8 100644 --- a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/TDKCandidates/VwrCandidateTDK_SwiftUI.swift +++ b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/TDKCandidates/VwrCandidateTDK_SwiftUI.swift @@ -72,9 +72,10 @@ private extension VwrCandidateTDK { .id(rowIndex) } if thePool.maxLinesPerPage - thePool.lineRangeForCurrentPage.count > 0 { + let copied = CandidatePool.blankCell.cleanCopy ForEach(thePool.lineRangeForFinalPageBlanked, id: \.self) { _ in HStack(spacing: 0) { - attributedStringFor(cell: thePool.blankCell) + attributedStringFor(cell: copied) .frame(alignment: .topLeading) .contentShape(Rectangle()) Spacer() @@ -102,8 +103,9 @@ private extension VwrCandidateTDK { } .opacity(columnIndex == thePool.currentLineNumber ? 1 : 0.85) if thePool.candidateLines[columnIndex].count < thePool.maxLineCapacity { + let copied = CandidatePool.blankCell.cleanCopy ForEach(0 ..< thePool.dummyCellsRequiredForCurrentLine, id: \.self) { _ in - drawCandidate(thePool.blankCell) + drawCandidate(copied) } } } @@ -112,17 +114,15 @@ private extension VwrCandidateTDK { alignment: .topLeading ) .id(columnIndex) - if thePool.maxLinesPerPage > 1, thePool.maxLinesPerPage <= loopIndex + 1 { - Spacer(minLength: 0) - } } if thePool.maxLinesPerPage - thePool.lineRangeForCurrentPage.count > 0 { ForEach(Array(thePool.lineRangeForFinalPageBlanked.enumerated()), id: \.offset) { loopIndex, _ in VStack(alignment: .leading, spacing: 0) { + let copied = CandidatePool.blankCell.cleanCopy ForEach(0 ..< thePool.maxLineCapacity, id: \.self) { _ in - attributedStringFor(cell: thePool.blankCell).fixedSize() + attributedStringFor(cell: copied).fixedSize() .frame( - width: ceil(thePool.blankCell.cellLength(isMatrix: true)), + width: ceil(CandidatePool.blankCell.cellLength(isMatrix: true)), alignment: .topLeading ) .contentShape(Rectangle()) @@ -132,11 +132,6 @@ private extension VwrCandidateTDK { maxWidth: .infinity, alignment: .topLeading ) - if thePool.maxLinesPerPage > 1, - loopIndex >= thePool.maxLinesPerPage - thePool.lineRangeForCurrentPage.count - 1 - { - Spacer(minLength: 0) - } } } } @@ -195,7 +190,7 @@ extension VwrCandidateTDK { let spacings: CGFloat = horizontalCellSpacing * Double(thePool.maxLineCapacity - 1) let maxWindowWith: CGFloat = ceil( - Double(thePool.maxLineCapacity) * (thePool.blankCell.cellLength()) + Double(thePool.maxLineCapacity) * (CandidatePool.blankCell.cellLength()) + spacings ) return thePool.layout == .horizontal && thePool.maxLinesPerPage > 1 ? maxWindowWith : nil @@ -245,7 +240,7 @@ extension VwrCandidateTDK { HStack { if !tooltip.isEmpty { ZStack(alignment: .center) { - Circle().fill(thePool.blankCell.themeColor.opacity(0.8)) + Circle().fill(CandidatePool.blankCell.themeColor.opacity(0.8)) Text(tooltip.first?.description ?? "").padding(2).font(.system(size: CandidateCellData.unifiedSize)) }.frame(width: ceil(CandidateCellData.unifiedSize * 1.7), height: ceil(CandidateCellData.unifiedSize * 1.7)) } @@ -371,7 +366,7 @@ struct VwrCandidateTDK_Previews: PreviewProvider { ] @State static var reverseLookupResult = ["mmmmm", "dddd"] @State static var tooltip = "📼" - @State static var oldOS: Bool = true + @State static var oldOS: Bool = false static var testCandidatesConverted: [(keyArray: [String], value: String)] { testCandidates.map { candidate in @@ -381,7 +376,7 @@ struct VwrCandidateTDK_Previews: PreviewProvider { } static var thePoolX: CandidatePool { - var result = CandidatePool( + let result = CandidatePool( candidates: testCandidatesConverted, lines: 4, selectionKeys: "123456", layout: .horizontal ) @@ -392,7 +387,7 @@ struct VwrCandidateTDK_Previews: PreviewProvider { } static var thePoolXS: CandidatePool { - var result = CandidatePool( + let result = CandidatePool( candidates: testCandidatesConverted, lines: 1, selectionKeys: "123456", layout: .horizontal ) @@ -403,7 +398,7 @@ struct VwrCandidateTDK_Previews: PreviewProvider { } static var thePoolY: CandidatePool { - var result = CandidatePool( + let result = CandidatePool( candidates: testCandidatesConverted, lines: 4, selectionKeys: "123456", layout: .vertical ) @@ -411,11 +406,12 @@ struct VwrCandidateTDK_Previews: PreviewProvider { result.tooltip = Self.tooltip result.flipPage(isBackward: false) result.highlight(at: 2) + result.highlight(at: 21) return result } static var thePoolYS: CandidatePool { - var result = CandidatePool( + let result = CandidatePool( candidates: testCandidatesConverted, lines: 1, selectionKeys: "123456", layout: .vertical ) diff --git a/Source/Modules/SessionCtl_Core.swift b/Source/Modules/SessionCtl_Core.swift index 48305e53..be43b903 100644 --- a/Source/Modules/SessionCtl_Core.swift +++ b/Source/Modules/SessionCtl_Core.swift @@ -273,6 +273,14 @@ public extension SessionCtl { inputMode = IMEApp.currentInputMode } } + DispatchQueue.main.async { + // 清理掉上一個會話的選字窗及其選單。 + self.candidateUI = nil + CtlCandidateTDK.currentMenu?.cancelTracking() + CtlCandidateTDK.currentMenu = nil + CtlCandidateTDK.currentWindow?.orderOut(nil) + CtlCandidateTDK.currentWindow = nil + } DispatchQueue.main.async { [self] in if isActivated { return }