general: testing and simplifying
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user