Improve playback settings UI controls on macOS

Standardized picker and button sizing with consistent alignment and control sizes. Added SettingsPickerModifier to all macOS pickers with menu style. Improved rate buttons with proper sizing and alignment. Added text truncation for stream descriptions to prevent overflow.
This commit is contained in:
Arkadiusz Fal
2025-11-14 19:28:26 +01:00
parent 500c787063
commit 6aef3f10b1
3 changed files with 53 additions and 22 deletions

View File

@@ -64,10 +64,10 @@ struct PlaybackSettings: View {
.padding(.vertical, 10) .padding(.vertical, 10)
if player.activeBackend == .mpv || !player.avPlayerUsesSystemControls { if player.activeBackend == .mpv || !player.avPlayerUsesSystemControls {
HStack { HStack(alignment: .center) {
controlsHeader("Rate".localized()) controlsHeader("Rate".localized())
Spacer() Spacer()
HStack(spacing: rateButtonsSpacing) { HStack(alignment: .center, spacing: rateButtonsSpacing) {
decreaseRateButton decreaseRateButton
#if os(tvOS) #if os(tvOS)
.focused($focusedField, equals: .decreaseRate) .focused($focusedField, equals: .decreaseRate)
@@ -205,8 +205,9 @@ struct PlaybackSettings: View {
@ViewBuilder private var rateButton: some View { @ViewBuilder private var rateButton: some View {
#if os(macOS) #if os(macOS)
ratePicker ratePicker
.labelsHidden() .modifier(SettingsPickerModifier())
.frame(maxWidth: 100) .controlSize(.large)
.frame(width: 100, alignment: .center)
#elseif os(iOS) #elseif os(iOS)
Menu { Menu {
ratePicker ratePicker
@@ -241,11 +242,15 @@ struct PlaybackSettings: View {
player.currentRate = rate player.currentRate = rate
} }
} label: { } label: {
#if os(macOS)
Image(systemName: "plus")
.imageScale(.large)
.frame(width: 16, height: 16)
#else
Label("Increase rate", systemImage: "plus") Label("Increase rate", systemImage: "plus")
.foregroundColor(.accentColor)
.imageScale(.large) .imageScale(.large)
.labelStyle(.iconOnly) .labelStyle(.iconOnly)
#if os(iOS) .foregroundColor(.accentColor)
.padding(12) .padding(12)
.frame(width: 40, height: 40) .frame(width: 40, height: 40)
.background(RoundedRectangle(cornerRadius: 4).strokeBorder(Color.accentColor, lineWidth: 1)) .background(RoundedRectangle(cornerRadius: 4).strokeBorder(Color.accentColor, lineWidth: 1))
@@ -254,6 +259,12 @@ struct PlaybackSettings: View {
} }
#if os(macOS) #if os(macOS)
.buttonStyle(.bordered) .buttonStyle(.bordered)
.controlSize(.large)
.frame(minWidth: 32, minHeight: 28)
.fixedSize()
#else
.buttonStyle(.bordered)
.controlSize(.large)
#endif #endif
.disabled(increasedRate.isNil) .disabled(increasedRate.isNil)
} }
@@ -266,11 +277,15 @@ struct PlaybackSettings: View {
player.currentRate = rate player.currentRate = rate
} }
} label: { } label: {
#if os(macOS)
Image(systemName: "minus")
.imageScale(.large)
.frame(width: 16, height: 16)
#else
Label("Decrease rate", systemImage: "minus") Label("Decrease rate", systemImage: "minus")
.foregroundColor(.accentColor)
.imageScale(.large) .imageScale(.large)
.labelStyle(.iconOnly) .labelStyle(.iconOnly)
#if os(iOS) .foregroundColor(.accentColor)
.padding(12) .padding(12)
.frame(width: 40, height: 40) .frame(width: 40, height: 40)
.background(RoundedRectangle(cornerRadius: 4).strokeBorder(Color.accentColor, lineWidth: 1)) .background(RoundedRectangle(cornerRadius: 4).strokeBorder(Color.accentColor, lineWidth: 1))
@@ -279,9 +294,14 @@ struct PlaybackSettings: View {
} }
#if os(macOS) #if os(macOS)
.buttonStyle(.bordered) .buttonStyle(.bordered)
#elseif os(iOS) .controlSize(.large)
.frame(minWidth: 32, minHeight: 28)
.fixedSize()
#else
.buttonStyle(.bordered)
.controlSize(.large)
#endif #endif
.disabled(decreasedRate.isNil) .disabled(decreasedRate.isNil)
} }
private var rateButtonsSpacing: Double { private var rateButtonsSpacing: Double {
@@ -304,9 +324,8 @@ struct PlaybackSettings: View {
#elseif os(macOS) #elseif os(macOS)
playbackModePicker playbackModePicker
.modifier(SettingsPickerModifier()) .modifier(SettingsPickerModifier())
#if os(macOS) .controlSize(.large)
.frame(maxWidth: 150) .frame(width: 300, alignment: .trailing)
#endif
#else #else
Menu { Menu {
playbackModePicker playbackModePicker
@@ -329,8 +348,9 @@ struct PlaybackSettings: View {
@ViewBuilder private var qualityProfileButton: some View { @ViewBuilder private var qualityProfileButton: some View {
#if os(macOS) #if os(macOS)
qualityProfilePicker qualityProfilePicker
.labelsHidden() .modifier(SettingsPickerModifier())
.frame(maxWidth: 300) .controlSize(.large)
.frame(width: 300, alignment: .trailing)
#elseif os(iOS) #elseif os(iOS)
Menu { Menu {
qualityProfilePicker qualityProfilePicker
@@ -378,8 +398,9 @@ struct PlaybackSettings: View {
@ViewBuilder private var streamButton: some View { @ViewBuilder private var streamButton: some View {
#if os(macOS) #if os(macOS)
StreamControl() StreamControl()
.labelsHidden() .modifier(SettingsPickerModifier())
.frame(maxWidth: 300) .controlSize(.large)
.frame(width: 300, alignment: .trailing)
#elseif os(iOS) #elseif os(iOS)
Menu { Menu {
StreamControl() StreamControl()
@@ -400,8 +421,9 @@ struct PlaybackSettings: View {
let videoCaptions = player.currentVideo?.captions let videoCaptions = player.currentVideo?.captions
#if os(macOS) #if os(macOS)
captionsPicker captionsPicker
.labelsHidden() .modifier(SettingsPickerModifier())
.frame(maxWidth: 300) .controlSize(.large)
.frame(width: 300, alignment: .trailing)
#elseif os(iOS) #elseif os(iOS)
Menu { Menu {
if videoCaptions?.isEmpty == false { if videoCaptions?.isEmpty == false {
@@ -470,8 +492,9 @@ struct PlaybackSettings: View {
@ViewBuilder private var audioTrackButton: some View { @ViewBuilder private var audioTrackButton: some View {
#if os(macOS) #if os(macOS)
audioTrackPicker audioTrackPicker
.labelsHidden() .modifier(SettingsPickerModifier())
.frame(maxWidth: 300) .controlSize(.large)
.frame(width: 300, alignment: .trailing)
#elseif os(iOS) #elseif os(iOS)
Menu { Menu {
audioTrackPicker audioTrackPicker

View File

@@ -20,7 +20,12 @@ struct StreamControl: View {
ForEach(kinds, id: \.self) { key in ForEach(kinds, id: \.self) { key in
ForEach(availableStreamsByKind[key] ?? []) { stream in ForEach(availableStreamsByKind[key] ?? []) { stream in
Text(stream.description).tag(Stream?.some(stream)) Text(stream.description)
#if os(macOS)
.lineLimit(1)
.truncationMode(.middle)
#endif
.tag(Stream?.some(stream))
} }
#if os(macOS) #if os(macOS)
@@ -36,6 +41,8 @@ struct StreamControl: View {
.frame(minWidth: 110) .frame(minWidth: 110)
.fixedSize(horizontal: true, vertical: true) .fixedSize(horizontal: true, vertical: true)
.disabled(player.isLoadingAvailableStreams) .disabled(player.isLoadingAvailableStreams)
#elseif os(macOS)
.fixedSize()
#endif #endif
#else #else
ControlsOverlayButton(focusedField: focusedField!, field: .stream) { ControlsOverlayButton(focusedField: focusedField!, field: .stream) {

View File

@@ -15,6 +15,7 @@ struct SettingsPickerModifier: ViewModifier {
#else #else
content content
.labelsHidden() .labelsHidden()
.pickerStyle(.menu)
#endif #endif
} }
} }