From 9912327448926726ecff1d5e5bd3ec6a8482d1ae Mon Sep 17 00:00:00 2001 From: Arkadiusz Fal Date: Mon, 20 Apr 2026 23:34:43 +0200 Subject: [PATCH] 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. --- .../LayoutNavigationSettingsView.swift | 60 ++++++------------- Yattee/Views/Settings/MacOSSettings.swift | 48 ++++++++++++++- 2 files changed, 64 insertions(+), 44 deletions(-) diff --git a/Yattee/Views/Settings/LayoutNavigationSettingsView.swift b/Yattee/Views/Settings/LayoutNavigationSettingsView.swift index d2e3c18f..61e70670 100644 --- a/Yattee/Views/Settings/LayoutNavigationSettingsView.swift +++ b/Yattee/Views/Settings/LayoutNavigationSettingsView.swift @@ -11,7 +11,7 @@ struct LayoutNavigationSettingsView: View { @Environment(\.appEnvironment) private var appEnvironment var body: some View { - Form { + SettingsFormContainer { if let settings = appEnvironment?.settingsManager { CustomizationSection() #if os(iOS) @@ -51,7 +51,7 @@ private struct HapticsSection: View { var body: some View { if SettingsManager.deviceSupportsHaptics { - Section { + SettingsFormSection { Toggle( String(localized: "settings.haptics.title"), isOn: $settings.hapticFeedbackEnabled @@ -77,42 +77,38 @@ private struct HapticsSection: View { private struct CustomizationSection: View { var body: some View { - Section { + SettingsFormSection { + #if os(tvOS) NavigationLink { - #if os(tvOS) TVSidebarDetailContainer( systemImage: SidebarItem.home.systemImage, title: String(localized: "settings.appearance.home.customize") ) { HomeSettingsView() } - #else - HomeSettingsView() - #endif } label: { 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) - NavigationLink { + SettingsNavigationRow("settings.tabBar.title", systemImage: "square.grid.3x3") { TabBarSettingsView() - } label: { - Label(String(localized: "settings.tabBar.title"), systemImage: "square.grid.3x3") } // Sidebar settings only on iPad (not iPhone) if UIDevice.current.userInterfaceIdiom == .pad { - NavigationLink { + SettingsNavigationRow("settings.sidebar.title", systemImage: "sidebar.leading") { SidebarSettingsView() - } label: { - Label(String(localized: "settings.sidebar.title"), systemImage: "sidebar.leading") } } #endif #if os(macOS) - NavigationLink { + SettingsNavigationRow("settings.sidebar.title", systemImage: "sidebar.leading") { SidebarSettingsView() - } label: { - Label(String(localized: "settings.sidebar.title"), systemImage: "sidebar.leading") } #elseif os(tvOS) NavigationLink { @@ -134,7 +130,7 @@ private struct LinkActionSection: View { @Bindable var settings: SettingsManager var body: some View { - Section { + SettingsFormSection("settings.behavior.linkAction.header", footer: "settings.behavior.linkAction.footer") { Picker( String(localized: "settings.behavior.linkAction"), selection: $settings.defaultLinkAction @@ -143,10 +139,6 @@ private struct LinkActionSection: View { 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 var body: some View { - Section { + SettingsFormSection("settings.dataPrivacy.clipboard.header", footer: "settings.behavior.clipboardMonitoring.footer") { Toggle( String(localized: "settings.behavior.clipboardMonitoring"), 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 var body: some View { - Section { + SettingsFormSection("settings.videoActions.header") { Picker( String(localized: "settings.behavior.thumbnailTapAction"), selection: $settings.thumbnailTapAction @@ -203,8 +191,6 @@ private struct VideoActionsSection: View { Label(String(localized: "settings.appearance.swipeActions"), systemImage: "hand.draw") } #endif - } header: { - Text(String(localized: "settings.videoActions.header")) } } } @@ -217,7 +203,7 @@ private struct TVVideoActionsSection: View { @Bindable var settings: SettingsManager var body: some View { - Section { + SettingsFormSection("settings.videoActions.header") { LabeledContent(String(localized: "settings.behavior.tvOSVideoTapAction")) { Picker( String(localized: "settings.behavior.tvOSVideoTapAction"), @@ -229,8 +215,6 @@ private struct TVVideoActionsSection: View { .pickerStyle(.menu) .labelsHidden() } - } header: { - Text(String(localized: "settings.videoActions.header")) } } } @@ -244,7 +228,7 @@ private struct MiniPlayerMinimizeBehaviorSection: View { @Bindable var settings: SettingsManager var body: some View { - Section { + SettingsFormSection("settings.behavior.miniPlayer.header", footer: "settings.behavior.miniPlayer.minimizeBehavior.footer") { Picker( String(localized: "settings.behavior.miniPlayer.minimizeBehavior"), selection: $settings.miniPlayerMinimizeBehavior @@ -253,10 +237,6 @@ private struct MiniPlayerMinimizeBehaviorSection: View { 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 var body: some View { - Section { + SettingsFormSection("settings.behavior.handoff.header", footer: "settings.behavior.handoff.footer") { Toggle( String(localized: "settings.behavior.handoff"), isOn: $settings.handoffEnabled ) - } header: { - Text(String(localized: "settings.behavior.handoff.header")) - } footer: { - Text(String(localized: "settings.behavior.handoff.footer")) } } } diff --git a/Yattee/Views/Settings/MacOSSettings.swift b/Yattee/Views/Settings/MacOSSettings.swift index d8955c01..31292b84 100644 --- a/Yattee/Views/Settings/MacOSSettings.swift +++ b/Yattee/Views/Settings/MacOSSettings.swift @@ -78,9 +78,9 @@ struct SettingsFormSection: View { .padding(.horizontal, 16) .padding(.top, 12) .padding(.bottom, 4) - } - Divider() + Divider() + } VStack(alignment: .leading, spacing: 10) { content() @@ -133,3 +133,47 @@ struct SettingsFormSection: View { } #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: 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 + } +}