mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 23:24:32 +00:00
working - trying to fix cpu graphs
This commit is contained in:
parent
5603ce4c1e
commit
fc522ccf84
4 changed files with 227 additions and 104 deletions
48
DynamicIsland/components/Stats/CircularGaugeView.swift
Normal file
48
DynamicIsland/components/Stats/CircularGaugeView.swift
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import SwiftUI
|
||||
|
||||
struct CircularGaugeView: View {
|
||||
let title: String
|
||||
let value: Double
|
||||
let tint: Color
|
||||
let centerPrimaryText: String
|
||||
var centerSecondaryText: String? = nil
|
||||
var subtitle: String? = nil
|
||||
var size: CGFloat = 72
|
||||
var lineWidth: CGFloat = 8
|
||||
var backgroundTint: Color = Color.primary.opacity(0.08)
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 8) {
|
||||
ZStack {
|
||||
Circle()
|
||||
.stroke(backgroundTint, lineWidth: lineWidth)
|
||||
.frame(width: size, height: size)
|
||||
if value > 0 {
|
||||
RingArc(start: 0, end: min(max(value, 0), 1), color: tint, lineWidth: lineWidth)
|
||||
.frame(width: size, height: size)
|
||||
}
|
||||
VStack(spacing: 2) {
|
||||
Text(centerPrimaryText)
|
||||
.font(.system(size: 16, weight: .semibold, design: .rounded))
|
||||
.foregroundColor(tint)
|
||||
if let centerSecondaryText, !centerSecondaryText.isEmpty {
|
||||
Text(centerSecondaryText)
|
||||
.font(.system(size: 11, weight: .medium))
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
VStack(spacing: 2) {
|
||||
Text(title)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
if let subtitle, !subtitle.isEmpty {
|
||||
Text(subtitle)
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -31,15 +31,15 @@ struct GPUStatsDetailView: View {
|
|||
)
|
||||
}
|
||||
|
||||
StatsCard(title: "Top Processes", padding: 16, background: cardBackground, cornerRadius: 12) {
|
||||
CPUProcessList(processes: topProcesses, accentColor: accentColor, displayLimit: processDisplayLimit)
|
||||
}
|
||||
|
||||
if !statsManager.gpuDevices.isEmpty {
|
||||
StatsCard(title: "Devices", padding: 16, background: cardBackground, cornerRadius: 12) {
|
||||
GPUDeviceList(devices: statsManager.gpuDevices, accentColor: accentColor)
|
||||
}
|
||||
}
|
||||
|
||||
StatsCard(title: "Top Processes", padding: 16, background: cardBackground, cornerRadius: 12) {
|
||||
CPUProcessList(processes: topProcesses, accentColor: accentColor, displayLimit: processDisplayLimit)
|
||||
}
|
||||
}
|
||||
.padding(16)
|
||||
}
|
||||
|
|
@ -65,49 +65,15 @@ private struct GPUUsageDashboard: View {
|
|||
let primaryDevice: GPUDeviceMetrics?
|
||||
|
||||
var body: some View {
|
||||
HStack(alignment: .center, spacing: 24) {
|
||||
ZStack {
|
||||
Circle()
|
||||
.stroke(accentColor.opacity(0.25), lineWidth: 14)
|
||||
.frame(width: 120, height: 120)
|
||||
|
||||
RingArc(start: 0, end: CGFloat(min(max(usage / 100, 0), 1)), color: accentColor, lineWidth: 14)
|
||||
.frame(width: 120, height: 120)
|
||||
|
||||
VStack(spacing: 4) {
|
||||
Text(StatsFormatting.percentage(usage))
|
||||
.font(.system(size: 28, weight: .bold, design: .rounded))
|
||||
Text("Active")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
ViewThatFits(in: .horizontal) {
|
||||
HStack(alignment: .top, spacing: 20) {
|
||||
leftColumn
|
||||
breakdownAndMeta
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
DetailRow(color: accentColor.opacity(0.9), label: "Render", value: StatsFormatting.percentage(breakdown.render))
|
||||
DetailRow(color: accentColor.opacity(0.7), label: "Compute", value: StatsFormatting.percentage(breakdown.compute))
|
||||
DetailRow(color: accentColor.opacity(0.55), label: "Video", value: StatsFormatting.percentage(breakdown.video))
|
||||
DetailRow(color: accentColor.opacity(0.45), label: "Other", value: StatsFormatting.percentage(breakdown.other))
|
||||
VStack(alignment: .leading, spacing: 20) {
|
||||
leftColumn
|
||||
breakdownAndMeta
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text("Averages")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
DetailRow(color: nil, label: "Session Avg", value: StatsFormatting.percentage(averageUsage))
|
||||
DetailRow(color: nil, label: "Last Update", value: formattedTimestamp(lastUpdated))
|
||||
if let device = primaryDevice {
|
||||
Divider().padding(.vertical, 4)
|
||||
DetailRow(color: nil, label: "Active GPU", value: device.formattedVendorModel)
|
||||
DetailRow(color: nil, label: "Status", value: device.isActive ? "Active" : "Idle")
|
||||
DetailRow(color: nil, label: "Temperature", value: device.temperatureText)
|
||||
if let cores = device.cores {
|
||||
DetailRow(color: nil, label: "Cores", value: "\(cores)")
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -117,6 +83,143 @@ private struct GPUUsageDashboard: View {
|
|||
formatter.timeStyle = .medium
|
||||
return formatter.string(from: date)
|
||||
}
|
||||
|
||||
private var leftColumn: some View {
|
||||
VStack(alignment: .center, spacing: 16) {
|
||||
usageRing
|
||||
GPUEngineGauges(render: renderEngineUtilization, tiler: tilerUtilization, accentColor: accentColor)
|
||||
}
|
||||
}
|
||||
|
||||
private var breakdownAndMeta: some View {
|
||||
ViewThatFits(in: .horizontal) {
|
||||
HStack(alignment: .top, spacing: 20) {
|
||||
breakdownSection
|
||||
metaSection
|
||||
}
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
breakdownSection
|
||||
Divider().padding(.vertical, 4)
|
||||
metaSection
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var usageRing: some View {
|
||||
ZStack {
|
||||
Circle()
|
||||
.stroke(accentColor.opacity(0.25), lineWidth: 12)
|
||||
.frame(width: 108, height: 108)
|
||||
|
||||
RingArc(start: 0, end: CGFloat(min(max(usage / 100, 0), 1)), color: accentColor, lineWidth: 12)
|
||||
.frame(width: 108, height: 108)
|
||||
|
||||
VStack(spacing: 4) {
|
||||
Text(StatsFormatting.percentage(usage))
|
||||
.font(.system(size: 28, weight: .bold, design: .rounded))
|
||||
Text("Active")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var breakdownSection: some View {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
DetailRow(color: accentColor.opacity(0.9), label: "Render", value: StatsFormatting.percentage(breakdown.render))
|
||||
DetailRow(color: accentColor.opacity(0.7), label: "Compute", value: StatsFormatting.percentage(breakdown.compute))
|
||||
DetailRow(color: accentColor.opacity(0.55), label: "Video", value: StatsFormatting.percentage(breakdown.video))
|
||||
DetailRow(color: accentColor.opacity(0.45), label: "Other", value: StatsFormatting.percentage(breakdown.other))
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
private var metaSection: some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text("Averages")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
DetailRow(color: nil, label: "Session Avg", value: StatsFormatting.percentage(averageUsage))
|
||||
DetailRow(color: nil, label: "Last Update", value: formattedTimestamp(lastUpdated))
|
||||
if let device = primaryDevice {
|
||||
Divider().padding(.vertical, 4)
|
||||
DetailRow(color: nil, label: "Active GPU", value: device.formattedVendorModel)
|
||||
DetailRow(color: nil, label: "Status", value: device.isActive ? "Active" : "Idle")
|
||||
DetailRow(color: nil, label: "Temperature", value: device.temperatureText)
|
||||
if let cores = device.cores {
|
||||
DetailRow(color: nil, label: "Cores", value: "\(cores)")
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
private var renderEngineUtilization: Double? {
|
||||
primaryDevice?.renderUtilization ?? breakdown.render
|
||||
}
|
||||
|
||||
private var tilerUtilization: Double? {
|
||||
primaryDevice?.tilerUtilization
|
||||
}
|
||||
}
|
||||
|
||||
private struct GPUEngineGauges: View {
|
||||
let render: Double?
|
||||
let tiler: Double?
|
||||
let accentColor: Color
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
Text("Engines")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
HStack(alignment: .center, spacing: 16) {
|
||||
EngineGaugeView(title: "Render", value: render, tint: accentColor)
|
||||
EngineGaugeView(title: "Tiler", value: tiler, tint: accentColor.opacity(0.75))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct EngineGaugeView: View {
|
||||
let title: String
|
||||
let value: Double?
|
||||
let tint: Color
|
||||
private let size: CGFloat = 68
|
||||
|
||||
var body: some View {
|
||||
if let value {
|
||||
CircularGaugeView(
|
||||
title: title,
|
||||
value: min(max(value / 100, 0), 1),
|
||||
tint: tint,
|
||||
centerPrimaryText: StatsFormatting.percentage(value),
|
||||
centerSecondaryText: nil,
|
||||
subtitle: nil,
|
||||
size: size,
|
||||
lineWidth: 7,
|
||||
backgroundTint: tint.opacity(0.18)
|
||||
)
|
||||
} else {
|
||||
placeholder
|
||||
}
|
||||
}
|
||||
|
||||
private var placeholder: some View {
|
||||
VStack(spacing: 8) {
|
||||
ZStack {
|
||||
Circle()
|
||||
.stroke(Color.primary.opacity(0.08), lineWidth: 7)
|
||||
.frame(width: size, height: size)
|
||||
Text("—")
|
||||
.font(.system(size: 18, weight: .semibold))
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
Text(title)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct GPUDeviceList: View {
|
||||
|
|
|
|||
|
|
@ -28,10 +28,6 @@ struct MemoryStatsDetailView: View {
|
|||
MemoryHealthView(breakdown: statsManager.memoryBreakdown, accentColor: accentColor)
|
||||
}
|
||||
|
||||
StatsCard(title: "Details", padding: 16, background: cardBackground, cornerRadius: 12) {
|
||||
MemoryDetailsGrid(breakdown: statsManager.memoryBreakdown, accentColor: accentColor)
|
||||
}
|
||||
|
||||
StatsCard(title: "Top Processes", padding: 16, background: cardBackground, cornerRadius: 12) {
|
||||
CPUProcessList(processes: topProcesses, accentColor: accentColor, displayLimit: processDisplayLimit)
|
||||
}
|
||||
|
|
@ -78,46 +74,13 @@ private struct MemoryUsageDashboard: View {
|
|||
|
||||
return ViewThatFits(in: .horizontal) {
|
||||
VStack(alignment: .leading, spacing: 20) {
|
||||
ZStack {
|
||||
Circle()
|
||||
.stroke(freeColor.opacity(0.35), lineWidth: 14)
|
||||
.frame(width: 128, height: 128)
|
||||
|
||||
MemoryUsageRing(breakdown: breakdown, usedColor: accentColor, freeColor: freeColor)
|
||||
.frame(width: 128, height: 128)
|
||||
|
||||
VStack(spacing: 4) {
|
||||
Text(StatsFormatting.percentage(breakdown.usedPercentage))
|
||||
.font(.system(size: 30, weight: .bold, design: .rounded))
|
||||
Text("Used")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
usageRing
|
||||
totalsSection
|
||||
Divider().padding(.vertical, 4)
|
||||
breakdownSection
|
||||
}
|
||||
HStack(alignment: .top, spacing: 28) {
|
||||
ZStack {
|
||||
Circle()
|
||||
.stroke(freeColor.opacity(0.35), lineWidth: 14)
|
||||
.frame(width: 128, height: 128)
|
||||
|
||||
MemoryUsageRing(breakdown: breakdown, usedColor: accentColor, freeColor: freeColor)
|
||||
.frame(width: 128, height: 128)
|
||||
|
||||
VStack(spacing: 4) {
|
||||
Text(StatsFormatting.percentage(breakdown.usedPercentage))
|
||||
.font(.system(size: 30, weight: .bold, design: .rounded))
|
||||
Text("Used")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
usageRing
|
||||
totalsSection
|
||||
breakdownSection
|
||||
}
|
||||
|
|
@ -127,6 +90,26 @@ private struct MemoryUsageDashboard: View {
|
|||
private func formattedBytes(_ value: UInt64) -> String {
|
||||
StatsFormatting.bytes(value)
|
||||
}
|
||||
|
||||
private var usageRing: some View {
|
||||
ZStack {
|
||||
Circle()
|
||||
.stroke(freeColor.opacity(0.35), lineWidth: 14)
|
||||
.frame(width: 128, height: 128)
|
||||
|
||||
MemoryUsageRing(breakdown: breakdown, usedColor: accentColor, freeColor: freeColor)
|
||||
.frame(width: 128, height: 128)
|
||||
|
||||
VStack(spacing: 4) {
|
||||
Text(StatsFormatting.percentage(breakdown.usedPercentage))
|
||||
.font(.system(size: 30, weight: .bold, design: .rounded))
|
||||
Text("Used")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
}
|
||||
|
||||
private struct MemoryUsageRing: View {
|
||||
|
|
@ -159,31 +142,12 @@ private struct MemoryBreakdownRow: View {
|
|||
}
|
||||
}
|
||||
|
||||
private struct MemoryDetailsGrid: View {
|
||||
let breakdown: MemoryBreakdown
|
||||
let accentColor: Color
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 10) {
|
||||
DetailRow(color: accentColor.opacity(0.85), label: "App Memory", value: StatsFormatting.bytes(breakdown.appBytes))
|
||||
DetailRow(color: accentColor.opacity(0.7), label: "Cached Files", value: StatsFormatting.bytes(breakdown.cacheBytes))
|
||||
DetailRow(color: accentColor.opacity(0.6), label: "Active", value: StatsFormatting.bytes(breakdown.activeBytes))
|
||||
DetailRow(color: accentColor.opacity(0.45), label: "Inactive", value: StatsFormatting.bytes(breakdown.inactiveBytes))
|
||||
DetailRow(color: accentColor.opacity(0.35), label: "Wired", value: StatsFormatting.bytes(breakdown.wiredBytes))
|
||||
DetailRow(color: accentColor.opacity(0.25), label: "Compressed", value: StatsFormatting.bytes(breakdown.compressedBytes))
|
||||
DetailRow(color: accentColor.opacity(0.18), label: "Free", value: StatsFormatting.bytes(breakdown.freeBytes))
|
||||
Divider().padding(.vertical, 4)
|
||||
DetailRow(color: nil, label: "Total Memory", value: StatsFormatting.bytes(breakdown.totalBytes))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct MemoryHealthView: View {
|
||||
let breakdown: MemoryBreakdown
|
||||
let accentColor: Color
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 14) {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
MemoryPressureIndicator(pressure: breakdown.pressure)
|
||||
Divider().padding(.vertical, 4)
|
||||
SwapUsageView(swap: breakdown.swap, accentColor: accentColor)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue