Shiki: Swiftify // Voltaire UI Revamp - Vertical
This commit is contained in:
parent
15e2986c0a
commit
93943f0d1d
|
@ -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).
|
||||||
|
@ -46,17 +51,15 @@ fileprivate class VerticalKeyLabelStripView: NSView {
|
||||||
|
|
||||||
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)
|
||||||
|
@ -64,20 +67,23 @@ fileprivate class VerticalKeyLabelStripView: NSView {
|
||||||
|
|
||||||
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)
|
||||||
|
@ -110,9 +115,19 @@ public class VerticalCandidateController: CandidateController {
|
||||||
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
|
||||||
|
@ -124,11 +139,18 @@ public class VerticalCandidateController: CandidateController {
|
||||||
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
|
||||||
|
@ -139,11 +161,12 @@ public class VerticalCandidateController: CandidateController {
|
||||||
tableView.headerView = nil
|
tableView.headerView = nil
|
||||||
tableView.allowsMultipleSelection = false
|
tableView.allowsMultipleSelection = false
|
||||||
tableView.allowsEmptySelection = false
|
tableView.allowsEmptySelection = false
|
||||||
|
tableView.backgroundColor = NSColor.clear
|
||||||
|
tableView.gridColor = NSColor.clear
|
||||||
|
|
||||||
if #available(macOS 10.16, *) {
|
if #available(macOS 11.0, *) {
|
||||||
tableView.style = .fullWidth
|
tableView.style = .plain
|
||||||
candidateTextPadding = kCandidateTextPaddingWithMandatedTableViewPadding
|
tableView.enclosingScrollView?.borderType = .noBorder
|
||||||
candidateTextLeftMargin = kCandidateTextLeftMarginWithMandatedTableViewPadding
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollView.documentView = tableView
|
scrollView.documentView = tableView
|
||||||
|
@ -268,7 +291,6 @@ extension VerticalCandidateController: NSTableViewDataSource, NSTableViewDelegat
|
||||||
])
|
])
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -416,8 +438,8 @@ 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
|
||||||
|
@ -439,7 +461,7 @@ extension VerticalCandidateController: NSTableViewDataSource, NSTableViewDelegat
|
||||||
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
|
||||||
|
@ -449,7 +471,7 @@ extension VerticalCandidateController: NSTableViewDataSource, NSTableViewDelegat
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue