From c4d7007f2962958fbee999cb471982c9f94f935f Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Fri, 14 Jan 2022 22:52:32 +0800 Subject: [PATCH] Shiki: Swiftify // Voltaire UI Revamp - Horizontal --- .../HorizontalCandidateController.swift | 213 ++++++++++-------- 1 file changed, 115 insertions(+), 98 deletions(-) diff --git a/Source/CandidateUI/HorizontalCandidateController.swift b/Source/CandidateUI/HorizontalCandidateController.swift index 74bd033d..61611897 100644 --- a/Source/CandidateUI/HorizontalCandidateController.swift +++ b/Source/CandidateUI/HorizontalCandidateController.swift @@ -1,11 +1,16 @@ // // HorizontalCandidateController.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: -// Mengjuei Hsieh (@mjhsieh) -// Weizhong Yang (@zonble) +// Lukhnos Liu (@lukhnos) @ OpenVanilla // Original Developer +// Weizhong Yang (@zonble) @ OpenVanilla // Rewriter to Swift +// Shiki Suen (ShikiSuen) @ vChewing // Beautification (objC) + // // Based on the Syrup Project and the Formosana Library // by Lukhnos Liu (@lukhnos). @@ -38,112 +43,122 @@ fileprivate class HorizontalCandidateView: 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 elementWidths: [CGFloat] = [] private var trackingHighlightedIndex: UInt = UInt.max - + override var isFlipped: Bool { true } - + var sizeForView: NSSize { var result = NSSize.zero - + if !elementWidths.isEmpty { result.width = elementWidths.reduce(0, +) result.width += CGFloat(elementWidths.count) - result.height = keyLabelHeight + candidateTextHeight + 1.0 + // result.height = keyLabelHeight + candidateTextHeight + 1.0 + result.height = candidateTextHeight + cellPadding; } return result } - + @objc (setKeyLabels:displayedCandidates:) func set(keyLabels labels: [String], displayedCandidates candidates: [String]) { let count = min(labels.count, candidates.count) keyLabels = Array(labels[0.. UInt? { let location = convert(event.locationInWindow, to: nil) if !NSPointInRect(location, self.bounds) { @@ -152,16 +167,16 @@ fileprivate class HorizontalCandidateView: NSView { var accuWidth: CGFloat = 0.0 for index in 0..= accuWidth && location.x <= accuWidth + currentWidth { return UInt(index) } accuWidth += currentWidth + 1.0 } return nil - + } - + override func mouseUp(with event: NSEvent) { trackingHighlightedIndex = highlightedIndex guard let newIndex = findHitIndex(event: event) else { @@ -170,7 +185,7 @@ fileprivate class HorizontalCandidateView: NSView { highlightedIndex = newIndex self.setNeedsDisplay(self.bounds) } - + override func mouseDown(with event: NSEvent) { guard let newIndex = findHitIndex(event: event) else { return @@ -181,7 +196,7 @@ fileprivate class HorizontalCandidateView: NSView { } else { highlightedIndex = trackingHighlightedIndex } - + trackingHighlightedIndex = 0 self.setNeedsDisplay(self.bounds) if triggerAction { @@ -198,89 +213,99 @@ public class HorizontalCandidateController: CandidateController { 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) let styleMask: NSWindow.StyleMask = [.borderless, .nonactivatingPanel] let panel = NSPanel(contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false) - panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel)) + panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel) + 1) panel.hasShadow = true + panel.isOpaque = false + panel.backgroundColor = NSColor.clear // Transparentify everything outside of the candidate list panel contentRect.origin = NSPoint.zero candidateView = HorizontalCandidateView(frame: contentRect) panel.contentView?.addSubview(candidateView) - contentRect.size = NSSize(width: 36.0, height: 20.0) + contentRect.size = NSSize(width: 20.0, height: 15.0) // Reduce the button width nextPageButton = NSButton(frame: contentRect) nextPageButton.setButtonType(.momentaryLight) - nextPageButton.bezelStyle = .smallSquare - nextPageButton.title = "»" - + 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 = .smallSquare - prevPageButton.title = "«" - + 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) - + 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) { fatalError("init(coder:) has not been implemented") } - + public override func reloadData() { candidateView.highlightedIndex = 0 currentPage = 0 layoutCandidateView() } - + public override func showNextPage() -> Bool { 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 { guard delegate != nil else { return false } - + if currentPage == 0 { return false } - + currentPage -= 1 candidateView.highlightedIndex = 0 layoutCandidateView() return true } - + public override func highlightNextCandidate() -> Bool { guard let delegate = delegate else { return false } - + let currentIndex = selectedCandidateIndex if currentIndex + 1 >= delegate.candidateCountForController(self) { return false @@ -288,30 +313,30 @@ public class HorizontalCandidateController: CandidateController { selectedCandidateIndex = currentIndex + 1 return true } - + public override func highlightPreviousCandidate() -> Bool { 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 { guard let delegate = delegate else { return UInt.max } - + let result = currentPage * UInt(keyLabels.count) + index return result < delegate.candidateCountForController(self) ? result : UInt.max } - + public override var selectedCandidateIndex: UInt { get { currentPage * UInt(keyLabels.count) + candidateView.highlightedIndex @@ -331,7 +356,7 @@ public class HorizontalCandidateController: CandidateController { } extension HorizontalCandidateController { - + private var pageCount: UInt { guard let delegate = delegate else { return 0 @@ -340,17 +365,17 @@ extension HorizontalCandidateController { let keyLabelCount = UInt(keyLabels.count) return totalCount / keyLabelCount + ((totalCount % keyLabelCount) != 0 ? 1 : 0) } - + private func layoutCandidateView() { guard let delegate = delegate else { return } - + candidateView.set(keyLabelFont: keyLabelFont, candidateFont: candidateFont) var candidates = [String]() let count = delegate.candidateCountForController(self) let keyLabelCount = UInt(keyLabels.count) - + let begin = currentPage * keyLabelCount for index in begin.. 1 { var buttonRect = nextPageButton.frame - var spacing = 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) - } - + let spacing:CGFloat = 0.0 + + 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) 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 @@ -390,16 +407,16 @@ extension HorizontalCandidateController { 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 @@ -410,9 +427,9 @@ extension HorizontalCandidateController { _ = showPreviousPage() } } - + @objc fileprivate func candidateViewMouseDidClick(_ sender: Any) { delegate?.candidateController(self, didSelectCandidateAtIndex: selectedCandidateIndex) } - + }