mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 23:24:32 +00:00
121 lines
No EOL
3.5 KiB
Swift
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 |