Add glass background theme setting for macOS control bar

Rename the macOS "Control Bar Buttons" settings entry to "Control Bar"
and add an Auto/Light/Dark glass background picker on that page. The
choice is stored per-preset as GlobalLayoutSettings.controlBarTheme and
forces the bar's glass color scheme via glassBackground(colorScheme:).
This commit is contained in:
Arkadiusz Fal
2026-06-02 19:38:46 +02:00
parent 0d56dd02b7
commit 96fef3663f
5 changed files with 75 additions and 5 deletions

View File

@@ -1859,6 +1859,17 @@
}
}
},
"controls.theme.auto" : {
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Auto"
}
}
}
},
"controls.theme.dark" : {
"localizations" : {
"en" : {
@@ -13315,13 +13326,13 @@
}
}
},
"settings.playerControls.controlBarButtons" : {
"settings.playerControls.controlBar" : {
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Control Bar Buttons"
"value" : "Control Bar"
}
}
}
@@ -13396,6 +13407,17 @@
}
}
},
"settings.playerControls.glassBackground" : {
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Glass Background"
}
}
}
},
"settings.playerControls.import.error.emptyFile" : {
"localizations" : {
"en" : {

View File

@@ -411,6 +411,9 @@ struct GlobalLayoutSettings: Codable, Hashable, Sendable {
/// How volume is controlled during playback.
var volumeMode: VolumeMode
/// Forced appearance for the macOS control bar's glass background.
var controlBarTheme: ControlsTheme
/// Theme derived from style (for backwards compatibility).
var theme: ControlsTheme { style.theme }
@@ -433,6 +436,7 @@ struct GlobalLayoutSettings: Codable, Hashable, Sendable {
case systemControlsMode
case systemControlsSeekDuration
case volumeMode
case controlBarTheme
}
init(from decoder: Decoder) throws {
@@ -445,6 +449,7 @@ struct GlobalLayoutSettings: Codable, Hashable, Sendable {
systemControlsMode = try container.decodeIfPresent(SystemControlsMode.self, forKey: .systemControlsMode) ?? .seek
systemControlsSeekDuration = try container.decodeIfPresent(SystemControlsSeekDuration.self, forKey: .systemControlsSeekDuration) ?? .tenSeconds
volumeMode = try container.decodeIfPresent(VolumeMode.self, forKey: .volumeMode) ?? .mpv
controlBarTheme = try container.decodeIfPresent(ControlsTheme.self, forKey: .controlBarTheme) ?? .system
}
func encode(to encoder: Encoder) throws {
@@ -457,6 +462,7 @@ struct GlobalLayoutSettings: Codable, Hashable, Sendable {
try container.encode(systemControlsMode, forKey: .systemControlsMode)
try container.encode(systemControlsSeekDuration, forKey: .systemControlsSeekDuration)
try container.encode(volumeMode, forKey: .volumeMode)
try container.encode(controlBarTheme, forKey: .controlBarTheme)
}
// MARK: - Initialization
@@ -469,7 +475,8 @@ struct GlobalLayoutSettings: Codable, Hashable, Sendable {
controlsFadeOpacity: Double = 0.5,
systemControlsMode: SystemControlsMode = .seek,
systemControlsSeekDuration: SystemControlsSeekDuration = .tenSeconds,
volumeMode: VolumeMode = .mpv
volumeMode: VolumeMode = .mpv,
controlBarTheme: ControlsTheme = .system
) {
self.style = style
self.buttonSize = buttonSize
@@ -478,6 +485,7 @@ struct GlobalLayoutSettings: Codable, Hashable, Sendable {
self.systemControlsMode = systemControlsMode
self.systemControlsSeekDuration = systemControlsSeekDuration
self.volumeMode = volumeMode
self.controlBarTheme = controlBarTheme
}
// MARK: - Defaults

View File

@@ -80,7 +80,7 @@ struct MacOSControlBar: View {
.padding(.horizontal, 16)
.padding(.vertical, 10)
.frame(maxWidth: 500)
.glassBackground(.regular, in: .rect(cornerRadius: 16))
.glassBackground(.regular, in: .rect(cornerRadius: 16), colorScheme: globalSettings.controlBarTheme.colorScheme)
.shadow(color: .black.opacity(0.3), radius: 8, y: 4)
// Seek preview overlay - positioned above the control bar
.overlay(alignment: .bottom) {

View File

@@ -354,7 +354,7 @@ private struct LayoutSectionsSection: View {
HStack {
#if os(macOS)
Label(
String(localized: "settings.playerControls.controlBarButtons", defaultValue: "Control Bar Buttons"),
String(localized: "settings.playerControls.controlBar", defaultValue: "Control Bar"),
systemImage: "rectangle.bottomthird.inset.filled"
)
#else

View File

@@ -15,6 +15,7 @@ struct SectionEditorView: View {
// Local state for immediate UI updates
@State private var buttons: [ControlButtonConfiguration] = []
@State private var availableTypes: [ControlButtonType] = []
@State private var controlBarTheme: ControlsTheme = .system
var body: some View {
List {
@@ -45,6 +46,29 @@ struct SectionEditorView: View {
.listRowBackground(Color.clear)
#endif
// The macOS bar draws a glass capsule whose appearance can be forced.
#if os(macOS)
if sectionType == .bottom {
Section {
Picker(
String(localized: "settings.playerControls.glassBackground", defaultValue: "Glass Background"),
selection: $controlBarTheme
) {
ForEach(ControlsTheme.allCases, id: \.self) { theme in
Text(glassBackgroundLabel(for: theme)).tag(theme)
}
}
.disabled(!viewModel.canEditActivePreset)
.onChange(of: controlBarTheme) { _, newTheme in
guard newTheme != viewModel.currentLayout.globalSettings.controlBarTheme else { return }
viewModel.updateGlobalSettingsSync { $0.controlBarTheme = newTheme }
}
} header: {
Text(String(localized: "settings.playerControls.appearance"))
}
}
#endif
// Added buttons
Section {
ForEach(buttons) { button in
@@ -139,6 +163,7 @@ struct SectionEditorView: View {
case .bottom:
buttons = preset.layout.bottomSection.buttons
}
controlBarTheme = preset.layout.globalSettings.controlBarTheme
syncAvailableTypes()
}
@@ -160,12 +185,27 @@ struct SectionEditorView: View {
viewModel.addButtonSync(buttonType, to: sectionType)
}
#if os(macOS)
private func glassBackgroundLabel(for theme: ControlsTheme) -> String {
switch theme {
case .system:
return String(localized: "controls.theme.auto", defaultValue: "Auto")
case .light, .dark:
return theme.displayName
}
}
#endif
private var sectionTitle: String {
switch sectionType {
case .top:
return String(localized: "settings.playerControls.topButtons")
case .bottom:
#if os(macOS)
return String(localized: "settings.playerControls.controlBar", defaultValue: "Control Bar")
#else
return String(localized: "settings.playerControls.bottomButtons")
#endif
}
}
}