Files
macos-stats/Stats/libs/Extensions.swift
Serhiy Mytrovtsiy b448979aa2 - add temperature module (CPU and GPU temperature);
- fix value margin in network widget view;
- add missing widgets name;
- improve battery module updates;
2020-04-04 11:57:38 +02:00

290 lines
9.1 KiB
Swift
Executable File

//
// Extensions.swift
// Stats
//
// Created by Serhiy Mytrovtsiy on 29/05/2019.
// Copyright © 2019 Serhiy Mytrovtsiy. All rights reserved.
//
import Cocoa
import Charts
extension Double {
func roundTo(decimalPlaces: Int) -> String {
return NSString(format: "%.\(decimalPlaces)f" as NSString, self) as String
}
func rounded(toPlaces places:Int) -> Double {
let divisor = pow(10.0, Double(places))
return (self * divisor).rounded() / divisor
}
func usageColor(reversed: Bool = false, color: Bool = false) -> NSColor {
if !color {
return NSColor.textColor
}
if reversed {
switch self {
case 0.6...0.8:
return NSColor.systemOrange
case 0.8...1:
return NSColor.systemGreen
default:
return NSColor.systemRed
}
} else {
switch self {
case 0.6...0.8:
return NSColor.systemOrange
case 0.8...1:
return NSColor.systemRed
default:
return NSColor.systemGreen
}
}
}
func batteryColor(color: Bool = false) -> NSColor {
switch self {
case 0.2...0.4:
if !color {
return NSColor.controlTextColor
}
return NSColor.systemOrange
case 0.4...1:
if self == 1 {
return NSColor.controlTextColor
}
if !color {
return NSColor.controlTextColor
}
return NSColor.systemGreen
default:
return NSColor.systemRed
}
}
func temperatureColor(color: Bool = false) -> NSColor {
switch self {
case 0...70:
return NSColor.controlTextColor
case 70...90:
if !color {
return NSColor.controlTextColor
}
return NSColor.systemOrange
default:
if !color {
return NSColor.controlTextColor
}
return NSColor.systemRed
}
}
func splitAtDecimal() -> [Int64] {
return "\(self)".split(separator: ".").map{Int64($0)!}
}
}
public enum Unit : Float {
case byte = 1
case kilobyte = 1024
case megabyte = 1048576
case gigabyte = 1073741824
}
public struct Units {
public let bytes: Int64
public init(bytes: Int64) {
self.bytes = bytes
}
public var kilobytes: Double {
return Double(bytes) / 1_024
}
public var megabytes: Double {
return kilobytes / 1_024
}
public var gigabytes: Double {
return megabytes / 1_024
}
public func getReadableTuple() -> (Double, String) {
switch bytes {
case 0..<1_024:
return (0, "KB/s")
case 1_024..<(1_024 * 1_024):
return (Double(String(format: "%.2f", kilobytes))!, "KB/s")
case 1_024..<(1_024 * 1_024 * 1_024):
return (Double(String(format: "%.2f", megabytes))!, "MB/s")
case (1_024 * 1_024 * 1_024)...Int64.max:
return (Double(String(format: "%.2f", gigabytes))!, "GB/s")
default:
return (Double(String(format: "%.2f", kilobytes))!, "KB/s")
}
}
public func getReadableSpeed() -> String {
switch bytes {
case 0..<1_024:
return "0 KB/s"
case 1_024..<(1_024 * 1_024):
return String(format: "%.0f KB/s", kilobytes)
case 1_024..<(1_024 * 1_024 * 100):
return String(format: "%.1f MB/s", megabytes)
case (1_024 * 1_024 * 100)..<(1_024 * 1_024 * 1_024):
return String(format: "%.0f MB/s", megabytes)
case (1_024 * 1_024 * 1_024)...Int64.max:
return String(format: "%.1f GB/s", gigabytes)
default:
return String(format: "%.0f KB/s", kilobytes)
}
}
public func getReadableMemory() -> String {
switch bytes {
case 0..<1_024:
return "0 KB/s"
case 1_024..<(1_024 * 1_024):
return String(format: "%.0f KB", kilobytes)
case 1_024..<(1_024 * 1_024 * 1_024):
return String(format: "%.0f MB", megabytes)
case (1_024 * 1_024 * 1_024)...Int64.max:
return String(format: "%.2f GB", gigabytes)
default:
return String(format: "%.0f KB", kilobytes)
}
}
}
extension Double {
func secondsToHoursMinutesSeconds () -> (Int?, Int?, Int?) {
let hrs = self / 3600
let mins = (self.truncatingRemainder(dividingBy: 3600)) / 60
let seconds = (self.truncatingRemainder(dividingBy:3600)).truncatingRemainder(dividingBy:60)
return (Int(hrs) > 0 ? Int(hrs) : nil , Int(mins) > 0 ? Int(mins) : nil, Int(seconds) > 0 ? Int(seconds) : nil)
}
func printSecondsToHoursMinutesSeconds () -> String {
let time = self.secondsToHoursMinutesSeconds()
switch time {
case (nil, let x? , let y?):
return "\(x) min \(y) sec"
case (nil, let x?, nil):
return "\(x) min"
case (let x?, nil, nil):
return "\(x) h"
case (nil, nil, let x?):
return "\(x) sec"
case (let x?, nil, let z?):
return "\(x) h \(z) sec"
case (let x?, let y?, nil):
return "\(x) h \(y) min"
case (let x?, let y?, let z?):
return "\(x) h \(y) min \(z) sec"
default:
return "n/a"
}
}
}
extension String {
func condenseWhitespace() -> String {
let components = self.components(separatedBy: .whitespacesAndNewlines)
return components.filter { !$0.isEmpty }.joined(separator: " ")
}
var UTF8CString: UnsafeMutablePointer<Int8> {
return UnsafeMutablePointer(mutating: (self as NSString).utf8String!)
}
}
extension NSBezierPath {
func addArrow(start: CGPoint, end: CGPoint, pointerLineLength: CGFloat, arrowAngle: CGFloat) {
self.move(to: start)
self.line(to: end)
let startEndAngle = atan((end.y - start.y) / (end.x - start.x)) + ((end.x - start.x) < 0 ? CGFloat(Double.pi) : 0)
let arrowLine1 = CGPoint(x: end.x + pointerLineLength * cos(CGFloat(Double.pi) - startEndAngle + arrowAngle), y: end.y - pointerLineLength * sin(CGFloat(Double.pi) - startEndAngle + arrowAngle))
let arrowLine2 = CGPoint(x: end.x + pointerLineLength * cos(CGFloat(Double.pi) - startEndAngle - arrowAngle), y: end.y - pointerLineLength * sin(CGFloat(Double.pi) - startEndAngle - arrowAngle))
self.line(to: arrowLine1)
self.move(to: end)
self.line(to: arrowLine2)
}
}
extension NSColor {
convenience init(hexString: String, alpha: CGFloat = 1.0) {
let hexString: String = hexString.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
let scanner = Scanner(string: hexString)
if (hexString.hasPrefix("#")) {
scanner.scanLocation = 1
}
var color: UInt32 = 0
scanner.scanHexInt32(&color)
let mask = 0x000000FF
let r = Int(color >> 16) & mask
let g = Int(color >> 8) & mask
let b = Int(color) & mask
let red = CGFloat(r) / 255.0
let green = CGFloat(g) / 255.0
let blue = CGFloat(b) / 255.0
self.init(red:red, green:green, blue:blue, alpha:alpha)
}
func toHexString() -> String {
var r:CGFloat = 0
var g:CGFloat = 0
var b:CGFloat = 0
var a:CGFloat = 0
getRed(&r, green: &g, blue: &b, alpha: &a)
let rgb:Int = (Int)(r*255)<<16 | (Int)(g*255)<<8 | (Int)(b*255)<<0
return String(format:"#%06x", rgb)
}
}
extension String {
mutating func findAndCrop(pattern: String) -> String {
let regex = try! NSRegularExpression(pattern: pattern)
let stringRange = NSRange(location: 0, length: self.utf16.count)
var line = self
if let searchRange = regex.firstMatch(in: self, options: [], range: stringRange) {
let start = self.index(self.startIndex, offsetBy: searchRange.range.lowerBound)
let end = self.index(self.startIndex, offsetBy: searchRange.range.upperBound)
let value = String(self[start..<end]).trimmingCharacters(in: .whitespaces)
line = self.replacingOccurrences(
of: value,
with: "",
options: .regularExpression
)
self = line.trimmingCharacters(in: .whitespaces)
return value.trimmingCharacters(in: .whitespaces)
}
return ""
}
func matches(_ regex: String) -> Bool {
return self.range(of: regex, options: .regularExpression, range: nil, locale: nil) != nil
}
}
extension URL {
func checkFileExist() -> Bool {
return FileManager.default.fileExists(atPath: self.path)
}
}
class ChartsNetworkAxisFormatter: IAxisValueFormatter {
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
return Units(bytes: Int64(value)).getReadableSpeed()
}
}