Feature: show speedtest history in proxy group
This commit is contained in:
parent
670ad02b49
commit
61e87ed07c
|
@ -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(){}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}()
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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=
|
||||||
|
|
Loading…
Reference in New Issue