mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
add an option to see the title and artist name on non notch display
This commit is contained in:
parent
a6d5251842
commit
2524bc13c3
6 changed files with 92 additions and 53 deletions
|
|
@ -454,12 +454,12 @@
|
|||
AUTOMATION_APPLE_EVENTS = NO;
|
||||
CODE_SIGN_ENTITLEMENTS = DynamicIsland/DynamicIsland.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 12;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"DynamicIsland/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = 9Y64TRM77N;
|
||||
DEVELOPMENT_TEAM = RPC964BP8A;
|
||||
ENABLE_APP_SANDBOX = NO;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
|
|
@ -526,13 +526,13 @@
|
|||
AUTOMATION_APPLE_EVENTS = NO;
|
||||
CODE_SIGN_ENTITLEMENTS = DynamicIsland/DynamicIsland.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application";
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 12;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"DynamicIsland/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = 9Y64TRM77N;
|
||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = RPC964BP8A;
|
||||
ENABLE_APP_SANDBOX = NO;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
|
|
|
|||
|
|
@ -1139,6 +1139,14 @@ struct ContentView: View {
|
|||
.foregroundStyle(timerManager.timerColor)
|
||||
.padding(.trailing, 8)
|
||||
.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()
|
||||
|
|
|
|||
|
|
@ -5622,9 +5622,6 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Amazon Music" : {
|
||||
|
||||
},
|
||||
"Animation" : {
|
||||
"extractionState" : "stale",
|
||||
|
|
@ -38036,6 +38033,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Show song title and artist on non-notch displays" : {
|
||||
|
||||
},
|
||||
"Show start time after event begins" : {
|
||||
"localizations" : {
|
||||
|
|
|
|||
|
|
@ -46,9 +46,9 @@ struct MarqueeText: View {
|
|||
let minDuration: Double
|
||||
let frameWidth: CGFloat
|
||||
|
||||
@State private var animate = false
|
||||
@State private var textSize: CGSize = .zero
|
||||
@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) {
|
||||
_text = text
|
||||
|
|
@ -65,55 +65,80 @@ struct MarqueeText: View {
|
|||
}
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geometry in
|
||||
ZStack(alignment: .leading) {
|
||||
HStack(spacing: 20) {
|
||||
Text(text)
|
||||
Text(text)
|
||||
.opacity(needsScrolling ? 1 : 0)
|
||||
}
|
||||
.id(text)
|
||||
.font(font)
|
||||
.foregroundColor(textColor)
|
||||
.fixedSize(horizontal: true, vertical: false)
|
||||
.offset(x: self.animate ? offset : 0)
|
||||
.animation(
|
||||
self.animate ?
|
||||
.linear(duration: Double(textSize.width / 30))
|
||||
.delay(minDuration)
|
||||
.repeatForever(autoreverses: false) : .none,
|
||||
value: self.animate
|
||||
)
|
||||
.background(backgroundColor)
|
||||
.modifier(MeasureSizeModifier())
|
||||
.onPreferenceChange(SizePreferenceKey.self) { size in
|
||||
self.textSize = CGSize(width: size.width / 2, height: NSFont.preferredFont(forTextStyle: nsFont).pointSize)
|
||||
self.animate = false
|
||||
self.offset = 0
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
ZStack(alignment: .leading) {
|
||||
HStack(spacing: 20) {
|
||||
Text(text)
|
||||
Text(text)
|
||||
.opacity(needsScrolling ? 1 : 0)
|
||||
}
|
||||
.id(text)
|
||||
.font(font)
|
||||
.foregroundColor(textColor)
|
||||
.fixedSize(horizontal: true, vertical: false)
|
||||
.offset(x: offset)
|
||||
.background(backgroundColor)
|
||||
.modifier(MeasureSizeModifier())
|
||||
.onPreferenceChange(SizePreferenceKey.self) { size in
|
||||
self.textSize = CGSize(width: size.width / 2, height: NSFont.preferredFont(forTextStyle: nsFont).pointSize)
|
||||
resetAndStart()
|
||||
}
|
||||
.onChange(of: text) { _, _ in
|
||||
resetAndStart()
|
||||
}
|
||||
.onAppear {
|
||||
resetAndStart()
|
||||
}
|
||||
.onDisappear {
|
||||
isAnimating = false
|
||||
}
|
||||
.frame(width: frameWidth, alignment: .leading)
|
||||
.clipped()
|
||||
}
|
||||
.frame(width: frameWidth, alignment: .leading)
|
||||
.clipped()
|
||||
.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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
Text("Media playback live activity")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1291,4 +1291,5 @@ extension Defaults.Keys {
|
|||
let fallback = MusicAuxiliaryControl.alternative(excluding: current)
|
||||
Defaults[.musicAuxRightControl] = fallback
|
||||
}
|
||||
static let showSongMetadataInClosedNotch = Key<Bool>("showSongMetadataInClosedNotch", default: false)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue