TDKCandidates // Ensure compatibility with macOS 10.9 Mavericks.
* Refine constraint calculation methods. * Reintroduce `window.isOpaque = false`. * Fix translatesAutoresizingMaskIntoConstraints(). * Debugging dimensions of UI components. * Avoid using zero rects on construction.
This commit is contained in:
parent
cf04e09acd
commit
ab4ae41af6
|
@ -15,6 +15,7 @@ import SwiftUIBackports
|
|||
|
||||
/// 用來管理選字窗內顯示的候選字的單位。用 class 型別會比較方便一些。
|
||||
public class CandidateCellData: Hashable {
|
||||
public var visualDimension: CGSize = .zero
|
||||
public var locale = ""
|
||||
public static var unifiedSize: Double = 16
|
||||
public var key: String
|
||||
|
|
|
@ -89,6 +89,7 @@ public class CtlCandidateTDK: CtlCandidate, NSWindowDelegate {
|
|||
Self.thePool.reverseLookupResult = reverseLookupResult
|
||||
}
|
||||
DispatchQueue.main.async { [self] in
|
||||
window.isOpaque = false
|
||||
window.backgroundColor = .clear
|
||||
viewCheck: if #available(macOS 10.15, *) {
|
||||
if useCocoa {
|
||||
|
|
|
@ -13,13 +13,15 @@ import Shared
|
|||
public class VwrCandidateTDKCocoa: NSStackView {
|
||||
public weak var controller: CtlCandidateTDK?
|
||||
public var thePool: CandidatePool
|
||||
private var lineDimension: CGSize = .zero
|
||||
private var candidateAreaDimension: CGSize = .zero
|
||||
|
||||
// MARK: - Constructors.
|
||||
|
||||
public init(controller: CtlCandidateTDK? = nil, thePool pool: CandidatePool) {
|
||||
self.controller = controller
|
||||
thePool = pool
|
||||
super.init(frame: .init(origin: .zero, size: .zero))
|
||||
super.init(frame: .init(origin: .zero, size: .init(width: 114_514, height: 114_514)))
|
||||
refresh()
|
||||
}
|
||||
|
||||
|
@ -33,6 +35,15 @@ public class VwrCandidateTDKCocoa: NSStackView {
|
|||
|
||||
public extension VwrCandidateTDKCocoa {
|
||||
func refresh() {
|
||||
defer {
|
||||
vCLog(Self.strForConstraintStatistics.description)
|
||||
Self.strForConstraintStatistics = .init()
|
||||
}
|
||||
// 用來登記全部的行容器,方便在收尾階段統一設定 constraints。
|
||||
var arrStackViewsOfLines = [NSStackView]()
|
||||
// 清理兩個計數器。
|
||||
lineDimension = .zero
|
||||
candidateAreaDimension = .zero
|
||||
// 容器自身美化。
|
||||
edgeInsets = .init(top: 5, left: 5, bottom: 5, right: 5)
|
||||
wantsLayer = true
|
||||
|
@ -51,6 +62,7 @@ public extension VwrCandidateTDKCocoa {
|
|||
var theLine = thePool.candidateLines[lineID]
|
||||
let vwrCurrentLine = generateLineContainer(&theLine)
|
||||
candidateContainer.addView(vwrCurrentLine, in: isVerticalListing ? .top : .leading)
|
||||
arrStackViewsOfLines.append(vwrCurrentLine)
|
||||
}
|
||||
if thePool.maxLinesPerPage - thePool.lineRangeForCurrentPage.count > 0 {
|
||||
thePool.lineRangeForFinalPageBlanked.enumerated().forEach { _ in
|
||||
|
@ -60,26 +72,54 @@ public extension VwrCandidateTDKCocoa {
|
|||
}
|
||||
let vwrCurrentLine = generateLineContainer(&theLine)
|
||||
candidateContainer.addView(vwrCurrentLine, in: isVerticalListing ? .top : .leading)
|
||||
arrStackViewsOfLines.append(vwrCurrentLine)
|
||||
}
|
||||
}
|
||||
|
||||
// 處理行寬或列高。
|
||||
switch thePool.layout {
|
||||
case .vertical:
|
||||
let minHeight = Double(thePool.maxLineCapacity)
|
||||
* drawCellCocoa(thePool.blankCell).fittingSize.height
|
||||
candidateContainer.subviews.forEach { vwrCurrentLine in
|
||||
Self.makeSimpleConstraint(item: vwrCurrentLine, attribute: .height, relation: .equal, value: minHeight)
|
||||
var accumulatedWidth: CGFloat = 0
|
||||
var lines = [[CandidateCellData]]()
|
||||
thePool.lineRangeForCurrentPage.forEach { lines.append(thePool.candidateLines[$0]) }
|
||||
arrStackViewsOfLines.enumerated().forEach { viewLineID, vwrCurrentLine in
|
||||
var columnWidth: CGFloat = 0
|
||||
if (0 ..< lines.count).contains(viewLineID), !lines.isEmpty {
|
||||
let line = Array(lines[viewLineID])
|
||||
columnWidth = line.map(\.visualDimension.width).max() ?? lineDimension.width
|
||||
} else {
|
||||
columnWidth = thePool.blankCell.visualDimension.width
|
||||
}
|
||||
accumulatedWidth += columnWidth
|
||||
Self.makeSimpleConstraint(item: vwrCurrentLine, attribute: .width, relation: .equal, value: columnWidth)
|
||||
Self.makeSimpleConstraint(item: vwrCurrentLine, attribute: .height, relation: .equal, value: lineDimension.height)
|
||||
Self.addStatistics(vwrCurrentLine, memo: "vwrCurrentLine")
|
||||
}
|
||||
case .horizontal where thePool.maxLinesPerPage > 1:
|
||||
let containerWidth = candidateContainer.fittingSize.width
|
||||
candidateContainer.subviews.forEach { vwrCurrentLine in
|
||||
Self.makeSimpleConstraint(item: vwrCurrentLine, attribute: .width, relation: .equal, value: containerWidth)
|
||||
candidateAreaDimension.width = accumulatedWidth
|
||||
candidateAreaDimension.height = lineDimension.height
|
||||
case .horizontal:
|
||||
arrStackViewsOfLines.forEach { vwrCurrentLine in
|
||||
Self.makeSimpleConstraint(item: vwrCurrentLine, attribute: .width, relation: .equal, value: lineDimension.width)
|
||||
Self.makeSimpleConstraint(item: vwrCurrentLine, attribute: .height, relation: .equal, value: lineDimension.height)
|
||||
Self.addStatistics(vwrCurrentLine, memo: "vwrCurrentLine")
|
||||
}
|
||||
default: break
|
||||
candidateAreaDimension.width = lineDimension.width
|
||||
candidateAreaDimension.height = lineDimension.height * Double(thePool.maxLinesPerPage)
|
||||
}
|
||||
|
||||
Self.makeSimpleConstraint(item: candidateContainer, attribute: .width, relation: .equal, value: candidateAreaDimension.width)
|
||||
Self.makeSimpleConstraint(item: candidateContainer, attribute: .height, relation: .equal, value: candidateAreaDimension.height)
|
||||
Self.addStatistics(candidateContainer, memo: "candidateContainer")
|
||||
|
||||
let vwrPeripherals = Self.makeLabel(thePool.attributedDescriptionBottomPanes)
|
||||
Self.makeSimpleConstraint(
|
||||
item: vwrPeripherals, attribute: .height, relation: .greaterThanOrEqual,
|
||||
value: vwrPeripherals.fittingSize.height
|
||||
)
|
||||
Self.makeSimpleConstraint(
|
||||
item: vwrPeripherals, attribute: .width, relation: .greaterThanOrEqual,
|
||||
value: vwrPeripherals.fittingSize.width
|
||||
)
|
||||
|
||||
// 組裝。
|
||||
let finalContainer = NSStackView()
|
||||
|
@ -94,12 +134,23 @@ public extension VwrCandidateTDKCocoa {
|
|||
finalContainer.spacing = 5
|
||||
} else {
|
||||
finalContainer.spacing = 2
|
||||
Self.makeSimpleConstraint(item: vwrPeripherals, attribute: .width, relation: .greaterThanOrEqual, value: vwrPeripherals.fittingSize.width)
|
||||
}
|
||||
Self.addStatistics(vwrPeripherals, memo: "vwrPeripherals")
|
||||
|
||||
finalContainer.orientation = finalContainerOrientation
|
||||
finalContainer.alignment = finalContainerOrientation == .vertical ? .leading : .centerY
|
||||
finalContainer.addView(candidateContainer, in: .leading)
|
||||
finalContainer.addView(vwrPeripherals, in: .leading)
|
||||
Self.makeSimpleConstraint(
|
||||
item: finalContainer, attribute: .width,
|
||||
relation: .equal, value: finalContainer.fittingSize.width
|
||||
)
|
||||
Self.makeSimpleConstraint(
|
||||
item: finalContainer, attribute: .height,
|
||||
relation: .equal, value: finalContainer.fittingSize.height
|
||||
)
|
||||
Self.addStatistics(finalContainer, memo: "finalContainer")
|
||||
|
||||
// 更換容器內容為上文生成的新內容。
|
||||
subviews.forEach { removeView($0) }
|
||||
|
@ -119,6 +170,9 @@ private extension VwrCandidateTDKCocoa {
|
|||
let theCell = theCell ?? thePool.blankCell
|
||||
let cellLabel = VwrCandidateCell(cell: theCell)
|
||||
cellLabel.target = self
|
||||
Self.makeSimpleConstraint(item: cellLabel, attribute: .width, relation: .equal, value: cellLabel.fittingSize.width)
|
||||
Self.makeSimpleConstraint(item: cellLabel, attribute: .height, relation: .equal, value: cellLabel.fittingSize.height)
|
||||
Self.addStatistics(cellLabel, memo: "cellLabel")
|
||||
let wrappedCell = NSStackView()
|
||||
let padding: CGFloat = 3
|
||||
wrappedCell.edgeInsets = .init(top: padding, left: padding, bottom: padding, right: padding)
|
||||
|
@ -128,11 +182,10 @@ private extension VwrCandidateTDKCocoa {
|
|||
wrappedCell.layer?.backgroundColor = theCell.themeColorCocoa.cgColor
|
||||
wrappedCell.layer?.cornerRadius = padding * 2
|
||||
}
|
||||
let cellWidth = thePool.cellWidth(theCell).min ?? wrappedCell.fittingSize.width
|
||||
let cellWidth = max(thePool.cellWidth(theCell).min ?? wrappedCell.fittingSize.width, wrappedCell.fittingSize.width)
|
||||
let cellHeight = wrappedCell.fittingSize.height
|
||||
wrappedCell.setHuggingPriority(.fittingSizeCompression, for: .horizontal)
|
||||
wrappedCell.setHuggingPriority(.fittingSizeCompression, for: .vertical)
|
||||
wrappedCell.translatesAutoresizingMaskIntoConstraints = false
|
||||
Self.makeSimpleConstraint(item: wrappedCell, attribute: .height, relation: .equal, value: cellHeight)
|
||||
switch thePool.layout {
|
||||
case .horizontal where thePool.maxLinesPerPage > 1:
|
||||
|
@ -140,6 +193,8 @@ private extension VwrCandidateTDKCocoa {
|
|||
default:
|
||||
Self.makeSimpleConstraint(item: wrappedCell, attribute: .width, relation: .greaterThanOrEqual, value: cellWidth)
|
||||
}
|
||||
Self.addStatistics(wrappedCell, memo: "wrappedCell")
|
||||
theCell.visualDimension = .init(width: cellWidth, height: cellHeight)
|
||||
return wrappedCell
|
||||
}
|
||||
|
||||
|
@ -162,9 +217,20 @@ private extension VwrCandidateTDKCocoa {
|
|||
let vwrCurrentLine = NSStackView()
|
||||
vwrCurrentLine.spacing = 0
|
||||
vwrCurrentLine.orientation = isVerticalListing ? .vertical : .horizontal
|
||||
var cellHeight = 0.0
|
||||
var lineSize: CGSize = .zero
|
||||
let isCurrentLine = theLine.hasHighlightedCell
|
||||
theLine.forEach { theCell in
|
||||
vwrCurrentLine.addView(drawCellCocoa(theCell), in: isVerticalListing ? .top : .leading)
|
||||
switch thePool.layout {
|
||||
case .horizontal:
|
||||
lineSize.width += theCell.visualDimension.width
|
||||
lineSize.height = max(lineSize.height, theCell.visualDimension.height)
|
||||
case .vertical:
|
||||
lineSize.width = max(lineSize.width, theCell.visualDimension.width)
|
||||
lineSize.height += theCell.visualDimension.height
|
||||
}
|
||||
cellHeight = max(theCell.visualDimension.height, cellHeight)
|
||||
}
|
||||
let lineBg = lineBackground(isCurrentLine: isCurrentLine, isMatrix: isMatrix)
|
||||
vwrCurrentLine.wantsLayer = isCurrentLine && isMatrix
|
||||
|
@ -172,6 +238,11 @@ private extension VwrCandidateTDKCocoa {
|
|||
vwrCurrentLine.layer?.backgroundColor = lineBg.cgColor
|
||||
vwrCurrentLine.layer?.cornerRadius = 6
|
||||
}
|
||||
lineDimension.width = max(lineSize.width, lineDimension.width)
|
||||
switch thePool.layout {
|
||||
case .horizontal: lineDimension.height = max(lineSize.height, lineDimension.height)
|
||||
case .vertical: lineDimension.height = cellHeight * Double(thePool.maxLineCapacity)
|
||||
}
|
||||
return vwrCurrentLine
|
||||
}
|
||||
|
||||
|
@ -192,7 +263,17 @@ private extension VwrCandidateTDKCocoa {
|
|||
// MARK: - Constraint Utilities
|
||||
|
||||
private extension VwrCandidateTDKCocoa {
|
||||
static var strForConstraintStatistics = NSMutableString(string: "TDKCandidates Dimensions (Debug):\n")
|
||||
|
||||
static func addStatistics(_ target: NSView, memo: String = "") {
|
||||
if Self.strForConstraintStatistics.length == 0 {
|
||||
Self.strForConstraintStatistics.append("TDKCandidates Dimensions (Debug):\n")
|
||||
}
|
||||
Self.strForConstraintStatistics.append("\(target.fittingSize) \(memo)\n")
|
||||
}
|
||||
|
||||
static func makeSimpleConstraint(item: NSView, attribute: NSLayoutConstraint.Attribute, relation: NSLayoutConstraint.Relation, value: CGFloat) {
|
||||
item.translatesAutoresizingMaskIntoConstraints = false
|
||||
let widthConstraint = NSLayoutConstraint(
|
||||
item: item, attribute: attribute, relatedBy: relation, toItem: nil,
|
||||
attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: value
|
||||
|
@ -208,7 +289,7 @@ private extension VwrCandidateTDKCocoa {
|
|||
public var cellData: CandidateCellData
|
||||
public init(cell: CandidateCellData) {
|
||||
cellData = cell
|
||||
super.init(frame: .init(origin: .zero, size: .zero))
|
||||
super.init(frame: .init(origin: .zero, size: .init(width: 114_514, height: 114_514)))
|
||||
isSelectable = false
|
||||
isEditable = false
|
||||
isBordered = false
|
||||
|
|
Loading…
Reference in New Issue