1import AppKit2import SwiftUI34@MainActor5class MenuController: NSObject {6 static private(set) weak var shared: MenuController?78 let statusItem: NSStatusItem9 let popover: NSPopover10 var eventMonitor: EventMonitor?1112 init(statusItem: NSStatusItem, popover: NSPopover? = nil) {13 self.statusItem = statusItem14 self.popover = popover ?? NSPopover()15 super.init()1617 if let button = statusItem.button {18 button.action = #selector(togglePopover(_:))19 button.target = self20 }2122 23 self.popover.contentSize = NSSize(width: 450, height: 640)24 self.popover.behavior = .transient25 self.popover.contentViewController = NSHostingController(rootView: MainView())2627 eventMonitor = EventMonitor(mask: [.leftMouseDown, .rightMouseDown]) { [weak self] event in28 if let self = self, self.popover.isShown {29 self.closePopover(sender: event)30 }31 }3233 MenuController.shared = self34 }3536 37 38 39 func beginModalPresentation() {40 eventMonitor?.stop()41 popover.behavior = .applicationDefined42 }4344 func endModalPresentation() {45 popover.behavior = .transient46 if popover.isShown {47 eventMonitor?.start()48 }49 }5051 @objc func togglePopover(_ sender: AnyObject?) {52 if popover.isShown {53 closePopover(sender: sender)54 } else {55 showPopover(sender: sender)56 }57 }5859 func showPopover(sender: AnyObject?) {60 if let button = statusItem.button {61 popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)62 eventMonitor?.start()63 }64 }6566 func closePopover(sender: AnyObject?) {67 popover.performClose(sender)68 eventMonitor?.stop()69 }70}7172class EventMonitor {73 private var monitor: Any?74 private let mask: NSEvent.EventTypeMask75 let handler: (NSEvent?) -> Void7677 init(mask: NSEvent.EventTypeMask, handler: @escaping (NSEvent?) -> Void) {78 self.mask = mask79 self.handler = handler80 }8182 deinit {83 stop()84 }8586 func start() {87 monitor = NSEvent.addGlobalMonitorForEvents(matching: mask, handler: handler)88 }8990 func stop() {91 if let monitor = monitor {92 NSEvent.removeMonitor(monitor)93 self.monitor = nil94 }95 }96}