Extend opaque background to settings and other pushed pages on macOS

The NavigationStack-level background wasn't enough: macOS draws its own
translucent material behind pushed navigation pages, and Form/List
containers draw a translucent scroll background on top of anything
placed behind them, so several pages kept the wallpaper-tinted look.

Bake the opaque background into SettingsFormContainer (covers all pages
built on it, pushed or not) and add opaqueSettingsFormBackground(),
which hides the container's scroll background before applying the
opaque one, to every macOS-reachable Form/List settings page: player
controls and its sub-editors, sidebar settings, edit source, legacy
data import, subscription/playlist import, customize home and its
shortcut style page, and the log viewer. Contributors, translators and
the remote control device page get the plain opaque background.

Claude-Session: https://claude.ai/code/session_0154KH8RAVAvm6iVanhmoW8o
This commit is contained in:
Arkadiusz Fal
2026-07-07 22:16:09 +02:00
parent 8d4f472348
commit 9e2b0fa095
22 changed files with 42 additions and 0 deletions

View File

@@ -70,6 +70,7 @@ struct HomeSettingsView: View {
)
}
#endif
.opaqueSettingsFormBackground()
#if !os(tvOS)
.navigationTitle(String(localized: "home.settings.title"))
#endif

View File

@@ -110,6 +110,7 @@ struct HomeShortcutStyleView: View {
#if os(macOS)
.listStyle(.inset)
#endif
.opaqueSettingsFormBackground()
.navigationTitle(String(localized: "home.settings.shortcuts.style"))
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)

View File

@@ -163,6 +163,7 @@ struct RemoteControlView: View {
#if os(tvOS)
.safeAreaInset(edge: .leading) { tvOSSidebar }
#else
.opaqueWindowBackground()
.navigationTitle(device.name)
#endif
#if os(iOS)

View File

@@ -18,6 +18,7 @@ struct ContributorsView: View {
var body: some View {
content
.opaqueWindowBackground()
.navigationTitle(String(localized: "settings.contributors.title"))
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)

View File

@@ -328,6 +328,7 @@ private struct EditRemoteServerContent: View {
#if os(macOS)
.formStyle(.grouped)
#endif
.opaqueSettingsFormBackground()
.confirmationDialog(
String(localized: "sources.delete.confirmation.single \(instance.displayName)"),
isPresented: $showingDeleteConfirmation,
@@ -696,6 +697,7 @@ private struct EditFileSourceContent: View {
#if os(macOS)
.formStyle(.grouped)
#endif
.opaqueSettingsFormBackground()
.confirmationDialog(
String(localized: "sources.delete.confirmation.single \(source.name)"),
isPresented: $showingDeleteConfirmation,

View File

@@ -163,6 +163,7 @@ struct ImportPlaylistsView: View {
.accessibilityIdentifier(AccessibilityID.row(playlist.id.playlistID))
}
}
.opaqueSettingsFormBackground()
.accessibilityIdentifier(AccessibilityID.list)
}

View File

@@ -133,6 +133,7 @@ struct ImportSubscriptionsView: View {
.accessibilityIdentifier(AccessibilityID.row(channel.id.channelID))
}
}
.opaqueSettingsFormBackground()
.accessibilityIdentifier(AccessibilityID.list)
}

View File

@@ -46,6 +46,7 @@ struct LegacyAccountsImportView: View {
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)
#endif
.opaqueSettingsFormBackground()
.toolbar {
#if !os(tvOS)
if showsDoneButton {

View File

@@ -25,6 +25,7 @@ struct LogViewerView: View {
// Log list
logList
}
.opaqueSettingsFormBackground()
.navigationTitle(String(localized: "settings.advanced.logs.title"))
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)

View File

@@ -29,6 +29,7 @@ struct SettingsFormContainer<Content: View>: View {
.padding(.vertical, 8)
.frame(maxWidth: .infinity, alignment: .leading)
}
.opaqueWindowBackground()
#else
Form {
content()
@@ -37,6 +38,22 @@ struct SettingsFormContainer<Content: View>: View {
}
}
extension View {
/// Unified background for settings pages whose root is a `Form` or `List`.
///
/// On macOS those containers draw their own translucent scroll background
/// (wallpaper-tinted) on top of any background placed behind them, so it
/// has to be hidden before the opaque window background can show through.
/// No-op on iOS/tvOS.
func opaqueSettingsFormBackground() -> some View {
#if os(macOS)
return scrollContentBackground(.hidden).opaqueWindowBackground()
#else
return self
#endif
}
}
/// A settings section with header and optional footer.
///
/// - On macOS: renders an uppercase `.subheadline` header, a top divider,

View File

@@ -77,6 +77,7 @@ struct ButtonConfigurationView: View {
#elseif os(macOS)
.formStyle(.grouped)
#endif
.opaqueSettingsFormBackground()
.onAppear {
syncFromConfiguration(config)
}

View File

@@ -281,6 +281,7 @@ struct CenterControlsSettingsView: View {
#else
.navigationTitle(String(localized: "settings.playerControls.centerControls"))
#endif
.opaqueSettingsFormBackground()
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)
#endif

View File

@@ -33,6 +33,7 @@ struct MiniPlayerEditorView: View {
#elseif os(macOS)
.formStyle(.grouped)
#endif
.opaqueSettingsFormBackground()
.onAppear {
syncLocalState()
}
@@ -293,6 +294,7 @@ struct MiniPlayerButtonConfigurationView: View {
#elseif os(macOS)
.formStyle(.grouped)
#endif
.opaqueSettingsFormBackground()
.onAppear {
syncFromConfiguration(config)
}

View File

@@ -33,6 +33,7 @@ struct PillButtonConfigurationView: View {
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)
#endif
.opaqueSettingsFormBackground()
.onAppear {
syncFromConfiguration(config)
}

View File

@@ -100,6 +100,7 @@ private struct PlayerControlsSettingsContent: View {
#if os(macOS)
.formStyle(.grouped)
#endif
.opaqueSettingsFormBackground()
.id(refreshID)
.alert(
String(localized: "settings.playerControls.error"),

View File

@@ -28,6 +28,7 @@ struct PlayerPillEditorView: View {
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)
#endif
.opaqueSettingsFormBackground()
.onAppear {
syncLocalState()
}

View File

@@ -63,6 +63,7 @@ struct PresetSelectorView: View {
var body: some View {
presetList
.opaqueSettingsFormBackground()
.navigationTitle(String(localized: "settings.playerControls.presets"))
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)

View File

@@ -32,6 +32,7 @@ struct ProgressBarSettingsView: View {
#elseif os(macOS)
.formStyle(.grouped)
#endif
.opaqueSettingsFormBackground()
.onAppear {
loadSettings()
}

View File

@@ -146,6 +146,7 @@ struct SectionEditorView: View {
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)
#endif
.opaqueSettingsFormBackground()
.toolbar {
#if os(iOS)
EditButton()

View File

@@ -79,6 +79,9 @@ struct SettingsView: View {
}
}
}
// Covers all section pages and their pushed sub-pages, which
// otherwise sit on the translucent wallpaper-tinted material.
.opaqueWindowBackground()
}
}
}

View File

@@ -148,6 +148,7 @@ struct SidebarSettingsView: View {
#if os(macOS)
.listStyle(.inset)
#endif
.opaqueSettingsFormBackground()
#if !os(tvOS)
.navigationTitle(String(localized: "settings.sidebar.title"))
#endif

View File

@@ -13,6 +13,7 @@ struct TranslationContributorsView: View {
var body: some View {
content
.opaqueWindowBackground()
.navigationTitle(String(localized: "settings.translators.title"))
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)