From 8a3a4ccc27bc87df95f26a45a1345dd1d893300a Mon Sep 17 00:00:00 2001 From: Serhiy Mytrovtsiy Date: Sat, 14 Mar 2026 20:25:39 +0100 Subject: [PATCH] feat: renamed internal class Settings to Window (preparation for in-app statistics view) --- Kit/module/module.swift | 57 ++++++-- Kit/module/{settings.swift => window.swift} | 154 ++++++-------------- Kit/process.swift | 2 +- Kit/types.swift | 2 + Stats.xcodeproj/project.pbxproj | 8 +- Stats/Supporting Files/Info.plist | 2 +- Stats/Views/Settings.swift | 86 ++++++++++- Widgets/Supporting Files/Info.plist | 2 +- 8 files changed, 185 insertions(+), 128 deletions(-) rename Kit/module/{settings.swift => window.swift} (86%) diff --git a/Kit/module/module.swift b/Kit/module/module.swift index 684c4da4..85db463b 100644 --- a/Kit/module/module.swift +++ b/Kit/module/module.swift @@ -9,7 +9,7 @@ import Cocoa public struct module_c { - public var name: String = "" + public var name: String public var icon: NSImage? public var defaultState: Bool = false @@ -18,13 +18,19 @@ public struct module_c { internal var widgetsConfig: NSDictionary = NSDictionary() internal var settingsConfig: NSDictionary = NSDictionary() + internal var previewConfig: NSDictionary = NSDictionary() + + public var hasPreview: Bool { self.previewConfig["enabled"] as? Bool ?? false } init(in path: String) { let dict: NSDictionary = NSDictionary(contentsOfFile: path)! if let name = dict["Name"] as? String { self.name = name + } else { + fatalError("failed to initialize module, name is missing") } + if let state = dict["State"] as? Bool { self.defaultState = state } @@ -60,6 +66,10 @@ public struct module_c { if let settingsDict = dict["Settings"] as? NSDictionary { self.settingsConfig = settingsDict } + + if let previewDict = dict["Preview"] as? NSDictionary { + self.previewConfig = previewDict + } } } @@ -70,7 +80,7 @@ open class Module { public var enabled: Bool = false public var menuBar: MenuBar - public var settings: Settings_p? = nil + public var window: Window? = nil public let portal: Portal_p? public var name: String { config.name } @@ -88,6 +98,7 @@ open class Module { private var popup: PopupWindow? = nil private var popupView: Popup_p? = nil private var notificationsView: NotificationsWrapper? = nil + private var previewView: Preview_v? = nil private let log: NextLog private var readers: [Reader_p] = [] @@ -97,7 +108,14 @@ open class Module { set { Store.shared.set(key: "pause", value: newValue) } } - public init(moduleType: ModuleType, popup: Popup_p? = nil, settings: Settings_v? = nil, portal: Portal_p? = nil, notifications: NotificationsWrapper? = nil) { + public init( + moduleType: ModuleType, + popup: Popup_p? = nil, + settings: Settings_v? = nil, + portal: Portal_p? = nil, + notifications: NotificationsWrapper? = nil, + preview: Preview_v? = nil + ) { self.moduleType = moduleType self.portal = portal self.config = module_c(in: Bundle(for: type(of: self)).path(forResource: "config", ofType: "plist")!) @@ -106,6 +124,7 @@ open class Module { self.settingsView = settings self.popupView = popup self.notificationsView = notifications + self.previewView = preview self.menuBar = MenuBar(moduleName: self.config.name) self.available = self.isAvailable() self.enabled = Store.shared.bool(key: "\(self.config.name)_state", defaultValue: self.config.defaultState) @@ -128,6 +147,7 @@ open class Module { NotificationCenter.default.addObserver(self, selector: #selector(listenForModuleToggle), name: .toggleModule, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(listenForPopupToggle), name: .togglePopup, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(listenForToggleWidget), name: .toggleWidget, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(listenForWindowOpen), name: .openWindow, object: nil) // swiftlint:disable empty_count if self.config.widgetsConfig.count != 0 { @@ -137,15 +157,16 @@ open class Module { debug("Module started without widget", log: self.log) } - self.settings = Settings( + self.window = Window( config: &self.config, widgets: &self.menuBar.widgets, + modulePreview: self.previewView, moduleSettings: self.settingsView, popupSettings: self.popupView, notificationsSettings: self.notificationsView ) - self.popup = PopupWindow(title: self.config.name, module: self.moduleType, view: self.popupView, visibilityCallback: self.visibilityCallback) + self.popup = PopupWindow(title: self.config.name, module: self.moduleType, view: self.popupView, visibilityCallback: self.popupVisibilityCallback) } deinit { @@ -194,7 +215,7 @@ open class Module { reader.start() } self.menuBar.enable() - self.settings?.setState(self.enabled) + self.window?.setState(self.enabled) debug("Module enabled", log: self.log) } @@ -209,7 +230,7 @@ open class Module { } self.readers.forEach{ $0.stop() } self.menuBar.disable() - self.settings?.setState(self.enabled) + self.window?.setState(self.enabled) self.popup?.setIsVisible(false) debug("Module disabled", log: self.log) } @@ -237,8 +258,26 @@ open class Module { } // call when popup appear/disappear - private func visibilityCallback(_ state: Bool) { - self.readers.filter{ $0.popup }.forEach { (reader: Reader_p) in + private func popupVisibilityCallback(_ state: Bool) { + self.readers.filter{ $0.popup || $0.sleep }.forEach { (reader: Reader_p) in + if state { + reader.unlock() + reader.start() + } else { + reader.pause() + reader.lock() + } + } + } + + @objc private func listenForWindowOpen(_ notification: Notification) { + guard var state = notification.userInfo?["state"] as? Bool else { return } + + if state, let name = notification.userInfo?["module"] as? String, self.config.name != name { + state = false + } + + self.readers.filter{ $0.preview || $0.sleep }.forEach { (reader: Reader_p) in if state { reader.unlock() reader.start() diff --git a/Kit/module/settings.swift b/Kit/module/window.swift similarity index 86% rename from Kit/module/settings.swift rename to Kit/module/window.swift index 55a85817..5794222e 100644 --- a/Kit/module/settings.swift +++ b/Kit/module/window.swift @@ -11,21 +11,20 @@ import Cocoa -public protocol Settings_p: NSView { - func setState(_ newState: Bool) -} - public protocol Settings_v: NSView { func load(widgets: [widget_t]) } -open class Settings: NSStackView, Settings_p { +public protocol Preview_v: NSView {} + +open class Window: NSStackView { private var config: UnsafePointer private var widgets: [SWidget] private var segmentedControl: NSSegmentedControl? private var tabView: NSTabView? + private var modulePreview: Preview_v? private var moduleSettings: Settings_v? private var popupSettings: Popup_p? private var notificationsSettings: NotificationsWrapper? @@ -50,19 +49,30 @@ open class Settings: NSStackView, Settings_p { set { Store.shared.set(key: "\(self.config.pointee.name)_oneView", value: newValue) } } + private var isPreviewAvailable: Bool private var isPopupSettingsAvailable: Bool private var isNotificationsSettingsAvailable: Bool private var previewView: NSView? = nil private var settingsView: NSView? = nil - init(config: UnsafePointer, widgets: UnsafeMutablePointer<[SWidget]>, moduleSettings: Settings_v?, popupSettings: Popup_p?, notificationsSettings: NotificationsWrapper?) { + init( + config: UnsafePointer, + widgets: UnsafeMutablePointer<[SWidget]>, + modulePreview: Preview_v?, + moduleSettings: Settings_v?, + popupSettings: Popup_p?, + notificationsSettings: NotificationsWrapper? + ) { self.config = config self.widgets = widgets.pointee + self.modulePreview = modulePreview self.moduleSettings = moduleSettings self.popupSettings = popupSettings self.notificationsSettings = notificationsSettings + self.isPreviewAvailable = config.pointee.previewConfig["enabled"] as? Bool ?? false + self.isPopupSettingsAvailable = config.pointee.settingsConfig["popup"] as? Bool ?? false self.isNotificationsSettingsAvailable = config.pointee.settingsConfig["notifications"] as? Bool ?? false @@ -79,22 +89,27 @@ open class Settings: NSStackView, Settings_p { right: Constants.Settings.margin ) - let header = self.header() let settingsView = self.settings() self.settingsView = settingsView let previewView = self.preview() self.previewView = previewView - self.addArrangedSubview(header) self.addArrangedSubview(settingsView) self.addArrangedSubview(previewView) NotificationCenter.default.addObserver(self, selector: #selector(listenForOneView), name: .toggleOneView, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(listenForToggleView), name: .togglePreview, object: nil) + self.segmentedControl?.widthAnchor.constraint(equalTo: self.widthAnchor, constant: -(Constants.Settings.margin*2)).isActive = true + + if self.isPreviewAvailable { + self.toggleView() + } } deinit { NotificationCenter.default.removeObserver(self, name: .toggleOneView, object: nil) + NotificationCenter.default.removeObserver(self, name: .togglePreview, object: nil) } required public init?(coder: NSCoder) { @@ -105,28 +120,20 @@ open class Settings: NSStackView, Settings_p { toggleNSControlState(self.enableControl, state: newState ? .on : .off) } - private func header() -> NSView { - let view = NSStackView() - view.orientation = .horizontal - view.spacing = Constants.Settings.margin - - let widgetSelector = WidgetSelectorView(module: self.config.pointee.name, widgets: self.widgets, stateCallback: self.loadWidget) -// let button = ButtonSelectorView { [weak self] in -// self?.toggleView() -// } - - view.addArrangedSubview(widgetSelector) -// view.addArrangedSubview(button) - - return view - } - private func preview() -> NSView { - let view = NSStackView() - view.isHidden = true - view.orientation = .vertical - view.addArrangedSubview(EmptyView(height: 0, msg: localizedString("Preview is not available for that module"))) - return view + let container = NSStackView() + container.isHidden = true + container.orientation = .vertical + + var view: NSView = EmptyView(height: 0, msg: localizedString("Preview is not available for that module")) + + if self.isPreviewAvailable, let v = self.modulePreview { + view = v + } + + container.addArrangedSubview(view) + + return container } private func settings() -> NSView { @@ -208,6 +215,9 @@ open class Settings: NSStackView, Settings_p { tabView.addTabViewItem(notificationsTab) } + let widgetSelector = WidgetSelectorView(module: self.config.pointee.name, widgets: self.widgets, stateCallback: self.loadWidget) + + view.addArrangedSubview(widgetSelector) view.addArrangedSubview(segmentedControl) view.addArrangedSubview(tabView) @@ -296,9 +306,12 @@ open class Settings: NSStackView, Settings_p { } } - @objc private func toggleView() { + @objc private func listenForToggleView(_ notification: Notification) { + guard let moduleName = notification.userInfo?["module"], self.config.pointee.name == moduleName as? String else { return } + self.toggleView() + } + private func toggleView() { guard let preview = self.previewView, let settings = self.settingsView else { return } - preview.isHidden = !preview.isHidden settings.isHidden = !settings.isHidden } @@ -310,7 +323,7 @@ private class WidgetSelectorView: NSStackView { private var moved: Bool = false private var background: NSVisualEffectView = { - let view = NSVisualEffectView(frame: NSRect.zero) + let view = NSVisualEffectView(frame: .zero) view.blendingMode = .withinWindow view.translatesAutoresizingMaskIntoConstraints = false if #available(macOS 26.0, *) { @@ -666,80 +679,3 @@ private class WidgetSettings: NSStackView { return container } } - -private class ButtonSelectorView: NSStackView { - private var callback: () -> Void - - private var background: NSVisualEffectView = { - let view = NSVisualEffectView(frame: NSRect.zero) - view.blendingMode = .withinWindow - view.material = .contentBackground - view.state = .active - view.wantsLayer = true - view.layer?.cornerRadius = 5 - return view - }() - - private var settingsIcon: NSImage { iconFromSymbol(name: "gear", scale: .large) } - private var previewIcon: NSImage { iconFromSymbol(name: "command", scale: .large) } - - private var button: NSButton? = nil - private var isSettingsEnabled: Bool = false - - fileprivate init(callback: @escaping () -> Void) { - self.callback = callback - - super.init(frame: NSRect.zero) - - self.heightAnchor.constraint(equalToConstant: Constants.Widget.height + (Constants.Settings.margin*2)).isActive = true - 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.addSubview(self.background, positioned: .below, relativeTo: .none) - - let button = NSButton() - button.toolTip = localizedString("Open module settings") - button.bezelStyle = .regularSquare - button.translatesAutoresizingMaskIntoConstraints = false - button.imageScaling = .scaleNone - button.image = self.settingsIcon - button.contentTintColor = .secondaryLabelColor - button.isBordered = false - button.action = #selector(self.action) - button.target = self - button.focusRingType = .none - button.widthAnchor.constraint(equalToConstant: Constants.Widget.height).isActive = true - self.button = button - - self.addArrangedSubview(button) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func updateLayer() { - self.background.setFrameSize(self.frame.size) - } - - @objc private func action() { - guard let button = self.button else { return } - self.callback() - - self.isSettingsEnabled = !self.isSettingsEnabled - - if self.isSettingsEnabled { - button.image = self.previewIcon - button.toolTip = localizedString("Close module settings") - } else { - button.image = self.settingsIcon - button.toolTip = localizedString("Open module settings") - } - } -} diff --git a/Kit/process.swift b/Kit/process.swift index d8839ea8..853c34c9 100644 --- a/Kit/process.swift +++ b/Kit/process.swift @@ -26,7 +26,7 @@ public class ProcessesView: NSStackView { private var list: [ProcessView] = [] private var colorViews: [ColorView] = [] - public init(frame: NSRect, values: [ProcessHeader], n: Int = 0) { + public init(frame: NSRect = .zero, values: [ProcessHeader], n: Int = 0) { super.init(frame: frame) self.orientation = .vertical diff --git a/Kit/types.swift b/Kit/types.swift index 2028ea75..78ee6a71 100644 --- a/Kit/types.swift +++ b/Kit/types.swift @@ -266,6 +266,7 @@ public extension Notification.Name { static let toggleModule = Notification.Name("toggleModule") static let togglePopup = Notification.Name("togglePopup") static let toggleWidget = Notification.Name("toggleWidget") + static let togglePreview = Notification.Name("togglePreview") static let openModuleSettings = Notification.Name("openModuleSettings") static let clickInSettings = Notification.Name("clickInSettings") static let refreshPublicIP = Notification.Name("refreshPublicIP") @@ -281,6 +282,7 @@ public extension Notification.Name { static let combinedModulesPopup = Notification.Name("combinedModulesPopup") static let remoteLoginSuccess = Notification.Name("remoteLoginSuccess") static let remoteState = Notification.Name("remoteState") + static let openWindow = Notification.Name("openWindow") } public var isARM: Bool { diff --git a/Stats.xcodeproj/project.pbxproj b/Stats.xcodeproj/project.pbxproj index 0e7cdfa1..04aa308c 100644 --- a/Stats.xcodeproj/project.pbxproj +++ b/Stats.xcodeproj/project.pbxproj @@ -104,7 +104,7 @@ 9A2847672666AA2700EC1F6D /* BarChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A28475D2666AA2700EC1F6D /* BarChart.swift */; }; 9A2847682666AA2700EC1F6D /* Stack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A28475E2666AA2700EC1F6D /* Stack.swift */; }; 9A2847792666AA5000EC1F6D /* module.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2847742666AA5000EC1F6D /* module.swift */; }; - 9A28477A2666AA5000EC1F6D /* settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2847752666AA5000EC1F6D /* settings.swift */; }; + 9A28477A2666AA5000EC1F6D /* window.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2847752666AA5000EC1F6D /* window.swift */; }; 9A28477B2666AA5000EC1F6D /* popup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2847762666AA5000EC1F6D /* popup.swift */; }; 9A28477C2666AA5000EC1F6D /* reader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2847772666AA5000EC1F6D /* reader.swift */; }; 9A28477D2666AA5000EC1F6D /* widget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2847782666AA5000EC1F6D /* widget.swift */; }; @@ -603,7 +603,7 @@ 9A28475D2666AA2700EC1F6D /* BarChart.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarChart.swift; sourceTree = ""; }; 9A28475E2666AA2700EC1F6D /* Stack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stack.swift; sourceTree = ""; }; 9A2847742666AA5000EC1F6D /* module.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = module.swift; sourceTree = ""; }; - 9A2847752666AA5000EC1F6D /* settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = settings.swift; sourceTree = ""; }; + 9A2847752666AA5000EC1F6D /* window.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = window.swift; sourceTree = ""; }; 9A2847762666AA5000EC1F6D /* popup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = popup.swift; sourceTree = ""; }; 9A2847772666AA5000EC1F6D /* reader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = reader.swift; sourceTree = ""; }; 9A2847782666AA5000EC1F6D /* widget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = widget.swift; sourceTree = ""; }; @@ -1060,7 +1060,7 @@ 9A2847762666AA5000EC1F6D /* popup.swift */, 9A2847782666AA5000EC1F6D /* widget.swift */, 9A2847772666AA5000EC1F6D /* reader.swift */, - 9A2847752666AA5000EC1F6D /* settings.swift */, + 9A2847752666AA5000EC1F6D /* window.swift */, 5C23BC0329A014AC00DBA990 /* portal.swift */, 5CF2210C2B1E7EAF006C583F /* notifications.swift */, ); @@ -2095,7 +2095,7 @@ 9A28477D2666AA5000EC1F6D /* widget.swift in Sources */, 9A2848212666AB3600EC1F6D /* helpers.swift in Sources */, 5CAA50722C8E417700B13E13 /* Text.swift in Sources */, - 9A28477A2666AA5000EC1F6D /* settings.swift in Sources */, + 9A28477A2666AA5000EC1F6D /* window.swift in Sources */, 9A28475F2666AA2700EC1F6D /* LineChart.swift in Sources */, 9A302614286A2A3B00B41D57 /* Repeater.swift in Sources */, 5C4E8BA12B6EEE8E00F148B6 /* lldb.m in Sources */, diff --git a/Stats/Supporting Files/Info.plist b/Stats/Supporting Files/Info.plist index c8213a8d..d43d1e4e 100755 --- a/Stats/Supporting Files/Info.plist +++ b/Stats/Supporting Files/Info.plist @@ -17,7 +17,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 756 + 757 Description Simple macOS system monitor in your menu bar LSApplicationCategoryType diff --git a/Stats/Views/Settings.swift b/Stats/Views/Settings.swift index fe90e0ab..5d3993f1 100644 --- a/Stats/Views/Settings.swift +++ b/Stats/Views/Settings.swift @@ -14,6 +14,7 @@ import Kit public extension NSToolbarItem.Identifier { static let toggleButton = NSToolbarItem.Identifier("toggleButton") + static let previewButton = NSToolbarItem.Identifier("previewButton") } class SettingsWindow: NSWindow, NSWindowDelegate, NSToolbarDelegate { @@ -28,6 +29,7 @@ class SettingsWindow: NSWindow, NSWindowDelegate, NSToolbarDelegate { private var toggleButton: NSControl? = nil private var activeModuleName: String? = nil + private var settingsPreviewButton: NSView? = nil private var pauseState: Bool { Store.shared.bool(key: "pause", defaultValue: false) } @@ -119,6 +121,18 @@ class SettingsWindow: NSWindow, NSWindowDelegate, NSToolbarDelegate { func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? { switch itemIdentifier { + case .previewButton: + let button = SettingsPreviewButton { [weak self] in + guard let moduleName = self?.activeModuleName else { return } + NotificationCenter.default.post(name: .togglePreview, object: nil, userInfo: ["module": moduleName]) + } + self.settingsPreviewButton = button + + let toolbarItem = NSToolbarItem(itemIdentifier: itemIdentifier) + toolbarItem.view = button + toolbarItem.isBordered = false + + return toolbarItem case .toggleButton: let switchButton = NSSwitch() switchButton.state = .on @@ -139,10 +153,10 @@ class SettingsWindow: NSWindow, NSWindowDelegate, NSToolbarDelegate { } func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { - return [.flexibleSpace, .toggleButton] + return [.flexibleSpace, .previewButton, .toggleButton] } func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { - return [.flexibleSpace, .toggleButton] + return [.flexibleSpace, .previewButton, .toggleButton] } @objc private func toggleSettingsHandler(_ notification: Notification) { @@ -164,19 +178,25 @@ class SettingsWindow: NSWindow, NSWindowDelegate, NSToolbarDelegate { if let title = notification.userInfo?["module"] as? String { var view: NSView = NSView() if let detectedModule = modules.first(where: { $0.config.name == title }) { - if let v = detectedModule.settings { + if let v = detectedModule.window { view = v } self.activeModuleName = detectedModule.config.name toggleNSControlState(self.toggleButton, state: detectedModule.enabled ? .on : .off) self.toggleButton?.isHidden = false + self.settingsPreviewButton?.isHidden = !detectedModule.config.hasPreview + NotificationCenter.default.post(name: .openWindow, object: nil, userInfo: ["module": detectedModule.config.name, "state": true]) } else if title == "Dashboard" { view = self.dashboard self.toggleButton?.isHidden = true + self.settingsPreviewButton?.isHidden = true + NotificationCenter.default.post(name: .openWindow, object: nil, userInfo: ["state": false]) } else if title == "Settings" { self.settings.viewWillAppear() view = self.settings self.toggleButton?.isHidden = true + self.settingsPreviewButton?.isHidden = true + NotificationCenter.default.post(name: .openWindow, object: nil, userInfo: ["state": false]) } self.title = localizedString(title) @@ -523,3 +543,63 @@ private class MenuItem: NSView { self.active = false } } + +private class SettingsPreviewButton: NSStackView { + private var callback: () -> Void + + private var settingsIcon: NSImage { iconFromSymbol(name: "gear", scale: .large) } + private var previewIcon: NSImage { iconFromSymbol(name: "command", scale: .large) } + + private var button: NSButton? = nil + private var isSettingsEnabled: Bool = false + + fileprivate init(callback: @escaping () -> Void) { + self.callback = callback + + super.init(frame: .zero) + + 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 + + let button = NSButton() + button.toolTip = localizedString("Open module settings") + button.bezelStyle = .regularSquare + button.translatesAutoresizingMaskIntoConstraints = false + button.imageScaling = .scaleNone + button.image = self.settingsIcon + button.contentTintColor = .secondaryLabelColor + button.isBordered = false + button.action = #selector(self.action) + button.target = self + button.focusRingType = .none + button.widthAnchor.constraint(equalToConstant: Constants.Widget.height).isActive = true + self.button = button + + self.addArrangedSubview(button) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func action() { + guard let button = self.button else { return } + self.callback() + + self.isSettingsEnabled = !self.isSettingsEnabled + + if self.isSettingsEnabled { + button.image = self.previewIcon + button.toolTip = localizedString("Close module settings") + } else { + button.image = self.settingsIcon + button.toolTip = localizedString("Open module settings") + } + } +} diff --git a/Widgets/Supporting Files/Info.plist b/Widgets/Supporting Files/Info.plist index 8cb7b9cc..2b4b8e82 100644 --- a/Widgets/Supporting Files/Info.plist +++ b/Widgets/Supporting Files/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 2.12.3 CFBundleVersion - 756 + 757 NSExtension NSExtensionPointIdentifier