Monitor/Sources/FloatingMonitor/App/AppDelegate.swift
JOJO 4ab9a7b28c Initial: FloatingMonitor — macOS 悬浮系统监控
- 实时 CPU/GPU/内存/Swap 监控 + Top 进程
- 5 种计量器样式(圆环/条状/纯文本/扇形/紧凑)
- 半透明悬浮窗 + 菜单栏图标控制
- 独立设置窗口,每模块可指定行号+列位布局
- 窗口宽高/透明度/字体全局可调
2026-05-05 21:26:44 +08:00

121 lines
4.2 KiB
Swift

import AppKit
// MARK: -
extension Notification.Name {
static let showSettingsPanel = Notification.Name("FloatingMonitor.ShowSettings")
static let resizeMonitorWindow = Notification.Name("FloatingMonitor.ResizeWindow")
static let refreshIntervalChanged = Notification.Name("FloatingMonitor.RefreshIntervalChanged")
}
// MARK: - AppDelegate
final class AppDelegate: NSObject, NSApplicationDelegate {
private var windowController: FloatingWindowController?
private var settingsWindowController: SettingsWindowController?
private var statusItem: NSStatusItem?
private var statusMenu: NSMenu!
let systemMonitor = SystemMonitor()
func applicationDidFinishLaunching(_ notification: Notification) {
NSApp.setActivationPolicy(.accessory)
windowController = FloatingWindowController()
let contentView = ContentView(monitor: systemMonitor, windowController: windowController)
windowController?.embed(rootView: contentView)
let cfg = Configuration.shared
windowController?.resize(width: cfg.windowWidth, height: cfg.windowHeight)
systemMonitor.interval = cfg.refreshInterval
systemMonitor.topN = cfg.processCount
systemMonitor.start()
setupStatusItem()
observeResize()
}
// MARK: -
private func setupStatusItem() {
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
guard let button = statusItem?.button else { return }
if let image = NSImage(systemSymbolName: "gauge.with.dots.needle.33percent",
accessibilityDescription: "Monitor") {
image.isTemplate = true
button.image = image
}
button.toolTip = "Monitor"
statusMenu = NSMenu()
let toggleItem = NSMenuItem(title: "显示 Monitor", action: #selector(menuToggleWindow), keyEquivalent: "")
toggleItem.target = self
statusMenu.addItem(toggleItem)
statusMenu.addItem(.separator())
let settingsItem = NSMenuItem(title: "显示设置…", action: #selector(menuOpenSettings), keyEquivalent: ",")
settingsItem.keyEquivalentModifierMask = .command
settingsItem.target = self
statusMenu.addItem(settingsItem)
statusMenu.addItem(.separator())
let quitItem = NSMenuItem(title: "退出 Monitor", action: #selector(menuQuit), keyEquivalent: "q")
quitItem.keyEquivalentModifierMask = .command
quitItem.target = self
statusMenu.addItem(quitItem)
statusItem?.menu = statusMenu
button.target = self
button.action = #selector(statusItemClicked)
button.sendAction(on: [.leftMouseDown, .rightMouseDown])
}
@objc private func statusItemClicked() {
guard let event = NSApp.currentEvent else { return }
if event.type == .rightMouseDown {
updateToggleMenuItemTitle()
} else {
toggleWindow()
}
}
@objc private func menuToggleWindow() { toggleWindow() }
@objc private func toggleWindow() { windowController?.toggleVisible() }
@objc private func menuOpenSettings() { openSettingsWindow() }
@objc private func menuQuit() { NSApp.terminate(nil) }
private func updateToggleMenuItemTitle() {
let visible = windowController?.isVisible ?? false
statusMenu.item(at: 0)?.title = visible ? "隐藏 Monitor" : "显示 Monitor"
}
// MARK: -
private func openSettingsWindow() {
if settingsWindowController == nil {
settingsWindowController = SettingsWindowController(monitor: systemMonitor)
}
settingsWindowController?.show()
}
// MARK: -
private func observeResize() {
NotificationCenter.default.addObserver(
forName: .resizeMonitorWindow, object: nil, queue: .main
) { [weak self] notification in
guard let size = notification.object as? NSSize else { return }
self?.windowController?.resize(width: size.width, height: size.height)
}
}
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
windowController?.show()
return true
}
}