feat: proper menubarextra
This commit is contained in:
@@ -12,8 +12,6 @@ import Combine
|
||||
@MainActor
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
var timerEngine: TimerEngine?
|
||||
private var statusItem: NSStatusItem?
|
||||
private var popover: NSPopover?
|
||||
private var settingsManager: SettingsManager?
|
||||
private var reminderWindowController: NSWindowController?
|
||||
private var settingsWindowController: NSWindowController?
|
||||
@@ -28,7 +26,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
settingsManager = SettingsManager.shared
|
||||
timerEngine = TimerEngine(settingsManager: settingsManager!)
|
||||
|
||||
setupMenuBar()
|
||||
setupLifecycleObservers()
|
||||
observeSettingsChanges()
|
||||
|
||||
@@ -42,48 +39,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
startTimers()
|
||||
}
|
||||
|
||||
private func setupMenuBar() {
|
||||
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
|
||||
|
||||
if let button = statusItem?.button {
|
||||
button.image = NSImage(systemSymbolName: "eye.fill", accessibilityDescription: "Gaze")
|
||||
button.action = #selector(togglePopover)
|
||||
button.target = self
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func togglePopover() {
|
||||
if let popover = popover, popover.isShown {
|
||||
popover.close()
|
||||
} else {
|
||||
showPopover()
|
||||
}
|
||||
}
|
||||
|
||||
private func showPopover() {
|
||||
// Reuse existing popover or create new one
|
||||
if popover == nil {
|
||||
let newPopover = NSPopover()
|
||||
newPopover.contentSize = NSSize(width: 300, height: 400)
|
||||
newPopover.behavior = .transient
|
||||
popover = newPopover
|
||||
}
|
||||
|
||||
// Always set fresh content
|
||||
popover?.contentViewController = NSHostingController(
|
||||
rootView: MenuBarContentView(
|
||||
timerEngine: timerEngine!,
|
||||
settingsManager: settingsManager!,
|
||||
onQuit: { NSApplication.shared.terminate(nil) },
|
||||
onOpenSettings: { [weak self] in self?.openSettings() }
|
||||
)
|
||||
)
|
||||
|
||||
if let button = statusItem?.button {
|
||||
popover?.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
|
||||
}
|
||||
}
|
||||
|
||||
private func startTimers() {
|
||||
guard !hasStartedTimers else { return }
|
||||
hasStartedTimers = true
|
||||
@@ -226,11 +181,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
reminderWindowController = nil
|
||||
}
|
||||
|
||||
// Public method to get menubar icon position for animations
|
||||
func getMenuBarIconPosition() -> NSRect? {
|
||||
return statusItem?.button?.window?.frame
|
||||
}
|
||||
|
||||
// Public method to open settings window
|
||||
func openSettings() {
|
||||
// If window already exists, just bring it to front
|
||||
|
||||
@@ -13,6 +13,7 @@ struct GazeApp: App {
|
||||
@StateObject private var settingsManager = SettingsManager.shared
|
||||
|
||||
var body: some Scene {
|
||||
// Onboarding window (only shown when not completed)
|
||||
WindowGroup {
|
||||
if settingsManager.settings.hasCompletedOnboarding {
|
||||
EmptyView()
|
||||
@@ -33,6 +34,19 @@ struct GazeApp: App {
|
||||
.commands {
|
||||
CommandGroup(replacing: .newItem) { }
|
||||
}
|
||||
|
||||
// Menu bar extra (always present once onboarding is complete)
|
||||
MenuBarExtra("Gaze", systemImage: "eye.fill") {
|
||||
if let timerEngine = appDelegate.timerEngine {
|
||||
MenuBarContentView(
|
||||
timerEngine: timerEngine,
|
||||
settingsManager: settingsManager,
|
||||
onQuit: { NSApplication.shared.terminate(nil) },
|
||||
onOpenSettings: { appDelegate.openSettings() }
|
||||
)
|
||||
}
|
||||
}
|
||||
.menuBarExtraStyle(.window)
|
||||
}
|
||||
|
||||
private func closeAllWindows() {
|
||||
|
||||
@@ -188,30 +188,14 @@ struct OnboardingContainerView: View {
|
||||
return
|
||||
}
|
||||
|
||||
// Get menubar icon position from AppDelegate
|
||||
let appDelegate = NSApplication.shared.delegate as? AppDelegate
|
||||
let targetFrame = appDelegate?.getMenuBarIconPosition()
|
||||
|
||||
// Calculate target position (menubar icon or top-center as fallback)
|
||||
let targetRect: NSRect
|
||||
if let menuBarFrame = targetFrame {
|
||||
// Use menubar icon position
|
||||
targetRect = NSRect(
|
||||
x: menuBarFrame.midX,
|
||||
y: menuBarFrame.midY,
|
||||
width: 0,
|
||||
height: 0
|
||||
)
|
||||
} else {
|
||||
// Fallback to top-center of screen
|
||||
let screen = NSScreen.main?.frame ?? .zero
|
||||
targetRect = NSRect(
|
||||
x: screen.midX,
|
||||
y: screen.maxY,
|
||||
width: 0,
|
||||
height: 0
|
||||
)
|
||||
}
|
||||
// Calculate target position (top-center of screen where menu bar is)
|
||||
let screen = NSScreen.main?.frame ?? .zero
|
||||
let targetRect = NSRect(
|
||||
x: screen.midX,
|
||||
y: screen.maxY,
|
||||
width: 0,
|
||||
height: 0
|
||||
)
|
||||
|
||||
// Start SwiftUI animation for visual effects
|
||||
withAnimation(.easeInOut(duration: 0.7)) {
|
||||
|
||||
Reference in New Issue
Block a user