TDKCandidates // Make CandidatePool into a class, etc.

This commit is contained in:
ShikiSuen 2023-05-30 15:29:43 +08:00
parent fc29b52f8f
commit 682aaf5053
8 changed files with 99 additions and 56 deletions

View File

@ -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

View File

@ -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)
}

View File

@ -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-Hanszh-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:

View File

@ -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 {

View File

@ -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 {

View File

@ -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) {

View File

@ -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
)

View File

@ -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 }