diff --git a/Kit/Supporting Files/Assets.xcassets/tune.imageset/Contents.json b/Kit/Supporting Files/Assets.xcassets/tune.imageset/Contents.json new file mode 100644 index 00000000..db1581b1 --- /dev/null +++ b/Kit/Supporting Files/Assets.xcassets/tune.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "outline_tune_black_18pt_1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "outline_tune_black_18pt_2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "outline_tune_black_18pt_3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Kit/Supporting Files/Assets.xcassets/tune.imageset/outline_tune_black_18pt_1x.png b/Kit/Supporting Files/Assets.xcassets/tune.imageset/outline_tune_black_18pt_1x.png new file mode 100644 index 00000000..2ebaa3f7 Binary files /dev/null and b/Kit/Supporting Files/Assets.xcassets/tune.imageset/outline_tune_black_18pt_1x.png differ diff --git a/Kit/Supporting Files/Assets.xcassets/tune.imageset/outline_tune_black_18pt_2x.png b/Kit/Supporting Files/Assets.xcassets/tune.imageset/outline_tune_black_18pt_2x.png new file mode 100644 index 00000000..6e4125fe Binary files /dev/null and b/Kit/Supporting Files/Assets.xcassets/tune.imageset/outline_tune_black_18pt_2x.png differ diff --git a/Kit/Supporting Files/Assets.xcassets/tune.imageset/outline_tune_black_18pt_3x.png b/Kit/Supporting Files/Assets.xcassets/tune.imageset/outline_tune_black_18pt_3x.png new file mode 100644 index 00000000..4eaf2a18 Binary files /dev/null and b/Kit/Supporting Files/Assets.xcassets/tune.imageset/outline_tune_black_18pt_3x.png differ diff --git a/Kit/helpers.swift b/Kit/helpers.swift index fb05a6bb..8f876919 100644 --- a/Kit/helpers.swift +++ b/Kit/helpers.swift @@ -303,7 +303,6 @@ public func separatorView(_ title: String, origin: NSPoint = NSPoint(x: 0, y: 0) labelView.alignment = .center labelView.textColor = .secondaryLabelColor labelView.font = NSFont.systemFont(ofSize: 12, weight: .medium) - labelView.stringValue = title view.addSubview(labelView) diff --git a/Kit/types.swift b/Kit/types.swift index 6540f80c..b168344c 100644 --- a/Kit/types.swift +++ b/Kit/types.swift @@ -246,6 +246,7 @@ public extension Notification.Name { static let widgetRearrange = Notification.Name("widgetRearrange") static let moduleRearrange = Notification.Name("moduleRearrange") static let pause = Notification.Name("pause") + static let toggleFanControl = Notification.Name("toggleFanControl") } public var isARM: Bool { diff --git a/Modules/Sensors/popup.swift b/Modules/Sensors/popup.swift index f288ae85..c169602f 100644 --- a/Modules/Sensors/popup.swift +++ b/Modules/Sensors/popup.swift @@ -21,7 +21,7 @@ private struct Sensor_t: KeyValue_p { var index: Int { get { - return Store.shared.int(key: "sensors_\(self.key)_index", defaultValue: -1) + Store.shared.int(key: "sensors_\(self.key)_index", defaultValue: -1) } set { Store.shared.set(key: "sensors_\(self.key)_index", value: newValue) @@ -37,17 +37,28 @@ private struct Sensor_t: KeyValue_p { internal class Popup: PopupWrapper { private var list: [String: NSView] = [:] + private var unknownSensorsState: Bool { Store.shared.bool(key: "Sensors_unknown", defaultValue: false) } private var sensors: [Sensor_p] = [] private let settingsView: NSStackView = SettingsContainerView() + private var fanControlState: Bool { + get { + Store.shared.bool(key: "Sensors_fanControl", defaultValue: true) + } + set { + Store.shared.set(key: "Sensors_fanControl", value: newValue) + } + } + public init() { super.init(frame: NSRect( x: 0, y: 0, width: Constants.Popup.width, height: 0)) self.orientation = .vertical self.spacing = 0 + self.translatesAutoresizingMaskIntoConstraints = false } required init?(coder: NSCoder) { @@ -68,6 +79,8 @@ internal class Popup: PopupWrapper { } if !fans.isEmpty { + self.addArrangedSubview(self.fansSeparatorView()) + let container = NSStackView() container.orientation = .vertical container.spacing = Constants.Popup.margins @@ -99,7 +112,7 @@ internal class Popup: PopupWrapper { types.append(s.type) } } - + types.forEach { (typ: SensorType) in var filtered = sensors.filter{ $0.type == typ } var groups: [SensorGroup] = [] @@ -108,44 +121,42 @@ internal class Popup: PopupWrapper { groups.append(s.group) } } - + if !reload { let header = NSStackView() header.heightAnchor.constraint(equalToConstant: Constants.Settings.row).isActive = true header.spacing = 0 - + let titleField: NSTextField = LabelField(frame: NSRect(x: 0, y: 0, width: 0, height: 0), localizedString(typ.rawValue)) titleField.font = NSFont.systemFont(ofSize: 13, weight: .medium) titleField.textColor = .labelColor - + header.addArrangedSubview(titleField) header.addArrangedSubview(NSView()) - + self.settingsView.addArrangedSubview(header) - + let container = NSStackView() container.orientation = .vertical container.edgeInsets = NSEdgeInsets(top: 0, left: Constants.Settings.margin, bottom: 0, right: Constants.Settings.margin) container.spacing = 0 - + groups.forEach { (group: SensorGroup) in filtered.filter{ $0.group == group }.forEach { (s: Sensor_p) in - let row: NSView = toggleSettingRow(title: s.name, action: #selector(self.toggleFan), state: s.popupState) + let row: NSView = toggleSettingRow(title: s.name, action: #selector(self.toggleSensor), state: s.popupState) row.subviews.filter{ $0 is NSControl }.forEach { (control: NSView) in control.identifier = NSUserInterfaceItemIdentifier(rawValue: s.key) } container.addArrangedSubview(row) } } - + self.settingsView.addArrangedSubview(container) } - - // popup - + filtered = filtered.filter{ $0.popupState } if filtered.isEmpty { return } - + self.addArrangedSubview(separatorView(localizedString(typ.rawValue), width: self.frame.width)) groups.forEach { (group: SensorGroup) in filtered.filter{ $0.group == group }.forEach { (s: Sensor_p) in @@ -205,11 +216,53 @@ internal class Popup: PopupWrapper { // MARK: helpers - @objc private func toggleFan(_ sender: NSControl) { + private func fansSeparatorView() -> NSView { + let view: NSStackView = NSStackView(frame: NSRect(x: 0, y: 0, width: self.frame.width, height: 26)) + view.widthAnchor.constraint(equalToConstant: view.frame.width).isActive = true + view.heightAnchor.constraint(equalToConstant: view.bounds.height).isActive = true + view.orientation = .horizontal + view.spacing = 0 + view.distribution = .fillEqually + view.alignment = .top + + let labelView: NSTextField = TextView() + labelView.stringValue = localizedString("Fans") + labelView.alignment = .center + labelView.textColor = .secondaryLabelColor + labelView.font = NSFont.systemFont(ofSize: 12, weight: .medium) + + let btnContainer = NSView() + + let button = NSButton() + button.frame = CGRect(x: (self.frame.width/3)-20, y: 10, width: 15, height: 15) + button.bezelStyle = .regularSquare + button.isBordered = false + button.imageScaling = NSImageScaling.scaleAxesIndependently + button.contentTintColor = .lightGray + button.action = #selector(self.toggleFanControl) + button.target = self + button.toolTip = localizedString("Control") + button.image = Bundle(for: Module.self).image(forResource: "tune")! + + btnContainer.addSubview(button) + + view.addArrangedSubview(NSView()) + view.addArrangedSubview(labelView) + view.addArrangedSubview(btnContainer) + + return view + } + + @objc private func toggleSensor(_ sender: NSControl) { guard let id = sender.identifier else { return } Store.shared.set(key: "sensor_\(id.rawValue)_popup", value: controlState(sender)) self.setup(reload: true) } + + @objc private func toggleFanControl() { + self.fanControlState = !self.fanControlState + NotificationCenter.default.post(name: .toggleFanControl, object: nil, userInfo: ["state": self.fanControlState]) + } } // MARK: - Sensor view @@ -391,7 +444,7 @@ internal class FanView: NSStackView { private var speedState: Bool { Store.shared.bool(key: "Sensors_speed", defaultValue: false) } - private var fansSyncState: Bool { + private var syncState: Bool { Store.shared.bool(key: "Sensors_fansSync", defaultValue: false) } private var speed: Double { @@ -403,6 +456,7 @@ internal class FanView: NSStackView { } } private var resetModeAfterSleep: Bool = false + private var controlState: Bool private var horizontalMargin: CGFloat { self.edgeInsets.top + self.edgeInsets.bottom + (self.spacing*CGFloat(self.arrangedSubviews.count)) @@ -414,6 +468,7 @@ internal class FanView: NSStackView { public init(_ fan: Fan, width: CGFloat, callback: @escaping (() -> Void)) { self.fan = fan self.sizeCallback = callback + self.controlState = Store.shared.bool(key: "Sensors_fanControl", defaultValue: true) let inset: CGFloat = 5 super.init(frame: NSRect(x: 0, y: 0, width: width - (inset*2), height: 0)) @@ -432,28 +487,13 @@ internal class FanView: NSStackView { self.layer?.backgroundColor = NSColor.red.cgColor self.nameAndSpeed() - - if !SMCHelper.shared.isInstalled { - if let v = self.helperView { - self.addArrangedSubview(v) - } - } else { - if self.fan.maxSpeed != self.fan.minSpeed, let v = self.buttonsView { - self.addArrangedSubview(v) - } - if fan.mode == .forced, let view = self.controlView { - self.addArrangedSubview(view) - } - } - - let h = self.arrangedSubviews.map({ $0.bounds.height }).reduce(0, +) - self.setFrameSize(NSSize(width: self.frame.width, height: h + self.horizontalMargin)) - self.sizeCallback() + self.setupControls() NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(self.wakeListener), name: NSWorkspace.didWakeNotification, object: nil) NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(self.sleepListener), name: NSWorkspace.willSleepNotification, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(syncFanSpeed), name: .syncFansControl, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(changeHelperState), name: .fanHelperState, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.syncFanSpeed), name: .syncFansControl, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.changeHelperState), name: .fanHelperState, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.controlCallback), name: .toggleFanControl, object: nil) if let fanMode = self.fan.customMode, self.speedState && fanMode != FanMode.automatic { SMCHelper.shared.setFanMode(fan.id, mode: fanMode.rawValue) @@ -473,7 +513,9 @@ internal class FanView: NSStackView { deinit { NSWorkspace.shared.notificationCenter.removeObserver(self) - NotificationCenter.default.removeObserver(self) + NotificationCenter.default.removeObserver(self, name: .syncFansControl, object: nil) + NotificationCenter.default.removeObserver(self, name: .fanHelperState, object: nil) + NotificationCenter.default.removeObserver(self, name: .toggleSettings, object: nil) } override func updateLayer() { @@ -496,7 +538,6 @@ internal class FanView: NSStackView { let value = self.fan.value let valueField: NSTextField = TextView() valueField.font = NSFont.systemFont(ofSize: 13, weight: .regular) - valueField.stringValue = self.fan.formattedValue valueField.alignment = .right valueField.stringValue = "\(self.fan.percentage)%" valueField.toolTip = "\(value)" @@ -766,7 +807,7 @@ internal class FanView: NSStackView { } @objc private func syncFanSpeed(_ notification: Notification) { - guard self.fansSyncState else { return } + guard self.syncState else { return } var speed = notification.userInfo?["speed"] as? Int if let percentage = notification.userInfo?["percentage"] as? Int { speed = ((Int(self.fan.maxSpeed - self.fan.minSpeed)*percentage)/100) + Int(self.fan.minSpeed) @@ -819,20 +860,28 @@ internal class FanView: NSStackView { } } - private func setupControls(_ state: Bool) { - if state { + private func setupControls(_ isInstalled: Bool? = nil) { + let helperState = isInstalled ?? SMCHelper.shared.isInstalled + + if !self.controlState { self.helperView?.removeFromSuperview() - if self.fan.maxSpeed != self.fan.minSpeed, let v = self.buttonsView { - self.addArrangedSubview(v) - } - if self.fan.mode == .forced, let v = self.controlView { - self.addArrangedSubview(v) - } - } else { - self.buttonsView?.removeFromSuperview() self.controlView?.removeFromSuperview() - if let v = self.helperView { - self.addArrangedSubview(v) + self.buttonsView?.removeFromSuperview() + } else { + if helperState { + self.helperView?.removeFromSuperview() + if self.fan.maxSpeed != self.fan.minSpeed, let v = self.buttonsView { + self.addArrangedSubview(v) + } + if self.fan.mode == .forced, let v = self.controlView { + self.addArrangedSubview(v) + } + } else { + self.buttonsView?.removeFromSuperview() + self.controlView?.removeFromSuperview() + if let v = self.helperView { + self.addArrangedSubview(v) + } } } @@ -845,6 +894,12 @@ internal class FanView: NSStackView { guard let state = notification.userInfo?["state"] as? Bool else { return } self.setupControls(state) } + + @objc private func controlCallback(_ notification: Notification) { + guard let state = notification.userInfo?["state"] as? Bool else { return } + self.controlState = state + self.setupControls() + } } private class ModeButtons: NSStackView {