Convert Advanced and Developer settings to macOS-native helpers

Extend SettingsFormSection to accept a @ViewBuilder footer for sections
with dynamic multi-line content (last background refresh, orphaned
files status). Move trailing button accessories (size, progress) out of
button labels so buttons size to their content on macOS.
This commit is contained in:
Arkadiusz Fal
2026-04-21 00:48:30 +02:00
parent 72778870e1
commit 48963a9e2e
3 changed files with 162 additions and 125 deletions

View File

@@ -43,20 +43,10 @@ struct SettingsFormContainer<Content: View>: View {
/// content with consistent padding, a bottom divider, and an optional
/// caption-sized footer.
/// - On iOS/tvOS: renders a standard `Section { } header: { } footer: { }`.
struct SettingsFormSection<Content: View>: View {
struct SettingsFormSection<Content: View, Footer: View>: View {
let header: LocalizedStringKey?
let footer: LocalizedStringKey?
@ViewBuilder let content: () -> Content
init(
_ header: LocalizedStringKey? = nil,
footer: LocalizedStringKey? = nil,
@ViewBuilder content: @escaping () -> Content
) {
self.header = header
self.footer = footer
self.content = content
}
@ViewBuilder let footer: () -> Footer
var body: some View {
#if os(macOS)
@@ -91,49 +81,83 @@ struct SettingsFormSection<Content: View>: View {
Divider()
if let footer {
Text(footer)
.font(.caption)
.foregroundStyle(.secondary)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, 16)
.padding(.top, 6)
}
footer()
.font(.caption)
.foregroundStyle(.secondary)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, 16)
.padding(.top, 6)
}
.padding(.bottom, 12)
}
#else
@ViewBuilder
private var platformSection: some View {
if let header, let footer {
if let header {
Section {
content()
} header: {
Text(header)
} footer: {
Text(footer)
}
} else if let header {
Section {
content()
} header: {
Text(header)
}
} else if let footer {
Section {
content()
} footer: {
Text(footer)
footer()
}
} else {
Section {
content()
} footer: {
footer()
}
}
}
#endif
}
extension SettingsFormSection where Footer == EmptyView {
init(
_ header: LocalizedStringKey? = nil,
@ViewBuilder content: @escaping () -> Content
) {
self.header = header
self.content = content
self.footer = { EmptyView() }
}
}
extension SettingsFormSection where Footer == Text {
init(
_ header: LocalizedStringKey? = nil,
footer: LocalizedStringKey,
@ViewBuilder content: @escaping () -> Content
) {
self.header = header
self.content = content
self.footer = { Text(footer) }
}
init(
_ header: LocalizedStringKey? = nil,
footer: LocalizedStringKey?,
@ViewBuilder content: @escaping () -> Content
) {
self.header = header
self.content = content
let footerKey = footer
self.footer = { footerKey.map { Text($0) } ?? Text(verbatim: "") }
}
}
extension SettingsFormSection {
init(
_ header: LocalizedStringKey? = nil,
@ViewBuilder content: @escaping () -> Content,
@ViewBuilder footer: @escaping () -> Footer
) {
self.header = header
self.content = content
self.footer = footer
}
}
/// A label style that forces the icon to a fixed width so adjacent
/// labels align regardless of icon glyph width. Use when a section has
/// a vertical stack of `Label`s with mixed-width SF Symbols.