Shiki: Swiftify // Voltaire UI Revamp - Vertical

This commit is contained in:
ShikiSuen 2022-01-14 22:49:45 +08:00
parent e6047395ac
commit 3f66d725b6
1 changed files with 107 additions and 85 deletions

View File

@ -1,11 +1,16 @@
// //
// VerticalCandidateController.swift // VerticalCandidateController.swift
// //
// Copyright (c) 2011 The McBopomofo Project. // Voltaire IME Candidate Controller Module
//
// Copyright (c) 2011-2022 The OpenVanilla Project.
// Beautified by (c) 2021-2022 The vChewing Project.
// //
// Contributors: // Contributors:
// Mengjuei Hsieh (@mjhsieh) // Lukhnos Liu (@lukhnos) @ OpenVanilla // Original Developer
// Weizhong Yang (@zonble) // Weizhong Yang (@zonble) @ OpenVanilla // Rewriter to Swift
// Shiki Suen (ShikiSuen) @ vChewing // Beautification (objC)
// //
// Based on the Syrup Project and the Formosana Library // Based on the Syrup Project and the Formosana Library
// by Lukhnos Liu (@lukhnos). // by Lukhnos Liu (@lukhnos).
@ -39,45 +44,46 @@ fileprivate class VerticalKeyLabelStripView: NSView {
var labelOffsetY: CGFloat = 0 var labelOffsetY: CGFloat = 0
var keyLabels: [String] = [] var keyLabels: [String] = []
var highlightedIndex: UInt = UInt.max var highlightedIndex: UInt = UInt.max
override var isFlipped: Bool { override var isFlipped: Bool {
true true
} }
override func draw(_ dirtyRect: NSRect) { override func draw(_ dirtyRect: NSRect) {
let bounds = self.bounds let bounds = self.bounds
NSColor.white.setFill() NSColor.clear.setFill() // Disable white base color, just in case.
NSBezierPath.fill(bounds) NSBezierPath.fill(bounds)
let count = UInt(keyLabels.count) let count = UInt(keyLabels.count)
if count == 0 { if count == 0 {
return return
} }
let cellHeight: CGFloat = bounds.size.height / CGFloat(count) let cellHeight: CGFloat = bounds.size.height / CGFloat(count)
let black = NSColor.black
let darkGray = NSColor(deviceWhite: 0.7, alpha: 1.0)
let lightGray = NSColor(deviceWhite: 0.8, alpha: 1.0)
let paraStyle = NSMutableParagraphStyle() let paraStyle = NSMutableParagraphStyle()
paraStyle.setParagraphStyle(NSParagraphStyle.default) paraStyle.setParagraphStyle(NSParagraphStyle.default)
paraStyle.alignment = .center paraStyle.alignment = .center
let textAttr: [NSAttributedString.Key: AnyObject] = [ let textAttr: [NSAttributedString.Key: AnyObject] = [
.font: keyLabelFont, .font: keyLabelFont,
.foregroundColor: black, .foregroundColor: NSColor.secondaryLabelColor, // The index text color of the non-highlightened candidate
.paragraphStyle: paraStyle]
let textAttrHighlight: [NSAttributedString.Key: AnyObject] = [
.font: keyLabelFont,
.foregroundColor: NSColor.selectedMenuItemTextColor.withAlphaComponent(0.84), // The index text color of the highlightened candidate
.paragraphStyle: paraStyle] .paragraphStyle: paraStyle]
for index in 0..<count { for index in 0..<count {
let textRect = NSRect(x: 0.0, y: CGFloat(index) * cellHeight + labelOffsetY, width: bounds.size.width, height: cellHeight - labelOffsetY) let textRect = NSRect(x: 0.0, y: CGFloat(index) * cellHeight + labelOffsetY, width: bounds.size.width, height: cellHeight - labelOffsetY)
var cellRect = NSRect(x: 0.0, y: CGFloat(index) * cellHeight, width: bounds.size.width, height: cellHeight - 1) var cellRect = NSRect(x: 0.0, y: CGFloat(index) * cellHeight, width: bounds.size.width, height: cellHeight - 0.0) // Remove the splitting line between the candidate text label
if index + 1 >= count { if index + 1 >= count {
cellRect.size.height += 1.0 cellRect.size.height += 1.0
} }
(index == highlightedIndex ? darkGray : lightGray).setFill() (index == highlightedIndex ? NSColor.alternateSelectedControlColor : NSColor.controlBackgroundColor).setFill() // The background color of the candidate (highlightened : non-highlightened)
NSBezierPath.fill(cellRect) NSBezierPath.fill(cellRect)
let text = keyLabels[Int(index)] let text = keyLabels[Int(index)]
(text as NSString).draw(in: textRect, withAttributes: textAttr) (text as NSString).draw(in: textRect, withAttributes: (index == highlightedIndex ? textAttrHighlight : textAttr))
} }
} }
} }
@ -91,10 +97,9 @@ fileprivate class VerticalCandidateTableView: NSTableView {
} }
} }
private let kCandidateTextPadding = 24.0 private let kCandidateTextPadding:CGFloat = 24.0
private let kCandidateTextLeftMargin = 8.0 private let kCandidateTextLeftMargin:CGFloat = 8.0
private let kCandidateTextPaddingWithMandatedTableViewPadding = 18.0
private let kCandidateTextLeftMarginWithMandatedTableViewPadding = 0.0
@objc (VTVerticalCandidateController) @objc (VTVerticalCandidateController)
@ -106,67 +111,85 @@ public class VerticalCandidateController: CandidateController {
private var candidateTextPadding: CGFloat = kCandidateTextPadding private var candidateTextPadding: CGFloat = kCandidateTextPadding
private var candidateTextLeftMargin: CGFloat = kCandidateTextLeftMargin private var candidateTextLeftMargin: CGFloat = kCandidateTextLeftMargin
private var maxCandidateAttrStringWidth: CGFloat = 0 private var maxCandidateAttrStringWidth: CGFloat = 0
public init() { public init() {
var contentRect = NSRect(x: 128.0, y: 128.0, width: 0.0, height: 0.0) var contentRect = NSRect(x: 128.0, y: 128.0, width: 0.0, height: 0.0)
let styleMask: NSWindow.StyleMask = [.borderless, .nonactivatingPanel] let styleMask: NSWindow.StyleMask = [.borderless, .nonactivatingPanel]
let panelView = NSView(frame: contentRect) // We need an NSView as a round-cornered container for the candidate panel.
let panel = NSPanel(contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false) let panel = NSPanel(contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false)
panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel) + 1) panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel) + 1)
panel.contentView = panelView; // Specify the NSView to the panel as its content view.
panel.hasShadow = true panel.hasShadow = true
panel.isOpaque = false // Again transparentify the panel. Otherwise, the cornerRadius below will be meaningless.
panel.backgroundColor = NSColor.clear // One more insurance to transparentify the panel.
// Rounded panelView container.
panelView.wantsLayer = true
panelView.layer?.borderColor = NSColor.selectedMenuItemTextColor.withAlphaComponent(0.30).cgColor
panelView.layer?.borderWidth = 1
panelView.layer?.cornerRadius = 6.0
contentRect.origin = NSPoint.zero contentRect.origin = NSPoint.zero
var stripRect = contentRect var stripRect = contentRect
stripRect.size.width = 10.0 stripRect.size.width = 10.0
keyLabelStripView = VerticalKeyLabelStripView(frame: stripRect) keyLabelStripView = VerticalKeyLabelStripView(frame: stripRect)
panel.contentView?.addSubview(keyLabelStripView) panel.contentView?.addSubview(keyLabelStripView)
var scrollViewRect = contentRect var scrollViewRect = contentRect
scrollViewRect.origin.x = stripRect.size.width scrollViewRect.origin.x = stripRect.size.width
scrollViewRect.size.width -= stripRect.size.width scrollViewRect.size.width -= stripRect.size.width
scrollView = NSScrollView(frame: scrollViewRect) scrollView = NSScrollView(frame: scrollViewRect)
scrollView.autohidesScrollers = true // Our aesthetics of UI design has to stay close to Apple.
scrollView.drawsBackground = true // Allow scrollView to draw background.
scrollView.backgroundColor = NSColor.clear // Draw a tramsparent background.
scrollView.verticalScrollElasticity = .none scrollView.verticalScrollElasticity = .none
tableView = NSTableView(frame: contentRect) tableView = NSTableView(frame: contentRect)
let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "candidate")) let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "candidate"))
column.dataCell = NSTextFieldCell() column.dataCell = NSTextFieldCell()
if let dataCell = column.dataCell as? NSTextFieldCell {
dataCell.textColor = NSColor.labelColor // candidate phrase text color for conveniences of customization.
}
column.isEditable = false column.isEditable = false
candidateTextPadding = kCandidateTextPadding candidateTextPadding = kCandidateTextPadding
candidateTextLeftMargin = kCandidateTextLeftMargin candidateTextLeftMargin = kCandidateTextLeftMargin
tableView.addTableColumn(column) tableView.addTableColumn(column)
tableView.intercellSpacing = NSSize(width: 0.0, height: 1.0) tableView.intercellSpacing = NSSize(width: 0.0, height: 1.0)
tableView.headerView = nil tableView.headerView = nil
tableView.allowsMultipleSelection = false tableView.allowsMultipleSelection = false
tableView.allowsEmptySelection = false tableView.allowsEmptySelection = false
tableView.backgroundColor = NSColor.clear
if #available(macOS 10.16, *) { tableView.gridColor = NSColor.clear
tableView.style = .fullWidth
candidateTextPadding = kCandidateTextPaddingWithMandatedTableViewPadding if #available(macOS 11.0, *) {
candidateTextLeftMargin = kCandidateTextLeftMarginWithMandatedTableViewPadding tableView.style = .plain
tableView.enclosingScrollView?.borderType = .noBorder
} }
scrollView.documentView = tableView scrollView.documentView = tableView
panel.contentView?.addSubview(scrollView) panel.contentView?.addSubview(scrollView)
let paraStyle = NSMutableParagraphStyle() let paraStyle = NSMutableParagraphStyle()
paraStyle.setParagraphStyle(NSParagraphStyle.default) paraStyle.setParagraphStyle(NSParagraphStyle.default)
paraStyle.firstLineHeadIndent = candidateTextLeftMargin paraStyle.firstLineHeadIndent = candidateTextLeftMargin
paraStyle.lineBreakMode = .byClipping paraStyle.lineBreakMode = .byClipping
candidateTextParagraphStyle = paraStyle candidateTextParagraphStyle = paraStyle
super.init(window: panel) super.init(window: panel)
tableView.dataSource = self tableView.dataSource = self
tableView.delegate = self tableView.delegate = self
tableView.doubleAction = #selector(rowDoubleClicked(_:)) tableView.doubleAction = #selector(rowDoubleClicked(_:))
tableView.target = self tableView.target = self
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
public override func reloadData() { public override func reloadData() {
maxCandidateAttrStringWidth = ceil(candidateFont.pointSize * 2.0 + candidateTextPadding) maxCandidateAttrStringWidth = ceil(candidateFont.pointSize * 2.0 + candidateTextPadding)
tableView.reloadData() tableView.reloadData()
@ -175,28 +198,28 @@ public class VerticalCandidateController: CandidateController {
selectedCandidateIndex = 0 selectedCandidateIndex = 0
} }
} }
public override func showNextPage() -> Bool { public override func showNextPage() -> Bool {
scrollPageByOne(true) scrollPageByOne(true)
} }
public override func showPreviousPage() -> Bool { public override func showPreviousPage() -> Bool {
scrollPageByOne(false) scrollPageByOne(false)
} }
public override func highlightNextCandidate() -> Bool { public override func highlightNextCandidate() -> Bool {
moveSelectionByOne(true) moveSelectionByOne(true)
} }
public override func highlightPreviousCandidate() -> Bool { public override func highlightPreviousCandidate() -> Bool {
moveSelectionByOne(false) moveSelectionByOne(false)
} }
public override func candidateIndexAtKeyLabelIndex(_ index: UInt) -> UInt { public override func candidateIndexAtKeyLabelIndex(_ index: UInt) -> UInt {
guard let delegate = delegate else { guard let delegate = delegate else {
return UInt.max return UInt.max
} }
let firstVisibleRow = tableView.row(at: scrollView.documentVisibleRect.origin) let firstVisibleRow = tableView.row(at: scrollView.documentVisibleRect.origin)
if firstVisibleRow != -1 { if firstVisibleRow != -1 {
let result = UInt(firstVisibleRow) + index let result = UInt(firstVisibleRow) + index
@ -204,15 +227,15 @@ public class VerticalCandidateController: CandidateController {
return result return result
} }
} }
return UInt.max return UInt.max
} }
public override var selectedCandidateIndex: UInt { public override var selectedCandidateIndex: UInt {
get { get {
let selectedRow = tableView.selectedRow let selectedRow = tableView.selectedRow
return selectedRow == -1 ? UInt.max : UInt(selectedRow) return selectedRow == -1 ? UInt.max : UInt(selectedRow)
} }
set { set {
guard let delegate = delegate else { guard let delegate = delegate else {
@ -222,7 +245,7 @@ public class VerticalCandidateController: CandidateController {
let selectedRow = tableView.selectedRow let selectedRow = tableView.selectedRow
let labelCount = keyLabels.count let labelCount = keyLabels.count
let itemCount = delegate.candidateCountForController(self) let itemCount = delegate.candidateCountForController(self)
if newIndex == UInt.max { if newIndex == UInt.max {
if itemCount == 0 { if itemCount == 0 {
tableView.deselectAll(self) tableView.deselectAll(self)
@ -230,16 +253,16 @@ public class VerticalCandidateController: CandidateController {
} }
newIndex = 0 newIndex = 0
} }
var lastVisibleRow = newValue var lastVisibleRow = newValue
if selectedRow != -1 && itemCount > 0 && itemCount > labelCount { if selectedRow != -1 && itemCount > 0 && itemCount > labelCount {
if newIndex > selectedRow && (Int(newIndex) - selectedRow) > 1 { if newIndex > selectedRow && (Int(newIndex) - selectedRow) > 1 {
lastVisibleRow = min(newIndex + UInt(labelCount) - 1, itemCount - 1) lastVisibleRow = min(newIndex + UInt(labelCount) - 1, itemCount - 1)
} }
// no need to handle the backward case: (newIndex < selectedRow && selectedRow - newIndex > 1) // no need to handle the backward case: (newIndex < selectedRow && selectedRow - newIndex > 1)
} }
if itemCount > labelCount { if itemCount > labelCount {
tableView.scrollRowToVisible(Int(lastVisibleRow)) tableView.scrollRowToVisible(Int(lastVisibleRow))
} }
@ -249,11 +272,11 @@ public class VerticalCandidateController: CandidateController {
} }
extension VerticalCandidateController: NSTableViewDataSource, NSTableViewDelegate { extension VerticalCandidateController: NSTableViewDataSource, NSTableViewDelegate {
public func numberOfRows(in tableView: NSTableView) -> Int { public func numberOfRows(in tableView: NSTableView) -> Int {
Int(delegate?.candidateCountForController(self) ?? 0) Int(delegate?.candidateCountForController(self) ?? 0)
} }
public func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? { public func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
guard let delegate = delegate else { guard let delegate = delegate else {
return nil return nil
@ -266,9 +289,8 @@ extension VerticalCandidateController: NSTableViewDataSource, NSTableViewDelegat
.font: candidateFont, .font: candidateFont,
.paragraphStyle: candidateTextParagraphStyle .paragraphStyle: candidateTextParagraphStyle
]) ])
// we do more work than what this method is expected to; normally not a good practice, but for the amount of data (9 to 10 rows max), we can afford the overhead // we do more work than what this method is expected to; normally not a good practice, but for the amount of data (9 to 10 rows max), we can afford the overhead
// expand the window width if text overflows // expand the window width if text overflows
let boundingRect = attrString.boundingRect(with: NSSize(width: 10240.0, height: 10240.0), options: .usesLineFragmentOrigin) let boundingRect = attrString.boundingRect(with: NSSize(width: 10240.0, height: 10240.0), options: .usesLineFragmentOrigin)
let textWidth = boundingRect.size.width + candidateTextPadding let textWidth = boundingRect.size.width + candidateTextPadding
@ -276,16 +298,16 @@ extension VerticalCandidateController: NSTableViewDataSource, NSTableViewDelegat
maxCandidateAttrStringWidth = textWidth maxCandidateAttrStringWidth = textWidth
layoutCandidateView() layoutCandidateView()
} }
// keep track of the highlighted index in the key label strip // keep track of the highlighted index in the key label strip
let count = UInt(keyLabels.count) let count = UInt(keyLabels.count)
let selectedRow = tableView.selectedRow let selectedRow = tableView.selectedRow
if selectedRow != -1 { if selectedRow != -1 {
var newHilightIndex = 0 var newHilightIndex = 0
if keyLabelStripView.highlightedIndex != -1 && if keyLabelStripView.highlightedIndex != -1 &&
(row >= selectedRow + Int(count) || (selectedRow > count && row <= selectedRow - Int(count))) { (row >= selectedRow + Int(count) || (selectedRow > count && row <= selectedRow - Int(count))) {
newHilightIndex = -1 newHilightIndex = -1
} else { } else {
let firstVisibleRow = tableView.row(at: scrollView.documentVisibleRect.origin) let firstVisibleRow = tableView.row(at: scrollView.documentVisibleRect.origin)
@ -294,16 +316,16 @@ extension VerticalCandidateController: NSTableViewDataSource, NSTableViewDelegat
newHilightIndex = -1 newHilightIndex = -1
} }
} }
if newHilightIndex != keyLabelStripView.highlightedIndex && newHilightIndex >= 0 { if newHilightIndex != keyLabelStripView.highlightedIndex && newHilightIndex >= 0 {
keyLabelStripView.highlightedIndex = UInt(newHilightIndex) keyLabelStripView.highlightedIndex = UInt(newHilightIndex)
keyLabelStripView.setNeedsDisplay(keyLabelStripView.frame) keyLabelStripView.setNeedsDisplay(keyLabelStripView.frame)
} }
} }
return attrString return attrString
} }
public func tableViewSelectionDidChange(_ notification: Notification) { public func tableViewSelectionDidChange(_ notification: Notification) {
let selectedRow = tableView.selectedRow let selectedRow = tableView.selectedRow
if selectedRow != -1 { if selectedRow != -1 {
@ -311,7 +333,7 @@ extension VerticalCandidateController: NSTableViewDataSource, NSTableViewDelegat
let firstVisibleRow = tableView.row(at: scrollView.documentVisibleRect.origin) let firstVisibleRow = tableView.row(at: scrollView.documentVisibleRect.origin)
keyLabelStripView.highlightedIndex = UInt(selectedRow - firstVisibleRow) keyLabelStripView.highlightedIndex = UInt(selectedRow - firstVisibleRow)
keyLabelStripView.setNeedsDisplay(keyLabelStripView.frame) keyLabelStripView.setNeedsDisplay(keyLabelStripView.frame)
// fix a subtle OS X "bug" that, since we force the scroller to appear, // fix a subtle OS X "bug" that, since we force the scroller to appear,
// scrolling sometimes shows a temporarily "broken" scroll bar // scrolling sometimes shows a temporarily "broken" scroll bar
// (but quickly disappears) // (but quickly disappears)
@ -320,14 +342,14 @@ extension VerticalCandidateController: NSTableViewDataSource, NSTableViewDelegat
} }
} }
} }
@objc func rowDoubleClicked(_ sender: Any) { @objc func rowDoubleClicked(_ sender: Any) {
let clickedRow = tableView.clickedRow let clickedRow = tableView.clickedRow
if clickedRow != -1 { if clickedRow != -1 {
delegate?.candidateController(self, didSelectCandidateAtIndex: UInt(clickedRow)) delegate?.candidateController(self, didSelectCandidateAtIndex: UInt(clickedRow))
} }
} }
func scrollPageByOne(_ forward: Bool) -> Bool { func scrollPageByOne(_ forward: Bool) -> Bool {
guard let delegate = delegate else { guard let delegate = delegate else {
return false return false
@ -340,7 +362,7 @@ extension VerticalCandidateController: NSTableViewDataSource, NSTableViewDelegat
if itemCount <= labelCount { if itemCount <= labelCount {
return false return false
} }
var newIndex = selectedCandidateIndex var newIndex = selectedCandidateIndex
if forward { if forward {
if newIndex == itemCount - 1 { if newIndex == itemCount - 1 {
@ -351,7 +373,7 @@ extension VerticalCandidateController: NSTableViewDataSource, NSTableViewDelegat
if newIndex == 0 { if newIndex == 0 {
return false return false
} }
if newIndex < labelCount { if newIndex < labelCount {
newIndex = 0 newIndex = 0
} else { } else {
@ -361,7 +383,7 @@ extension VerticalCandidateController: NSTableViewDataSource, NSTableViewDelegat
selectedCandidateIndex = newIndex selectedCandidateIndex = newIndex
return true return true
} }
private func moveSelectionByOne(_ forward: Bool) -> Bool { private func moveSelectionByOne(_ forward: Bool) -> Bool {
guard let delegate = delegate else { guard let delegate = delegate else {
return false return false
@ -385,13 +407,13 @@ extension VerticalCandidateController: NSTableViewDataSource, NSTableViewDelegat
selectedCandidateIndex = newIndex selectedCandidateIndex = newIndex
return true return true
} }
private func layoutCandidateView() { private func layoutCandidateView() {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) { [self] in DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) { [self] in
doLayoutCandidateView() doLayoutCandidateView()
} }
} }
private func doLayoutCandidateView() { private func doLayoutCandidateView() {
guard let delegate = delegate else { guard let delegate = delegate else {
return return
@ -400,13 +422,13 @@ extension VerticalCandidateController: NSTableViewDataSource, NSTableViewDelegat
if 0 == count { if 0 == count {
return return
} }
let candidateFontSize = ceil(candidateFont.pointSize) let candidateFontSize = ceil(candidateFont.pointSize)
let keyLabelFontSize = ceil(keyLabelFont.pointSize) let keyLabelFontSize = ceil(keyLabelFont.pointSize)
let fontSize = max(candidateFontSize, keyLabelFontSize) let fontSize = max(candidateFontSize, keyLabelFontSize)
let controlSize: NSControl.ControlSize = fontSize > 36.0 ? .regular : .small let controlSize: NSControl.ControlSize = fontSize > 36.0 ? .regular : .small
var keyLabelCount = UInt(keyLabels.count) var keyLabelCount = UInt(keyLabels.count)
var scrollerWidth: CGFloat = 0.0 var scrollerWidth: CGFloat = 0.0
if count <= keyLabelCount { if count <= keyLabelCount {
@ -416,40 +438,40 @@ extension VerticalCandidateController: NSTableViewDataSource, NSTableViewDelegat
scrollView.hasVerticalScroller = true scrollView.hasVerticalScroller = true
let verticalScroller = scrollView.verticalScroller let verticalScroller = scrollView.verticalScroller
verticalScroller?.controlSize = controlSize verticalScroller?.controlSize = controlSize
verticalScroller?.scrollerStyle = .legacy verticalScroller?.scrollerStyle = .overlay // Aesthetics
scrollerWidth = NSScroller.scrollerWidth(for: controlSize, scrollerStyle: .legacy) scrollerWidth = NSScroller.scrollerWidth(for: controlSize, scrollerStyle: .overlay) // Aesthetics
} }
keyLabelStripView.keyLabelFont = keyLabelFont keyLabelStripView.keyLabelFont = keyLabelFont
keyLabelStripView.keyLabels = Array(keyLabels[0..<Int(keyLabelCount)]) keyLabelStripView.keyLabels = Array(keyLabels[0..<Int(keyLabelCount)])
keyLabelStripView.labelOffsetY = (keyLabelFontSize >= candidateFontSize) ? 0.0 : floor((candidateFontSize - keyLabelFontSize) / 2.0) keyLabelStripView.labelOffsetY = (keyLabelFontSize >= candidateFontSize) ? 0.0 : floor((candidateFontSize - keyLabelFontSize) / 2.0)
let rowHeight = ceil(fontSize * 1.25) let rowHeight = ceil(fontSize * 1.25)
tableView.rowHeight = rowHeight tableView.rowHeight = rowHeight
var maxKeyLabelWidth = keyLabelFontSize var maxKeyLabelWidth = keyLabelFontSize
let textAttr: [NSAttributedString.Key: AnyObject] = [.font: keyLabelFont] let textAttr: [NSAttributedString.Key: AnyObject] = [.font: keyLabelFont]
let boundingBox = NSSize(width: 1600.0, height: 1600.0) let boundingBox = NSSize(width: 1600.0, height: 1600.0)
for label in keyLabels { for label in keyLabels {
let rect = (label as NSString).boundingRect(with: boundingBox, options: .usesLineFragmentOrigin, attributes: textAttr) let rect = (label as NSString).boundingRect(with: boundingBox, options: .usesLineFragmentOrigin, attributes: textAttr)
maxKeyLabelWidth = max(rect.size.width, maxKeyLabelWidth) maxKeyLabelWidth = max(rect.size.width, maxKeyLabelWidth)
} }
let rowSpacing = tableView.intercellSpacing.height let rowSpacing = tableView.intercellSpacing.height
let stripWidth = ceil(maxKeyLabelWidth * 1.20) let stripWidth = ceil(maxKeyLabelWidth * 1.20)
let tableViewStartWidth = ceil(maxCandidateAttrStringWidth + scrollerWidth) let tableViewStartWidth = ceil(maxCandidateAttrStringWidth + scrollerWidth)
let windowWidth = stripWidth + 1.0 + tableViewStartWidth let windowWidth = stripWidth + 0.0 + tableViewStartWidth // Compensation to the removal of the border line between the index labels and the candidate phrase list
let windowHeight = CGFloat(keyLabelCount) * (rowHeight + rowSpacing) let windowHeight = CGFloat(keyLabelCount) * (rowHeight + rowSpacing)
var frameRect = self.window?.frame ?? NSRect.zero var frameRect = self.window?.frame ?? NSRect.zero
let topLeftPoint = NSMakePoint(frameRect.origin.x, frameRect.origin.y + frameRect.size.height) let topLeftPoint = NSMakePoint(frameRect.origin.x, frameRect.origin.y + frameRect.size.height)
frameRect.size = NSMakeSize(windowWidth, windowHeight) frameRect.size = NSMakeSize(windowWidth, windowHeight)
frameRect.origin = NSMakePoint(topLeftPoint.x, topLeftPoint.y - frameRect.size.height) frameRect.origin = NSMakePoint(topLeftPoint.x, topLeftPoint.y - frameRect.size.height)
keyLabelStripView.frame = NSRect(x: 0.0, y: 0.0, width: stripWidth, height: windowHeight) keyLabelStripView.frame = NSRect(x: 0.0, y: 0.0, width: stripWidth, height: windowHeight)
scrollView.frame = NSRect(x: stripWidth + 1.0, y: 0.0, width: tableViewStartWidth, height: windowHeight) scrollView.frame = NSRect(x: stripWidth + 0.0, y: 0.0, width: tableViewStartWidth, height: windowHeight) // Remove the border line between the index labels and the candidate phrase list
self.window?.setFrame(frameRect, display: false) self.window?.setFrame(frameRect, display: false)
} }
} }