Feature: show speedtest history in proxy group

This commit is contained in:
yicheng 2019-03-23 19:13:28 +08:00
parent 670ad02b49
commit 61e87ed07c
5 changed files with 94 additions and 30 deletions

View File

@ -18,24 +18,30 @@ class MenuItemFactory {
completionHandler(menuItems) completionHandler(menuItems)
return return
} }
for proxy in proxyInfo.proxies.sorted(by: { $0.name < $1.name}) {
for proxy in proxyInfo.proxyGroups {
var menu:NSMenuItem? var menu:NSMenuItem?
switch proxy.type { switch proxy.type {
case .select: menu = self.generateSelectorMenuItem(proxyGroup: proxy, proxyMap: proxyInfo.proxiesMap) case .select: menu = self.generateSelectorMenuItem(proxyGroup: proxy, proxyInfo: proxyInfo)
case .urltest,.fallback: menu = generateUrlTestMenuItem(proxyGroup: proxy) case .urltest,.fallback: menu = generateUrlTestMenuItem(proxyGroup: proxy, proxyInfo: proxyInfo)
case .loadBalance: case .loadBalance:
menu = generateLoadBalanceMenuItem(proxyGroup: proxy, proxyMap: proxyInfo.proxiesMap) menu = generateLoadBalanceMenuItem(proxyGroup: proxy, proxyInfo: proxyInfo)
default: continue 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, static func generateSelectorMenuItem(proxyGroup:ClashProxy,
proxyMap:[ClashProxyName:ClashProxy]) -> NSMenuItem? { proxyInfo:ClashProxyResp) -> NSMenuItem? {
let proxyMap = proxyInfo.proxiesMap
let isGlobalMode = ConfigManager.shared.currentConfig?.mode == .global let isGlobalMode = ConfigManager.shared.currentConfig?.mode == .global
if (isGlobalMode) { if (isGlobalMode) {
if proxyGroup.name != "GLOBAL" {return nil} if proxyGroup.name != "GLOBAL" {return nil}
@ -54,18 +60,13 @@ class MenuItemFactory {
if isGlobalMode && proxyModel.type == .select { if isGlobalMode && proxyModel.type == .select {
continue continue
} }
let proxyItem = ProxyMenuItem(proxy: proxyModel, action: #selector(MenuItemFactory.actionSelectProxy(sender:)), let proxyItem = ProxyMenuItem(proxy: proxyModel, action: #selector(MenuItemFactory.actionSelectProxy(sender:)),
maxProxyNameLength:proxyGroup.maxProxyNameLength) maxProxyNameLength:proxyGroup.maxProxyNameLength)
proxyItem.target = MenuItemFactory.self proxyItem.target = MenuItemFactory.self
proxyItem.isSelected = proxy == selectedName proxyItem.isSelected = proxy == selectedName
if proxyItem.isSelected {hasSelected = true} if proxyItem.isSelected {hasSelected = true}
submenu.addItem(proxyItem) submenu.addItem(proxyItem)
submenu.autoenablesItems = false
} }
menu.submenu = submenu menu.submenu = submenu
@ -75,29 +76,56 @@ class MenuItemFactory {
return menu 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 menu = NSMenuItem(title: proxyGroup.name, action: nil, keyEquivalent: "")
let selectedName = proxyGroup.now ?? "" let selectedName = proxyGroup.now ?? ""
let submenu = NSMenu(title: proxyGroup.name) 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(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 menu.submenu = submenu
return menu 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 menu = NSMenuItem(title: proxyGroup.name, action: nil, keyEquivalent: "")
let submenu = NSMenu(title: proxyGroup.name) let submenu = NSMenu(title: proxyGroup.name)
for proxy in proxyGroup.all ?? [] { for proxy in proxyGroup.all ?? [] {
guard let proxyModel = proxyMap[proxy] else {continue} guard let proxyModel = proxyMap[proxy] else {continue}
let proxyItem = ProxyMenuItem(proxy: proxyModel, let proxyItem = ProxyMenuItem(proxy: proxyModel,
action:nil, action:#selector(empty),
maxProxyNameLength:proxyGroup.maxProxyNameLength) maxProxyNameLength:proxyGroup.maxProxyNameLength)
proxyItem.isSelected = false proxyItem.isSelected = false
proxyItem.target = MenuItemFactory.self
submenu.addItem(proxyItem) submenu.addItem(proxyItem)
} }
@ -142,5 +170,7 @@ extension MenuItemFactory {
ConfigManager.selectConfigName = config ConfigManager.selectConfigName = config
NotificationCenter.default.post(Notification(name: kShouldUpDateConfig)) NotificationCenter.default.post(Notification(name: kShouldUpDateConfig))
} }
@objc static func empty(){}
} }

View File

@ -27,6 +27,26 @@ typealias ClashProxyName = String
class ClashProxySpeedHistory:Codable { class ClashProxySpeedHistory:Codable {
let time:Date let time:Date
let delay:Int 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, options: .usesLineFragmentOrigin,
attributes: attr).width; attributes: attr).width;
}() }()
} }
class ClashProxyResp{ class ClashProxyResp{
@ -96,6 +115,28 @@ class ClashProxyResp{
self.proxies = proxiesModel 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;
}()
} }

View File

@ -16,21 +16,14 @@ class ProxyMenuItem:NSMenuItem {
proxyName = proxy.name proxyName = proxy.name
if let delay = proxy.history.first?.delay { if let his = proxy.history.first {
func getDelayDisplay() -> String {
switch delay {
case 0: return "fail"
default:return "\(delay) ms"
}
}
let paragraph = NSMutableParagraphStyle() let paragraph = NSMutableParagraphStyle()
paragraph.tabStops = [ paragraph.tabStops = [
NSTextTab(textAlignment: .right, location: maxProxyNameLength + 80, options: [:]), NSTextTab(textAlignment: .right, location: maxProxyNameLength + 80, options: [:]),
] ]
let str = "\(proxy.name)\t\(getDelayDisplay())" let str = "\(proxy.name)\t\(his.delayDisplay)"
let attributed = NSMutableAttributedString( let attributed = NSMutableAttributedString(
string: str, string: str,

View File

@ -1,3 +1,3 @@
module github.com/yichengchen/clashX/ClashX 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

View File

@ -1,5 +1,5 @@
github.com/Dreamacro/clash v0.12.1-0.20190317065239-7a9d986ff38c h1:SRELShfh3m6DJxlnNqKdhAPNY2eXUvFZNsUtUlTrkzk= github.com/Dreamacro/clash v0.12.1-0.20190323082927-f99da37168ac h1:9WV1wjtHo9Gm9Dq1chmEjpJkefLC1ck64yzhd6aQXUA=
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/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 h1:1axYxE0ZLJy40+ulq46XQt7MaJDJr4iGer1NQz7jmKw=
github.com/Dreamacro/go-shadowsocks2 v0.1.3-0.20190202135136-da4602d8f112/go.mod h1:giIuN+TuUudTxHc1jjTOyyQYiJ3VXp1pWOHdJbSCAPo= 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= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=