[Features] Supports user-facing @Options with custom SwiftUI views
@Options with non-nil names will be exposed in Delta’s settings and can be configured by users via provided SwiftUI view.
This commit is contained in:
parent
4d30ef2929
commit
240b74de94
@ -10,8 +10,14 @@ import SwiftUI
|
||||
import Combine
|
||||
|
||||
@propertyWrapper
|
||||
public class Option<Value: OptionValue>: _AnyOption
|
||||
public class Option<Value: OptionValue, DetailView: View>: _AnyOption
|
||||
{
|
||||
// Nil name == hidden option.
|
||||
public let name: LocalizedStringKey?
|
||||
public let description: LocalizedStringKey?
|
||||
|
||||
public private(set) var detailView: () -> DetailView? = { nil }
|
||||
|
||||
// Assigned to property name.
|
||||
public internal(set) var key: String = ""
|
||||
|
||||
@ -27,6 +33,14 @@ public class Option<Value: OptionValue>: _AnyOption
|
||||
|
||||
private let defaultValue: Value
|
||||
|
||||
private var valueBinding: Binding<Value> {
|
||||
Binding(get: {
|
||||
self.wrappedValue
|
||||
}, set: { newValue in
|
||||
self.wrappedValue = newValue
|
||||
})
|
||||
}
|
||||
|
||||
/// @propertyWrapper
|
||||
public var projectedValue: some Option {
|
||||
return self
|
||||
@ -59,21 +73,75 @@ public class Option<Value: OptionValue>: _AnyOption
|
||||
}
|
||||
}
|
||||
|
||||
// Non-Optional
|
||||
public init(wrappedValue: Value)
|
||||
private init(defaultValue: Value, name: LocalizedStringKey?, description: LocalizedStringKey?)
|
||||
{
|
||||
self.defaultValue = wrappedValue
|
||||
self.defaultValue = defaultValue
|
||||
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.detailView = { nil }
|
||||
}
|
||||
}
|
||||
|
||||
// "Hidden" Option (no name or custom SwiftUI view)
|
||||
public extension Option where DetailView == EmptyView
|
||||
{
|
||||
// Non-Optional
|
||||
convenience init(wrappedValue: Value)
|
||||
{
|
||||
self.init(defaultValue: wrappedValue, name: nil, description: nil)
|
||||
}
|
||||
|
||||
// Optional, default = nil
|
||||
public init() where Value: OptionalProtocol
|
||||
convenience init() where Value: OptionalProtocol
|
||||
{
|
||||
self.defaultValue = Value.none
|
||||
self.init(defaultValue: Value.none, name: nil, description: nil)
|
||||
}
|
||||
|
||||
// Optional, default = non-nil
|
||||
public init(wrappedValue: Value) where Value: OptionalProtocol
|
||||
convenience init(wrappedValue: Value) where Value: OptionalProtocol
|
||||
{
|
||||
self.defaultValue = wrappedValue
|
||||
self.init(defaultValue: wrappedValue, name: nil, description: nil)
|
||||
|
||||
// "Custom" Option (User-visible, provides SwiftUI view to configure option)
|
||||
public extension Option where Value: LocalizedOptionValue
|
||||
{
|
||||
// Non-Optional
|
||||
convenience init(wrappedValue: Value, name: LocalizedStringKey, description: LocalizedStringKey? = nil, @ViewBuilder detailView: @escaping (Binding<Value>) -> DetailView)
|
||||
{
|
||||
self.init(defaultValue: wrappedValue, name: name, description: description)
|
||||
|
||||
self.detailView = { [weak self] in
|
||||
guard let self else { return nil }
|
||||
|
||||
let view = detailView(self.valueBinding)
|
||||
return view
|
||||
}
|
||||
}
|
||||
|
||||
// Optional, default = nil
|
||||
convenience init(name: LocalizedStringKey, description: LocalizedStringKey? = nil, @ViewBuilder detailView: @escaping (Binding<Value>) -> DetailView) where Value: OptionalProtocol, Value.Wrapped: LocalizedOptionValue
|
||||
{
|
||||
self.init(defaultValue: Value.none, name: name, description: description)
|
||||
|
||||
self.detailView = { [weak self] in
|
||||
guard let self else { return nil }
|
||||
|
||||
let view = detailView(self.valueBinding)
|
||||
return view
|
||||
}
|
||||
}
|
||||
|
||||
// Optional, default = non-nil
|
||||
convenience init(wrappedValue: Value, name: LocalizedStringKey, description: LocalizedStringKey? = nil, @ViewBuilder detailView: @escaping (Binding<Value>) -> DetailView) where Value: OptionalProtocol, Value.Wrapped: LocalizedOptionValue
|
||||
{
|
||||
self.init(defaultValue: wrappedValue, name: name, description: description)
|
||||
|
||||
self.detailView = { [weak self] in
|
||||
guard let self else { return nil }
|
||||
|
||||
let view = detailView(self.valueBinding)
|
||||
return view
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,10 +11,16 @@ import SwiftUI
|
||||
public protocol AnyOption<Value>: AnyObject, Identifiable
|
||||
{
|
||||
associatedtype Value: OptionValue
|
||||
associatedtype DetailView: View
|
||||
|
||||
var name: LocalizedStringKey? { get }
|
||||
var description: LocalizedStringKey? { get }
|
||||
|
||||
var key: String { get }
|
||||
var settingsKey: SettingsName { get }
|
||||
|
||||
var detailView: () -> DetailView? { get }
|
||||
|
||||
var value: Value { get set }
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user