From 2d2ac570cdd56b8e350585391f6a44e09bd08e1a Mon Sep 17 00:00:00 2001 From: Hariharan Mudaliar Date: Wed, 19 Nov 2025 22:47:18 +0530 Subject: [PATCH] Update Metal Enabler and Smoothened Animations --- DynamicIsland.xcodeproj/project.pbxproj | 2 + .../LockScreen/LockScreenMusicPanel.swift | 8 ++++ .../managers/LockScreenPanelManager.swift | 37 +++++++++++++++---- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/DynamicIsland.xcodeproj/project.pbxproj b/DynamicIsland.xcodeproj/project.pbxproj index ea84660..087636a 100644 --- a/DynamicIsland.xcodeproj/project.pbxproj +++ b/DynamicIsland.xcodeproj/project.pbxproj @@ -412,6 +412,7 @@ INFOPLIST_KEY_CFBundleDisplayName = Atoll; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; INFOPLIST_KEY_LSUIElement = YES; + INFOPLIST_KEY_MetalCaptureEnabled = YES; INFOPLIST_KEY_NSAppleEventsUsageDescription = "Dynamic Island uses AppleScripts to control Spotify and Apple Music"; INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "Dynamic Island needs Bluetooth access to detect when audio devices connect and display their battery status in the HUD."; INFOPLIST_KEY_NSCalendarsFullAccessUsageDescription = "Show calender events inside the notch"; @@ -479,6 +480,7 @@ INFOPLIST_KEY_CFBundleDisplayName = Atoll; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; INFOPLIST_KEY_LSUIElement = YES; + INFOPLIST_KEY_MetalCaptureEnabled = YES; INFOPLIST_KEY_NSAppleEventsUsageDescription = "Dynamic Island uses AppleScripts to control Spotify and Apple Music"; INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "Dynamic Island needs Bluetooth access to detect when audio devices connect and display their battery status in the HUD."; INFOPLIST_KEY_NSCalendarsFullAccessUsageDescription = "Show calender events inside the notch"; diff --git a/DynamicIsland/components/LockScreen/LockScreenMusicPanel.swift b/DynamicIsland/components/LockScreen/LockScreenMusicPanel.swift index 952870d..78889bc 100644 --- a/DynamicIsland/components/LockScreen/LockScreenMusicPanel.swift +++ b/DynamicIsland/components/LockScreen/LockScreenMusicPanel.swift @@ -15,6 +15,7 @@ struct LockScreenMusicPanel: View { @ObservedObject var musicManager = MusicManager.shared @ObservedObject private var routeManager = AudioRouteManager.shared @StateObject private var volumeModel = MediaOutputVolumeViewModel() + @ObservedObject private var animator: LockScreenPanelAnimator @State private var sliderValue: Double = 0 @State private var dragging: Bool = false @State private var isActive = true @@ -30,6 +31,10 @@ struct LockScreenMusicPanel: View { @Default(.musicAuxLeftControl) private var leftAuxControl @Default(.musicAuxRightControl) private var rightAuxControl @Default(.enableLyrics) private var enableLyrics + + init(animator: LockScreenPanelAnimator) { + _animator = ObservedObject(wrappedValue: animator) + } private let collapsedPanelCornerRadius: CGFloat = 28 private let expandedPanelCornerRadius: CGFloat = 52 @@ -100,6 +105,9 @@ struct LockScreenMusicPanel: View { updatePanelSize() } } + .scaleEffect(animator.isPresented ? 1 : 0.9, anchor: .center) + .opacity(animator.isPresented ? 1 : 0) + .animation(.spring(response: 0.52, dampingFraction: 0.8), value: animator.isPresented) } @ViewBuilder diff --git a/DynamicIsland/managers/LockScreenPanelManager.swift b/DynamicIsland/managers/LockScreenPanelManager.swift index 439d205..4703304 100644 --- a/DynamicIsland/managers/LockScreenPanelManager.swift +++ b/DynamicIsland/managers/LockScreenPanelManager.swift @@ -11,6 +11,11 @@ import SkyLightWindow import Defaults import QuartzCore +@MainActor +final class LockScreenPanelAnimator: ObservableObject { + @Published var isPresented: Bool = false +} + @MainActor class LockScreenPanelManager { static let shared = LockScreenPanelManager() @@ -21,6 +26,8 @@ class LockScreenPanelManager { private let collapsedPanelCornerRadius: CGFloat = 28 private let expandedPanelCornerRadius: CGFloat = 52 private(set) var latestFrame: NSRect? + private let panelAnimator = LockScreenPanelAnimator() + private var hideTask: Task? private init() { print("[\(timestamp())] LockScreenPanelManager: initialized") @@ -81,7 +88,10 @@ class LockScreenPanelManager { window.setFrame(targetFrame, display: true) latestFrame = targetFrame - let hosting = NSHostingView(rootView: LockScreenMusicPanel()) + hideTask?.cancel() + panelAnimator.isPresented = false + + let hosting = NSHostingView(rootView: LockScreenMusicPanel(animator: panelAnimator)) hosting.frame = NSRect(origin: .zero, size: targetFrame.size) window.contentView = hosting @@ -100,6 +110,11 @@ class LockScreenPanelManager { // Keep the window alive and simply order it out on unlock to avoid SkyLight crashes. window.orderFrontRegardless() + DispatchQueue.main.async { [weak self] in + guard let self else { return } + self.panelAnimator.isPresented = true + } + print("[\(timestamp())] LockScreenPanelManager: panel visible") } @@ -142,16 +157,24 @@ class LockScreenPanelManager { func hidePanel() { print("[\(timestamp())] LockScreenPanelManager: hidePanel") + panelAnimator.isPresented = false + hideTask?.cancel() + guard let window = panelWindow else { print("LockScreenPanelManager: no panel to hide") + latestFrame = nil return } - window.orderOut(nil) - window.contentView = nil - - latestFrame = nil - - print("[\(timestamp())] LockScreenPanelManager: panel hidden") + hideTask = Task { [weak self, weak window] in + try? await Task.sleep(for: .milliseconds(360)) + guard let self else { return } + await MainActor.run { + window?.orderOut(nil) + window?.contentView = nil + self.latestFrame = nil + print("[\(self.timestamp())] LockScreenPanelManager: panel hidden") + } + } } }