general: screw the close anim
This commit is contained in:
@@ -216,22 +216,43 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
|||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
|
||||||
guard let self = self, let settingsManager = self.settingsManager else { return }
|
guard let self = self, let settingsManager = self.settingsManager else { return }
|
||||||
|
|
||||||
let window = NSWindow(
|
// Check if onboarding window already exists from the WindowGroup
|
||||||
contentRect: NSRect(x: 0, y: 0, width: 700, height: 700),
|
let existingWindow = NSApplication.shared.windows.first { window in
|
||||||
styleMask: [.titled, .closable, .miniaturizable],
|
// Check if window contains OnboardingContainerView by examining its content view
|
||||||
backing: .buffered,
|
if let hostingView = window.contentView as? NSHostingView<OnboardingContainerView> {
|
||||||
defer: false
|
return true
|
||||||
)
|
}
|
||||||
|
// Also check for windows with our expected size (onboarding window dimensions)
|
||||||
|
return window.frame.size.width == 700 && window.frame.size.height == 700
|
||||||
|
&& window.styleMask.contains(.titled)
|
||||||
|
&& window.title.isEmpty // WindowGroup windows have empty title by default
|
||||||
|
}
|
||||||
|
|
||||||
window.title = "Gaze Onboarding"
|
if let window = existingWindow {
|
||||||
window.center()
|
// Reuse existing window - just bring it to front
|
||||||
window.isReleasedWhenClosed = true
|
window.makeKeyAndOrderFront(nil)
|
||||||
window.contentView = NSHostingView(
|
NSApp.activate(ignoringOtherApps: true)
|
||||||
rootView: OnboardingContainerView(settingsManager: settingsManager)
|
} else {
|
||||||
)
|
// Create new window matching WindowGroup style
|
||||||
|
let window = NSWindow(
|
||||||
|
contentRect: NSRect(x: 0, y: 0, width: 700, height: 700),
|
||||||
|
styleMask: [.titled, .closable, .miniaturizable, .fullSizeContentView],
|
||||||
|
backing: .buffered,
|
||||||
|
defer: false
|
||||||
|
)
|
||||||
|
|
||||||
window.makeKeyAndOrderFront(nil)
|
// Match the WindowGroup style: hiddenTitleBar
|
||||||
NSApp.activate(ignoringOtherApps: true)
|
window.titleVisibility = .hidden
|
||||||
|
window.titlebarAppearsTransparent = true
|
||||||
|
window.center()
|
||||||
|
window.isReleasedWhenClosed = true
|
||||||
|
window.contentView = NSHostingView(
|
||||||
|
rootView: OnboardingContainerView(settingsManager: settingsManager)
|
||||||
|
)
|
||||||
|
|
||||||
|
window.makeKeyAndOrderFront(nil)
|
||||||
|
NSApp.activate(ignoringOtherApps: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ struct OnboardingContainerView: View {
|
|||||||
@State private var postureIntervalMinutes = 30
|
@State private var postureIntervalMinutes = 30
|
||||||
@State private var launchAtLogin = false
|
@State private var launchAtLogin = false
|
||||||
@State private var subtleReminderSize: ReminderSize = .medium
|
@State private var subtleReminderSize: ReminderSize = .medium
|
||||||
@State private var isAnimatingOut = false
|
|
||||||
@Environment(\.dismiss) private var dismiss
|
@Environment(\.dismiss) private var dismiss
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@@ -151,8 +150,6 @@ struct OnboardingContainerView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(minWidth: 1000, minHeight: 800)
|
.frame(minWidth: 1000, minHeight: 800)
|
||||||
.opacity(isAnimatingOut ? 0 : 1)
|
|
||||||
.scaleEffect(isAnimatingOut ? 0.3 : 1.0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func completeOnboarding() {
|
private func completeOnboarding() {
|
||||||
@@ -188,49 +185,19 @@ struct OnboardingContainerView: View {
|
|||||||
print("Failed to set launch at login: \(error)")
|
print("Failed to set launch at login: \(error)")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform vacuum animation
|
// Close window with standard macOS animation
|
||||||
performVacuumAnimation()
|
dismiss()
|
||||||
}
|
|
||||||
|
|
||||||
private func performVacuumAnimation() {
|
// After a brief delay, trigger the menu bar extra to open
|
||||||
// Get the NSWindow reference
|
DispatchQueue.main.asyncAfter(deadline: .now()) {
|
||||||
guard
|
if let menuBarWindow = NSApp.windows.first(where: {
|
||||||
let window = NSApplication.shared.windows.first(where: {
|
$0.className.contains("MenuBarExtra") || $0.className.contains("StatusBar")
|
||||||
$0.isVisible && $0.contentView != nil
|
}),
|
||||||
})
|
let statusItem = menuBarWindow.value(forKey: "statusItem") as? NSStatusItem
|
||||||
else {
|
{
|
||||||
// Fallback: just dismiss without animation
|
statusItem.button?.performClick(nil)
|
||||||
dismiss()
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)) {
|
|
||||||
isAnimatingOut = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Animate window frame using AppKit
|
|
||||||
NSAnimationContext.runAnimationGroup(
|
|
||||||
{ context in
|
|
||||||
context.duration = 0.7
|
|
||||||
context.timingFunction = CAMediaTimingFunction(name: .easeIn)
|
|
||||||
window.animator().setFrame(targetRect, display: true)
|
|
||||||
window.animator().alphaValue = 0
|
|
||||||
},
|
|
||||||
completionHandler: {
|
|
||||||
// Close window after animation completes
|
|
||||||
self.dismiss()
|
|
||||||
window.close()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#Preview("Onboarding Container") {
|
#Preview("Onboarding Container") {
|
||||||
|
|||||||
Reference in New Issue
Block a user