From 881531505946a06be28c1eeebc70582e9ab88714 Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Wed, 14 Jan 2026 19:59:45 -0500 Subject: [PATCH] general: testing and simplifying --- Gaze/AppDelegate.swift | 4 + Gaze/Constants/EyeTrackingConstants.swift | 16 +-- Gaze/Services/EyeTrackingService.swift | 8 +- .../Views/Containers/SettingsWindowView.swift | 114 +++++------------- 4 files changed, 43 insertions(+), 99 deletions(-) diff --git a/Gaze/AppDelegate.swift b/Gaze/AppDelegate.swift index b955cee..9245313 100644 --- a/Gaze/AppDelegate.swift +++ b/Gaze/AppDelegate.swift @@ -76,6 +76,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject { .sink { [weak self] smartMode in self?.idleService?.updateThreshold(minutes: smartMode.idleThresholdMinutes) self?.usageTrackingService?.updateResetThreshold(minutes: smartMode.usageResetAfterMinutes) + + // Force state check when settings change to apply immediately + self?.fullscreenService?.forceUpdate() + self?.idleService?.forceUpdate() } .store(in: &cancellables) } diff --git a/Gaze/Constants/EyeTrackingConstants.swift b/Gaze/Constants/EyeTrackingConstants.swift index 4717ffc..9b81ae9 100644 --- a/Gaze/Constants/EyeTrackingConstants.swift +++ b/Gaze/Constants/EyeTrackingConstants.swift @@ -11,32 +11,32 @@ enum EyeTrackingConstants { // MARK: - Logging /// Interval between log messages in seconds static let logInterval: TimeInterval = 0.5 - + // MARK: - Eye Closure Detection /// Threshold for eye closure (smaller value means eye must be more closed to trigger) /// Range: 0.0 to 1.0 (approximate eye opening ratio) static let eyeClosedThreshold: CGFloat = 0.02 - + // MARK: - Face Pose Thresholds /// Maximum yaw (left/right head turn) in radians before considering user looking away /// 0.20 radians ≈ 11.5 degrees (Tightened from 0.35) - static let yawThreshold: Double = 0.20 - + static let yawThreshold: Double = 0.1 + /// Pitch threshold for looking UP (above screen). /// Since camera is at top, looking at screen is negative pitch. /// Values > 0.1 imply looking straight ahead or up (away from screen). static let pitchUpThreshold: Double = 0.1 - + /// Pitch threshold for looking DOWN (at keyboard/lap). /// Values < -0.45 imply looking too far down. - static let pitchDownThreshold: Double = -0.45 - + static let pitchDownThreshold: Double = -0.2 + // MARK: - Pupil Tracking Thresholds /// Minimum horizontal pupil ratio (0.0 = right edge, 1.0 = left edge) /// Values below this are considered looking right (camera view) /// Tightened from 0.25 to 0.35 static let minPupilRatio: Double = 0.35 - + /// Maximum horizontal pupil ratio /// Values above this are considered looking left (camera view) /// Tightened from 0.75 to 0.65 diff --git a/Gaze/Services/EyeTrackingService.swift b/Gaze/Services/EyeTrackingService.swift index d684930..e5f3ce7 100644 --- a/Gaze/Services/EyeTrackingService.swift +++ b/Gaze/Services/EyeTrackingService.swift @@ -24,10 +24,6 @@ class EyeTrackingService: NSObject, ObservableObject { label: "com.gaze.videoDataOutput", qos: .userInitiated) private var _previewLayer: AVCaptureVideoPreviewLayer? - // Logging throttle - private var lastLogTime: Date = .distantPast - private let logInterval: TimeInterval = EyeTrackingConstants.logInterval - var previewLayer: AVCaptureVideoPreviewLayer? { guard let session = captureSession else { _previewLayer = nil @@ -190,7 +186,9 @@ class EyeTrackingService: NSObject, ObservableObject { // - Camera at top = looking at screen is negative pitch // - Looking above screen (straight ahead) is ~0 or positive -> Look Away // - Looking at keyboard/lap is very negative -> Look Away - let pitchLookingAway = pitch > EyeTrackingConstants.pitchUpThreshold || pitch < EyeTrackingConstants.pitchDownThreshold + let pitchLookingAway = + pitch > EyeTrackingConstants.pitchUpThreshold + || pitch < EyeTrackingConstants.pitchDownThreshold let poseLookingAway = abs(yaw) > yawThreshold || pitchLookingAway diff --git a/Gaze/Views/Containers/SettingsWindowView.swift b/Gaze/Views/Containers/SettingsWindowView.swift index fba2922..8872d9d 100644 --- a/Gaze/Views/Containers/SettingsWindowView.swift +++ b/Gaze/Views/Containers/SettingsWindowView.swift @@ -18,87 +18,18 @@ struct SettingsWindowView: View { var body: some View { VStack(spacing: 0) { - if #available(macOS 15.0, *) { - TabView(selection: $selectedSection) { - Tab( - SettingsSection.general.title, - systemImage: SettingsSection.general.iconName, - value: SettingsSection.general - ) { - GeneralSetupView( - settingsManager: settingsManager, - isOnboarding: false - ) - } - - Tab( - SettingsSection.lookAway.title, - systemImage: SettingsSection.lookAway.iconName, - value: SettingsSection.lookAway - ) { - LookAwaySetupView(settingsManager: settingsManager) - } - - Tab( - SettingsSection.blink.title, systemImage: SettingsSection.blink.iconName, - value: SettingsSection.blink - ) { - BlinkSetupView(settingsManager: settingsManager) - } - - Tab( - SettingsSection.posture.title, - systemImage: SettingsSection.posture.iconName, - value: SettingsSection.posture - ) { - PostureSetupView(settingsManager: settingsManager) - } - - Tab( - SettingsSection.userTimers.title, - systemImage: SettingsSection.userTimers.iconName, - value: SettingsSection.userTimers - ) { - UserTimersView( - userTimers: Binding( - get: { settingsManager.settings.userTimers }, - set: { settingsManager.settings.userTimers = $0 } - ) - ) - } - Tab( - SettingsSection.enforceMode.title, - systemImage: SettingsSection.enforceMode.iconName, - value: SettingsSection.enforceMode - ) { - EnforceModeSetupView(settingsManager: settingsManager) - } - - Tab( - SettingsSection.smartMode.title, - systemImage: SettingsSection.smartMode.iconName, - value: SettingsSection.smartMode - ) { - SmartModeSetupView(settingsManager: settingsManager) + NavigationSplitView { + List(SettingsSection.allCases, selection: $selectedSection) { section in + NavigationLink(value: section) { + Label(section.title, systemImage: section.iconName) } } - .tabViewStyle(.sidebarAdaptable) - } else { - // Fallback for macOS 14 and earlier - use a consistent sidebar approach without collapse button - NavigationSplitView { - List(SettingsSection.allCases, selection: $selectedSection) { section in - NavigationLink(value: section) { - Label(section.title, systemImage: section.iconName) - } - } - .navigationTitle("Settings") - .listStyle(.sidebar) - } detail: { - detailView(for: selectedSection) - } - // Disable the ability to collapse the sidebar by explicitly setting a fixed width - .navigationSplitViewColumnWidth(min: 200, ideal: 250, max: 300) + .navigationTitle("Settings") + .listStyle(.sidebar) + } detail: { + detailView(for: selectedSection) } + .navigationSplitViewColumnWidth(min: 200, ideal: 250, max: 300) Divider() @@ -178,16 +109,27 @@ struct SettingsWindowView: View { #if DEBUG private func retriggerOnboarding() { - // Close settings window first + // Get AppDelegate reference first + guard let appDelegate = NSApplication.shared.delegate as? AppDelegate else { return } + + // Step 1: Close any existing onboarding window + if let onboardingWindow = NSApplication.shared.windows.first(where: { + $0.identifier == WindowIdentifiers.onboarding + }) { + onboardingWindow.close() + } + + // Step 2: Close settings window closeWindow() - // Get AppDelegate and open onboarding - if let appDelegate = NSApplication.shared.delegate as? AppDelegate { - // Reset onboarding state so it shows as fresh - settingsManager.settings.hasCompletedOnboarding = false + // Step 3: Reset onboarding state with a delay to ensure settings window is closed + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { + self.settingsManager.settings.hasCompletedOnboarding = false - // Open onboarding window - appDelegate.openOnboarding() + // Step 4: Open onboarding window with another delay to ensure state is saved + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { + appDelegate.openOnboarding() + } } } #endif @@ -195,4 +137,4 @@ struct SettingsWindowView: View { #Preview { SettingsWindowView(settingsManager: SettingsManager.shared) -} \ No newline at end of file +}