Files
yattee/Yattee/Views/Settings/PlayerControls/ProgressBarSettingsView.swift
Arkadiusz Fal 9e2b0fa095 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
2026-07-07 22:16:09 +02:00

200 lines
6.3 KiB
Swift

//
// ProgressBarSettingsView.swift
// Yattee
//
// View for configuring progress bar appearance settings.
//
import SwiftUI
/// View for configuring progress bar settings.
struct ProgressBarSettingsView: View {
@Bindable var viewModel: PlayerControlsSettingsViewModel
@State private var playedColor: Color = .red
@State private var showChapters: Bool = true
@State private var showSponsorSegments: Bool = true
@State private var categorySettings: [String: SponsorBlockCategorySettings] = [:]
var body: some View {
Form {
appearanceSection
chaptersSection
sponsorSegmentsSection
if showSponsorSegments {
segmentColorsSection
}
}
.navigationTitle(String(localized: "settings.playerControls.progressBar"))
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)
#elseif os(macOS)
.formStyle(.grouped)
#endif
.opaqueSettingsFormBackground()
.onAppear {
loadSettings()
}
}
// MARK: - Appearance Section
private var appearanceSection: some View {
Section {
#if !os(tvOS)
ColorPicker(
String(localized: "settings.playerControls.progressBar.playedColor"),
selection: $playedColor,
supportsOpacity: false
)
.onChange(of: playedColor) { _, newValue in
viewModel.updateProgressBarSettingsSync { $0.playedColor = CodableColor(newValue) }
}
.disabled(!viewModel.canEditActivePreset)
#else
HStack {
Text(String(localized: "settings.playerControls.progressBar.playedColor"))
Spacer()
Circle()
.fill(playedColor)
.frame(width: 24, height: 24)
}
#endif
} header: {
Text(String(localized: "settings.playerControls.progressBar.appearance"))
}
}
// MARK: - Chapters Section
private var chaptersSection: some View {
Section {
Toggle(
String(localized: "settings.playerControls.progressBar.showChapters"),
isOn: $showChapters
)
.onChange(of: showChapters) { _, newValue in
viewModel.updateProgressBarSettingsSync { $0.showChapters = newValue }
}
.disabled(!viewModel.canEditActivePreset)
} header: {
Text(String(localized: "settings.playerControls.progressBar.chapters"))
} footer: {
Text(String(localized: "settings.playerControls.progressBar.chaptersFooter"))
}
}
// MARK: - Sponsor Segments Section
private var sponsorSegmentsSection: some View {
Section {
Toggle(
String(localized: "settings.playerControls.progressBar.showSponsorSegments"),
isOn: $showSponsorSegments
)
.onChange(of: showSponsorSegments) { _, newValue in
viewModel.updateProgressBarSettingsSync {
$0.sponsorBlockSettings.showSegments = newValue
}
}
.disabled(!viewModel.canEditActivePreset)
} header: {
Text(String(localized: "settings.playerControls.progressBar.sponsorSegments"))
}
}
// MARK: - Segment Colors Section
private var segmentColorsSection: some View {
Section {
ForEach(SponsorBlockCategory.allCases, id: \.rawValue) { category in
categoryRow(for: category)
}
} header: {
Text(String(localized: "settings.playerControls.progressBar.segmentColors"))
}
}
@ViewBuilder
private func categoryRow(for category: SponsorBlockCategory) -> some View {
let settings = categorySettings[category.rawValue]
?? SponsorBlockSegmentSettings.defaultCategorySettings[category.rawValue]
?? SponsorBlockCategorySettings()
let isVisible = Binding<Bool>(
get: { settings.isVisible },
set: { newValue in
updateCategorySettings(for: category) { $0.isVisible = newValue }
}
)
let color = Binding<Color>(
get: { settings.color.color },
set: { newValue in
updateCategorySettings(for: category) { $0.color = CodableColor(newValue) }
}
)
HStack {
Toggle(category.displayName, isOn: isVisible)
.disabled(!viewModel.canEditActivePreset)
Spacer()
#if !os(tvOS)
ColorPicker("", selection: color, supportsOpacity: false)
.labelsHidden()
.disabled(!viewModel.canEditActivePreset || !settings.isVisible)
#else
Circle()
.fill(color.wrappedValue)
.frame(width: 24, height: 24)
.opacity(settings.isVisible ? 1 : 0.4)
#endif
}
}
// MARK: - Helpers
private func loadSettings() {
let progressBarSettings = viewModel.progressBarSettings
playedColor = progressBarSettings.playedColor.color
showChapters = progressBarSettings.showChapters
showSponsorSegments = progressBarSettings.sponsorBlockSettings.showSegments
categorySettings = progressBarSettings.sponsorBlockSettings.categorySettings
}
private func updateCategorySettings(
for category: SponsorBlockCategory,
mutation: (inout SponsorBlockCategorySettings) -> Void
) {
var settings = categorySettings[category.rawValue]
?? SponsorBlockSegmentSettings.defaultCategorySettings[category.rawValue]
?? SponsorBlockCategorySettings()
mutation(&settings)
categorySettings[category.rawValue] = settings
viewModel.updateProgressBarSettingsSync {
$0.sponsorBlockSettings = $0.sponsorBlockSettings.withUpdatedSettings(
forKey: category.rawValue,
settings
)
}
}
}
// MARK: - Preview
#Preview {
NavigationStack {
ProgressBarSettingsView(
viewModel: PlayerControlsSettingsViewModel(
layoutService: PlayerControlsLayoutService(),
settingsManager: SettingsManager()
)
)
}
}