diff --git a/Cartfile.resolved b/Cartfile.resolved index 052c3eaf..993e4258 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "ashleymills/Reachability.swift" "v5.0.0" +github "ashleymills/Reachability.swift" "v5.1.0" github "malcommac/Repeat" "0.6.0" diff --git a/Modules/CPU/popup.swift b/Modules/CPU/popup.swift index 3ca66aa2..7e412951 100644 --- a/Modules/CPU/popup.swift +++ b/Modules/CPU/popup.swift @@ -168,11 +168,7 @@ internal class Popup: NSView { ]) if tempValue != nil { self.temperatureCircle?.setValue(tempValue!) - - let formatter = MeasurementFormatter() - formatter.numberFormatter.maximumFractionDigits = 0 - let measurement = Measurement(value: tempValue!, unit: UnitTemperature.celsius) - self.temperatureCircle?.setText(formatter.string(from: measurement)) + self.temperatureCircle?.setText(Temperature(tempValue!)) } } self.chart?.addValue(value.totalUsage) diff --git a/Modules/GPU/popup.swift b/Modules/GPU/popup.swift index 6409265e..5b065ea1 100644 --- a/Modules/GPU/popup.swift +++ b/Modules/GPU/popup.swift @@ -147,10 +147,7 @@ private class GPUView: NSView { view.addSubview(self.temperatureCirle!) self.temperatureCirle?.setValue(Double(self.value.temperature)) - let formatter = MeasurementFormatter() - formatter.numberFormatter.maximumFractionDigits = 0 - let measurement = Measurement(value: Double(self.value.temperature), unit: UnitTemperature.celsius) - self.temperatureCirle?.setText(formatter.string(from: measurement)) + self.temperatureCirle?.setText(Temperature(Double(self.value.temperature))) self.temperatureChart?.addValue(Double(self.value.temperature) / 100) self.addSubview(view) @@ -204,10 +201,7 @@ private class GPUView: NSView { self.stateView?.toolTip = "GPU \(gpu.state ? "enabled" : "disabled")" self.temperatureCirle?.setValue(Double(gpu.temperature)) - let formatter = MeasurementFormatter() - formatter.numberFormatter.maximumFractionDigits = 0 - let measurement = Measurement(value: Double(gpu.temperature), unit: UnitTemperature.celsius) - self.temperatureCirle?.setText(formatter.string(from: measurement)) + self.temperatureCirle?.setText(Temperature(Double(gpu.temperature))) self.utilizationCircle?.setValue(gpu.utilization) self.utilizationCircle?.setText("\(Int(gpu.utilization*100))%") diff --git a/Modules/Sensors/settings.swift b/Modules/Sensors/settings.swift index e8b03537..8609ec82 100644 --- a/Modules/Sensors/settings.swift +++ b/Modules/Sensors/settings.swift @@ -14,6 +14,7 @@ import StatsKit import ModuleKit internal class Settings: NSView, Settings_v { + private var unitsValue: String = "system" private var updateIntervalValue: String = "3" private let listOfUpdateIntervals: [String] = ["1", "2", "3", "5", "10", "15", "30"] @@ -40,6 +41,7 @@ internal class Settings: NSView, Settings_v { self.canDrawConcurrently = true self.updateIntervalValue = store.pointee.string(key: "\(self.title)_updateInterval", defaultValue: self.updateIntervalValue) + self.unitsValue = store.pointee.string(key: "temperature_units", defaultValue: self.unitsValue) } required init?(coder: NSCoder) { @@ -60,9 +62,16 @@ internal class Settings: NSView, Settings_v { } let rowHeight: CGFloat = 30 - let height: CGFloat = ((rowHeight+Constants.Settings.margin) * CGFloat(self.list.pointee.count) + rowHeight) + ((rowHeight+Constants.Settings.margin) * CGFloat(types.count) + 1) + let settingsHeight: CGFloat = (rowHeight*2) + Constants.Settings.margin + let sensorsListHeight: CGFloat = (rowHeight+Constants.Settings.margin) * CGFloat(self.list.pointee.count) + ((rowHeight+Constants.Settings.margin) * CGFloat(types.count) + 1) + let height: CGFloat = settingsHeight + sensorsListHeight let x: CGFloat = height < 360 ? 0 : Constants.Settings.margin - let view: NSView = NSView(frame: NSRect(x: Constants.Settings.margin, y: Constants.Settings.margin, width: self.frame.width - (Constants.Settings.margin*2) - x, height: height)) + let view: NSView = NSView(frame: NSRect( + x: Constants.Settings.margin, + y: Constants.Settings.margin, + width: self.frame.width - (Constants.Settings.margin*2) - x, + height: height + )) self.addSubview(SelectTitleRow( frame: NSRect(x: Constants.Settings.margin, y: height - rowHeight, width: view.frame.width, height: rowHeight), @@ -72,6 +81,14 @@ internal class Settings: NSView, Settings_v { selected: "\(self.updateIntervalValue) sec" )) + self.addSubview(SelectRow( + frame: NSRect(x: Constants.Settings.margin, y: height - (rowHeight*2) - Constants.Settings.margin, width: view.frame.width, height: rowHeight), + title: LocalizedString("Temperature unit"), + action: #selector(changeUnits), + items: TemperatureUnits, + selected: self.unitsValue + )) + var y: CGFloat = 0 types.reversed().forEach { (typ: SensorType_t) in let filtered = self.list.pointee.filter{ $0.type == typ } @@ -136,4 +153,13 @@ internal class Settings: NSView, Settings_v { self.setInterval(value) } } + + @objc private func changeUnits(_ sender: NSMenuItem) { + guard let key = sender.representedObject as? String else { + return + } + self.unitsValue = key + store.pointee.set(key: "temperature_units", value: key) + self.callback() + } } diff --git a/Modules/Sensors/values.swift b/Modules/Sensors/values.swift index db418c2c..ea38fb1f 100644 --- a/Modules/Sensors/values.swift +++ b/Modules/Sensors/values.swift @@ -55,11 +55,7 @@ struct Sensor_t { get { switch self.type { case SensorType.Temperature.rawValue: - let formatter = MeasurementFormatter() - formatter.numberFormatter.maximumFractionDigits = 0 - let measurement = Measurement(value: (value ?? 0), unit: UnitTemperature.celsius) - - return formatter.string(from: measurement) + return Temperature(value ?? 0) case SensorType.Voltage.rawValue: return String(format: "%.3f \(unit)", value ?? 0) case SensorType.Power.rawValue: @@ -72,11 +68,7 @@ struct Sensor_t { get { switch self.type { case SensorType.Temperature.rawValue: - let formatter = MeasurementFormatter() - formatter.numberFormatter.maximumFractionDigits = 0 - let measurement = Measurement(value: (value ?? 0), unit: UnitTemperature.celsius) - - return formatter.string(from: measurement).replacingOccurrences(of: "C", with: "").replacingOccurrences(of: "F", with: "") + return Temperature(value ?? 0).replacingOccurrences(of: "C", with: "").replacingOccurrences(of: "F", with: "") case SensorType.Voltage.rawValue: return String(format: "%.1f\(unit)", value ?? 0) case SensorType.Power.rawValue: diff --git a/Stats/Supporting Files/en.lproj/Localizable.strings b/Stats/Supporting Files/en.lproj/Localizable.strings index c0dd56c5..dc9b8820 100644 --- a/Stats/Supporting Files/en.lproj/Localizable.strings +++ b/Stats/Supporting Files/en.lproj/Localizable.strings @@ -98,6 +98,9 @@ "Used disk memory" = "Used %0 from %1"; "Disk to show" = "Disk to show"; +// Sensors +"Temperature unit" = "Temperature unit"; + // Network "Uploading" = "Upload"; "Downloading" = "Download"; diff --git a/Stats/Supporting Files/pl.lproj/Localizable.strings b/Stats/Supporting Files/pl.lproj/Localizable.strings index e708da21..ea24f976 100644 --- a/Stats/Supporting Files/pl.lproj/Localizable.strings +++ b/Stats/Supporting Files/pl.lproj/Localizable.strings @@ -98,6 +98,9 @@ "Used disk memory" = "Wykorzystano %0 z %1"; "Disk to show" = "Aktywny dysk"; +// Sensors +"Temperature unit" = "Jednostka temperatury"; + // Network "Uploading" = "Wysyłka"; "Downloading" = "Pobieranie"; diff --git a/Stats/Supporting Files/ru.lproj/Localizable.strings b/Stats/Supporting Files/ru.lproj/Localizable.strings index f68cfda6..aefe18c0 100644 --- a/Stats/Supporting Files/ru.lproj/Localizable.strings +++ b/Stats/Supporting Files/ru.lproj/Localizable.strings @@ -93,11 +93,14 @@ "Compressed" = "Сжатая"; "Free" = "Свободная"; -// DiskF +// Disk "Show removable disks" = "Показать съемные диски"; "Used disk memory" = "Использовано %0 с %1"; "Disk to show" = "Активный диск"; +// Sensors +"Temperature unit" = "Единица измерения температуры"; + // Network "Uploading" = "Висилання"; "Downloading" = "Загрузка"; diff --git a/Stats/Supporting Files/tr.lproj/Localizable.strings b/Stats/Supporting Files/tr.lproj/Localizable.strings index 36eac679..c15ecbbd 100644 --- a/Stats/Supporting Files/tr.lproj/Localizable.strings +++ b/Stats/Supporting Files/tr.lproj/Localizable.strings @@ -98,6 +98,9 @@ "Used disk memory" = "kullanılan %0 , toplam %1"; "Disk to show" = "Gösterilecek disk"; +// Sensors +"Temperature unit" = "Sıcaklık birimi"; + // Network "Uploading" = "Yükleme"; "Downloading" = "İndirme"; diff --git a/Stats/Supporting Files/uk.lproj/Localizable.strings b/Stats/Supporting Files/uk.lproj/Localizable.strings index e6c3d7b5..f10a3d71 100644 --- a/Stats/Supporting Files/uk.lproj/Localizable.strings +++ b/Stats/Supporting Files/uk.lproj/Localizable.strings @@ -93,11 +93,14 @@ "Compressed" = "Стисненна"; "Free" = "Вільна"; -// DiskF +// Disk "Show removable disks" = "Показати зйомні диски"; "Used disk memory" = "Використано %0 з %1"; "Disk to show" = "Активний диск"; +// Sensors +"Temperature unit" = "Одиниця виміру температури"; + // Network "Uploading" = "Вислання"; "Downloading" = "Завантаження"; diff --git a/Stats/Supporting Files/zh-Hans.lproj/Localizable.strings b/Stats/Supporting Files/zh-Hans.lproj/Localizable.strings index f6dab607..82784681 100644 --- a/Stats/Supporting Files/zh-Hans.lproj/Localizable.strings +++ b/Stats/Supporting Files/zh-Hans.lproj/Localizable.strings @@ -98,6 +98,9 @@ "Used disk memory" = "%1 中已用 %0"; "Disk to show" = "显示的磁盘"; +// Sensors +"Temperature unit" = "Temperature unit"; + // Network "Uploading" = "上传"; "Downloading" = "下载"; diff --git a/StatsKit/extensions.swift b/StatsKit/extensions.swift index 5f6d7bfa..2e0aed3c 100644 --- a/StatsKit/extensions.swift +++ b/StatsKit/extensions.swift @@ -418,6 +418,43 @@ public extension NSView { return row } + + func SelectRow(frame: NSRect, title: String, action: Selector, items: [KeyValue_t], selected: String) -> NSView { + let row: NSView = NSView(frame: frame) + + let rowTitle: NSTextField = LabelField(frame: NSRect(x: 0, y: (row.frame.height - 16)/2, width: row.frame.width - 52, height: 17), title) + rowTitle.font = NSFont.systemFont(ofSize: 13, weight: .light) + rowTitle.textColor = .textColor + + let select: NSPopUpButton = NSPopUpButton(frame: NSRect(x: row.frame.width - 50, y: (row.frame.height-26)/2, width: 50, height: 26)) + select.target = self + select.action = action + + let menu = NSMenu() + items.forEach { (item) in + if item.key.contains("separator") { + menu.addItem(NSMenuItem.separator()) + } else { + let interfaceMenu = NSMenuItem(title: item.value, action: nil, keyEquivalent: "") + interfaceMenu.representedObject = item.key + menu.addItem(interfaceMenu) + if selected == item.key { + interfaceMenu.state = .on + } + } + } + + select.menu = menu + select.sizeToFit() + + rowTitle.setFrameSize(NSSize(width: row.frame.width - select.frame.width, height: rowTitle.frame.height)) + select.setFrameOrigin(NSPoint(x: row.frame.width - select.frame.width, y: select.frame.origin.y)) + + row.addSubview(select) + row.addSubview(rowTitle) + + return row + } } public extension Notification.Name { @@ -827,6 +864,25 @@ public enum updateIntervals: updateInterval { } extension updateIntervals: CaseIterable {} +public struct KeyValue_t { + let key: String + let value: String + let additional: Any? + + init(key: String, value: String, additional: Any? = nil) { + self.key = key + self.value = value + self.additional = additional + } +} + +public let TemperatureUnits: [KeyValue_t] = [ + KeyValue_t(key: "system", value: "System"), + KeyValue_t(key: "separator", value: "separator"), + KeyValue_t(key: "celsius", value: "Celsius", additional: UnitTemperature.celsius), + KeyValue_t(key: "fahrenheit", value: "Fahrenheit", additional: UnitTemperature.fahrenheit) +] + public func showNotification(title: String, subtitle: String, id: String = UUID().uuidString, icon: NSImage? = nil) -> NSUserNotification { let notification = NSUserNotification() @@ -962,3 +1018,19 @@ public func LocalizedString(_ key: String, _ params: String..., comment: String } return string } + +public func Temperature(_ value: Double) -> String { + let stringUnit: String = Store.shared.string(key: "temperature_units", defaultValue: "system") + let formatter = MeasurementFormatter() + formatter.numberFormatter.maximumFractionDigits = 0 + formatter.unitOptions = .providedUnit + + var measurement = Measurement(value: value, unit: UnitTemperature.celsius) + if stringUnit != "system" { + if let temperatureUnit = TemperatureUnits.first(where: { $0.key == stringUnit }), let unit = temperatureUnit.additional as? UnitTemperature { + measurement.convert(to: unit) + } + } + + return formatter.string(from: measurement) +} diff --git a/StatsKit/store.swift b/StatsKit/store.swift index 5fe15bba..6c04e0d9 100644 --- a/StatsKit/store.swift +++ b/StatsKit/store.swift @@ -12,6 +12,7 @@ import Cocoa public class Store { + public static let shared = Store() private let defaults = UserDefaults.standard public init() {}