mirror of
https://github.com/morgan9e/macos-stats
synced 2026-04-14 00:04:15 +09:00
feat: show both public IPs: v4 and v6 (#307)
This commit is contained in:
@@ -27,12 +27,17 @@ public struct Network_interface {
|
||||
var address: String = ""
|
||||
}
|
||||
|
||||
public struct Network_addr {
|
||||
var v4: String? = nil
|
||||
var v6: String? = nil
|
||||
}
|
||||
|
||||
public struct Network_Usage: value_t {
|
||||
var bandwidth: Bandwidth = (0, 0)
|
||||
var total: Bandwidth = (0, 0)
|
||||
|
||||
var laddr: String? = nil // local ip
|
||||
var raddr: String? = nil // remote ip
|
||||
var raddr: Network_addr = Network_addr() // remote ip
|
||||
|
||||
var interface: Network_interface? = nil
|
||||
var connectionType: Network_t? = nil
|
||||
@@ -44,7 +49,7 @@ public struct Network_Usage: value_t {
|
||||
self.bandwidth = (0, 0)
|
||||
|
||||
self.laddr = nil
|
||||
self.raddr = nil
|
||||
self.raddr = Network_addr()
|
||||
|
||||
self.interface = nil
|
||||
self.connectionType = nil
|
||||
|
||||
@@ -13,19 +13,12 @@ import Cocoa
|
||||
import ModuleKit
|
||||
import StatsKit
|
||||
|
||||
internal class Popup: NSView, Popup_p {
|
||||
internal class Popup: NSStackView, Popup_p {
|
||||
public var sizeCallback: ((NSSize) -> Void)? = nil
|
||||
|
||||
private var store: UnsafePointer<Store>
|
||||
private var title: String
|
||||
|
||||
private var grid: NSGridView? = nil
|
||||
|
||||
private let dashboardHeight: CGFloat = 90
|
||||
private let chartHeight: CGFloat = 90 + Constants.Popup.separatorHeight
|
||||
private let detailsHeight: CGFloat = (22*7) + Constants.Popup.separatorHeight
|
||||
private let processHeight: CGFloat = 22
|
||||
|
||||
private var dashboardView: NSView? = nil
|
||||
|
||||
private var uploadView: NSView? = nil
|
||||
private var uploadValue: Int64 = 0
|
||||
private var uploadValueField: NSTextField? = nil
|
||||
@@ -38,7 +31,6 @@ internal class Popup: NSView, Popup_p {
|
||||
private var downloadUnitField: NSTextField? = nil
|
||||
private var downloadStateView: ColorView? = nil
|
||||
|
||||
private var publicIPField: ValueField? = nil
|
||||
private var localIPField: ValueField? = nil
|
||||
private var interfaceField: ValueField? = nil
|
||||
private var ssidField: ValueField? = nil
|
||||
@@ -46,6 +38,12 @@ internal class Popup: NSView, Popup_p {
|
||||
private var totalUploadField: ValueField? = nil
|
||||
private var totalDownloadField: ValueField? = nil
|
||||
|
||||
private var publicIPStackView: NSStackView? = nil
|
||||
private var publicIPv4Field: ValueField? = nil
|
||||
private var publicIPv6Field: ValueField? = nil
|
||||
|
||||
private var processesView: NSView? = nil
|
||||
|
||||
private var initialized: Bool = false
|
||||
private var processesInitialized: Bool = false
|
||||
|
||||
@@ -65,110 +63,90 @@ internal class Popup: NSView, Popup_p {
|
||||
private var processesHeight: CGFloat {
|
||||
get {
|
||||
let num = self.numberOfProcesses
|
||||
return (self.processHeight*CGFloat(num)) + (num == 0 ? 0 : Constants.Popup.separatorHeight)
|
||||
return (22*CGFloat(num)) + (num == 0 ? 0 : Constants.Popup.separatorHeight)
|
||||
}
|
||||
}
|
||||
|
||||
public var sizeCallback: ((NSSize) -> Void)? = nil
|
||||
|
||||
public init(_ title: String, store: UnsafePointer<Store>) {
|
||||
self.store = store
|
||||
self.title = title
|
||||
self.store = store
|
||||
|
||||
super.init(frame: NSRect(
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: Constants.Popup.width,
|
||||
height: self.dashboardHeight + self.chartHeight + self.detailsHeight
|
||||
height: 0
|
||||
))
|
||||
self.setFrameSize(NSSize(width: self.frame.width, height: self.frame.height+self.processesHeight))
|
||||
|
||||
let gridView: NSGridView = NSGridView(frame: NSRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height))
|
||||
gridView.rowSpacing = 0
|
||||
gridView.yPlacement = .fill
|
||||
self.spacing = 0
|
||||
self.orientation = .vertical
|
||||
|
||||
gridView.addRow(with: [self.initDashboard()])
|
||||
gridView.addRow(with: [self.initChart()])
|
||||
gridView.addRow(with: [self.initDetails()])
|
||||
gridView.addRow(with: [self.initProcesses()])
|
||||
self.addArrangedSubview(self.initDashboard())
|
||||
self.addArrangedSubview(self.initChart())
|
||||
self.addArrangedSubview(self.initDetails())
|
||||
self.addArrangedSubview(self.initPublicIP())
|
||||
self.addArrangedSubview(self.initProcesses())
|
||||
|
||||
gridView.row(at: 0).height = self.dashboardHeight
|
||||
gridView.row(at: 1).height = self.chartHeight
|
||||
gridView.row(at: 2).height = self.detailsHeight
|
||||
|
||||
self.addSubview(gridView)
|
||||
self.grid = gridView
|
||||
self.recalculateHeight()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public func numberOfProcessesUpdated() {
|
||||
if self.processes.count == self.numberOfProcesses {
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.processes = []
|
||||
|
||||
let h: CGFloat = self.dashboardHeight + self.chartHeight + self.detailsHeight + self.processesHeight
|
||||
private func recalculateHeight() {
|
||||
let h = self.arrangedSubviews.map({ $0.bounds.height }).reduce(0, +)
|
||||
if self.frame.size.height != h {
|
||||
self.setFrameSize(NSSize(width: self.frame.width, height: h))
|
||||
|
||||
self.grid?.setFrameSize(NSSize(width: self.frame.width, height: h))
|
||||
|
||||
self.grid?.row(at: 3).cell(at: 0).contentView?.removeFromSuperview()
|
||||
self.grid?.removeRow(at: 3)
|
||||
self.grid?.addRow(with: [self.initProcesses()])
|
||||
self.processesInitialized = false
|
||||
|
||||
self.sizeCallback?(self.frame.size)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - views
|
||||
|
||||
private func initDashboard() -> NSView {
|
||||
let view: NSView = NSView(frame: NSRect(x: 0, y: self.frame.height - self.dashboardHeight, width: self.frame.width, height: self.dashboardHeight))
|
||||
let container: NSView = NSView(frame: NSRect(x: 0, y: 0, width: view.frame.width, height: self.dashboardHeight))
|
||||
let view: NSView = NSView(frame: NSRect(x: 0, y: 0, width: self.frame.width, height: 90))
|
||||
view.heightAnchor.constraint(equalToConstant: view.bounds.height).isActive = true
|
||||
|
||||
let leftPart: NSView = NSView(frame: NSRect(x: 0, y: 0, width: container.frame.width / 2, height: container.frame.height))
|
||||
let leftPart: NSView = NSView(frame: NSRect(x: 0, y: 0, width: view.frame.width / 2, height: view.frame.height))
|
||||
let uploadFields = self.topValueView(leftPart, title: LocalizedString("Uploading"), color: NSColor.systemRed)
|
||||
self.uploadView = uploadFields.0
|
||||
self.uploadValueField = uploadFields.1
|
||||
self.uploadUnitField = uploadFields.2
|
||||
self.uploadStateView = uploadFields.3
|
||||
|
||||
let rightPart: NSView = NSView(frame: NSRect(x: container.frame.width / 2, y: 0, width: container.frame.width / 2, height: container.frame.height))
|
||||
let rightPart: NSView = NSView(frame: NSRect(x: view.frame.width / 2, y: 0, width: view.frame.width / 2, height: view.frame.height))
|
||||
let downloadFields = self.topValueView(rightPart, title: LocalizedString("Downloading"), color: NSColor.systemBlue)
|
||||
self.downloadView = downloadFields.0
|
||||
self.downloadValueField = downloadFields.1
|
||||
self.downloadUnitField = downloadFields.2
|
||||
self.downloadStateView = downloadFields.3
|
||||
|
||||
container.addSubview(leftPart)
|
||||
container.addSubview(rightPart)
|
||||
|
||||
view.addSubview(container)
|
||||
self.dashboardView = container
|
||||
view.addSubview(leftPart)
|
||||
view.addSubview(rightPart)
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
private func initChart() -> NSView {
|
||||
let view: NSView = NSView(frame: NSRect(x: 0, y: 0, width: self.frame.width, height: self.chartHeight))
|
||||
let separator = SeparatorView(LocalizedString("Usage history"), origin: NSPoint(x: 0, y: self.chartHeight-Constants.Popup.separatorHeight), width: self.frame.width)
|
||||
let view: NSView = NSView(frame: NSRect(x: 0, y: 0, width: self.frame.width, height: 90 + Constants.Popup.separatorHeight))
|
||||
view.heightAnchor.constraint(equalToConstant: view.bounds.height).isActive = true
|
||||
|
||||
let separator = SeparatorView(LocalizedString("Usage history"), origin: NSPoint(x: 0, y: 90), width: self.frame.width)
|
||||
let container: NSView = NSView(frame: NSRect(x: 0, y: 0, width: self.frame.width, height: separator.frame.origin.y))
|
||||
container.wantsLayer = true
|
||||
container.layer?.backgroundColor = NSColor.lightGray.withAlphaComponent(0.1).cgColor
|
||||
container.layer?.cornerRadius = 3
|
||||
|
||||
self.chart = NetworkChartView(frame: NSRect(
|
||||
let chart = NetworkChartView(frame: NSRect(
|
||||
x: 0,
|
||||
y: 1,
|
||||
width: container.frame.width,
|
||||
height: container.frame.height - 2
|
||||
), num: 120)
|
||||
self.chart?.base = self.base
|
||||
container.addSubview(self.chart!)
|
||||
chart.base = self.base
|
||||
container.addSubview(chart)
|
||||
self.chart = chart
|
||||
|
||||
view.addSubview(separator)
|
||||
view.addSubview(container)
|
||||
@@ -177,22 +155,23 @@ internal class Popup: NSView, Popup_p {
|
||||
}
|
||||
|
||||
private func initDetails() -> NSView {
|
||||
let view: NSView = NSView(frame: NSRect(x: 0, y: 0, width: self.frame.width, height: self.detailsHeight))
|
||||
let separator = SeparatorView(LocalizedString("Details"), origin: NSPoint(x: 0, y: self.detailsHeight-Constants.Popup.separatorHeight), width: self.frame.width)
|
||||
let height: CGFloat = 22*6
|
||||
let view: NSView = NSView(frame: NSRect(x: 0, y: 0, width: self.frame.width, height: height + Constants.Popup.separatorHeight))
|
||||
view.heightAnchor.constraint(equalToConstant: view.bounds.height).isActive = true
|
||||
|
||||
let separator = SeparatorView(LocalizedString("Details"), origin: NSPoint(x: 0, y: height), width: self.frame.width)
|
||||
let container: NSView = NSView(frame: NSRect(x: 0, y: 0, width: self.frame.width, height: separator.frame.origin.y))
|
||||
|
||||
self.totalUploadField = PopupWithColorRow(container, color: NSColor.systemRed, n: 6, title: "\(LocalizedString("Total upload")):", value: "")
|
||||
self.totalDownloadField = PopupWithColorRow(container, color: NSColor.systemBlue, n: 5, title: "\(LocalizedString("Total download")):", value: "")
|
||||
self.totalUploadField = PopupWithColorRow(container, color: NSColor.systemRed, n: 5, title: "\(LocalizedString("Total upload")):", value: "0")
|
||||
self.totalDownloadField = PopupWithColorRow(container, color: NSColor.systemBlue, n: 4, title: "\(LocalizedString("Total download")):", value: "0")
|
||||
|
||||
self.publicIPField = PopupRow(container, n: 4, title: "\(LocalizedString("Public IP")):", value: "").1
|
||||
self.localIPField = PopupRow(container, n: 3, title: "\(LocalizedString("Local IP")):", value: "").1
|
||||
self.interfaceField = PopupRow(container, n: 2, title: "\(LocalizedString("Interface")):", value: "").1
|
||||
self.ssidField = PopupRow(container, n: 1, title: "\(LocalizedString("Network")):", value: "").1
|
||||
self.macAdressField = PopupRow(container, n: 0, title: "\(LocalizedString("Physical address")):", value: "").1
|
||||
self.interfaceField = PopupRow(container, n: 3, title: "\(LocalizedString("Interface")):", value: LocalizedString("Unknown")).1
|
||||
self.ssidField = PopupRow(container, n: 2, title: "\(LocalizedString("Network")):", value: LocalizedString("Unknown")).1
|
||||
self.macAdressField = PopupRow(container, n: 1, title: "\(LocalizedString("Physical address")):", value: LocalizedString("Unknown")).1
|
||||
self.localIPField = PopupRow(container, n: 0, title: "\(LocalizedString("Local IP")):", value: LocalizedString("Unknown")).1
|
||||
|
||||
self.publicIPField?.toolTip = LocalizedString("Click to copy public IP address")
|
||||
self.localIPField?.toolTip = LocalizedString("Click to copy local IP address")
|
||||
self.macAdressField?.toolTip = LocalizedString("Click to copy mac address")
|
||||
self.localIPField?.isSelectable = true
|
||||
self.macAdressField?.isSelectable = true
|
||||
|
||||
view.addSubview(separator)
|
||||
view.addSubview(container)
|
||||
@@ -200,6 +179,36 @@ internal class Popup: NSView, Popup_p {
|
||||
return view
|
||||
}
|
||||
|
||||
private func initPublicIP() -> NSView {
|
||||
let view: NSView = NSView(frame: NSRect(x: 0, y: 0, width: self.frame.width, height: 0))
|
||||
let container: NSStackView = NSStackView(frame: NSRect(x: 0, y: 0, width: view.frame.width, height: 0))
|
||||
container.orientation = .vertical
|
||||
container.spacing = 0
|
||||
|
||||
container.addArrangedSubview(SeparatorView(LocalizedString("Public IP"), origin: NSPoint(x: 0, y: 0), width: self.frame.width))
|
||||
self.publicIPv4Field = PopupRow(container, title: "\(LocalizedString("v4")):", value: LocalizedString("Unknown")).1
|
||||
self.publicIPv6Field = PopupRow(container, title: "\(LocalizedString("v6")):", value: LocalizedString("Unknown")).1
|
||||
|
||||
self.publicIPv4Field?.isSelectable = true
|
||||
if let valueView = self.publicIPv6Field {
|
||||
valueView.isSelectable = true
|
||||
valueView.font = NSFont.systemFont(ofSize: 10, weight: .regular)
|
||||
valueView.setFrameOrigin(NSPoint(x: valueView.frame.origin.x, y: 1))
|
||||
}
|
||||
|
||||
view.addSubview(container)
|
||||
|
||||
let h = container.arrangedSubviews.map({ $0.bounds.height }).reduce(0, +)
|
||||
view.setFrameSize(NSSize(width: self.frame.width, height: h))
|
||||
container.setFrameSize(NSSize(width: self.frame.width, height: view.bounds.height))
|
||||
view.heightAnchor.constraint(equalToConstant: view.bounds.height).isActive = true
|
||||
container.heightAnchor.constraint(equalToConstant: view.bounds.height).isActive = true
|
||||
|
||||
self.publicIPStackView = container
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
private func initProcesses() -> NSView {
|
||||
let view: NSView = NSView(frame: NSRect(x: 0, y: 0, width: self.frame.width, height: self.processesHeight))
|
||||
let separator = SeparatorView(LocalizedString("Top processes"), origin: NSPoint(x: 0, y: self.processesHeight-Constants.Popup.separatorHeight), width: self.frame.width)
|
||||
@@ -214,9 +223,111 @@ internal class Popup: NSView, Popup_p {
|
||||
view.addSubview(separator)
|
||||
view.addSubview(container)
|
||||
|
||||
self.processesView = view
|
||||
return view
|
||||
}
|
||||
|
||||
// MARK: - callbacks
|
||||
|
||||
public func numberOfProcessesUpdated() {
|
||||
if self.processes.count == self.numberOfProcesses {
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.processes = []
|
||||
|
||||
if let view = self.processesView {
|
||||
self.removeView(view)
|
||||
}
|
||||
self.addArrangedSubview(self.initProcesses())
|
||||
self.processesInitialized = false
|
||||
|
||||
self.recalculateHeight()
|
||||
})
|
||||
}
|
||||
|
||||
public func usageCallback(_ value: Network_Usage) {
|
||||
DispatchQueue.main.async(execute: {
|
||||
if (self.window?.isVisible ?? false) || !self.initialized {
|
||||
self.uploadValue = value.bandwidth.upload
|
||||
self.downloadValue = value.bandwidth.download
|
||||
self.setUploadDownloadFields()
|
||||
|
||||
self.totalUploadField?.stringValue = Units(bytes: value.total.upload).getReadableMemory()
|
||||
self.totalDownloadField?.stringValue = Units(bytes: value.total.download).getReadableMemory()
|
||||
|
||||
if let interface = value.interface {
|
||||
self.interfaceField?.stringValue = "\(interface.displayName) (\(interface.BSDName))"
|
||||
self.macAdressField?.stringValue = interface.address
|
||||
} else {
|
||||
self.interfaceField?.stringValue = LocalizedString("Unknown")
|
||||
self.macAdressField?.stringValue = LocalizedString("Unknown")
|
||||
}
|
||||
|
||||
if value.connectionType == .wifi {
|
||||
self.ssidField?.stringValue = value.ssid ?? "Unknown"
|
||||
} else {
|
||||
self.ssidField?.stringValue = LocalizedString("Unavailable")
|
||||
}
|
||||
|
||||
if let view = self.publicIPv4Field, view.stringValue != value.raddr.v4 {
|
||||
if let addr = value.raddr.v4 {
|
||||
view.stringValue = (value.countryCode != nil) ? "\(addr) (\(value.countryCode!))" : addr
|
||||
} else {
|
||||
view.stringValue = LocalizedString("Unknown")
|
||||
}
|
||||
}
|
||||
if let view = self.publicIPv6Field, view.stringValue != value.raddr.v6 {
|
||||
if let addr = value.raddr.v6 {
|
||||
view.stringValue = addr
|
||||
} else {
|
||||
view.stringValue = LocalizedString("Unknown")
|
||||
}
|
||||
}
|
||||
|
||||
if self.localIPField?.stringValue != value.laddr {
|
||||
self.localIPField?.stringValue = value.laddr ?? LocalizedString("Unknown")
|
||||
}
|
||||
|
||||
self.initialized = true
|
||||
}
|
||||
|
||||
if let chart = self.chart {
|
||||
if chart.base != self.base {
|
||||
chart.base = self.base
|
||||
}
|
||||
chart.addValue(upload: Double(value.bandwidth.upload), download: Double(value.bandwidth.download))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public func processCallback(_ list: [Network_Process]) {
|
||||
DispatchQueue.main.async(execute: {
|
||||
if !(self.window?.isVisible ?? false) && self.processesInitialized {
|
||||
return
|
||||
}
|
||||
|
||||
if list.count != self.processes.count {
|
||||
self.processes.forEach { processView in
|
||||
processView.clear()
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..<list.count {
|
||||
let process = list[i]
|
||||
let index = list.count-i-1
|
||||
self.processes[index].attachProcess(process)
|
||||
self.processes[index].upload = Units(bytes: Int64(process.upload)).getReadableSpeed(base: self.base)
|
||||
self.processes[index].download = Units(bytes: Int64(process.download)).getReadableSpeed(base: self.base)
|
||||
}
|
||||
|
||||
self.processesInitialized = true
|
||||
})
|
||||
}
|
||||
|
||||
// MARK: - helpers
|
||||
|
||||
private func topValueView(_ view: NSView, title: String, color: NSColor) -> (NSView, NSTextField, NSTextField, ColorView) {
|
||||
let topHeight: CGFloat = 30
|
||||
let titleHeight: CGFloat = 15
|
||||
@@ -301,96 +412,6 @@ internal class Popup: NSView, Popup_p {
|
||||
self.uploadStateView?.setState(self.uploadValue != 0)
|
||||
self.downloadStateView?.setState(self.downloadValue != 0)
|
||||
}
|
||||
|
||||
public func usageCallback(_ value: Network_Usage) {
|
||||
DispatchQueue.main.async(execute: {
|
||||
if (self.window?.isVisible ?? false) || !self.initialized {
|
||||
self.uploadValue = value.bandwidth.upload
|
||||
self.downloadValue = value.bandwidth.download
|
||||
self.setUploadDownloadFields()
|
||||
|
||||
self.totalUploadField?.stringValue = Units(bytes: value.total.upload).getReadableMemory()
|
||||
self.totalDownloadField?.stringValue = Units(bytes: value.total.download).getReadableMemory()
|
||||
|
||||
if let interface = value.interface {
|
||||
self.interfaceField?.stringValue = "\(interface.displayName) (\(interface.BSDName))"
|
||||
self.macAdressField?.stringValue = interface.address
|
||||
} else {
|
||||
self.interfaceField?.stringValue = LocalizedString("Unknown")
|
||||
self.macAdressField?.stringValue = LocalizedString("Unknown")
|
||||
}
|
||||
|
||||
if value.connectionType == .wifi {
|
||||
self.ssidField?.stringValue = value.ssid ?? "Unknown"
|
||||
} else {
|
||||
self.ssidField?.stringValue = LocalizedString("Unavailable")
|
||||
}
|
||||
|
||||
if self.publicIPField?.stringValue != value.raddr {
|
||||
if value.raddr == nil {
|
||||
self.publicIPField?.stringValue = LocalizedString("Unknown")
|
||||
} else {
|
||||
if value.countryCode == nil {
|
||||
self.publicIPField?.stringValue = value.raddr!
|
||||
} else {
|
||||
self.publicIPField?.stringValue = "\(value.raddr!) (\(value.countryCode!))"
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.localIPField?.stringValue != value.laddr {
|
||||
self.localIPField?.stringValue = value.laddr ?? LocalizedString("Unknown")
|
||||
}
|
||||
|
||||
self.initialized = true
|
||||
}
|
||||
|
||||
if let chart = self.chart {
|
||||
if chart.base != self.base {
|
||||
chart.base = self.base
|
||||
}
|
||||
chart.addValue(upload: Double(value.bandwidth.upload), download: Double(value.bandwidth.download))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public func processCallback(_ list: [Network_Process]) {
|
||||
DispatchQueue.main.async(execute: {
|
||||
if !(self.window?.isVisible ?? false) && self.processesInitialized {
|
||||
return
|
||||
}
|
||||
|
||||
if list.count != self.processes.count {
|
||||
self.processes.forEach { processView in
|
||||
processView.clear()
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..<list.count {
|
||||
let process = list[i]
|
||||
let index = list.count-i-1
|
||||
self.processes[index].attachProcess(process)
|
||||
self.processes[index].upload = Units(bytes: Int64(process.upload)).getReadableSpeed(base: self.base)
|
||||
self.processes[index].download = Units(bytes: Int64(process.download)).getReadableSpeed(base: self.base)
|
||||
}
|
||||
|
||||
self.processesInitialized = true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
extension ValueField {
|
||||
public override func mouseDown(with: NSEvent) {
|
||||
guard self.stringValue != LocalizedString("No connection") && self.stringValue != LocalizedString("Unknown") && self.stringValue != LocalizedString("Unavailable") && self.toolTip != nil else {
|
||||
return
|
||||
}
|
||||
|
||||
let arr = self.stringValue.split(separator: " ")
|
||||
let value: String = arr.count > 0 ? String(arr[0]) : self.stringValue
|
||||
|
||||
let pasteboard = NSPasteboard.general
|
||||
pasteboard.declareTypes([.string], owner: nil)
|
||||
pasteboard.setString(value, forType: .string)
|
||||
}
|
||||
}
|
||||
|
||||
public class NetworkProcessView: NSView {
|
||||
|
||||
@@ -229,14 +229,23 @@ internal class UsageReader: Reader<Network_Usage> {
|
||||
}
|
||||
|
||||
private func getPublicIP() {
|
||||
let url = URL(string: "https://api.ipify.org")
|
||||
|
||||
do {
|
||||
if let url = url {
|
||||
self.usage.raddr = try String(contentsOf: url)
|
||||
if let url = URL(string: "https://api.ipify.org") {
|
||||
self.usage.raddr.v4 = try String(contentsOf: url)
|
||||
}
|
||||
} catch let error {
|
||||
os_log(.error, log: log, "get public ip %s", "\(error)")
|
||||
os_log(.error, log: log, "get public ipv4 %s", "\(error)")
|
||||
}
|
||||
|
||||
do {
|
||||
if let url = URL(string: "https://api64.ipify.org") {
|
||||
let v6 = try String(contentsOf: url)
|
||||
if self.usage.raddr.v4 != v6 {
|
||||
self.usage.raddr.v6 = v6
|
||||
}
|
||||
}
|
||||
} catch let error {
|
||||
os_log(.error, log: log, "get public ipv6 %s", "\(error)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -316,6 +316,7 @@ public extension NSBezierPath {
|
||||
|
||||
public func SeparatorView(_ title: String, origin: NSPoint, width: CGFloat) -> NSView {
|
||||
let view: NSView = NSView(frame: NSRect(x: origin.x, y: origin.y, width: width, height: 30))
|
||||
view.heightAnchor.constraint(equalToConstant: view.bounds.height).isActive = true
|
||||
|
||||
let labelView: NSTextField = TextView(frame: NSRect(x: 0, y: (view.frame.height-15)/2, width: view.frame.width, height: 15))
|
||||
labelView.stringValue = title
|
||||
@@ -328,16 +329,22 @@ public func SeparatorView(_ title: String, origin: NSPoint, width: CGFloat) -> N
|
||||
return view
|
||||
}
|
||||
|
||||
public func PopupRow(_ view: NSView, n: CGFloat, title: String, value: String) -> (LabelField, ValueField) {
|
||||
public func PopupRow(_ view: NSView, n: CGFloat = 0, title: String, value: String) -> (LabelField, ValueField) {
|
||||
let rowView: NSView = NSView(frame: NSRect(x: 0, y: 22*n, width: view.frame.width, height: 22))
|
||||
|
||||
let labelWidth = title.widthOfString(usingFont: .systemFont(ofSize: 13, weight: .regular)) + 5
|
||||
let labelWidth = title.widthOfString(usingFont: .systemFont(ofSize: 13, weight: .regular)) + 4
|
||||
let labelView: LabelField = LabelField(frame: NSRect(x: 0, y: (22-15)/2, width: labelWidth, height: 15), title)
|
||||
let valueView: ValueField = ValueField(frame: NSRect(x: labelWidth, y: (22-15)/2, width: rowView.frame.width - labelWidth, height: 16), value)
|
||||
|
||||
rowView.addSubview(labelView)
|
||||
rowView.addSubview(valueView)
|
||||
view.addSubview(rowView)
|
||||
|
||||
if let view = view as? NSStackView {
|
||||
rowView.heightAnchor.constraint(equalToConstant: rowView.bounds.height).isActive = true
|
||||
view.addArrangedSubview(rowView)
|
||||
} else {
|
||||
view.addSubview(rowView)
|
||||
}
|
||||
|
||||
return (labelView, valueView)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user