Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions boringNotch.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
1153BD982D9881F900979FB0 /* AppleScriptHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1153BD972D9881F900979FB0 /* AppleScriptHelper.swift */; };
1153BD9A2D98824300979FB0 /* SpotifyController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1153BD992D98824300979FB0 /* SpotifyController.swift */; };
1153BD9C2D98853B00979FB0 /* NowPlayingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1153BD9B2D98853B00979FB0 /* NowPlayingController.swift */; };
A1B2C3D4E5F60718293A4B5D /* NowPlayingStreamSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D4E5F60718293A4B5C /* NowPlayingStreamSupport.swift */; };
1153BDA72D99B22200979FB0 /* YouTubeMusicController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1153BDA62D99B22200979FB0 /* YouTubeMusicController.swift */; };
115C12EC2ED3D003009754CA /* OpenNotchOSD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 115C12EB2ED3D003009754CA /* OpenNotchOSD.swift */; };
1160F8D82DD98230006FBB94 /* NotchShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1160F8D72DD98230006FBB94 /* NotchShape.swift */; };
Expand Down Expand Up @@ -235,6 +236,7 @@
1153BD972D9881F900979FB0 /* AppleScriptHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleScriptHelper.swift; sourceTree = "<group>"; };
1153BD992D98824300979FB0 /* SpotifyController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpotifyController.swift; sourceTree = "<group>"; };
1153BD9B2D98853B00979FB0 /* NowPlayingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NowPlayingController.swift; sourceTree = "<group>"; };
A1B2C3D4E5F60718293A4B5C /* NowPlayingStreamSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NowPlayingStreamSupport.swift; sourceTree = "<group>"; };
1153BDA62D99B22200979FB0 /* YouTubeMusicController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YouTubeMusicController.swift; sourceTree = "<group>"; };
115C12EB2ED3D003009754CA /* OpenNotchOSD.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenNotchOSD.swift; sourceTree = "<group>"; };
1160F8D72DD98230006FBB94 /* NotchShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchShape.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -486,6 +488,7 @@
1153BD922D986E4300979FB0 /* AppleMusicController.swift */,
1153BD992D98824300979FB0 /* SpotifyController.swift */,
1153BD9B2D98853B00979FB0 /* NowPlayingController.swift */,
A1B2C3D4E5F60718293A4B5C /* NowPlayingStreamSupport.swift */,
);
path = MediaControllers;
sourceTree = "<group>";
Expand Down Expand Up @@ -1073,6 +1076,7 @@
14D570CB2C5F4B2C0011E668 /* BatteryStatusViewModel.swift in Sources */,
9A0887322C7A693000C160EA /* TabButton.swift in Sources */,
1153BD9C2D98853B00979FB0 /* NowPlayingController.swift in Sources */,
A1B2C3D4E5F60718293A4B5D /* NowPlayingStreamSupport.swift in Sources */,
11985BEF2F37E48900F81585 /* OSDIconView.swift in Sources */,
1194E9402EACC652009C82D6 /* Color+AccentColor.swift in Sources */,
B141C2412CA5F53F00AC8CC8 /* SparkleView.swift in Sources */,
Expand Down
83 changes: 76 additions & 7 deletions boringNotch/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ struct ContentView: View {

private let extendedHoverPadding: CGFloat = 30
private let zeroHeightHoverPadding: CGFloat = 10
private let nowPlayingFallbackNoticeWidth: CGFloat = 330

// MARK: - Corner Radius Scaling
private var cornerRadiusScaleFactor: CGFloat? {
Expand Down Expand Up @@ -88,7 +89,9 @@ struct ContentView: View {
private var computedChinWidth: CGFloat {
var chinWidth: CGFloat = vm.closedNotchSize.width

if coordinator.expandingView.type == .battery && coordinator.expandingView.show
if shouldDisplayNowPlayingFallbackNotice {
chinWidth = nowPlayingFallbackNoticeWidth
} else if coordinator.expandingView.type == .battery && coordinator.expandingView.show
&& vm.notchState == .closed && Defaults[.showPowerStatusNotifications]
{
chinWidth = 640
Expand All @@ -107,6 +110,21 @@ struct ContentView: View {
return chinWidth
}

private var shouldDisplayNowPlayingFallbackNotice: Bool {
guard musicManager.nowPlayingNotice != nil else { return false }

let selectedScreen = NSScreen.screen(withUUID: coordinator.selectedScreenUUID)
let targetScreenUUID = selectedScreen?.displayUUID ?? NSScreen.main?.displayUUID
let currentScreen = vm.screenUUID.flatMap { NSScreen.screen(withUUID: $0) }
let isConnected = vm.screenUUID == nil || currentScreen != nil
let isTargetDisplay = vm.screenUUID == nil || vm.screenUUID == targetScreenUUID

return isConnected
&& isTargetDisplay
&& vm.notchState == .closed
&& !isNotchHeightZero
}

// If the closed notch height is 0 (any display/setting), display a 10pt nearly-invisible notch
// instead of fully hiding it. This preserves layout while avoiding visual artifacts.
private var isNotchHeightZero: Bool { vm.effectiveClosedNotchHeight == 0 }
Expand Down Expand Up @@ -160,23 +178,23 @@ struct ContentView: View {
handleHover(hovering)
}
.onTapGesture {
if vm.notchState == .closed {
if vm.notchState == .closed && !shouldDisplayNowPlayingFallbackNotice {
doOpen()
}
}
.conditionalModifier(Defaults[.enableGestures]) { view in
.conditionalModifier(Defaults[.enableGestures] && !shouldDisplayNowPlayingFallbackNotice) { view in
view
.panGesture(direction: .down) { translation, phase in
handleDownGesture(translation: translation, phase: phase)
}
}
.conditionalModifier(Defaults[.closeGestureEnabled] && Defaults[.enableGestures]) { view in
.conditionalModifier(Defaults[.closeGestureEnabled] && Defaults[.enableGestures] && !shouldDisplayNowPlayingFallbackNotice) { view in
view
.panGesture(direction: .up) { translation, phase in
handleUpGesture(translation: translation, phase: phase)
}
}
.conditionalModifier(Defaults[.enableHorizontalMediaGestures] && Defaults[.enableGestures]) { view in
.conditionalModifier(Defaults[.enableHorizontalMediaGestures] && Defaults[.enableGestures] && !shouldDisplayNowPlayingFallbackNotice) { view in
view
.panGesture(direction: .left) { translation, phase in
handleNextTrackGesture(translation: translation, phase: phase)
Expand Down Expand Up @@ -300,7 +318,11 @@ struct ContentView: View {
.padding(.top, 40)
Spacer()
} else {
if coordinator.expandingView.type == .battery && coordinator.expandingView.show
if shouldDisplayNowPlayingFallbackNotice,
let notice = musicManager.nowPlayingNotice {
nowPlayingFallbackNotice(notice)
.transition(.opacity.combined(with: .scale(scale: 0.96, anchor: .top)))
} else if coordinator.expandingView.type == .battery && coordinator.expandingView.show
&& vm.notchState == .closed && Defaults[.showPowerStatusNotifications]
{
HStack(spacing: 0) {
Expand Down Expand Up @@ -427,6 +449,51 @@ struct ContentView: View {
.onDrop(of: [.fileURL, .url, .utf8PlainText, .plainText, .data], delegate: GeneralDropTargetDelegate(isTargeted: $dropInteraction.generalDropTargeting))
}

private func nowPlayingFallbackNotice(_ notice: NowPlayingFallbackNotice) -> some View {
HStack(spacing: 11) {
Image(systemName: "exclamationmark.triangle.fill")
.font(.system(size: 16, weight: .semibold))
.foregroundStyle(Color.orange)
.frame(width: 24, height: 24)
.accessibilityHidden(true)

VStack(alignment: .leading, spacing: 2) {
Text(notice.title)
.font(.subheadline.weight(.semibold))
.foregroundStyle(.white)

Text(notice.subtitle)
.font(.caption)
.foregroundStyle(.white.opacity(0.62))
}
.lineLimit(2)

Spacer(minLength: 5)
}
.padding(.horizontal, 14)
.padding(.vertical, 9)
.frame(width: nowPlayingFallbackNoticeWidth)
.frame(minHeight: 58)
.accessibilityElement(children: .combine)
.onAppear {
if musicManager.markNowPlayingNoticePresented(notice.id) {
announceNowPlayingFallbackNotice(notice)
}
}
}

private func announceNowPlayingFallbackNotice(_ notice: NowPlayingFallbackNotice) {
let announcement = "\(String(localized: notice.title)). \(String(localized: notice.subtitle))."
NSAccessibility.post(
element: NSApplication.shared,
notification: .announcementRequested,
userInfo: [
.announcement: announcement,
.priority: NSAccessibilityPriorityLevel.high.rawValue,
]
)
}

@ViewBuilder
func BoringFaceAnimation() -> some View {
HStack {
Expand Down Expand Up @@ -552,7 +619,7 @@ struct ContentView: View {
var dragDetector: some View {
@Bindable var dropInteraction = vm.dropInteraction

if Defaults[.boringShelf] && vm.notchState == .closed {
if Defaults[.boringShelf] && vm.notchState == .closed && !shouldDisplayNowPlayingFallbackNotice {
Color.clear
.frame(maxWidth: .infinity, maxHeight: .infinity)
.contentShape(Rectangle())
Expand Down Expand Up @@ -591,6 +658,7 @@ struct ContentView: View {
}

guard vm.notchState == .closed,
!shouldDisplayNowPlayingFallbackNotice,
!coordinator.shouldShowSneakPeek(on: vm.screenUUID),
Defaults[.openNotchOnHover] else { return }

Expand All @@ -601,6 +669,7 @@ struct ContentView: View {
await MainActor.run {
guard self.vm.notchState == .closed,
self.isHovering,
!self.shouldDisplayNowPlayingFallbackNotice,
!self.coordinator.shouldShowSneakPeek(on: self.vm.screenUUID) else { return }

self.doOpen()
Expand Down
Loading
Loading