-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPanelManager.swift
More file actions
103 lines (86 loc) · 2.65 KB
/
PanelManager.swift
File metadata and controls
103 lines (86 loc) · 2.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
//
// PanelManager.swift
// MistTray
//
import Cocoa
import SwiftUI
// Borderless NSPanel that can become key (required for SwiftUI interaction)
private class KeyablePanel: NSPanel {
override var canBecomeKey: Bool { true }
override func sendEvent(_ event: NSEvent) {
// If the panel lost key status (e.g. after a view hierarchy change that
// invalidated the responder chain), re-establish it before dispatching
// the click so SwiftUI's gesture system can process it.
if event.type == .leftMouseDown && !isKeyWindow {
makeKeyAndOrderFront(nil)
}
super.sendEvent(event)
}
}
class PanelManager: NSObject, NSWindowDelegate {
private var panel: NSPanel?
private let appState: AppState
private let panelWidth: CGFloat = 380
private let panelHeight: CGFloat = 520
init(appState: AppState) {
self.appState = appState
super.init()
}
// MARK: - Panel Lifecycle
func togglePanel(relativeTo button: NSStatusBarButton) {
if let panel = panel, panel.isVisible {
closePanel()
} else {
showPanel(relativeTo: button)
}
}
private func showPanel(relativeTo button: NSStatusBarButton) {
let panel = makePanel()
let hostingView = NSHostingController(
rootView: DashboardView(appState: appState, closePanel: { [weak self] in
self?.closePanel()
})
)
panel.contentViewController = hostingView
panel.setContentSize(NSSize(width: panelWidth, height: panelHeight))
// Position below the status bar button
if let buttonWindow = button.window {
let buttonFrame = buttonWindow.frame
let x = buttonFrame.midX - (panelWidth / 2)
let y = buttonFrame.minY - panelHeight - 4
panel.setFrameOrigin(NSPoint(x: x, y: y))
} else {
panel.center()
}
self.panel = panel
// Activate the app so the panel can properly become key
NSApp.activate(ignoringOtherApps: true)
panel.makeKeyAndOrderFront(nil)
}
func closePanel() {
panel?.orderOut(nil)
panel = nil
}
// MARK: - Panel Construction
private func makePanel() -> NSPanel {
let panel = KeyablePanel(
contentRect: NSRect(x: 0, y: 0, width: panelWidth, height: panelHeight),
styleMask: [.borderless, .nonactivatingPanel],
backing: .buffered,
defer: false
)
panel.isFloatingPanel = true
panel.level = .floating
panel.hasShadow = true
panel.isOpaque = false
panel.backgroundColor = .clear
panel.delegate = self
panel.isMovableByWindowBackground = false
panel.hidesOnDeactivate = false
return panel
}
// MARK: - NSWindowDelegate
func windowDidResignKey(_ notification: Notification) {
closePanel()
}
}