mirror of
https://github.com/yattee/yattee.git
synced 2026-05-13 19:05:03 +00:00
Use menu-style pickers in tvOS settings
Introduce PlatformMenuPicker that wraps short-option pickers in LabeledContent + .pickerStyle(.menu) on tvOS so they render as a compact dropdown instead of pushing a full-screen option list. On iOS/macOS it falls through to a plain Picker, leaving rendering unchanged. Applied across Playback, Subtitles, Sidebar, Privacy, and Advanced settings. Long language lists in PlaybackSettingsView are left as push-style.
This commit is contained in:
@@ -99,7 +99,7 @@ struct AdvancedSettingsView: View {
|
|||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private func feedSection(settingsManager: SettingsManager) -> some View {
|
private func feedSection(settingsManager: SettingsManager) -> some View {
|
||||||
Section {
|
Section {
|
||||||
Picker(selection: Binding(
|
PlatformMenuPicker(selection: Binding(
|
||||||
get: { settingsManager.feedCacheValidityMinutes },
|
get: { settingsManager.feedCacheValidityMinutes },
|
||||||
set: { settingsManager.feedCacheValidityMinutes = $0 }
|
set: { settingsManager.feedCacheValidityMinutes = $0 }
|
||||||
)) {
|
)) {
|
||||||
@@ -192,7 +192,7 @@ struct AdvancedSettingsView: View {
|
|||||||
private var mpvSection: some View {
|
private var mpvSection: some View {
|
||||||
if let settingsManager = appEnvironment?.settingsManager {
|
if let settingsManager = appEnvironment?.settingsManager {
|
||||||
Section {
|
Section {
|
||||||
Picker(selection: Binding(
|
PlatformMenuPicker(selection: Binding(
|
||||||
get: { settingsManager.mpvBufferSeconds },
|
get: { settingsManager.mpvBufferSeconds },
|
||||||
set: { settingsManager.mpvBufferSeconds = $0 }
|
set: { settingsManager.mpvBufferSeconds = $0 }
|
||||||
)) {
|
)) {
|
||||||
|
|||||||
42
Yattee/Views/Settings/PlatformMenuPicker.swift
Normal file
42
Yattee/Views/Settings/PlatformMenuPicker.swift
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
//
|
||||||
|
// PlatformMenuPicker.swift
|
||||||
|
// Yattee
|
||||||
|
//
|
||||||
|
// A Picker wrapper that renders as a compact menu inside LabeledContent on tvOS,
|
||||||
|
// and as a standard inline Picker on iOS/macOS. Use this for short option lists
|
||||||
|
// in settings forms so tvOS shows a dropdown menu rather than pushing a sub-view.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct PlatformMenuPicker<Selection: Hashable, Label: View, Content: View>: View {
|
||||||
|
@Binding var selection: Selection
|
||||||
|
@ViewBuilder var content: () -> Content
|
||||||
|
@ViewBuilder var label: () -> Label
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
#if os(tvOS)
|
||||||
|
LabeledContent {
|
||||||
|
Picker(selection: $selection, content: content) { EmptyView() }
|
||||||
|
.pickerStyle(.menu)
|
||||||
|
.labelsHidden()
|
||||||
|
} label: {
|
||||||
|
label()
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
Picker(selection: $selection, content: content, label: label)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PlatformMenuPicker where Label == Text {
|
||||||
|
init(
|
||||||
|
_ titleKey: String,
|
||||||
|
selection: Binding<Selection>,
|
||||||
|
@ViewBuilder content: @escaping () -> Content
|
||||||
|
) {
|
||||||
|
self._selection = selection
|
||||||
|
self.content = content
|
||||||
|
self.label = { Text(titleKey) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -42,7 +42,7 @@ private struct QualitySection: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section {
|
Section {
|
||||||
Picker(
|
PlatformMenuPicker(
|
||||||
String(localized: "settings.playback.quality.preferred"),
|
String(localized: "settings.playback.quality.preferred"),
|
||||||
selection: $settings.preferredQuality
|
selection: $settings.preferredQuality
|
||||||
) {
|
) {
|
||||||
@@ -241,7 +241,7 @@ private struct BehaviorSection: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section(String(localized: "settings.playback.behavior.header")) {
|
Section(String(localized: "settings.playback.behavior.header")) {
|
||||||
Picker(
|
PlatformMenuPicker(
|
||||||
String(localized: "settings.playback.resumeAction"),
|
String(localized: "settings.playback.resumeAction"),
|
||||||
selection: $settings.resumeAction
|
selection: $settings.resumeAction
|
||||||
) {
|
) {
|
||||||
@@ -273,7 +273,7 @@ private struct QueueSection: View {
|
|||||||
)
|
)
|
||||||
|
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
Picker(
|
PlatformMenuPicker(
|
||||||
String(localized: "settings.playback.queue.autoPlayCountdown"),
|
String(localized: "settings.playback.queue.autoPlayCountdown"),
|
||||||
selection: $settings.queueAutoPlayCountdown
|
selection: $settings.queueAutoPlayCountdown
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ struct PrivacySettingsView: View {
|
|||||||
isOn: Bindable(settingsManager).saveWatchHistory
|
isOn: Bindable(settingsManager).saveWatchHistory
|
||||||
)
|
)
|
||||||
|
|
||||||
Picker(
|
PlatformMenuPicker(
|
||||||
String(localized: "settings.behavior.historyRetention"),
|
String(localized: "settings.behavior.historyRetention"),
|
||||||
selection: Binding(
|
selection: Binding(
|
||||||
get: { settingsManager.historyRetentionDays },
|
get: { settingsManager.historyRetentionDays },
|
||||||
@@ -104,7 +104,7 @@ struct PrivacySettingsView: View {
|
|||||||
isOn: Bindable(settingsManager).saveRecentPlaylists
|
isOn: Bindable(settingsManager).saveRecentPlaylists
|
||||||
)
|
)
|
||||||
|
|
||||||
Picker(
|
PlatformMenuPicker(
|
||||||
String(localized: "settings.behavior.searchHistoryLimit"),
|
String(localized: "settings.behavior.searchHistoryLimit"),
|
||||||
selection: Binding(
|
selection: Binding(
|
||||||
get: { settingsManager.searchHistoryLimit },
|
get: { settingsManager.searchHistoryLimit },
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ struct SidebarSettingsView: View {
|
|||||||
|
|
||||||
private var startupSection: some View {
|
private var startupSection: some View {
|
||||||
Section {
|
Section {
|
||||||
Picker(String(localized: "settings.sidebar.startup.tab"), selection: startupTabBinding) {
|
PlatformMenuPicker(String(localized: "settings.sidebar.startup.tab"), selection: startupTabBinding) {
|
||||||
ForEach(validStartupTabs) { item in
|
ForEach(validStartupTabs) { item in
|
||||||
Text(item.localizedTitle).tag(item)
|
Text(item.localizedTitle).tag(item)
|
||||||
}
|
}
|
||||||
@@ -288,7 +288,7 @@ struct SidebarSettingsView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Source sort order
|
// Source sort order
|
||||||
Picker(String(localized: "settings.sidebar.sourceSort"), selection: sourceSortBinding) {
|
PlatformMenuPicker(String(localized: "settings.sidebar.sourceSort"), selection: sourceSortBinding) {
|
||||||
ForEach(SidebarSourceSort.allCases) { sort in
|
ForEach(SidebarSourceSort.allCases) { sort in
|
||||||
Text(sort.localizedTitle).tag(sort)
|
Text(sort.localizedTitle).tag(sort)
|
||||||
}
|
}
|
||||||
@@ -309,7 +309,7 @@ struct SidebarSettingsView: View {
|
|||||||
if sourcesLimitEnabledBinding.wrappedValue {
|
if sourcesLimitEnabledBinding.wrappedValue {
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
// tvOS uses Picker instead of Slider (Slider/Stepper unavailable)
|
// tvOS uses Picker instead of Slider (Slider/Stepper unavailable)
|
||||||
Picker(String(localized: "settings.sidebar.maxSources"), selection: maxSourcesBinding) {
|
PlatformMenuPicker(String(localized: "settings.sidebar.maxSources"), selection: maxSourcesBinding) {
|
||||||
ForEach([5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 60, 70, 80, 90, 100], id: \.self) { value in
|
ForEach([5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 60, 70, 80, 90, 100], id: \.self) { value in
|
||||||
Text("\(value)").tag(value)
|
Text("\(value)").tag(value)
|
||||||
}
|
}
|
||||||
@@ -360,7 +360,7 @@ struct SidebarSettingsView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Channel sort order
|
// Channel sort order
|
||||||
Picker(String(localized: "settings.sidebar.channelSort"), selection: channelSortBinding) {
|
PlatformMenuPicker(String(localized: "settings.sidebar.channelSort"), selection: channelSortBinding) {
|
||||||
ForEach(SidebarChannelSort.allCases.filter { $0 != .custom }) { sort in
|
ForEach(SidebarChannelSort.allCases.filter { $0 != .custom }) { sort in
|
||||||
Text(sort.localizedTitle).tag(sort)
|
Text(sort.localizedTitle).tag(sort)
|
||||||
}
|
}
|
||||||
@@ -381,7 +381,7 @@ struct SidebarSettingsView: View {
|
|||||||
if channelsLimitEnabledBinding.wrappedValue {
|
if channelsLimitEnabledBinding.wrappedValue {
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
// tvOS uses Picker instead of Slider (Slider/Stepper unavailable)
|
// tvOS uses Picker instead of Slider (Slider/Stepper unavailable)
|
||||||
Picker(String(localized: "settings.sidebar.maxChannels"), selection: maxChannelsBinding) {
|
PlatformMenuPicker(String(localized: "settings.sidebar.maxChannels"), selection: maxChannelsBinding) {
|
||||||
ForEach([5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 60, 70, 80, 90, 100], id: \.self) { value in
|
ForEach([5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 60, 70, 80, 90, 100], id: \.self) { value in
|
||||||
Text("\(value)").tag(value)
|
Text("\(value)").tag(value)
|
||||||
}
|
}
|
||||||
@@ -432,7 +432,7 @@ struct SidebarSettingsView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Playlist sort order
|
// Playlist sort order
|
||||||
Picker(String(localized: "settings.sidebar.playlistSort"), selection: playlistSortBinding) {
|
PlatformMenuPicker(String(localized: "settings.sidebar.playlistSort"), selection: playlistSortBinding) {
|
||||||
ForEach(SidebarPlaylistSort.allCases) { sort in
|
ForEach(SidebarPlaylistSort.allCases) { sort in
|
||||||
Text(sort.localizedTitle).tag(sort)
|
Text(sort.localizedTitle).tag(sort)
|
||||||
}
|
}
|
||||||
@@ -453,7 +453,7 @@ struct SidebarSettingsView: View {
|
|||||||
if playlistsLimitEnabledBinding.wrappedValue {
|
if playlistsLimitEnabledBinding.wrappedValue {
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
// tvOS uses Picker instead of Slider (Slider/Stepper unavailable)
|
// tvOS uses Picker instead of Slider (Slider/Stepper unavailable)
|
||||||
Picker(String(localized: "settings.sidebar.maxPlaylists"), selection: maxPlaylistsBinding) {
|
PlatformMenuPicker(String(localized: "settings.sidebar.maxPlaylists"), selection: maxPlaylistsBinding) {
|
||||||
ForEach([5, 10, 15, 20, 25, 30], id: \.self) { value in
|
ForEach([5, 10, 15, 20, 25, 30], id: \.self) { value in
|
||||||
Text("\(value)").tag(value)
|
Text("\(value)").tag(value)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ struct SubtitlesSettingsView: View {
|
|||||||
|
|
||||||
private var fontSection: some View {
|
private var fontSection: some View {
|
||||||
Section {
|
Section {
|
||||||
Picker(
|
PlatformMenuPicker(
|
||||||
String(localized: "settings.subtitles.font"),
|
String(localized: "settings.subtitles.font"),
|
||||||
selection: $settings.font
|
selection: $settings.font
|
||||||
) {
|
) {
|
||||||
@@ -49,7 +49,7 @@ struct SubtitlesSettingsView: View {
|
|||||||
|
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
// tvOS uses Picker instead of Slider (Slider unavailable)
|
// tvOS uses Picker instead of Slider (Slider unavailable)
|
||||||
Picker(String(localized: "settings.subtitles.fontSize"), selection: $settings.fontSize) {
|
PlatformMenuPicker(String(localized: "settings.subtitles.fontSize"), selection: $settings.fontSize) {
|
||||||
ForEach([20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100], id: \.self) { size in
|
ForEach([20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100], id: \.self) { size in
|
||||||
Text("settings.subtitles.fontSize \(size)").tag(size)
|
Text("settings.subtitles.fontSize \(size)").tag(size)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user