feat: added widget for the RAM module

This commit is contained in:
Serhiy Mytrovtsiy
2024-07-05 19:47:18 +02:00
parent d85f148cc3
commit 376a64c42d
4 changed files with 126 additions and 2 deletions

View File

@@ -11,6 +11,7 @@
import Cocoa
import Kit
import WidgetKit
public struct RAM_Usage: Codable {
var total: Double
@@ -81,6 +82,8 @@ public class RAM: Module {
return color.additional as! NSColor
}
private var userDefaults: UserDefaults? = UserDefaults(suiteName: "eu.exelban.Stats.widgets")
public init() {
self.settingsView = Settings(.RAM)
self.popupView = Popup(.RAM)
@@ -132,6 +135,12 @@ public class RAM: Module {
self.portalView.callback(value)
self.notificationsView.loadCallback(value)
if #available(macOS 11.0, *) {
guard let blobData = try? JSONEncoder().encode(value) else { return }
self.userDefaults?.set(blobData, forKey: "RAM@UsageReader")
WidgetCenter.shared.reloadTimelines(ofKind: RAM_entry.kind)
}
let total: Double = value.total == 0 ? 1 : value.total
self.menuBar.widgets.filter{ $0.isActive }.forEach { (w: SWidget) in
switch w.item {

111
Modules/RAM/widget.swift Normal file
View File

@@ -0,0 +1,111 @@
//
// widget.swift
// RAM
//
// Created by Serhiy Mytrovtsiy on 03/07/2024
// Using Swift 5.0
// Running on macOS 14.5
//
// Copyright © 2024 Serhiy Mytrovtsiy. All rights reserved.
//
import SwiftUI
import WidgetKit
import Charts
import Kit
public struct RAM_entry: TimelineEntry {
public static let kind = "RAMWidget"
public static var snapshot: RAM_entry = RAM_entry()
public var date: Date {
Calendar.current.date(byAdding: .second, value: 5, to: Date())!
}
public var value: RAM_Usage? = nil
}
@available(macOS 11.0, *)
public struct Provider: TimelineProvider {
public typealias Entry = RAM_entry
private let userDefaults: UserDefaults? = UserDefaults(suiteName: "eu.exelban.Stats.widgets")
public func placeholder(in context: Context) -> RAM_entry {
RAM_entry()
}
public func getSnapshot(in context: Context, completion: @escaping (RAM_entry) -> Void) {
completion(RAM_entry.snapshot)
}
public func getTimeline(in context: Context, completion: @escaping (Timeline<RAM_entry>) -> Void) {
var entry = RAM_entry()
if let raw = userDefaults?.data(forKey: "RAM@UsageReader"), let load = try? JSONDecoder().decode(RAM_Usage.self, from: raw) {
entry.value = load
}
let entries: [RAM_entry] = [entry]
completion(Timeline(entries: entries, policy: .atEnd))
}
}
@available(macOS 14.0, *)
public struct RAMWidget: Widget {
var usedColor: Color = Color(nsColor: NSColor.systemBlue)
var freeColor: Color = Color(nsColor: NSColor.lightGray)
public init() {}
public var body: some WidgetConfiguration {
StaticConfiguration(kind: RAM_entry.kind, provider: Provider()) { entry in
VStack(spacing: 10) {
if let value = entry.value {
HStack {
Chart {
SectorMark(angle: .value(localizedString("Used"), value.used/value.total), innerRadius: .ratio(0.8)).foregroundStyle(self.usedColor)
SectorMark(angle: .value(localizedString("Free"), 1-(value.used/value.total)), innerRadius: .ratio(0.8)).foregroundStyle(self.freeColor)
}
.frame(maxWidth: .infinity, maxHeight: 84)
.chartLegend(.hidden)
.chartBackground { chartProxy in
GeometryReader { geometry in
if let anchor = chartProxy.plotFrame {
let frame = geometry[anchor]
Text("\(Int((value.used/value.total)*100))%")
.font(.system(size: 16, weight: .regular))
.position(x: frame.midX, y: frame.midY)
}
}
}
}
VStack(spacing: 3) {
HStack {
Rectangle().fill(self.usedColor).frame(width: 12, height: 12).cornerRadius(2)
Text(localizedString("Used")).font(.system(size: 12, weight: .regular)).foregroundColor(.secondary)
Spacer()
Text(Units(bytes: Int64(value.used)).getReadableMemory())
}
HStack {
Rectangle().fill(self.freeColor).frame(width: 12, height: 12).cornerRadius(2)
Text(localizedString("Free")).font(.system(size: 12, weight: .regular)).foregroundColor(.secondary)
Spacer()
Text(Units(bytes: Int64(value.free)).getReadableMemory())
}
HStack {
Text(localizedString("Pressure level")).font(.system(size: 12, weight: .regular)).foregroundColor(.secondary)
Spacer()
Text("\(value.rawPressureLevel)")
}
}
} else {
Text("No data")
}
}
.containerBackground(for: .widget) {
Color.clear
}
}
.configurationDisplayName("RAM widget")
.description("Displays RAM stats")
.supportedFamilies([.systemSmall])
}
}

View File

@@ -51,6 +51,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 */; };
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 */; };
5CE7E78E2C318512006BC92C /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE7E78D2C318512006BC92C /* SwiftUI.framework */; };
@@ -122,7 +123,6 @@
9A46BF8A266D7D00001A1117 /* smc in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9ADE6FD8265D032100D2FBA8 /* smc */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
9A46C05F266D85F8001A1117 /* smc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE7038265D059000D2FBA8 /* smc.swift */; };
9A46C06B266D8602001A1117 /* smc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE7038265D059000D2FBA8 /* smc.swift */; };
9A46C077266D8606001A1117 /* smc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADE7038265D059000D2FBA8 /* smc.swift */; };
9A53EBF924EAFA5200648841 /* settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A53EBF824EAFA5200648841 /* settings.swift */; };
9A53EBFB24EB041E00648841 /* popup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A53EBFA24EB041E00648841 /* popup.swift */; };
9A58DE9E24B363D800716A9F /* popup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A58DE9D24B363D800716A9F /* popup.swift */; };
@@ -496,6 +496,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>"; };
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; };
5CE7E78B2C318512006BC92C /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
@@ -1072,6 +1073,7 @@
5C23BC0729A03D1200DBA990 /* portal.swift */,
9A953A1324B9D22D0038EF4B /* settings.swift */,
5CF221162B1F4ACB006C583F /* notifications.swift */,
5CB387892C35A7110030459D /* widget.swift */,
9A81C7592449A41400825D92 /* Info.plist */,
9AF9EE192464A7B3005D2270 /* config.plist */,
);
@@ -2042,6 +2044,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5CB3878A2C35A7110030459D /* widget.swift in Sources */,
9A81C76A2449A43600825D92 /* readers.swift in Sources */,
9AA64260244B274200416A33 /* popup.swift in Sources */,
5C23BC0829A03D1200DBA990 /* portal.swift in Sources */,
@@ -2069,7 +2072,6 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9A46C077266D8606001A1117 /* smc.swift in Sources */,
9A97CEF1253733D200742D8F /* readers.swift in Sources */,
9A97CEF6253733E400742D8F /* popup.swift in Sources */,
5C3068732C32E83200B05EFA /* widget.swift in Sources */,

View File

@@ -12,10 +12,12 @@
import SwiftUI
import CPU
import RAM
@main
struct WidgetsBundle: WidgetBundle {
var body: some Widget {
CPUWidget()
RAMWidget()
}
}