feat: adapt macOS 11 look

This commit is contained in:
yicheng 2020-11-14 15:01:28 +08:00
parent d9d225d0f8
commit 3e165db995
7 changed files with 133 additions and 42 deletions

View File

@ -955,6 +955,7 @@
F9A7C06E2306E874007163C7 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = x86_64;
CODE_SIGN_ENTITLEMENTS = ProxyConfigHelper/com.west2online.ClashX.ProxyConfigHelper.entitlements;
CODE_SIGN_IDENTITY = "Developer ID Application";
CODE_SIGN_STYLE = Manual;
@ -985,6 +986,7 @@
F9A7C06F2306E874007163C7 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = x86_64;
CODE_SIGN_ENTITLEMENTS = ProxyConfigHelper/com.west2online.ClashX.ProxyConfigHelper.entitlements;
CODE_SIGN_IDENTITY = "Developer ID Application";
CODE_SIGN_STYLE = Manual;

View File

@ -687,6 +687,7 @@ extension AppDelegate {
@IBAction func actionUpdateProxyGroupMenu(_ sender: Any) {
ConfigManager.shared.disableShowCurrentProxyInMenu = !ConfigManager.shared.disableShowCurrentProxyInMenu
updateExperimentalFeatureStatus()
MenuItemFactory.recreateProxyMenuItems()
}
@IBAction func actionSetBenchmarkUrl(_ sender: Any) {
@ -806,6 +807,14 @@ extension AppDelegate {
self?.syncConfig()
}
}
func hasMenuSelected() -> Bool {
if #available(macOS 11, *) {
return statusMenu.items.contains { $0.state == .on }
} else {
return true
}
}
}
// MARK: NSMenuDelegate
@ -815,6 +824,9 @@ extension AppDelegate: NSMenuDelegate {
MenuItemFactory.refreshExistingMenuItems()
updateConfigFiles()
syncConfig()
NotificationCenter.default.post(name: .proxyMeneViewShowLeftPadding,
object: nil,
userInfo: ["show": hasMenuSelected()])
}
func menu(_ menu: NSMenu, willHighlight item: NSMenuItem?) {
@ -839,9 +851,9 @@ extension AppDelegate {
}
guard let components = URLComponents(string: url),
let scheme = components.scheme,
scheme.hasPrefix("clash"),
let host = components.host
let scheme = components.scheme,
scheme.hasPrefix("clash"),
let host = components.host
else { return }
if host == "install-config" {

View File

@ -45,13 +45,14 @@ class MenuItemFactory {
}
static func refreshMenuItems(mergedData proxyInfo: ClashProxyResp?) {
let leftPadding = AppDelegate.shared.hasMenuSelected()
guard let proxyInfo = proxyInfo else { return }
var menuItems = [NSMenuItem]()
for proxy in proxyInfo.proxyGroups {
var menu: NSMenuItem?
switch proxy.type {
case .select: menu = generateSelectorMenuItem(proxyGroup: proxy, proxyInfo: proxyInfo)
case .urltest, .fallback: menu = generateUrlTestFallBackMenuItem(proxyGroup: proxy, proxyInfo: proxyInfo)
case .select: menu = generateSelectorMenuItem(proxyGroup: proxy, proxyInfo: proxyInfo, leftPadding: leftPadding)
case .urltest, .fallback: menu = generateUrlTestFallBackMenuItem(proxyGroup: proxy, proxyInfo: proxyInfo, leftPadding: leftPadding)
case .loadBalance:
menu = generateLoadBalanceMenuItem(proxyGroup: proxy, proxyInfo: proxyInfo)
case .relay:
@ -106,7 +107,9 @@ class MenuItemFactory {
// MARK: Generators
private static func generateSelectorMenuItem(proxyGroup: ClashProxy,
proxyInfo: ClashProxyResp) -> NSMenuItem? {
proxyInfo: ClashProxyResp,
leftPadding: Bool) -> NSMenuItem?
{
let proxyMap = proxyInfo.proxiesMap
let isGlobalMode = ConfigManager.shared.currentConfig?.mode == .global
@ -117,7 +120,7 @@ class MenuItemFactory {
let menu = NSMenuItem(title: proxyGroup.name, action: nil, keyEquivalent: "")
let selectedName = proxyGroup.now ?? ""
if !ConfigManager.shared.disableShowCurrentProxyInMenu {
menu.view = ProxyGroupMenuItemView(group: proxyGroup.name, targetProxy: selectedName)
menu.view = ProxyGroupMenuItemView(group: proxyGroup.name, targetProxy: selectedName, hasLeftPadding: leftPadding)
}
let submenu = ProxyGroupMenu(title: proxyGroup.name)
@ -137,18 +140,18 @@ class MenuItemFactory {
addSpeedTestMenuItem(submenu, proxyGroup: proxyGroup)
menu.submenu = submenu
if !ConfigManager.shared.disableShowCurrentProxyInMenu {
menu.view = ProxyGroupMenuItemView(group: proxyGroup.name, targetProxy: selectedName)
}
return menu
}
private static func generateUrlTestFallBackMenuItem(proxyGroup: ClashProxy, proxyInfo: ClashProxyResp) -> NSMenuItem? {
private static func generateUrlTestFallBackMenuItem(proxyGroup: ClashProxy,
proxyInfo: ClashProxyResp,
leftPadding: Bool) -> NSMenuItem?
{
let proxyMap = proxyInfo.proxiesMap
let selectedName = proxyGroup.now ?? ""
let menu = NSMenuItem(title: proxyGroup.name, action: nil, keyEquivalent: "")
if !ConfigManager.shared.disableShowCurrentProxyInMenu {
menu.view = ProxyGroupMenuItemView(group: proxyGroup.name, targetProxy: selectedName)
menu.view = ProxyGroupMenuItemView(group: proxyGroup.name, targetProxy: selectedName, hasLeftPadding: leftPadding)
}
let submenu = NSMenu(title: proxyGroup.name)

View File

@ -13,8 +13,10 @@ extension Notification.Name {
static let reloadDashboard = Notification.Name("kReloadDashboard")
static let systemNetworkStatusIPUpdate = Notification.Name("systemNetworkStatusIPUpdate")
static let systemNetworkStatusDidChange = Notification.Name("kSystemNetworkStatusDidChange")
static let proxyMeneViewShowLeftPadding = Notification.Name("kProxyMeneViewShowLeftPadding")
static func proxyUpdate(for name: ClashProxyName) -> Notification.Name {
return Notification.Name("kProxyUpdate_\(name)")
}
}

View File

@ -15,13 +15,7 @@ class MenuItemBaseView: NSView {
// MARK: Public
var isHighlighted: Bool = false {
didSet {
if #available(macOS 11, *), isHighlighted != oldValue {
setNeedsDisplay()
}
}
}
var isHighlighted: Bool = false
let effectView: NSVisualEffectView = {
let effectView = NSVisualEffectView()
@ -41,9 +35,22 @@ class MenuItemBaseView: NSView {
return []
}
static let labelFont: NSFont = NSFont.monospacedDigitSystemFont(ofSize: 14, weight: .regular)
static let menuBarHeight: CGFloat = {
if #available(macOS 11, *) {
return 22
} else {
return 20
}
}()
init(frame frameRect: NSRect = NSRect(x: 0, y: 0, width: 0, height: 20), autolayout: Bool) {
static let labelFont: NSFont = {
if #available(macOS 11, *) {
return NSFont.menuFont(ofSize: 0)
}
return NSFont.monospacedDigitSystemFont(ofSize: 14, weight: .regular)
}()
init(frame frameRect: NSRect = NSRect(x: 0, y: 0, width: 0, height: menuBarHeight), autolayout: Bool) {
self.autolayout = autolayout
super.init(frame: frameRect)
setupView()
@ -65,13 +72,24 @@ class MenuItemBaseView: NSView {
private func setupView() {
translatesAutoresizingMaskIntoConstraints = false
heightAnchor.constraint(equalToConstant: 20).isActive = true
heightAnchor.constraint(equalToConstant: type(of: self).menuBarHeight).isActive = true
// background
addSubview(effectView)
effectView.translatesAutoresizingMaskIntoConstraints = false
if #available(macOS 11, *) {
effectView.wantsLayer = true
effectView.layer?.cornerRadius = 3
effectView.layer?.masksToBounds = true
}
if autolayout {
effectView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
effectView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
let padding: CGFloat
if #available(macOS 11, *) {
padding = 5
} else {
padding = 0
}
effectView.leftAnchor.constraint(equalTo: leftAnchor, constant: padding).isActive = true
effectView.rightAnchor.constraint(equalTo: rightAnchor, constant: -padding).isActive = true
effectView.topAnchor.constraint(equalTo: topAnchor).isActive = true
effectView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
}
@ -82,7 +100,11 @@ class MenuItemBaseView: NSView {
override func layout() {
super.layout()
if !autolayout {
effectView.frame = bounds
if #available(macOS 11, *) {
effectView.frame = CGRect(x: 5, y: 0, width: bounds.width - 10, height: bounds.height)
} else {
effectView.frame = bounds
}
}
}

View File

@ -9,15 +9,29 @@
import Cocoa
class ProxyGroupMenuItemView: MenuItemBaseView {
let groupNameLabel: NSTextField
let selectProxyLabel: NSTextField
let arrowLabel = NSTextField(labelWithString: "")
private let groupNameLabel: NSTextField
private let selectProxyLabel: NSTextField
private let arrowLabel: NSControl = {
if #available(macOS 11, *) {
let image = NSImage(named: NSImage.goForwardTemplateName)!.withSymbolConfiguration(NSImage.SymbolConfiguration(pointSize: 14, weight: .bold, scale: .small))!
return NSImageView(image: image)
} else {
let label = NSTextField(labelWithString: "")
label.setContentHuggingPriority(.required, for: .horizontal)
label.setContentCompressionResistancePriority(.required, for: .horizontal)
label.textColor = NSColor.labelColor
return label
}
}()
private var leftPaddingConstraint: NSLayoutConstraint?
private let leftPadding: CGFloat = 20
override var cells: [NSCell?] {
return [groupNameLabel.cell, selectProxyLabel.cell, arrowLabel.cell]
}
init(group: ClashProxyName, targetProxy: ClashProxyName) {
init(group: ClashProxyName, targetProxy: ClashProxyName, hasLeftPadding: Bool) {
groupNameLabel = VibrancyTextField(labelWithString: group)
selectProxyLabel = VibrancyTextField(labelWithString: targetProxy)
super.init(autolayout: true)
@ -25,14 +39,20 @@ class ProxyGroupMenuItemView: MenuItemBaseView {
// arrow
effectView.addSubview(arrowLabel)
arrowLabel.translatesAutoresizingMaskIntoConstraints = false
arrowLabel.rightAnchor.constraint(equalTo: effectView.rightAnchor, constant: -10).isActive = true
let rightConstraint: CGFloat
if #available(macOS 11, *) {
rightConstraint = -8
} else {
rightConstraint = -10
}
arrowLabel.rightAnchor.constraint(equalTo: effectView.rightAnchor, constant: rightConstraint).isActive = true
arrowLabel.centerYAnchor.constraint(equalTo: effectView.centerYAnchor).isActive = true
arrowLabel.setContentHuggingPriority(.required, for: .horizontal)
arrowLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
// group
groupNameLabel.translatesAutoresizingMaskIntoConstraints = false
effectView.addSubview(groupNameLabel)
groupNameLabel.leftAnchor.constraint(equalTo: effectView.leftAnchor, constant: 20).isActive = true
leftPaddingConstraint = groupNameLabel.leftAnchor.constraint(equalTo: effectView.leftAnchor, constant: leftPadding)
leftPaddingConstraint?.isActive = true
groupNameLabel.centerYAnchor.constraint(equalTo: effectView.centerYAnchor).isActive = true
groupNameLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
@ -53,10 +73,21 @@ class ProxyGroupMenuItemView: MenuItemBaseView {
selectProxyLabel.font = type(of: self).labelFont
groupNameLabel.textColor = NSColor.labelColor
selectProxyLabel.textColor = NSColor.secondaryLabelColor
arrowLabel.textColor = NSColor.labelColor
// noti
NotificationCenter.default.addObserver(self, selector: #selector(proxyInfoDidUpdate(note:)), name: .proxyUpdate(for: group), object: nil)
if #available(macOS 11, *) {
updateLeftMenuPadding(show: hasLeftPadding)
NotificationCenter.default.addObserver(self, selector: #selector(showLeftPaddingUpdate(note:)), name: .proxyMeneViewShowLeftPadding, object: nil)
}
}
private func updateLeftMenuPadding(show: Bool) {
if show {
leftPaddingConstraint?.constant = leftPadding
} else {
leftPaddingConstraint?.constant = 10
}
}
required init?(coder: NSCoder) {
@ -71,6 +102,11 @@ class ProxyGroupMenuItemView: MenuItemBaseView {
guard let info = note.object as? ClashProxy else { assertionFailure(); return }
selectProxyLabel.stringValue = info.now ?? ""
}
@objc private func showLeftPaddingUpdate(note: NSNotification) {
guard let show = note.userInfo?["show"] as? Bool else { assertionFailure(); return }
updateLeftMenuPadding(show: show)
}
}
extension ProxyGroupMenuItemView: ProxyGroupMenuHighlightDelegate {

View File

@ -20,7 +20,11 @@ class ProxyItemView: MenuItemBaseView {
delayLabel = VibrancyTextField(labelWithString: "").setup(allowsVibrancy: false)
let cell = PaddedNSTextFieldCell()
cell.widthPadding = 2
cell.heightPadding = 1
if #available(macOS 11, *) {
cell.heightPadding = 2
} else {
cell.heightPadding = 1
}
delayLabel.cell = cell
super.init(autolayout: false)
effectView.addSubview(nameLabel)
@ -30,7 +34,11 @@ class ProxyItemView: MenuItemBaseView {
delayLabel.translatesAutoresizingMaskIntoConstraints = false
nameLabel.font = type(of: self).labelFont
delayLabel.font = NSFont.monospacedDigitSystemFont(ofSize: 10, weight: .medium)
if #available(macOS 11, *) {
delayLabel.font = NSFont.monospacedDigitSystemFont(ofSize: 9, weight: .medium)
} else {
delayLabel.font = NSFont.monospacedDigitSystemFont(ofSize: 10, weight: .medium)
}
nameLabel.alignment = .left
delayLabel.alignment = .right
@ -45,13 +53,13 @@ class ProxyItemView: MenuItemBaseView {
super.layout()
nameLabel.sizeToFit()
delayLabel.sizeToFit()
imageView?.frame = CGRect(x: 5, y: bounds.height / 2 - 6, width: 12, height: 12)
imageView?.frame = CGRect(x: 5, y: effectView.bounds.height / 2 - 6, width: 12, height: 12)
nameLabel.frame = CGRect(x: 18,
y: (bounds.height - nameLabel.bounds.height) / 2,
y: (effectView.bounds.height - nameLabel.bounds.height) / 2,
width: nameLabel.bounds.width,
height: nameLabel.bounds.height)
delayLabel.frame = CGRect(x: bounds.width - delayLabel.bounds.width - 8,
y: (bounds.height - delayLabel.bounds.height) / 2,
delayLabel.frame = CGRect(x: effectView.bounds.width - delayLabel.bounds.width - 8,
y: (effectView.bounds.height - delayLabel.bounds.height) / 2,
width: delayLabel.bounds.width,
height: delayLabel.bounds.height)
}
@ -77,7 +85,13 @@ class ProxyItemView: MenuItemBaseView {
func update(selected: Bool) {
if selected {
if imageView == nil {
imageView = NSImageView(image: NSImage(named: NSImage.menuOnStateTemplateName)!)
let image: NSImage
if #available(OSX 11.0, *) {
image = NSImage(named: NSImage.menuOnStateTemplateName)!.withSymbolConfiguration(NSImage.SymbolConfiguration(pointSize: 13, weight: .bold, scale: .small))!
} else {
image = NSImage(named: NSImage.menuOnStateTemplateName)!
}
imageView = NSImageView(image: image)
imageView?.translatesAutoresizingMaskIntoConstraints = false
effectView.addSubview(imageView!)
}