Atoll/DynamicIsland/components/Stats/RankedProcessPopover.swift
Hariharan Mudaliar 7a1945dd21 Working stuff
2025-10-19 08:43:58 +05:30

121 lines
No EOL
3.5 KiB
Swift

//
// RankedProcessPopover.swift
// DynamicIsland
//
// Popover component for displaying ranked processes by different metrics
// Adapted from boring.notch implementation
import SwiftUI
#if os(macOS)
import AppKit
#endif
struct RankedProcessPopover: View {
let rankingType: ProcessRankingType
@Environment(\.dismiss) private var dismiss
// Callback to notify parent about hover state
var onHoverChange: ((Bool) -> Void)?
private var configuration: (width: CGFloat, minHeight: CGFloat, padding: CGFloat) {
switch rankingType {
case .cpu:
return (420, 420, 0)
case .memory:
return (400, 380, 0)
case .gpu:
return (380, 360, 0)
case .network, .disk:
return (360, 320, 0)
}
}
var body: some View {
popoverContent
.padding(configuration.padding)
.frame(width: configuration.width)
.frame(minHeight: configuration.minHeight)
.background(Color(NSColor.controlBackgroundColor))
.clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous))
.shadow(color: .black.opacity(0.28), radius: 12, x: 0, y: 8)
.overlay(alignment: .topTrailing) {
Button(action: { dismiss() }) {
Image(systemName: "xmark.circle.fill")
.font(.title3)
.foregroundColor(.secondary)
.padding(10)
}
.buttonStyle(PlainButtonStyle())
}
.overlay(alignment: .center) {
PopoverHoverSensor { hovering in
onHoverChange?(hovering)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.allowsHitTesting(false)
}
}
@ViewBuilder
private var popoverContent: some View {
switch rankingType {
case .cpu:
CPUStatsDetailView()
case .memory:
MemoryStatsDetailView()
case .gpu:
GPUStatsDetailView()
case .network:
NetworkStatsDetailView()
case .disk:
DiskStatsDetailView()
}
}
}
#if os(macOS)
private struct PopoverHoverSensor: NSViewRepresentable {
var onHoverChange: ((Bool) -> Void)?
func makeNSView(context: Context) -> TrackingView {
let view = TrackingView()
view.onHoverChange = onHoverChange
return view
}
func updateNSView(_ nsView: TrackingView, context: Context) {
nsView.onHoverChange = onHoverChange
}
final class TrackingView: NSView {
var onHoverChange: ((Bool) -> Void)?
private var trackingArea: NSTrackingArea?
override func updateTrackingAreas() {
super.updateTrackingAreas()
if let trackingArea {
removeTrackingArea(trackingArea)
}
let options: NSTrackingArea.Options = [.mouseEnteredAndExited, .activeAlways, .inVisibleRect]
let area = NSTrackingArea(rect: bounds, options: options, owner: self, userInfo: nil)
addTrackingArea(area)
trackingArea = area
}
override func mouseEntered(with event: NSEvent) {
onHoverChange?(true)
}
override func mouseExited(with event: NSEvent) {
onHoverChange?(false)
}
deinit {
onHoverChange?(false)
if let trackingArea {
removeTrackingArea(trackingArea)
}
}
}
}
#endif