84 lines
2.9 KiB
Swift
84 lines
2.9 KiB
Swift
import AppKit
|
|
|
|
extension NSScreen {
|
|
|
|
// MARK: - Stable display identifier
|
|
|
|
/// Returns a stable UUID string for this screen by querying CoreGraphics.
|
|
/// Falls back to the localized name if the CG UUID is unavailable.
|
|
var displayUUID: String {
|
|
guard let screenNumber = deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? CGDirectDisplayID else {
|
|
return localizedName
|
|
}
|
|
guard let uuid = CGDisplayCreateUUIDFromDisplayID(screenNumber) else {
|
|
return localizedName
|
|
}
|
|
return CFUUIDCreateString(nil, uuid.takeUnretainedValue()) as String
|
|
}
|
|
|
|
// MARK: - Notch detection
|
|
|
|
/// `true` when this screen has a physical camera notch (safe area inset at top > 0).
|
|
var hasNotch: Bool {
|
|
safeAreaInsets.top > 0
|
|
}
|
|
|
|
// MARK: - Closed notch sizing
|
|
|
|
/// Computes the closed-state notch size for this screen,
|
|
/// respecting the user's height mode and custom height preferences.
|
|
func closedNotchSize(using settings: AppSettings.DisplaySettings) -> CGSize {
|
|
let height = closedNotchHeight(using: settings)
|
|
let width = closedNotchWidth()
|
|
return CGSize(width: width, height: height)
|
|
}
|
|
|
|
/// Height of the closed notch bar, determined by the user's chosen mode.
|
|
private func closedNotchHeight(using settings: AppSettings.DisplaySettings) -> CGFloat {
|
|
if hasNotch {
|
|
let mode = NotchHeightMode(rawValue: settings.notchHeightMode)
|
|
?? .matchRealNotchSize
|
|
switch mode {
|
|
case .matchRealNotchSize:
|
|
return safeAreaInsets.top
|
|
case .matchMenuBar:
|
|
return menuBarHeight()
|
|
case .custom:
|
|
return settings.notchHeight
|
|
}
|
|
} else {
|
|
let mode = NonNotchHeightMode(rawValue: settings.nonNotchHeightMode)
|
|
?? .matchMenuBar
|
|
switch mode {
|
|
case .matchMenuBar:
|
|
return menuBarHeight()
|
|
case .custom:
|
|
return settings.nonNotchHeight
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Width of the closed notch.
|
|
/// On notch screens, spans from one auxiliary top area to the other.
|
|
/// On non-notch screens, uses a reasonable fixed width.
|
|
private func closedNotchWidth() -> CGFloat {
|
|
if hasNotch {
|
|
if let topLeft = auxiliaryTopLeftArea,
|
|
let topRight = auxiliaryTopRightArea {
|
|
// The notch occupies the space between the two menu bar segments
|
|
return frame.width - topLeft.width - topRight.width + 4
|
|
}
|
|
// Fallback for older API — approximate from safe area
|
|
return 220
|
|
} else {
|
|
// Non-notch screens: a compact simulated notch
|
|
return 220
|
|
}
|
|
}
|
|
|
|
/// The effective menu bar height for this screen.
|
|
private func menuBarHeight() -> CGFloat {
|
|
return frame.maxY - visibleFrame.maxY
|
|
}
|
|
}
|