From f873fdaa3d0462d304100dd08511ed1995539736 Mon Sep 17 00:00:00 2001 From: Serhiy Mytrovtsiy Date: Wed, 24 Apr 2024 06:10:27 +0200 Subject: [PATCH] feat: moved all modules settings to the new design --- Kit/extensions.swift | 151 ++++----------------- Kit/helpers.swift | 118 ++++++++++++----- Kit/module/module.swift | 5 + Kit/module/notifications.swift | 6 - Kit/module/settings.swift | 192 ++++++++++++++------------- Modules/Battery/config.plist | 7 + Modules/Battery/notifications.swift | 25 ++-- Modules/Battery/popup.swift | 11 +- Modules/Battery/settings.swift | 41 +++--- Modules/Bluetooth/config.plist | 7 + Modules/Bluetooth/settings.swift | 39 +++--- Modules/CPU/config.plist | 7 + Modules/CPU/notifications.swift | 62 ++++----- Modules/CPU/popup.swift | 121 ++++++++--------- Modules/CPU/settings.swift | 125 +++++++++--------- Modules/Clock/config.plist | 7 + Modules/Clock/settings.swift | 42 +++--- Modules/Disk/config.plist | 7 + Modules/Disk/notifications.swift | 13 +- Modules/Disk/popup.swift | 52 ++++++-- Modules/Disk/settings.swift | 118 +++++------------ Modules/GPU/config.plist | 7 + Modules/GPU/notifications.swift | 13 +- Modules/GPU/settings.swift | 95 ++++--------- Modules/Net/config.plist | 7 + Modules/Net/popup.swift | 60 +++++---- Modules/Net/settings.swift | 198 ++++++++++++---------------- Modules/RAM/config.plist | 7 + Modules/RAM/notifications.swift | 46 ++++--- Modules/RAM/popup.swift | 100 +++++++------- Modules/RAM/settings.swift | 88 ++++--------- Modules/Sensors/config.plist | 7 + Modules/Sensors/notifications.swift | 37 +----- Modules/Sensors/popup.swift | 73 ++++------ Modules/Sensors/settings.swift | 136 +++++++------------ Stats.xcodeproj/project.pbxproj | 2 +- 36 files changed, 901 insertions(+), 1131 deletions(-) diff --git a/Kit/extensions.swift b/Kit/extensions.swift index ff82a6b9..ba74f320 100644 --- a/Kit/extensions.swift +++ b/Kit/extensions.swift @@ -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 { diff --git a/Kit/helpers.swift b/Kit/helpers.swift index df151549..6b5dabff 100644 --- a/Kit/helpers.swift +++ b/Kit/helpers.swift @@ -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) } diff --git a/Kit/module/module.swift b/Kit/module/module.swift index e2008e30..bc322ffb 100644 --- a/Kit/module/module.swift +++ b/Kit/module/module.swift @@ -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 + } } } diff --git a/Kit/module/notifications.swift b/Kit/module/notifications.swift index 2e174e62..523da673 100644 --- a/Kit/module/notifications.swift +++ b/Kit/module/notifications.swift @@ -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 } diff --git a/Kit/module/settings.swift b/Kit/module/settings.swift index b837689a..bbfad48e 100644 --- a/Kit/module/settings.swift +++ b/Kit/module/settings.swift @@ -23,6 +23,10 @@ public protocol Settings_v: NSView { open class Settings: NSStackView, Settings_p { private var config: UnsafePointer 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, 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 - } } diff --git a/Modules/Battery/config.plist b/Modules/Battery/config.plist index aeba13de..c7fd69aa 100644 --- a/Modules/Battery/config.plist +++ b/Modules/Battery/config.plist @@ -77,5 +77,12 @@ 4 + Settings + + popup + + notifications + + diff --git a/Modules/Battery/notifications.swift b/Modules/Battery/notifications.swift index 023b11aa..f0f43e19 100644 --- a/Modules/Battery/notifications.swift +++ b/Modules/Battery/notifications.swift @@ -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)" diff --git a/Modules/Battery/popup.swift b/Modules/Battery/popup.swift index 1682ffc5..8e83c4ff 100644 --- a/Modules/Battery/popup.swift +++ b/Modules/Battery/popup.swift @@ -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 } diff --git a/Modules/Battery/settings.swift b/Modules/Battery/settings.swift index 72809582..fc2ed593 100644 --- a/Modules/Battery/settings.swift +++ b/Modules/Battery/settings.swift @@ -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() diff --git a/Modules/Bluetooth/config.plist b/Modules/Bluetooth/config.plist index 3716ebc5..e682d921 100644 --- a/Modules/Bluetooth/config.plist +++ b/Modules/Bluetooth/config.plist @@ -34,5 +34,12 @@ 1 + Settings + + popup + + notifications + + diff --git a/Modules/Bluetooth/settings.swift b/Modules/Bluetooth/settings.swift index e64741a5..b9008644 100644 --- a/Modules/Bluetooth/settings.swift +++ b/Modules/Bluetooth/settings.swift @@ -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() } } diff --git a/Modules/CPU/config.plist b/Modules/CPU/config.plist index 19d8b6df..d034f744 100644 --- a/Modules/CPU/config.plist +++ b/Modules/CPU/config.plist @@ -81,5 +81,12 @@ 5 + Settings + + popup + + notifications + + diff --git a/Modules/CPU/notifications.swift b/Modules/CPU/notifications.swift index e92927f1..248893fd 100644 --- a/Modules/CPU/notifications.swift +++ b/Modules/CPU/notifications.swift @@ -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 } diff --git a/Modules/CPU/popup.swift b/Modules/CPU/popup.swift index 4ef39477..4fbda7a8 100644 --- a/Modules/CPU/popup.swift +++ b/Modules/CPU/popup.swift @@ -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) %" } diff --git a/Modules/CPU/settings.swift b/Modules/CPU/settings.swift index 18c75101..0645fffc 100644 --- a/Modules/CPU/settings.swift +++ b/Modules/CPU/settings.swift @@ -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) diff --git a/Modules/Clock/config.plist b/Modules/Clock/config.plist index ed007513..135756c4 100644 --- a/Modules/Clock/config.plist +++ b/Modules/Clock/config.plist @@ -32,5 +32,12 @@ 1 + Settings + + popup + + notifications + + diff --git a/Modules/Clock/settings.swift b/Modules/Clock/settings.swift index 377e8d79..54e006de 100644 --- a/Modules/Clock/settings.swift +++ b/Modules/Clock/settings.swift @@ -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")!) + } } diff --git a/Modules/Disk/config.plist b/Modules/Disk/config.plist index d2f48543..994972f1 100644 --- a/Modules/Disk/config.plist +++ b/Modules/Disk/config.plist @@ -117,5 +117,12 @@ + Settings + + popup + + notifications + + diff --git a/Modules/Disk/notifications.swift b/Modules/Disk/notifications.swift index 7aceb4d0..12884062 100644 --- a/Modules/Disk/notifications.swift +++ b/Modules/Disk/notifications.swift @@ -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) { diff --git a/Modules/Disk/popup.swift b/Modules/Disk/popup.swift index a507ee43..4b9e61cb 100644 --- a/Modules/Disk/popup.swift +++ b/Modules/Disk/popup.swift @@ -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 { diff --git a/Modules/Disk/settings.swift b/Modules/Disk/settings.swift index 55c55441..7ff6d482 100644 --- a/Modules/Disk/settings.swift +++ b/Modules/Disk/settings.swift @@ -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) diff --git a/Modules/GPU/config.plist b/Modules/GPU/config.plist index 1f60da38..18dbe1dc 100644 --- a/Modules/GPU/config.plist +++ b/Modules/GPU/config.plist @@ -73,5 +73,12 @@ 4 + Settings + + popup + + notifications + + diff --git a/Modules/GPU/notifications.swift b/Modules/GPU/notifications.swift index 326c3896..be7ebb58 100644 --- a/Modules/GPU/notifications.swift +++ b/Modules/GPU/notifications.swift @@ -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) { diff --git a/Modules/GPU/settings.swift b/Modules/GPU/settings.swift index a7c561f0..1df1ffb6 100644 --- a/Modules/GPU/settings.swift +++ b/Modules/GPU/settings.swift @@ -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() } diff --git a/Modules/Net/config.plist b/Modules/Net/config.plist index fbb313e3..40f1fb05 100644 --- a/Modules/Net/config.plist +++ b/Modules/Net/config.plist @@ -55,5 +55,12 @@ + Settings + + popup + + notifications + + diff --git a/Modules/Net/popup.swift b/Modules/Net/popup.swift index 56ba5d7d..cc24c232 100644 --- a/Modules/Net/popup.swift +++ b/Modules/Net/popup.swift @@ -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 } diff --git a/Modules/Net/settings.swift b/Modules/Net/settings.swift index c2802dce..3b93a87e 100644 --- a/Modules/Net/settings.swift +++ b/Modules/Net/settings.swift @@ -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 diff --git a/Modules/RAM/config.plist b/Modules/RAM/config.plist index ad15742d..83061491 100644 --- a/Modules/RAM/config.plist +++ b/Modules/RAM/config.plist @@ -93,5 +93,12 @@ 6 + Settings + + popup + + notifications + + diff --git a/Modules/RAM/notifications.swift b/Modules/RAM/notifications.swift index cad5765f..dfac72b5 100644 --- a/Modules/RAM/notifications.swift +++ b/Modules/RAM/notifications.swift @@ -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) { diff --git a/Modules/RAM/popup.swift b/Modules/RAM/popup.swift index 867a20aa..8f144ff0 100644 --- a/Modules/RAM/popup.swift +++ b/Modules/RAM/popup.swift @@ -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) %" } diff --git a/Modules/RAM/settings.swift b/Modules/RAM/settings.swift index b558edfe..920acd8c 100644 --- a/Modules/RAM/settings.swift +++ b/Modules/RAM/settings.swift @@ -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)") - } - } } diff --git a/Modules/Sensors/config.plist b/Modules/Sensors/config.plist index 9f0d0c8b..4b4de803 100644 --- a/Modules/Sensors/config.plist +++ b/Modules/Sensors/config.plist @@ -51,5 +51,12 @@ 2 + Settings + + popup + + notifications + + diff --git a/Modules/Sensors/notifications.swift b/Modules/Sensors/notifications.swift index addbf9c6..ec63e9fd 100644 --- a/Modules/Sensors/notifications.swift +++ b/Modules/Sensors/notifications.swift @@ -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) } } diff --git a/Modules/Sensors/popup.swift b/Modules/Sensors/popup.swift index 3b2ef270..494cd12d 100644 --- a/Modules/Sensors/popup.swift +++ b/Modules/Sensors/popup.swift @@ -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 } diff --git a/Modules/Sensors/settings.swift b/Modules/Sensors/settings.swift index aa5cb0b7..d8f611a2 100644 --- a/Modules/Sensors/settings.swift +++ b/Modules/Sensors/settings.swift @@ -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 diff --git a/Stats.xcodeproj/project.pbxproj b/Stats.xcodeproj/project.pbxproj index 57333c7a..5450300d 100644 --- a/Stats.xcodeproj/project.pbxproj +++ b/Stats.xcodeproj/project.pbxproj @@ -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 */,