feat: proper menubarextra

This commit is contained in:
Michael Freno
2026-01-08 22:28:27 -05:00
parent 7cae818d2d
commit 3fc49333c4
3 changed files with 22 additions and 74 deletions

View File

@@ -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

View File

@@ -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() {

View File

@@ -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)) {