add an option to see the title and artist name on non notch display

This commit is contained in:
delli 2026-04-29 16:44:31 +02:00
commit 2524bc13c3
6 changed files with 92 additions and 53 deletions

View file

@ -454,12 +454,12 @@
AUTOMATION_APPLE_EVENTS = NO; AUTOMATION_APPLE_EVENTS = NO;
CODE_SIGN_ENTITLEMENTS = DynamicIsland/DynamicIsland.entitlements; CODE_SIGN_ENTITLEMENTS = DynamicIsland/DynamicIsland.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 12; CURRENT_PROJECT_VERSION = 12;
DEVELOPMENT_ASSET_PATHS = "\"DynamicIsland/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"DynamicIsland/Preview Content\"";
DEVELOPMENT_TEAM = 9Y64TRM77N; DEVELOPMENT_TEAM = RPC964BP8A;
ENABLE_APP_SANDBOX = NO; ENABLE_APP_SANDBOX = NO;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@ -526,13 +526,13 @@
AUTOMATION_APPLE_EVENTS = NO; AUTOMATION_APPLE_EVENTS = NO;
CODE_SIGN_ENTITLEMENTS = DynamicIsland/DynamicIsland.entitlements; CODE_SIGN_ENTITLEMENTS = DynamicIsland/DynamicIsland.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 12; CURRENT_PROJECT_VERSION = 12;
DEVELOPMENT_ASSET_PATHS = "\"DynamicIsland/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"DynamicIsland/Preview Content\"";
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=macosx*]" = 9Y64TRM77N; "DEVELOPMENT_TEAM[sdk=macosx*]" = RPC964BP8A;
ENABLE_APP_SANDBOX = NO; ENABLE_APP_SANDBOX = NO;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;

View file

@ -1139,6 +1139,14 @@ struct ContentView: View {
.foregroundStyle(timerManager.timerColor) .foregroundStyle(timerManager.timerColor)
.padding(.trailing, 8) .padding(.trailing, 8)
.opacity((coordinator.expandingView.show && coordinator.expandingView.type == .timer && Defaults[.enableSneakPeek] && Defaults[.sneakPeekStyles] == .inline) ? 1 : 0) .opacity((coordinator.expandingView.show && coordinator.expandingView.type == .timer && Defaults[.enableSneakPeek] && Defaults[.sneakPeekStyles] == .inline) ? 1 : 0)
} else if Defaults[.showSongMetadataInClosedNotch] && isNonNotchScreen && !musicManager.songTitle.isEmpty {
MarqueeText(
.constant("\(musicManager.songTitle) • \(musicManager.artistName)"),
textColor: Defaults[.coloredSpectrogram] ? Color(nsColor: musicManager.avgColor) : Color.gray,
minDuration: 3,
frameWidth: max(0, effectiveCenterWidth - 16)
)
.padding(.horizontal, 8)
} }
} }
.clipped() .clipped()

View file

@ -5622,9 +5622,6 @@
} }
} }
} }
},
"Amazon Music" : {
}, },
"Animation" : { "Animation" : {
"extractionState" : "stale", "extractionState" : "stale",
@ -38036,6 +38033,9 @@
} }
} }
} }
},
"Show song title and artist on non-notch displays" : {
}, },
"Show start time after event begins" : { "Show start time after event begins" : {
"localizations" : { "localizations" : {

View file

@ -46,9 +46,9 @@ struct MarqueeText: View {
let minDuration: Double let minDuration: Double
let frameWidth: CGFloat let frameWidth: CGFloat
@State private var animate = false
@State private var textSize: CGSize = .zero @State private var textSize: CGSize = .zero
@State private var offset: CGFloat = 0 @State private var offset: CGFloat = 0
@State private var isAnimating: Bool = false
init(_ text: Binding<String>, font: Font = .body, nsFont: NSFont.TextStyle = .body, textColor: Color = .primary, backgroundColor: Color = .clear, minDuration: Double = 3.0, frameWidth: CGFloat = 200) { init(_ text: Binding<String>, font: Font = .body, nsFont: NSFont.TextStyle = .body, textColor: Color = .primary, backgroundColor: Color = .clear, minDuration: Double = 3.0, frameWidth: CGFloat = 200) {
_text = text _text = text
@ -65,55 +65,80 @@ struct MarqueeText: View {
} }
var body: some View { var body: some View {
GeometryReader { geometry in ZStack(alignment: .leading) {
ZStack(alignment: .leading) { HStack(spacing: 20) {
HStack(spacing: 20) { Text(text)
Text(text) Text(text)
Text(text) .opacity(needsScrolling ? 1 : 0)
.opacity(needsScrolling ? 1 : 0) }
} .id(text)
.id(text) .font(font)
.font(font) .foregroundColor(textColor)
.foregroundColor(textColor) .fixedSize(horizontal: true, vertical: false)
.fixedSize(horizontal: true, vertical: false) .offset(x: offset)
.offset(x: self.animate ? offset : 0) .background(backgroundColor)
.animation( .modifier(MeasureSizeModifier())
self.animate ? .onPreferenceChange(SizePreferenceKey.self) { size in
.linear(duration: Double(textSize.width / 30)) self.textSize = CGSize(width: size.width / 2, height: NSFont.preferredFont(forTextStyle: nsFont).pointSize)
.delay(minDuration) resetAndStart()
.repeatForever(autoreverses: false) : .none, }
value: self.animate .onChange(of: text) { _, _ in
) resetAndStart()
.background(backgroundColor) }
.modifier(MeasureSizeModifier()) .onAppear {
.onPreferenceChange(SizePreferenceKey.self) { size in resetAndStart()
self.textSize = CGSize(width: size.width / 2, height: NSFont.preferredFont(forTextStyle: nsFont).pointSize) }
self.animate = false .onDisappear {
self.offset = 0 isAnimating = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.02){
if needsScrolling {
self.animate = true
self.offset = -(textSize.width + 20)
}
}
}
.onChange(of: text) { _, _ in
// Reset animation when text changes
self.animate = false
self.offset = 0
DispatchQueue.main.asyncAfter(deadline: .now() + 0.02){
if needsScrolling {
self.animate = true
self.offset = -(textSize.width + 20)
}
}
}
} }
.frame(width: frameWidth, alignment: .leading)
.clipped()
} }
.frame(width: frameWidth, alignment: .leading)
.clipped()
.frame(height: textSize.height * 1.3) .frame(height: textSize.height * 1.3)
}
private func resetAndStart() {
isAnimating = false
withAnimation(.none) {
offset = 0
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
if needsScrolling {
isAnimating = true
startAnimationLoop()
}
}
}
private func startAnimationLoop() {
guard isAnimating && needsScrolling else { return }
// Duration based on speed (approx 30 pts per second)
let duration = Double(textSize.width / 30)
// 1. Initial/Restart Pause
DispatchQueue.main.asyncAfter(deadline: .now() + minDuration) {
guard isAnimating else { return }
// 2. Linear Move
withAnimation(.linear(duration: duration)) {
offset = -(textSize.width + 20) // Text width + spacing
}
// 3. Wait for move to finish
DispatchQueue.main.asyncAfter(deadline: .now() + duration) {
guard isAnimating else { return }
// 4. Snap back instantly
withAnimation(.none) {
offset = 0
}
// 5. Repeat
startAnimationLoop()
}
}
} }
} }

View file

@ -2633,6 +2633,11 @@ struct Media: View {
} }
} }
} }
Defaults.Toggle(key: .showSongMetadataInClosedNotch) {
Text("Show song title and artist on non-notch displays")
}
.settingsHighlight(id: highlightID("Show song title and artist in closed notch"))
} header: { } header: {
Text("Media playback live activity") Text("Media playback live activity")
} }

View file

@ -1291,4 +1291,5 @@ extension Defaults.Keys {
let fallback = MusicAuxiliaryControl.alternative(excluding: current) let fallback = MusicAuxiliaryControl.alternative(excluding: current)
Defaults[.musicAuxRightControl] = fallback Defaults[.musicAuxRightControl] = fallback
} }
static let showSongMetadataInClosedNotch = Key<Bool>("showSongMetadataInClosedNotch", default: false)
} }