From 3bba53c58327f872dc351d2597e70da3b2f9962b Mon Sep 17 00:00:00 2001 From: Hariharan Mudaliar Date: Wed, 26 Nov 2025 23:23:49 +0530 Subject: [PATCH] UIFix: Window Shadows and Notch Shadows --- DynamicIsland/ContentView.swift | 45 ++++++++++++++----- DynamicIsland/DynamicIslandApp.swift | 6 ++- .../models/DynamicIslandViewModel.swift | 24 ++++++++-- DynamicIsland/sizing/matters.swift | 10 +++++ 4 files changed, 68 insertions(+), 17 deletions(-) diff --git a/DynamicIsland/ContentView.swift b/DynamicIsland/ContentView.swift index d554847..e070a19 100644 --- a/DynamicIsland/ContentView.swift +++ b/DynamicIsland/ContentView.swift @@ -126,6 +126,20 @@ struct ContentView: View { } return cornerRadiusInsets } + + private var currentShadowPadding: CGFloat { + notchShadowPaddingValue(isMinimalistic: enableMinimalisticUI) + } + + private var currentNotchShape: NotchShape { + let topRadius = (vm.notchState == .open && Defaults[.cornerRadiusScaling]) + ? activeCornerRadiusInsets.opened.top + : activeCornerRadiusInsets.closed.top + let bottomRadius = (vm.notchState == .open && Defaults[.cornerRadiusScaling]) + ? activeCornerRadiusInsets.opened.bottom + : activeCornerRadiusInsets.closed.bottom + return NotchShape(topCornerRadius: topRadius, bottomCornerRadius: bottomRadius) + } var body: some View { let interactionsEnabled = !lockScreenManager.isLocked @@ -139,16 +153,20 @@ struct ContentView: View { ) .padding([.horizontal, .bottom], vm.notchState == .open ? 12 : 0) .background(.black) - .mask { - ((vm.notchState == .open) && Defaults[.cornerRadiusScaling]) - ? NotchShape(topCornerRadius: activeCornerRadiusInsets.opened.top, bottomCornerRadius: activeCornerRadiusInsets.opened.bottom) - .drawingGroup() - : NotchShape(topCornerRadius: activeCornerRadiusInsets.closed.top, bottomCornerRadius: activeCornerRadiusInsets.closed.bottom) - .drawingGroup() - } - .padding(.bottom, vm.notchState == .open && Defaults[.extendHoverArea] ? 0 : (vm.effectiveClosedNotchHeight == 0) - ? zeroHeightHoverPadding - : 0 + .clipShape(currentNotchShape) + .compositingGroup() + .shadow( + color: ((vm.notchState == .open || isHovering) && Defaults[.enableShadow]) + ? .black.opacity(0.6) + : .clear, + radius: Defaults[.cornerRadiusScaling] ? 10 : 5 + ) + .padding(.bottom, + currentShadowPadding + ( + vm.notchState == .open && Defaults[.extendHoverArea] + ? 0 + : (vm.effectiveClosedNotchHeight == 0 ? zeroHeightHoverPadding : 0) + ) ) mainLayout @@ -280,7 +298,11 @@ struct ContentView: View { // .keyboardShortcut("E", modifiers: .command) } } - .frame(maxWidth: dynamicNotchSize.width, maxHeight: dynamicNotchSize.height, alignment: .top) + .frame( + maxWidth: dynamicNotchSize.width, + maxHeight: dynamicNotchSize.height + currentShadowPadding, + alignment: .top + ) .animation(dynamicNotchResizeAnimation, value: dynamicNotchSize) .animation(.easeInOut(duration: 0.4), value: coordinator.currentView) .environmentObject(privacyManager) @@ -291,7 +313,6 @@ struct ContentView: View { vm.shouldRecheckHover.toggle() } } - .shadow(color: ((vm.notchState == .open || isHovering) && Defaults[.enableShadow]) ? .black.opacity(0.6) : .clear, radius: Defaults[.cornerRadiusScaling] ? 10 : 5) .background(dragDetector) .environmentObject(vm) .environmentObject(webcamManager) diff --git a/DynamicIsland/DynamicIslandApp.swift b/DynamicIsland/DynamicIslandApp.swift index 3beb5b5..80944c4 100644 --- a/DynamicIsland/DynamicIslandApp.swift +++ b/DynamicIsland/DynamicIslandApp.swift @@ -278,11 +278,15 @@ class AppDelegate: NSObject, NSApplicationDelegate { // Use minimalistic or normal size based on settings, then adjust for stats layout let baseSize = Defaults[.enableMinimalisticUI] ? minimalisticOpenNotchSize : openNotchSize - return statsAdjustedNotchSize( + let adjustedContentSize = statsAdjustedNotchSize( from: baseSize, isStatsTabActive: coordinator.currentView == .stats, secondRowProgress: coordinator.statsSecondRowExpansion ) + return addShadowPadding( + to: adjustedContentSize, + isMinimalistic: Defaults[.enableMinimalisticUI] + ) } func ensureWindowSize(_ size: CGSize, animated: Bool, force: Bool = false) { diff --git a/DynamicIsland/models/DynamicIslandViewModel.swift b/DynamicIsland/models/DynamicIslandViewModel.swift index 8dd164d..7d7937c 100644 --- a/DynamicIsland/models/DynamicIslandViewModel.swift +++ b/DynamicIsland/models/DynamicIslandViewModel.swift @@ -84,7 +84,11 @@ class DynamicIslandViewModel: NSObject, ObservableObject { self.notchSize = updatedTarget } if let delegate = AppDelegate.shared { - delegate.ensureWindowSize(updatedTarget, animated: true, force: false) + delegate.ensureWindowSize( + addShadowPadding(to: updatedTarget, isMinimalistic: Defaults[.enableMinimalisticUI]), + animated: true, + force: false + ) } } .store(in: &cancellables) @@ -106,7 +110,11 @@ class DynamicIslandViewModel: NSObject, ObservableObject { self.notchSize = updatedTarget } if let delegate = AppDelegate.shared { - delegate.ensureWindowSize(updatedTarget, animated: true, force: false) + delegate.ensureWindowSize( + addShadowPadding(to: updatedTarget, isMinimalistic: Defaults[.enableMinimalisticUI]), + animated: true, + force: false + ) } } .store(in: &cancellables) @@ -123,7 +131,11 @@ class DynamicIslandViewModel: NSObject, ObservableObject { self.notchSize = updatedTarget } if let delegate = AppDelegate.shared { - delegate.ensureWindowSize(updatedTarget, animated: false, force: false) + delegate.ensureWindowSize( + addShadowPadding(to: updatedTarget, isMinimalistic: Defaults[.enableMinimalisticUI]), + animated: false, + force: false + ) } } .store(in: &cancellables) @@ -184,7 +196,11 @@ class DynamicIslandViewModel: NSObject, ObservableObject { let applyWindowResize: () -> Void = { guard let delegate = AppDelegate.shared else { return } - delegate.ensureWindowSize(targetSize, animated: false, force: true) + delegate.ensureWindowSize( + addShadowPadding(to: targetSize, isMinimalistic: Defaults[.enableMinimalisticUI]), + animated: false, + force: true + ) } if Thread.isMainThread { diff --git a/DynamicIsland/sizing/matters.swift b/DynamicIsland/sizing/matters.swift index 64f2723..9166a96 100644 --- a/DynamicIsland/sizing/matters.swift +++ b/DynamicIsland/sizing/matters.swift @@ -19,6 +19,8 @@ private let minimalisticBaseOpenNotchSize: CGSize = .init(width: 420, height: 18 private let minimalisticLyricsExtraHeight: CGFloat = 40 let statsSecondRowContentHeight: CGFloat = 120 let statsGridSpacingHeight: CGFloat = 12 +let notchShadowPaddingStandard: CGFloat = 18 +let notchShadowPaddingMinimalistic: CGFloat = 12 @MainActor var minimalisticOpenNotchSize: CGSize { @@ -71,6 +73,14 @@ func statsAdjustedNotchSize( return adjustedSize } +func notchShadowPaddingValue(isMinimalistic: Bool) -> CGFloat { + isMinimalistic ? notchShadowPaddingMinimalistic : notchShadowPaddingStandard +} + +func addShadowPadding(to size: CGSize, isMinimalistic: Bool) -> CGSize { + CGSize(width: size.width, height: size.height + notchShadowPaddingValue(isMinimalistic: isMinimalistic)) +} + enum MusicPlayerImageSizes { static let cornerRadiusInset: (opened: CGFloat, closed: CGFloat) = (opened: 13.0, closed: 4.0) static let size = (opened: CGSize(width: 90, height: 90), closed: CGSize(width: 20, height: 20))