From 446f17e195f82b5b6208b316943bbbacfb1cc1bf Mon Sep 17 00:00:00 2001 From: Serhiy Mytrovtsiy Date: Fri, 16 May 2025 21:42:18 +0200 Subject: [PATCH] feat: added Text widget to the Disk module that allows to show more details in the menu bar (#2532) --- Modules/Disk/config.plist | 7 +++++ Modules/Disk/main.swift | 35 ++++++++++++++++++++++ Modules/Disk/settings.swift | 60 ++++++++++++++++++++++++++++++++++++- 3 files changed, 101 insertions(+), 1 deletion(-) diff --git a/Modules/Disk/config.plist b/Modules/Disk/config.plist index dfb16507..9772411a 100644 --- a/Modules/Disk/config.plist +++ b/Modules/Disk/config.plist @@ -125,6 +125,13 @@ system + text + + Default + + Order + 7 + Settings diff --git a/Modules/Disk/main.swift b/Modules/Disk/main.swift index ca9cabc3..a4ac05db 100644 --- a/Modules/Disk/main.swift +++ b/Modules/Disk/main.swift @@ -217,6 +217,10 @@ public class Disk: Module { private var selectedDisk: String = "" + private var textValue: String { + Store.shared.string(key: "\(self.name)_textWidgetValue", defaultValue: "$capacity.free/$capacity.total") + } + public init() { super.init( moduleType: .disk, @@ -290,6 +294,37 @@ public class Disk: Module { widget.setValue([ circle_segment(value: d.percentage, color: NSColor.systemBlue) ]) + case let widget as TextWidget: + var text = "\(self.textValue)" + let pairs = TextWidget.parseText(text) + pairs.forEach { pair in + var replacement: String? = nil + + switch pair.key { + case "$capacity": + switch pair.value { + case "total": replacement = DiskSize(d.size).getReadableMemory() + case "used": replacement = DiskSize(d.size - d.free).getReadableMemory() + case "free": replacement = DiskSize(d.free).getReadableMemory() + default: return + } + case "$percentage": + var percentage: Int + switch pair.value { + case "used": percentage = Int((Double(d.size - d.free) / Double(d.size)) * 100) + case "free": percentage = Int((Double(d.free) / Double(d.size)) * 100) + default: return + } + replacement = "\(percentage < 0 ? 0 : percentage)%" + default: return + } + + if let replacement { + let key = pair.value.isEmpty ? pair.key : "\(pair.key).\(pair.value)" + text = text.replacingOccurrences(of: key, with: replacement) + } + } + widget.setValue(text) default: break } } diff --git a/Modules/Disk/settings.swift b/Modules/Disk/settings.swift index 6bbff602..14df548b 100644 --- a/Modules/Disk/settings.swift +++ b/Modules/Disk/settings.swift @@ -12,7 +12,26 @@ import Cocoa import Kit -internal class Settings: NSStackView, Settings_v { +var textWidgetHelp = """ +

Description

+You can use a combination of any of the variables. +

Examples:

+ +

Available variables

+ +""" + +internal class Settings: NSStackView, Settings_v, NSTextFieldDelegate { private let title: String private var removableState: Bool = false @@ -20,6 +39,7 @@ internal class Settings: NSStackView, Settings_v { private var numberOfProcesses: Int = 5 private var baseValue: String = "byte" private var SMARTState: Bool = true + private var textValue: String = "$capacity.free/$capacity.total" public var selectedDiskHandler: (String) -> Void = {_ in } public var callback: (() -> Void) = {} @@ -31,6 +51,8 @@ internal class Settings: NSStackView, Settings_v { private var list: [String] = [] + private let textWidgetHelpPanel: HelpHUD = HelpHUD(textWidgetHelp) + public init(_ module: ModuleType) { self.title = module.stringValue @@ -40,6 +62,7 @@ internal class Settings: NSStackView, Settings_v { self.numberOfProcesses = Store.shared.int(key: "\(self.title)_processes", defaultValue: self.numberOfProcesses) self.baseValue = Store.shared.string(key: "\(self.title)_base", defaultValue: self.baseValue) self.SMARTState = Store.shared.bool(key: "\(self.title)_SMART", defaultValue: self.SMARTState) + self.textValue = Store.shared.string(key: "\(self.title)_textWidgetValue", defaultValue: self.textValue) super.init(frame: NSRect.zero) @@ -99,6 +122,15 @@ internal class Settings: NSStackView, Settings_v { state: self.SMARTState )) ])) + + if widgets.contains(where: { $0 == .text }) { + let textField = self.inputField(id: "text", value: self.textValue, placeholder: localizedString("This will be visible in the text widget")) + self.addArrangedSubview(PreferencesSection([ + PreferencesRow(localizedString("Text widget value"), component: textField) { [weak self] in + self?.textWidgetHelpPanel.show() + } + ])) + } } internal func setList(_ list: Disks) { @@ -118,6 +150,23 @@ internal class Settings: NSStackView, Settings_v { }) } + private func inputField(id: String, value: String, placeholder: String) -> NSView { + let field: NSTextField = NSTextField() + field.identifier = NSUserInterfaceItemIdentifier(id) + field.widthAnchor.constraint(equalToConstant: 250).isActive = true + field.font = NSFont.systemFont(ofSize: 12, weight: .regular) + field.textColor = .textColor + field.isEditable = true + field.isSelectable = true + field.usesSingleLineMode = true + field.maximumNumberOfLines = 1 + field.focusRingType = .none + field.stringValue = value + field.delegate = self + field.placeholderString = placeholder + return field + } + @objc private func changeNumberOfProcesses(_ sender: NSMenuItem) { if let value = Int(sender.title) { self.numberOfProcesses = value @@ -156,4 +205,13 @@ internal class Settings: NSStackView, Settings_v { Store.shared.set(key: "\(self.title)_SMART", value: self.SMARTState) self.callback() } + + func controlTextDidChange(_ notification: Notification) { + if let field = notification.object as? NSTextField { + if field.identifier == NSUserInterfaceItemIdentifier("text") { + self.textValue = field.stringValue + Store.shared.set(key: "\(self.title)_textWidgetValue", value: self.textValue) + } + } + } }