GBA001/Delta/Settings/Experimental Features/FeatureDetailView.swift
Riley Testut 39522fda58 [Experimental Feature] Supports AirPlay controller skins
Allows users to customize controller skin when AirPlaying games to an external display.
2023-04-27 14:58:51 -05:00

126 lines
3.5 KiB
Swift

//
// FeatureDetailView.swift
// Delta
//
// Created by Riley Testut on 4/10/23.
// Copyright © 2023 Riley Testut. All rights reserved.
//
import SwiftUI
import DeltaFeatures
struct FeatureDetailView<Feature: AnyFeature>: View
{
@ObservedObject
var feature: Feature
var body: some View {
Form {
Section {
Toggle(isOn: $feature.isEnabled.animation()) {
Text(feature.name)
.bold()
}
} footer: {
if let description = feature.description
{
Text(description)
}
}
if feature.isEnabled
{
ForEach(feature.allOptions, id: \.key) { option in
if let optionView = optionView(option)
{
Section {
optionView
} footer: {
if let description = option.description
{
Text(description)
}
}
}
}
}
}
}
// Cannot open existential if return type uses concrete type T in non-covariant position (e.g. Box<T>).
// So instead we erase return type to AnyView.
private func optionView<T: AnyOption>(_ option: T) -> AnyView?
{
guard let view = OptionRow(option: option) else { return nil }
return AnyView(view)
}
}
private struct OptionRow<Option: AnyOption, DetailView: View>: View where DetailView == Option.DetailView
{
let name: LocalizedStringKey
let value: any LocalizedOptionValue
let detailView: DetailView
let option: Option
@State
private var displayInline: Bool = false
init?(option: Option)
{
// Only show if option has a name, localizable value, and detailView.
guard
let name = option.name,
let value = option.value as? any LocalizedOptionValue,
let detailView = option.detailView()
else { return nil }
self.name = name
self.value = value
self.detailView = detailView
self.option = option
}
var body: some View {
VStack {
let detailView = detailView
.environment(\.managedObjectContext, DatabaseManager.shared.viewContext)
.environment(\.featureOption, option)
if displayInline
{
// Display entire view inline.
detailView
}
else
{
let wrappedDetailView = Form {
detailView
}
NavigationLink(destination: wrappedDetailView) {
HStack {
Text(name)
Spacer()
value.localizedDescription
.foregroundColor(.secondary)
}
}
.overlay(
// Hack to ensure displayInline preference is in View hierarchy.
detailView
.hidden()
.frame(width: 0, height: 0)
)
}
}
.onPreferenceChange(DisplayInlineKey.self) { displayInline in
self.displayInline = displayInline
}
}
}