Fully working

This commit is contained in:
Hariharan Mudaliar 2025-11-23 00:08:43 +05:30
commit 574a64ef34
2 changed files with 145 additions and 26 deletions

View file

@ -75,6 +75,10 @@ struct ContentView: View {
@State private var pendingMusicControlTask: Task<Void, Never>? @State private var pendingMusicControlTask: Task<Void, Never>?
@State private var musicControlHideTask: Task<Void, Never>? @State private var musicControlHideTask: Task<Void, Never>?
@State private var musicControlVisibilityDeadline: Date? @State private var musicControlVisibilityDeadline: Date?
@State private var isMusicControlWindowSuppressed = false
@State private var hasPendingMusicControlSync = false
@State private var pendingMusicControlForceRefresh = false
@State private var musicControlSuppressionTask: Task<Void, Never>?
@State private var haptics: Bool = false @State private var haptics: Bool = false
@ -85,6 +89,24 @@ struct ContentView: View {
@Default(.showNotHumanFace) var showNotHumanFace @Default(.showNotHumanFace) var showNotHumanFace
@Default(.useModernCloseAnimation) var useModernCloseAnimation @Default(.useModernCloseAnimation) var useModernCloseAnimation
@Default(.enableMinimalisticUI) var enableMinimalisticUI @Default(.enableMinimalisticUI) var enableMinimalisticUI
private static let musicControlLogFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm:ss.SSS"
return formatter
}()
private func logMusicControlEvent(_ message: String) {
#if DEBUG
let timestamp = Self.musicControlLogFormatter.string(from: Date())
print("[MusicControl] \(timestamp): \(message)")
#endif
}
private func requestMusicControlWindowSyncIfHidden(forceRefresh: Bool = false, delay: TimeInterval = 0) {
guard !isMusicControlWindowVisible else { return }
enqueueMusicControlWindowSync(forceRefresh: forceRefresh, delay: delay)
}
private var dynamicNotchResizeAnimation: Animation? { private var dynamicNotchResizeAnimation: Animation? {
if enableMinimalisticUI && reminderManager.activeWindowReminders.isEmpty == false { if enableMinimalisticUI && reminderManager.activeWindowReminders.isEmpty == false {
return nil return nil
@ -96,6 +118,7 @@ struct ContentView: View {
private let zeroHeightHoverPadding: CGFloat = 10 private let zeroHeightHoverPadding: CGFloat = 10
private let statsAdditionalRowHeight: CGFloat = 110 private let statsAdditionalRowHeight: CGFloat = 110
private let musicControlPauseGrace: TimeInterval = 10 private let musicControlPauseGrace: TimeInterval = 10
private let musicControlResumeDelay: TimeInterval = 0.24
// Use minimalistic corner radius ONLY when opened, keep normal when closed // Use minimalistic corner radius ONLY when opened, keep normal when closed
private var activeCornerRadiusInsets: (opened: (top: CGFloat, bottom: CGFloat), closed: (top: CGFloat, bottom: CGFloat)) { private var activeCornerRadiusInsets: (opened: (top: CGFloat, bottom: CGFloat), closed: (top: CGFloat, bottom: CGFloat)) {
@ -374,20 +397,23 @@ struct ContentView: View {
.environmentObject(vm) .environmentObject(vm)
.environmentObject(webcamManager) .environmentObject(webcamManager)
.onAppear { .onAppear {
isMusicControlWindowSuppressed = vm.notchState != .closed || lockScreenManager.isLocked
if musicManager.isPlaying || !musicManager.isPlayerIdle { if musicManager.isPlaying || !musicManager.isPlayerIdle {
clearMusicControlVisibilityDeadline() clearMusicControlVisibilityDeadline()
} }
if let deadline = musicControlVisibilityDeadline, Date() > deadline { if let deadline = musicControlVisibilityDeadline, Date() > deadline {
clearMusicControlVisibilityDeadline() clearMusicControlVisibilityDeadline()
} }
scheduleMusicControlWindowSync(forceRefresh: true) enqueueMusicControlWindowSync(forceRefresh: true)
} }
.onChange(of: vm.notchState) { _, state in .onChange(of: vm.notchState) { _, state in
if state == .open { if state == .open {
suppressMusicControlWindowUpdates()
cancelMusicControlWindowSync() cancelMusicControlWindowSync()
hideMusicControlWindow() hideMusicControlWindow()
} else { } else {
scheduleMusicControlWindowSync(forceRefresh: true, delay: 0.05) releaseMusicControlWindowUpdates(after: musicControlResumeDelay)
enqueueMusicControlWindowSync(forceRefresh: true, delay: 0.05)
} }
} }
.onChange(of: musicControlWindowEnabled) { _, enabled in .onChange(of: musicControlWindowEnabled) { _, enabled in
@ -395,20 +421,24 @@ struct ContentView: View {
if musicManager.isPlaying || !musicManager.isPlayerIdle { if musicManager.isPlaying || !musicManager.isPlayerIdle {
clearMusicControlVisibilityDeadline() clearMusicControlVisibilityDeadline()
} }
scheduleMusicControlWindowSync(forceRefresh: true) enqueueMusicControlWindowSync(forceRefresh: true)
} else { } else {
cancelMusicControlWindowSync() cancelMusicControlWindowSync()
hideMusicControlWindow() hideMusicControlWindow()
clearMusicControlVisibilityDeadline() clearMusicControlVisibilityDeadline()
hasPendingMusicControlSync = false
pendingMusicControlForceRefresh = false
} }
} }
.onChange(of: coordinator.musicLiveActivityEnabled) { _, enabled in .onChange(of: coordinator.musicLiveActivityEnabled) { _, enabled in
if enabled { if enabled {
scheduleMusicControlWindowSync(forceRefresh: true) enqueueMusicControlWindowSync(forceRefresh: true)
} else { } else {
cancelMusicControlWindowSync() cancelMusicControlWindowSync()
hideMusicControlWindow() hideMusicControlWindow()
clearMusicControlVisibilityDeadline() clearMusicControlVisibilityDeadline()
hasPendingMusicControlSync = false
pendingMusicControlForceRefresh = false
} }
} }
.onChange(of: vm.hideOnClosed) { _, hidden in .onChange(of: vm.hideOnClosed) { _, hidden in
@ -416,25 +446,27 @@ struct ContentView: View {
cancelMusicControlWindowSync() cancelMusicControlWindowSync()
hideMusicControlWindow() hideMusicControlWindow()
} else { } else {
scheduleMusicControlWindowSync(forceRefresh: true, delay: 0.05) enqueueMusicControlWindowSync(forceRefresh: true, delay: 0.05)
} }
} }
.onChange(of: lockScreenManager.isLocked) { _, locked in .onChange(of: lockScreenManager.isLocked) { _, locked in
if locked { if locked {
suppressMusicControlWindowUpdates()
cancelMusicControlWindowSync() cancelMusicControlWindowSync()
hideMusicControlWindow() hideMusicControlWindow()
} else { } else {
scheduleMusicControlWindowSync(forceRefresh: true, delay: 0.05) releaseMusicControlWindowUpdates(after: musicControlResumeDelay)
enqueueMusicControlWindowSync(forceRefresh: true, delay: 0.05)
} }
} }
.onChange(of: gestureProgress) { _, _ in .onChange(of: gestureProgress) { _, _ in
if shouldShowMusicControlWindow() { if shouldShowMusicControlWindow() {
scheduleMusicControlWindowSync(forceRefresh: true, delay: 0.05) enqueueMusicControlWindowSync(forceRefresh: true, delay: 0.05)
} }
} }
.onChange(of: isHovering) { _, hovering in .onChange(of: isHovering) { _, hovering in
if shouldShowMusicControlWindow() { if shouldShowMusicControlWindow() {
scheduleMusicControlWindowSync(forceRefresh: true, delay: hovering ? 0.05 : 0.12) enqueueMusicControlWindowSync(forceRefresh: true, delay: hovering ? 0.05 : 0.12)
} }
} }
.onChange(of: musicManager.isPlaying) { _, isPlaying in .onChange(of: musicManager.isPlaying) { _, isPlaying in
@ -445,12 +477,12 @@ struct ContentView: View {
} }
.onChange(of: vm.closedNotchSize) { _, _ in .onChange(of: vm.closedNotchSize) { _, _ in
if shouldShowMusicControlWindow() { if shouldShowMusicControlWindow() {
scheduleMusicControlWindowSync(forceRefresh: true) enqueueMusicControlWindowSync(forceRefresh: true)
} }
} }
.onChange(of: vm.effectiveClosedNotchHeight) { _, _ in .onChange(of: vm.effectiveClosedNotchHeight) { _, _ in
if shouldShowMusicControlWindow() { if shouldShowMusicControlWindow() {
scheduleMusicControlWindowSync(forceRefresh: true) enqueueMusicControlWindowSync(forceRefresh: true)
} }
} }
.onDisappear { .onDisappear {
@ -464,6 +496,7 @@ struct ContentView: View {
hideMusicControlWindow() hideMusicControlWindow()
cancelMusicControlVisibilityTimer() cancelMusicControlVisibilityTimer()
clearMusicControlVisibilityDeadline() clearMusicControlVisibilityDeadline()
musicControlSuppressionTask?.cancel()
} }
} }
@ -1005,9 +1038,7 @@ struct ContentView: View {
if isPlaying { if isPlaying {
clearMusicControlVisibilityDeadline() clearMusicControlVisibilityDeadline()
if vm.notchState == .closed { requestMusicControlWindowSyncIfHidden()
scheduleMusicControlWindowSync(forceRefresh: false)
}
} else { } else {
extendMusicControlVisibilityAfterPause() extendMusicControlVisibilityAfterPause()
} }
@ -1029,9 +1060,7 @@ struct ContentView: View {
let deadline = Date().addingTimeInterval(musicControlPauseGrace) let deadline = Date().addingTimeInterval(musicControlPauseGrace)
musicControlVisibilityDeadline = deadline musicControlVisibilityDeadline = deadline
scheduleMusicControlVisibilityCheck(deadline: deadline) scheduleMusicControlVisibilityCheck(deadline: deadline)
if vm.notchState == .closed { requestMusicControlWindowSyncIfHidden()
scheduleMusicControlWindowSync(forceRefresh: false)
}
} }
private func clearMusicControlVisibilityDeadline() { private func clearMusicControlVisibilityDeadline() {
@ -1044,7 +1073,7 @@ struct ContentView: View {
let interval = max(0, deadline.timeIntervalSinceNow) let interval = max(0, deadline.timeIntervalSinceNow)
musicControlHideTask = Task { [interval] in musicControlHideTask = Task.detached(priority: .background) { [interval] in
if interval > 0 { if interval > 0 {
let nanoseconds = UInt64(interval * 1_000_000_000) let nanoseconds = UInt64(interval * 1_000_000_000)
try? await Task.sleep(nanoseconds: nanoseconds) try? await Task.sleep(nanoseconds: nanoseconds)
@ -1057,9 +1086,7 @@ struct ContentView: View {
musicControlVisibilityDeadline = nil musicControlVisibilityDeadline = nil
} }
if vm.notchState == .closed { enqueueMusicControlWindowSync(forceRefresh: false)
scheduleMusicControlWindowSync(forceRefresh: false)
}
musicControlHideTask = nil musicControlHideTask = nil
} }
@ -1080,30 +1107,101 @@ struct ContentView: View {
return Date() <= deadline return Date() <= deadline
} }
private func suppressMusicControlWindowUpdates() {
isMusicControlWindowSuppressed = true
musicControlSuppressionTask?.cancel()
musicControlSuppressionTask = nil
}
private func releaseMusicControlWindowUpdates(after delay: TimeInterval) {
musicControlSuppressionTask?.cancel()
musicControlSuppressionTask = Task { [delay] in
if delay > 0 {
let nanoseconds = UInt64(delay * 1_000_000_000)
try? await Task.sleep(nanoseconds: nanoseconds)
}
guard !Task.isCancelled else { return }
await MainActor.run {
if vm.notchState == .closed && !lockScreenManager.isLocked {
isMusicControlWindowSuppressed = false
triggerPendingMusicControlSyncIfNeeded()
} else {
isMusicControlWindowSuppressed = true
}
musicControlSuppressionTask = nil
}
}
}
private func triggerPendingMusicControlSyncIfNeeded() {
guard hasPendingMusicControlSync else { return }
let shouldForce = pendingMusicControlForceRefresh
hasPendingMusicControlSync = false
pendingMusicControlForceRefresh = false
logMusicControlEvent("Flushing pending floating window sync (force: \(shouldForce))")
scheduleMusicControlWindowSync(forceRefresh: shouldForce, bypassSuppression: true)
}
private func shouldDeferMusicControlSync() -> Bool {
vm.notchState != .closed || lockScreenManager.isLocked || isMusicControlWindowSuppressed
}
private func enqueueMusicControlWindowSync(forceRefresh: Bool, delay: TimeInterval = 0) {
if shouldDeferMusicControlSync() {
hasPendingMusicControlSync = true
if forceRefresh {
pendingMusicControlForceRefresh = true
}
logMusicControlEvent("Queued floating window sync (force: \(forceRefresh)) while deferred")
return
}
logMusicControlEvent("Scheduling floating window sync (force: \(forceRefresh), delay: \(delay))")
scheduleMusicControlWindowSync(forceRefresh: forceRefresh, delay: delay)
}
private func shouldShowMusicControlWindow() -> Bool { private func shouldShowMusicControlWindow() -> Bool {
guard musicControlWindowEnabled, guard musicControlWindowEnabled,
coordinator.musicLiveActivityEnabled, coordinator.musicLiveActivityEnabled,
vm.notchState == .closed, vm.notchState == .closed,
!vm.hideOnClosed, !vm.hideOnClosed,
!lockScreenManager.isLocked else { !lockScreenManager.isLocked,
!isMusicControlWindowSuppressed else {
return false return false
} }
return musicControlVisibilityIsActive() return musicControlVisibilityIsActive()
} }
private func scheduleMusicControlWindowSync(forceRefresh: Bool, delay: TimeInterval = 0) { private func scheduleMusicControlWindowSync(forceRefresh: Bool, delay: TimeInterval = 0, bypassSuppression: Bool = false) {
#if os(macOS) #if os(macOS)
cancelMusicControlWindowSync() cancelMusicControlWindowSync()
guard shouldShowMusicControlWindow() else { guard shouldShowMusicControlWindow() else {
hasPendingMusicControlSync = false
pendingMusicControlForceRefresh = false
hideMusicControlWindow() hideMusicControlWindow()
return return
} }
if !bypassSuppression && (isMusicControlWindowSuppressed || lockScreenManager.isLocked) {
hasPendingMusicControlSync = true
if forceRefresh {
pendingMusicControlForceRefresh = true
}
return
}
hasPendingMusicControlSync = false
pendingMusicControlForceRefresh = false
let syncDelay = max(0, delay) let syncDelay = max(0, delay)
pendingMusicControlTask = Task { [forceRefresh, syncDelay] in pendingMusicControlTask = Task.detached(priority: .userInitiated) { [forceRefresh, syncDelay] in
if syncDelay > 0 { if syncDelay > 0 {
let nanoseconds = UInt64(syncDelay * 1_000_000_000) let nanoseconds = UInt64(syncDelay * 1_000_000_000)
try? await Task.sleep(nanoseconds: nanoseconds) try? await Task.sleep(nanoseconds: nanoseconds)
@ -1113,8 +1211,10 @@ struct ContentView: View {
await MainActor.run { await MainActor.run {
if shouldShowMusicControlWindow() { if shouldShowMusicControlWindow() {
logMusicControlEvent("Running floating window sync (force: \(forceRefresh))")
syncMusicControlWindow(forceRefresh: forceRefresh) syncMusicControlWindow(forceRefresh: forceRefresh)
} else { } else {
logMusicControlEvent("Skipping floating window sync (conditions changed)")
hideMusicControlWindow() hideMusicControlWindow()
} }

View file

@ -4,7 +4,7 @@ import SwiftUI
import SkyLightWindow import SkyLightWindow
import QuartzCore import QuartzCore
struct MusicControlWindowMetrics { struct MusicControlWindowMetrics: Equatable {
let notchHeight: CGFloat let notchHeight: CGFloat
let notchWidth: CGFloat let notchWidth: CGFloat
let rightWingWidth: CGFloat let rightWingWidth: CGFloat
@ -86,15 +86,23 @@ final class MusicControlWindowManager {
guard window != nil else { guard window != nil else {
return present(using: viewModel, metrics: metrics) return present(using: viewModel, metrics: metrics)
} }
if let lastMetrics, lastMetrics == metrics {
return true
}
return present(using: viewModel, metrics: metrics) return present(using: viewModel, metrics: metrics)
} }
func hide(animated: Bool = true) { func hide(animated: Bool = true, tearDown: Bool = true) {
guard let window else { return } guard let window else { return }
guard animated, window.alphaValue > 0.01 else { guard animated, window.alphaValue > 0.01 else {
window.orderOut(nil) window.orderOut(nil)
window.alphaValue = 0 window.alphaValue = 0
if tearDown {
tearDownHostingView()
}
return return
} }
@ -106,13 +114,24 @@ final class MusicControlWindowManager {
context.timingFunction = CAMediaTimingFunction(name: .easeIn) context.timingFunction = CAMediaTimingFunction(name: .easeIn)
window.animator().setFrame(retreatFrame, display: true) window.animator().setFrame(retreatFrame, display: true)
window.animator().alphaValue = 0 window.animator().alphaValue = 0
} completionHandler: { } completionHandler: { [weak self] in
window.setFrame(originalFrame, display: false) window.setFrame(originalFrame, display: false)
window.orderOut(nil) window.orderOut(nil)
window.alphaValue = 0 window.alphaValue = 0
if tearDown {
self?.tearDownHostingView()
}
} }
} }
private func tearDownHostingView() {
if let window {
window.contentView = nil
}
hostingView = nil
lastMetrics = nil
}
private func ensureHostingView(with overlay: MusicControlOverlay) -> NSHostingView<MusicControlOverlay> { private func ensureHostingView(with overlay: MusicControlOverlay) -> NSHostingView<MusicControlOverlay> {
if let hostingView { if let hostingView {
hostingView.rootView = overlay hostingView.rootView = overlay