Skip to content

Commit

Permalink
feat: added Text widget to the Network module with a description of t…
Browse files Browse the repository at this point in the history
…he value that could be used in it (#1868)
  • Loading branch information
exelban committed Sep 13, 2024
1 parent 6fc3b58 commit f053555
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 7 deletions.
6 changes: 6 additions & 0 deletions Kit/extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -558,3 +558,9 @@ public extension TimeZone {
}
}
}

extension CGFloat {
func roundedUpToNearestTen() -> CGFloat {
return ceil(self / 10) * 10
}
}
25 changes: 25 additions & 0 deletions Kit/helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import Cocoa
import ServiceManagement
import UserNotifications
import WebKit

public struct LaunchAtLogin {
private static let id = "\(Bundle.main.bundleIdentifier!).LaunchAtLogin"
Expand Down Expand Up @@ -1571,3 +1572,27 @@ public class PreferencesSwitch: NSStackView {
self.with.isEnabled = controlState(sender)
}
}

public class HelpHUD: NSPanel {
public init(_ text: String, origin: CGPoint = CGPoint(x: 0, y: 0), size: CGSize = CGSize(width: 420, height: 300)) {
super.init(
contentRect: NSRect(origin: origin, size: size),
styleMask: [.hudWindow, .titled, .closable],
backing: .buffered, defer: false
)
self.isFloatingPanel = true
self.isMovableByWindowBackground = true
self.level = .floating
self.title = "Help"

let webView = WKWebView()
webView.setValue(false, forKey: "drawsBackground")
webView.loadHTMLString("<html><body style='color: #ffffff;margin: 10px;'>\(text)</body></html>", baseURL: nil)
self.contentView = webView
}

public func show() {
self.makeKeyAndOrderFront(self)
self.center()
}
}
12 changes: 12 additions & 0 deletions Modules/Net/config.plist
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
68 changes: 66 additions & 2 deletions Modules/Net/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
}
}
Expand Down
85 changes: 80 additions & 5 deletions Modules/Net/settings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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) = {}
Expand All @@ -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] = []

Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
}
}
}

Expand Down

0 comments on commit f053555

Please sign in to comment.