Skip to content

Commit

Permalink
update the look of the candidate sheet
Browse files Browse the repository at this point in the history
- rounded rectangles
- honor light/dark modes
  • Loading branch information
shuang886 committed Nov 23, 2023
1 parent 2e61a36 commit 6da939c
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,10 @@ fileprivate class HorizontalCandidateView: NSView {

keyLabelAttrDict = [.font: labelFont,
.paragraphStyle: paraStyle,
.foregroundColor: NSColor.black]
.foregroundColor: NSColor.secondaryLabelColor]
candidateAttrDict = [.font: candidateFont,
.paragraphStyle: paraStyle,
.foregroundColor: NSColor.textColor]
.foregroundColor: NSColor.labelColor]

let labelFontSize = labelFont.pointSize
let candidateFontSize = candidateFont.pointSize
Expand All @@ -112,13 +112,9 @@ fileprivate class HorizontalCandidateView: NSView {
}

override func draw(_ dirtyRect: NSRect) {
let backgroundColor = NSColor.controlBackgroundColor
let darkGray = NSColor(deviceWhite: 0.7, alpha: 1.0)
let lightGray = NSColor(deviceWhite: 0.8, alpha: 1.0)

let bounds = self.bounds
backgroundColor.setFill()
NSBezierPath.fill(bounds)

if #available(macOS 10.14, *) {
NSColor.separatorColor.setStroke()
Expand All @@ -141,20 +137,25 @@ fileprivate class HorizontalCandidateView: NSView {
let currentWidth = elementWidths[index]
let labelRect = NSRect(x: accuWidth, y: tooltipSize.height, width: currentWidth, height: keyLabelHeight)
let candidateRect = NSRect(x: accuWidth, y: tooltipSize.height + keyLabelHeight + 1.0, width: currentWidth, height: candidateTextHeight)
(index == highlightedIndex ? darkGray : lightGray).setFill()
NSBezierPath.fill(labelRect)
(keyLabels[index] as NSString).draw(in: labelRect, withAttributes: keyLabelAttrDict)
var activeKeyLabelAttrDict = keyLabelAttrDict
if index == highlightedIndex {
NSColor.selectedControlColor.setFill()
NSBezierPath.fill(labelRect)
activeKeyLabelAttrDict[.foregroundColor] = NSColor.selectedControlTextColor
}
(keyLabels[index] as NSString).draw(in: labelRect, withAttributes: activeKeyLabelAttrDict)

var activeCandidateAttr = candidateAttrDict
if index == highlightedIndex {
NSColor.selectedTextBackgroundColor.setFill()
activeCandidateAttr = candidateAttrDict
activeCandidateAttr[.foregroundColor] = NSColor.selectedTextColor
} else {
backgroundColor.setFill()
if #available(macOS 10.14, *) {
NSColor.controlAccentColor.setFill()
} else {
NSColor.selectedControlColor.setFill()
}
NSBezierPath.fill(candidateRect)
activeCandidateAttr[.foregroundColor] = NSColor.white
}

NSBezierPath.fill(candidateRect)
(displayedCandidates[index] as NSString).draw(in: candidateRect, withAttributes: activeCandidateAttr)
accuWidth += currentWidth + 1.0
}
Expand Down Expand Up @@ -221,6 +222,19 @@ public class HorizontalCandidateController: CandidateController {
let panel = NSPanel(contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false)
panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel) + 1)
panel.hasShadow = true
panel.backgroundColor = .clear
panel.isOpaque = false

let effect = NSVisualEffectView(frame: NSRect(x: 0, y: 0, width: 0, height: 0))
effect.blendingMode = .behindWindow
if #available(macOS 10.14, *) {
effect.material = .contentBackground
} else {
effect.material = .appearanceBased
}
effect.wantsLayer = true
effect.maskImage = .mask(withCornerRadius: 4)
panel.contentView = effect

contentRect.origin = NSPoint.zero
candidateView = HorizontalCandidateView(frame: contentRect)
Expand All @@ -229,13 +243,15 @@ public class HorizontalCandidateController: CandidateController {
contentRect.size = NSSize(width: 36.0, height: 20.0)
nextPageButton = NSButton(frame: contentRect)
nextPageButton.setButtonType(.momentaryLight)
nextPageButton.bezelStyle = .smallSquare
nextPageButton.title = "»"
nextPageButton.bezelStyle = .toolbar
nextPageButton.isBordered = false
nextPageButton.attributedTitle = "»".withColor(.controlTextColor)

prevPageButton = NSButton(frame: contentRect)
prevPageButton.setButtonType(.momentaryLight)
prevPageButton.bezelStyle = .smallSquare
prevPageButton.title = "«"
prevPageButton.bezelStyle = .toolbar
prevPageButton.isBordered = false
prevPageButton.attributedTitle = "«".withColor(.controlTextColor)

panel.contentView?.addSubview(nextPageButton)
panel.contentView?.addSubview(prevPageButton)
Expand Down Expand Up @@ -433,3 +449,10 @@ extension HorizontalCandidateController {
}

}

extension String {
func withColor(_ color: NSColor) -> NSAttributedString {
let attrDict: [NSAttributedString.Key: AnyObject] = [.foregroundColor: color]
return NSAttributedString(string: self, attributes: attrDict)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,38 +35,35 @@ fileprivate class VerticalKeyLabelStripView: NSView {

override func draw(_ dirtyRect: NSRect) {
let bounds = self.bounds
NSColor.white.setFill()
NSBezierPath.fill(bounds)

let count = UInt(keyLabels.count)
if count == 0 {
return
}
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()
paraStyle.setParagraphStyle(NSParagraphStyle.default)
paraStyle.alignment = .center

let textAttr: [NSAttributedString.Key: AnyObject] = [
.font: keyLabelFont,
.foregroundColor: black,
.foregroundColor: NSColor.secondaryLabelColor,
.paragraphStyle: paraStyle]
let textAttrHighlighted: [NSAttributedString.Key: AnyObject] = [
.font: keyLabelFont,
.foregroundColor: NSColor.selectedControlTextColor,
.paragraphStyle: paraStyle]
for index in 0..<count {
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)
let cellRect = NSRect(x: 0.0, y: CGFloat(index) * cellHeight, width: bounds.size.width, height: cellHeight)

if index + 1 >= count {
cellRect.size.height += 1.0
if index == highlightedIndex {
NSColor.selectedControlColor.setFill()
NSBezierPath.fill(cellRect)
}

(index == highlightedIndex ? darkGray : lightGray).setFill()
NSBezierPath.fill(cellRect)
let text = keyLabels[Int(index)]
(text as NSString).draw(in: textRect, withAttributes: textAttr)
(text as NSString).draw(in: textRect, withAttributes: (index == highlightedIndex) ? textAttrHighlighted : textAttr)
}
}
}
Expand All @@ -85,13 +82,6 @@ private let kCandidateTextLeftMargin: CGFloat = 8.0
private let kCandidateTextPaddingWithMandatedTableViewPadding: CGFloat = 18.0
private let kCandidateTextLeftMarginWithMandatedTableViewPadding: CGFloat = 0.0

private class BackgroundView: NSView {
override func draw(_ dirtyRect: NSRect) {
NSColor.windowBackgroundColor.setFill()
NSBezierPath.fill(self.bounds)
}
}

@objc(VTVerticalCandidateController)
public class VerticalCandidateController: CandidateController {
private var keyLabelStripView: VerticalKeyLabelStripView
Expand All @@ -110,7 +100,19 @@ 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()
panel.backgroundColor = .clear
panel.isOpaque = false

let effect = NSVisualEffectView(frame: NSRect(x: 0, y: 0, width: 0, height: 0))
effect.blendingMode = .behindWindow
if #available(macOS 10.14, *) {
effect.material = .contentBackground
} else {
effect.material = .appearanceBased
}
effect.wantsLayer = true
effect.maskImage = .mask(withCornerRadius: 4)
panel.contentView = effect

tooltipView = NSTextField(frame: NSRect.zero)
tooltipView.isEditable = false
Expand All @@ -130,6 +132,8 @@ public class VerticalCandidateController: CandidateController {
scrollViewRect.size.width -= stripRect.size.width
scrollView = NSScrollView(frame: scrollViewRect)
scrollView.verticalScrollElasticity = .none
scrollView.drawsBackground = false
scrollView.contentView.drawsBackground = false

tableView = NSTableView(frame: contentRect)
let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "candidate"))
Expand All @@ -144,6 +148,7 @@ public class VerticalCandidateController: CandidateController {
tableView.headerView = nil
tableView.allowsMultipleSelection = false
tableView.allowsEmptySelection = false
tableView.backgroundColor = .clear

if #available(macOS 10.16, *) {
tableView.style = .fullWidth
Expand Down Expand Up @@ -484,3 +489,18 @@ extension VerticalCandidateController: NSTableViewDataSource, NSTableViewDelegat
self.window?.setFrame(frameRect, display: false)
}
}

extension NSImage {
static func mask(withCornerRadius radius: CGFloat) -> NSImage {
let image = NSImage(size: NSSize(width: radius * 2, height: radius * 2), flipped: false) {
NSBezierPath(roundedRect: $0, xRadius: radius, yRadius: radius).fill()
NSColor.black.set()
return true
}

image.capInsets = NSEdgeInsets(top: radius, left: radius, bottom: radius, right: radius)
image.resizingMode = .stretch

return image
}
}

0 comments on commit 6da939c

Please sign in to comment.