From 9e26cc36ba439e2560fe4c7d06d62d9e18b4cdcc Mon Sep 17 00:00:00 2001 From: Serhiy Mytrovtsiy Date: Mon, 6 May 2024 19:04:25 +0200 Subject: [PATCH] feat: moved CPU and GPU notification settings to the new selector that allows specifying custom threshold and not from a predefined list --- Kit/helpers.swift | 65 ++++++++++- Modules/CPU/notifications.swift | 186 +++++++++++++++++++++----------- Modules/GPU/notifications.swift | 40 ++++--- 3 files changed, 207 insertions(+), 84 deletions(-) diff --git a/Kit/helpers.swift b/Kit/helpers.swift index 2a170ea5..fbba1209 100644 --- a/Kit/helpers.swift +++ b/Kit/helpers.swift @@ -1346,7 +1346,7 @@ var isDarkMode: Bool { public class PreferencesSection: NSStackView { private let container: NSStackView = NSStackView() - public init(label: String = "", _ components: [PreferencesRow] = []) { + public init(label: String = "", _ components: [NSView] = []) { super.init(frame: .zero) self.orientation = .vertical @@ -1489,16 +1489,29 @@ public func restartApp(_ sender: Any, afterDelay seconds: TimeInterval = 0.5) -> exit(0) } -public class StepperInput: NSStackView, NSTextFieldDelegate { - public var callback: ((Int) -> Void) = {_ in } +public class StepperInput: NSStackView, NSTextFieldDelegate, PreferencesSwitchWith_p { + public var callback: ((Int) -> Void) private let value: NSTextField = NSTextField() private let stepper: NSStepper = NSStepper() + private var symbol: NSTextField? = nil private let range: NSRange? - public init(_ value: Int, range: NSRange? = nil) { + private var _isEnabled: Bool = true + public var isEnabled: Bool { + get { self._isEnabled } + set { + self.value.isEnabled = newValue + self.stepper.isEnabled = newValue + self.symbol?.isEnabled = newValue + self._isEnabled = newValue + } + } + + public init(_ value: Int, range: NSRange? = nil, symbol: String? = nil, callback: @escaping (Int) -> Void = {_ in }) { self.range = range + self.callback = callback super.init(frame: .zero) @@ -1514,6 +1527,7 @@ public class StepperInput: NSStackView, NSTextFieldDelegate { self.value.focusRingType = .none self.value.delegate = self self.value.stringValue = "\(value)" + self.value.translatesAutoresizingMaskIntoConstraints = false self.stepper.font = NSFont.systemFont(ofSize: 12, weight: .regular) self.stepper.doubleValue = Double(value)/100 @@ -1528,6 +1542,13 @@ public class StepperInput: NSStackView, NSTextFieldDelegate { self.addArrangedSubview(self.value) self.addArrangedSubview(self.stepper) + + if let symbol { + let symbol: NSTextField = LabelField(symbol) + symbol.textColor = .textColor + self.addArrangedSubview(symbol) + self.symbol = symbol + } } required init?(coder: NSCoder) { @@ -1561,3 +1582,39 @@ public class StepperInput: NSStackView, NSTextFieldDelegate { self.callback(value) } } + +public protocol PreferencesSwitchWith_p: NSView { + var isEnabled: Bool { get set } +} +public class PreferencesSwitch: NSStackView { + private let action: (_ sender: NSControl) -> Void + private let with: PreferencesSwitchWith_p + + public init(action: @escaping (_ sender: NSControl) -> Void, state: Bool, with: PreferencesSwitchWith_p) { + self.action = action + self.with = with + + super.init(frame: .zero) + + self.orientation = .horizontal + self.alignment = .centerY + self.spacing = Constants.Settings.margin + + let btn = switchView(action: #selector(self.callback), state: state) + with.widthAnchor.constraint(equalToConstant: 68).isActive = true + with.isEnabled = state + + self.addArrangedSubview(NSView()) + self.addArrangedSubview(btn) + self.addArrangedSubview(with) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc func callback(_ sender: NSControl) { + self.action(sender) + self.with.isEnabled = controlState(sender) + } +} diff --git a/Modules/CPU/notifications.swift b/Modules/CPU/notifications.swift index 248893fd..1a1dfb58 100644 --- a/Modules/CPU/notifications.swift +++ b/Modules/CPU/notifications.swift @@ -19,56 +19,99 @@ class Notifications: NotificationsWrapper { private let eCoresLoadID: String = "eCoresUsage" private let pCoresLoadID: String = "pCoresUsage" - private var totalLoadLevel: String = "" - private var systemLoadLevel: String = "" - private var userLoadLevel: String = "" - private var eCoresLoadLevel: String = "" - private var pCoresLoadLevel: String = "" + private var totalLoadState: Bool = false + private var systemLoadState: Bool = false + private var userLoadState: Bool = false + private var eCoresLoadState: Bool = false + private var pCoresLoadState: Bool = false + + private var totalLoad: Int = 75 + private var systemLoad: Int = 75 + private var userLoad: Int = 75 + private var eCoresLoad: Int = 75 + private var pCoresLoad: Int = 75 public init(_ module: ModuleType) { super.init(module, [self.totalLoadID, self.systemLoadID, self.userLoadID, self.eCoresLoadID, self.pCoresLoadID]) - if Store.shared.exist(key: "\(self.module)_notificationLevel") { - let value = Store.shared.string(key: "\(self.module)_notificationLevel", defaultValue: self.totalLoadLevel) - Store.shared.set(key: "\(self.module)_notifications_totalLoad", value: value) - Store.shared.remove("\(self.module)_notificationLevel") + if Store.shared.exist(key: "\(self.module)_notifications_totalLoad") { + let value = Store.shared.string(key: "\(self.module)_notifications_totalLoad", defaultValue: "") + if let v = Double(value) { + Store.shared.set(key: "\(self.module)_notifications_totalLoad_state", value: true) + Store.shared.set(key: "\(self.module)_notifications_totalLoad_value", value: Int(v*100)) + Store.shared.remove("\(self.module)_notifications_totalLoad") + } + } + if Store.shared.exist(key: "\(self.module)_notifications_systemLoad") { + let value = Store.shared.string(key: "\(self.module)_notifications_systemLoad", defaultValue: "") + if let v = Double(value) { + Store.shared.set(key: "\(self.module)_notifications_systemLoad_state", value: true) + Store.shared.set(key: "\(self.module)_notifications_systemLoad_value", value: Int(v*100)) + Store.shared.remove("\(self.module)_notifications_systemLoad") + } + } + if Store.shared.exist(key: "\(self.module)_notifications_userLoad") { + let value = Store.shared.string(key: "\(self.module)_notifications_userLoad", defaultValue: "") + if let v = Double(value) { + Store.shared.set(key: "\(self.module)_notifications_userLoad_state", value: true) + Store.shared.set(key: "\(self.module)_notifications_userLoad_value", value: Int(v*100)) + Store.shared.remove("\(self.module)_notifications_userLoad") + } } - self.totalLoadLevel = Store.shared.string(key: "\(self.module)_notifications_totalLoad", defaultValue: self.totalLoadLevel) - self.systemLoadLevel = Store.shared.string(key: "\(self.module)_notifications_systemLoad", defaultValue: self.systemLoadLevel) - self.userLoadLevel = Store.shared.string(key: "\(self.module)_notifications_userLoad", defaultValue: self.userLoadLevel) - self.eCoresLoadLevel = Store.shared.string(key: "\(self.module)_notifications_eCoresLoad", defaultValue: self.eCoresLoadLevel) - self.pCoresLoadLevel = Store.shared.string(key: "\(self.module)_notifications_pCoresLoad", defaultValue: self.pCoresLoadLevel) + if Store.shared.exist(key: "\(self.module)_notifications_eCoresLoad") { + let value = Store.shared.string(key: "\(self.module)_notifications_eCoresLoad", defaultValue: "") + if let v = Double(value) { + Store.shared.set(key: "\(self.module)_notifications_eCoresLoad_state", value: true) + Store.shared.set(key: "\(self.module)_notifications_eCoresLoad_value", value: Int(v*100)) + Store.shared.remove("\(self.module)_notifications_eCoresLoad") + } + } + if Store.shared.exist(key: "\(self.module)_notifications_pCoresLoad") { + let value = Store.shared.string(key: "\(self.module)_notifications_pCoresLoad", defaultValue: "") + if let v = Double(value) { + Store.shared.set(key: "\(self.module)_notifications_pCoresLoad_state", value: true) + Store.shared.set(key: "\(self.module)_notifications_pCoresLoad_value", value: Int(v*100)) + Store.shared.remove("\(self.module)_notifications_pCoresLoad") + } + } + + self.totalLoadState = Store.shared.bool(key: "\(self.module)_notifications_totalLoad_state", defaultValue: self.totalLoadState) + self.totalLoad = Store.shared.int(key: "\(self.module)_notifications_totalLoad_value", defaultValue: self.totalLoad) + self.systemLoadState = Store.shared.bool(key: "\(self.module)_notifications_systemLoad_state", defaultValue: self.systemLoadState) + self.systemLoad = Store.shared.int(key: "\(self.module)_notifications_systemLoad_value", defaultValue: self.systemLoad) + self.userLoadState = Store.shared.bool(key: "\(self.module)_notifications_userLoad_state", defaultValue: self.userLoadState) + self.userLoad = Store.shared.int(key: "\(self.module)_notifications_userLoad_value", defaultValue: self.userLoad) + + self.eCoresLoadState = Store.shared.bool(key: "\(self.module)_notifications_eCoresLoad_state", defaultValue: self.eCoresLoadState) + self.eCoresLoad = Store.shared.int(key: "\(self.module)_notifications_eCoresLoad_value", defaultValue: self.eCoresLoad) + self.pCoresLoadState = Store.shared.bool(key: "\(self.module)_notifications_pCoresLoad_state", defaultValue: self.pCoresLoadState) + self.pCoresLoad = Store.shared.int(key: "\(self.module)_notifications_pCoresLoad_value", defaultValue: self.pCoresLoad) self.addArrangedSubview(PreferencesSection([ - PreferencesRow(localizedString("Total load"), component: selectView( - action: #selector(self.changeTotalLoad), - items: notificationLevels, - selected: self.totalLoadLevel + PreferencesRow(localizedString("Total load"), component: PreferencesSwitch( + action: self.toggleTotalLoad, state: self.totalLoadState, + with: StepperInput(self.totalLoad, range: NSRange(location: 1, length: 99), symbol: "%", callback: self.changeTotalLoad) )), - PreferencesRow(localizedString("System load"), component: selectView( - action: #selector(self.changeSystemLoad), - items: notificationLevels, - selected: self.systemLoadLevel + PreferencesRow(localizedString("System load"), component: PreferencesSwitch( + action: self.toggleSystemLoad, state: self.systemLoadState, + with: StepperInput(self.systemLoad, range: NSRange(location: 1, length: 99), symbol: "%", callback: self.changeSystemLoad) )), - PreferencesRow(localizedString("User load"), component: selectView( - action: #selector(self.changeUserLoad), - items: notificationLevels, - selected: self.userLoadLevel + PreferencesRow(localizedString("User load"), component: PreferencesSwitch( + action: self.toggleUserLoad, state: self.userLoadState, + with: StepperInput(self.userLoad, range: NSRange(location: 1, length: 99), symbol: "%", callback: self.changeUserLoad) )) ])) #if arch(arm64) self.addArrangedSubview(PreferencesSection([ - PreferencesRow(localizedString("Efficiency cores load"), component: selectView( - action: #selector(self.changeECoresLoad), - items: notificationLevels, - selected: self.eCoresLoadLevel + PreferencesRow(localizedString("Efficiency cores load"), component: PreferencesSwitch( + action: self.toggleECoresLoad, state: self.eCoresLoadState, + with: StepperInput(self.eCoresLoad, range: NSRange(location: 1, length: 99), symbol: "%", callback: self.changeECoresLoad) )), - PreferencesRow(localizedString("Performance cores load"), component: selectView( - action: #selector(self.changePCoresLoad), - items: notificationLevels, - selected: self.pCoresLoadLevel + PreferencesRow(localizedString("Performance cores load"), component: PreferencesSwitch( + action: self.togglePCoresLoad, state: self.pCoresLoadState, + with: StepperInput(self.pCoresLoad, range: NSRange(location: 1, length: 99), symbol: "%", callback: self.changePCoresLoad) )) ])) #endif @@ -81,61 +124,76 @@ class Notifications: NotificationsWrapper { internal func loadCallback(_ value: CPU_Load) { let title = localizedString("CPU usage threshold") - if let threshold = Double(self.totalLoadLevel) { + if self.totalLoadState { let subtitle = "\(localizedString("Total load")): \(Int((value.totalUsage)*100))%" - self.checkDouble(id: self.totalLoadID, value: value.totalUsage, threshold: threshold, title: title, subtitle: subtitle) + self.checkDouble(id: self.totalLoadID, value: value.totalUsage, threshold: Double(self.totalLoad)/100, title: title, subtitle: subtitle) } - if let threshold = Double(self.systemLoadLevel) { + if self.systemLoadState { let subtitle = "\(localizedString("System load")): \(Int((value.systemLoad)*100))%" - self.checkDouble(id: self.systemLoadID, value: value.systemLoad, threshold: threshold, title: title, subtitle: subtitle) + self.checkDouble(id: self.systemLoadID, value: value.systemLoad, threshold: Double(self.systemLoad)/100, title: title, subtitle: subtitle) } - if let threshold = Double(self.userLoadLevel) { + if self.userLoadState { let subtitle = "\(localizedString("User load")): \(Int((value.userLoad)*100))%" - self.checkDouble(id: self.userLoadID, value: value.userLoad, threshold: threshold, title: title, subtitle: subtitle) + self.checkDouble(id: self.userLoadID, value: value.userLoad, threshold: Double(self.userLoad)/100, title: title, subtitle: subtitle) } - if let threshold = Double(self.eCoresLoadLevel), let usage = value.usageECores { + if self.eCoresLoadState, let usage = value.usageECores { let subtitle = "\(localizedString("Efficiency cores load")): \(Int((usage)*100))%" - self.checkDouble(id: self.eCoresLoadID, value: usage, threshold: threshold, title: title, subtitle: subtitle) + self.checkDouble(id: self.eCoresLoadID, value: usage, threshold: Double(self.eCoresLoad)/100, title: title, subtitle: subtitle) } - if let threshold = Double(self.pCoresLoadLevel), let usage = value.usagePCores { + if self.pCoresLoadState, let usage = value.usagePCores { let subtitle = "\(localizedString("Performance cores load")): \(Int((usage)*100))%" - self.checkDouble(id: self.pCoresLoadID, value: usage, threshold: threshold, title: title, subtitle: subtitle) + self.checkDouble(id: self.pCoresLoadID, value: usage, threshold: Double(self.pCoresLoad)/100, title: title, subtitle: subtitle) } } // MARK: - change helpers - @objc private func changeTotalLoad(_ sender: NSMenuItem) { - guard let key = sender.representedObject as? String else { return } - self.totalLoadLevel = key.isEmpty ? "" : "\(Double(key) ?? 0)" - Store.shared.set(key: "\(self.module)_notifications_totalLoad", value: self.totalLoadLevel) + @objc private func toggleTotalLoad(_ sender: NSControl) { + self.totalLoadState = controlState(sender) + Store.shared.set(key: "\(self.module)_notifications_totalLoad_state", value: self.totalLoadState) + } + @objc private func changeTotalLoad(_ newValue: Int) { + self.totalLoad = newValue + Store.shared.set(key: "\(self.module)_notifications_totalLoad_value", value: self.totalLoad) } - @objc private func changeSystemLoad(_ sender: NSMenuItem) { - guard let key = sender.representedObject as? String else { return } - self.systemLoadLevel = key.isEmpty ? "" : "\(Double(key) ?? 0)" - Store.shared.set(key: "\(self.module)_notifications_systemLoad", value: self.systemLoadLevel) + @objc private func toggleSystemLoad(_ sender: NSControl) { + self.systemLoadState = controlState(sender) + Store.shared.set(key: "\(self.module)_notifications_systemLoad_state", value: self.systemLoadState) + } + @objc private func changeSystemLoad(_ newValue: Int) { + self.systemLoad = newValue + Store.shared.set(key: "\(self.module)_notifications_systemLoad_value", value: self.systemLoad) } - @objc private func changeUserLoad(_ sender: NSMenuItem) { - guard let key = sender.representedObject as? String else { return } - self.userLoadLevel = key.isEmpty ? "" : "\(Double(key) ?? 0)" - Store.shared.set(key: "\(self.module)_notifications_userLoad", value: self.userLoadLevel) + @objc private func toggleUserLoad(_ sender: NSControl) { + self.userLoadState = controlState(sender) + Store.shared.set(key: "\(self.module)_notifications_userLoad_state", value: self.userLoadState) + } + @objc private func changeUserLoad(_ newValue: Int) { + self.userLoad = newValue + Store.shared.set(key: "\(self.module)_notifications_userLoad_value", value: self.userLoad) } - @objc private func changeECoresLoad(_ sender: NSMenuItem) { - guard let key = sender.representedObject as? String else { return } - self.eCoresLoadLevel = key.isEmpty ? "" : "\(Double(key) ?? 0)" - Store.shared.set(key: "\(self.module)_notifications_eCoresLoad", value: self.eCoresLoadLevel) + @objc private func toggleECoresLoad(_ sender: NSControl) { + self.eCoresLoadState = controlState(sender) + Store.shared.set(key: "\(self.module)_notifications_eCoresLoad_state", value: self.eCoresLoadState) + } + @objc private func changeECoresLoad(_ newValue: Int) { + self.eCoresLoad = newValue + Store.shared.set(key: "\(self.module)_notifications_eCoresLoad_value", value: self.eCoresLoad) } - @objc private func changePCoresLoad(_ sender: NSMenuItem) { - guard let key = sender.representedObject as? String else { return } - self.pCoresLoadLevel = key.isEmpty ? "" : "\(Double(key) ?? 0)" - Store.shared.set(key: "\(self.module)_notifications_pCoresLoad", value: self.pCoresLoadLevel) + @objc private func togglePCoresLoad(_ sender: NSControl) { + self.pCoresLoadState = controlState(sender) + Store.shared.set(key: "\(self.module)_notifications_pCoresLoad_state", value: self.pCoresLoadState) + } + @objc private func changePCoresLoad(_ newValue: Int) { + self.pCoresLoad = newValue + Store.shared.set(key: "\(self.module)_notifications_pCoresLoad_value", value: self.pCoresLoad) } } diff --git a/Modules/GPU/notifications.swift b/Modules/GPU/notifications.swift index be7ebb58..d26b9d42 100644 --- a/Modules/GPU/notifications.swift +++ b/Modules/GPU/notifications.swift @@ -14,24 +14,29 @@ import Kit class Notifications: NotificationsWrapper { private let usageID: String = "usage" - private var usageLevel: String = "" + + private var usageState: Bool = false + private var usageLevel: Int = 75 public init(_ module: ModuleType) { super.init(module, [self.usageID]) - if Store.shared.exist(key: "\(self.module)_notificationLevel") { - let value = Store.shared.string(key: "\(self.module)_notificationLevel", defaultValue: self.usageLevel) - Store.shared.set(key: "\(self.module)_notifications_usage", value: value) - Store.shared.remove("\(self.module)_notificationLevel") + if Store.shared.exist(key: "\(self.module)_notifications_usage") { + let value = Store.shared.string(key: "\(self.module)_notifications_usage", defaultValue: "") + if let v = Double(value) { + Store.shared.set(key: "\(self.module)_notifications_usage_state", value: true) + Store.shared.set(key: "\(self.module)_notifications_usage_value", value: Int(v*100)) + Store.shared.remove("\(self.module)_notifications_usage") + } } - self.usageLevel = Store.shared.string(key: "\(self.module)_notifications_usage", defaultValue: self.usageLevel) + self.usageState = Store.shared.bool(key: "\(self.module)_notifications_usage_state", defaultValue: self.usageState) + self.usageLevel = Store.shared.int(key: "\(self.module)_notifications_usage_value", defaultValue: self.usageLevel) self.addArrangedSubview(PreferencesSection([ - PreferencesRow(localizedString("Usage"), component: selectView( - action: #selector(self.changeUsage), - items: notificationLevels, - selected: self.usageLevel + PreferencesRow(localizedString("Usage"), component: PreferencesSwitch( + action: self.toggleUsage, state: self.usageState, + with: StepperInput(self.usageLevel, range: NSRange(location: 1, length: 99), symbol: "%", callback: self.changeUsage) )) ])) } @@ -43,15 +48,18 @@ class Notifications: NotificationsWrapper { internal func usageCallback(_ value: Double) { let title = localizedString("GPU usage threshold") - if let threshold = Double(self.usageLevel) { + if self.usageState { let subtitle = localizedString("GPU usage is", "\(Int((value)*100))%") - self.checkDouble(id: self.usageID, value: value, threshold: threshold, title: title, subtitle: subtitle) + self.checkDouble(id: self.usageID, value: value, threshold: Double(self.usageLevel)/100, title: title, subtitle: subtitle) } } - @objc private func changeUsage(_ sender: NSMenuItem) { - guard let key = sender.representedObject as? String else { return } - self.usageLevel = key.isEmpty ? "" : "\(Double(key) ?? 0)" - Store.shared.set(key: "\(self.module)_notifications_usage", value: self.usageLevel) + @objc private func toggleUsage(_ sender: NSControl) { + self.usageState = controlState(sender) + Store.shared.set(key: "\(self.module)_notifications_usage_state", value: self.usageState) + } + @objc private func changeUsage(_ newValue: Int) { + self.usageLevel = newValue + Store.shared.set(key: "\(self.module)_notifications_usage_value", value: self.usageLevel) } }