mirror of
https://github.com/morgan9e/macos-stats
synced 2026-04-14 00:04:15 +09:00
feat: moved all popups to new ProcessesView for top processes
This commit is contained in:
@@ -49,19 +49,16 @@ internal class Popup: PopupWrapper {
|
||||
private var powerField: NSTextField? = nil
|
||||
private var chargingStateField: NSTextField? = nil
|
||||
|
||||
private var processes: [ProcessView] = []
|
||||
private var processes: ProcessesView? = nil
|
||||
private var processesInitialized: Bool = false
|
||||
|
||||
private var colorState: Bool = false
|
||||
|
||||
private var numberOfProcesses: Int {
|
||||
return Store.shared.int(key: "\(self.title)_processes", defaultValue: 8)
|
||||
Store.shared.int(key: "\(self.title)_processes", defaultValue: 8)
|
||||
}
|
||||
private var processesHeight: CGFloat {
|
||||
get {
|
||||
let num = self.numberOfProcesses
|
||||
return (self.processHeight*CGFloat(num)) + (num == 0 ? 0 : Constants.Popup.separatorHeight)
|
||||
}
|
||||
(self.processHeight*CGFloat(self.numberOfProcesses)) + (self.numberOfProcesses == 0 ? 0 : Constants.Popup.separatorHeight + 22)
|
||||
}
|
||||
private var timeFormat: String {
|
||||
Store.shared.string(key: "\(self.title)_timeFormat", defaultValue: "short")
|
||||
@@ -104,23 +101,20 @@ internal class Popup: PopupWrapper {
|
||||
}
|
||||
|
||||
public override func disappear() {
|
||||
self.processes.forEach{ $0.setLock(false) }
|
||||
self.processes?.setLock(false)
|
||||
}
|
||||
|
||||
public func numberOfProcessesUpdated() {
|
||||
if self.processes.count == self.numberOfProcesses {
|
||||
return
|
||||
}
|
||||
if self.processes?.count == self.numberOfProcesses { return }
|
||||
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.processes = []
|
||||
|
||||
let h: CGFloat = self.dashboardHeight + self.detailsHeight + self.batteryHeight + self.adapterHeight + self.processesHeight
|
||||
self.setFrameSize(NSSize(width: self.frame.width, height: h))
|
||||
|
||||
self.grid?.setFrameSize(NSSize(width: self.frame.width, height: h))
|
||||
|
||||
self.grid?.row(at: 4).cell(at: 0).contentView?.removeFromSuperview()
|
||||
self.processes = nil
|
||||
self.grid?.removeRow(at: 4)
|
||||
self.grid?.addRow(with: [self.initProcesses()])
|
||||
self.processesInitialized = false
|
||||
@@ -203,17 +197,16 @@ internal class Popup: PopupWrapper {
|
||||
}
|
||||
|
||||
private func initProcesses() -> NSView {
|
||||
if self.numberOfProcesses == 0 { return 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)
|
||||
let container: NSStackView = NSStackView(frame: NSRect(x: 0, y: 0, width: self.frame.width, height: separator.frame.origin.y))
|
||||
container.orientation = .vertical
|
||||
container.spacing = 0
|
||||
|
||||
for _ in 0..<self.numberOfProcesses {
|
||||
let processView = ProcessView()
|
||||
self.processes.append(processView)
|
||||
container.addArrangedSubview(processView)
|
||||
}
|
||||
let container: ProcessesView = ProcessesView(
|
||||
frame: NSRect(x: 0, y: 0, width: self.frame.width, height: separator.frame.origin.y),
|
||||
values: [(localizedString("Usage"), nil)],
|
||||
n: self.numberOfProcesses
|
||||
)
|
||||
self.processes = container
|
||||
|
||||
view.addSubview(separator)
|
||||
view.addSubview(container)
|
||||
@@ -318,15 +311,12 @@ internal class Popup: PopupWrapper {
|
||||
if !(self.window?.isVisible ?? false) && self.processesInitialized {
|
||||
return
|
||||
}
|
||||
|
||||
if list.count != self.processes.count {
|
||||
self.processes.forEach { processView in
|
||||
processView.clear()
|
||||
}
|
||||
}
|
||||
let list = list.map { $0 }
|
||||
if list.count != self.processes?.count { self.processes?.clear() }
|
||||
|
||||
for i in 0..<list.count {
|
||||
self.processes[i].set(list[i], "\(list[i].usage)%")
|
||||
let process = list[i]
|
||||
self.processes?.set(i, process, ["\(process.usage)%"])
|
||||
}
|
||||
|
||||
self.processesInitialized = true
|
||||
|
||||
@@ -66,7 +66,7 @@ internal class Popup: PopupWrapper {
|
||||
private var initializedLimits: Bool = false
|
||||
private var initializedAverage: Bool = false
|
||||
|
||||
private var processes: [ProcessView] = []
|
||||
private var processes: ProcessesView? = nil
|
||||
private var maxFreq: Double = 0
|
||||
|
||||
private var systemColorState: Color = .secondRed
|
||||
@@ -119,15 +119,10 @@ internal class Popup: PopupWrapper {
|
||||
}
|
||||
|
||||
private var numberOfProcesses: Int {
|
||||
get {
|
||||
return Store.shared.int(key: "\(self.title)_processes", defaultValue: 8)
|
||||
}
|
||||
Store.shared.int(key: "\(self.title)_processes", defaultValue: 8)
|
||||
}
|
||||
private var processesHeight: CGFloat {
|
||||
get {
|
||||
let num = self.numberOfProcesses
|
||||
return (self.processHeight*CGFloat(num)) + (num == 0 ? 0 : Constants.Popup.separatorHeight)
|
||||
}
|
||||
(self.processHeight*CGFloat(self.numberOfProcesses)) + (self.numberOfProcesses == 0 ? 0 : Constants.Popup.separatorHeight + 22)
|
||||
}
|
||||
|
||||
public init(_ title: String) {
|
||||
@@ -176,23 +171,20 @@ internal class Popup: PopupWrapper {
|
||||
}
|
||||
|
||||
public override func disappear() {
|
||||
self.processes.forEach{ $0.setLock(false) }
|
||||
self.processes?.setLock(false)
|
||||
}
|
||||
|
||||
public func numberOfProcessesUpdated() {
|
||||
if self.processes.count == self.numberOfProcesses {
|
||||
return
|
||||
}
|
||||
if self.processes?.count == self.numberOfProcesses { return }
|
||||
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.processes = []
|
||||
|
||||
let h: CGFloat = self.dashboardHeight + self.chartHeight + self.detailsHeight + self.averageHeight + self.processesHeight
|
||||
self.setFrameSize(NSSize(width: self.frame.width, height: h))
|
||||
|
||||
self.grid?.setFrameSize(NSSize(width: self.frame.width, height: h))
|
||||
|
||||
self.grid?.row(at: 4).cell(at: 0).contentView?.removeFromSuperview()
|
||||
self.processes = nil
|
||||
self.grid?.removeRow(at: 4)
|
||||
self.grid?.addRow(with: [self.initProcesses()])
|
||||
self.initializedProcesses = false
|
||||
@@ -329,17 +321,16 @@ internal class Popup: PopupWrapper {
|
||||
}
|
||||
|
||||
private func initProcesses() -> NSView {
|
||||
if self.numberOfProcesses == 0 { return 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)
|
||||
let container: NSStackView = NSStackView(frame: NSRect(x: 0, y: 0, width: self.frame.width, height: separator.frame.origin.y))
|
||||
container.orientation = .vertical
|
||||
container.spacing = 0
|
||||
|
||||
for _ in 0..<self.numberOfProcesses {
|
||||
let processView = ProcessView(valueSize: 60)
|
||||
self.processes.append(processView)
|
||||
container.addArrangedSubview(processView)
|
||||
}
|
||||
let container: ProcessesView = ProcessesView(
|
||||
frame: NSRect(x: 0, y: 0, width: self.frame.width, height: separator.frame.origin.y),
|
||||
values: [(localizedString("Usage"), nil)],
|
||||
n: self.numberOfProcesses
|
||||
)
|
||||
self.processes = container
|
||||
|
||||
view.addSubview(separator)
|
||||
view.addSubview(container)
|
||||
@@ -426,15 +417,12 @@ internal class Popup: PopupWrapper {
|
||||
if !(self.window?.isVisible ?? false) && self.initializedProcesses {
|
||||
return
|
||||
}
|
||||
|
||||
if list.count != self.processes.count {
|
||||
self.processes.forEach { processView in
|
||||
processView.clear()
|
||||
}
|
||||
}
|
||||
let list = list.map { $0 }
|
||||
if list.count != self.processes?.count { self.processes?.clear() }
|
||||
|
||||
for i in 0..<list.count {
|
||||
self.processes[i].set(list[i], "\(list[i].usage)%")
|
||||
let process = list[i]
|
||||
self.processes?.set(i, process, ["\(process.usage)%"])
|
||||
}
|
||||
|
||||
self.initializedProcesses = true
|
||||
|
||||
@@ -175,13 +175,6 @@ public struct Disk_process: Process_p, Codable {
|
||||
var read: Int
|
||||
var write: Int
|
||||
|
||||
public var input: String {
|
||||
Units(bytes: Int64(self.read)).getReadableSpeed(base: self.base)
|
||||
}
|
||||
public var output: String {
|
||||
Units(bytes: Int64(self.write)).getReadableSpeed(base: self.base)
|
||||
}
|
||||
|
||||
init(pid: Int, name: String, read: Int, write: Int) {
|
||||
self.pid = pid
|
||||
self.name = name
|
||||
|
||||
@@ -13,6 +13,8 @@ import Cocoa
|
||||
import Kit
|
||||
|
||||
internal class Popup: PopupWrapper {
|
||||
private var title: String
|
||||
|
||||
private var readColorState: Color = .secondBlue
|
||||
private var readColor: NSColor {
|
||||
var value = NSColor.systemRed
|
||||
@@ -36,13 +38,21 @@ internal class Popup: PopupWrapper {
|
||||
view.orientation = .vertical
|
||||
return view
|
||||
}()
|
||||
private var processes: IOProcessView = IOProcessView(
|
||||
countKey: "\(Disk.name)_processes",
|
||||
inputColorKey: "\(Disk.name)_readColor",
|
||||
outputColorKey: "\(Disk.name)_writeColor"
|
||||
)
|
||||
|
||||
private var processesInitialized: Bool = false
|
||||
|
||||
private var numberOfProcesses: Int {
|
||||
Store.shared.int(key: "\(self.title)_processes", defaultValue: 8)
|
||||
}
|
||||
private var processesHeight: CGFloat {
|
||||
(22*CGFloat(self.numberOfProcesses)) + (self.numberOfProcesses == 0 ? 0 : Constants.Popup.separatorHeight + 22)
|
||||
}
|
||||
private var processes: ProcessesView? = nil
|
||||
private var processesView: NSView? = nil
|
||||
|
||||
public init() {
|
||||
self.title = Disk.name
|
||||
|
||||
super.init(frame: NSRect(x: 0, y: 0, width: Constants.Popup.width, height: 0))
|
||||
|
||||
self.readColorState = Color.fromString(Store.shared.string(key: "\(Disk.name)_readColor", defaultValue: self.readColorState.key))
|
||||
@@ -53,7 +63,7 @@ internal class Popup: PopupWrapper {
|
||||
self.spacing = 0
|
||||
|
||||
self.addArrangedSubview(self.disks)
|
||||
self.addArrangedSubview(self.processes)
|
||||
self.addArrangedSubview(self.initProcesses())
|
||||
|
||||
self.recalculateHeight()
|
||||
}
|
||||
@@ -70,6 +80,27 @@ internal class Popup: PopupWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
private func initProcesses() -> NSView {
|
||||
if self.numberOfProcesses == 0 {
|
||||
let v = NSView()
|
||||
self.processesView = v
|
||||
return v
|
||||
}
|
||||
|
||||
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)
|
||||
let container: ProcessesView = ProcessesView(
|
||||
frame: NSRect(x: 0, y: 0, width: self.frame.width, height: separator.frame.origin.y),
|
||||
values: [(localizedString("Read"), self.readColor), (localizedString("Write"), self.writeColor)],
|
||||
n: self.numberOfProcesses
|
||||
)
|
||||
self.processes = container
|
||||
view.addSubview(separator)
|
||||
view.addSubview(container)
|
||||
self.processesView = view
|
||||
return view
|
||||
}
|
||||
|
||||
internal func capacityCallback(_ value: Disks) {
|
||||
defer {
|
||||
let h = self.disks.subviews.map({ $0.bounds.height + self.disks.spacing }).reduce(0, +) - self.disks.spacing
|
||||
@@ -111,13 +142,38 @@ internal class Popup: PopupWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - callbacks
|
||||
|
||||
internal func processCallback(_ list: [Disk_process]) {
|
||||
self.processes.update(list)
|
||||
DispatchQueue.main.async(execute: {
|
||||
if !(self.window?.isVisible ?? false) && self.processesInitialized {
|
||||
return
|
||||
}
|
||||
let list = list.map{ $0 }
|
||||
if list.count != self.processes?.count { self.processes?.clear("-") }
|
||||
|
||||
for i in 0..<list.count {
|
||||
let process = list[i]
|
||||
let write = Units(bytes: Int64(process.write)).getReadableSpeed(base: process.base)
|
||||
let read = Units(bytes: Int64(process.read)).getReadableSpeed(base: process.base)
|
||||
self.processes?.set(i, process, [read, write])
|
||||
}
|
||||
|
||||
self.processesInitialized = true
|
||||
})
|
||||
}
|
||||
|
||||
internal func numberOfProcessesUpdated() {
|
||||
self.processes.reinit()
|
||||
self.recalculateHeight()
|
||||
if self.processes?.count == self.numberOfProcesses { return }
|
||||
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.processesView?.removeFromSuperview()
|
||||
self.processesView = nil
|
||||
self.processes = nil
|
||||
self.addArrangedSubview(self.initProcesses())
|
||||
self.processesInitialized = false
|
||||
self.recalculateHeight()
|
||||
})
|
||||
}
|
||||
|
||||
// MARK: - Settings
|
||||
@@ -150,11 +206,11 @@ internal class Popup: PopupWrapper {
|
||||
self.writeColorState = newValue
|
||||
Store.shared.set(key: "\(Disk.name)_writeColor", value: key)
|
||||
if let color = newValue.additional as? NSColor {
|
||||
self.processes?.setColor(1, color)
|
||||
for view in self.disks.subviews.filter({ $0 is DiskView }).map({ $0 as! DiskView }) {
|
||||
view.setChartColor(write: color)
|
||||
}
|
||||
}
|
||||
self.processes.updateColors()
|
||||
}
|
||||
@objc private func toggleReadColor(_ sender: NSMenuItem) {
|
||||
guard let key = sender.representedObject as? String,
|
||||
@@ -164,11 +220,11 @@ internal class Popup: PopupWrapper {
|
||||
self.readColorState = newValue
|
||||
Store.shared.set(key: "\(Disk.name)_readColor", value: key)
|
||||
if let color = newValue.additional as? NSColor {
|
||||
self.processes?.setColor(0, color)
|
||||
for view in self.disks.subviews.filter({ $0 is DiskView }).map({ $0 as! DiskView }) {
|
||||
view.setChartColor(read: color)
|
||||
}
|
||||
}
|
||||
self.processes.updateColors()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -676,192 +732,3 @@ internal class LifeView: NSStackView {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public protocol IOProcess_p {
|
||||
var pid: Int32 { get }
|
||||
var name: String { get }
|
||||
var icon: NSImage { get }
|
||||
|
||||
var input: String { get }
|
||||
var output: String { get }
|
||||
}
|
||||
|
||||
public class IOProcessView: NSStackView {
|
||||
private let countKey: String
|
||||
private let inputColorKey: String
|
||||
private let outputColorKey: String
|
||||
|
||||
private var initialized: Bool = false
|
||||
|
||||
private var numberOfProcesses: Int {
|
||||
Store.shared.int(key: countKey, defaultValue: 5)
|
||||
}
|
||||
private var readColor: NSColor {
|
||||
Color.fromString(Store.shared.string(key: inputColorKey, defaultValue: Color.secondBlue.key)).additional as! NSColor
|
||||
}
|
||||
private var writeColor: NSColor {
|
||||
Color.fromString(Store.shared.string(key: outputColorKey, defaultValue: Color.secondRed.key)).additional as! NSColor
|
||||
}
|
||||
|
||||
private var inputBoxView: NSView?
|
||||
private var outputBoxView: NSView?
|
||||
|
||||
public var height: CGFloat {
|
||||
CGFloat((self.numberOfProcesses+1) * 22) + Constants.Popup.separatorHeight
|
||||
}
|
||||
|
||||
init(countKey: String, inputColorKey: String, outputColorKey: String) {
|
||||
self.countKey = countKey
|
||||
self.inputColorKey = inputColorKey
|
||||
self.outputColorKey = outputColorKey
|
||||
|
||||
super.init(frame: NSRect.zero)
|
||||
|
||||
self.orientation = .vertical
|
||||
self.spacing = 1
|
||||
|
||||
self.reinit()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public func reinit() {
|
||||
self.subviews.forEach({ $0.removeFromSuperview() })
|
||||
|
||||
if self.numberOfProcesses == 0 {
|
||||
self.setFrameSize(NSSize(width: self.frame.width, height: 0))
|
||||
return
|
||||
}
|
||||
|
||||
self.addArrangedSubview(separatorView(localizedString("Top processes"), width: Constants.Popup.width))
|
||||
self.addArrangedSubview(self.legendRow())
|
||||
for _ in 0..<self.numberOfProcesses {
|
||||
self.addArrangedSubview(TopProcess())
|
||||
}
|
||||
|
||||
self.setFrameSize(NSSize(width: self.frame.width, height: self.height))
|
||||
}
|
||||
|
||||
private func legendRow() -> NSView {
|
||||
let view: NSStackView = NSStackView()
|
||||
view.spacing = 50
|
||||
view.orientation = .horizontal
|
||||
view.heightAnchor.constraint(equalToConstant: 21).isActive = true
|
||||
|
||||
let inputView: NSView = NSView()
|
||||
inputView.widthAnchor.constraint(equalToConstant: 10).isActive = true
|
||||
inputView.heightAnchor.constraint(equalToConstant: 10).isActive = true
|
||||
inputView.wantsLayer = true
|
||||
inputView.layer?.backgroundColor = self.readColor.cgColor
|
||||
inputView.layer?.cornerRadius = 2
|
||||
self.inputBoxView = inputView
|
||||
|
||||
let outputView: NSView = NSView()
|
||||
outputView.widthAnchor.constraint(equalToConstant: 10).isActive = true
|
||||
outputView.heightAnchor.constraint(equalToConstant: 10).isActive = true
|
||||
outputView.wantsLayer = true
|
||||
outputView.layer?.backgroundColor = self.writeColor.cgColor
|
||||
outputView.layer?.cornerRadius = 2
|
||||
self.outputBoxView = outputView
|
||||
|
||||
view.addArrangedSubview(NSView())
|
||||
view.addArrangedSubview(inputView)
|
||||
view.addArrangedSubview(outputView)
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
public func update(_ list: [IOProcess_p]) {
|
||||
DispatchQueue.main.async(execute: {
|
||||
if !(self.window?.isVisible ?? false) && self.initialized {
|
||||
return
|
||||
}
|
||||
|
||||
for (i, p) in self.subviews.compactMap({ $0 as? TopProcess }).enumerated() {
|
||||
if list.count != self.numberOfProcesses && self.initialized {
|
||||
p.clear()
|
||||
}
|
||||
if list.indices.contains(i) {
|
||||
p.set(list[i])
|
||||
}
|
||||
}
|
||||
|
||||
self.initialized = true
|
||||
})
|
||||
}
|
||||
|
||||
public func updateColors() {
|
||||
self.inputBoxView?.layer?.backgroundColor = self.readColor.cgColor
|
||||
self.outputBoxView?.layer?.backgroundColor = self.writeColor.cgColor
|
||||
}
|
||||
}
|
||||
|
||||
public class TopProcess: NSStackView {
|
||||
private var imageView: NSImageView = NSImageView()
|
||||
private var labelView: NSTextField = LabelField()
|
||||
private var inputView: NSTextField = ValueField()
|
||||
private var outputView: NSTextField = ValueField()
|
||||
|
||||
init() {
|
||||
super.init(frame: NSRect.zero)
|
||||
|
||||
self.orientation = .horizontal
|
||||
self.spacing = 0
|
||||
self.alignment = .centerY
|
||||
self.layer?.cornerRadius = 3
|
||||
|
||||
self.labelView.cell?.truncatesLastVisibleLine = true
|
||||
self.inputView.font = NSFont.systemFont(ofSize: 10, weight: .regular)
|
||||
self.outputView.font = NSFont.systemFont(ofSize: 10, weight: .regular)
|
||||
|
||||
self.addArrangedSubview(self.imageView)
|
||||
self.addArrangedSubview(self.labelView)
|
||||
self.addArrangedSubview(NSView())
|
||||
self.addArrangedSubview(self.inputView)
|
||||
self.addArrangedSubview(self.outputView)
|
||||
|
||||
self.addTrackingArea(NSTrackingArea(
|
||||
rect: NSRect(x: 0, y: 0, width: 264, height: 21),
|
||||
options: [NSTrackingArea.Options.activeAlways, NSTrackingArea.Options.mouseEnteredAndExited, NSTrackingArea.Options.activeInActiveApp],
|
||||
owner: self,
|
||||
userInfo: nil
|
||||
))
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
self.imageView.widthAnchor.constraint(equalToConstant: 12),
|
||||
self.labelView.heightAnchor.constraint(equalToConstant: 16),
|
||||
self.inputView.widthAnchor.constraint(equalToConstant: 60),
|
||||
self.outputView.widthAnchor.constraint(equalToConstant: 60),
|
||||
self.heightAnchor.constraint(equalToConstant: 21)
|
||||
])
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public override func mouseEntered(with: NSEvent) {
|
||||
self.layer?.backgroundColor = .init(gray: 0.01, alpha: 0.05)
|
||||
}
|
||||
public override func mouseExited(with: NSEvent) {
|
||||
self.layer?.backgroundColor = .none
|
||||
}
|
||||
|
||||
public func set(_ process: IOProcess_p) {
|
||||
self.imageView.image = process.icon
|
||||
self.labelView.stringValue = process.name
|
||||
self.inputView.stringValue = process.input
|
||||
self.outputView.stringValue = process.output
|
||||
self.toolTip = "pid: \(process.pid)"
|
||||
}
|
||||
|
||||
public func clear() {
|
||||
self.imageView.image = nil
|
||||
self.labelView.stringValue = "-"
|
||||
self.inputView.stringValue = "-"
|
||||
self.outputView.stringValue = "-"
|
||||
self.toolTip = ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ internal class Popup: PopupWrapper {
|
||||
|
||||
private var chart: NetworkChartView? = nil
|
||||
private var connectivityChart: GridChartView? = nil
|
||||
private var processes: [NetworkProcessView] = []
|
||||
private var processes: ProcessesView? = nil
|
||||
|
||||
private var lastReset: Date = Date()
|
||||
|
||||
@@ -68,10 +68,7 @@ internal class Popup: PopupWrapper {
|
||||
Store.shared.int(key: "\(self.title)_processes", defaultValue: 8)
|
||||
}
|
||||
private var processesHeight: CGFloat {
|
||||
get {
|
||||
let num = self.numberOfProcesses
|
||||
return (22*CGFloat(num)) + (num == 0 ? 0 : Constants.Popup.separatorHeight)
|
||||
}
|
||||
(22*CGFloat(self.numberOfProcesses)) + (self.numberOfProcesses == 0 ? 0 : Constants.Popup.separatorHeight + 22)
|
||||
}
|
||||
|
||||
private var downloadColorState: Color = .secondBlue
|
||||
@@ -318,19 +315,22 @@ internal class Popup: PopupWrapper {
|
||||
}
|
||||
|
||||
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)
|
||||
let container: NSView = NSView(frame: NSRect(x: 0, y: 0, width: self.frame.width, height: separator.frame.origin.y))
|
||||
|
||||
for i in 0..<self.numberOfProcesses {
|
||||
let processView = NetworkProcessView(CGFloat(i))
|
||||
self.processes.append(processView)
|
||||
container.addSubview(processView)
|
||||
if self.numberOfProcesses == 0 {
|
||||
let v = NSView()
|
||||
self.processesView = v
|
||||
return v
|
||||
}
|
||||
|
||||
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)
|
||||
let container: ProcessesView = ProcessesView(
|
||||
frame: NSRect(x: 0, y: 0, width: self.frame.width, height: separator.frame.origin.y),
|
||||
values: [(localizedString("Downloading"), self.downloadColor), (localizedString("Uploading"), self.uploadColor)],
|
||||
n: self.numberOfProcesses
|
||||
)
|
||||
self.processes = container
|
||||
view.addSubview(separator)
|
||||
view.addSubview(container)
|
||||
|
||||
self.processesView = view
|
||||
return view
|
||||
}
|
||||
@@ -338,19 +338,14 @@ internal class Popup: PopupWrapper {
|
||||
// MARK: - callbacks
|
||||
|
||||
public func numberOfProcessesUpdated() {
|
||||
if self.processes.count == self.numberOfProcesses {
|
||||
return
|
||||
}
|
||||
if self.processes?.count == self.numberOfProcesses { return }
|
||||
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.processes = []
|
||||
|
||||
if let view = self.processesView {
|
||||
self.removeView(view)
|
||||
}
|
||||
self.processesView?.removeFromSuperview()
|
||||
self.processesView = nil
|
||||
self.processes = nil
|
||||
self.addArrangedSubview(self.initProcesses())
|
||||
self.processesInitialized = false
|
||||
|
||||
self.recalculateHeight()
|
||||
})
|
||||
}
|
||||
@@ -477,19 +472,14 @@ internal class Popup: PopupWrapper {
|
||||
if !(self.window?.isVisible ?? false) && self.processesInitialized {
|
||||
return
|
||||
}
|
||||
|
||||
if list.count != self.processes.count {
|
||||
self.processes.forEach { processView in
|
||||
processView.clear()
|
||||
}
|
||||
}
|
||||
let list = list.map{ $0 }
|
||||
if list.count != self.processes?.count { self.processes?.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)
|
||||
let upload = Units(bytes: Int64(process.upload)).getReadableSpeed(base: self.base)
|
||||
let download = Units(bytes: Int64(process.download)).getReadableSpeed(base: self.base)
|
||||
self.processes?.set(i, process, [download, upload])
|
||||
}
|
||||
|
||||
self.processesInitialized = true
|
||||
@@ -536,6 +526,7 @@ internal class Popup: PopupWrapper {
|
||||
self.uploadColorState = newValue
|
||||
Store.shared.set(key: "\(self.title)_uploadColor", value: key)
|
||||
if let color = newValue.additional as? NSColor {
|
||||
self.processes?.setColor(1, color)
|
||||
self.uploadColorView?.layer?.backgroundColor = color.cgColor
|
||||
self.uploadStateView?.setColor(color)
|
||||
self.chart?.setColors(out: color)
|
||||
@@ -549,6 +540,7 @@ internal class Popup: PopupWrapper {
|
||||
self.downloadColorState = newValue
|
||||
Store.shared.set(key: "\(self.title)_downloadColor", value: key)
|
||||
if let color = newValue.additional as? NSColor {
|
||||
self.processes?.setColor(0, color)
|
||||
self.downloadColorView?.layer?.backgroundColor = color.cgColor
|
||||
self.downloadStateView?.setColor(color)
|
||||
self.chart?.setColors(in: color)
|
||||
@@ -661,85 +653,3 @@ internal class Popup: PopupWrapper {
|
||||
self.lastReset = Date()
|
||||
}
|
||||
}
|
||||
|
||||
public class NetworkProcessView: NSView {
|
||||
public var width: CGFloat {
|
||||
get { return 0 }
|
||||
set {
|
||||
self.setFrameSize(NSSize(width: newValue, height: self.frame.height))
|
||||
}
|
||||
}
|
||||
|
||||
public var icon: NSImage? {
|
||||
get { return NSImage() }
|
||||
set {
|
||||
self.imageView?.image = newValue
|
||||
}
|
||||
}
|
||||
public var label: String {
|
||||
get { return "" }
|
||||
set {
|
||||
self.labelView?.stringValue = newValue
|
||||
}
|
||||
}
|
||||
public var upload: String {
|
||||
get { return "" }
|
||||
set {
|
||||
self.uploadView?.stringValue = newValue
|
||||
}
|
||||
}
|
||||
public var download: String {
|
||||
get { return "" }
|
||||
set {
|
||||
self.downloadView?.stringValue = newValue
|
||||
}
|
||||
}
|
||||
|
||||
private var imageView: NSImageView? = nil
|
||||
private var labelView: LabelField? = nil
|
||||
private var uploadView: ValueField? = nil
|
||||
private var downloadView: ValueField? = nil
|
||||
|
||||
public init(_ n: CGFloat) {
|
||||
super.init(frame: NSRect(x: 0, y: n*22, width: Constants.Popup.width, height: 16))
|
||||
|
||||
let rowView: NSView = NSView(frame: NSRect(x: 0, y: 0, width: self.frame.width, height: 16))
|
||||
|
||||
let imageView: NSImageView = NSImageView(frame: NSRect(x: 2, y: 2, width: 12, height: 12))
|
||||
let labelView: LabelField = LabelField(frame: NSRect(x: 18, y: 0, width: rowView.frame.width - 138, height: 16), "")
|
||||
let uploadView: ValueField = ValueField(frame: NSRect(x: rowView.frame.width - 120, y: 1.75, width: 60, height: 12), "")
|
||||
let downloadView: ValueField = ValueField(frame: NSRect(x: rowView.frame.width - 60, y: 1.75, width: 60, height: 12), "")
|
||||
uploadView.font = NSFont.systemFont(ofSize: 10, weight: .regular)
|
||||
downloadView.font = NSFont.systemFont(ofSize: 10, weight: .regular)
|
||||
|
||||
rowView.addSubview(imageView)
|
||||
rowView.addSubview(labelView)
|
||||
rowView.addSubview(uploadView)
|
||||
rowView.addSubview(downloadView)
|
||||
|
||||
self.imageView = imageView
|
||||
self.labelView = labelView
|
||||
self.uploadView = uploadView
|
||||
self.downloadView = downloadView
|
||||
|
||||
self.addSubview(rowView)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public func attachProcess(_ process: Network_Process) {
|
||||
self.label = process.name
|
||||
self.icon = process.icon
|
||||
self.toolTip = "pid: \(process.pid)"
|
||||
}
|
||||
|
||||
public func clear() {
|
||||
self.label = ""
|
||||
self.download = ""
|
||||
self.upload = ""
|
||||
self.icon = nil
|
||||
self.toolTip = ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,18 +42,13 @@ internal class Popup: PopupWrapper {
|
||||
private var initialized: Bool = false
|
||||
private var processesInitialized: Bool = false
|
||||
|
||||
private var processes: [ProcessView] = []
|
||||
private var processes: ProcessesView? = nil
|
||||
|
||||
private var numberOfProcesses: Int {
|
||||
get {
|
||||
return Store.shared.int(key: "\(self.title)_processes", defaultValue: 8)
|
||||
}
|
||||
Store.shared.int(key: "\(self.title)_processes", defaultValue: 8)
|
||||
}
|
||||
private var processesHeight: CGFloat {
|
||||
get {
|
||||
let num = self.numberOfProcesses
|
||||
return (self.processHeight*CGFloat(num)) + (num == 0 ? 0 : Constants.Popup.separatorHeight)
|
||||
}
|
||||
(self.processHeight*CGFloat(self.numberOfProcesses)) + (self.numberOfProcesses == 0 ? 0 : Constants.Popup.separatorHeight + 22)
|
||||
}
|
||||
|
||||
private var appColorState: Color = .secondBlue
|
||||
@@ -140,23 +135,20 @@ internal class Popup: PopupWrapper {
|
||||
}
|
||||
|
||||
public override func disappear() {
|
||||
self.processes.forEach{ $0.setLock(false) }
|
||||
self.processes?.setLock(false)
|
||||
}
|
||||
|
||||
public func numberOfProcessesUpdated() {
|
||||
if self.processes.count == self.numberOfProcesses {
|
||||
return
|
||||
}
|
||||
if self.processes?.count == self.numberOfProcesses { return }
|
||||
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.processes = []
|
||||
|
||||
let h: CGFloat = self.dashboardHeight + self.chartHeight + self.detailsHeight + self.processesHeight
|
||||
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.processes = nil
|
||||
self.grid?.removeRow(at: 3)
|
||||
self.grid?.addRow(with: [self.initProcesses()])
|
||||
self.processesInitialized = false
|
||||
@@ -226,17 +218,16 @@ internal class Popup: PopupWrapper {
|
||||
}
|
||||
|
||||
private func initProcesses() -> NSView {
|
||||
if self.numberOfProcesses == 0 { return 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)
|
||||
let container: NSStackView = NSStackView(frame: NSRect(x: 0, y: 0, width: self.frame.width, height: separator.frame.origin.y))
|
||||
container.orientation = .vertical
|
||||
container.spacing = 0
|
||||
|
||||
for _ in 0..<self.numberOfProcesses {
|
||||
let processView = ProcessView()
|
||||
self.processes.append(processView)
|
||||
container.addArrangedSubview(processView)
|
||||
}
|
||||
let container: ProcessesView = ProcessesView(
|
||||
frame: NSRect(x: 0, y: 0, width: self.frame.width, height: separator.frame.origin.y),
|
||||
values: [(localizedString("Usage"), nil)],
|
||||
n: self.numberOfProcesses
|
||||
)
|
||||
self.processes = container
|
||||
|
||||
view.addSubview(separator)
|
||||
view.addSubview(container)
|
||||
@@ -297,15 +288,12 @@ internal class Popup: PopupWrapper {
|
||||
if !(self.window?.isVisible ?? false) && self.processesInitialized {
|
||||
return
|
||||
}
|
||||
|
||||
if list.count != self.processes.count {
|
||||
self.processes.forEach { processView in
|
||||
processView.clear()
|
||||
}
|
||||
}
|
||||
let list = list.map { $0 }
|
||||
if list.count != self.processes?.count { self.processes?.clear() }
|
||||
|
||||
for i in 0..<list.count {
|
||||
self.processes[i].set(list[i], Units(bytes: Int64(list[i].usage)).getReadableMemory())
|
||||
let process = list[i]
|
||||
self.processes?.set(i, process, [Units(bytes: Int64(process.usage)).getReadableMemory()])
|
||||
}
|
||||
|
||||
self.processesInitialized = true
|
||||
|
||||
Reference in New Issue
Block a user