mirror of
https://github.com/morgan9e/macos-stats
synced 2026-04-14 00:04:15 +09:00
feat: added Text widget to the Network module with a description of the value that could be used in it (#1868)
This commit is contained in:
@@ -54,6 +54,18 @@
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>text</key>
|
||||
<dict>
|
||||
<key>Default</key>
|
||||
<false/>
|
||||
<key>Order</key>
|
||||
<integer>4</integer>
|
||||
<key>Preview</key>
|
||||
<dict>
|
||||
<key>Value</key>
|
||||
<string>192.168.0.1</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>Settings</key>
|
||||
<dict>
|
||||
|
||||
@@ -146,6 +146,9 @@ public class Network: Module {
|
||||
private var publicIPRefreshInterval: String {
|
||||
Store.shared.string(key: "\(self.name)_publicIPRefreshInterval", defaultValue: "never")
|
||||
}
|
||||
private var textValue: String {
|
||||
Store.shared.string(key: "\(self.name)_textWidgetValue", defaultValue: "$addr.public - $status")
|
||||
}
|
||||
|
||||
public init() {
|
||||
self.settingsView = Settings(.network)
|
||||
@@ -233,13 +236,74 @@ public class Network: Module {
|
||||
switch w.item {
|
||||
case let widget as SpeedWidget: widget.setValue(upload: upload, download: download)
|
||||
case let widget as NetworkChart: widget.setValue(upload: Double(upload), download: Double(download))
|
||||
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 "$addr":
|
||||
switch pair.value {
|
||||
case "public": replacement = value.raddr.v4 ?? value.raddr.v6 ?? "-"
|
||||
case "publicV4": replacement = value.raddr.v4 ?? "-"
|
||||
case "publicV6": replacement = value.raddr.v6 ?? "-"
|
||||
case "private": replacement = value.laddr ?? "-"
|
||||
default: return
|
||||
}
|
||||
case "$interface":
|
||||
switch pair.value {
|
||||
case "displayName": replacement = value.interface?.displayName ?? "-"
|
||||
case "BSDName": replacement = value.interface?.BSDName ?? "-"
|
||||
case "address": replacement = value.interface?.address ?? "-"
|
||||
default: return
|
||||
}
|
||||
case "$wifi":
|
||||
switch pair.value {
|
||||
case "ssid": replacement = value.wifiDetails.ssid ?? "-"
|
||||
case "bssid": replacement = value.wifiDetails.bssid ?? "-"
|
||||
case "RSSI": replacement = "\(value.wifiDetails.RSSI ?? 0)"
|
||||
case "noise": replacement = "\(value.wifiDetails.noise ?? 0)"
|
||||
case "transmitRate": replacement = "\(value.wifiDetails.transmitRate ?? 0)"
|
||||
case "standard": replacement = value.wifiDetails.standard ?? "-"
|
||||
case "mode": replacement = value.wifiDetails.mode ?? "-"
|
||||
case "security": replacement = value.wifiDetails.security ?? "-"
|
||||
case "channel": replacement = value.wifiDetails.channel ?? "-"
|
||||
case "channelBand": replacement = value.wifiDetails.channelBand ?? "-"
|
||||
case "channelWidth": replacement = value.wifiDetails.channelWidth ?? "-"
|
||||
case "channelNumber": replacement = value.wifiDetails.channelNumber ?? "-"
|
||||
default: return
|
||||
}
|
||||
case "$status":
|
||||
replacement = localizedString(value.status ? "UP" : "DOWN")
|
||||
case "$upload":
|
||||
switch pair.value {
|
||||
case "total": replacement = Units(bytes: value.total.upload).getReadableMemory()
|
||||
default: replacement = Units(bytes: value.bandwidth.upload).getReadableMemory()
|
||||
}
|
||||
case "$download":
|
||||
switch pair.value {
|
||||
case "total": replacement = Units(bytes: value.total.download).getReadableMemory()
|
||||
default: replacement = Units(bytes: value.bandwidth.download).getReadableMemory()
|
||||
}
|
||||
case "$type":
|
||||
replacement = value.connectionType?.rawValue ?? "-"
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
if #available(macOS 11.0, *) {
|
||||
guard let blobData = try? JSONEncoder().encode(raw) else { return }
|
||||
self.userDefaults?.set(blobData, forKey: "Network@UsageReader")
|
||||
// guard let blobData = try? JSONEncoder().encode(raw) else { return }
|
||||
// self.userDefaults?.set(blobData, forKey: "Network@UsageReader")
|
||||
WidgetCenter.shared.reloadTimelines(ofKind: Network_entry.kind)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,44 @@ import Cocoa
|
||||
import Kit
|
||||
import SystemConfiguration
|
||||
|
||||
var textWidgetHelp = """
|
||||
<h2>Description</h2>
|
||||
You can use a combination of any of the variables. There is only one limitation: there must be a space between each variable.
|
||||
<h3>Examples:</h3>
|
||||
<ul>
|
||||
<li>$addr.public - $status</li>
|
||||
<li>$addr.public - $wifi.ssid - $status</li>
|
||||
</ul>
|
||||
<h2>Available variables</h2>
|
||||
<ul>
|
||||
<li><b>$addr.public</b>: <small>Public IP address.</small></li>
|
||||
<li><b>$addr.publicV4</b>: <small>Public IPv4 address.</small></li>
|
||||
<li><b>$addr.publicV6</b>: <small>Public IPv6 address.</small></li>
|
||||
<li><b>$addr.private</b>: <small>Private/local IP address.</small></li>
|
||||
<li><b>$interface.displayName</b>: <small>Network interface name.</small></li>
|
||||
<li><b>$interface.BSDName</b>: <small>BSD name of the network interface.</small></li>
|
||||
<li><b>$interface.address</b>: <small>MAC address of the network interface.</small></li>
|
||||
<li><b>$wifi.ssid</b>: <small>Wi-Fi network name.</small></li>
|
||||
<li><b>$wifi.bssid</b>: <small>MAC address of the Wi-Fi access point (BSSID).</small></li>
|
||||
<li><b>$wifi.RSSI</b>: <small>Signal strength of the Wi-Fi network (RSSI).</small></li>
|
||||
<li><b>$wifi.noise</b>: <small>Noise level of the Wi-Fi network.</small></li>
|
||||
<li><b>$wifi.transmitRate</b>: <small>Transmit rate (connection speed) of the Wi-Fi network.</small></li>
|
||||
<li><b>$wifi.standard</b>: <small>Wi-Fi standard (e.g., 802.11a/b/g/n/ac).</small></li>
|
||||
<li><b>$wifi.mode</b>: <small>Operating mode of the Wi-Fi (e.g., infrastructure, adhoc).</small></li>
|
||||
<li><b>$wifi.security</b>: <small>Type of security used by the Wi-Fi network.</small></li>
|
||||
<li><b>$wifi.channel</b>: <small>Wi-Fi channel being used.</small></li>
|
||||
<li><b>$wifi.channelBand</b>: <small>Frequency band of the Wi-Fi channel (e.g., 2.4 GHz, 5 GHz).</small></li>
|
||||
<li><b>$wifi.channelWidth</b>: <small>Channel width used in MHz.</small></li>
|
||||
<li><b>$wifi.channelNumber</b>: <small>Channel number used by the Wi-Fi network.</small></li>
|
||||
<li><b>$status</b>: <small>Status of the network connection. "UP" if active, "DOWN" if inactive.</small></li>
|
||||
<li><b>$upload.total</b>: <small>Total amount of data uploaded over the connection.</small></li>
|
||||
<li><b>$upload</b>: <small>Current upload bandwidth used.</small></li>
|
||||
<li><b>$download.total</b>: <small>Total amount of data downloaded over the connection.</small></li>
|
||||
<li><b>$download</b>: <small>Current download bandwidth used.</small></li>
|
||||
<li><b>$type</b>: <small>Type of network connection (e.g., Ethernet, Wi-Fi, Cellular).</small></li>
|
||||
</ul>
|
||||
"""
|
||||
|
||||
internal class Settings: NSStackView, Settings_v, NSTextFieldDelegate {
|
||||
private var numberOfProcesses: Int = 8
|
||||
private var readerType: String = "interface"
|
||||
@@ -24,6 +62,7 @@ internal class Settings: NSStackView, Settings_v, NSTextFieldDelegate {
|
||||
private var ICMPHost: String = "1.1.1.1"
|
||||
private var publicIPRefreshInterval: String = "never"
|
||||
private var baseValue: String = "byte"
|
||||
private var textValue: String = "$addr.public - $status"
|
||||
|
||||
public var callback: (() -> Void) = {}
|
||||
public var callbackWhenUpdateNumberOfProcesses: (() -> Void) = {}
|
||||
@@ -35,6 +74,7 @@ internal class Settings: NSStackView, Settings_v, NSTextFieldDelegate {
|
||||
private var sliderView: NSView? = nil
|
||||
private var section: PreferencesSection? = nil
|
||||
private var widgetThresholdSection: PreferencesSection? = nil
|
||||
private let textWidgetHelpPanel: HelpHUD = HelpHUD(textWidgetHelp)
|
||||
|
||||
private var list: [Network_interface] = []
|
||||
|
||||
@@ -57,6 +97,7 @@ internal class Settings: NSStackView, Settings_v, NSTextFieldDelegate {
|
||||
self.ICMPHost = Store.shared.string(key: "\(self.title)_ICMPHost", defaultValue: self.ICMPHost)
|
||||
self.publicIPRefreshInterval = Store.shared.string(key: "\(self.title)_publicIPRefreshInterval", defaultValue: self.publicIPRefreshInterval)
|
||||
self.baseValue = Store.shared.string(key: "\(self.title)_base", defaultValue: self.baseValue)
|
||||
self.textValue = Store.shared.string(key: "\(self.title)_textWidgetValue", defaultValue: self.textValue)
|
||||
|
||||
super.init(frame: NSRect.zero)
|
||||
self.orientation = .vertical
|
||||
@@ -170,9 +211,38 @@ internal class Settings: NSStackView, Settings_v, NSTextFieldDelegate {
|
||||
valueField.delegate = self
|
||||
valueField.placeholderString = localizedString("Leave empty to disable the check")
|
||||
|
||||
let ICMPField = self.inputField(id: "ICMP", value: self.ICMPHost, placeholder: localizedString("Leave empty to disable the check"))
|
||||
self.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Connectivity host (ICMP)"), component: valueField)
|
||||
PreferencesRow(localizedString("Connectivity host (ICMP)"), component: ICMPField) {
|
||||
NSWorkspace.shared.open(URL(string: "https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol")!)
|
||||
}
|
||||
]))
|
||||
|
||||
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()
|
||||
}
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
||||
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 handleSelection(_ sender: NSPopUpButton) {
|
||||
@@ -229,10 +299,15 @@ internal class Settings: NSStackView, Settings_v, NSTextFieldDelegate {
|
||||
}
|
||||
|
||||
func controlTextDidChange(_ notification: Notification) {
|
||||
if let textField = notification.object as? NSTextField {
|
||||
self.ICMPHost = textField.stringValue
|
||||
Store.shared.set(key: "\(self.title)_ICMPHost", value: self.ICMPHost)
|
||||
self.ICMPHostCallback(self.ICMPHost.isEmpty)
|
||||
if let field = notification.object as? NSTextField {
|
||||
if field.identifier == NSUserInterfaceItemIdentifier("ICMP") {
|
||||
self.ICMPHost = field.stringValue
|
||||
Store.shared.set(key: "\(self.title)_ICMPHost", value: self.ICMPHost)
|
||||
self.ICMPHostCallback(self.ICMPHost.isEmpty)
|
||||
} else if field.identifier == NSUserInterfaceItemIdentifier("text") {
|
||||
self.textValue = field.stringValue
|
||||
Store.shared.set(key: "\(self.title)_textWidgetValue", value: self.textValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user