From ad5263c6e3d4978f9f5e38727c49a3fa04f7e036 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sat, 4 Jun 2022 18:07:31 +0800 Subject: [PATCH] ctlCandidate // Add pageCounter. --- .../Modules/IMEModules/ctlInputMethod.swift | 13 ++- .../CandidateUI/ctlCandidateUniversal.swift | 95 +++++++++++++++++-- 2 files changed, 98 insertions(+), 10 deletions(-) diff --git a/Source/Modules/IMEModules/ctlInputMethod.swift b/Source/Modules/IMEModules/ctlInputMethod.swift index 8b856e0c..a371cfb7 100644 --- a/Source/Modules/IMEModules/ctlInputMethod.swift +++ b/Source/Modules/IMEModules/ctlInputMethod.swift @@ -487,12 +487,18 @@ extension ctlInputMethod { ctlCandidateCurrent.delegate = nil + /// 下面這一段本可直接指定 currentLayout,但這樣的話翻頁按鈕位置無法精準地重新繪製。 + /// 所以只能重新初期化。壞處就是得在 ctlCandidate() 當中與 SymbolTable 控制有關的地方 + /// 新增一個空狀態請求、防止縱排與橫排選字窗同時出現。 + /// layoutCandidateView 在這裡無法起到糾正作用。 + /// 該問題徹底解決的價值並不大,直接等到 macOS 10.x 全線淘汰之後用 SwiftUI 重寫選字窗吧。 + if isCandidateWindowVertical { // 縱排輸入時強制使用縱排選字窗 - ctlCandidateCurrent.currentLayout = .vertical + ctlCandidateCurrent = .init(.vertical) } else if mgrPrefs.useHorizontalCandidateList { - ctlCandidateCurrent.currentLayout = .horizontal + ctlCandidateCurrent = .init(.horizontal) } else { - ctlCandidateCurrent.currentLayout = .vertical + ctlCandidateCurrent = .init(.vertical) } // set the attributes for the candidate panel (which uses NSAttributedString) @@ -667,6 +673,7 @@ extension ctlInputMethod: ctlCandidateDelegate { let node = state.node.children?[index] { if let children = node.children, !children.isEmpty { + handle(state: .Empty(), client: client) // 防止縱橫排選字窗同時出現 handle( state: .SymbolTable(node: node, isTypingVertical: state.isTypingVertical), client: currentClient diff --git a/Source/UI/CandidateUI/ctlCandidateUniversal.swift b/Source/UI/CandidateUI/ctlCandidateUniversal.swift index a622265c..7447cd16 100644 --- a/Source/UI/CandidateUI/ctlCandidateUniversal.swift +++ b/Source/UI/CandidateUI/ctlCandidateUniversal.swift @@ -33,6 +33,7 @@ private class vwrCandidateUniversal: NSView { var action: Selector? weak var target: AnyObject? var isVerticalLayout: Bool = false + var fractionFontSize: CGFloat = 12.0 private var keyLabels: [String] = [] private var displayedCandidates: [String] = [] @@ -135,6 +136,7 @@ private class vwrCandidateUniversal: NSView { let labelFontSize = labelFont.pointSize let candidateFontSize = candidateFont.pointSize let biggestSize = max(labelFontSize, candidateFontSize) + fractionFontSize = round(biggestSize * 0.75) keyLabelWidth = ceil(labelFontSize) keyLabelHeight = ceil(labelFontSize * 2) candidateTextHeight = ceil(candidateFontSize * 1.20) @@ -365,6 +367,7 @@ public class ctlCandidateUniversal: ctlCandidate { private var candidateView: vwrCandidateUniversal private var prevPageButton: NSButton private var nextPageButton: NSButton + private var pageCounterLabel: NSTextField private var currentPageIndex: Int = 0 override public var currentLayout: Layout { get { candidateView.isVerticalLayout ? .vertical : .horizontal } @@ -400,10 +403,12 @@ public class ctlCandidateUniversal: ctlCandidate { panel.contentView?.addSubview(candidateView) + // MARK: Add Buttons + contentRect.size = NSSize(width: 20.0, height: 10.0) // Reduce the button width let buttonAttribute: [NSAttributedString.Key: Any] = [.font: NSFont.systemFont(ofSize: 9.0)] - nextPageButton = NSButton(frame: contentRect) + nextPageButton = .init(frame: contentRect) NSColor.controlBackgroundColor.setFill() NSBezierPath.fill(nextPageButton.bounds) nextPageButton.wantsLayer = true @@ -416,7 +421,7 @@ public class ctlCandidateUniversal: ctlCandidate { nextPageButton.attributedTitle = NSMutableAttributedString( string: " ", attributes: buttonAttribute ) // Next Page Arrow - prevPageButton = NSButton(frame: contentRect) + prevPageButton = .init(frame: contentRect) NSColor.controlBackgroundColor.setFill() NSBezierPath.fill(prevPageButton.bounds) prevPageButton.wantsLayer = true @@ -432,6 +437,24 @@ public class ctlCandidateUniversal: ctlCandidate { panel.contentView?.addSubview(nextPageButton) panel.contentView?.addSubview(prevPageButton) + // MARK: Add Page Counter + + contentRect.size = NSSize(width: 40.0, height: 20.0) + pageCounterLabel = .init(frame: contentRect) + pageCounterLabel.isEditable = false + pageCounterLabel.isSelectable = false + pageCounterLabel.isBezeled = false + pageCounterLabel.textColor = NSColor( + red: 0.86, green: 0.86, blue: 0.86, alpha: 1.00 + ) + pageCounterLabel.drawsBackground = true + pageCounterLabel.backgroundColor = NSColor( + red: 0.18, green: 0.18, blue: 0.18, alpha: 1.00 + ) + panel.contentView?.addSubview(pageCounterLabel) + + // MARK: Post-Init() + super.init(window: panel) currentLayout = layout @@ -443,6 +466,8 @@ public class ctlCandidateUniversal: ctlCandidate { prevPageButton.target = self prevPageButton.action = #selector(pageButtonAction(_:)) + + pageCounterLabel.font = pageCounterFont } @available(*, unavailable) @@ -529,6 +554,30 @@ extension ctlCandidateUniversal { return totalCount / keyLabelCount + ((totalCount % keyLabelCount) != 0 ? 1 : 0) } + // 用來顯示頁面計數器的 NSFont。因為結果可 nil,所以最好 guard-let 再用。 + private var pageCounterFont: NSFont? { + var pointSize: CGFloat { candidateView.fractionFontSize } + let systemFontDesc = NSFont.systemFont(ofSize: pointSize, weight: .light).fontDescriptor + let fractionFontDesc = systemFontDesc.addingAttributes( + [ + NSFontDescriptor.AttributeName.traits: [ + [ + NSFontDescriptor.FeatureKey.typeIdentifier: kFractionsType, + NSFontDescriptor.FeatureKey.selectorIdentifier: kDiagonalFractionsSelector, + ] + ] + ] + ) + return NSFont(descriptor: fractionFontDesc, size: pointSize) ?? nil + } + + // 用來生成拿給頁面計數器用的顯示字串。 + // TODO: 這衰洨的 pageCount 總是返回空字串,需要調查。 + private var pageCounterText: String { + if pageCount < 2 { return .init() } + return "\(currentPageIndex + 1)/" + } + private func layoutCandidateView() { guard let delegate = delegate else { return @@ -551,23 +600,25 @@ extension ctlCandidateUniversal { var frameRect = candidateView.frame frameRect.size = newSize candidateView.frame = frameRect + let counterHeight: CGFloat = newSize.height - 24 if pageCount > 1, mgrPrefs.showPageButtonsInCandidateWindow { var buttonRect = nextPageButton.frame let spacing: CGFloat = 0.0 if currentLayout == .horizontal { buttonRect.size.height = floor(newSize.height / 2) } - var buttonOriginY = newSize.height - (buttonRect.size.height * 2.0 + spacing) - if currentLayout == .horizontal { buttonOriginY /= 2.0 } - + let buttonOriginY: CGFloat = { + if currentLayout == .vertical { + return counterHeight + } + return (newSize.height - (buttonRect.size.height * 2.0 + spacing)) / 2.0 + }() buttonRect.origin = NSPoint(x: newSize.width, y: buttonOriginY) nextPageButton.frame = buttonRect - buttonRect.origin = NSPoint( x: newSize.width, y: buttonOriginY + buttonRect.size.height + spacing ) prevPageButton.frame = buttonRect - newSize.width += 20 nextPageButton.isHidden = false prevPageButton.isHidden = false @@ -576,6 +627,36 @@ extension ctlCandidateUniversal { prevPageButton.isHidden = true } + if !pageCounterText.isEmpty { + let attrString = NSAttributedString( + string: pageCounterText.appending(String(pageCount)), + attributes: [ + .font: pageCounterFont as AnyObject + ] + ) + pageCounterLabel.attributedStringValue = attrString + var rect = attrString.boundingRect( + with: NSSize(width: 1600.0, height: 1600.0), + options: .usesLineFragmentOrigin + ) + + rect.size.height += 3 + let rectOriginY: CGFloat = + (currentLayout == .horizontal) + ? (newSize.height - rect.height) / 2 + : counterHeight + let rectOriginX: CGFloat = + mgrPrefs.showPageButtonsInCandidateWindow + ? newSize.width + : newSize.width + 4 + rect.origin = NSPoint(x: rectOriginX, y: rectOriginY) + pageCounterLabel.frame = rect + newSize.width += rect.width + 4 + pageCounterLabel.isHidden = false + } else { + pageCounterLabel.isHidden = true + } + frameRect = window?.frame ?? NSRect.zero let topLeftPoint = NSPoint(x: frameRect.origin.x, y: frameRect.origin.y + frameRect.size.height)