TDKCandidates // Implement page-expansion feature.

This commit is contained in:
ShikiSuen 2023-08-09 23:53:09 +08:00
parent 2abb86f4b8
commit b2ee0e3972
5 changed files with 62 additions and 20 deletions

View File

@ -14,16 +14,17 @@ public class CandidatePool {
// cell // cell
public static let shitCell = CandidateCellData(key: " ", displayedText: "💩", isSelected: false) public static let shitCell = CandidateCellData(key: " ", displayedText: "💩", isSelected: false)
public static let blankCell = CandidateCellData(key: " ", displayedText: " ", isSelected: false) public static let blankCell = CandidateCellData(key: " ", displayedText: " ", isSelected: false)
public private(set) var maxLinesPerPage: Int public private(set) var _maxLinesPerPage: Int
public private(set) var layout: LayoutOrientation public private(set) var layout: LayoutOrientation
public private(set) var selectionKeys: String public private(set) var selectionKeys: String
public private(set) var candidateDataAll: [CandidateCellData] public private(set) var candidateDataAll: [CandidateCellData]
public var candidateLines: [[CandidateCellData]] = [] public private(set) var candidateLines: [[CandidateCellData]] = []
public var tooltip: String = ""
public var reverseLookupResult: [String] = []
public private(set) var highlightedIndex: Int = 0 public private(set) var highlightedIndex: Int = 0
public private(set) var currentLineNumber = 0 public private(set) var currentLineNumber = 0
public private(set) var isExpanded: Bool = false
public var metrics: UIMetrics = .allZeroed public var metrics: UIMetrics = .allZeroed
public var tooltip: String = ""
public var reverseLookupResult: [String] = []
private var recordedLineRangeForCurrentPage: Range<Int>? private var recordedLineRangeForCurrentPage: Range<Int>?
private var previouslyRecordedLineRangeForPreviousPage: Range<Int>? private var previouslyRecordedLineRangeForPreviousPage: Range<Int>?
@ -47,9 +48,15 @@ public class CandidatePool {
public let cellRadius: CGFloat = 4 public let cellRadius: CGFloat = 4
public var windowRadius: CGFloat { originDelta + cellRadius } public var windowRadius: CGFloat { originDelta + cellRadius }
/// / /// /
public var maxLinesPerPage: Int { isExpanded ? _maxLinesPerPage : 1 }
/// /
public var isMatrix: Bool { maxLinesPerPage > 1 } public var isMatrix: Bool { maxLinesPerPage > 1 }
/// /
public var isExpandable: Bool { _maxLinesPerPage > 1 }
/// ///
/// ///
public var maxRowWidth: Double { ceil(Double(maxLineCapacity) * Self.blankCell.cellLength()) } public var maxRowWidth: Double { ceil(Double(maxLineCapacity) * Self.blankCell.cellLength()) }
@ -99,15 +106,16 @@ public class CandidatePool {
/// - direction: /// - direction:
/// - locale: zh-Hanszh-Hant /// - locale: zh-Hanszh-Hant
public init( public init(
candidates: [(keyArray: [String], value: String)], lines: Int = 3, selectionKeys: String = "123456789", candidates: [(keyArray: [String], value: String)], lines: Int = 3, isExpanded expanded: Bool = true, selectionKeys: String = "123456789",
layout: LayoutOrientation = .vertical, locale: String = "" layout: LayoutOrientation = .vertical, locale: String = ""
) { ) {
maxLinesPerPage = 1 _maxLinesPerPage = max(1, lines)
isExpanded = expanded
self.layout = .horizontal self.layout = .horizontal
self.selectionKeys = "123456789" self.selectionKeys = "123456789"
candidateDataAll = [] candidateDataAll = []
// compiler // compiler
construct(candidates: candidates, lines: lines, selectionKeys: selectionKeys, layout: layout, locale: locale) construct(candidates: candidates, selectionKeys: selectionKeys, layout: layout, locale: locale)
} }
/// ///
@ -117,11 +125,10 @@ public class CandidatePool {
/// - direction: /// - direction:
/// - locale: zh-Hanszh-Hant /// - locale: zh-Hanszh-Hant
private func construct( private func construct(
candidates: [(keyArray: [String], value: String)], lines: Int = 3, selectionKeys: String = "123456789", candidates: [(keyArray: [String], value: String)], selectionKeys: String = "123456789",
layout: LayoutOrientation = .vertical, locale: String = "" layout: LayoutOrientation = .vertical, locale: String = ""
) { ) {
self.layout = layout self.layout = layout
maxLinesPerPage = max(1, lines)
Self.blankCell.locale = locale Self.blankCell.locale = locale
self.selectionKeys = selectionKeys.isEmpty ? "123456789" : selectionKeys self.selectionKeys = selectionKeys.isEmpty ? "123456789" : selectionKeys
var allCandidates = candidates.map { var allCandidates = candidates.map {
@ -172,13 +179,47 @@ public extension CandidatePool {
} }
} }
func expandIfNeeded(isBackward: Bool) {
guard !candidateLines.isEmpty, !isExpanded, isExpandable else { return }
let candidatesShown: [CandidateCellData] = candidateLines[lineRangeForCurrentPage].flatMap { $0 }
guard !candidatesShown.filter(\.isHighlighted).isEmpty else { return }
isExpanded = true
if candidateLines.count <= _maxLinesPerPage {
recordedLineRangeForCurrentPage = max(0, currentLineNumber - _maxLinesPerPage + 1) ..< currentLineNumber + 1
} else {
switch isBackward {
case true:
if lineRangeForFirstPage.contains(currentLineNumber) {
recordedLineRangeForCurrentPage = lineRangeForFirstPage
} else {
recordedLineRangeForCurrentPage = max(0, currentLineNumber - _maxLinesPerPage + 1) ..< currentLineNumber + 1
}
case false:
if lineRangeForFinalPage.contains(currentLineNumber) {
recordedLineRangeForCurrentPage = lineRangeForFinalPage
} else {
recordedLineRangeForCurrentPage = currentLineNumber ..< min(candidateLines.count, currentLineNumber + _maxLinesPerPage)
}
}
}
updateMetrics()
}
/// ///
/// - Parameter isBackward: /// - Parameter isBackward:
/// - Returns: /// - Returns:
@discardableResult func flipPage(isBackward: Bool) -> Bool { @discardableResult func flipPage(isBackward: Bool) -> Bool {
if !isExpanded, isExpandable {
expandIfNeeded(isBackward: isBackward)
return true
}
backupLineRangeForCurrentPage() backupLineRangeForCurrentPage()
defer { flipLineRangeToNeighborPage(isBackward: isBackward) } defer { flipLineRangeToNeighborPage(isBackward: isBackward) }
return consecutivelyFlipLines(isBackward: isBackward, count: maxLinesPerPage) var theCount = maxLinesPerPage
let rareConditionA: Bool = isBackward && currentLineNumber == 0
let rareConditionB: Bool = !isBackward && currentLineNumber == candidateLines.count - 1
if rareConditionA || rareConditionB { theCount = 1 }
return consecutivelyFlipLines(isBackward: isBackward, count: theCount)
} }
/// ///
@ -196,6 +237,7 @@ public extension CandidatePool {
/// - count: /// - count:
/// - Returns: /// - Returns:
@discardableResult func consecutivelyFlipLines(isBackward: Bool, count: Int) -> Bool { @discardableResult func consecutivelyFlipLines(isBackward: Bool, count: Int) -> Bool {
expandIfNeeded(isBackward: isBackward)
switch isBackward { switch isBackward {
case false where currentLineNumber == candidateLines.count - 1: case false where currentLineNumber == candidateLines.count - 1:
return highlightNeighborCandidate(isBackward: false) return highlightNeighborCandidate(isBackward: false)

View File

@ -170,7 +170,7 @@ extension CandidatePool {
arrLine.enumerated().forEach { cellID, currentCell in arrLine.enumerated().forEach { cellID, currentCell in
let cellString = NSMutableAttributedString( let cellString = NSMutableAttributedString(
attributedString: currentCell.attributedString( attributedString: currentCell.attributedString(
noSpacePadding: false, withHighlight: true, isMatrix: maxLinesPerPage > 1 noSpacePadding: false, withHighlight: true, isMatrix: isMatrix
) )
) )
if lineID != currentLineNumber { if lineID != currentLineNumber {
@ -184,7 +184,7 @@ extension CandidatePool {
result.append(spacer) result.append(spacer)
} }
} }
if lineID < lineRangeForCurrentPage.upperBound - 1 || maxLinesPerPage > 1 { if lineID < lineRangeForCurrentPage.upperBound - 1 || isMatrix {
result.append(lineFeed) result.append(lineFeed)
} else { } else {
result.append(spacer) result.append(spacer)
@ -211,7 +211,7 @@ extension CandidatePool {
let currentCell = lineData[inlineIndex] let currentCell = lineData[inlineIndex]
let cellString = NSMutableAttributedString( let cellString = NSMutableAttributedString(
attributedString: currentCell.attributedString( attributedString: currentCell.attributedString(
noSpacePadding: false, withHighlight: true, isMatrix: maxLinesPerPage > 1 noSpacePadding: false, withHighlight: true, isMatrix: isMatrix
) )
) )
if lineID != currentLineNumber { if lineID != currentLineNumber {
@ -221,7 +221,7 @@ extension CandidatePool {
) )
} }
result.append(cellString) result.append(cellString)
if maxLinesPerPage > 1, currentCell.displayedText.count > 1 { if isMatrix, currentCell.displayedText.count > 1 {
if currentCell.isHighlighted { if currentCell.isHighlighted {
spacer.addAttribute( spacer.addAttribute(
.backgroundColor, .backgroundColor,

View File

@ -96,7 +96,7 @@ public class CtlCandidateTDK: CtlCandidate, NSWindowDelegate {
CandidateCellData.unifiedSize = candidateFont.pointSize CandidateCellData.unifiedSize = candidateFont.pointSize
guard let delegate = delegate else { return } guard let delegate = delegate else { return }
Self.thePool = .init( Self.thePool = .init(
candidates: delegate.candidatePairs(conv: true), lines: maxLinesPerPage, candidates: delegate.candidatePairs(conv: true), lines: maxLinesPerPage, isExpanded: false,
selectionKeys: delegate.selectionKeys, layout: currentLayout.layoutTDK, locale: locale selectionKeys: delegate.selectionKeys, layout: currentLayout.layoutTDK, locale: locale
) )
Self.thePool.tooltip = tooltip Self.thePool.tooltip = tooltip

View File

@ -191,7 +191,7 @@
wrappedCell.setHuggingPriority(.fittingSizeCompression, for: .vertical) wrappedCell.setHuggingPriority(.fittingSizeCompression, for: .vertical)
Self.makeSimpleConstraint(item: wrappedCell, attribute: .height, relation: .equal, value: cellHeight) Self.makeSimpleConstraint(item: wrappedCell, attribute: .height, relation: .equal, value: cellHeight)
switch thePool.layout { switch thePool.layout {
case .horizontal where thePool.maxLinesPerPage > 1: case .horizontal where thePool.isMatrix:
Self.makeSimpleConstraint(item: wrappedCell, attribute: .width, relation: .equal, value: cellWidth) Self.makeSimpleConstraint(item: wrappedCell, attribute: .width, relation: .equal, value: cellWidth)
default: default:
Self.makeSimpleConstraint(item: wrappedCell, attribute: .width, relation: .greaterThanOrEqual, value: cellWidth) Self.makeSimpleConstraint(item: wrappedCell, attribute: .width, relation: .greaterThanOrEqual, value: cellWidth)
@ -216,7 +216,7 @@
private func generateLineContainer(_ theLine: inout [CandidateCellData]) -> NSStackView { private func generateLineContainer(_ theLine: inout [CandidateCellData]) -> NSStackView {
let isVerticalListing: Bool = thePool.layout == .vertical let isVerticalListing: Bool = thePool.layout == .vertical
let isMatrix = thePool.maxLinesPerPage > 1 let isMatrix = thePool.isMatrix
let vwrCurrentLine = NSStackView() let vwrCurrentLine = NSStackView()
vwrCurrentLine.spacing = 0 vwrCurrentLine.spacing = 0
vwrCurrentLine.orientation = isVerticalListing ? .vertical : .horizontal vwrCurrentLine.orientation = isVerticalListing ? .vertical : .horizontal

View File

@ -41,7 +41,7 @@ public struct VwrCandidateTDK: View {
default: default:
mainViewVertical.background(candidateListBackground) mainViewVertical.background(candidateListBackground)
} }
if thePool.maxLinesPerPage > 1 || thePool.layout == .vertical { if thePool.isMatrix || thePool.layout == .vertical {
statusBarContent statusBarContent
} }
} }
@ -193,7 +193,7 @@ extension VwrCandidateTDK {
Double(thePool.maxLineCapacity) * (CandidatePool.blankCell.cellLength()) Double(thePool.maxLineCapacity) * (CandidatePool.blankCell.cellLength())
+ spacings + spacings
) )
return thePool.layout == .horizontal && thePool.maxLinesPerPage > 1 ? maxWindowWith : nil return thePool.layout == .horizontal && thePool.isMatrix ? maxWindowWith : nil
} }
var firstReverseLookupResult: String { var firstReverseLookupResult: String {