From 7a1ea2371910c626659445c3fbc0bee3099a9a27 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Mon, 7 Feb 2022 00:18:12 +0800 Subject: [PATCH] Reset // Beautifying Candidate Window. --- .../HorizontalCandidateController.swift | 141 ++-- .../VerticalCandidateController.swift | 690 ++++++++---------- 2 files changed, 384 insertions(+), 447 deletions(-) diff --git a/Source/UI/CandidateUI/HorizontalCandidateController.swift b/Source/UI/CandidateUI/HorizontalCandidateController.swift index 5aa6729a..c5e53d59 100644 --- a/Source/UI/CandidateUI/HorizontalCandidateController.swift +++ b/Source/UI/CandidateUI/HorizontalCandidateController.swift @@ -1,3 +1,10 @@ +/* + * HorizontalCandidateController.swift + * + * Copyright 2021-2022 vChewing Project (3-Clause BSD License). + * Derived from 2011-2022 OpenVanilla Project (MIT License). + * Some rights reserved. See "LICENSE.TXT" for details. + */ import Cocoa @@ -8,31 +15,17 @@ fileprivate class HorizontalCandidateView: NSView { private var keyLabels: [String] = [] private var displayedCandidates: [String] = [] + private var dispCandidatesWithLabels: [String] = [] private var keyLabelHeight: CGFloat = 0 + private var keyLabelWidth: CGFloat = 0 private var candidateTextHeight: CGFloat = 0 private var cellPadding: CGFloat = 0 private var keyLabelAttrDict: [NSAttributedString.Key: AnyObject] = [:] private var candidateAttrDict: [NSAttributedString.Key: AnyObject] = [:] + private var candidateWithLabelAttrDict: [NSAttributedString.Key: AnyObject] = [:] private var elementWidths: [CGFloat] = [] private var trackingHighlightedIndex: UInt = UInt.max - private let tooltipPadding: CGFloat = 2.0 - private var tooltipSize: NSSize = NSSize.zero - - override var toolTip: String? { - didSet { - if let toolTip = toolTip, !toolTip.isEmpty { - let baseSize = NSSize(width: 10240.0, height: 10240.0) - var tooltipRect = (toolTip as NSString).boundingRect(with: baseSize, options: .usesLineFragmentOrigin, attributes: keyLabelAttrDict) - tooltipRect.size.height += tooltipPadding * 2 - tooltipRect.size.width += tooltipPadding * 2 - self.tooltipSize = tooltipRect.size - } else { - self.tooltipSize = NSSize.zero - } - } - } - override var isFlipped: Bool { true } @@ -43,59 +36,63 @@ fileprivate class HorizontalCandidateView: NSView { if !elementWidths.isEmpty { result.width = elementWidths.reduce(0, +) result.width += CGFloat(elementWidths.count) - result.height = keyLabelHeight + candidateTextHeight + 1.0 + result.height = candidateTextHeight + cellPadding } - - result.height += tooltipSize.height - result.width = max(tooltipSize.width, result.width) return result } + @objc(setKeyLabels:displayedCandidates:) func set(keyLabels labels: [String], displayedCandidates candidates: [String]) { let count = min(labels.count, candidates.count) keyLabels = Array(labels[0.. 1 { var buttonRect = nextPageButton.frame - var spacing: CGFloat = 0.0 + let spacing:CGFloat = 0.0 - if newSize.height < 40.0 { - buttonRect.size.height = floor(newSize.height / 2) - } else { - buttonRect.size.height = 20.0 - } - - if newSize.height >= 60.0 { - spacing = ceil(newSize.height * 0.1) - } + buttonRect.size.height = floor(newSize.height / 2) let buttonOriginY = (newSize.height - (buttonRect.size.height * 2.0 + spacing)) / 2.0 buttonRect.origin = NSPoint(x: newSize.width + 8.0, y: buttonOriginY) diff --git a/Source/UI/CandidateUI/VerticalCandidateController.swift b/Source/UI/CandidateUI/VerticalCandidateController.swift index 28c224a9..91db7282 100644 --- a/Source/UI/CandidateUI/VerticalCandidateController.swift +++ b/Source/UI/CandidateUI/VerticalCandidateController.swift @@ -1,86 +1,195 @@ +/* + * VerticalCandidateController.swift + * + * Copyright 2021-2022 vChewing Project (3-Clause BSD License). + * Derived from 2011-2022 OpenVanilla Project (MIT License). + * Some rights reserved. See "LICENSE.TXT" for details. + */ import Cocoa -fileprivate class VerticalKeyLabelStripView: NSView { - var keyLabelFont: NSFont = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) - var labelOffsetY: CGFloat = 0 - var keyLabels: [String] = [] - var highlightedIndex: UInt = UInt.max +fileprivate class VerticalCandidateView: NSView { + var highlightedIndex: UInt = 0 + var action: Selector? + weak var target: AnyObject? + + private var keyLabels: [String] = [] + private var displayedCandidates: [String] = [] + private var dispCandidatesWithLabels: [String] = [] + private var keyLabelHeight: CGFloat = 0 + private var keyLabelWidth: CGFloat = 0 + private var candidateTextHeight: CGFloat = 0 + private var cellPadding: CGFloat = 0 + private var keyLabelAttrDict: [NSAttributedString.Key: AnyObject] = [:] + private var candidateAttrDict: [NSAttributedString.Key: AnyObject] = [:] + private var candidateWithLabelAttrDict: [NSAttributedString.Key: AnyObject] = [:] + private var windowWidth: CGFloat = 0 + private var elementWidths: [CGFloat] = [] + private var elementHeights: [CGFloat] = [] + private var trackingHighlightedIndex: UInt = UInt.max override var isFlipped: Bool { true } - override func draw(_ dirtyRect: NSRect) { - let bounds = self.bounds - NSColor.white.setFill() - NSBezierPath.fill(bounds) + var sizeForView: NSSize { + var result = NSSize.zero - let count = UInt(keyLabels.count) - if count == 0 { - return + if !elementWidths.isEmpty { + result.width = windowWidth + result.height = elementHeights.reduce(0, +) } - 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) + return result + } + @objc(setKeyLabels:displayedCandidates:) + func set(keyLabels labels: [String], displayedCandidates candidates: [String]) { + let count = min(labels.count, candidates.count) + keyLabels = Array(labels[0..= count { - cellRect.size.height += 1.0 + if #available(macOS 10.14, *) { + NSColor.separatorColor.setStroke() + } else { + NSColor.darkGray.setStroke() + } + + NSBezierPath.strokeLine(from: NSPoint(x: bounds.size.width, y: 0.0), to: NSPoint(x: bounds.size.width, y: bounds.size.height)) + + var accuHeight: CGFloat = 0 + for index in 0.. NSRect { - var scrollRect = newVisible - let rowHeightPlusSpacing = rowHeight + intercellSpacing.height - scrollRect.origin.y = (scrollRect.origin.y / rowHeightPlusSpacing) * rowHeightPlusSpacing - return scrollRect + private func findHitIndex(event: NSEvent) -> UInt? { + let location = convert(event.locationInWindow, to: nil) + if !NSPointInRect(location, self.bounds) { + return nil + } + var accuHeight: CGFloat = 0.0 + for index in 0..= accuHeight && location.y <= accuHeight + currentHeight { + return UInt(index) + } + accuHeight += currentHeight + } + return nil + } -} -private let kCandidateTextPadding: CGFloat = 24.0 -private let kCandidateTextLeftMargin: CGFloat = 8.0 -private let kCandidateTextPaddingWithMandatedTableViewPadding: CGFloat = 18.0 -private let kCandidateTextLeftMarginWithMandatedTableViewPadding: CGFloat = 0.0 + override func mouseUp(with event: NSEvent) { + trackingHighlightedIndex = highlightedIndex + guard let newIndex = findHitIndex(event: event) else { + return + } + highlightedIndex = newIndex + self.setNeedsDisplay(self.bounds) + } -private class BackgroundView: NSView { - override func draw(_ dirtyRect: NSRect) { - NSColor.windowBackgroundColor.setFill() - NSBezierPath.fill(self.bounds) + override func mouseDown(with event: NSEvent) { + guard let newIndex = findHitIndex(event: event) else { + return + } + var triggerAction = false + if newIndex == highlightedIndex { + triggerAction = true + } else { + highlightedIndex = trackingHighlightedIndex + } + + trackingHighlightedIndex = 0 + self.setNeedsDisplay(self.bounds) + if triggerAction { + if let target = target as? NSObject, let action = action { + target.perform(action, with: self) + } + } } } @objc(VTVerticalCandidateController) public class VerticalCandidateController: CandidateController { - private var keyLabelStripView: VerticalKeyLabelStripView - private var scrollView: NSScrollView - private var tableView: NSTableView - private var candidateTextParagraphStyle: NSMutableParagraphStyle - private var candidateTextPadding: CGFloat = kCandidateTextPadding - private var candidateTextLeftMargin: CGFloat = kCandidateTextLeftMargin - private var maxCandidateAttrStringWidth: CGFloat = 0 - private let tooltipPadding: CGFloat = 2.0 - private var tooltipView: NSTextField + private var candidateView: VerticalCandidateView + private var prevPageButton: NSButton + private var nextPageButton: NSButton + private var currentPage: UInt = 0 public init() { var contentRect = NSRect(x: 128.0, y: 128.0, width: 0.0, height: 0.0) @@ -88,62 +197,45 @@ public class VerticalCandidateController: CandidateController { let panel = NSPanel(contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false) panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel) + 1) panel.hasShadow = true - panel.contentView = BackgroundView() - - tooltipView = NSTextField(frame: NSRect.zero) - tooltipView.isEditable = false - tooltipView.isSelectable = false - tooltipView.isBezeled = false - tooltipView.drawsBackground = true - tooltipView.lineBreakMode = .byTruncatingTail - + panel.isOpaque = false + panel.backgroundColor = NSColor.clear // Transparentify everything outside of the candidate list panel + contentRect.origin = NSPoint.zero - var stripRect = contentRect - stripRect.size.width = 10.0 - keyLabelStripView = VerticalKeyLabelStripView(frame: stripRect) - panel.contentView?.addSubview(keyLabelStripView) - - var scrollViewRect = contentRect - scrollViewRect.origin.x = stripRect.size.width - scrollViewRect.size.width -= stripRect.size.width - scrollView = NSScrollView(frame: scrollViewRect) - scrollView.verticalScrollElasticity = .none - - tableView = NSTableView(frame: contentRect) - let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "candidate")) - column.dataCell = NSTextFieldCell() - column.isEditable = false - - candidateTextPadding = kCandidateTextPadding - candidateTextLeftMargin = kCandidateTextLeftMargin - - tableView.addTableColumn(column) - tableView.intercellSpacing = NSSize(width: 0.0, height: 1.0) - tableView.headerView = nil - tableView.allowsMultipleSelection = false - tableView.allowsEmptySelection = false - - if #available(macOS 10.16, *) { - tableView.style = .fullWidth - candidateTextPadding = kCandidateTextPaddingWithMandatedTableViewPadding - candidateTextLeftMargin = kCandidateTextLeftMarginWithMandatedTableViewPadding - } - - scrollView.documentView = tableView - panel.contentView?.addSubview(scrollView) - - let paraStyle = NSMutableParagraphStyle() - paraStyle.setParagraphStyle(NSParagraphStyle.default) - paraStyle.firstLineHeadIndent = candidateTextLeftMargin - paraStyle.lineBreakMode = .byClipping - - candidateTextParagraphStyle = paraStyle + candidateView = VerticalCandidateView(frame: contentRect) + panel.contentView?.addSubview(candidateView) + + contentRect.size = NSSize(width: 20.0, height: 15.0) // Reduce the button width + nextPageButton = NSButton(frame: contentRect) + nextPageButton.setButtonType(.momentaryLight) + nextPageButton.bezelStyle = .shadowlessSquare + nextPageButton.wantsLayer = true + nextPageButton.layer?.masksToBounds = true + nextPageButton.layer?.borderColor = NSColor.clear.cgColor // Attempt to remove the system default layer border color - step 1 + nextPageButton.layer?.borderWidth = 0.0 // Attempt to remove the system default layer border color - step 2 + nextPageButton.layer?.backgroundColor = NSColor.black.cgColor // Button Background Color. Otherwise the button will be half-transparent in macOS Monterey Dark Mode. + nextPageButton.attributedTitle = NSMutableAttributedString(string: "➡︎") // Next Page Arrow + prevPageButton = NSButton(frame: contentRect) + prevPageButton.setButtonType(.momentaryLight) + prevPageButton.bezelStyle = .shadowlessSquare + prevPageButton.wantsLayer = true + prevPageButton.layer?.masksToBounds = true + prevPageButton.layer?.borderColor = NSColor.clear.cgColor // Attempt to remove the system default layer border color - step 1 + prevPageButton.layer?.borderWidth = 0.0 // Attempt to remove the system default layer border color - step 2 + prevPageButton.layer?.backgroundColor = NSColor.black.cgColor // Button Background Color. Otherwise the button will be half-transparent in macOS Monterey Dark Mode. + prevPageButton.attributedTitle = NSMutableAttributedString(string: "⬅︎") // Previous Page Arrow + panel.contentView?.addSubview(nextPageButton) + panel.contentView?.addSubview(prevPageButton) super.init(window: panel) - tableView.dataSource = self - tableView.delegate = self - tableView.doubleAction = #selector(rowDoubleClicked(_:)) - tableView.target = self + + candidateView.target = self + candidateView.action = #selector(candidateViewMouseDidClick(_:)) + + nextPageButton.target = self + nextPageButton.action = #selector(pageButtonAction(_:)) + + prevPageButton.target = self + prevPageButton.action = #selector(pageButtonAction(_:)) } required init?(coder: NSCoder) { @@ -151,28 +243,66 @@ public class VerticalCandidateController: CandidateController { } public override func reloadData() { - maxCandidateAttrStringWidth = ceil(candidateFont.pointSize * 2.0 + candidateTextPadding) - tableView.reloadData() + candidateView.highlightedIndex = 0 + currentPage = 0 layoutCandidateView() - if delegate?.candidateCountForController(self) ?? 0 > 0 { - selectedCandidateIndex = 0 - } } public override func showNextPage() -> Bool { - scrollPageByOne(true) + guard delegate != nil else { + return false + } + + if currentPage + 1 >= pageCount { + return false + } + + currentPage += 1 + candidateView.highlightedIndex = 0 + layoutCandidateView() + return true } public override func showPreviousPage() -> Bool { - scrollPageByOne(false) + guard delegate != nil else { + return false + } + + if currentPage == 0 { + return false + } + + currentPage -= 1 + candidateView.highlightedIndex = 0 + layoutCandidateView() + return true } public override func highlightNextCandidate() -> Bool { - moveSelectionByOne(true) + guard let delegate = delegate else { + return false + } + + let currentIndex = selectedCandidateIndex + if currentIndex + 1 >= delegate.candidateCountForController(self) { + return false + } + selectedCandidateIndex = currentIndex + 1 + return true } public override func highlightPreviousCandidate() -> Bool { - moveSelectionByOne(false) + guard delegate != nil else { + return false + } + + let currentIndex = selectedCandidateIndex + if currentIndex == 0 { + return false + } + + selectedCandidateIndex = currentIndex - 1 + return true } public override func candidateIndexAtKeyLabelIndex(_ index: UInt) -> UInt { @@ -180,285 +310,103 @@ public class VerticalCandidateController: CandidateController { return UInt.max } - let firstVisibleRow = tableView.row(at: scrollView.documentVisibleRect.origin) - if firstVisibleRow != -1 { - let result = UInt(firstVisibleRow) + index - if result < delegate.candidateCountForController(self) { - return result - } - } - - return UInt.max + let result = currentPage * UInt(keyLabels.count) + index + return result < delegate.candidateCountForController(self) ? result : UInt.max } public override var selectedCandidateIndex: UInt { get { - let selectedRow = tableView.selectedRow - return selectedRow == -1 ? UInt.max : UInt(selectedRow) - + currentPage * UInt(keyLabels.count) + candidateView.highlightedIndex } set { guard let delegate = delegate else { return } - var newIndex = newValue - let selectedRow = tableView.selectedRow - let labelCount = keyLabels.count - let itemCount = delegate.candidateCountForController(self) - - if newIndex == UInt.max { - if itemCount == 0 { - tableView.deselectAll(self) - return - } - newIndex = 0 + let keyLabelCount = UInt(keyLabels.count) + if newValue < delegate.candidateCountForController(self) { + currentPage = newValue / keyLabelCount + candidateView.highlightedIndex = newValue % keyLabelCount + layoutCandidateView() } - - var lastVisibleRow = newValue - - if selectedRow != -1 && itemCount > 0 && itemCount > labelCount { - if newIndex > selectedRow && (Int(newIndex) - selectedRow) > 1 { - lastVisibleRow = min(newIndex + UInt(labelCount) - 1, itemCount - 1) - } - // no need to handle the backward case: (newIndex < selectedRow && selectedRow - newIndex > 1) - } - - if itemCount > labelCount { - tableView.scrollRowToVisible(Int(lastVisibleRow)) - } - tableView.selectRowIndexes(IndexSet(integer: Int(newIndex)), byExtendingSelection: false) } } } -extension VerticalCandidateController: NSTableViewDataSource, NSTableViewDelegate { +extension VerticalCandidateController { - public func numberOfRows(in tableView: NSTableView) -> Int { - Int(delegate?.candidateCountForController(self) ?? 0) - } - - public func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? { + private var pageCount: UInt { guard let delegate = delegate else { - return nil + return 0 } - var candidate = "" - if row < delegate.candidateCountForController(self) { - candidate = delegate.candidateController(self, candidateAtIndex: UInt(row)) - } - let attrString = NSAttributedString(string: candidate, attributes: [ - .font: candidateFont, - .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 - - // expand the window width if text overflows - let boundingRect = attrString.boundingRect(with: NSSize(width: 10240.0, height: 10240.0), options: .usesLineFragmentOrigin) - let textWidth = boundingRect.size.width + candidateTextPadding - if textWidth > maxCandidateAttrStringWidth { - maxCandidateAttrStringWidth = textWidth - layoutCandidateView() - } - - // keep track of the highlighted index in the key label strip - let count = UInt(keyLabels.count) - let selectedRow = tableView.selectedRow - - if selectedRow != -1 { - var newHilightIndex = 0 - - if keyLabelStripView.highlightedIndex != -1 && - (row >= selectedRow + Int(count) || (selectedRow > count && row <= selectedRow - Int(count))) { - newHilightIndex = -1 - } else { - let firstVisibleRow = tableView.row(at: scrollView.documentVisibleRect.origin) - newHilightIndex = selectedRow - firstVisibleRow - if newHilightIndex < -1 { - newHilightIndex = -1 - } - } - - if newHilightIndex != keyLabelStripView.highlightedIndex && newHilightIndex >= 0 { - keyLabelStripView.highlightedIndex = UInt(newHilightIndex) - keyLabelStripView.setNeedsDisplay(keyLabelStripView.frame) - } - - } - return attrString - } - - public func tableViewSelectionDidChange(_ notification: Notification) { - let selectedRow = tableView.selectedRow - if selectedRow != -1 { - // keep track of the highlighted index in the key label strip - let firstVisibleRow = tableView.row(at: scrollView.documentVisibleRect.origin) - // firstVisibleRow cannot be larger than selectedRow. - if selectedRow >= firstVisibleRow { - keyLabelStripView.highlightedIndex = UInt(selectedRow - firstVisibleRow) - } else { - keyLabelStripView.highlightedIndex = UInt.max - } - - keyLabelStripView.setNeedsDisplay(keyLabelStripView.frame) - - // fix a subtle OS X "bug" that, since we force the scroller to appear, - // scrolling sometimes shows a temporarily "broken" scroll bar - // (but quickly disappears) - if scrollView.hasVerticalScroller { - scrollView.verticalScroller?.setNeedsDisplay() - } - } - } - - @objc func rowDoubleClicked(_ sender: Any) { - let clickedRow = tableView.clickedRow - if clickedRow != -1 { - delegate?.candidateController(self, didSelectCandidateAtIndex: UInt(clickedRow)) - } - } - - func scrollPageByOne(_ forward: Bool) -> Bool { - guard let delegate = delegate else { - return false - } - let labelCount = UInt(keyLabels.count) - let itemCount = delegate.candidateCountForController(self) - if 0 == itemCount { - return false - } - if itemCount <= labelCount { - return false - } - - var newIndex = selectedCandidateIndex - if forward { - if newIndex >= itemCount - 1 { - return false - } - newIndex = min(newIndex + labelCount, itemCount - 1) - } else { - if newIndex == 0 { - return false - } - - if newIndex < labelCount { - newIndex = 0 - } else { - newIndex -= labelCount - } - } - selectedCandidateIndex = newIndex - return true - } - - private func moveSelectionByOne(_ forward: Bool) -> Bool { - guard let delegate = delegate else { - return false - } - let itemCount = delegate.candidateCountForController(self) - if 0 == itemCount { - return false - } - var newIndex = selectedCandidateIndex - if newIndex == UInt.max { - return false - } - - if forward { - if newIndex >= itemCount - 1 { - return false - } - newIndex += 1 - } else { - if 0 == newIndex { - return false - } - newIndex -= 1 - } - selectedCandidateIndex = newIndex - return true + let totalCount = delegate.candidateCountForController(self) + let keyLabelCount = UInt(keyLabels.count) + return totalCount / keyLabelCount + ((totalCount % keyLabelCount) != 0 ? 1 : 0) } private func layoutCandidateView() { - DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) { [self] in - doLayoutCandidateView() - } - } - - private func doLayoutCandidateView() { guard let delegate = delegate else { return } + + candidateView.set(keyLabelFont: keyLabelFont, candidateFont: candidateFont) + var candidates = [String]() let count = delegate.candidateCountForController(self) - if 0 == count { + let keyLabelCount = UInt(keyLabels.count) + + let begin = currentPage * keyLabelCount + for index in begin.. 1 { + var buttonRect = nextPageButton.frame + let spacing:CGFloat = 0.0 + + // buttonRect.size.height = floor(candidateTextHeight + cellPadding / 2) + + let buttonOriginY = (newSize.height - (buttonRect.size.height * 2.0 + spacing)) // / 2.0 + buttonRect.origin = NSPoint(x: newSize.width + 8.0, y: buttonOriginY) + nextPageButton.frame = buttonRect + + buttonRect.origin = NSPoint(x: newSize.width + 8.0, y: buttonOriginY + buttonRect.size.height + spacing) + prevPageButton.frame = buttonRect + + newSize.width += 52.0 + nextPageButton.isHidden = false + prevPageButton.isHidden = false + } else { + nextPageButton.isHidden = true + prevPageButton.isHidden = true + } + + frameRect = window?.frame ?? NSRect.zero + + let topLeftPoint = NSMakePoint(frameRect.origin.x, frameRect.origin.y + frameRect.size.height) + frameRect.size = newSize + frameRect.origin = NSMakePoint(topLeftPoint.x, topLeftPoint.y - frameRect.size.height) + self.window?.setFrame(frameRect, display: false) + candidateView.setNeedsDisplay(candidateView.bounds) + } + + @objc fileprivate func pageButtonAction(_ sender: Any) { + guard let sender = sender as? NSButton else { return } - - - var tooltipHeight: CGFloat = 0 - var tooltipWidth: CGFloat = 0 - - if !tooltip.isEmpty { - tooltipView.stringValue = tooltip - let size = tooltipView.intrinsicContentSize - tooltipWidth = size.width + tooltipPadding * 2 - tooltipHeight = size.height + tooltipPadding * 2 - self.window?.contentView?.addSubview(tooltipView) - } else { - tooltipView.removeFromSuperview() + if sender == nextPageButton { + _ = showNextPage() + } else if sender == prevPageButton { + _ = showPreviousPage() } - - let candidateFontSize = ceil(candidateFont.pointSize) - let keyLabelFontSize = ceil(keyLabelFont.pointSize) - let fontSize = max(candidateFontSize, keyLabelFontSize) - - let controlSize: NSControl.ControlSize = fontSize > 36.0 ? .regular : .small - - var keyLabelCount = UInt(keyLabels.count) - var scrollerWidth: CGFloat = 0.0 - if count <= keyLabelCount { - keyLabelCount = count - scrollView.hasVerticalScroller = false - } else { - scrollView.hasVerticalScroller = true - let verticalScroller = scrollView.verticalScroller - verticalScroller?.controlSize = controlSize - verticalScroller?.scrollerStyle = .legacy - scrollerWidth = NSScroller.scrollerWidth(for: controlSize, scrollerStyle: .legacy) - } - - keyLabelStripView.keyLabelFont = keyLabelFont - let actualKeyLabels = keyLabels[0..= candidateFontSize) ? 0.0 : floor((candidateFontSize - keyLabelFontSize) / 2.0) - - let rowHeight = ceil(fontSize * 1.25) - tableView.rowHeight = rowHeight - - var maxKeyLabelWidth = keyLabelFontSize - let textAttr: [NSAttributedString.Key: AnyObject] = [.font: keyLabelFont] - let boundingBox = NSSize(width: 1600.0, height: 1600.0) - - for label in actualKeyLabels { - let rect = (label as NSString).boundingRect(with: boundingBox, options: .usesLineFragmentOrigin, attributes: textAttr) - maxKeyLabelWidth = max(rect.size.width, maxKeyLabelWidth) - } - - let rowSpacing = tableView.intercellSpacing.height - let stripWidth = ceil(maxKeyLabelWidth * 1.20) - let tableViewStartWidth = ceil(maxCandidateAttrStringWidth + scrollerWidth) - let windowWidth = max(stripWidth + 1.0 + tableViewStartWidth, tooltipWidth) - let windowHeight = CGFloat(keyLabelCount) * (rowHeight + rowSpacing) + tooltipHeight - - var frameRect = self.window?.frame ?? NSRect.zero - let topLeftPoint = NSMakePoint(frameRect.origin.x, frameRect.origin.y + frameRect.size.height) - - frameRect.size = NSMakeSize(windowWidth, windowHeight) - frameRect.origin = NSMakePoint(topLeftPoint.x, topLeftPoint.y - frameRect.size.height) - - keyLabelStripView.frame = NSRect(x: 0.0, y: 0, width: stripWidth, height: windowHeight - tooltipHeight) - scrollView.frame = NSRect(x: stripWidth + 1.0, y: 0, width: (windowWidth - stripWidth - 1), height: windowHeight - tooltipHeight) - tooltipView.frame = NSRect(x: tooltipPadding, y: windowHeight - tooltipHeight + tooltipPadding, width: windowWidth, height: tooltipHeight) - self.window?.setFrame(frameRect, display: false) } + + @objc fileprivate func candidateViewMouseDidClick(_ sender: Any) { + delegate?.candidateController(self, didSelectCandidateAtIndex: selectedCandidateIndex) + } + }