diff --git a/ClashX/General/Managers/MenuItemFactory.swift b/ClashX/General/Managers/MenuItemFactory.swift index 6525402..66644d3 100644 --- a/ClashX/General/Managers/MenuItemFactory.swift +++ b/ClashX/General/Managers/MenuItemFactory.swift @@ -18,24 +18,30 @@ class MenuItemFactory { completionHandler(menuItems) return } - for proxy in proxyInfo.proxies.sorted(by: { $0.name < $1.name}) { + + for proxy in proxyInfo.proxyGroups { var menu:NSMenuItem? switch proxy.type { - case .select: menu = self.generateSelectorMenuItem(proxyGroup: proxy, proxyMap: proxyInfo.proxiesMap) - case .urltest,.fallback: menu = generateUrlTestMenuItem(proxyGroup: proxy) + case .select: menu = self.generateSelectorMenuItem(proxyGroup: proxy, proxyInfo: proxyInfo) + case .urltest,.fallback: menu = generateUrlTestMenuItem(proxyGroup: proxy, proxyInfo: proxyInfo) case .loadBalance: - menu = generateLoadBalanceMenuItem(proxyGroup: proxy, proxyMap: proxyInfo.proxiesMap) + menu = generateLoadBalanceMenuItem(proxyGroup: proxy, proxyInfo: proxyInfo) default: continue } - if let menu = menu {menuItems.append(menu)} + if let menu = menu { + menuItems.append(menu) + menu.isEnabled=true + } } - completionHandler(menuItems.reversed()) + completionHandler(menuItems) } } static func generateSelectorMenuItem(proxyGroup:ClashProxy, - proxyMap:[ClashProxyName:ClashProxy]) -> NSMenuItem? { + proxyInfo:ClashProxyResp) -> NSMenuItem? { + let proxyMap = proxyInfo.proxiesMap + let isGlobalMode = ConfigManager.shared.currentConfig?.mode == .global if (isGlobalMode) { if proxyGroup.name != "GLOBAL" {return nil} @@ -54,18 +60,13 @@ class MenuItemFactory { if isGlobalMode && proxyModel.type == .select { continue } - let proxyItem = ProxyMenuItem(proxy: proxyModel, action: #selector(MenuItemFactory.actionSelectProxy(sender:)), maxProxyNameLength:proxyGroup.maxProxyNameLength) - proxyItem.target = MenuItemFactory.self proxyItem.isSelected = proxy == selectedName - if proxyItem.isSelected {hasSelected = true} submenu.addItem(proxyItem) - submenu.autoenablesItems = false - } menu.submenu = submenu @@ -75,29 +76,56 @@ class MenuItemFactory { return menu } - static func generateUrlTestMenuItem(proxyGroup:ClashProxy)->NSMenuItem? { + static func generateUrlTestMenuItem(proxyGroup:ClashProxy,proxyInfo:ClashProxyResp)->NSMenuItem? { + + let proxyMap = proxyInfo.proxiesMap let menu = NSMenuItem(title: proxyGroup.name, action: nil, keyEquivalent: "") let selectedName = proxyGroup.now ?? "" let submenu = NSMenu(title: proxyGroup.name) - let nowMenuItem = NSMenuItem(title: "now:\(selectedName)", action: nil, keyEquivalent: "") - + let nowMenuItem = NSMenuItem(title: "now:\(selectedName)", action: #selector(empty), keyEquivalent: "") + nowMenuItem.target = MenuItemFactory.self + submenu.addItem(nowMenuItem) + submenu.addItem(NSMenuItem.separator()) + + for proxyName in proxyGroup.all ?? [] { + guard let proxy = proxyMap[proxyName] else {continue} + let proxyMenuItem = NSMenuItem(title: proxy.name, action: nil, keyEquivalent: "") + if proxy.name == selectedName { + proxyMenuItem.state = .on + } + var historyItems = [NSMenuItem]() + for his in proxy.history { + historyItems.append( + NSMenuItem(title: "\(his.dateDisplay) \(his.delayDisplay)", action: nil, keyEquivalent: "")) + } + if historyItems.count > 0 { + let historyMenu = NSMenu(title: "") + historyMenu.items = historyItems + proxyMenuItem.submenu = historyMenu + submenu.addItem(proxyMenuItem) + } + } menu.submenu = submenu return menu } - static func generateLoadBalanceMenuItem(proxyGroup:ClashProxy, proxyMap:[ClashProxyName:ClashProxy])->NSMenuItem? { + static func generateLoadBalanceMenuItem(proxyGroup:ClashProxy, proxyInfo:ClashProxyResp)->NSMenuItem? { + + let proxyMap = proxyInfo.proxiesMap + let menu = NSMenuItem(title: proxyGroup.name, action: nil, keyEquivalent: "") let submenu = NSMenu(title: proxyGroup.name) for proxy in proxyGroup.all ?? [] { guard let proxyModel = proxyMap[proxy] else {continue} let proxyItem = ProxyMenuItem(proxy: proxyModel, - action:nil, + action:#selector(empty), maxProxyNameLength:proxyGroup.maxProxyNameLength) proxyItem.isSelected = false + proxyItem.target = MenuItemFactory.self submenu.addItem(proxyItem) } @@ -142,5 +170,7 @@ extension MenuItemFactory { ConfigManager.selectConfigName = config NotificationCenter.default.post(Notification(name: kShouldUpDateConfig)) } + + @objc static func empty(){} } diff --git a/ClashX/Models/ClashProxy.swift b/ClashX/Models/ClashProxy.swift index 27697a9..72d2e89 100644 --- a/ClashX/Models/ClashProxy.swift +++ b/ClashX/Models/ClashProxy.swift @@ -27,6 +27,26 @@ typealias ClashProxyName = String class ClashProxySpeedHistory:Codable { let time:Date let delay:Int + + class hisDateFormaterInstance { + static let shared = hisDateFormaterInstance() + lazy var formater:DateFormatter = { + var f = DateFormatter() + f.dateFormat = "HH:mm" + return f + }() + } + + lazy var delayDisplay:String = { + switch delay { + case 0: return "fail" + default:return "\(delay) ms" + } + }() + + lazy var dateDisplay:String = { + return hisDateFormaterInstance.shared.formater.string(from: time) + }() } @@ -54,7 +74,6 @@ class ClashProxy:Codable { options: .usesLineFragmentOrigin, attributes: attr).width; }() - } class ClashProxyResp{ @@ -96,6 +115,28 @@ class ClashProxyResp{ self.proxies = proxiesModel } + lazy var proxyGroups:[ClashProxy] = { + return proxies.filter{ + switch $0.type { + case .select,.urltest,.fallback,.loadBalance:return true + default:return false + } + }.sorted(by: {$0.name < $1.name}) + }() + + lazy var longestProxyGroupName = { + return proxyGroups.max{$1.name.count > $0.name.count}?.name ?? "" + }() + + lazy var maxProxyNameLength:CGFloat = { + let rect = CGSize(width: CGFloat.greatestFiniteMagnitude, height: 20) + let attr = [NSAttributedString.Key.font: NSFont.menuBarFont(ofSize: 0)] + return (self.longestProxyGroupName as NSString) + .boundingRect(with: rect, + options: .usesLineFragmentOrigin, + attributes: attr).width; + }() + } diff --git a/ClashX/Views/ProxyMenuItem.swift b/ClashX/Views/ProxyMenuItem.swift index 2717ef8..ebca368 100644 --- a/ClashX/Views/ProxyMenuItem.swift +++ b/ClashX/Views/ProxyMenuItem.swift @@ -16,21 +16,14 @@ class ProxyMenuItem:NSMenuItem { proxyName = proxy.name - if let delay = proxy.history.first?.delay { - - func getDelayDisplay() -> String { - switch delay { - case 0: return "fail" - default:return "\(delay) ms" - } - } + if let his = proxy.history.first { let paragraph = NSMutableParagraphStyle() paragraph.tabStops = [ NSTextTab(textAlignment: .right, location: maxProxyNameLength + 80, options: [:]), ] - let str = "\(proxy.name)\t\(getDelayDisplay())" + let str = "\(proxy.name)\t\(his.delayDisplay)" let attributed = NSMutableAttributedString( string: str, diff --git a/ClashX/go.mod b/ClashX/go.mod index 96eb515..ea69358 100644 --- a/ClashX/go.mod +++ b/ClashX/go.mod @@ -1,3 +1,3 @@ module github.com/yichengchen/clashX/ClashX -require github.com/Dreamacro/clash v0.12.1-0.20190317065239-7a9d986ff38c +require github.com/Dreamacro/clash v0.12.1-0.20190323082927-f99da37168ac diff --git a/ClashX/go.sum b/ClashX/go.sum index f9c5229..9b8a491 100644 --- a/ClashX/go.sum +++ b/ClashX/go.sum @@ -1,5 +1,5 @@ -github.com/Dreamacro/clash v0.12.1-0.20190317065239-7a9d986ff38c h1:SRELShfh3m6DJxlnNqKdhAPNY2eXUvFZNsUtUlTrkzk= -github.com/Dreamacro/clash v0.12.1-0.20190317065239-7a9d986ff38c/go.mod h1:jIxj6LTv5e5Yr+0Yt682at2dyXqUR9p16asV9yya6Vc= +github.com/Dreamacro/clash v0.12.1-0.20190323082927-f99da37168ac h1:9WV1wjtHo9Gm9Dq1chmEjpJkefLC1ck64yzhd6aQXUA= +github.com/Dreamacro/clash v0.12.1-0.20190323082927-f99da37168ac/go.mod h1:jIxj6LTv5e5Yr+0Yt682at2dyXqUR9p16asV9yya6Vc= github.com/Dreamacro/go-shadowsocks2 v0.1.3-0.20190202135136-da4602d8f112 h1:1axYxE0ZLJy40+ulq46XQt7MaJDJr4iGer1NQz7jmKw= github.com/Dreamacro/go-shadowsocks2 v0.1.3-0.20190202135136-da4602d8f112/go.mod h1:giIuN+TuUudTxHc1jjTOyyQYiJ3VXp1pWOHdJbSCAPo= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=