diff --git a/Stats.xcodeproj/project.pbxproj b/Stats.xcodeproj/project.pbxproj index 5d8721e3..81214b0b 100644 --- a/Stats.xcodeproj/project.pbxproj +++ b/Stats.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 9A141100229E721200D29793 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9A1410FE229E721200D29793 /* Main.storyboard */; }; 9A426DB822C2B5EE00C064C4 /* macAppUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A426DB722C2B5EE00C064C4 /* macAppUpdater.swift */; }; 9A426DBE22C2BE0000C064C4 /* Updates.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9A426DBD22C2BE0000C064C4 /* Updates.storyboard */; }; + 9A493CDF23202B620064570C /* MemoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A493CDE23202B620064570C /* MemoryView.swift */; }; 9A57A18522A1D26D0033E318 /* MenuBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A57A18422A1D26D0033E318 /* MenuBar.swift */; }; 9A57A19D22A1E3270033E318 /* CPU.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A57A19C22A1E3270033E318 /* CPU.swift */; }; 9A58D1B022C150C800405315 /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A58D1AF22C150C800405315 /* Network.swift */; }; @@ -80,6 +81,7 @@ 9A141102229E721200D29793 /* Stats.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Stats.entitlements; sourceTree = ""; }; 9A426DB722C2B5EE00C064C4 /* macAppUpdater.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = macAppUpdater.swift; sourceTree = ""; }; 9A426DBD22C2BE0000C064C4 /* Updates.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Updates.storyboard; sourceTree = ""; }; + 9A493CDE23202B620064570C /* MemoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryView.swift; sourceTree = ""; }; 9A57A18422A1D26D0033E318 /* MenuBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBar.swift; sourceTree = ""; }; 9A57A19C22A1E3270033E318 /* CPU.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CPU.swift; sourceTree = ""; }; 9A58D1AF22C150C800405315 /* Network.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = ""; }; @@ -256,6 +258,7 @@ children = ( 9A7B8F6822A2C3A100DEB352 /* Memory.swift */, 9A7B8F6C22A2C3D600DEB352 /* MemoryReader.swift */, + 9A493CDE23202B620064570C /* MemoryView.swift */, ); path = Memory; sourceTree = ""; @@ -549,6 +552,7 @@ 9A5B1CC5229E7B40008B9D3C /* Extensions.swift in Sources */, 9A79B36A22D3BEE600BF1C3A /* Widget.swift in Sources */, 9AF0F32122DA92AD00026AE6 /* NetworkDots.swift in Sources */, + 9A493CDF23202B620064570C /* MemoryView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Stats/Modules/CPU/CPUReader.swift b/Stats/Modules/CPU/CPUReader.swift index 1bbd3a23..11d144b2 100644 --- a/Stats/Modules/CPU/CPUReader.swift +++ b/Stats/Modules/CPU/CPUReader.swift @@ -15,7 +15,7 @@ struct CPUUsage { var idle: Double = 0 } -struct CPUProcess { +struct TopProcess { var pid: Int = 0 var command: String = "" var usage: Double = 0 @@ -24,7 +24,7 @@ struct CPUProcess { class CPUReader: Reader { public var value: Observable<[Double]>! public var usage: Observable = Observable(CPUUsage()) - public var processes: Observable<[CPUProcess]> = Observable([CPUProcess]()) + public var processes: Observable<[TopProcess]> = Observable([TopProcess]()) public var available: Bool = true public var updateTimer: Timer! public var perCoreMode: Bool = false @@ -81,14 +81,14 @@ class CPUReader: Reader { } let outputString = String(data: output, encoding: String.Encoding.utf8) ?? "" - var processes: [CPUProcess] = [] + var processes: [TopProcess] = [] outputString.enumerateLines { (line, stop) -> () in if line.matches("^\\d+ + .+ +\\d+.\\d *$") { let arr = line.condenseWhitespace().split(separator: " ") let pid = Int(arr[0]) ?? 0 let command = String(arr[1]) let usage = Double(arr[2]) ?? 0 - let process = CPUProcess(pid: pid, command: command, usage: usage) + let process = TopProcess(pid: pid, command: command, usage: usage) processes.append(process) } } diff --git a/Stats/Modules/CPU/CPUView.swift b/Stats/Modules/CPU/CPUView.swift index ea87fe9a..28f17251 100644 --- a/Stats/Modules/CPU/CPUView.swift +++ b/Stats/Modules/CPU/CPUView.swift @@ -32,7 +32,7 @@ extension CPU { self.chart = LineChartView(frame: CGRect(x: 0, y: TabHeight - 110, width: TabWidth, height: 102)) self.chart.animate(xAxisDuration: 2.0, yAxisDuration: 2.0, easingOption: .easeInCubic) self.chart.backgroundColor = .white - self.chart.noDataText = "No data about CPU usage" + self.chart.noDataText = "No \(self.name) usage data" self.chart.legend.enabled = false self.chart.scaleXEnabled = false self.chart.scaleYEnabled = false @@ -63,12 +63,8 @@ extension CPU { marker.chartView = self.chart self.chart.marker = marker - var lineChartEntry = [ChartDataEntry]() - - lineChartEntry.append(ChartDataEntry(x: 0, y: 50)) - lineChartEntry.append(ChartDataEntry(x: 1, y: 25)) - - let chartDataSet = LineChartDataSet(entries: lineChartEntry, label: "CPU Usage") + let lineChartEntry = [ChartDataEntry]() + let chartDataSet = LineChartDataSet(entries: lineChartEntry, label: "\(self.name) Usage") chartDataSet.drawCirclesEnabled = false chartDataSet.mode = .cubicBezier chartDataSet.cubicIntensity = 0.1 @@ -210,10 +206,12 @@ extension CPU { (self.reader as! CPUReader).processes.subscribe(observer: self) { (processes, _) in for (i, process) in processes.enumerated() { - let processView = processViewList[i] - - (processView.subviews[0] as! NSTextField).stringValue = process.command - (processView.subviews[1] as! NSTextField).stringValue = "\(process.usage.roundTo(decimalPlaces: 2)) %" + if i < 5 { + let processView = processViewList[i] + + (processView.subviews[0] as! NSTextField).stringValue = process.command + (processView.subviews[1] as! NSTextField).stringValue = "\(process.usage.roundTo(decimalPlaces: 2)) %" + } } } } diff --git a/Stats/Modules/Memory/Memory.swift b/Stats/Modules/Memory/Memory.swift index 1ba0cf95..78e0947f 100644 --- a/Stats/Modules/Memory/Memory.swift +++ b/Stats/Modules/Memory/Memory.swift @@ -7,23 +7,23 @@ // import Cocoa +import Charts class Memory: Module { - let name: String = "Memory" - let shortName: String = "MEM" - var view: NSView = NSView() - var menu: NSMenuItem = NSMenuItem() - var submenu: NSMenu = NSMenu() - var active: Observable - var available: Observable - var reader: Reader = MemoryReader() - var widgetType: WidgetType - var viewAvailable: Bool = true - var tabView: NSTabViewItem = NSTabViewItem() + public let name: String = "Memory" + public let shortName: String = "MEM" + public var view: NSView = NSView() + public var menu: NSMenuItem = NSMenuItem() + public var active: Observable + public var available: Observable + public var reader: Reader = MemoryReader() + public var widgetType: WidgetType + public var viewAvailable: Bool = true + public var tabView: NSTabViewItem = NSTabViewItem() + public var chart: LineChartView = LineChartView() - let defaults = UserDefaults.standard - - @IBOutlet weak var value: NSTextField! + private let defaults = UserDefaults.standard + private var submenu: NSMenu = NSMenu() init() { self.available = Observable(true) @@ -34,24 +34,6 @@ class Memory: Module { initTab() } - func initTab() { - self.tabView.view?.frame = NSRect(x: 0, y: 0, width: TabWidth, height: TabHeight) - - let text: NSTextField = NSTextField(string: self.name) - text.isEditable = false - text.isSelectable = false - text.isBezeled = false - text.wantsLayer = true - text.textColor = .labelColor - text.canDrawSubviewsIntoLayer = true - text.alignment = .natural - text.font = NSFont.systemFont(ofSize: 13, weight: .regular) - text.frame.origin.x = ((self.tabView.view?.frame.size.width)! - 50) / 2 - text.frame.origin.y = ((self.tabView.view?.frame.size.height)! - 22) / 2 - - self.tabView.view?.addSubview(text) - } - func initMenu() { menu = NSMenuItem(title: name, action: #selector(toggle), keyEquivalent: "") submenu = NSMenu() diff --git a/Stats/Modules/Memory/MemoryReader.swift b/Stats/Modules/Memory/MemoryReader.swift index 58101500..7603e66c 100644 --- a/Stats/Modules/Memory/MemoryReader.swift +++ b/Stats/Modules/Memory/MemoryReader.swift @@ -8,17 +8,32 @@ import Foundation +struct MemoryUsage { + var total: Double = 0 + var used: Double = 0 + var free: Double = 0 +} + class MemoryReader: Reader { - var value: Observable<[Double]>! - var available: Bool = true - var updateTimer: Timer! - var totalSize: Float + public var value: Observable<[Double]>! + public var usage: Observable = Observable(MemoryUsage()) + public var processes: Observable<[TopProcess]> = Observable([TopProcess]()) + public var available: Bool = true + public var updateTimer: Timer! + public var totalSize: Float + + private var topProcess: Process = Process() + private var pipe: Pipe = Pipe() init() { self.value = Observable([]) var stats = host_basic_info() var count = UInt32(MemoryLayout.size / MemoryLayout.size) + self.topProcess.launchPath = "/usr/bin/top" + self.topProcess.arguments = ["-s", "1", "-o", "mem", "-n", "5", "-stats", "pid,command,mem"] + self.topProcess.standardOutput = pipe + let kerr: kern_return_t = withUnsafeMutablePointer(to: &stats) { $0.withMemoryRebound(to: integer_t.self, capacity: Int(count)) { host_info(mach_host_self(), HOST_BASIC_INFO, $0, &count) @@ -41,6 +56,43 @@ class MemoryReader: Reader { return } updateTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(read), userInfo: nil, repeats: true) + + if topProcess.isRunning { + return + } + self.pipe.fileHandleForReading.waitForDataInBackgroundAndNotify() + + NotificationCenter.default.addObserver(forName: NSNotification.Name.NSFileHandleDataAvailable, object: self.pipe.fileHandleForReading , queue: nil) { _ -> Void in + defer { + self.pipe.fileHandleForReading.waitForDataInBackgroundAndNotify() + } + + let output = self.pipe.fileHandleForReading.availableData + if output.isEmpty { + return + } + + let outputString = String(data: output, encoding: String.Encoding.utf8) ?? "" + var processes: [TopProcess] = [] + outputString.enumerateLines { (line, stop) -> () in + if line.matches("^\\d+ + .+ +\\d+.\\d[M\\+\\-]+ *$") { + let arr = line.condenseWhitespace().split(separator: " ") + let pid = Int(arr[0]) ?? 0 + let command = String(arr[1]) + let usage = Double(arr[2].filter("01234567890.".contains))! * Double(1024 * 1024) + let process = TopProcess(pid: pid, command: command, usage: usage) + processes.append(process) + } + } + + self.processes << processes + } + + do { + try topProcess.run() + } catch let error { + print(error) + } } func stop() { @@ -49,6 +101,7 @@ class MemoryReader: Reader { } updateTimer.invalidate() updateTimer = nil + NotificationCenter.default.removeObserver(self, name: NSNotification.Name.NSFileHandleDataAvailable, object: nil) } @objc func read() { @@ -63,11 +116,14 @@ class MemoryReader: Reader { if kerr == KERN_SUCCESS { let active = Float(stats.active_count) * Float(PAGE_SIZE) - // let inactive = Float(stats.inactive_count) * Float(PAGE_SIZE) +// let inactive = Float(stats.inactive_count) * Float(PAGE_SIZE) let wired = Float(stats.wire_count) * Float(PAGE_SIZE) let compressed = Float(stats.compressor_page_count) * Float(PAGE_SIZE) - let free = totalSize - (active + wired + compressed) + let used = active + wired + compressed + let free = totalSize - used + + self.usage << MemoryUsage(total: Double(totalSize), used: Double(used), free: Double(free)) self.value << [Double((totalSize - free) / totalSize)] } else { diff --git a/Stats/Modules/Memory/MemoryView.swift b/Stats/Modules/Memory/MemoryView.swift new file mode 100644 index 00000000..ecf1ac20 --- /dev/null +++ b/Stats/Modules/Memory/MemoryView.swift @@ -0,0 +1,258 @@ +// +// MemoryView.swift +// Stats +// +// Created by Serhiy Mytrovtsiy on 04/09/2019. +// Copyright © 2019 Serhiy Mytrovtsiy. All rights reserved. +// + +import Cocoa +import Foundation +import Charts + +extension Memory { + + func initTab() { + self.tabView.view?.frame = NSRect(x: 0, y: 0, width: TabWidth, height: TabHeight) + + makeChart() + makeOverview() + makeProcesses() + + (self.reader as! MemoryReader).usage.subscribe(observer: self) { (value, _) in + self.updateChart(value: Units(bytes: Int64(value.used)).getReadableTuple().0) + } + } + + func makeChart() { + let reader = self.reader as! MemoryReader + let lineColor: NSColor = NSColor(red: (26/255.0), green: (126/255.0), blue: (252/255.0), alpha: 1.0) + let gradientColor: NSColor = NSColor(red: (26/255.0), green: (126/255.0), blue: (252/255.0), alpha: 0.5) + + self.chart = LineChartView(frame: CGRect(x: 0, y: TabHeight - 110, width: TabWidth, height: 102)) + self.chart.animate(xAxisDuration: 2.0, yAxisDuration: 2.0, easingOption: .easeInCubic) + self.chart.backgroundColor = .white + self.chart.noDataText = "No \(self.name) usage data" + self.chart.legend.enabled = false + self.chart.scaleXEnabled = false + self.chart.scaleYEnabled = false + self.chart.pinchZoomEnabled = false + self.chart.doubleTapToZoomEnabled = false + self.chart.drawBordersEnabled = false + + self.chart.rightAxis.enabled = false + + self.chart.leftAxis.axisMinimum = 0 + self.chart.leftAxis.axisMaximum = Units(bytes: Int64(reader.totalSize)).gigabytes + self.chart.leftAxis.labelCount = Units(bytes: Int64(reader.totalSize)).gigabytes > 16 ? 6 : 4 + self.chart.leftAxis.drawGridLinesEnabled = false + self.chart.leftAxis.drawAxisLineEnabled = false + + self.chart.leftAxis.gridColor = NSColor(red:220/255, green:220/255, blue:220/255, alpha:1) + self.chart.leftAxis.gridLineWidth = 0.5 + self.chart.leftAxis.drawGridLinesEnabled = true + self.chart.leftAxis.labelTextColor = NSColor(red:150/255, green:150/255, blue:150/255, alpha:1) + + self.chart.xAxis.drawAxisLineEnabled = false + self.chart.xAxis.drawLimitLinesBehindDataEnabled = false + self.chart.xAxis.gridLineWidth = 0.5 + self.chart.xAxis.drawGridLinesEnabled = false + self.chart.xAxis.drawLabelsEnabled = false + + let marker = ChartMarker() + marker.chartView = self.chart + self.chart.marker = marker + + let lineChartEntry = [ChartDataEntry]() + let chartDataSet = LineChartDataSet(entries: lineChartEntry, label: "\(self.name) Usage") + chartDataSet.drawCirclesEnabled = false + chartDataSet.mode = .cubicBezier + chartDataSet.cubicIntensity = 0.1 + chartDataSet.colors = [lineColor] + chartDataSet.fillColor = gradientColor + chartDataSet.drawFilledEnabled = true + + let data = LineChartData() + data.addDataSet(chartDataSet) + data.setDrawValues(false) + + self.chart.data = LineChartData(dataSet: chartDataSet) + + self.tabView.view?.addSubview(self.chart) + } + + func updateChart(value: Double) { + let index = Double((self.chart.data?.getDataSetByIndex(0)?.entryCount)!) + self.chart.data?.addEntry(ChartDataEntry(x: index, y: value), dataSetIndex: 0) + + if index > 120 { + self.chart.xAxis.axisMinimum = index - 120 + } + self.chart.xAxis.axisMaximum = index + self.chart.notifyDataSetChanged() + self.chart.moveViewToX(index) + } + + func makeOverview() { + let overviewLabel: NSView = NSView(frame: NSRect(x: 0, y: TabHeight - 140, width: TabWidth, height: 25)) + + overviewLabel.wantsLayer = true + overviewLabel.layer?.backgroundColor = NSColor(hexString: "#eeeeee", alpha: 0.5).cgColor + + let overviewText: NSTextField = NSTextField(string: "Overview") + overviewText.frame = NSRect(x: 0, y: 0, width: TabWidth, height: overviewLabel.frame.size.height - 4) + overviewText.isEditable = false + overviewText.isSelectable = false + overviewText.isBezeled = false + overviewText.wantsLayer = true + overviewText.textColor = .darkGray + overviewText.canDrawSubviewsIntoLayer = true + overviewText.alignment = .center + overviewText.backgroundColor = NSColor(hexString: "#dddddd", alpha: 0) + overviewText.font = NSFont.systemFont(ofSize: 12, weight: .medium) + + overviewLabel.addSubview(overviewText) + self.tabView.view?.addSubview(overviewLabel) + + let stackHeight: CGFloat = 22 + let vertical: NSStackView = NSStackView(frame: NSRect(x: 0, y: 147, width: TabWidth, height: stackHeight*3)) + vertical.orientation = .vertical + + let total: NSStackView = NSStackView(frame: NSRect(x: 10, y: stackHeight*2, width: TabWidth - 20, height: stackHeight)) + total.orientation = .horizontal + total.distribution = .equalCentering + let totalLabel = labelField(string: "Total") + let totalValue = valueField(string: "0 GB") + total.addView(totalLabel, in: .center) + total.addView(totalValue, in: .center) + + let used: NSStackView = NSStackView(frame: NSRect(x: 10, y: stackHeight*1, width: TabWidth - 20, height: stackHeight)) + used.orientation = .horizontal + used.distribution = .equalCentering + let usedLabel = labelField(string: "Used") + let usedValue = valueField(string: "0 GB") + used.addView(usedLabel, in: .center) + used.addView(usedValue, in: .center) + + let free: NSStackView = NSStackView(frame: NSRect(x: 10, y: 0, width: TabWidth - 20, height: stackHeight)) + free.orientation = .horizontal + free.distribution = .equalCentering + let freeLabel = labelField(string: "Free") + let freeValue = valueField(string: "0 GB") + free.addView(freeLabel, in: .center) + free.addView(freeValue, in: .center) + + vertical.addSubview(total) + vertical.addSubview(used) + vertical.addSubview(free) + + self.tabView.view?.addSubview(vertical) + + (self.reader as! MemoryReader).usage.subscribe(observer: self) { (value, _) in + totalValue.stringValue = Units(bytes: Int64(value.total)).getReadableUnit() + usedValue.stringValue = Units(bytes: Int64(value.used)).getReadableUnit() + freeValue.stringValue = Units(bytes: Int64(value.free)).getReadableUnit() + } + } + + func makeProcesses() { + let label: NSView = NSView(frame: NSRect(x: 0, y: 0, width: TabWidth, height: 25)) + + label.wantsLayer = true + label.layer?.backgroundColor = NSColor(hexString: "#eeeeee", alpha: 0.5).cgColor + + let text: NSTextField = NSTextField(string: "Top Processes") + text.frame = NSRect(x: 0, y: 0, width: TabWidth, height: label.frame.size.height - 4) + text.isEditable = false + text.isSelectable = false + text.isBezeled = false + text.wantsLayer = true + text.textColor = .darkGray + text.canDrawSubviewsIntoLayer = true + text.alignment = .center + text.backgroundColor = NSColor(hexString: "#dddddd", alpha: 0) + text.font = NSFont.systemFont(ofSize: 12, weight: .medium) + + label.addSubview(text) + self.tabView.view?.addSubview(label) + + let stackHeight: CGFloat = 22 + let vertical: NSStackView = NSStackView(frame: NSRect(x: 0, y: 4, width: TabWidth, height: stackHeight*5)) + vertical.orientation = .vertical + vertical.distribution = .fill + + var processViewList: [NSStackView] = [] + let process_1 = makeProcessView(num: 4, height: stackHeight, label: "", value: "") + let process_2 = makeProcessView(num: 3, height: stackHeight, label: "", value: "") + let process_3 = makeProcessView(num: 2, height: stackHeight, label: "", value: "") + let process_4 = makeProcessView(num: 1, height: stackHeight, label: "", value: "") + let process_5 = makeProcessView(num: 0, height: stackHeight, label: "", value: "") + + processViewList.append(process_1) + processViewList.append(process_2) + processViewList.append(process_3) + processViewList.append(process_4) + processViewList.append(process_5) + + vertical.addSubview(process_1) + vertical.addSubview(process_2) + vertical.addSubview(process_3) + vertical.addSubview(process_4) + vertical.addSubview(process_5) + self.tabView.view?.addSubview(vertical) + + label.frame = NSRect(x: 0, y: vertical.frame.origin.y + vertical.frame.size.height + 2, width: TabWidth, height: 25) + self.tabView.view?.addSubview(label) + + (self.reader as! MemoryReader).processes.subscribe(observer: self) { (processes, _) in + for (i, process) in processes.enumerated() { + if i < 5 { + let processView = processViewList[i] + + (processView.subviews[0] as! NSTextField).stringValue = process.command + (processView.subviews[1] as! NSTextField).stringValue = Units(bytes: Int64(process.usage)).getReadableUnit() + } + } + } + } + + func makeProcessView(num: Int, height: CGFloat, label: String, value: String) -> NSStackView { + let view: NSStackView = NSStackView(frame: NSRect(x: 10, y: CGFloat(num)*height, width: TabWidth - 20, height: height)) + view.orientation = .horizontal + view.distribution = .equalCentering + let viewLabel = labelField(string: label) + let viewValue = valueField(string: value) + view.addView(viewLabel, in: .center) + view.addView(viewValue, in: .center) + + return view + } + + func labelField(string: String) -> NSTextField { + let label: NSTextField = NSTextField(string: string) + + label.isEditable = false + label.isSelectable = false + label.isBezeled = false + label.textColor = .black + label.alignment = .center + label.font = NSFont.systemFont(ofSize: 12, weight: .regular) + label.backgroundColor = NSColor(hexString: "#dddddd", alpha: 0) + + return label + } + + func valueField(string: String) -> NSTextField { + let label: NSTextField = NSTextField(string: string) + + label.isEditable = false + label.isSelectable = false + label.isBezeled = false + label.textColor = .black + label.alignment = .center + label.font = NSFont.systemFont(ofSize: 13, weight: .regular) + label.backgroundColor = NSColor(hexString: "#dddddd", alpha: 0) + + return label + } +} diff --git a/Stats/Supporting Files/Info.plist b/Stats/Supporting Files/Info.plist index 05e59ac5..589d68b1 100755 --- a/Stats/Supporting Files/Info.plist +++ b/Stats/Supporting Files/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.2.11 + 1.3.0 CFBundleVersion 1 LSApplicationCategoryType diff --git a/Stats/Widgets/Network/NetworkArrowsText.swift b/Stats/Widgets/Network/NetworkArrowsText.swift index 82426c4f..d6b9adeb 100644 --- a/Stats/Widgets/Network/NetworkArrowsText.swift +++ b/Stats/Widgets/Network/NetworkArrowsText.swift @@ -94,11 +94,11 @@ class NetworkArrowsTextView: NSView, Widget { if self.download != download { self.download = download - downloadValue.stringValue = Units(bytes: self.download).getReadableUnit() + downloadValue.stringValue = "\(Units(bytes: self.download).getReadableUnit())/s" } if self.upload != upload { self.upload = upload - uploadValue.stringValue = Units(bytes: self.upload).getReadableUnit() + uploadValue.stringValue = "\(Units(bytes: self.upload).getReadableUnit())/s" } } diff --git a/Stats/Widgets/Network/NetworkDotsText.swift b/Stats/Widgets/Network/NetworkDotsText.swift index ab57aa7b..f37e006c 100644 --- a/Stats/Widgets/Network/NetworkDotsText.swift +++ b/Stats/Widgets/Network/NetworkDotsText.swift @@ -80,11 +80,11 @@ class NetworkDotsTextView: NSView, Widget { if self.download != download { self.download = download - downloadValue.stringValue = Units(bytes: self.download).getReadableUnit() + downloadValue.stringValue = "\(Units(bytes: self.download).getReadableUnit())/s" } if self.upload != upload { self.upload = upload - uploadValue.stringValue = Units(bytes: self.upload).getReadableUnit() + uploadValue.stringValue = "\(Units(bytes: self.upload).getReadableUnit())/s" } } diff --git a/Stats/Widgets/Network/NetworkText.swift b/Stats/Widgets/Network/NetworkText.swift index 883c7aba..8f4bf952 100644 --- a/Stats/Widgets/Network/NetworkText.swift +++ b/Stats/Widgets/Network/NetworkText.swift @@ -44,8 +44,8 @@ class NetworkTextView: NSView, Widget { let download: Int64 = Int64(data[0]) let upload: Int64 = Int64(data[1]) - downloadValue.stringValue = Units(bytes: download).getReadableUnit() - uploadValue.stringValue = Units(bytes: upload).getReadableUnit() + downloadValue.stringValue = "\(Units(bytes: download).getReadableUnit())/s" + uploadValue.stringValue = "\(Units(bytes: upload).getReadableUnit())/s" } func valueView() { diff --git a/Stats/libs/Extensions.swift b/Stats/libs/Extensions.swift index 7eda9133..5d482afa 100755 --- a/Stats/libs/Extensions.swift +++ b/Stats/libs/Extensions.swift @@ -107,15 +107,15 @@ public struct Units { public func getReadableUnit() -> String { switch bytes { case 0..<1_024: - return "0 KB/s" + return "0 KB" case 1_024..<(1_024 * 1_024): - return String(format: "%.0f KB/s", kilobytes) + return String(format: "%.0f KB", kilobytes) case 1_024..<(1_024 * 1_024 * 1_024): - return String(format: "%.2f MB/s", megabytes) + return String(format: "%.2f MB", megabytes) case (1_024 * 1_024 * 1_024)...Int64.max: - return String(format: "%.2f GB/s", gigabytes) + return String(format: "%.2f GB", gigabytes) default: - return String(format: "%.0f KB/s", kilobytes) + return String(format: "%.0f KB", kilobytes) } } }