mirror of
https://github.com/yattee/yattee.git
synced 2026-05-12 18:35:05 +00:00
Convert Layout & Navigation settings to macOS-native helpers
Add SettingsNavigationRow helper that renders destination-pushing rows as plain list rows with a trailing chevron on macOS, and drop the top divider in headerless sections so they don't render a stray rule.
This commit is contained in:
@@ -11,7 +11,7 @@ struct LayoutNavigationSettingsView: View {
|
|||||||
@Environment(\.appEnvironment) private var appEnvironment
|
@Environment(\.appEnvironment) private var appEnvironment
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
SettingsFormContainer {
|
||||||
if let settings = appEnvironment?.settingsManager {
|
if let settings = appEnvironment?.settingsManager {
|
||||||
CustomizationSection()
|
CustomizationSection()
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@@ -51,7 +51,7 @@ private struct HapticsSection: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
if SettingsManager.deviceSupportsHaptics {
|
if SettingsManager.deviceSupportsHaptics {
|
||||||
Section {
|
SettingsFormSection {
|
||||||
Toggle(
|
Toggle(
|
||||||
String(localized: "settings.haptics.title"),
|
String(localized: "settings.haptics.title"),
|
||||||
isOn: $settings.hapticFeedbackEnabled
|
isOn: $settings.hapticFeedbackEnabled
|
||||||
@@ -77,42 +77,38 @@ private struct HapticsSection: View {
|
|||||||
|
|
||||||
private struct CustomizationSection: View {
|
private struct CustomizationSection: View {
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section {
|
SettingsFormSection {
|
||||||
|
#if os(tvOS)
|
||||||
NavigationLink {
|
NavigationLink {
|
||||||
#if os(tvOS)
|
|
||||||
TVSidebarDetailContainer(
|
TVSidebarDetailContainer(
|
||||||
systemImage: SidebarItem.home.systemImage,
|
systemImage: SidebarItem.home.systemImage,
|
||||||
title: String(localized: "settings.appearance.home.customize")
|
title: String(localized: "settings.appearance.home.customize")
|
||||||
) { HomeSettingsView() }
|
) { HomeSettingsView() }
|
||||||
#else
|
|
||||||
HomeSettingsView()
|
|
||||||
#endif
|
|
||||||
} label: {
|
} label: {
|
||||||
Label(String(localized: "settings.appearance.home.customize"), systemImage: SidebarItem.home.systemImage)
|
Label(String(localized: "settings.appearance.home.customize"), systemImage: SidebarItem.home.systemImage)
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
SettingsNavigationRow("settings.appearance.home.customize", systemImage: SidebarItem.home.systemImage) {
|
||||||
|
HomeSettingsView()
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
NavigationLink {
|
SettingsNavigationRow("settings.tabBar.title", systemImage: "square.grid.3x3") {
|
||||||
TabBarSettingsView()
|
TabBarSettingsView()
|
||||||
} label: {
|
|
||||||
Label(String(localized: "settings.tabBar.title"), systemImage: "square.grid.3x3")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sidebar settings only on iPad (not iPhone)
|
// Sidebar settings only on iPad (not iPhone)
|
||||||
if UIDevice.current.userInterfaceIdiom == .pad {
|
if UIDevice.current.userInterfaceIdiom == .pad {
|
||||||
NavigationLink {
|
SettingsNavigationRow("settings.sidebar.title", systemImage: "sidebar.leading") {
|
||||||
SidebarSettingsView()
|
SidebarSettingsView()
|
||||||
} label: {
|
|
||||||
Label(String(localized: "settings.sidebar.title"), systemImage: "sidebar.leading")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
NavigationLink {
|
SettingsNavigationRow("settings.sidebar.title", systemImage: "sidebar.leading") {
|
||||||
SidebarSettingsView()
|
SidebarSettingsView()
|
||||||
} label: {
|
|
||||||
Label(String(localized: "settings.sidebar.title"), systemImage: "sidebar.leading")
|
|
||||||
}
|
}
|
||||||
#elseif os(tvOS)
|
#elseif os(tvOS)
|
||||||
NavigationLink {
|
NavigationLink {
|
||||||
@@ -134,7 +130,7 @@ private struct LinkActionSection: View {
|
|||||||
@Bindable var settings: SettingsManager
|
@Bindable var settings: SettingsManager
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section {
|
SettingsFormSection("settings.behavior.linkAction.header", footer: "settings.behavior.linkAction.footer") {
|
||||||
Picker(
|
Picker(
|
||||||
String(localized: "settings.behavior.linkAction"),
|
String(localized: "settings.behavior.linkAction"),
|
||||||
selection: $settings.defaultLinkAction
|
selection: $settings.defaultLinkAction
|
||||||
@@ -143,10 +139,6 @@ private struct LinkActionSection: View {
|
|||||||
Text(action.displayName).tag(action)
|
Text(action.displayName).tag(action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} header: {
|
|
||||||
Text(String(localized: "settings.behavior.linkAction.header"))
|
|
||||||
} footer: {
|
|
||||||
Text(String(localized: "settings.behavior.linkAction.footer"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -157,15 +149,11 @@ private struct ClipboardSection: View {
|
|||||||
@Bindable var settings: SettingsManager
|
@Bindable var settings: SettingsManager
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section {
|
SettingsFormSection("settings.dataPrivacy.clipboard.header", footer: "settings.behavior.clipboardMonitoring.footer") {
|
||||||
Toggle(
|
Toggle(
|
||||||
String(localized: "settings.behavior.clipboardMonitoring"),
|
String(localized: "settings.behavior.clipboardMonitoring"),
|
||||||
isOn: $settings.clipboardURLDetectionEnabled
|
isOn: $settings.clipboardURLDetectionEnabled
|
||||||
)
|
)
|
||||||
} header: {
|
|
||||||
Text(String(localized: "settings.dataPrivacy.clipboard.header"))
|
|
||||||
} footer: {
|
|
||||||
Text(String(localized: "settings.behavior.clipboardMonitoring.footer"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -177,7 +165,7 @@ private struct VideoActionsSection: View {
|
|||||||
@Bindable var settings: SettingsManager
|
@Bindable var settings: SettingsManager
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section {
|
SettingsFormSection("settings.videoActions.header") {
|
||||||
Picker(
|
Picker(
|
||||||
String(localized: "settings.behavior.thumbnailTapAction"),
|
String(localized: "settings.behavior.thumbnailTapAction"),
|
||||||
selection: $settings.thumbnailTapAction
|
selection: $settings.thumbnailTapAction
|
||||||
@@ -203,8 +191,6 @@ private struct VideoActionsSection: View {
|
|||||||
Label(String(localized: "settings.appearance.swipeActions"), systemImage: "hand.draw")
|
Label(String(localized: "settings.appearance.swipeActions"), systemImage: "hand.draw")
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
} header: {
|
|
||||||
Text(String(localized: "settings.videoActions.header"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,7 +203,7 @@ private struct TVVideoActionsSection: View {
|
|||||||
@Bindable var settings: SettingsManager
|
@Bindable var settings: SettingsManager
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section {
|
SettingsFormSection("settings.videoActions.header") {
|
||||||
LabeledContent(String(localized: "settings.behavior.tvOSVideoTapAction")) {
|
LabeledContent(String(localized: "settings.behavior.tvOSVideoTapAction")) {
|
||||||
Picker(
|
Picker(
|
||||||
String(localized: "settings.behavior.tvOSVideoTapAction"),
|
String(localized: "settings.behavior.tvOSVideoTapAction"),
|
||||||
@@ -229,8 +215,6 @@ private struct TVVideoActionsSection: View {
|
|||||||
.pickerStyle(.menu)
|
.pickerStyle(.menu)
|
||||||
.labelsHidden()
|
.labelsHidden()
|
||||||
}
|
}
|
||||||
} header: {
|
|
||||||
Text(String(localized: "settings.videoActions.header"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -244,7 +228,7 @@ private struct MiniPlayerMinimizeBehaviorSection: View {
|
|||||||
@Bindable var settings: SettingsManager
|
@Bindable var settings: SettingsManager
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section {
|
SettingsFormSection("settings.behavior.miniPlayer.header", footer: "settings.behavior.miniPlayer.minimizeBehavior.footer") {
|
||||||
Picker(
|
Picker(
|
||||||
String(localized: "settings.behavior.miniPlayer.minimizeBehavior"),
|
String(localized: "settings.behavior.miniPlayer.minimizeBehavior"),
|
||||||
selection: $settings.miniPlayerMinimizeBehavior
|
selection: $settings.miniPlayerMinimizeBehavior
|
||||||
@@ -253,10 +237,6 @@ private struct MiniPlayerMinimizeBehaviorSection: View {
|
|||||||
Text(behavior.displayName).tag(behavior)
|
Text(behavior.displayName).tag(behavior)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} header: {
|
|
||||||
Text(String(localized: "settings.behavior.miniPlayer.header"))
|
|
||||||
} footer: {
|
|
||||||
Text(String(localized: "settings.behavior.miniPlayer.minimizeBehavior.footer"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -268,15 +248,11 @@ private struct HandoffSection: View {
|
|||||||
@Bindable var settings: SettingsManager
|
@Bindable var settings: SettingsManager
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section {
|
SettingsFormSection("settings.behavior.handoff.header", footer: "settings.behavior.handoff.footer") {
|
||||||
Toggle(
|
Toggle(
|
||||||
String(localized: "settings.behavior.handoff"),
|
String(localized: "settings.behavior.handoff"),
|
||||||
isOn: $settings.handoffEnabled
|
isOn: $settings.handoffEnabled
|
||||||
)
|
)
|
||||||
} header: {
|
|
||||||
Text(String(localized: "settings.behavior.handoff.header"))
|
|
||||||
} footer: {
|
|
||||||
Text(String(localized: "settings.behavior.handoff.footer"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,9 +78,9 @@ struct SettingsFormSection<Content: View>: View {
|
|||||||
.padding(.horizontal, 16)
|
.padding(.horizontal, 16)
|
||||||
.padding(.top, 12)
|
.padding(.top, 12)
|
||||||
.padding(.bottom, 4)
|
.padding(.bottom, 4)
|
||||||
}
|
|
||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
|
}
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: 10) {
|
VStack(alignment: .leading, spacing: 10) {
|
||||||
content()
|
content()
|
||||||
@@ -133,3 +133,47 @@ struct SettingsFormSection<Content: View>: View {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A settings row that pushes a destination view onto the navigation stack.
|
||||||
|
///
|
||||||
|
/// On macOS it renders as a plain full-width list row with a trailing
|
||||||
|
/// chevron, matching the native macOS System Settings look. On iOS/tvOS
|
||||||
|
/// it renders as a standard `NavigationLink` with a `Label`.
|
||||||
|
struct SettingsNavigationRow<Destination: View>: View {
|
||||||
|
let titleKey: LocalizedStringKey
|
||||||
|
let systemImage: String
|
||||||
|
@ViewBuilder var destination: () -> Destination
|
||||||
|
|
||||||
|
init(
|
||||||
|
_ titleKey: LocalizedStringKey,
|
||||||
|
systemImage: String,
|
||||||
|
@ViewBuilder destination: @escaping () -> Destination
|
||||||
|
) {
|
||||||
|
self.titleKey = titleKey
|
||||||
|
self.systemImage = systemImage
|
||||||
|
self.destination = destination
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
NavigationLink {
|
||||||
|
destination()
|
||||||
|
} label: {
|
||||||
|
#if os(macOS)
|
||||||
|
HStack(spacing: 8) {
|
||||||
|
Label(titleKey, systemImage: systemImage)
|
||||||
|
Spacer()
|
||||||
|
Image(systemName: "chevron.right")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.tertiary)
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.contentShape(Rectangle())
|
||||||
|
#else
|
||||||
|
Label(titleKey, systemImage: systemImage)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#if os(macOS)
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user