mirror of
https://github.com/morgan9e/macos-stats
synced 2026-04-15 00:34:08 +09:00
feat: added a new Text widget that allows to visualize custom values in the menu bar
This commit is contained in:
88
Kit/Widgets/Text.swift
Normal file
88
Kit/Widgets/Text.swift
Normal file
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// Text.swift
|
||||
// Kit
|
||||
//
|
||||
// Created by Serhiy Mytrovtsiy on 08/09/2024
|
||||
// Using Swift 5.0
|
||||
// Running on macOS 14.6
|
||||
//
|
||||
// Copyright © 2024 Serhiy Mytrovtsiy. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
public class TextWidget: WidgetWrapper {
|
||||
private var value: String = ""
|
||||
|
||||
public init(title: String, config: NSDictionary?, preview: Bool = false) {
|
||||
super.init(.text, title: title, frame: CGRect(
|
||||
x: 0,
|
||||
y: Constants.Widget.margin.y,
|
||||
width: 30 + (2*Constants.Widget.margin.x),
|
||||
height: Constants.Widget.height - (2*Constants.Widget.margin.y)
|
||||
))
|
||||
|
||||
if preview {
|
||||
self.value = "Text"
|
||||
}
|
||||
|
||||
self.canDrawConcurrently = true
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public override func draw(_ dirtyRect: NSRect) {
|
||||
super.draw(dirtyRect)
|
||||
|
||||
var value: String = ""
|
||||
self.queue.sync {
|
||||
value = self.value
|
||||
}
|
||||
|
||||
if value.isEmpty {
|
||||
self.setWidth(0)
|
||||
return
|
||||
}
|
||||
|
||||
let valueSize: CGFloat = 12
|
||||
let style = NSMutableParagraphStyle()
|
||||
style.alignment = .center
|
||||
let stringAttributes = [
|
||||
NSAttributedString.Key.font: NSFont.systemFont(ofSize: valueSize, weight: .regular),
|
||||
NSAttributedString.Key.foregroundColor: NSColor.textColor,
|
||||
NSAttributedString.Key.paragraphStyle: style
|
||||
]
|
||||
let attributedString = NSAttributedString(string: value, attributes: stringAttributes)
|
||||
let size = attributedString.boundingRect(
|
||||
with: CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude),
|
||||
options: [.usesLineFragmentOrigin, .usesFontLeading]
|
||||
)
|
||||
let width = (size.width+Constants.Widget.margin.x*2).roundedUpToNearestTen()
|
||||
let origin: CGPoint = CGPoint(x: Constants.Widget.margin.x, y: ((Constants.Widget.height-valueSize-1)/2))
|
||||
let rect = CGRect(x: origin.x, y: origin.y, width: width - (Constants.Widget.margin.x*2), height: valueSize)
|
||||
attributedString.draw(with: rect)
|
||||
|
||||
self.setWidth(width)
|
||||
}
|
||||
|
||||
public func setValue(_ newValue: String) {
|
||||
guard self.value != newValue else { return }
|
||||
self.value = newValue
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.display()
|
||||
})
|
||||
}
|
||||
|
||||
static public func parseText(_ raw: String) -> [KeyValue_t] {
|
||||
var pairs: [KeyValue_t] = []
|
||||
raw.split(separator: " ", omittingEmptySubsequences: true).filter({ $0.hasPrefix("$") }).forEach { v in
|
||||
let arr = v.split(separator: ".", omittingEmptySubsequences: true)
|
||||
guard let key = arr.first else { return }
|
||||
let value = arr.count == 1 ? nil : arr.last
|
||||
pairs.append(KeyValue_t(key: String(key), value: String(value ?? "")))
|
||||
}
|
||||
return pairs
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ public enum widget_t: String {
|
||||
case label = "label"
|
||||
case tachometer = "tachometer"
|
||||
case state = "state"
|
||||
case text = "text"
|
||||
|
||||
public func new(module: String, config: NSDictionary, defaultWidget: widget_t) -> SWidget? {
|
||||
guard let widgetConfig: NSDictionary = config[self.rawValue] as? NSDictionary else { return nil }
|
||||
@@ -74,6 +75,9 @@ public enum widget_t: String {
|
||||
case .state:
|
||||
preview = StateWidget(title: module, config: widgetConfig, preview: true)
|
||||
item = StateWidget(title: module, config: widgetConfig, preview: false)
|
||||
case .text:
|
||||
preview = TextWidget(title: module, config: widgetConfig, preview: true)
|
||||
item = TextWidget(title: module, config: widgetConfig, preview: false)
|
||||
default: break
|
||||
}
|
||||
|
||||
@@ -137,6 +141,7 @@ public enum widget_t: String {
|
||||
case .label: return localizedString("Label widget")
|
||||
case .tachometer: return localizedString("Tachometer widget")
|
||||
case .state: return localizedString("State widget")
|
||||
case .text: return localizedString("Text widget")
|
||||
default: return ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
5C7C1DF42C29A3A00060387D /* notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7C1DF32C29A3A00060387D /* notifications.swift */; };
|
||||
5C8E001029269C7F0027C75A /* protocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE493829265055000F2856 /* protocol.swift */; };
|
||||
5CA518382B543FE600EBCCC4 /* portal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA518372B543FE600EBCCC4 /* portal.swift */; };
|
||||
5CAA50722C8E417700B13E13 /* Text.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CAA50712C8E417700B13E13 /* Text.swift */; };
|
||||
5CB3878A2C35A7110030459D /* widget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB387892C35A7110030459D /* widget.swift */; };
|
||||
5CD342F42B2F2FB700225631 /* notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CD342F32B2F2FB700225631 /* notifications.swift */; };
|
||||
5CE7E78C2C318512006BC92C /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE7E78B2C318512006BC92C /* WidgetKit.framework */; };
|
||||
@@ -534,6 +535,7 @@
|
||||
5C7C1DF32C29A3A00060387D /* notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = notifications.swift; sourceTree = "<group>"; };
|
||||
5C9F90A02A76B30500D41748 /* et */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = et; path = et.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
5CA518372B543FE600EBCCC4 /* portal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = portal.swift; sourceTree = "<group>"; };
|
||||
5CAA50712C8E417700B13E13 /* Text.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Text.swift; sourceTree = "<group>"; };
|
||||
5CB387892C35A7110030459D /* widget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = widget.swift; sourceTree = "<group>"; };
|
||||
5CD342F32B2F2FB700225631 /* notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = notifications.swift; sourceTree = "<group>"; };
|
||||
5CE7E78A2C318512006BC92C /* WidgetsExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WidgetsExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@@ -1018,6 +1020,7 @@
|
||||
9A28475C2666AA2700EC1F6D /* Speed.swift */,
|
||||
9A9B8C9C27149A3700218374 /* Tachometer.swift */,
|
||||
9AEBBE4C28D773430082A6A1 /* State.swift */,
|
||||
5CAA50712C8E417700B13E13 /* Text.swift */,
|
||||
);
|
||||
path = Widgets;
|
||||
sourceTree = "<group>";
|
||||
@@ -2064,6 +2067,7 @@
|
||||
9A28480A2666AB3000EC1F6D /* SystemKit.swift in Sources */,
|
||||
9A28477D2666AA5000EC1F6D /* widget.swift in Sources */,
|
||||
9A2848212666AB3600EC1F6D /* helpers.swift in Sources */,
|
||||
5CAA50722C8E417700B13E13 /* Text.swift in Sources */,
|
||||
9A28477A2666AA5000EC1F6D /* settings.swift in Sources */,
|
||||
9A28475F2666AA2700EC1F6D /* LineChart.swift in Sources */,
|
||||
9A302614286A2A3B00B41D57 /* Repeater.swift in Sources */,
|
||||
|
||||
Reference in New Issue
Block a user