mirror of
https://github.com/morgan9e/macos-stats
synced 2026-04-14 00:04:15 +09:00
feat: moved all modules settings to the new design
This commit is contained in:
@@ -305,130 +305,6 @@ public extension NSView {
|
||||
return view
|
||||
}
|
||||
|
||||
func selectSettingsRowV1(title: String, action: Selector, items: [String], selected: String) -> NSView {
|
||||
let view = NSStackView()
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.heightAnchor.constraint(equalToConstant: Constants.Settings.row).isActive = true
|
||||
view.orientation = .horizontal
|
||||
view.alignment = .centerY
|
||||
view.distribution = .fill
|
||||
view.spacing = 0
|
||||
|
||||
let titleField: NSTextField = LabelField(frame: NSRect(x: 0, y: 0, width: 0, height: 0), title)
|
||||
titleField.font = NSFont.systemFont(ofSize: 12, weight: .regular)
|
||||
titleField.textColor = .textColor
|
||||
|
||||
let select: NSPopUpButton = NSPopUpButton()
|
||||
select.target = self
|
||||
select.action = action
|
||||
|
||||
let menu = NSMenu()
|
||||
items.forEach { (color: String) in
|
||||
if color.contains("separator") {
|
||||
menu.addItem(NSMenuItem.separator())
|
||||
} else {
|
||||
let interfaceMenu = NSMenuItem(title: color, action: nil, keyEquivalent: "")
|
||||
menu.addItem(interfaceMenu)
|
||||
if selected == color {
|
||||
interfaceMenu.state = .on
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
select.menu = menu
|
||||
select.sizeToFit()
|
||||
|
||||
view.addArrangedSubview(titleField)
|
||||
view.addArrangedSubview(NSView())
|
||||
view.addArrangedSubview(select)
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
func fieldSettingRow(_ sender: NSTextFieldDelegate, title: String, value: String, placeholder: String? = nil, width: CGFloat = 215) -> NSView {
|
||||
let view: NSStackView = NSStackView()
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.heightAnchor.constraint(equalToConstant: Constants.Settings.row).isActive = true
|
||||
view.orientation = .horizontal
|
||||
view.alignment = .centerY
|
||||
view.distribution = .fill
|
||||
view.spacing = 0
|
||||
|
||||
let titleField: NSTextField = LabelField(frame: NSRect.zero, title)
|
||||
titleField.font = NSFont.systemFont(ofSize: 12, weight: .regular)
|
||||
titleField.textColor = .textColor
|
||||
|
||||
let valueField: NSTextField = NSTextField()
|
||||
valueField.font = NSFont.systemFont(ofSize: 12, weight: .regular)
|
||||
valueField.textColor = .textColor
|
||||
valueField.isEditable = true
|
||||
valueField.isSelectable = true
|
||||
valueField.isBezeled = false
|
||||
valueField.wantsLayer = true
|
||||
valueField.canDrawSubviewsIntoLayer = true
|
||||
valueField.usesSingleLineMode = true
|
||||
valueField.maximumNumberOfLines = 1
|
||||
valueField.focusRingType = .none
|
||||
valueField.stringValue = value
|
||||
valueField.delegate = sender
|
||||
if let placeholder {
|
||||
valueField.placeholderString = placeholder
|
||||
}
|
||||
valueField.alignment = .natural
|
||||
|
||||
view.addArrangedSubview(titleField)
|
||||
view.addArrangedSubview(NSView())
|
||||
view.addArrangedSubview(valueField)
|
||||
|
||||
valueField.widthAnchor.constraint(equalToConstant: width).isActive = true
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
func sliderSettingsRow(title: String, action: Selector, value: Int, initialValue: String, isHidden: Bool = false, min: Double = 1, max: Double = 100) -> NSView {
|
||||
let view: NSStackView = NSStackView()
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.heightAnchor.constraint(equalToConstant: Constants.Settings.row * 1.2).isActive = true
|
||||
view.orientation = .horizontal
|
||||
view.alignment = .centerY
|
||||
view.distribution = .fill
|
||||
view.spacing = 0
|
||||
view.isHidden = isHidden
|
||||
|
||||
let titleField: NSTextField = LabelField(frame: NSRect(x: 0, y: 0, width: 0, height: 0), title)
|
||||
titleField.font = NSFont.systemFont(ofSize: 12, weight: .regular)
|
||||
titleField.textColor = .textColor
|
||||
|
||||
let container: NSView = NSView()
|
||||
container.identifier = NSUserInterfaceItemIdentifier("container")
|
||||
|
||||
let valueField: NSTextField = LabelField(frame: NSRect(x: 0, y: 21, width: 195, height: 15), initialValue)
|
||||
valueField.font = NSFont.systemFont(ofSize: 12, weight: .regular)
|
||||
valueField.textColor = .textColor
|
||||
valueField.alignment = .center
|
||||
|
||||
let slider = NSSlider(frame: NSRect(x: 0, y: -5, width: 195, height: 0))
|
||||
slider.minValue = min
|
||||
slider.maxValue = max
|
||||
slider.intValue = Int32(value)
|
||||
slider.target = self
|
||||
slider.isContinuous = true
|
||||
slider.action = action
|
||||
slider.sizeToFit()
|
||||
|
||||
container.addSubview(valueField)
|
||||
container.addSubview(slider)
|
||||
|
||||
view.addArrangedSubview(titleField)
|
||||
view.addArrangedSubview(NSView())
|
||||
view.addArrangedSubview(container)
|
||||
|
||||
container.widthAnchor.constraint(equalToConstant: 195).isActive = true
|
||||
container.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
func selectView(action: Selector, items: [KeyValue_p], selected: String) -> NSPopUpButton {
|
||||
let select: NSPopUpButton = NSPopUpButton(frame: NSRect(x: 0, y: 0, width: 50, height: 28))
|
||||
select.target = self
|
||||
@@ -483,6 +359,33 @@ public extension NSView {
|
||||
field.isSelectable = true
|
||||
return field
|
||||
}
|
||||
|
||||
func sliderView(action: Selector, value: Int, initialValue: String, min: Double = 1, max: Double = 100, valueWidth: CGFloat = 40) -> NSView {
|
||||
let view: NSStackView = NSStackView()
|
||||
view.orientation = .horizontal
|
||||
view.widthAnchor.constraint(equalToConstant: 195).isActive = true
|
||||
|
||||
let valueField: NSTextField = LabelField(initialValue)
|
||||
valueField.font = NSFont.systemFont(ofSize: 12, weight: .regular)
|
||||
valueField.textColor = .textColor
|
||||
valueField.alignment = .center
|
||||
valueField.widthAnchor.constraint(equalToConstant: valueWidth).isActive = true
|
||||
|
||||
let slider = NSSlider()
|
||||
slider.controlSize = .small
|
||||
slider.minValue = min
|
||||
slider.maxValue = max
|
||||
slider.intValue = Int32(value)
|
||||
slider.target = self
|
||||
slider.isContinuous = true
|
||||
slider.action = action
|
||||
slider.sizeToFit()
|
||||
|
||||
view.addArrangedSubview(slider)
|
||||
view.addArrangedSubview(valueField)
|
||||
|
||||
return view
|
||||
}
|
||||
}
|
||||
|
||||
public class NSButtonWithPadding: NSButton {
|
||||
|
||||
@@ -960,17 +960,9 @@ public func process(path: String, arguments: [String]) -> String? {
|
||||
|
||||
public class SettingsContainerView: NSStackView {
|
||||
public init() {
|
||||
super.init(frame: NSRect(x: 0, y: 0, width: 0, height: 0))
|
||||
|
||||
super.init(frame: NSRect.zero)
|
||||
self.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.orientation = .vertical
|
||||
self.distribution = .gravityAreas
|
||||
self.edgeInsets = NSEdgeInsets(
|
||||
top: Constants.Settings.margin,
|
||||
left: Constants.Settings.margin,
|
||||
bottom: Constants.Settings.margin,
|
||||
right: Constants.Settings.margin
|
||||
)
|
||||
self.spacing = Constants.Settings.margin
|
||||
}
|
||||
|
||||
@@ -1351,24 +1343,32 @@ var isDarkMode: Bool {
|
||||
}
|
||||
|
||||
public class PreferencesSection: NSStackView {
|
||||
public init(_ components: [PreferencesRow] = []) {
|
||||
private let container: NSStackView = NSStackView()
|
||||
|
||||
public init(label: String = "", _ components: [PreferencesRow] = []) {
|
||||
super.init(frame: .zero)
|
||||
|
||||
self.orientation = .vertical
|
||||
self.spacing = 0
|
||||
|
||||
self.wantsLayer = true
|
||||
self.layer?.cornerRadius = 5
|
||||
self.layer?.borderColor = NSColor.separatorColor.withAlphaComponent(0.05).cgColor
|
||||
self.layer?.borderWidth = 1
|
||||
self.layer?.backgroundColor = NSColor.quaternaryLabelColor.withAlphaComponent(0.025).cgColor
|
||||
if label != "" {
|
||||
self.addLabel(label)
|
||||
}
|
||||
|
||||
self.edgeInsets = NSEdgeInsets(
|
||||
self.container.orientation = .vertical
|
||||
self.container.wantsLayer = true
|
||||
self.container.layer?.cornerRadius = 5
|
||||
self.container.layer?.borderColor = NSColor.separatorColor.withAlphaComponent(0.05).cgColor
|
||||
self.container.layer?.borderWidth = 1
|
||||
self.container.layer?.backgroundColor = NSColor.quaternaryLabelColor.withAlphaComponent(0.025).cgColor
|
||||
self.container.edgeInsets = NSEdgeInsets(
|
||||
top: Constants.Settings.margin/2,
|
||||
left: Constants.Settings.margin,
|
||||
bottom: Constants.Settings.margin/2,
|
||||
right: Constants.Settings.margin
|
||||
)
|
||||
self.spacing = Constants.Settings.margin/2
|
||||
self.container.spacing = Constants.Settings.margin/2
|
||||
self.addArrangedSubview(self.container)
|
||||
|
||||
for item in components {
|
||||
self.add(item)
|
||||
@@ -1380,21 +1380,39 @@ public class PreferencesSection: NSStackView {
|
||||
}
|
||||
|
||||
public override func updateLayer() {
|
||||
self.layer?.borderColor = NSColor.separatorColor.withAlphaComponent(0.05).cgColor
|
||||
self.layer?.backgroundColor = NSColor.quaternaryLabelColor.withAlphaComponent(0.025).cgColor
|
||||
self.container.layer?.borderColor = NSColor.separatorColor.withAlphaComponent(0.05).cgColor
|
||||
self.container.layer?.backgroundColor = NSColor.quaternaryLabelColor.withAlphaComponent(0.025).cgColor
|
||||
}
|
||||
|
||||
public func add(_ view: NSView) {
|
||||
if !self.subviews.isEmpty {
|
||||
self.addArrangedSubview(PreferencesSeparator())
|
||||
}
|
||||
private func addLabel(_ value: String) {
|
||||
let view = NSStackView()
|
||||
view.heightAnchor.constraint(equalToConstant: 26).isActive = true
|
||||
|
||||
let space = NSView()
|
||||
space.widthAnchor.constraint(equalToConstant: 4).isActive = true
|
||||
|
||||
let field: NSTextField = TextView()
|
||||
field.font = NSFont.systemFont(ofSize: 12, weight: .semibold)
|
||||
field.stringValue = value
|
||||
|
||||
view.addArrangedSubview(space)
|
||||
view.addArrangedSubview(field)
|
||||
view.addArrangedSubview(NSView())
|
||||
|
||||
self.addArrangedSubview(view)
|
||||
}
|
||||
|
||||
public func add(_ view: NSView) {
|
||||
if !self.container.subviews.isEmpty {
|
||||
self.container.addArrangedSubview(PreferencesSeparator())
|
||||
}
|
||||
self.container.addArrangedSubview(view)
|
||||
}
|
||||
|
||||
public func toggleVisibility(_ at: Int, newState: Bool) {
|
||||
for i in self.subviews.indices where i/2 == at && Double(i).remainder(dividingBy: 2) == 0 {
|
||||
self.subviews[i-1].isHidden = !newState
|
||||
self.subviews[i].isHidden = !newState
|
||||
for i in self.container.subviews.indices where i/2 == at && Double(i).remainder(dividingBy: 2) == 0 {
|
||||
self.container.subviews[i-1].isHidden = !newState
|
||||
self.container.subviews[i].isHidden = !newState
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1417,22 +1435,16 @@ public class PreferencesSeparator: NSView {
|
||||
}
|
||||
|
||||
public class PreferencesRow: NSStackView {
|
||||
public init(_ title: String? = nil, component: NSView) {
|
||||
public init(_ title: String? = nil, _ description: String? = nil, component: NSView) {
|
||||
super.init(frame: .zero)
|
||||
|
||||
self.orientation = .horizontal
|
||||
self.distribution = .fill
|
||||
self.alignment = .top
|
||||
self.edgeInsets = NSEdgeInsets(top: Constants.Settings.margin/2, left: 0, bottom: Constants.Settings.margin/2, right: 0)
|
||||
self.alignment = .centerY
|
||||
self.edgeInsets = NSEdgeInsets(top: Constants.Settings.margin/2, left: 0, bottom: (Constants.Settings.margin/2) - 1, right: 0)
|
||||
self.spacing = 0
|
||||
|
||||
let field: NSTextField = TextView()
|
||||
field.font = NSFont.systemFont(ofSize: 13, weight: .regular)
|
||||
if let title {
|
||||
field.stringValue = title
|
||||
self.addArrangedSubview(field)
|
||||
}
|
||||
|
||||
self.addArrangedSubview(self.text(title, description))
|
||||
self.addArrangedSubview(NSView())
|
||||
self.addArrangedSubview(component)
|
||||
}
|
||||
@@ -1440,4 +1452,38 @@ public class PreferencesRow: NSStackView {
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func text(_ title: String? = nil, _ description: String? = nil) -> NSView {
|
||||
let view: NSStackView = NSStackView()
|
||||
view.orientation = .vertical
|
||||
view.spacing = 0
|
||||
|
||||
if let title {
|
||||
let field: NSTextField = TextView()
|
||||
field.font = NSFont.systemFont(ofSize: 12, weight: .regular)
|
||||
field.stringValue = title
|
||||
view.addArrangedSubview(field)
|
||||
}
|
||||
|
||||
if let description {
|
||||
let field: NSTextField = TextView()
|
||||
field.font = NSFont.systemFont(ofSize: 12, weight: .regular)
|
||||
field.textColor = .secondaryLabelColor
|
||||
field.stringValue = description
|
||||
view.addArrangedSubview(field)
|
||||
view.addArrangedSubview(NSView())
|
||||
view.alignment = .leading
|
||||
}
|
||||
|
||||
return view
|
||||
}
|
||||
}
|
||||
|
||||
public func restartApp(_ sender: Any, afterDelay seconds: TimeInterval = 0.5) -> Never {
|
||||
let task = Process()
|
||||
task.launchPath = "/bin/sh"
|
||||
task.arguments = ["-c", "sleep \(seconds); open \"\(Bundle.main.bundlePath)\""]
|
||||
task.launch()
|
||||
NSApp.terminate(sender)
|
||||
exit(0)
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ public struct module_c {
|
||||
internal var availableWidgets: [widget_t] = []
|
||||
|
||||
internal var widgetsConfig: NSDictionary = NSDictionary()
|
||||
internal var settingsConfig: NSDictionary = NSDictionary()
|
||||
|
||||
init(in path: String) {
|
||||
let dict: NSDictionary = NSDictionary(contentsOfFile: path)!
|
||||
@@ -67,6 +68,10 @@ public struct module_c {
|
||||
|
||||
self.availableWidgets = list.sorted(by: { $0.1 < $1.1 }).map{ (widget_t(rawValue: $0.key) ?? .unknown) }
|
||||
}
|
||||
|
||||
if let settingsDict = dict["Settings"] as? NSDictionary {
|
||||
self.settingsConfig = settingsDict
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,12 +25,6 @@ open class NotificationsWrapper: NSStackView {
|
||||
self.orientation = .vertical
|
||||
self.distribution = .gravityAreas
|
||||
self.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.edgeInsets = NSEdgeInsets(
|
||||
top: Constants.Settings.margin,
|
||||
left: Constants.Settings.margin,
|
||||
bottom: Constants.Settings.margin,
|
||||
right: Constants.Settings.margin
|
||||
)
|
||||
self.spacing = Constants.Settings.margin
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,10 @@ public protocol Settings_v: NSView {
|
||||
open class Settings: NSStackView, Settings_p {
|
||||
private var config: UnsafePointer<module_c>
|
||||
private var widgets: [Widget]
|
||||
|
||||
private var segmentedControl: NSSegmentedControl?
|
||||
private var tabView: NSTabView?
|
||||
|
||||
private var moduleSettings: Settings_v?
|
||||
private var popupSettings: Popup_p?
|
||||
private var notificationsSettings: NotificationsWrapper?
|
||||
@@ -33,7 +37,7 @@ open class Settings: NSStackView, Settings_p {
|
||||
private var notificationsSettingsContainer: NSStackView?
|
||||
|
||||
private var enableControl: NSControl?
|
||||
private var oneViewRow: NSView?
|
||||
private var oneViewBtn: NSSwitch?
|
||||
|
||||
private let noWidgetsView: EmptyView = EmptyView(msg: localizedString("No available widgets to configure"))
|
||||
private let noPopupSettingsView: EmptyView = EmptyView(msg: localizedString("No options to configure for the popup in this module"))
|
||||
@@ -51,6 +55,9 @@ open class Settings: NSStackView, Settings_p {
|
||||
}
|
||||
}
|
||||
|
||||
private var isPopupSettingsAvailable: Bool
|
||||
private var isNotificationsSettingsAvailable: Bool
|
||||
|
||||
init(config: UnsafePointer<module_c>, widgets: UnsafeMutablePointer<[Widget]>, enabled: Bool, moduleSettings: Settings_v?, popupSettings: Popup_p?, notificationsSettings: NotificationsWrapper?) {
|
||||
self.config = config
|
||||
self.widgets = widgets.pointee
|
||||
@@ -58,6 +65,9 @@ open class Settings: NSStackView, Settings_p {
|
||||
self.popupSettings = popupSettings
|
||||
self.notificationsSettings = notificationsSettings
|
||||
|
||||
self.isPopupSettingsAvailable = config.pointee.settingsConfig["popup"] as? Bool ?? false
|
||||
self.isNotificationsSettingsAvailable = config.pointee.settingsConfig["notifications"] as? Bool ?? false
|
||||
|
||||
super.init(frame: NSRect.zero)
|
||||
|
||||
self.orientation = .vertical
|
||||
@@ -72,10 +82,53 @@ open class Settings: NSStackView, Settings_p {
|
||||
)
|
||||
|
||||
let widgetSelector = WidgetSelectorView(module: self.config.pointee.name, widgets: self.widgets, stateCallback: self.loadWidget)
|
||||
let tabView = self.settingsView()
|
||||
|
||||
self.addArrangedSubview(widgetSelector)
|
||||
self.addArrangedSubview(tabView)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(listenForOneView), name: .toggleOneView, object: nil)
|
||||
self.segmentedControl?.widthAnchor.constraint(equalTo: self.widthAnchor, constant: -(Constants.Settings.margin*2)).isActive = true
|
||||
}
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self, name: .toggleOneView, object: nil)
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public func setState(_ newState: Bool) {
|
||||
toggleNSControlState(self.enableControl, state: newState ? .on : .off)
|
||||
}
|
||||
|
||||
private func settingsView() -> NSView {
|
||||
let view = NSStackView()
|
||||
view.orientation = .vertical
|
||||
view.spacing = Constants.Settings.margin
|
||||
|
||||
var labels: [String] = [
|
||||
localizedString("Module"),
|
||||
localizedString("Widgets")
|
||||
]
|
||||
if self.isPopupSettingsAvailable {
|
||||
labels.append(localizedString("Popup"))
|
||||
}
|
||||
if self.isNotificationsSettingsAvailable {
|
||||
labels.append(localizedString("Notifications"))
|
||||
}
|
||||
|
||||
let segmentedControl = NSSegmentedControl(labels: labels, trackingMode: .selectOne, target: self, action: #selector(self.switchTabs))
|
||||
segmentedControl.segmentDistribution = .fillEqually
|
||||
segmentedControl.selectSegment(withTag: 0)
|
||||
self.segmentedControl = segmentedControl
|
||||
|
||||
let tabView = NSTabView()
|
||||
tabView.tabViewType = .topTabsBezelBorder
|
||||
tabView.tabViewBorderType = .line
|
||||
tabView.tabViewType = .noTabsNoBorder
|
||||
tabView.tabViewBorderType = .none
|
||||
tabView.drawsBackground = false
|
||||
self.tabView = tabView
|
||||
|
||||
let moduleTab: NSTabViewItem = NSTabViewItem()
|
||||
moduleTab.label = localizedString("Module")
|
||||
@@ -90,6 +143,7 @@ open class Settings: NSStackView, Settings_p {
|
||||
container.addArrangedSubview(scrollView)
|
||||
return container
|
||||
}()
|
||||
tabView.addTabViewItem(moduleTab)
|
||||
|
||||
let widgetTab: NSTabViewItem = NSTabViewItem()
|
||||
widgetTab.label = localizedString("Widgets")
|
||||
@@ -100,48 +154,38 @@ open class Settings: NSStackView, Settings_p {
|
||||
self.loadWidgetSettings()
|
||||
return view
|
||||
}()
|
||||
|
||||
let popupTab: NSTabViewItem = NSTabViewItem()
|
||||
popupTab.label = localizedString("Popup")
|
||||
popupTab.view = {
|
||||
let view = ScrollableStackView(frame: tabView.frame)
|
||||
view.stackView.spacing = 0
|
||||
self.popupSettingsContainer = view.stackView
|
||||
self.loadPopupSettings()
|
||||
return view
|
||||
}()
|
||||
|
||||
let notificationsTab: NSTabViewItem = NSTabViewItem()
|
||||
notificationsTab.label = localizedString("Notifications")
|
||||
notificationsTab.view = {
|
||||
let view = ScrollableStackView(frame: tabView.frame)
|
||||
view.stackView.spacing = 0
|
||||
self.notificationsSettingsContainer = view.stackView
|
||||
self.loadNotificationsSettings()
|
||||
return view
|
||||
}()
|
||||
|
||||
tabView.addTabViewItem(moduleTab)
|
||||
tabView.addTabViewItem(widgetTab)
|
||||
tabView.addTabViewItem(popupTab)
|
||||
tabView.addTabViewItem(notificationsTab)
|
||||
|
||||
self.addArrangedSubview(widgetSelector)
|
||||
self.addArrangedSubview(tabView)
|
||||
if self.isPopupSettingsAvailable {
|
||||
let popupTab: NSTabViewItem = NSTabViewItem()
|
||||
popupTab.label = localizedString("Popup")
|
||||
popupTab.view = {
|
||||
let view = ScrollableStackView(frame: tabView.frame)
|
||||
view.stackView.spacing = 0
|
||||
self.popupSettingsContainer = view.stackView
|
||||
self.loadPopupSettings()
|
||||
return view
|
||||
}()
|
||||
tabView.addTabViewItem(popupTab)
|
||||
}
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(listenForOneView), name: .toggleOneView, object: nil)
|
||||
}
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self, name: .toggleOneView, object: nil)
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public func setState(_ newState: Bool) {
|
||||
toggleNSControlState(self.enableControl, state: newState ? .on : .off)
|
||||
if self.isNotificationsSettingsAvailable {
|
||||
let notificationsTab: NSTabViewItem = NSTabViewItem()
|
||||
notificationsTab.label = localizedString("Notifications")
|
||||
notificationsTab.view = {
|
||||
let view = ScrollableStackView(frame: tabView.frame)
|
||||
view.stackView.spacing = 0
|
||||
self.notificationsSettingsContainer = view.stackView
|
||||
self.loadNotificationsSettings()
|
||||
return view
|
||||
}()
|
||||
tabView.addTabViewItem(notificationsTab)
|
||||
}
|
||||
|
||||
view.addArrangedSubview(segmentedControl)
|
||||
view.addArrangedSubview(tabView)
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
private func loadWidget() {
|
||||
@@ -169,31 +213,14 @@ open class Settings: NSStackView, Settings_p {
|
||||
}
|
||||
|
||||
if self.widgets.filter({ $0.isActive }).count > 1 {
|
||||
let container = NSStackView()
|
||||
container.orientation = .vertical
|
||||
container.distribution = .gravityAreas
|
||||
container.translatesAutoresizingMaskIntoConstraints = false
|
||||
container.edgeInsets = NSEdgeInsets(
|
||||
top: Constants.Settings.margin,
|
||||
left: Constants.Settings.margin,
|
||||
bottom: Constants.Settings.margin,
|
||||
right: Constants.Settings.margin
|
||||
)
|
||||
container.spacing = Constants.Settings.margin
|
||||
|
||||
let row = toggleSettingRow(
|
||||
title: "\(localizedString("Merge widgets"))",
|
||||
let btn = switchView(
|
||||
action: #selector(self.toggleOneView),
|
||||
state: self.oneViewState
|
||||
)
|
||||
container.addArrangedSubview(row)
|
||||
findAndToggleEnableNSControlState(row, state: !self.globalOneView)
|
||||
if self.globalOneView {
|
||||
findAndToggleNSControlState(row, state: .on)
|
||||
}
|
||||
self.oneViewRow = row
|
||||
|
||||
self.widgetSettingsContainer?.addArrangedSubview(container)
|
||||
self.oneViewBtn = btn
|
||||
self.widgetSettingsContainer?.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Merge widgets"), component: btn)
|
||||
]))
|
||||
}
|
||||
|
||||
for i in 0...list.count - 1 {
|
||||
@@ -225,6 +252,10 @@ open class Settings: NSStackView, Settings_p {
|
||||
}
|
||||
}
|
||||
|
||||
@objc func switchTabs(sender: NSSegmentedControl) {
|
||||
self.tabView?.selectTabViewItem(at: sender.selectedSegment)
|
||||
}
|
||||
|
||||
@objc private func toggleOneView(_ sender: NSControl) {
|
||||
guard !self.globalOneView else { return }
|
||||
self.oneViewState = controlState(sender)
|
||||
@@ -233,10 +264,9 @@ open class Settings: NSStackView, Settings_p {
|
||||
|
||||
@objc private func listenForOneView(_ notification: Notification) {
|
||||
guard notification.userInfo?["module"] == nil else { return }
|
||||
findAndToggleEnableNSControlState(self.oneViewRow, state: !self.globalOneView)
|
||||
|
||||
self.oneViewBtn?.isEnabled = !self.globalOneView
|
||||
if !self.globalOneView {
|
||||
findAndToggleNSControlState(self.oneViewRow, state: self.oneViewState ? .on : .off)
|
||||
self.oneViewBtn?.state = self.oneViewState ? .on : .off
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -527,20 +557,14 @@ internal class WidgetPreview: NSStackView {
|
||||
|
||||
internal class WidgetSettings: NSStackView {
|
||||
public init(title: String, image: NSImage, settingsView: NSView) {
|
||||
super.init(frame: NSRect(x: 0, y: 0, width: 0, height: 0))
|
||||
super.init(frame: NSRect.zero)
|
||||
|
||||
self.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.orientation = .vertical
|
||||
self.edgeInsets = NSEdgeInsets(
|
||||
top: 0,
|
||||
left: Constants.Settings.margin,
|
||||
bottom: 0,
|
||||
right: Constants.Settings.margin
|
||||
)
|
||||
self.spacing = 0
|
||||
|
||||
self.addArrangedSubview(self.header(title, image))
|
||||
self.addArrangedSubview(self.settings(settingsView))
|
||||
self.addArrangedSubview(settingsView)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@@ -597,22 +621,4 @@ internal class WidgetSettings: NSStackView {
|
||||
|
||||
return container
|
||||
}
|
||||
|
||||
private func settings(_ view: NSView) -> NSView {
|
||||
let container = NSStackView()
|
||||
container.orientation = .vertical
|
||||
container.spacing = 0
|
||||
container.wantsLayer = true
|
||||
container.layer?.backgroundColor = NSColor.init(calibratedWhite: 0.1, alpha: 0.06).cgColor
|
||||
container.layer?.cornerRadius = 4
|
||||
container.edgeInsets = NSEdgeInsets(
|
||||
top: 2,
|
||||
left: 2,
|
||||
bottom: 2,
|
||||
right: 2
|
||||
)
|
||||
container.addArrangedSubview(view)
|
||||
|
||||
return container
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,5 +77,12 @@
|
||||
<integer>4</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>Settings</key>
|
||||
<dict>
|
||||
<key>popup</key>
|
||||
<true/>
|
||||
<key>notifications</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -35,18 +35,18 @@ class Notifications: NotificationsWrapper {
|
||||
self.lowLevel = Store.shared.string(key: "\(self.module)_notifications_low", defaultValue: self.lowLevel)
|
||||
self.highLevel = Store.shared.string(key: "\(self.module)_notifications_high", defaultValue: self.highLevel)
|
||||
|
||||
self.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Low level notification"),
|
||||
action: #selector(self.changeLowLevel),
|
||||
items: notificationLevels,
|
||||
selected: self.lowLevel
|
||||
))
|
||||
self.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("High level notification"),
|
||||
action: #selector(self.changeHighLevel),
|
||||
items: notificationLevels,
|
||||
selected: self.highLevel
|
||||
))
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Low level notification"), component: selectView(
|
||||
action: #selector(self.changeLowLevel),
|
||||
items: notificationLevels,
|
||||
selected: self.lowLevel
|
||||
)),
|
||||
PreferencesRow(localizedString("High level notification"), component: selectView(
|
||||
action: #selector(self.changeHighLevel),
|
||||
items: notificationLevels,
|
||||
selected: self.highLevel
|
||||
))
|
||||
]))
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@@ -84,7 +84,6 @@ class Notifications: NotificationsWrapper {
|
||||
self.lowLevel = key.isEmpty ? "" : "\(Double(key) ?? 0)"
|
||||
Store.shared.set(key: "\(self.module)_notifications_low", value: self.lowLevel)
|
||||
}
|
||||
|
||||
@objc private func changeHighLevel(_ sender: NSMenuItem) {
|
||||
guard let key = sender.representedObject as? String else { return }
|
||||
self.highLevel = key.isEmpty ? "" : "\(Double(key) ?? 0)"
|
||||
|
||||
@@ -328,11 +328,12 @@ internal class Popup: PopupWrapper {
|
||||
public override func settings() -> NSView? {
|
||||
let view = SettingsContainerView()
|
||||
|
||||
view.addArrangedSubview(toggleSettingRow(
|
||||
title: localizedString("Colorize battery"),
|
||||
action: #selector(toggleColor),
|
||||
state: self.colorState
|
||||
))
|
||||
view.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Colorize battery"), component: switchView(
|
||||
action: #selector(self.toggleColor),
|
||||
state: self.colorState
|
||||
))
|
||||
]))
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
@@ -28,16 +28,8 @@ internal class Settings: NSStackView, Settings_v {
|
||||
self.numberOfProcesses = Store.shared.int(key: "\(self.title)_processes", defaultValue: self.numberOfProcesses)
|
||||
self.timeFormat = Store.shared.string(key: "\(self.title)_timeFormat", defaultValue: self.timeFormat)
|
||||
|
||||
super.init(frame: NSRect(x: 0, y: 0, width: 0, height: 0))
|
||||
|
||||
super.init(frame: NSRect.zero)
|
||||
self.orientation = .vertical
|
||||
self.distribution = .gravityAreas
|
||||
self.edgeInsets = NSEdgeInsets(
|
||||
top: Constants.Settings.margin,
|
||||
left: Constants.Settings.margin,
|
||||
bottom: Constants.Settings.margin,
|
||||
right: Constants.Settings.margin
|
||||
)
|
||||
self.spacing = Constants.Settings.margin
|
||||
}
|
||||
|
||||
@@ -48,20 +40,22 @@ internal class Settings: NSStackView, Settings_v {
|
||||
public func load(widgets: [widget_t]) {
|
||||
self.subviews.forEach{ $0.removeFromSuperview() }
|
||||
|
||||
self.addArrangedSubview(selectSettingsRowV1(
|
||||
title: localizedString("Number of top processes"),
|
||||
action: #selector(changeNumberOfProcesses),
|
||||
items: NumbersOfProcesses.map{ "\($0)" },
|
||||
selected: "\(self.numberOfProcesses)"
|
||||
))
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Number of top processes"), component: selectView(
|
||||
action: #selector(self.changeNumberOfProcesses),
|
||||
items: NumbersOfProcesses.map{ KeyValue_t(key: "\($0)", value: "\($0)") },
|
||||
selected: "\(self.numberOfProcesses)"
|
||||
))
|
||||
]))
|
||||
|
||||
if !widgets.filter({ $0 == .battery }).isEmpty {
|
||||
self.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Time format"),
|
||||
action: #selector(toggleTimeFormat),
|
||||
items: ShortLong,
|
||||
selected: self.timeFormat
|
||||
))
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Time format"), component: selectView(
|
||||
action: #selector(toggleTimeFormat),
|
||||
items: ShortLong,
|
||||
selected: self.timeFormat
|
||||
))
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,11 +66,8 @@ internal class Settings: NSStackView, Settings_v {
|
||||
self.callbackWhenUpdateNumberOfProcesses()
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func toggleTimeFormat(_ sender: NSMenuItem) {
|
||||
guard let key = sender.representedObject as? String else {
|
||||
return
|
||||
}
|
||||
guard let key = sender.representedObject as? String else { return }
|
||||
self.timeFormat = key
|
||||
Store.shared.set(key: "\(self.title)_timeFormat", value: key)
|
||||
self.callback()
|
||||
|
||||
@@ -34,5 +34,12 @@
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>Settings</key>
|
||||
<dict>
|
||||
<key>popup</key>
|
||||
<false/>
|
||||
<key>notifications</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -16,23 +16,22 @@ internal class Settings: NSStackView, Settings_v {
|
||||
public var callback: (() -> Void) = {}
|
||||
|
||||
private var list: [String: Bool] = [:]
|
||||
|
||||
private let emptyView: EmptyView = EmptyView(msg: localizedString("No Bluetooth devices are available"))
|
||||
private var section: PreferencesSection = PreferencesSection()
|
||||
|
||||
public init() {
|
||||
super.init(frame: NSRect(x: 0, y: 0, width: 0, height: 0))
|
||||
|
||||
self.orientation = .vertical
|
||||
self.distribution = .gravityAreas
|
||||
self.edgeInsets = NSEdgeInsets(
|
||||
top: Constants.Settings.margin,
|
||||
left: Constants.Settings.margin,
|
||||
bottom: Constants.Settings.margin,
|
||||
right: Constants.Settings.margin
|
||||
)
|
||||
self.spacing = Constants.Settings.margin
|
||||
|
||||
self.addArrangedSubview(NSView())
|
||||
self.addArrangedSubview(self.emptyView)
|
||||
self.addArrangedSubview(self.section)
|
||||
self.section.isHidden = true
|
||||
|
||||
self.addArrangedSubview(NSView())
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@@ -43,44 +42,38 @@ internal class Settings: NSStackView, Settings_v {
|
||||
|
||||
internal func setList(_ list: [BLEDevice]) {
|
||||
if self.list.count != list.count && !self.list.isEmpty {
|
||||
self.subviews.filter({ $0 is NSStackView && ($0 as! NSStackView).identifier != NSUserInterfaceItemIdentifier(rawValue: "emptyView") }).forEach{ $0.removeFromSuperview() }
|
||||
self.section.removeFromSuperview()
|
||||
self.section = PreferencesSection()
|
||||
self.addArrangedSubview(self.section)
|
||||
self.list = [:]
|
||||
}
|
||||
|
||||
if list.isEmpty && self.emptyView.isHidden {
|
||||
self.emptyView.isHidden = false
|
||||
self.section.isHidden = true
|
||||
return
|
||||
} else if !list.isEmpty && !self.emptyView.isHidden {
|
||||
self.emptyView.isHidden = true
|
||||
self.section.isHidden = false
|
||||
}
|
||||
|
||||
list.forEach { (d: BLEDevice) in
|
||||
if self.list[d.id] == nil {
|
||||
let row: NSView = toggleSettingRow(
|
||||
title: d.name,
|
||||
let btn = switchView(
|
||||
action: #selector(self.handleSelection),
|
||||
state: d.state
|
||||
)
|
||||
row.subviews.filter{ $0 is NSControl }.forEach { (control: NSView) in
|
||||
control.identifier = NSUserInterfaceItemIdentifier(rawValue: "\(d.uuid?.uuidString ?? d.address)")
|
||||
}
|
||||
btn.identifier = NSUserInterfaceItemIdentifier(rawValue: "\(d.uuid?.uuidString ?? d.address)")
|
||||
section.add(PreferencesRow(d.name, component: btn))
|
||||
self.list[d.id] = true
|
||||
self.addArrangedSubview(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func handleSelection(_ sender: NSControl) {
|
||||
guard let id = sender.identifier else { return }
|
||||
|
||||
var state: NSControl.StateValue? = nil
|
||||
if #available(OSX 10.15, *) {
|
||||
state = sender is NSSwitch ? (sender as! NSSwitch).state: nil
|
||||
} else {
|
||||
state = sender is NSButton ? (sender as! NSButton).state: nil
|
||||
}
|
||||
|
||||
Store.shared.set(key: "ble_\(id.rawValue)", value: state! == NSControl.StateValue.on)
|
||||
let value = controlState(sender)
|
||||
Store.shared.set(key: "ble_\(id.rawValue)", value: value)
|
||||
self.callback()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,5 +81,12 @@
|
||||
<integer>5</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>Settings</key>
|
||||
<dict>
|
||||
<key>popup</key>
|
||||
<true/>
|
||||
<key>notifications</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -40,41 +40,37 @@ class Notifications: NotificationsWrapper {
|
||||
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)
|
||||
|
||||
self.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Total load"),
|
||||
action: #selector(self.changeTotalLoad),
|
||||
items: notificationLevels,
|
||||
selected: self.totalLoadLevel
|
||||
))
|
||||
|
||||
self.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("System load"),
|
||||
action: #selector(self.changeSystemLoad),
|
||||
items: notificationLevels,
|
||||
selected: self.systemLoadLevel
|
||||
))
|
||||
|
||||
self.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("User load"),
|
||||
action: #selector(self.changeUserLoad),
|
||||
items: notificationLevels,
|
||||
selected: self.userLoadLevel
|
||||
))
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Total load"), component: selectView(
|
||||
action: #selector(self.changeTotalLoad),
|
||||
items: notificationLevels,
|
||||
selected: self.totalLoadLevel
|
||||
)),
|
||||
PreferencesRow(localizedString("System load"), component: selectView(
|
||||
action: #selector(self.changeSystemLoad),
|
||||
items: notificationLevels,
|
||||
selected: self.systemLoadLevel
|
||||
)),
|
||||
PreferencesRow(localizedString("User load"), component: selectView(
|
||||
action: #selector(self.changeUserLoad),
|
||||
items: notificationLevels,
|
||||
selected: self.userLoadLevel
|
||||
))
|
||||
]))
|
||||
|
||||
#if arch(arm64)
|
||||
self.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Efficiency cores load"),
|
||||
action: #selector(self.changeECoresLoad),
|
||||
items: notificationLevels,
|
||||
selected: self.eCoresLoadLevel
|
||||
))
|
||||
|
||||
self.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Performance cores load"),
|
||||
action: #selector(self.changePCoresLoad),
|
||||
items: notificationLevels,
|
||||
selected: self.pCoresLoadLevel
|
||||
))
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Efficiency cores load"), component: selectView(
|
||||
action: #selector(self.changeECoresLoad),
|
||||
items: notificationLevels,
|
||||
selected: self.eCoresLoadLevel
|
||||
)),
|
||||
PreferencesRow(localizedString("Performance cores load"), component: selectView(
|
||||
action: #selector(self.changePCoresLoad),
|
||||
items: notificationLevels,
|
||||
selected: self.pCoresLoadLevel
|
||||
))
|
||||
]))
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ internal class Popup: PopupWrapper {
|
||||
private var eCoresColorView: NSView? = nil
|
||||
private var pCoresColorView: NSView? = nil
|
||||
|
||||
private var chartPrefSection: PreferencesSection? = nil
|
||||
private var sliderView: NSView? = nil
|
||||
|
||||
private var lineChart: LineChartView? = nil
|
||||
@@ -472,71 +473,62 @@ internal class Popup: PopupWrapper {
|
||||
public override func settings() -> NSView? {
|
||||
let view = SettingsContainerView()
|
||||
|
||||
view.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("System color"),
|
||||
action: #selector(self.toggleSystemColor),
|
||||
items: Color.allColors,
|
||||
selected: self.systemColorState.key
|
||||
))
|
||||
view.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("System color"), component: selectView(
|
||||
action: #selector(self.toggleSystemColor),
|
||||
items: Color.allColors,
|
||||
selected: self.systemColorState.key
|
||||
)),
|
||||
PreferencesRow(localizedString("User color"), component: selectView(
|
||||
action: #selector(self.toggleUserColor),
|
||||
items: Color.allColors,
|
||||
selected: self.userColorState.key
|
||||
)),
|
||||
PreferencesRow(localizedString("Idle color"), component: selectView(
|
||||
action: #selector(self.toggleIdleColor),
|
||||
items: Color.allColors,
|
||||
selected: self.idleColorState.key
|
||||
))
|
||||
]))
|
||||
|
||||
view.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("User color"),
|
||||
action: #selector(self.toggleUserColor),
|
||||
items: Color.allColors,
|
||||
selected: self.userColorState.key
|
||||
))
|
||||
view.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Efficiency cores color"), component: selectView(
|
||||
action: #selector(self.toggleECoresColor),
|
||||
items: Color.allColors,
|
||||
selected: self.eCoresColorState.key
|
||||
)),
|
||||
PreferencesRow(localizedString("Performance cores color"), component: selectView(
|
||||
action: #selector(self.togglePCoresColor),
|
||||
items: Color.allColors,
|
||||
selected: self.pCoresColorState.key
|
||||
))
|
||||
]))
|
||||
|
||||
view.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Idle color"),
|
||||
action: #selector(self.toggleIdleColor),
|
||||
items: Color.allColors,
|
||||
selected: self.idleColorState.key
|
||||
))
|
||||
|
||||
view.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Chart color"),
|
||||
action: #selector(self.toggleChartColor),
|
||||
items: Color.allColors,
|
||||
selected: self.chartColorState.key
|
||||
))
|
||||
|
||||
view.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Efficiency cores color"),
|
||||
action: #selector(self.toggleeCoresColor),
|
||||
items: Color.allColors,
|
||||
selected: self.eCoresColorState.key
|
||||
))
|
||||
|
||||
view.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Performance cores color"),
|
||||
action: #selector(self.togglepCoresColor),
|
||||
items: Color.allColors,
|
||||
selected: self.pCoresColorState.key
|
||||
))
|
||||
|
||||
view.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Chart duration"),
|
||||
action: #selector(self.toggleLineChartHistory),
|
||||
items: LineChartHistory,
|
||||
selected: "\(self.lineChartHistory)"
|
||||
))
|
||||
|
||||
view.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Main chart scaling"),
|
||||
action: #selector(self.toggleLineChartScale),
|
||||
items: Scale.allCases,
|
||||
selected: self.lineChartScale.key
|
||||
))
|
||||
|
||||
let slider = sliderSettingsRow(
|
||||
title: localizedString("Scale value"),
|
||||
self.sliderView = sliderView(
|
||||
action: #selector(self.toggleLineChartFixedScale),
|
||||
value: Int(self.lineChartFixedScale * 100),
|
||||
initialValue: "\(Int(self.lineChartFixedScale * 100)) %",
|
||||
isHidden: self.lineChartScale != .fixed
|
||||
initialValue: "\(Int(self.lineChartFixedScale * 100)) %"
|
||||
)
|
||||
self.sliderView = slider
|
||||
view.addArrangedSubview(slider)
|
||||
self.chartPrefSection = PreferencesSection([
|
||||
PreferencesRow(localizedString("Chart color"), component: selectView(
|
||||
action: #selector(self.toggleChartColor),
|
||||
items: Color.allColors,
|
||||
selected: self.chartColorState.key
|
||||
)),
|
||||
PreferencesRow(localizedString("Chart history"), component: selectView(
|
||||
action: #selector(self.toggleLineChartHistory),
|
||||
items: LineChartHistory,
|
||||
selected: "\(self.lineChartHistory)"
|
||||
)),
|
||||
PreferencesRow(localizedString("Main chart scaling"), component: selectView(
|
||||
action: #selector(self.toggleLineChartScale),
|
||||
items: Scale.allCases,
|
||||
selected: self.lineChartScale.key
|
||||
)),
|
||||
PreferencesRow(localizedString("Scale value"), component: self.sliderView!)
|
||||
])
|
||||
view.addArrangedSubview(self.chartPrefSection!)
|
||||
self.chartPrefSection?.toggleVisibility(3, newState: self.lineChartScale == .fixed)
|
||||
|
||||
return view
|
||||
}
|
||||
@@ -578,7 +570,7 @@ internal class Popup: PopupWrapper {
|
||||
self.lineChart?.color = color
|
||||
}
|
||||
}
|
||||
@objc private func toggleeCoresColor(_ sender: NSMenuItem) {
|
||||
@objc private func toggleECoresColor(_ sender: NSMenuItem) {
|
||||
guard let key = sender.representedObject as? String, let newValue = Color.allColors.first(where: { $0.key == key }) else {
|
||||
return
|
||||
}
|
||||
@@ -586,7 +578,7 @@ internal class Popup: PopupWrapper {
|
||||
Store.shared.set(key: "\(self.title)_eCoresColor", value: key)
|
||||
self.eCoresColorView?.layer?.backgroundColor = (newValue.additional as? NSColor)?.cgColor
|
||||
}
|
||||
@objc private func togglepCoresColor(_ sender: NSMenuItem) {
|
||||
@objc private func togglePCoresColor(_ sender: NSMenuItem) {
|
||||
guard let key = sender.representedObject as? String, let newValue = Color.allColors.first(where: { $0.key == key }) else {
|
||||
return
|
||||
}
|
||||
@@ -603,7 +595,7 @@ internal class Popup: PopupWrapper {
|
||||
@objc private func toggleLineChartScale(_ sender: NSMenuItem) {
|
||||
guard let key = sender.representedObject as? String,
|
||||
let value = Scale.allCases.first(where: { $0.key == key }) else { return }
|
||||
self.sliderView?.isHidden = value != .fixed
|
||||
self.chartPrefSection?.toggleVisibility(3, newState: value == .fixed)
|
||||
self.lineChartScale = value
|
||||
self.lineChart?.setScale(self.lineChartScale, fixedScale: self.lineChartFixedScale)
|
||||
Store.shared.set(key: "\(self.title)_lineChartScale", value: key)
|
||||
@@ -612,8 +604,7 @@ internal class Popup: PopupWrapper {
|
||||
@objc private func toggleLineChartFixedScale(_ sender: NSSlider) {
|
||||
let value = Int(sender.doubleValue)
|
||||
|
||||
if let container = self.sliderView?.subviews.first(where: { $0.identifier == NSUserInterfaceItemIdentifier("container") }),
|
||||
let field = container.subviews.first(where: { $0 is NSTextField }), let view = field as? NSTextField {
|
||||
if let field = self.sliderView?.subviews.first(where: { $0 is NSTextField }), let view = field as? NSTextField {
|
||||
view.stringValue = "\(value) %"
|
||||
}
|
||||
|
||||
|
||||
@@ -31,10 +31,10 @@ internal class Settings: NSStackView, Settings_v {
|
||||
public var setInterval: ((_ value: Int) -> Void) = {_ in }
|
||||
public var setTopInterval: ((_ value: Int) -> Void) = {_ in }
|
||||
|
||||
private var hyperthreadView: NSView? = nil
|
||||
private var splitValueView: NSView? = nil
|
||||
private var usagePerCoreView: NSView? = nil
|
||||
private var groupByClustersView: NSView? = nil
|
||||
private var hyperthreadView: NSSwitch? = nil
|
||||
private var splitValueView: NSSwitch? = nil
|
||||
private var usagePerCoreView: NSSwitch? = nil
|
||||
private var groupByClustersView: NSSwitch? = nil
|
||||
|
||||
public init(_ module: ModuleType) {
|
||||
self.title = module.rawValue
|
||||
@@ -56,12 +56,6 @@ internal class Settings: NSStackView, Settings_v {
|
||||
self.orientation = .vertical
|
||||
self.distribution = .gravityAreas
|
||||
self.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.edgeInsets = NSEdgeInsets(
|
||||
top: Constants.Settings.margin,
|
||||
left: Constants.Settings.margin,
|
||||
bottom: Constants.Settings.margin,
|
||||
right: Constants.Settings.margin
|
||||
)
|
||||
self.spacing = Constants.Settings.margin
|
||||
}
|
||||
|
||||
@@ -79,78 +73,79 @@ internal class Settings: NSStackView, Settings_v {
|
||||
hasIPG = CFBundleCreate(kCFAllocatorDefault, bundleURL) != nil
|
||||
#endif
|
||||
|
||||
self.addArrangedSubview(selectSettingsRowV1(
|
||||
title: localizedString("Update interval"),
|
||||
action: #selector(changeUpdateInterval),
|
||||
items: ReaderUpdateIntervals.map{ "\($0) sec" },
|
||||
selected: "\(self.updateIntervalValue) sec"
|
||||
))
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Update interval"), component: selectView(
|
||||
action: #selector(self.changeUpdateInterval),
|
||||
items: ReaderUpdateIntervals.map{ KeyValue_t(key: "\($0)", value: "\($0) sec") },
|
||||
selected: "\(self.updateIntervalValue) sec"
|
||||
)),
|
||||
PreferencesRow(localizedString("Update interval for top processes"), component: selectView(
|
||||
action: #selector(self.changeUpdateTopInterval),
|
||||
items: ReaderUpdateIntervals.map{ KeyValue_t(key: "\($0)", value: "\($0) sec") },
|
||||
selected: "\(self.updateTopIntervalValue) sec"
|
||||
))
|
||||
]))
|
||||
|
||||
self.addArrangedSubview(selectSettingsRowV1(
|
||||
title: localizedString("Update interval for top processes"),
|
||||
action: #selector(changeUpdateTopInterval),
|
||||
items: ReaderUpdateIntervals.map{ "\($0) sec" },
|
||||
selected: "\(self.updateTopIntervalValue) sec"
|
||||
))
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Number of top processes"), component: selectView(
|
||||
action: #selector(self.changeNumberOfProcesses),
|
||||
items: NumbersOfProcesses.map{ KeyValue_t(key: "\($0)", value: "\($0)") },
|
||||
selected: "\(self.numberOfProcesses)"
|
||||
))
|
||||
]))
|
||||
|
||||
if !widgets.filter({ $0 == .barChart }).isEmpty {
|
||||
self.usagePerCoreView = toggleSettingRow(
|
||||
title: localizedString("Show usage per core"),
|
||||
action: #selector(toggleUsagePerCore),
|
||||
self.splitValueView = switchView(
|
||||
action: #selector(self.toggleSplitValue),
|
||||
state: self.splitValueState
|
||||
)
|
||||
self.usagePerCoreView = switchView(
|
||||
action: #selector(self.toggleUsagePerCore),
|
||||
state: self.usagePerCoreState
|
||||
)
|
||||
self.addArrangedSubview(self.usagePerCoreView!)
|
||||
if self.usagePerCoreState || self.clustersGroupState {
|
||||
self.splitValueView?.isEnabled = false
|
||||
self.splitValueView?.state = .off
|
||||
}
|
||||
|
||||
var rows: [PreferencesRow] = [
|
||||
PreferencesRow(localizedString("Show usage per core"), component: self.usagePerCoreView!)
|
||||
]
|
||||
|
||||
#if arch(arm64)
|
||||
self.groupByClustersView = toggleSettingRow(
|
||||
title: localizedString("Cluster grouping"),
|
||||
action: #selector(toggleClustersGroup),
|
||||
self.groupByClustersView = switchView(
|
||||
action: #selector(self.toggleClustersGroup),
|
||||
state: self.clustersGroupState
|
||||
)
|
||||
self.addArrangedSubview(self.groupByClustersView!)
|
||||
rows.append(PreferencesRow(localizedString("Cluster grouping"), component: self.groupByClustersView!))
|
||||
#endif
|
||||
|
||||
if self.hasHyperthreadingCores {
|
||||
self.hyperthreadView = toggleSettingRow(
|
||||
title: localizedString("Show hyper-threading cores"),
|
||||
action: #selector(toggleMultithreading),
|
||||
self.hyperthreadView = switchView(
|
||||
action: #selector(self.toggleMultithreading),
|
||||
state: self.hyperthreadState
|
||||
)
|
||||
if !self.usagePerCoreState {
|
||||
findAndToggleEnableNSControlState(self.hyperthreadView, state: false)
|
||||
findAndToggleNSControlState(self.hyperthreadView, state: .off)
|
||||
self.hyperthreadView?.isEnabled = false
|
||||
self.hyperthreadView?.state = .off
|
||||
}
|
||||
self.addArrangedSubview(self.hyperthreadView!)
|
||||
rows.append(PreferencesRow(localizedString("Show hyper-threading cores"), component: self.hyperthreadView!))
|
||||
}
|
||||
rows.append(PreferencesRow(localizedString("Split the value (System/User)"), component: self.splitValueView!))
|
||||
|
||||
self.splitValueView = toggleSettingRow(
|
||||
title: localizedString("Split the value (System/User)"),
|
||||
action: #selector(toggleSplitValue),
|
||||
state: self.splitValueState
|
||||
)
|
||||
if self.usagePerCoreState || self.clustersGroupState {
|
||||
findAndToggleEnableNSControlState(self.splitValueView, state: false)
|
||||
findAndToggleNSControlState(self.splitValueView, state: .off)
|
||||
}
|
||||
self.addArrangedSubview(self.splitValueView!)
|
||||
self.addArrangedSubview(PreferencesSection(rows))
|
||||
}
|
||||
|
||||
#if arch(x86_64)
|
||||
if hasIPG {
|
||||
self.addArrangedSubview(toggleSettingRow(
|
||||
title: "\(localizedString("CPU frequency")) (IPG)",
|
||||
action: #selector(toggleIPG),
|
||||
state: self.IPGState
|
||||
))
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow("\(localizedString("CPU frequency")) (IPG)", component: switchView(
|
||||
action: #selector(self.toggleIPG),
|
||||
state: self.IPGState
|
||||
))
|
||||
]))
|
||||
}
|
||||
#endif
|
||||
|
||||
self.addArrangedSubview(selectSettingsRowV1(
|
||||
title: localizedString("Number of top processes"),
|
||||
action: #selector(changeNumberOfProcesses),
|
||||
items: NumbersOfProcesses.map{ "\($0)" },
|
||||
selected: "\(self.numberOfProcesses)"
|
||||
))
|
||||
}
|
||||
|
||||
@objc private func changeUpdateInterval(_ sender: NSMenuItem) {
|
||||
@@ -189,23 +184,23 @@ internal class Settings: NSStackView, Settings_v {
|
||||
Store.shared.set(key: "\(self.title)_usagePerCore", value: self.usagePerCoreState)
|
||||
self.callback()
|
||||
|
||||
findAndToggleEnableNSControlState(self.hyperthreadView, state: self.usagePerCoreState)
|
||||
findAndToggleEnableNSControlState(self.splitValueView, state: !(self.usagePerCoreState || self.clustersGroupState))
|
||||
self.hyperthreadView?.isEnabled = self.usagePerCoreState
|
||||
self.splitValueView?.isEnabled = !(self.usagePerCoreState || self.clustersGroupState)
|
||||
|
||||
if !self.usagePerCoreState {
|
||||
self.hyperthreadState = false
|
||||
Store.shared.set(key: "\(self.title)_hyperhreading", value: self.hyperthreadState)
|
||||
findAndToggleNSControlState(self.hyperthreadView, state: .off)
|
||||
self.hyperthreadView?.state = .off
|
||||
} else {
|
||||
self.splitValueState = false
|
||||
Store.shared.set(key: "\(self.title)_splitValue", value: self.splitValueState)
|
||||
findAndToggleNSControlState(self.splitValueView, state: .off)
|
||||
self.splitValueView?.state = .off
|
||||
}
|
||||
|
||||
if self.clustersGroupState && self.usagePerCoreState {
|
||||
self.clustersGroupState = false
|
||||
Store.shared.set(key: "\(self.title)_clustersGroup", value: self.clustersGroupState)
|
||||
findAndToggleNSControlState(self.groupByClustersView, state: .off)
|
||||
self.groupByClustersView?.state = .off
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,10 +254,10 @@ internal class Settings: NSStackView, Settings_v {
|
||||
self.clustersGroupState = state! == .on ? true : false
|
||||
Store.shared.set(key: "\(self.title)_clustersGroup", value: self.clustersGroupState)
|
||||
|
||||
findAndToggleEnableNSControlState(self.splitValueView, state: !(self.usagePerCoreState || self.clustersGroupState))
|
||||
self.splitValueView?.isEnabled = !(self.usagePerCoreState || self.clustersGroupState)
|
||||
|
||||
if self.clustersGroupState && self.usagePerCoreState {
|
||||
findAndToggleNSControlState(self.usagePerCoreView, state: .off)
|
||||
self.usagePerCoreView?.state = .off
|
||||
let toggle: NSSwitch = NSSwitch()
|
||||
toggle.state = .off
|
||||
self.toggleUsagePerCore(toggle)
|
||||
|
||||
@@ -32,5 +32,12 @@
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>Settings</key>
|
||||
<dict>
|
||||
<key>popup</key>
|
||||
<true/>
|
||||
<key>notifications</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -57,12 +57,6 @@ internal class Settings: NSStackView, Settings_v, NSTableViewDelegate, NSTableVi
|
||||
|
||||
self.orientation = .vertical
|
||||
self.distribution = .gravityAreas
|
||||
self.edgeInsets = NSEdgeInsets(
|
||||
top: Constants.Settings.margin,
|
||||
left: Constants.Settings.margin,
|
||||
bottom: Constants.Settings.margin,
|
||||
right: Constants.Settings.margin
|
||||
)
|
||||
self.spacing = 0
|
||||
|
||||
self.scrollView.documentView = self.tableView
|
||||
@@ -115,7 +109,7 @@ internal class Settings: NSStackView, Settings_v, NSTableViewDelegate, NSTableVi
|
||||
self.addArrangedSubview(self.footer())
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
self.scrollView.heightAnchor.constraint(equalToConstant: 278)
|
||||
self.scrollView.heightAnchor.constraint(equalToConstant: 296)
|
||||
])
|
||||
}
|
||||
|
||||
@@ -126,21 +120,19 @@ internal class Settings: NSStackView, Settings_v, NSTableViewDelegate, NSTableVi
|
||||
private func footer() -> NSView {
|
||||
let view = NSStackView()
|
||||
view.heightAnchor.constraint(equalToConstant: 27).isActive = true
|
||||
view.spacing = 0
|
||||
view.spacing = 4
|
||||
view.orientation = .horizontal
|
||||
|
||||
var addButton: NSButton {
|
||||
let btn = NSButton()
|
||||
btn.widthAnchor.constraint(equalToConstant: 27).isActive = true
|
||||
btn.heightAnchor.constraint(equalToConstant: 27).isActive = true
|
||||
btn.bezelStyle = .regularSquare
|
||||
btn.translatesAutoresizingMaskIntoConstraints = false
|
||||
btn.bezelStyle = .rounded
|
||||
if #available(macOS 11.0, *) {
|
||||
btn.image = iconFromSymbol(name: "plus", scale: .large)
|
||||
btn.image = iconFromSymbol(name: "plus", scale: .medium)
|
||||
} else {
|
||||
btn.title = "Add"
|
||||
btn.title = localizedString("Add")
|
||||
}
|
||||
btn.isBordered = false
|
||||
btn.action = #selector(self.addNewClock)
|
||||
btn.target = self
|
||||
btn.toolTip = localizedString("Add new clock")
|
||||
@@ -151,14 +143,12 @@ internal class Settings: NSStackView, Settings_v, NSTableViewDelegate, NSTableVi
|
||||
let btn = NSButton()
|
||||
btn.widthAnchor.constraint(equalToConstant: 27).isActive = true
|
||||
btn.heightAnchor.constraint(equalToConstant: 27).isActive = true
|
||||
btn.bezelStyle = .regularSquare
|
||||
btn.translatesAutoresizingMaskIntoConstraints = false
|
||||
btn.bezelStyle = .rounded
|
||||
if #available(macOS 11.0, *) {
|
||||
btn.image = iconFromSymbol(name: "minus", scale: .large)
|
||||
btn.image = iconFromSymbol(name: "minus", scale: .medium)
|
||||
} else {
|
||||
btn.title = "Add"
|
||||
btn.title = localizedString("Delete")
|
||||
}
|
||||
btn.isBordered = false
|
||||
btn.action = #selector(self.deleteClock)
|
||||
btn.target = self
|
||||
btn.toolTip = localizedString("Delete selected clock")
|
||||
@@ -170,6 +160,16 @@ internal class Settings: NSStackView, Settings_v, NSTableViewDelegate, NSTableVi
|
||||
view.addArrangedSubview(addButton)
|
||||
view.addArrangedSubview(NSView())
|
||||
|
||||
let helpBtn = NSButton()
|
||||
helpBtn.widthAnchor.constraint(equalToConstant: 27).isActive = true
|
||||
helpBtn.heightAnchor.constraint(equalToConstant: 27).isActive = true
|
||||
helpBtn.bezelStyle = .helpButton
|
||||
helpBtn.title = ""
|
||||
helpBtn.action = #selector(self.openFormatHelp)
|
||||
helpBtn.target = self
|
||||
helpBtn.toolTip = localizedString("Help with datetime format")
|
||||
view.addArrangedSubview(helpBtn)
|
||||
|
||||
self.footerView = view
|
||||
return view
|
||||
}
|
||||
@@ -197,7 +197,6 @@ internal class Settings: NSStackView, Settings_v, NSTableViewDelegate, NSTableVi
|
||||
text.delegate = self
|
||||
text.stringValue = id == nameColumnID ? item.name : item.format
|
||||
text.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
cell.addSubview(text)
|
||||
text.widthAnchor.constraint(equalTo: cell.widthAnchor).isActive = true
|
||||
text.centerYAnchor.constraint(equalTo: cell.centerYAnchor).isActive = true
|
||||
@@ -251,12 +250,10 @@ internal class Settings: NSStackView, Settings_v, NSTableViewDelegate, NSTableVi
|
||||
guard let key = sender.representedObject as? String, let id = sender.identifier, let i = Int(id.rawValue) else { return }
|
||||
self.list[i].tz = key
|
||||
}
|
||||
|
||||
@objc private func toggleClock(_ sender: NSButton) {
|
||||
guard let id = sender.identifier, let i = Int(id.rawValue) else { return }
|
||||
self.list[i].enabled = sender.state == NSControl.StateValue.on
|
||||
}
|
||||
|
||||
@objc private func addNewClock(_ sender: Any) {
|
||||
self.list.append(Clock_t(name: "Clock \(self.list.count)", format: Clock.local.format, tz: Clock.local.tz))
|
||||
self.tableView.reloadData()
|
||||
@@ -267,4 +264,7 @@ internal class Settings: NSStackView, Settings_v, NSTableViewDelegate, NSTableVi
|
||||
self.tableView.reloadData()
|
||||
self.deleteButton?.removeFromSuperview()
|
||||
}
|
||||
@objc private func openFormatHelp(_ sender: NSButton) {
|
||||
NSWorkspace.shared.open(URL(string: "https://www.nsdateformatter.com")!)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,5 +117,12 @@
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>Settings</key>
|
||||
<dict>
|
||||
<key>popup</key>
|
||||
<true/>
|
||||
<key>notifications</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -27,12 +27,13 @@ class Notifications: NotificationsWrapper {
|
||||
|
||||
self.utilizationLevel = Store.shared.string(key: "\(self.module)_notifications_utilization", defaultValue: self.utilizationLevel)
|
||||
|
||||
self.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Usage"),
|
||||
action: #selector(self.changeUsage),
|
||||
items: notificationLevels,
|
||||
selected: self.utilizationLevel
|
||||
))
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Usage"), component: selectView(
|
||||
action: #selector(self.changeUsage),
|
||||
items: notificationLevels,
|
||||
selected: self.utilizationLevel
|
||||
))
|
||||
]))
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
|
||||
@@ -19,6 +19,7 @@ internal class Popup: PopupWrapper {
|
||||
private var readColor: NSColor { self.readColorState.additional as? NSColor ?? NSColor.systemRed }
|
||||
private var writeColorState: Color = .secondRed
|
||||
private var writeColor: NSColor { self.writeColorState.additional as? NSColor ?? NSColor.systemBlue }
|
||||
private var reverseOrderState: Bool = false
|
||||
|
||||
private var disks: NSStackView = {
|
||||
let view = NSStackView()
|
||||
@@ -45,6 +46,7 @@ internal class Popup: PopupWrapper {
|
||||
|
||||
self.readColorState = Color.fromString(Store.shared.string(key: "\(self.title)_readColor", defaultValue: self.readColorState.key))
|
||||
self.writeColorState = Color.fromString(Store.shared.string(key: "\(self.title)_writeColor", defaultValue: self.writeColorState.key))
|
||||
self.reverseOrderState = Store.shared.bool(key: "\(self.title)_reverseOrder", defaultValue: self.reverseOrderState)
|
||||
|
||||
self.orientation = .vertical
|
||||
self.distribution = .fill
|
||||
@@ -170,19 +172,25 @@ internal class Popup: PopupWrapper {
|
||||
public override func settings() -> NSView? {
|
||||
let view = SettingsContainerView()
|
||||
|
||||
view.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Write color"),
|
||||
action: #selector(toggleWriteColor),
|
||||
items: Color.allColors,
|
||||
selected: self.writeColorState.key
|
||||
))
|
||||
view.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Write color"), component: selectView(
|
||||
action: #selector(self.toggleWriteColor),
|
||||
items: Color.allColors,
|
||||
selected: self.writeColorState.key
|
||||
)),
|
||||
PreferencesRow(localizedString("Read color"), component: selectView(
|
||||
action: #selector(self.toggleReadColor),
|
||||
items: Color.allColors,
|
||||
selected: self.readColorState.key
|
||||
))
|
||||
]))
|
||||
|
||||
view.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Read color"),
|
||||
action: #selector(toggleReadColor),
|
||||
items: Color.allColors,
|
||||
selected: self.readColorState.key
|
||||
))
|
||||
view.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Reverse order"), component: switchView(
|
||||
action: #selector(self.toggleReverseOrder),
|
||||
state: self.reverseOrderState
|
||||
))
|
||||
]))
|
||||
|
||||
return view
|
||||
}
|
||||
@@ -215,6 +223,14 @@ internal class Popup: PopupWrapper {
|
||||
}
|
||||
}
|
||||
}
|
||||
@objc private func toggleReverseOrder(_ sender: NSControl) {
|
||||
self.reverseOrderState = controlState(sender)
|
||||
for view in self.disks.subviews.filter({ $0 is DiskView }).map({ $0 as! DiskView }) {
|
||||
view.setChartReverseOrder(self.reverseOrderState)
|
||||
}
|
||||
Store.shared.set(key: "\(self.title)_reverseOrder", value: self.reverseOrderState)
|
||||
self.display()
|
||||
}
|
||||
}
|
||||
|
||||
internal class DiskView: NSStackView {
|
||||
@@ -303,6 +319,9 @@ internal class DiskView: NSStackView {
|
||||
public func setChartColor(read: NSColor? = nil, write: NSColor? = nil) {
|
||||
self.chartView.setColors(read: read, write: write)
|
||||
}
|
||||
public func setChartReverseOrder(_ newValue: Bool) {
|
||||
self.chartView.setReverseOrder(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
internal class NameView: NSStackView {
|
||||
@@ -429,6 +448,9 @@ internal class ChartView: NSStackView {
|
||||
private var writeColor: NSColor {
|
||||
Color.fromString(Store.shared.string(key: "\(ModuleType.disk.rawValue)_writeColor", defaultValue: Color.secondRed.key)).additional as! NSColor
|
||||
}
|
||||
private var reverseOrder: Bool {
|
||||
Store.shared.bool(key: "\(ModuleType.disk.rawValue)_reverseOrder", defaultValue: false)
|
||||
}
|
||||
|
||||
public init(width: CGFloat) {
|
||||
super.init(frame: NSRect(x: 0, y: 0, width: width, height: 36))
|
||||
@@ -441,7 +463,7 @@ internal class ChartView: NSStackView {
|
||||
y: 1,
|
||||
width: self.frame.width,
|
||||
height: self.frame.height - 2
|
||||
), num: 120, outColor: self.writeColor, inColor: self.readColor)
|
||||
), num: 120, reversedOrder: self.reverseOrder, outColor: self.writeColor, inColor: self.readColor)
|
||||
self.chart = chart
|
||||
|
||||
self.addArrangedSubview(chart)
|
||||
@@ -465,6 +487,10 @@ internal class ChartView: NSStackView {
|
||||
public func setColors(read: NSColor? = nil, write: NSColor? = nil) {
|
||||
self.chart?.setColors(in: read, out: write)
|
||||
}
|
||||
|
||||
public func setReverseOrder(_ newValue: Bool) {
|
||||
self.chart?.setReverseOrder(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
internal class BarView: NSView {
|
||||
|
||||
@@ -17,7 +17,6 @@ internal class Settings: NSStackView, Settings_v {
|
||||
|
||||
private var removableState: Bool = false
|
||||
private var updateIntervalValue: Int = 10
|
||||
private var notificationLevel: String = "Disabled"
|
||||
private var numberOfProcesses: Int = 5
|
||||
private var baseValue: String = "byte"
|
||||
|
||||
@@ -28,7 +27,6 @@ internal class Settings: NSStackView, Settings_v {
|
||||
|
||||
private var selectedDisk: String
|
||||
private var button: NSPopUpButton?
|
||||
private var intervalSelectView: NSView? = nil
|
||||
|
||||
private var list: [String] = []
|
||||
|
||||
@@ -38,21 +36,12 @@ internal class Settings: NSStackView, Settings_v {
|
||||
self.selectedDisk = Store.shared.string(key: "\(self.title)_disk", defaultValue: "")
|
||||
self.removableState = Store.shared.bool(key: "\(self.title)_removable", defaultValue: self.removableState)
|
||||
self.updateIntervalValue = Store.shared.int(key: "\(self.title)_updateInterval", defaultValue: self.updateIntervalValue)
|
||||
self.notificationLevel = Store.shared.string(key: "\(self.title)_notificationLevel", defaultValue: self.notificationLevel)
|
||||
self.numberOfProcesses = Store.shared.int(key: "\(self.title)_processes", defaultValue: self.numberOfProcesses)
|
||||
self.baseValue = Store.shared.string(key: "\(self.title)_base", defaultValue: self.baseValue)
|
||||
|
||||
super.init(frame: NSRect(x: 0, y: 0, width: 0, height: 0))
|
||||
super.init(frame: NSRect.zero)
|
||||
|
||||
self.wantsLayer = true
|
||||
self.orientation = .vertical
|
||||
self.distribution = .gravityAreas
|
||||
self.edgeInsets = NSEdgeInsets(
|
||||
top: Constants.Settings.margin,
|
||||
left: Constants.Settings.margin,
|
||||
bottom: Constants.Settings.margin,
|
||||
right: Constants.Settings.margin
|
||||
)
|
||||
self.spacing = Constants.Settings.margin
|
||||
}
|
||||
|
||||
@@ -63,69 +52,44 @@ internal class Settings: NSStackView, Settings_v {
|
||||
public func load(widgets: [widget_t]) {
|
||||
self.subviews.forEach{ $0.removeFromSuperview() }
|
||||
|
||||
self.addArrangedSubview(selectSettingsRowV1(
|
||||
title: localizedString("Number of top processes"),
|
||||
action: #selector(changeNumberOfProcesses),
|
||||
items: NumbersOfProcesses.map{ "\($0)" },
|
||||
selected: "\(self.numberOfProcesses)"
|
||||
))
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Update interval"), component: selectView(
|
||||
action: #selector(self.changeUpdateInterval),
|
||||
items: ReaderUpdateIntervals.map{ KeyValue_t(key: "\($0)", value: "\($0) sec") },
|
||||
selected: "\(self.updateIntervalValue) sec"
|
||||
))
|
||||
]))
|
||||
|
||||
self.intervalSelectView = selectSettingsRowV1(
|
||||
title: localizedString("Update interval"),
|
||||
action: #selector(changeUpdateInterval),
|
||||
items: (ReaderUpdateIntervals + [90, 120, 180, 240, 300]).map{ "\($0) sec" },
|
||||
selected: "\(self.updateIntervalValue) sec"
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Number of top processes"), component: selectView(
|
||||
action: #selector(self.changeNumberOfProcesses),
|
||||
items: NumbersOfProcesses.map{ KeyValue_t(key: "\($0)", value: "\($0)") },
|
||||
selected: "\(self.numberOfProcesses)"
|
||||
))
|
||||
]))
|
||||
|
||||
self.button = selectView(
|
||||
action: #selector(self.handleSelection),
|
||||
items: [],
|
||||
selected: ""
|
||||
)
|
||||
self.addArrangedSubview(self.intervalSelectView!)
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Disk to show"), component: self.button!),
|
||||
PreferencesRow(localizedString("Show removable disks"), component: switchView(
|
||||
action: #selector(self.toggleRemovable),
|
||||
state: self.removableState
|
||||
))
|
||||
]))
|
||||
|
||||
if widgets.contains(where: { $0 == .speed }) {
|
||||
self.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Base"),
|
||||
action: #selector(toggleBase),
|
||||
items: SpeedBase,
|
||||
selected: self.baseValue
|
||||
))
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Base"), component: selectView(
|
||||
action: #selector(self.toggleBase),
|
||||
items: SpeedBase,
|
||||
selected: self.baseValue
|
||||
))
|
||||
]))
|
||||
}
|
||||
|
||||
self.addDiskSelector()
|
||||
|
||||
self.addArrangedSubview(toggleSettingRow(
|
||||
title: localizedString("Show removable disks"),
|
||||
action: #selector(toggleRemovable),
|
||||
state: self.removableState
|
||||
))
|
||||
|
||||
self.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Notification level"),
|
||||
action: #selector(changeNotificationLevel),
|
||||
items: notificationLevels,
|
||||
selected: self.notificationLevel == "Disabled" ? self.notificationLevel : "\(Int((Double(self.notificationLevel) ?? 0)*100))%"
|
||||
))
|
||||
}
|
||||
|
||||
private func addDiskSelector() {
|
||||
let view: NSStackView = NSStackView()
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.heightAnchor.constraint(equalToConstant: Constants.Settings.row).isActive = true
|
||||
view.orientation = .horizontal
|
||||
view.alignment = .centerY
|
||||
view.distribution = .fill
|
||||
view.spacing = 0
|
||||
|
||||
let rowTitle: NSTextField = LabelField(frame: NSRect(x: 0, y: 0, width: 0, height: 17), localizedString("Disk to show"))
|
||||
rowTitle.font = NSFont.systemFont(ofSize: 13, weight: .light)
|
||||
rowTitle.textColor = .textColor
|
||||
|
||||
self.button = NSPopUpButton(frame: NSRect(x: 0, y: 0, width: 0, height: 30))
|
||||
self.button!.target = self
|
||||
self.button?.action = #selector(self.handleSelection)
|
||||
self.button?.addItems(withTitles: list)
|
||||
|
||||
view.addArrangedSubview(rowTitle)
|
||||
view.addArrangedSubview(NSView())
|
||||
view.addArrangedSubview(self.button!)
|
||||
|
||||
self.addArrangedSubview(view)
|
||||
}
|
||||
|
||||
internal func setList(_ list: Disks) {
|
||||
@@ -152,36 +116,22 @@ internal class Settings: NSStackView, Settings_v {
|
||||
self.callbackWhenUpdateNumberOfProcesses()
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func handleSelection(_ sender: NSPopUpButton) {
|
||||
guard let item = sender.selectedItem else { return }
|
||||
self.selectedDisk = item.title
|
||||
Store.shared.set(key: "\(self.title)_disk", value: item.title)
|
||||
self.selectedDiskHandler(item.title)
|
||||
}
|
||||
|
||||
@objc private func toggleRemovable(_ sender: NSControl) {
|
||||
self.removableState = controlState(sender)
|
||||
Store.shared.set(key: "\(self.title)_removable", value: self.removableState)
|
||||
self.callback()
|
||||
}
|
||||
|
||||
@objc private func changeUpdateInterval(_ sender: NSMenuItem) {
|
||||
if let value = Int(sender.title.replacingOccurrences(of: " sec", with: "")) {
|
||||
self.setUpdateInterval(value: value)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func changeNotificationLevel(_ sender: NSMenuItem) {
|
||||
guard let key = sender.representedObject as? String else { return }
|
||||
|
||||
if key == "Disabled" {
|
||||
Store.shared.set(key: "\(self.title)_notificationLevel", value: key)
|
||||
} else if let value = Double(key.replacingOccurrences(of: "%", with: "")) {
|
||||
Store.shared.set(key: "\(self.title)_notificationLevel", value: "\(value/100)")
|
||||
}
|
||||
}
|
||||
|
||||
public func setUpdateInterval(value: Int) {
|
||||
self.updateIntervalValue = value
|
||||
Store.shared.set(key: "\(self.title)_updateInterval", value: value)
|
||||
|
||||
@@ -73,5 +73,12 @@
|
||||
<integer>4</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>Settings</key>
|
||||
<dict>
|
||||
<key>popup</key>
|
||||
<false/>
|
||||
<key>notifications</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -27,12 +27,13 @@ class Notifications: NotificationsWrapper {
|
||||
|
||||
self.usageLevel = Store.shared.string(key: "\(self.module)_notifications_usage", defaultValue: self.usageLevel)
|
||||
|
||||
self.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Usage"),
|
||||
action: #selector(self.changeUsage),
|
||||
items: notificationLevels,
|
||||
selected: self.usageLevel
|
||||
))
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Usage"), component: selectView(
|
||||
action: #selector(self.changeUsage),
|
||||
items: notificationLevels,
|
||||
selected: self.usageLevel
|
||||
))
|
||||
]))
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
|
||||
@@ -37,12 +37,6 @@ internal class Settings: NSStackView, Settings_v {
|
||||
self.wantsLayer = true
|
||||
self.orientation = .vertical
|
||||
self.distribution = .gravityAreas
|
||||
self.edgeInsets = NSEdgeInsets(
|
||||
top: Constants.Settings.margin,
|
||||
left: Constants.Settings.margin,
|
||||
bottom: Constants.Settings.margin,
|
||||
right: Constants.Settings.margin
|
||||
)
|
||||
self.spacing = Constants.Settings.margin
|
||||
}
|
||||
|
||||
@@ -53,51 +47,33 @@ internal class Settings: NSStackView, Settings_v {
|
||||
public func load(widgets: [widget_t]) {
|
||||
self.subviews.forEach{ $0.removeFromSuperview() }
|
||||
|
||||
self.addArrangedSubview(selectSettingsRowV1(
|
||||
title: localizedString("Update interval"),
|
||||
action: #selector(changeUpdateInterval),
|
||||
items: ReaderUpdateIntervals.map{ "\($0) sec" },
|
||||
selected: "\(self.updateIntervalValue) sec"
|
||||
))
|
||||
|
||||
if !widgets.filter({ $0 == .mini }).isEmpty {
|
||||
self.addArrangedSubview(toggleSettingRow(
|
||||
title: localizedString("Show GPU type"),
|
||||
action: #selector(toggleShowType),
|
||||
state: self.showTypeValue
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Update interval"), component: selectView(
|
||||
action: #selector(self.changeUpdateInterval),
|
||||
items: ReaderUpdateIntervals.map{ KeyValue_t(key: "\($0)", value: "\($0) sec") },
|
||||
selected: "\(self.updateIntervalValue) sec"
|
||||
))
|
||||
]))
|
||||
|
||||
#if arch(x86_64)
|
||||
if !widgets.filter({ $0 == .mini }).isEmpty {
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Show GPU type"), component: switchView(
|
||||
action: #selector(self.toggleShowType),
|
||||
state: self.showTypeValue
|
||||
))
|
||||
]))
|
||||
}
|
||||
#endif
|
||||
|
||||
self.addGPUSelector()
|
||||
}
|
||||
|
||||
private func addGPUSelector() {
|
||||
let view: NSStackView = NSStackView()
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.heightAnchor.constraint(equalToConstant: Constants.Settings.row).isActive = true
|
||||
view.orientation = .horizontal
|
||||
view.alignment = .centerY
|
||||
view.distribution = .fill
|
||||
view.spacing = 0
|
||||
|
||||
let title: NSTextField = LabelField(frame: NSRect(x: 0, y: 0, width: 0, height: 17), localizedString("GPU to show"))
|
||||
title.font = NSFont.systemFont(ofSize: 13, weight: .light)
|
||||
title.textColor = .textColor
|
||||
|
||||
let container: NSGridView = NSGridView(frame: NSRect(x: 0, y: 0, width: view.frame.width - 100, height: 26))
|
||||
container.yPlacement = .center
|
||||
container.xPlacement = .trailing
|
||||
let button = NSPopUpButton(frame: NSRect(x: 0, y: 0, width: 200, height: 30))
|
||||
button.target = self
|
||||
button.action = #selector(self.handleSelection)
|
||||
self.button = button
|
||||
container.addRow(with: [button])
|
||||
|
||||
view.addArrangedSubview(title)
|
||||
view.addArrangedSubview(NSView())
|
||||
view.addArrangedSubview(container)
|
||||
|
||||
self.addArrangedSubview(view)
|
||||
self.button = selectView(
|
||||
action: #selector(self.handleSelection),
|
||||
items: [],
|
||||
selected: ""
|
||||
)
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("GPU to show"), component: self.button!)
|
||||
]))
|
||||
}
|
||||
|
||||
internal func setList(_ gpus: GPUs) {
|
||||
@@ -108,10 +84,7 @@ internal class Settings: NSStackView, Settings_v {
|
||||
gpus.active().forEach{ list.append(KeyValue_t(key: $0.model, value: $0.model)) }
|
||||
|
||||
DispatchQueue.main.async(execute: {
|
||||
guard let button = self.button else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let button = self.button else { return }
|
||||
if button.menu?.items.count != list.count {
|
||||
let menu = NSMenu()
|
||||
|
||||
@@ -141,26 +114,14 @@ internal class Settings: NSStackView, Settings_v {
|
||||
self.setInterval(value)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func handleSelection(_ sender: NSMenuItem) {
|
||||
guard let key = sender.representedObject as? String else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let key = sender.representedObject as? String else { return }
|
||||
self.selectedGPU = key
|
||||
Store.shared.set(key: "\(self.title)_gpu", value: key)
|
||||
self.selectedGPUHandler(key)
|
||||
}
|
||||
|
||||
@objc func toggleShowType(_ sender: NSControl) {
|
||||
var state: NSControl.StateValue? = nil
|
||||
if #available(OSX 10.15, *) {
|
||||
state = sender is NSSwitch ? (sender as! NSSwitch).state: nil
|
||||
} else {
|
||||
state = sender is NSButton ? (sender as! NSButton).state: nil
|
||||
}
|
||||
|
||||
self.showTypeValue = state! == .on ? true : false
|
||||
@objc private func toggleShowType(_ sender: NSControl) {
|
||||
self.showTypeValue = controlState(sender)
|
||||
Store.shared.set(key: "\(self.title)_showType", value: self.showTypeValue)
|
||||
self.callback()
|
||||
}
|
||||
|
||||
@@ -55,5 +55,12 @@
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>Settings</key>
|
||||
<dict>
|
||||
<key>popup</key>
|
||||
<true/>
|
||||
<key>notifications</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -503,38 +503,40 @@ internal class Popup: PopupWrapper {
|
||||
public override func settings() -> NSView? {
|
||||
let view = SettingsContainerView()
|
||||
|
||||
view.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Color of upload"),
|
||||
action: #selector(toggleUploadColor),
|
||||
items: Color.allColors,
|
||||
selected: self.uploadColorState.key
|
||||
))
|
||||
view.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Color of upload"), component: selectView(
|
||||
action: #selector(self.toggleUploadColor),
|
||||
items: Color.allColors,
|
||||
selected: self.uploadColorState.key
|
||||
)),
|
||||
PreferencesRow(localizedString("Color of download"), component: selectView(
|
||||
action: #selector(self.toggleDownloadColor),
|
||||
items: Color.allColors,
|
||||
selected: self.downloadColorState.key
|
||||
))
|
||||
]))
|
||||
|
||||
view.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Color of download"),
|
||||
action: #selector(toggleDownloadColor),
|
||||
items: Color.allColors,
|
||||
selected: self.downloadColorState.key
|
||||
))
|
||||
view.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Reverse order"), component: switchView(
|
||||
action: #selector(self.toggleReverseOrder),
|
||||
state: self.reverseOrderState
|
||||
))
|
||||
]))
|
||||
|
||||
view.addArrangedSubview(toggleSettingRow(
|
||||
title: localizedString("Reverse order"),
|
||||
action: #selector(toggleReverseOrder),
|
||||
state: self.reverseOrderState
|
||||
))
|
||||
view.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Main chart scaling"), component: selectView(
|
||||
action: #selector(self.toggleChartScale),
|
||||
items: Scale.allCases.filter({ $0 != .fixed }),
|
||||
selected: self.chartScale.key
|
||||
))
|
||||
]))
|
||||
|
||||
view.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Main chart scaling"),
|
||||
action: #selector(self.toggleChartScale),
|
||||
items: Scale.allCases.filter({ $0 != .fixed }),
|
||||
selected: self.chartScale.key
|
||||
))
|
||||
|
||||
view.addArrangedSubview(toggleSettingRow(
|
||||
title: localizedString("Public IP"),
|
||||
action: #selector(self.togglePublicIP),
|
||||
state: self.publicIPState
|
||||
))
|
||||
view.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Public IP"), component: switchView(
|
||||
action: #selector(self.togglePublicIP),
|
||||
state: self.publicIPState
|
||||
))
|
||||
]))
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
@@ -30,9 +30,8 @@ internal class Settings: NSStackView, Settings_v, NSTextFieldDelegate {
|
||||
public var publicIPRefreshIntervalCallback: (() -> Void) = {}
|
||||
|
||||
private let title: String
|
||||
private var button: NSPopUpButton?
|
||||
private var valueField: NSTextField?
|
||||
private var sliderView: NSView? = nil
|
||||
private var section: PreferencesSection? = nil
|
||||
|
||||
private var list: [Network_interface] = []
|
||||
|
||||
@@ -54,7 +53,9 @@ internal class Settings: NSStackView, Settings_v, NSTextFieldDelegate {
|
||||
self.publicIPRefreshInterval = Store.shared.string(key: "\(self.title)_publicIPRefreshInterval", defaultValue: self.publicIPRefreshInterval)
|
||||
self.baseValue = Store.shared.string(key: "\(self.title)_base", defaultValue: self.baseValue)
|
||||
|
||||
super.init(frame: NSRect(x: 0, y: 0, width: 0, height: 0))
|
||||
super.init(frame: NSRect.zero)
|
||||
self.orientation = .vertical
|
||||
self.spacing = Constants.Settings.margin
|
||||
|
||||
for interface in SCNetworkInterfaceCopyAll() as NSArray {
|
||||
if let bsdName = SCNetworkInterfaceGetBSDName(interface as! SCNetworkInterface),
|
||||
@@ -62,16 +63,6 @@ internal class Settings: NSStackView, Settings_v, NSTextFieldDelegate {
|
||||
self.list.append(Network_interface(displayName: displayName as String, BSDName: bsdName as String))
|
||||
}
|
||||
}
|
||||
|
||||
self.orientation = .vertical
|
||||
self.distribution = .gravityAreas
|
||||
self.edgeInsets = NSEdgeInsets(
|
||||
top: Constants.Settings.margin,
|
||||
left: Constants.Settings.margin,
|
||||
bottom: Constants.Settings.margin,
|
||||
right: Constants.Settings.margin
|
||||
)
|
||||
self.spacing = Constants.Settings.margin
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@@ -81,88 +72,19 @@ internal class Settings: NSStackView, Settings_v, NSTextFieldDelegate {
|
||||
public func load(widgets: [widget_t]) {
|
||||
self.subviews.forEach{ $0.removeFromSuperview() }
|
||||
|
||||
let slider = sliderSettingsRow(
|
||||
title: localizedString("Widget activation threshold"),
|
||||
action: #selector(self.sliderCallback),
|
||||
value: self.widgetActivationThreshold,
|
||||
initialValue: self.widgetActivationThreshold != 0 ? "\(self.widgetActivationThreshold) KB" : localizedString("Disabled"),
|
||||
min: 0,
|
||||
max: 1024
|
||||
)
|
||||
self.sliderView = slider
|
||||
self.addArrangedSubview(slider)
|
||||
|
||||
self.addArrangedSubview(selectSettingsRowV1(
|
||||
title: localizedString("Number of top processes"),
|
||||
action: #selector(changeNumberOfProcesses),
|
||||
items: NumbersOfProcesses.map{ "\($0)" },
|
||||
selected: "\(self.numberOfProcesses)"
|
||||
))
|
||||
|
||||
self.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Reset data usage"),
|
||||
action: #selector(toggleUsageReset),
|
||||
items: AppUpdateIntervals.dropLast(2).filter({ $0.key != "Silent" }),
|
||||
selected: self.usageReset
|
||||
))
|
||||
|
||||
self.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Reader type"),
|
||||
action: #selector(changeReaderType),
|
||||
items: NetworkReaders,
|
||||
selected: self.readerType
|
||||
))
|
||||
|
||||
self.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Base"),
|
||||
action: #selector(toggleBase),
|
||||
items: SpeedBase,
|
||||
selected: self.baseValue
|
||||
))
|
||||
|
||||
self.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Auto-refresh public IP address"),
|
||||
action: #selector(toggleRefreshIPInterval),
|
||||
items: PublicIPAddressRefreshIntervals,
|
||||
selected: self.publicIPRefreshInterval
|
||||
))
|
||||
|
||||
self.addArrangedSubview(self.interfaceSelector())
|
||||
|
||||
if self.vpnConnection {
|
||||
self.addArrangedSubview(toggleSettingRow(
|
||||
title: localizedString("VPN mode"),
|
||||
action: #selector(toggleVPNMode),
|
||||
state: self.VPNModeState
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Number of top processes"), component: selectView(
|
||||
action: #selector(self.changeNumberOfProcesses),
|
||||
items: NumbersOfProcesses.map{ KeyValue_t(key: "\($0)", value: "\($0)") },
|
||||
selected: "\(self.numberOfProcesses)"
|
||||
))
|
||||
}
|
||||
|
||||
self.addArrangedSubview(fieldSettingRow(self,
|
||||
title: localizedString("Connectivity host (ICMP)"),
|
||||
value: self.ICMPHost,
|
||||
placeholder: localizedString("Leave empty to disable the check")
|
||||
))
|
||||
}
|
||||
|
||||
private func interfaceSelector() -> NSView {
|
||||
let view = NSStackView()
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.heightAnchor.constraint(equalToConstant: Constants.Settings.row).isActive = true
|
||||
view.orientation = .horizontal
|
||||
view.alignment = .centerY
|
||||
view.distribution = .fill
|
||||
view.spacing = 0
|
||||
|
||||
let titleField: NSTextField = LabelField(frame: NSRect(x: 0, y: 0, width: 0, height: 0), localizedString("Network interface"))
|
||||
titleField.font = NSFont.systemFont(ofSize: 12, weight: .regular)
|
||||
titleField.textColor = .textColor
|
||||
|
||||
let select: NSPopUpButton = NSPopUpButton()
|
||||
select.target = self
|
||||
select.action = #selector(self.handleSelection)
|
||||
select.isEnabled = self.readerType == "interface"
|
||||
self.button = select
|
||||
]))
|
||||
|
||||
let interfaces = selectView(
|
||||
action: #selector(self.handleSelection),
|
||||
items: [],
|
||||
selected: ""
|
||||
)
|
||||
let selectedInterface = Store.shared.string(key: "\(self.title)_interface", defaultValue: "")
|
||||
let menu = NSMenu()
|
||||
let autodetection = NSMenuItem(title: localizedString("Autodetection"), action: nil, keyEquivalent: "")
|
||||
@@ -170,7 +92,6 @@ internal class Settings: NSStackView, Settings_v, NSTextFieldDelegate {
|
||||
autodetection.tag = 128
|
||||
menu.addItem(autodetection)
|
||||
menu.addItem(NSMenuItem.separator())
|
||||
|
||||
self.list.forEach { (interface: Network_interface) in
|
||||
let interfaceMenu = NSMenuItem(title: "\(interface.displayName) (\(interface.BSDName))", action: nil, keyEquivalent: "")
|
||||
interfaceMenu.identifier = NSUserInterfaceItemIdentifier(rawValue: interface.BSDName)
|
||||
@@ -179,22 +100,79 @@ internal class Settings: NSStackView, Settings_v, NSTextFieldDelegate {
|
||||
interfaceMenu.state = .on
|
||||
}
|
||||
}
|
||||
|
||||
select.menu = menu
|
||||
select.sizeToFit()
|
||||
|
||||
interfaces.menu = menu
|
||||
interfaces.sizeToFit()
|
||||
if selectedInterface == "" {
|
||||
select.selectItem(withTag: 128)
|
||||
interfaces.selectItem(withTag: 128)
|
||||
}
|
||||
|
||||
view.addArrangedSubview(titleField)
|
||||
view.addArrangedSubview(NSView())
|
||||
view.addArrangedSubview(select)
|
||||
var prefs: [PreferencesRow] = [
|
||||
PreferencesRow(localizedString("Reader type"), component: selectView(
|
||||
action: #selector(self.changeReaderType),
|
||||
items: NetworkReaders,
|
||||
selected: self.readerType
|
||||
)),
|
||||
PreferencesRow(localizedString("Network interface"), component: interfaces),
|
||||
PreferencesRow(localizedString("Base"), component: selectView(
|
||||
action: #selector(self.toggleBase),
|
||||
items: SpeedBase,
|
||||
selected: self.baseValue
|
||||
)),
|
||||
PreferencesRow(localizedString("Reset data usage"), component: selectView(
|
||||
action: #selector(self.toggleUsageReset),
|
||||
items: AppUpdateIntervals.dropLast(2).filter({ $0.key != "Silent" }),
|
||||
selected: self.usageReset
|
||||
)),
|
||||
PreferencesRow(localizedString("Auto-refresh public IP address"), component: selectView(
|
||||
action: #selector(self.toggleRefreshIPInterval),
|
||||
items: PublicIPAddressRefreshIntervals,
|
||||
selected: self.publicIPRefreshInterval
|
||||
))
|
||||
]
|
||||
if self.vpnConnection {
|
||||
prefs.append(PreferencesRow(localizedString("VPN mode"), component: switchView(
|
||||
action: #selector(self.toggleVPNMode),
|
||||
state: self.VPNModeState
|
||||
)))
|
||||
}
|
||||
let section = PreferencesSection(prefs)
|
||||
section.toggleVisibility(1, newState: self.readerType == "interface")
|
||||
self.addArrangedSubview(section)
|
||||
self.section = section
|
||||
|
||||
return view
|
||||
self.sliderView = sliderView(
|
||||
action: #selector(self.sliderCallback),
|
||||
value: self.widgetActivationThreshold,
|
||||
initialValue: self.widgetActivationThreshold != 0 ? "\(self.widgetActivationThreshold) KB" : localizedString("Disabled"),
|
||||
min: 0,
|
||||
max: 1024,
|
||||
valueWidth: 70
|
||||
)
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Widget activation threshold"), component: self.sliderView!)
|
||||
]))
|
||||
|
||||
let valueField: NSTextField = NSTextField()
|
||||
valueField.widthAnchor.constraint(equalToConstant: 250).isActive = true
|
||||
valueField.font = NSFont.systemFont(ofSize: 12, weight: .regular)
|
||||
valueField.textColor = .textColor
|
||||
valueField.isEditable = true
|
||||
valueField.isSelectable = true
|
||||
valueField.isBezeled = false
|
||||
valueField.canDrawSubviewsIntoLayer = true
|
||||
valueField.usesSingleLineMode = true
|
||||
valueField.maximumNumberOfLines = 1
|
||||
valueField.focusRingType = .none
|
||||
valueField.stringValue = self.ICMPHost
|
||||
valueField.delegate = self
|
||||
valueField.placeholderString = localizedString("Leave empty to disable the check")
|
||||
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Connectivity host (ICMP)"), component: valueField)
|
||||
]))
|
||||
}
|
||||
|
||||
@objc func handleSelection(_ sender: NSPopUpButton) {
|
||||
@objc private func handleSelection(_ sender: NSPopUpButton) {
|
||||
guard let item = sender.selectedItem, let id = item.identifier?.rawValue else { return }
|
||||
|
||||
if id == "autodetection" {
|
||||
@@ -207,7 +185,6 @@ internal class Settings: NSStackView, Settings_v, NSTextFieldDelegate {
|
||||
|
||||
self.callback()
|
||||
}
|
||||
|
||||
@objc private func changeNumberOfProcesses(_ sender: NSMenuItem) {
|
||||
if let value = Int(sender.title) {
|
||||
self.numberOfProcesses = value
|
||||
@@ -215,32 +192,26 @@ internal class Settings: NSStackView, Settings_v, NSTextFieldDelegate {
|
||||
self.callbackWhenUpdateNumberOfProcesses()
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func changeReaderType(_ sender: NSMenuItem) {
|
||||
guard let key = sender.representedObject as? String else { return }
|
||||
self.readerType = key
|
||||
Store.shared.set(key: "\(self.title)_reader", value: key)
|
||||
self.button?.isEnabled = self.readerType == "interface"
|
||||
|
||||
self.section?.toggleVisibility(1, newState: self.readerType == "interface")
|
||||
NotificationCenter.default.post(name: .resetTotalNetworkUsage, object: nil, userInfo: nil)
|
||||
}
|
||||
|
||||
@objc private func toggleUsageReset(_ sender: NSMenuItem) {
|
||||
guard let key = sender.representedObject as? String else { return }
|
||||
self.usageReset = key
|
||||
Store.shared.set(key: "\(self.title)_usageReset", value: key)
|
||||
self.usageResetCallback()
|
||||
}
|
||||
|
||||
@objc func toggleVPNMode(_ sender: NSControl) {
|
||||
self.VPNModeState = controlState(sender)
|
||||
Store.shared.set(key: "\(self.title)_VPNMode", value: self.VPNModeState)
|
||||
}
|
||||
|
||||
@objc private func sliderCallback(_ sender: NSSlider) {
|
||||
let value = Int(sender.doubleValue)
|
||||
if let container = self.sliderView?.subviews.first(where: { $0.identifier == NSUserInterfaceItemIdentifier("container") }),
|
||||
let field = container.subviews.first(where: { $0 is NSTextField }), let view = field as? NSTextField {
|
||||
if let field = self.sliderView?.subviews.first(where: { $0 is NSTextField }), let view = field as? NSTextField {
|
||||
view.stringValue = value == 0 ? localizedString("Disabled") : "\(value) KB"
|
||||
}
|
||||
self.widgetActivationThreshold = value
|
||||
@@ -261,7 +232,6 @@ internal class Settings: NSStackView, Settings_v, NSTextFieldDelegate {
|
||||
Store.shared.set(key: "\(self.title)_publicIPRefreshInterval", value: self.publicIPRefreshInterval)
|
||||
self.publicIPRefreshIntervalCallback()
|
||||
}
|
||||
|
||||
@objc private func toggleBase(_ sender: NSMenuItem) {
|
||||
guard let key = sender.representedObject as? String else { return }
|
||||
self.baseValue = key
|
||||
|
||||
@@ -93,5 +93,12 @@
|
||||
<integer>6</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>Settings</key>
|
||||
<dict>
|
||||
<key>popup</key>
|
||||
<true/>
|
||||
<key>notifications</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -57,30 +57,28 @@ class Notifications: NotificationsWrapper {
|
||||
self.pressureLevel = Store.shared.string(key: "\(self.module)_notifications_pressure", defaultValue: self.pressureLevel)
|
||||
self.swapSize = Store.shared.string(key: "\(self.module)_notifications_swap", defaultValue: self.swapSize)
|
||||
|
||||
self.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Usage"),
|
||||
action: #selector(self.changeTotalUsage),
|
||||
items: notificationLevels,
|
||||
selected: self.totalUsageLevel
|
||||
))
|
||||
self.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Free memory (less than)"),
|
||||
action: #selector(self.changeFree),
|
||||
items: notificationLevels,
|
||||
selected: self.freeLevel
|
||||
))
|
||||
self.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Memory pressure"),
|
||||
action: #selector(self.changePressure),
|
||||
items: memoryPressureLevels.filter({ $0.key != "normal" }),
|
||||
selected: self.pressureLevel
|
||||
))
|
||||
self.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Swap size"),
|
||||
action: #selector(self.changeSwap),
|
||||
items: swapSizes,
|
||||
selected: self.swapSize
|
||||
))
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Usage"), component: selectView(
|
||||
action: #selector(self.changeTotalUsage),
|
||||
items: notificationLevels,
|
||||
selected: self.totalUsageLevel
|
||||
)),
|
||||
PreferencesRow(localizedString("Free memory (less than)"), component: selectView(
|
||||
action: #selector(self.changeFree),
|
||||
items: notificationLevels,
|
||||
selected: self.freeLevel
|
||||
)),
|
||||
PreferencesRow(localizedString("Memory pressure"), component: selectView(
|
||||
action: #selector(self.changePressure),
|
||||
items: memoryPressureLevels.filter({ $0.key != "normal" }),
|
||||
selected: self.pressureLevel
|
||||
)),
|
||||
PreferencesRow(localizedString("Swap size"), component: selectView(
|
||||
action: #selector(self.changeSwap),
|
||||
items: swapSizes,
|
||||
selected: self.swapSize
|
||||
))
|
||||
]))
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
|
||||
@@ -55,6 +55,7 @@ internal class Popup: PopupWrapper {
|
||||
private var lineChartHistory: Int = 180
|
||||
private var lineChartScale: Scale = .none
|
||||
private var lineChartFixedScale: Double = 1
|
||||
private var chartPrefSection: PreferencesSection? = nil
|
||||
|
||||
private var appColorState: Color = .secondBlue
|
||||
private var appColor: NSColor { self.appColorState.additional as? NSColor ?? NSColor.systemRed }
|
||||
@@ -284,60 +285,54 @@ internal class Popup: PopupWrapper {
|
||||
public override func settings() -> NSView? {
|
||||
let view = SettingsContainerView()
|
||||
|
||||
view.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("App color"),
|
||||
action: #selector(toggleAppColor),
|
||||
items: Color.allColors,
|
||||
selected: self.appColorState.key
|
||||
))
|
||||
view.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Wired color"),
|
||||
action: #selector(toggleWiredColor),
|
||||
items: Color.allColors,
|
||||
selected: self.wiredColorState.key
|
||||
))
|
||||
view.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Compressed color"),
|
||||
action: #selector(toggleCompressedColor),
|
||||
items: Color.allColors,
|
||||
selected: self.compressedColorState.key
|
||||
))
|
||||
view.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Free color"),
|
||||
action: #selector(toggleFreeColor),
|
||||
items: Color.allColors,
|
||||
selected: self.freeColorState.key
|
||||
))
|
||||
view.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Chart color"),
|
||||
action: #selector(toggleChartColor),
|
||||
items: Color.allColors,
|
||||
selected: self.chartColorState.key
|
||||
))
|
||||
view.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("App color"), component: selectView(
|
||||
action: #selector(toggleAppColor),
|
||||
items: Color.allColors,
|
||||
selected: self.appColorState.key
|
||||
)),
|
||||
PreferencesRow(localizedString("Wired color"), component: selectView(
|
||||
action: #selector(toggleWiredColor),
|
||||
items: Color.allColors,
|
||||
selected: self.wiredColorState.key
|
||||
)),
|
||||
PreferencesRow(localizedString("Compressed color"), component: selectView(
|
||||
action: #selector(toggleCompressedColor),
|
||||
items: Color.allColors,
|
||||
selected: self.compressedColorState.key
|
||||
)),
|
||||
PreferencesRow(localizedString("Free color"), component: selectView(
|
||||
action: #selector(toggleFreeColor),
|
||||
items: Color.allColors,
|
||||
selected: self.freeColorState.key
|
||||
))
|
||||
]))
|
||||
|
||||
view.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Chart duration"),
|
||||
action: #selector(self.toggleLineChartHistory),
|
||||
items: LineChartHistory,
|
||||
selected: "\(self.lineChartHistory)"
|
||||
))
|
||||
|
||||
view.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Main chart scaling"),
|
||||
action: #selector(self.toggleLineChartScale),
|
||||
items: Scale.allCases,
|
||||
selected: self.lineChartScale.key
|
||||
))
|
||||
|
||||
let slider = sliderSettingsRow(
|
||||
title: localizedString("Scale value"),
|
||||
self.sliderView = sliderView(
|
||||
action: #selector(self.toggleLineChartFixedScale),
|
||||
value: Int(self.lineChartFixedScale * 100),
|
||||
initialValue: "\(Int(self.lineChartFixedScale * 100)) %",
|
||||
isHidden: self.lineChartScale != .fixed
|
||||
initialValue: "\(Int(self.lineChartFixedScale * 100)) %"
|
||||
)
|
||||
self.sliderView = slider
|
||||
view.addArrangedSubview(slider)
|
||||
self.chartPrefSection = PreferencesSection([
|
||||
PreferencesRow(localizedString("Chart color"), component: selectView(
|
||||
action: #selector(self.toggleChartColor),
|
||||
items: Color.allColors,
|
||||
selected: self.chartColorState.key
|
||||
)),
|
||||
PreferencesRow(localizedString("Chart history"), component: selectView(
|
||||
action: #selector(self.toggleLineChartHistory),
|
||||
items: LineChartHistory,
|
||||
selected: "\(self.lineChartHistory)"
|
||||
)),
|
||||
PreferencesRow(localizedString("Main chart scaling"), component: selectView(
|
||||
action: #selector(self.toggleLineChartScale),
|
||||
items: Scale.allCases,
|
||||
selected: self.lineChartScale.key
|
||||
)),
|
||||
PreferencesRow(localizedString("Scale value"), component: self.sliderView!)
|
||||
])
|
||||
self.chartPrefSection?.toggleVisibility(3, newState: self.lineChartScale == .fixed)
|
||||
view.addArrangedSubview(self.chartPrefSection!)
|
||||
|
||||
return view
|
||||
}
|
||||
@@ -406,7 +401,7 @@ internal class Popup: PopupWrapper {
|
||||
@objc private func toggleLineChartScale(_ sender: NSMenuItem) {
|
||||
guard let key = sender.representedObject as? String,
|
||||
let value = Scale.allCases.first(where: { $0.key == key }) else { return }
|
||||
self.sliderView?.isHidden = value != .fixed
|
||||
self.chartPrefSection?.toggleVisibility(3, newState: value == .fixed)
|
||||
self.lineChartScale = value
|
||||
self.chart?.setScale(self.lineChartScale, fixedScale: self.lineChartFixedScale)
|
||||
Store.shared.set(key: "\(self.title)_lineChartScale", value: key)
|
||||
@@ -415,8 +410,7 @@ internal class Popup: PopupWrapper {
|
||||
@objc private func toggleLineChartFixedScale(_ sender: NSSlider) {
|
||||
let value = Int(sender.doubleValue)
|
||||
|
||||
if let container = self.sliderView?.subviews.first(where: { $0.identifier == NSUserInterfaceItemIdentifier("container") }),
|
||||
let field = container.subviews.first(where: { $0 is NSTextField }), let view = field as? NSTextField {
|
||||
if let field = self.sliderView?.subviews.first(where: { $0 is NSTextField }), let view = field as? NSTextField {
|
||||
view.stringValue = "\(value) %"
|
||||
}
|
||||
|
||||
|
||||
@@ -34,16 +34,10 @@ internal class Settings: NSStackView, Settings_v {
|
||||
self.splitValueState = Store.shared.bool(key: "\(self.title)_splitValue", defaultValue: self.splitValueState)
|
||||
self.notificationLevel = Store.shared.string(key: "\(self.title)_notificationLevel", defaultValue: self.notificationLevel)
|
||||
|
||||
super.init(frame: NSRect(x: 0, y: 0, width: 0, height: 0))
|
||||
super.init(frame: NSRect.zero)
|
||||
|
||||
self.orientation = .vertical
|
||||
self.distribution = .gravityAreas
|
||||
self.edgeInsets = NSEdgeInsets(
|
||||
top: Constants.Settings.margin,
|
||||
left: Constants.Settings.margin,
|
||||
bottom: Constants.Settings.margin,
|
||||
right: Constants.Settings.margin
|
||||
)
|
||||
self.spacing = Constants.Settings.margin
|
||||
}
|
||||
|
||||
@@ -54,41 +48,35 @@ internal class Settings: NSStackView, Settings_v {
|
||||
public func load(widgets: [widget_t]) {
|
||||
self.subviews.forEach{ $0.removeFromSuperview() }
|
||||
|
||||
self.addArrangedSubview(selectSettingsRowV1(
|
||||
title: localizedString("Update interval"),
|
||||
action: #selector(changeUpdateInterval),
|
||||
items: ReaderUpdateIntervals.map{ "\($0) sec" },
|
||||
selected: "\(self.updateIntervalValue) sec"
|
||||
))
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Update interval"), component: selectView(
|
||||
action: #selector(self.changeUpdateInterval),
|
||||
items: ReaderUpdateIntervals.map{ KeyValue_t(key: "\($0)", value: "\($0) sec") },
|
||||
selected: "\(self.updateIntervalValue) sec"
|
||||
)),
|
||||
PreferencesRow(localizedString("Update interval for top processes"), component: selectView(
|
||||
action: #selector(self.changeUpdateTopInterval),
|
||||
items: ReaderUpdateIntervals.map{ KeyValue_t(key: "\($0)", value: "\($0) sec") },
|
||||
selected: "\(self.updateTopIntervalValue) sec"
|
||||
))
|
||||
]))
|
||||
|
||||
self.addArrangedSubview(selectSettingsRowV1(
|
||||
title: localizedString("Update interval for top processes"),
|
||||
action: #selector(changeUpdateTopInterval),
|
||||
items: ReaderUpdateIntervals.map{ "\($0) sec" },
|
||||
selected: "\(self.updateTopIntervalValue) sec"
|
||||
))
|
||||
|
||||
self.addArrangedSubview(selectSettingsRowV1(
|
||||
title: localizedString("Number of top processes"),
|
||||
action: #selector(changeNumberOfProcesses),
|
||||
items: NumbersOfProcesses.map{ "\($0)" },
|
||||
selected: "\(self.numberOfProcesses)"
|
||||
))
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Number of top processes"), component: selectView(
|
||||
action: #selector(changeNumberOfProcesses),
|
||||
items: NumbersOfProcesses.map{ KeyValue_t(key: "\($0)", value: "\($0)") },
|
||||
selected: "\(self.numberOfProcesses)"
|
||||
))
|
||||
]))
|
||||
|
||||
if !widgets.filter({ $0 == .barChart }).isEmpty {
|
||||
self.addArrangedSubview(toggleSettingRow(
|
||||
title: localizedString("Split the value (App/Wired/Compressed)"),
|
||||
action: #selector(toggleSplitValue),
|
||||
state: self.splitValueState
|
||||
))
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Split the value (App/Wired/Compressed)"), component: switchView(
|
||||
action: #selector(toggleSplitValue),
|
||||
state: self.splitValueState
|
||||
))
|
||||
]))
|
||||
}
|
||||
|
||||
self.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Notification level"),
|
||||
action: #selector(changeNotificationLevel),
|
||||
items: notificationLevels,
|
||||
selected: self.notificationLevel == "disabled" ? self.notificationLevel : "\(Int((Double(self.notificationLevel) ?? 0)*100))%"
|
||||
))
|
||||
}
|
||||
|
||||
@objc private func changeUpdateInterval(_ sender: NSMenuItem) {
|
||||
@@ -98,7 +86,6 @@ internal class Settings: NSStackView, Settings_v {
|
||||
self.setInterval(value)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func changeUpdateTopInterval(_ sender: NSMenuItem) {
|
||||
if let value = Int(sender.title.replacingOccurrences(of: " sec", with: "")) {
|
||||
self.updateTopIntervalValue = value
|
||||
@@ -106,7 +93,6 @@ internal class Settings: NSStackView, Settings_v {
|
||||
self.setTopInterval(value)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func changeNumberOfProcesses(_ sender: NSMenuItem) {
|
||||
if let value = Int(sender.title) {
|
||||
self.numberOfProcesses = value
|
||||
@@ -114,27 +100,9 @@ internal class Settings: NSStackView, Settings_v {
|
||||
self.callbackWhenUpdateNumberOfProcesses()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func toggleSplitValue(_ sender: NSControl) {
|
||||
var state: NSControl.StateValue? = nil
|
||||
if #available(OSX 10.15, *) {
|
||||
state = sender is NSSwitch ? (sender as! NSSwitch).state: nil
|
||||
} else {
|
||||
state = sender is NSButton ? (sender as! NSButton).state: nil
|
||||
}
|
||||
|
||||
self.splitValueState = state! == .on ? true : false
|
||||
@objc private func toggleSplitValue(_ sender: NSControl) {
|
||||
self.splitValueState = controlState(sender)
|
||||
Store.shared.set(key: "\(self.title)_splitValue", value: self.splitValueState)
|
||||
self.callback()
|
||||
}
|
||||
|
||||
@objc func changeNotificationLevel(_ sender: NSMenuItem) {
|
||||
guard let key = sender.representedObject as? String else { return }
|
||||
|
||||
if key == "Disabled" {
|
||||
Store.shared.set(key: "\(self.title)_notificationLevel", value: key)
|
||||
} else if let value = Double(key.replacingOccurrences(of: "%", with: "")) {
|
||||
Store.shared.set(key: "\(self.title)_notificationLevel", value: "\(value/100)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,5 +51,12 @@
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>Settings</key>
|
||||
<dict>
|
||||
<key>popup</key>
|
||||
<true/>
|
||||
<key>notifications</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -60,44 +60,19 @@ class Notifications: NotificationsWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
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.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
|
||||
|
||||
let section = PreferencesSection(label: typ.rawValue)
|
||||
groups.forEach { (group: SensorGroup) in
|
||||
filtered.filter{ $0.group == group }.forEach { (s: Sensor_p) in
|
||||
var items = notificationLevels
|
||||
if s.type == .temperature {
|
||||
items = temperatureLevels
|
||||
}
|
||||
let row: NSView = selectSettingsRow(
|
||||
title: localizedString(s.name),
|
||||
let btn = selectView(
|
||||
action: #selector(self.changeSensorNotificaion),
|
||||
items: items,
|
||||
items: s.type == .temperature ? temperatureLevels : notificationLevels,
|
||||
selected: s.notificationThreshold
|
||||
)
|
||||
row.subviews.filter{ $0 is NSControl }.forEach { (control: NSView) in
|
||||
control.identifier = NSUserInterfaceItemIdentifier(rawValue: s.key)
|
||||
}
|
||||
container.addArrangedSubview(row)
|
||||
btn.identifier = NSUserInterfaceItemIdentifier(rawValue: s.key)
|
||||
section.add(PreferencesRow(localizedString(s.name), component: btn))
|
||||
}
|
||||
}
|
||||
|
||||
self.addArrangedSubview(container)
|
||||
self.addArrangedSubview(section)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,32 +44,32 @@ internal class Popup: PopupWrapper {
|
||||
private var fanValueState: FanValue = .percentage
|
||||
|
||||
private var sensors: [Sensor_p] = []
|
||||
private let settingsView: NSStackView = SettingsContainerView()
|
||||
private let settingsView: NSStackView = NSStackView()
|
||||
|
||||
private var fanControlState: Bool {
|
||||
get {
|
||||
Store.shared.bool(key: "Sensors_fanControl", defaultValue: true)
|
||||
}
|
||||
set {
|
||||
Store.shared.set(key: "Sensors_fanControl", value: newValue)
|
||||
}
|
||||
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.fanValueState = FanValue(rawValue: Store.shared.string(key: "Sensors_popup_fanValue", defaultValue: self.fanValueState.rawValue)) ?? .percentage
|
||||
|
||||
self.orientation = .vertical
|
||||
self.spacing = 0
|
||||
self.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
self.settingsView.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Fan value"),
|
||||
action: #selector(self.toggleFanValue),
|
||||
items: FanValues,
|
||||
selected: self.fanValueState.rawValue
|
||||
))
|
||||
self.settingsView.orientation = .vertical
|
||||
self.settingsView.spacing = Constants.Settings.margin
|
||||
|
||||
self.fanValueState = FanValue(rawValue: Store.shared.string(key: "Sensors_popup_fanValue", defaultValue: self.fanValueState.rawValue)) ?? .percentage
|
||||
self.settingsView.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Fan value"), component: selectView(
|
||||
action: #selector(self.toggleFanValue),
|
||||
items: FanValues,
|
||||
selected: self.fanValueState.rawValue
|
||||
))
|
||||
]))
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@@ -86,14 +86,9 @@ internal class Popup: PopupWrapper {
|
||||
|
||||
self.subviews.forEach({ $0.removeFromSuperview() })
|
||||
if !reload {
|
||||
self.settingsView.subviews.forEach({ $0.removeFromSuperview() })
|
||||
|
||||
self.settingsView.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Fan value"),
|
||||
action: #selector(self.toggleFanValue),
|
||||
items: FanValues,
|
||||
selected: self.fanValueState.rawValue
|
||||
))
|
||||
self.settingsView.subviews.filter({ $0.identifier == NSUserInterfaceItemIdentifier("sensor") }).forEach { v in
|
||||
v.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
if !fans.isEmpty {
|
||||
@@ -141,35 +136,19 @@ internal class Popup: PopupWrapper {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
let section = PreferencesSection(label: typ.rawValue)
|
||||
section.identifier = NSUserInterfaceItemIdentifier("sensor")
|
||||
groups.forEach { (group: SensorGroup) in
|
||||
filtered.filter{ $0.group == group }.forEach { (s: Sensor_p) in
|
||||
let row: NSView = toggleSettingRow(title: localizedString(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)
|
||||
let btn = switchView(
|
||||
action: #selector(self.toggleSensor),
|
||||
state: s.popupState
|
||||
)
|
||||
btn.identifier = NSUserInterfaceItemIdentifier(rawValue: s.key)
|
||||
section.add(PreferencesRow(localizedString(s.name), component: btn))
|
||||
}
|
||||
}
|
||||
|
||||
self.settingsView.addArrangedSubview(container)
|
||||
self.settingsView.addArrangedSubview(section)
|
||||
}
|
||||
|
||||
if typ == .fan { return }
|
||||
|
||||
@@ -33,18 +33,8 @@ internal class Settings: NSStackView, Settings_v {
|
||||
self.title = module.rawValue
|
||||
self.hidState = SystemKit.shared.device.platform == .m1 ? true : false
|
||||
|
||||
super.init(frame: NSRect(x: 0, y: 0, width: 0, height: 0))
|
||||
|
||||
self.wantsLayer = true
|
||||
super.init(frame: NSRect.zero)
|
||||
self.orientation = .vertical
|
||||
self.distribution = .gravityAreas
|
||||
self.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.edgeInsets = NSEdgeInsets(
|
||||
top: Constants.Settings.margin,
|
||||
left: Constants.Settings.margin,
|
||||
bottom: Constants.Settings.margin,
|
||||
right: Constants.Settings.margin
|
||||
)
|
||||
self.spacing = Constants.Settings.margin
|
||||
|
||||
self.updateIntervalValue = Store.shared.int(key: "\(self.title)_updateInterval", defaultValue: self.updateIntervalValue)
|
||||
@@ -53,6 +43,44 @@ internal class Settings: NSStackView, Settings_v {
|
||||
self.fansSyncState = Store.shared.bool(key: "\(self.title)_fansSync", defaultValue: self.fansSyncState)
|
||||
self.unknownSensorsState = Store.shared.bool(key: "\(self.title)_unknown", defaultValue: self.unknownSensorsState)
|
||||
self.fanValueState = FanValue(rawValue: Store.shared.string(key: "\(self.title)_fanValue", defaultValue: self.fanValueState.rawValue)) ?? .percentage
|
||||
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Update interval"), component: selectView(
|
||||
action: #selector(self.changeUpdateInterval),
|
||||
items: ReaderUpdateIntervals.map{ KeyValue_t(key: "\($0)", value: "\($0) sec") },
|
||||
selected: "\(self.updateIntervalValue) sec"
|
||||
))
|
||||
]))
|
||||
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Fan value"), component: selectView(
|
||||
action: #selector(self.toggleFanValue),
|
||||
items: FanValues,
|
||||
selected: self.fanValueState.rawValue
|
||||
)),
|
||||
PreferencesRow(localizedString("Save the fan speed"), component: switchView(
|
||||
action: #selector(self.toggleSpeedState),
|
||||
state: self.fanSpeedState
|
||||
)),
|
||||
PreferencesRow(localizedString("Synchronize fan's control"), component: switchView(
|
||||
action: #selector(self.toggleFansSync),
|
||||
state: self.fansSyncState
|
||||
))
|
||||
]))
|
||||
|
||||
var sensorsPrefs: [PreferencesRow] = [
|
||||
PreferencesRow(localizedString("Show unknown sensors"), component: switchView(
|
||||
action: #selector(self.toggleuUnknownSensors),
|
||||
state: self.unknownSensorsState
|
||||
))
|
||||
]
|
||||
if isARM {
|
||||
sensorsPrefs.append(PreferencesRow(localizedString("HID sensors"), component: switchView(
|
||||
action: #selector(self.toggleHID),
|
||||
state: self.hidState
|
||||
)))
|
||||
}
|
||||
self.addArrangedSubview(PreferencesSection(sensorsPrefs))
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@@ -68,48 +96,10 @@ internal class Settings: NSStackView, Settings_v {
|
||||
sensors = sensors.filter({ $0.group != .unknown })
|
||||
}
|
||||
|
||||
self.subviews.forEach{ $0.removeFromSuperview() }
|
||||
|
||||
self.addArrangedSubview(selectSettingsRowV1(
|
||||
title: localizedString("Update interval"),
|
||||
action: #selector(changeUpdateInterval),
|
||||
items: ReaderUpdateIntervals.map{ "\($0) sec" },
|
||||
selected: "\(self.updateIntervalValue) sec"
|
||||
))
|
||||
|
||||
self.addArrangedSubview(toggleSettingRow(
|
||||
title: localizedString("Save the fan speed"),
|
||||
action: #selector(toggleSpeedState),
|
||||
state: self.fanSpeedState
|
||||
))
|
||||
|
||||
self.addArrangedSubview(toggleSettingRow(
|
||||
title: localizedString("Synchronize fan's control"),
|
||||
action: #selector(toggleFansSync),
|
||||
state: self.fansSyncState
|
||||
))
|
||||
|
||||
self.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Fan value"),
|
||||
action: #selector(toggleFanValue),
|
||||
items: FanValues,
|
||||
selected: self.fanValueState.rawValue
|
||||
))
|
||||
|
||||
if isARM {
|
||||
self.addArrangedSubview(toggleSettingRow(
|
||||
title: localizedString("HID sensors"),
|
||||
action: #selector(toggleHID),
|
||||
state: self.hidState
|
||||
))
|
||||
self.subviews.filter({ $0.identifier == NSUserInterfaceItemIdentifier("sensor") }).forEach { v in
|
||||
v.removeFromSuperview()
|
||||
}
|
||||
|
||||
self.addArrangedSubview(toggleSettingRow(
|
||||
title: localizedString("Show unknown sensors"),
|
||||
action: #selector(toggleuUnknownSensors),
|
||||
state: self.unknownSensorsState
|
||||
))
|
||||
|
||||
var types: [SensorType] = []
|
||||
sensors.forEach { (s: Sensor_p) in
|
||||
if !types.contains(s.type) {
|
||||
@@ -118,18 +108,8 @@ internal class Settings: NSStackView, Settings_v {
|
||||
}
|
||||
|
||||
types.forEach { (typ: SensorType) in
|
||||
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.addArrangedSubview(header)
|
||||
let section = PreferencesSection(label: typ.rawValue)
|
||||
section.identifier = NSUserInterfaceItemIdentifier("sensor")
|
||||
|
||||
let filtered = sensors.filter{ $0.type == typ }
|
||||
var groups: [SensorGroup] = []
|
||||
@@ -138,28 +118,16 @@ internal class Settings: NSStackView, Settings_v {
|
||||
groups.append(s.group)
|
||||
}
|
||||
}
|
||||
|
||||
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: localizedString(s.name), action: #selector(self.toggleFan), state: s.state)
|
||||
row.subviews.filter{ $0 is NSControl }.forEach { (control: NSView) in
|
||||
control.identifier = NSUserInterfaceItemIdentifier(rawValue: s.key)
|
||||
}
|
||||
container.addArrangedSubview(row)
|
||||
section.add(PreferencesRow(localizedString(s.name), component: switchView(
|
||||
action: #selector(self.toggleSensor),
|
||||
state: s.state
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
self.addArrangedSubview(container)
|
||||
self.addArrangedSubview(section)
|
||||
}
|
||||
|
||||
self.widgets = widgets
|
||||
@@ -171,12 +139,11 @@ internal class Settings: NSStackView, Settings_v {
|
||||
self.load(widgets: self.widgets)
|
||||
}
|
||||
|
||||
@objc private func toggleFan(_ sender: NSControl) {
|
||||
@objc private func toggleSensor(_ sender: NSControl) {
|
||||
guard let id = sender.identifier else { return }
|
||||
Store.shared.set(key: "sensor_\(id.rawValue)", value: controlState(sender))
|
||||
self.callback()
|
||||
}
|
||||
|
||||
@objc private func changeUpdateInterval(_ sender: NSMenuItem) {
|
||||
if let value = Int(sender.title.replacingOccurrences(of: " sec", with: "")) {
|
||||
self.updateIntervalValue = value
|
||||
@@ -184,30 +151,25 @@ internal class Settings: NSStackView, Settings_v {
|
||||
self.setInterval(value)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func toggleSpeedState(_ sender: NSControl) {
|
||||
self.fanSpeedState = controlState(sender)
|
||||
Store.shared.set(key: "\(self.title)_speed", value: self.fanSpeedState)
|
||||
self.callback()
|
||||
}
|
||||
|
||||
@objc private func toggleHID(_ sender: NSControl) {
|
||||
self.hidState = controlState(sender)
|
||||
Store.shared.set(key: "\(self.title)_hid", value: self.hidState)
|
||||
self.HIDcallback()
|
||||
}
|
||||
|
||||
@objc private func toggleFansSync(_ sender: NSControl) {
|
||||
self.fansSyncState = controlState(sender)
|
||||
Store.shared.set(key: "\(self.title)_fansSync", value: self.fansSyncState)
|
||||
}
|
||||
|
||||
@objc private func toggleuUnknownSensors(_ sender: NSControl) {
|
||||
self.unknownSensorsState = controlState(sender)
|
||||
Store.shared.set(key: "\(self.title)_unknown", value: self.unknownSensorsState)
|
||||
self.unknownCallback()
|
||||
}
|
||||
|
||||
@objc private func toggleFanValue(_ sender: NSMenuItem) {
|
||||
if let key = sender.representedObject as? String, let value = FanValue(rawValue: key) {
|
||||
self.fanValueState = value
|
||||
|
||||
@@ -1078,8 +1078,8 @@
|
||||
children = (
|
||||
9ABFF902248BEBD700C9041A /* main.swift */,
|
||||
9ABFF90F248BEE7200C9041A /* readers.swift */,
|
||||
5EE8037E29C36BDD0063D37D /* portal.swift */,
|
||||
9ABFF913248C30A800C9041A /* popup.swift */,
|
||||
5EE8037E29C36BDD0063D37D /* portal.swift */,
|
||||
9AD64FA124BF86C100419D59 /* settings.swift */,
|
||||
5CD342F32B2F2FB700225631 /* notifications.swift */,
|
||||
9ABFF8F9248BEBCB00C9041A /* Info.plist */,
|
||||
|
||||
Reference in New Issue
Block a user