mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Merge pull request #182 from Ebullioscopic/ui/glasseffect
Fix: Glass Effect on Lock Screen widgets and the Menu Bar button in Settings View
This commit is contained in:
commit
792e9af2b6
5 changed files with 246 additions and 89 deletions
|
|
@ -8068,6 +8068,10 @@
|
||||||
"comment" : "A toggle that enables or disables the use of HUDs.",
|
"comment" : "A toggle that enables or disables the use of HUDs.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
|
"Enable media panel blur" : {
|
||||||
|
"comment" : "A toggle switch that enables or disables the blur effect on the media panel.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Enable music live activity" : {
|
"Enable music live activity" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"ar" : {
|
"ar" : {
|
||||||
|
|
@ -10988,6 +10992,10 @@
|
||||||
},
|
},
|
||||||
"Lock Screen Integration" : {
|
"Lock Screen Integration" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Lock Screen Liquid Glass" : {
|
||||||
|
"comment" : "The title of the music panel on the lock screen.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
"Lock screen panel preview" : {
|
"Lock screen panel preview" : {
|
||||||
"comment" : "A description of the content displayed in the lock screen panel.",
|
"comment" : "A description of the content displayed in the lock screen panel.",
|
||||||
|
|
@ -14872,6 +14880,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Only applies when Material is set to Frosted Glass." : {
|
||||||
|
"comment" : "A description of the media panel blur setting, visible only when Material is set to Frosted Glass.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Only available when Material is set to Frosted Glass." : {
|
||||||
|
"comment" : "A description of a feature that is only available when a specific setting is enabled.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Only download icon" : {
|
"Only download icon" : {
|
||||||
"extractionState" : "stale",
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,11 @@ import SwiftUI
|
||||||
import Defaults
|
import Defaults
|
||||||
|
|
||||||
struct LockScreenMusicPanel: View {
|
struct LockScreenMusicPanel: View {
|
||||||
|
private struct GlassLogSnapshot: Equatable {
|
||||||
|
let style: LockScreenGlassStyle
|
||||||
|
let usesLiquidGlass: Bool
|
||||||
|
}
|
||||||
|
|
||||||
static let collapsedSize = CGSize(width: 420, height: 180)
|
static let collapsedSize = CGSize(width: 420, height: 180)
|
||||||
static let expandedSize = CGSize(width: 720, height: 340)
|
static let expandedSize = CGSize(width: 720, height: 340)
|
||||||
|
|
||||||
|
|
@ -23,6 +28,7 @@ struct LockScreenMusicPanel: View {
|
||||||
@State private var isExpanded = false
|
@State private var isExpanded = false
|
||||||
@State private var isVolumeSliderVisible = false
|
@State private var isVolumeSliderVisible = false
|
||||||
@State private var collapseWorkItem: DispatchWorkItem?
|
@State private var collapseWorkItem: DispatchWorkItem?
|
||||||
|
@State private var lastLoggedGlassSnapshot: GlassLogSnapshot?
|
||||||
@Default(.lockScreenGlassStyle) var lockScreenGlassStyle
|
@Default(.lockScreenGlassStyle) var lockScreenGlassStyle
|
||||||
@Default(.lockScreenShowAppIcon) var showAppIcon
|
@Default(.lockScreenShowAppIcon) var showAppIcon
|
||||||
@Default(.lockScreenPanelShowsBorder) var showPanelBorder
|
@Default(.lockScreenPanelShowsBorder) var showPanelBorder
|
||||||
|
|
@ -48,6 +54,10 @@ struct LockScreenMusicPanel: View {
|
||||||
private let collapsedLyricsExtraHeight: CGFloat = 64
|
private let collapsedLyricsExtraHeight: CGFloat = 64
|
||||||
private let expandedLyricsExtraHeight: CGFloat = 96
|
private let expandedLyricsExtraHeight: CGFloat = 96
|
||||||
|
|
||||||
|
private var shouldUseFrostedBlur: Bool {
|
||||||
|
enableBlur && !usesLiquidGlass
|
||||||
|
}
|
||||||
|
|
||||||
private var currentSize: CGSize {
|
private var currentSize: CGSize {
|
||||||
let base = isExpanded ? Self.expandedSize : Self.collapsedSize
|
let base = isExpanded ? Self.expandedSize : Self.collapsedSize
|
||||||
return CGSize(width: base.width, height: base.height + totalExtraHeight)
|
return CGSize(width: base.width, height: base.height + totalExtraHeight)
|
||||||
|
|
@ -74,45 +84,61 @@ struct LockScreenMusicPanel: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private var panelContent: some View {
|
private var panelContent: some View {
|
||||||
panelCore
|
ZStack(alignment: .topLeading) {
|
||||||
.frame(width: currentSize.width, height: currentSize.height)
|
panelBackgroundLayer
|
||||||
.animation(.spring(response: 0.48, dampingFraction: 0.82, blendDuration: 0.18), value: isExpanded)
|
panelForeground
|
||||||
.animation(.spring(response: 0.45, dampingFraction: 0.85, blendDuration: 0.18), value: shouldShowVolumeSlider)
|
}
|
||||||
.onAppear {
|
.frame(width: currentSize.width, height: currentSize.height, alignment: .topLeading)
|
||||||
sliderValue = musicManager.elapsedTime
|
.clipShape(RoundedRectangle(cornerRadius: panelCornerRadius, style: .continuous))
|
||||||
isActive = true
|
.overlay {
|
||||||
logPanelAppearance()
|
if showPanelBorder && !usesLiquidGlass {
|
||||||
updatePanelSize(animated: false)
|
RoundedRectangle(cornerRadius: panelCornerRadius)
|
||||||
routeManager.refreshDevices()
|
.stroke(Color.white.opacity(0.35), lineWidth: 1.4)
|
||||||
}
|
}
|
||||||
.onDisappear {
|
}
|
||||||
isActive = false
|
.shadow(color: Color.black.opacity(0.3), radius: 20, x: 0, y: 10)
|
||||||
cancelCollapseTimer()
|
.contentShape(RoundedRectangle(cornerRadius: panelCornerRadius, style: .continuous))
|
||||||
isVolumeSliderVisible = false
|
.animation(.spring(response: 0.48, dampingFraction: 0.82, blendDuration: 0.18), value: isExpanded)
|
||||||
}
|
.animation(.spring(response: 0.45, dampingFraction: 0.85, blendDuration: 0.18), value: shouldShowVolumeSlider)
|
||||||
.onChange(of: isExpanded) { _, expanded in
|
.onAppear {
|
||||||
updatePanelSize()
|
sliderValue = musicManager.elapsedTime
|
||||||
}
|
isActive = true
|
||||||
.onChange(of: showMediaOutputControl) { _, enabled in
|
logPanelAppearance()
|
||||||
if !enabled {
|
updatePanelSize(animated: false)
|
||||||
withAnimation(.spring(response: 0.45, dampingFraction: 0.85)) {
|
routeManager.refreshDevices()
|
||||||
isVolumeSliderVisible = false
|
logGlassState(reason: "Panel appeared")
|
||||||
}
|
}
|
||||||
}
|
.onDisappear {
|
||||||
updatePanelSize()
|
isActive = false
|
||||||
}
|
cancelCollapseTimer()
|
||||||
.onChange(of: enableLyrics) { _, _ in
|
isVolumeSliderVisible = false
|
||||||
|
}
|
||||||
|
.onChange(of: isExpanded) { _, expanded in
|
||||||
|
updatePanelSize()
|
||||||
|
}
|
||||||
|
.onChange(of: showMediaOutputControl) { _, enabled in
|
||||||
|
if !enabled {
|
||||||
withAnimation(.spring(response: 0.45, dampingFraction: 0.85)) {
|
withAnimation(.spring(response: 0.45, dampingFraction: 0.85)) {
|
||||||
updatePanelSize()
|
isVolumeSliderVisible = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.scaleEffect(animator.isPresented ? 1 : 0.9, anchor: .center)
|
updatePanelSize()
|
||||||
.opacity(animator.isPresented ? 1 : 0)
|
}
|
||||||
.animation(.spring(response: 0.52, dampingFraction: 0.8), value: animator.isPresented)
|
.onChange(of: enableLyrics) { _, _ in
|
||||||
|
withAnimation(.spring(response: 0.45, dampingFraction: 0.85)) {
|
||||||
|
updatePanelSize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onChange(of: lockScreenGlassStyle) { _, _ in
|
||||||
|
logGlassState(reason: "Glass style updated")
|
||||||
|
}
|
||||||
|
.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
|
@ViewBuilder
|
||||||
private var panelCore: some View {
|
private var panelForeground: some View {
|
||||||
Group {
|
Group {
|
||||||
if isExpanded {
|
if isExpanded {
|
||||||
expandedLayout
|
expandedLayout
|
||||||
|
|
@ -122,17 +148,7 @@ struct LockScreenMusicPanel: View {
|
||||||
}
|
}
|
||||||
.padding(.horizontal, isExpanded ? 24 : 20)
|
.padding(.horizontal, isExpanded ? 24 : 20)
|
||||||
.padding(.vertical, isExpanded ? 22 : 16)
|
.padding(.vertical, isExpanded ? 22 : 16)
|
||||||
.frame(width: currentSize.width, height: currentSize.height, alignment: .topLeading)
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||||
.background(panelBackground)
|
|
||||||
.clipShape(RoundedRectangle(cornerRadius: panelCornerRadius, style: .continuous))
|
|
||||||
.overlay {
|
|
||||||
if showPanelBorder {
|
|
||||||
RoundedRectangle(cornerRadius: panelCornerRadius)
|
|
||||||
.stroke(Color.white.opacity(0.35), lineWidth: 1.4)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.shadow(color: Color.black.opacity(0.3), radius: 20, x: 0, y: 10)
|
|
||||||
.contentShape(RoundedRectangle(cornerRadius: panelCornerRadius, style: .continuous))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private var collapsedLayout: some View {
|
private var collapsedLayout: some View {
|
||||||
|
|
@ -601,10 +617,17 @@ struct LockScreenMusicPanel: View {
|
||||||
.padding(.horizontal, 12)
|
.padding(.horizontal, 12)
|
||||||
.background(
|
.background(
|
||||||
RoundedRectangle(cornerRadius: isExpanded ? 16 : 12, style: .continuous)
|
RoundedRectangle(cornerRadius: isExpanded ? 16 : 12, style: .continuous)
|
||||||
.fill(Color.white.opacity(0.08))
|
.fill(sliderBackgroundFill)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var sliderBackgroundFill: Color {
|
||||||
|
if usesLiquidGlass {
|
||||||
|
return Color.white.opacity(0.05)
|
||||||
|
}
|
||||||
|
return Color.white.opacity(0.08)
|
||||||
|
}
|
||||||
|
|
||||||
private var shouldShowVolumeSlider: Bool {
|
private var shouldShowVolumeSlider: Bool {
|
||||||
showMediaOutputControl && isVolumeSliderVisible
|
showMediaOutputControl && isVolumeSliderVisible
|
||||||
}
|
}
|
||||||
|
|
@ -679,35 +702,40 @@ struct LockScreenMusicPanel: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var panelBackground: some View {
|
private var panelBackgroundLayer: some View {
|
||||||
if enableBlur {
|
if usesLiquidGlass {
|
||||||
if usesLiquidGlass {
|
liquidPanelBackdrop
|
||||||
liquidPanelBackground
|
} else if shouldUseFrostedBlur {
|
||||||
} else {
|
frostedPanelBackground
|
||||||
frostedPanelBackground
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
RoundedRectangle(cornerRadius: panelCornerRadius)
|
opaquePanelBackground
|
||||||
.fill(Color.black.opacity(0.45))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var liquidPanelBackground: some View {
|
private var liquidPanelBackdrop: some View {
|
||||||
if #available(macOS 26.0, *) {
|
if #available(macOS 26.0, *) {
|
||||||
RoundedRectangle(cornerRadius: panelCornerRadius)
|
GlassTextBackdrop(cornerRadius: panelCornerRadius)
|
||||||
.glassEffect(
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
.clear
|
.allowsHitTesting(false)
|
||||||
//.tint(Color.white.opacity(0.12))
|
.accessibilityHidden(true)
|
||||||
.interactive(),
|
|
||||||
in: .rect(cornerRadius: panelCornerRadius)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var frostedPanelBackground: some View {
|
private var frostedPanelBackground: some View {
|
||||||
RoundedRectangle(cornerRadius: panelCornerRadius)
|
RoundedRectangle(cornerRadius: panelCornerRadius)
|
||||||
.fill(.ultraThinMaterial)
|
.fill(.ultraThinMaterial)
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
.allowsHitTesting(false)
|
||||||
|
.accessibilityHidden(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var opaquePanelBackground: some View {
|
||||||
|
RoundedRectangle(cornerRadius: panelCornerRadius)
|
||||||
|
.fill(Color.black.opacity(0.45))
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
.allowsHitTesting(false)
|
||||||
|
.accessibilityHidden(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func albumArtImage(size: CGFloat, cornerRadius: CGFloat) -> some View {
|
private func albumArtImage(size: CGFloat, cornerRadius: CGFloat) -> some View {
|
||||||
|
|
@ -720,24 +748,13 @@ struct LockScreenMusicPanel: View {
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private func albumArtBackground(cornerRadius: CGFloat) -> some View {
|
private func albumArtBackground(cornerRadius: CGFloat) -> some View {
|
||||||
if enableBlur {
|
if usesLiquidGlass {
|
||||||
if usesLiquidGlass {
|
if #available(macOS 26.0, *) {
|
||||||
if #available(macOS 26.0, *) {
|
clearLiquidGlassSurface(cornerRadius: cornerRadius)
|
||||||
RoundedRectangle(cornerRadius: cornerRadius)
|
|
||||||
.glassEffect(
|
|
||||||
.clear
|
|
||||||
//.tint(Color.white.opacity(0.16))
|
|
||||||
.interactive(),
|
|
||||||
in: .rect(cornerRadius: cornerRadius)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
RoundedRectangle(cornerRadius: cornerRadius)
|
|
||||||
.fill(.ultraThinMaterial)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
RoundedRectangle(cornerRadius: cornerRadius)
|
|
||||||
.fill(.ultraThinMaterial)
|
|
||||||
}
|
}
|
||||||
|
} else if shouldUseFrostedBlur {
|
||||||
|
RoundedRectangle(cornerRadius: cornerRadius)
|
||||||
|
.fill(.ultraThinMaterial)
|
||||||
} else {
|
} else {
|
||||||
RoundedRectangle(cornerRadius: cornerRadius)
|
RoundedRectangle(cornerRadius: cornerRadius)
|
||||||
.fill(Color.black.opacity(0.35))
|
.fill(Color.black.opacity(0.35))
|
||||||
|
|
@ -762,6 +779,50 @@ struct LockScreenMusicPanel: View {
|
||||||
isExpanded ? 18 : 12
|
isExpanded ? 18 : 12
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@available(macOS 26.0, *)
|
||||||
|
private func clearLiquidGlassSurface(cornerRadius: CGFloat) -> some View {
|
||||||
|
RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)
|
||||||
|
.fill(.clear)
|
||||||
|
.glassEffect(
|
||||||
|
.clear.interactive(),
|
||||||
|
in: .rect(cornerRadius: cornerRadius)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func logGlassState(reason: String) {
|
||||||
|
let snapshot = GlassLogSnapshot(style: lockScreenGlassStyle, usesLiquidGlass: usesLiquidGlass)
|
||||||
|
guard snapshot != lastLoggedGlassSnapshot else { return }
|
||||||
|
lastLoggedGlassSnapshot = snapshot
|
||||||
|
|
||||||
|
struct ComponentState {
|
||||||
|
let name: String
|
||||||
|
let isLiquid: Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
let states = [
|
||||||
|
ComponentState(name: "Panel Shell", isLiquid: usesLiquidGlass),
|
||||||
|
ComponentState(name: "Control Capsules", isLiquid: usesLiquidGlass),
|
||||||
|
ComponentState(name: "Volume Slider", isLiquid: usesLiquidGlass),
|
||||||
|
ComponentState(name: "Album Art Plate", isLiquid: usesLiquidGlass)
|
||||||
|
]
|
||||||
|
|
||||||
|
let componentSummary = states.map { entry -> String in
|
||||||
|
let mode = entry.isLiquid ? "Liquid" : "Frosted"
|
||||||
|
return "\(entry.name)=\(mode)"
|
||||||
|
}.joined(separator: ", ")
|
||||||
|
|
||||||
|
print("[LockScreenMusicPanel] \(reason) – style=\(lockScreenGlassStyle.rawValue), usesLiquidGlass=\(usesLiquidGlass), components[\(componentSummary)], macOS \(currentOSVersionDescription())")
|
||||||
|
|
||||||
|
if lockScreenGlassStyle == .liquid && !usesLiquidGlass {
|
||||||
|
print("[LockScreenMusicPanel] Liquid Glass requested but unavailable on this macOS build. Falling back to frosted visuals.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func currentOSVersionDescription() -> String {
|
||||||
|
let version = ProcessInfo.processInfo.operatingSystemVersion
|
||||||
|
return "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"
|
||||||
|
}
|
||||||
|
|
||||||
private func logPanelAppearance(event: String = "✅ View appeared") {
|
private func logPanelAppearance(event: String = "✅ View appeared") {
|
||||||
let formatter = DateFormatter()
|
let formatter = DateFormatter()
|
||||||
formatter.dateFormat = "HH:mm:ss.SSS"
|
formatter.dateFormat = "HH:mm:ss.SSS"
|
||||||
|
|
@ -889,3 +950,25 @@ private struct PanelControlButton: View {
|
||||||
case counterClockwise
|
case counterClockwise
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@available(macOS 26.0, *)
|
||||||
|
private struct GlassTextBackdrop: View {
|
||||||
|
let cornerRadius: CGFloat
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
GeometryReader { proxy in
|
||||||
|
let dynamicFontSize = max(min(proxy.size.width, proxy.size.height) / 8, 42)
|
||||||
|
|
||||||
|
Text("Lock Screen Liquid Glass")
|
||||||
|
.font(.system(size: dynamicFontSize, weight: .semibold, design: .rounded))
|
||||||
|
.foregroundStyle(Color.clear)
|
||||||
|
.frame(width: proxy.size.width, height: proxy.size.height)
|
||||||
|
.glassEffect(
|
||||||
|
.clear.interactive(),
|
||||||
|
in: .rect(cornerRadius: cornerRadius)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.allowsHitTesting(false)
|
||||||
|
.accessibilityHidden(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -301,12 +301,7 @@ struct SettingsView: View {
|
||||||
}
|
}
|
||||||
.navigationSplitViewStyle(.balanced)
|
.navigationSplitViewStyle(.balanced)
|
||||||
.toolbar(removing: .sidebarToggle)
|
.toolbar(removing: .sidebarToggle)
|
||||||
.toolbar {
|
.toolbar { toolbarSpacingShim }
|
||||||
Button("") {} // Empty label, does nothing
|
|
||||||
.controlSize(.extraLarge)
|
|
||||||
.opacity(0) // Invisible, but reserves space for a consistent look between tabs
|
|
||||||
.disabled(true)
|
|
||||||
}
|
|
||||||
.environmentObject(highlightCoordinator)
|
.environmentObject(highlightCoordinator)
|
||||||
.formStyle(.grouped)
|
.formStyle(.grouped)
|
||||||
.frame(width: 700)
|
.frame(width: 700)
|
||||||
|
|
@ -343,6 +338,28 @@ struct SettingsView: View {
|
||||||
availableTabs.contains(selectedTab) ? selectedTab : (availableTabs.first ?? .general)
|
availableTabs.contains(selectedTab) ? selectedTab : (availableTabs.first ?? .general)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ToolbarContentBuilder
|
||||||
|
private var toolbarSpacingShim: some ToolbarContent {
|
||||||
|
if #available(macOS 26.0, *) {
|
||||||
|
ToolbarItem(placement: .primaryAction) {
|
||||||
|
toolbarSpacerView
|
||||||
|
}
|
||||||
|
.sharedBackgroundVisibility(.hidden)
|
||||||
|
} else {
|
||||||
|
ToolbarItem(placement: .primaryAction) {
|
||||||
|
toolbarSpacerView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
private var toolbarSpacerView: some View {
|
||||||
|
Color.clear
|
||||||
|
.frame(width: 96, height: 32)
|
||||||
|
.allowsHitTesting(false)
|
||||||
|
.accessibilityHidden(true)
|
||||||
|
}
|
||||||
|
|
||||||
private var filteredTabs: [SettingsTab] {
|
private var filteredTabs: [SettingsTab] {
|
||||||
tabsMatchingSearch(searchText)
|
tabsMatchingSearch(searchText)
|
||||||
}
|
}
|
||||||
|
|
@ -1364,6 +1381,7 @@ struct Media: View {
|
||||||
@Default(.musicControlWindowEnabled) private var musicControlWindowEnabled
|
@Default(.musicControlWindowEnabled) private var musicControlWindowEnabled
|
||||||
@Default(.enableLockScreenMediaWidget) private var enableLockScreenMediaWidget
|
@Default(.enableLockScreenMediaWidget) private var enableLockScreenMediaWidget
|
||||||
@Default(.showSneakPeekOnTrackChange) private var showSneakPeekOnTrackChange
|
@Default(.showSneakPeekOnTrackChange) private var showSneakPeekOnTrackChange
|
||||||
|
@Default(.lockScreenGlassStyle) private var lockScreenGlassStyle
|
||||||
|
|
||||||
private func highlightID(_ title: String) -> String {
|
private func highlightID(_ title: String) -> String {
|
||||||
SettingsTab.media.highlightID(for: title)
|
SettingsTab.media.highlightID(for: title)
|
||||||
|
|
@ -1493,8 +1511,15 @@ struct Media: View {
|
||||||
.disabled(!enableLockScreenMediaWidget)
|
.disabled(!enableLockScreenMediaWidget)
|
||||||
Defaults.Toggle("Show panel border", key: .lockScreenPanelShowsBorder)
|
Defaults.Toggle("Show panel border", key: .lockScreenPanelShowsBorder)
|
||||||
.disabled(!enableLockScreenMediaWidget)
|
.disabled(!enableLockScreenMediaWidget)
|
||||||
Defaults.Toggle("Enable media panel blur", key: .lockScreenPanelUsesBlur)
|
if lockScreenGlassStyle == .frosted {
|
||||||
.disabled(!enableLockScreenMediaWidget)
|
Defaults.Toggle("Enable media panel blur", key: .lockScreenPanelUsesBlur)
|
||||||
|
.disabled(!enableLockScreenMediaWidget)
|
||||||
|
.settingsHighlight(id: highlightID("Enable media panel blur"))
|
||||||
|
} else {
|
||||||
|
unavailableBlurRow
|
||||||
|
.opacity(enableLockScreenMediaWidget ? 1 : 0.5)
|
||||||
|
.settingsHighlight(id: highlightID("Enable media panel blur"))
|
||||||
|
}
|
||||||
} header: {
|
} header: {
|
||||||
Text("Lock Screen Integration")
|
Text("Lock Screen Integration")
|
||||||
} footer: {
|
} footer: {
|
||||||
|
|
@ -1525,6 +1550,17 @@ struct Media: View {
|
||||||
return MediaControllerType.allCases
|
return MediaControllerType.allCases
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var unavailableBlurRow: some View {
|
||||||
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
|
Text("Enable media panel blur")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
Text("Only applies when Material is set to Frosted Glass.")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CalendarSettings: View {
|
struct CalendarSettings: View {
|
||||||
|
|
@ -2506,9 +2542,15 @@ struct LockScreenSettings: View {
|
||||||
Defaults.Toggle("Show panel border", key: .lockScreenPanelShowsBorder)
|
Defaults.Toggle("Show panel border", key: .lockScreenPanelShowsBorder)
|
||||||
.disabled(!enableLockScreenMediaWidget)
|
.disabled(!enableLockScreenMediaWidget)
|
||||||
.settingsHighlight(id: highlightID("Show panel border"))
|
.settingsHighlight(id: highlightID("Show panel border"))
|
||||||
Defaults.Toggle("Enable media panel blur", key: .lockScreenPanelUsesBlur)
|
if lockScreenGlassStyle == .frosted {
|
||||||
.disabled(!enableLockScreenMediaWidget)
|
Defaults.Toggle("Enable media panel blur", key: .lockScreenPanelUsesBlur)
|
||||||
.settingsHighlight(id: highlightID("Enable media panel blur"))
|
.disabled(!enableLockScreenMediaWidget)
|
||||||
|
.settingsHighlight(id: highlightID("Enable media panel blur"))
|
||||||
|
} else {
|
||||||
|
blurSettingUnavailableRow
|
||||||
|
.opacity(enableLockScreenMediaWidget ? 1 : 0.5)
|
||||||
|
.settingsHighlight(id: highlightID("Enable media panel blur"))
|
||||||
|
}
|
||||||
} header: {
|
} header: {
|
||||||
Text("Media Panel")
|
Text("Media Panel")
|
||||||
} footer: {
|
} footer: {
|
||||||
|
|
@ -2633,6 +2675,19 @@ struct LockScreenSettings: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension LockScreenSettings {
|
||||||
|
private var blurSettingUnavailableRow: some View {
|
||||||
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
|
Text("Enable media panel blur")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
Text("Only available when Material is set to Frosted Glass.")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private struct LockScreenPositioningControls: View {
|
private struct LockScreenPositioningControls: View {
|
||||||
@Default(.lockScreenWeatherVerticalOffset) private var weatherOffset
|
@Default(.lockScreenWeatherVerticalOffset) private var weatherOffset
|
||||||
@Default(.lockScreenMusicVerticalOffset) private var musicOffset
|
@Default(.lockScreenMusicVerticalOffset) private var musicOffset
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,7 @@ class LockScreenPanelManager {
|
||||||
content.wantsLayer = true
|
content.wantsLayer = true
|
||||||
content.layer?.masksToBounds = true
|
content.layer?.masksToBounds = true
|
||||||
content.layer?.cornerRadius = collapsedPanelCornerRadius
|
content.layer?.cornerRadius = collapsedPanelCornerRadius
|
||||||
|
content.layer?.backgroundColor = NSColor.clear.cgColor
|
||||||
}
|
}
|
||||||
|
|
||||||
if !hasDelegated {
|
if !hasDelegated {
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,8 @@ Atoll builds upon the work of several open-source projects and draws inspiration
|
||||||
- [**Open Meteo**](https://open-meteo.com) - weather apis for the lock screen widgets
|
- [**Open Meteo**](https://open-meteo.com) - weather apis for the lock screen widgets
|
||||||
|
|
||||||
- [**SkyLightWindow**](https://github.com/Lakr233/SkyLightWindow) - window rendering for Lock Screen Widgets
|
- [**SkyLightWindow**](https://github.com/Lakr233/SkyLightWindow) - window rendering for Lock Screen Widgets
|
||||||
|
|
||||||
|
- Wick - Thanks Nate for allowing us to replicate the iOS like Timer design for the Lock Screen Widget
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
||||||
<a href="https://github.com/Ebullioscopic/Atoll/graphs/contributors">
|
<a href="https://github.com/Ebullioscopic/Atoll/graphs/contributors">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue