feat: moved CPU and GPU notification settings to the new selector that allows specifying custom threshold and not from a predefined list

This commit is contained in:
Serhiy Mytrovtsiy
2024-05-06 19:04:25 +02:00
parent 89b7ac76db
commit 9e26cc36ba
3 changed files with 207 additions and 84 deletions

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}