From 9e2b0fa09555e65998476ac5838a3bb9f9c9b0b8 Mon Sep 17 00:00:00 2001 From: Arkadiusz Fal Date: Tue, 7 Jul 2026 22:16:09 +0200 Subject: [PATCH] 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 --- Yattee/Views/Home/HomeSettingsView.swift | 1 + Yattee/Views/Home/HomeShortcutStyleView.swift | 1 + .../Views/RemoteControl/RemoteControlView.swift | 1 + Yattee/Views/Settings/ContributorsView.swift | 1 + Yattee/Views/Settings/EditSourceView.swift | 2 ++ Yattee/Views/Settings/ImportPlaylistsView.swift | 1 + .../Settings/ImportSubscriptionsView.swift | 1 + .../Views/Settings/LegacyDataImportView.swift | 1 + Yattee/Views/Settings/LogViewerView.swift | 1 + Yattee/Views/Settings/MacOSSettings.swift | 17 +++++++++++++++++ .../ButtonConfigurationView.swift | 1 + .../CenterControlsSettingsView.swift | 1 + .../PlayerControls/MiniPlayerEditorView.swift | 2 ++ .../PillButtonConfigurationView.swift | 1 + .../PlayerControlsSettingsView.swift | 1 + .../PlayerControls/PlayerPillEditorView.swift | 1 + .../PlayerControls/PresetSelectorView.swift | 1 + .../ProgressBarSettingsView.swift | 1 + .../PlayerControls/SectionEditorView.swift | 1 + Yattee/Views/Settings/SettingsView.swift | 3 +++ Yattee/Views/Settings/SidebarSettingsView.swift | 1 + .../Settings/TranslationContributorsView.swift | 1 + 22 files changed, 42 insertions(+) diff --git a/Yattee/Views/Home/HomeSettingsView.swift b/Yattee/Views/Home/HomeSettingsView.swift index 0dac031e..23da1bba 100644 --- a/Yattee/Views/Home/HomeSettingsView.swift +++ b/Yattee/Views/Home/HomeSettingsView.swift @@ -70,6 +70,7 @@ struct HomeSettingsView: View { ) } #endif + .opaqueSettingsFormBackground() #if !os(tvOS) .navigationTitle(String(localized: "home.settings.title")) #endif diff --git a/Yattee/Views/Home/HomeShortcutStyleView.swift b/Yattee/Views/Home/HomeShortcutStyleView.swift index c21b7505..eb7924d4 100644 --- a/Yattee/Views/Home/HomeShortcutStyleView.swift +++ b/Yattee/Views/Home/HomeShortcutStyleView.swift @@ -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) diff --git a/Yattee/Views/RemoteControl/RemoteControlView.swift b/Yattee/Views/RemoteControl/RemoteControlView.swift index d60081dd..1c9d82f1 100644 --- a/Yattee/Views/RemoteControl/RemoteControlView.swift +++ b/Yattee/Views/RemoteControl/RemoteControlView.swift @@ -163,6 +163,7 @@ struct RemoteControlView: View { #if os(tvOS) .safeAreaInset(edge: .leading) { tvOSSidebar } #else + .opaqueWindowBackground() .navigationTitle(device.name) #endif #if os(iOS) diff --git a/Yattee/Views/Settings/ContributorsView.swift b/Yattee/Views/Settings/ContributorsView.swift index 34b2c78c..687d1712 100644 --- a/Yattee/Views/Settings/ContributorsView.swift +++ b/Yattee/Views/Settings/ContributorsView.swift @@ -18,6 +18,7 @@ struct ContributorsView: View { var body: some View { content + .opaqueWindowBackground() .navigationTitle(String(localized: "settings.contributors.title")) #if os(iOS) .navigationBarTitleDisplayMode(.inline) diff --git a/Yattee/Views/Settings/EditSourceView.swift b/Yattee/Views/Settings/EditSourceView.swift index a78d404e..a3e804af 100644 --- a/Yattee/Views/Settings/EditSourceView.swift +++ b/Yattee/Views/Settings/EditSourceView.swift @@ -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, diff --git a/Yattee/Views/Settings/ImportPlaylistsView.swift b/Yattee/Views/Settings/ImportPlaylistsView.swift index d6bca809..2c8acd6b 100644 --- a/Yattee/Views/Settings/ImportPlaylistsView.swift +++ b/Yattee/Views/Settings/ImportPlaylistsView.swift @@ -163,6 +163,7 @@ struct ImportPlaylistsView: View { .accessibilityIdentifier(AccessibilityID.row(playlist.id.playlistID)) } } + .opaqueSettingsFormBackground() .accessibilityIdentifier(AccessibilityID.list) } diff --git a/Yattee/Views/Settings/ImportSubscriptionsView.swift b/Yattee/Views/Settings/ImportSubscriptionsView.swift index 6b81de1c..1a58148c 100644 --- a/Yattee/Views/Settings/ImportSubscriptionsView.swift +++ b/Yattee/Views/Settings/ImportSubscriptionsView.swift @@ -133,6 +133,7 @@ struct ImportSubscriptionsView: View { .accessibilityIdentifier(AccessibilityID.row(channel.id.channelID)) } } + .opaqueSettingsFormBackground() .accessibilityIdentifier(AccessibilityID.list) } diff --git a/Yattee/Views/Settings/LegacyDataImportView.swift b/Yattee/Views/Settings/LegacyDataImportView.swift index 3561913a..fdca1ff6 100644 --- a/Yattee/Views/Settings/LegacyDataImportView.swift +++ b/Yattee/Views/Settings/LegacyDataImportView.swift @@ -46,6 +46,7 @@ struct LegacyAccountsImportView: View { #if os(iOS) .navigationBarTitleDisplayMode(.inline) #endif + .opaqueSettingsFormBackground() .toolbar { #if !os(tvOS) if showsDoneButton { diff --git a/Yattee/Views/Settings/LogViewerView.swift b/Yattee/Views/Settings/LogViewerView.swift index dbf0c22e..f2684dd2 100644 --- a/Yattee/Views/Settings/LogViewerView.swift +++ b/Yattee/Views/Settings/LogViewerView.swift @@ -25,6 +25,7 @@ struct LogViewerView: View { // Log list logList } + .opaqueSettingsFormBackground() .navigationTitle(String(localized: "settings.advanced.logs.title")) #if os(iOS) .navigationBarTitleDisplayMode(.inline) diff --git a/Yattee/Views/Settings/MacOSSettings.swift b/Yattee/Views/Settings/MacOSSettings.swift index 6e330608..01f26460 100644 --- a/Yattee/Views/Settings/MacOSSettings.swift +++ b/Yattee/Views/Settings/MacOSSettings.swift @@ -29,6 +29,7 @@ struct SettingsFormContainer: View { .padding(.vertical, 8) .frame(maxWidth: .infinity, alignment: .leading) } + .opaqueWindowBackground() #else Form { content() @@ -37,6 +38,22 @@ struct SettingsFormContainer: 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, diff --git a/Yattee/Views/Settings/PlayerControls/ButtonConfigurationView.swift b/Yattee/Views/Settings/PlayerControls/ButtonConfigurationView.swift index 3129cf98..cd98ca48 100644 --- a/Yattee/Views/Settings/PlayerControls/ButtonConfigurationView.swift +++ b/Yattee/Views/Settings/PlayerControls/ButtonConfigurationView.swift @@ -77,6 +77,7 @@ struct ButtonConfigurationView: View { #elseif os(macOS) .formStyle(.grouped) #endif + .opaqueSettingsFormBackground() .onAppear { syncFromConfiguration(config) } diff --git a/Yattee/Views/Settings/PlayerControls/CenterControlsSettingsView.swift b/Yattee/Views/Settings/PlayerControls/CenterControlsSettingsView.swift index 7eff93a0..5de301f2 100644 --- a/Yattee/Views/Settings/PlayerControls/CenterControlsSettingsView.swift +++ b/Yattee/Views/Settings/PlayerControls/CenterControlsSettingsView.swift @@ -281,6 +281,7 @@ struct CenterControlsSettingsView: View { #else .navigationTitle(String(localized: "settings.playerControls.centerControls")) #endif + .opaqueSettingsFormBackground() #if os(iOS) .navigationBarTitleDisplayMode(.inline) #endif diff --git a/Yattee/Views/Settings/PlayerControls/MiniPlayerEditorView.swift b/Yattee/Views/Settings/PlayerControls/MiniPlayerEditorView.swift index 719ae5eb..a0268ffc 100644 --- a/Yattee/Views/Settings/PlayerControls/MiniPlayerEditorView.swift +++ b/Yattee/Views/Settings/PlayerControls/MiniPlayerEditorView.swift @@ -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) } diff --git a/Yattee/Views/Settings/PlayerControls/PillButtonConfigurationView.swift b/Yattee/Views/Settings/PlayerControls/PillButtonConfigurationView.swift index 00a603c8..466b27ee 100644 --- a/Yattee/Views/Settings/PlayerControls/PillButtonConfigurationView.swift +++ b/Yattee/Views/Settings/PlayerControls/PillButtonConfigurationView.swift @@ -33,6 +33,7 @@ struct PillButtonConfigurationView: View { #if os(iOS) .navigationBarTitleDisplayMode(.inline) #endif + .opaqueSettingsFormBackground() .onAppear { syncFromConfiguration(config) } diff --git a/Yattee/Views/Settings/PlayerControls/PlayerControlsSettingsView.swift b/Yattee/Views/Settings/PlayerControls/PlayerControlsSettingsView.swift index 971f3a59..9aa6844a 100644 --- a/Yattee/Views/Settings/PlayerControls/PlayerControlsSettingsView.swift +++ b/Yattee/Views/Settings/PlayerControls/PlayerControlsSettingsView.swift @@ -100,6 +100,7 @@ private struct PlayerControlsSettingsContent: View { #if os(macOS) .formStyle(.grouped) #endif + .opaqueSettingsFormBackground() .id(refreshID) .alert( String(localized: "settings.playerControls.error"), diff --git a/Yattee/Views/Settings/PlayerControls/PlayerPillEditorView.swift b/Yattee/Views/Settings/PlayerControls/PlayerPillEditorView.swift index 7ef0bce3..583fad8c 100644 --- a/Yattee/Views/Settings/PlayerControls/PlayerPillEditorView.swift +++ b/Yattee/Views/Settings/PlayerControls/PlayerPillEditorView.swift @@ -28,6 +28,7 @@ struct PlayerPillEditorView: View { #if os(iOS) .navigationBarTitleDisplayMode(.inline) #endif + .opaqueSettingsFormBackground() .onAppear { syncLocalState() } diff --git a/Yattee/Views/Settings/PlayerControls/PresetSelectorView.swift b/Yattee/Views/Settings/PlayerControls/PresetSelectorView.swift index 250a2563..f722ae00 100644 --- a/Yattee/Views/Settings/PlayerControls/PresetSelectorView.swift +++ b/Yattee/Views/Settings/PlayerControls/PresetSelectorView.swift @@ -63,6 +63,7 @@ struct PresetSelectorView: View { var body: some View { presetList + .opaqueSettingsFormBackground() .navigationTitle(String(localized: "settings.playerControls.presets")) #if os(iOS) .navigationBarTitleDisplayMode(.inline) diff --git a/Yattee/Views/Settings/PlayerControls/ProgressBarSettingsView.swift b/Yattee/Views/Settings/PlayerControls/ProgressBarSettingsView.swift index fd813d96..9044b296 100644 --- a/Yattee/Views/Settings/PlayerControls/ProgressBarSettingsView.swift +++ b/Yattee/Views/Settings/PlayerControls/ProgressBarSettingsView.swift @@ -32,6 +32,7 @@ struct ProgressBarSettingsView: View { #elseif os(macOS) .formStyle(.grouped) #endif + .opaqueSettingsFormBackground() .onAppear { loadSettings() } diff --git a/Yattee/Views/Settings/PlayerControls/SectionEditorView.swift b/Yattee/Views/Settings/PlayerControls/SectionEditorView.swift index a409dcfb..69e40060 100644 --- a/Yattee/Views/Settings/PlayerControls/SectionEditorView.swift +++ b/Yattee/Views/Settings/PlayerControls/SectionEditorView.swift @@ -146,6 +146,7 @@ struct SectionEditorView: View { #if os(iOS) .navigationBarTitleDisplayMode(.inline) #endif + .opaqueSettingsFormBackground() .toolbar { #if os(iOS) EditButton() diff --git a/Yattee/Views/Settings/SettingsView.swift b/Yattee/Views/Settings/SettingsView.swift index a8ed218e..04f690c6 100644 --- a/Yattee/Views/Settings/SettingsView.swift +++ b/Yattee/Views/Settings/SettingsView.swift @@ -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() } } } diff --git a/Yattee/Views/Settings/SidebarSettingsView.swift b/Yattee/Views/Settings/SidebarSettingsView.swift index 5737c72c..59b5c76a 100644 --- a/Yattee/Views/Settings/SidebarSettingsView.swift +++ b/Yattee/Views/Settings/SidebarSettingsView.swift @@ -148,6 +148,7 @@ struct SidebarSettingsView: View { #if os(macOS) .listStyle(.inset) #endif + .opaqueSettingsFormBackground() #if !os(tvOS) .navigationTitle(String(localized: "settings.sidebar.title")) #endif diff --git a/Yattee/Views/Settings/TranslationContributorsView.swift b/Yattee/Views/Settings/TranslationContributorsView.swift index bcab52a8..e5499b33 100644 --- a/Yattee/Views/Settings/TranslationContributorsView.swift +++ b/Yattee/Views/Settings/TranslationContributorsView.swift @@ -13,6 +13,7 @@ struct TranslationContributorsView: View { var body: some View { content + .opaqueWindowBackground() .navigationTitle(String(localized: "settings.translators.title")) #if os(iOS) .navigationBarTitleDisplayMode(.inline)